Java tutorial
/** * ========================================================================================== * = JAHIA'S DUAL LICENSING - IMPORTANT INFORMATION = * ========================================================================================== * * http://www.jahia.com * * Copyright (C) 2002-2017 Jahia Solutions Group SA. All rights reserved. * * THIS FILE IS AVAILABLE UNDER TWO DIFFERENT LICENSES: * 1/GPL OR 2/JSEL * * 1/ GPL * ================================================================================== * * IF YOU DECIDE TO CHOOSE THE GPL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS: * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * * 2/ JSEL - Commercial and Supported Versions of the program * =================================================================================== * * IF YOU DECIDE TO CHOOSE THE JSEL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS: * * Alternatively, commercial and supported versions of the program - also known as * Enterprise Distributions - must be used in accordance with the terms and conditions * contained in a separate written agreement between you and Jahia Solutions Group SA. * * If you are unsure which license is appropriate for your use, * please contact the sales department at sales@jahia.com. */ package org.jahia.services.applications.pluto; import org.apache.commons.lang.StringUtils; import org.apache.pluto.driver.url.PortalURLParameter; import org.apache.pluto.driver.url.PortalURLParser; import org.apache.pluto.driver.url.PortalURL; import org.apache.pluto.driver.url.impl.PortalURLParserImpl; import org.apache.pluto.driver.url.impl.RelativePortalURLImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.portlet.PortletMode; import javax.portlet.WindowState; import javax.servlet.http.HttpServletRequest; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.Iterator; import java.util.Map; import java.util.StringTokenizer; import java.util.regex.Pattern; /** * * User: Serge Huber * Date: 28 juil. 2008 * Time: 15:13:28 * */ public class JahiaPortalURLParserImpl implements PortalURLParser { /** * Logger. */ private static final Logger LOG = LoggerFactory.getLogger(PortalURLParserImpl.class); /** * The singleton parser instance. */ private static final PortalURLParser PARSER = new JahiaPortalURLParserImpl(); private static final PortalURLParser DEFAULT_PARSER = PortalURLParserImpl.getParser(); // Constants used for Encoding/Decoding ------------------------------------ private static final String PREFIX = "__"; private static final String DELIM = "_"; private static final String PORTLET_ID = "pd"; private static final String ACTION = "ac"; private static final String RESOURCE = "rs"; private static final String RESOURCE_ID = "ri"; private static final String CACHE_LEVEL = "cl"; private static final String RENDER_PARAM = "rp"; private static final String PRIVATE_RENDER_PARAM = "pr"; private static final String PUBLIC_RENDER_PARAM = "sp"; private static final String WINDOW_STATE = "ws"; private static final String PORTLET_MODE = "pm"; private static final String VALUE_DELIM = "0x0"; private static final Pattern VALUE_DELIM_PATTERN = Pattern.compile(VALUE_DELIM); //This is a list of characters that need to be encoded to be protected //The ? is necessary to protect URI's with a query portion that is being passed as a parameter private static final String[][] ENCODINGS = new String[][] { new String[] { "_", "0x1" }, new String[] { ".", "0x2" }, new String[] { "/", "0x3" }, new String[] { "\r", "0x4" }, new String[] { "\n", "0x5" }, new String[] { "<", "0x6" }, new String[] { ">", "0x7" }, new String[] { " ", "0x8" }, new String[] { "#", "0x9" }, new String[] { "?", "0xa" }, new String[] { "\\", "0xb" }, new String[] { "%", "0xc" }, }; public static final String PORTLET_INFO = "portletInfo"; // Constructor ------------------------------------------------------------- /** * Private constructor that prevents external instantiation. */ private JahiaPortalURLParserImpl() { // Do nothing. } /** * Returns the singleton parser instance. * * @return the singleton parser instance. */ public static PortalURLParser getParser() { return PARSER; } /** * Parse a servlet request to a portal URL. * * @return the portal URL. */ public PortalURL parse(HttpServletRequest request) { final String urlBase = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort(); final String contextPath = request.getContextPath(); final String servletName = request.getServletPath(); final String pathInfo = request.getPathInfo(); return parse(urlBase, contextPath, servletName, pathInfo, request.getParameter(PORTLET_INFO)); } /** * Parse a servlet request to a portal URL. * * @return the portal URL. */ private PortalURL parse(String urlBase, String contextPath, String servletName, String pathInfo, String portletInfo) { // Construct portal URL using info retrieved from servlet request. PortalURL portalURL = new RelativePortalURLImpl(urlBase, contextPath, servletName + pathInfo, this); if (portletInfo == null) { if (servletName.contains(".jsp") && !servletName.endsWith(".jsp")) { int idx = servletName.indexOf(".jsp") + ".jsp".length(); portletInfo = servletName.substring(idx); servletName = servletName.substring(0, idx); portalURL = new RelativePortalURLImpl(urlBase, contextPath, servletName, this); } else { return portalURL; } } if (LOG.isDebugEnabled()) { LOG.debug("Parsing request pathInfo: " + portletInfo); } StringBuilder renderPath = new StringBuilder(); StringTokenizer st = new StringTokenizer(portletInfo, "/", false); while (st.hasMoreTokens()) { String token = st.nextToken(); // Part of the render path: append to renderPath. if (!token.startsWith(PREFIX)) { // renderPath.append(token); //Fix for PLUTO-243 renderPath.append('/').append(token); } // Resource window definition: portalURL.setResourceWindow(). else if (token.startsWith(PREFIX + RESOURCE)) { portalURL.setResourceWindow(decodeControlParameter(token)[0]); } // Action window definition: portalURL.setActionWindow(). else if (token.startsWith(PREFIX + ACTION)) { portalURL.setActionWindow(decodeControlParameter(token)[0]); } // Cacheability definition: portalURL.setCacheability(). else if (token.startsWith(PREFIX + CACHE_LEVEL)) { portalURL.setCacheability(decodeControlParameter(token)[0]); } // ResourceID definition: portalURL.setResourceID(). else if (token.startsWith(PREFIX + RESOURCE_ID)) { portalURL.setResourceID(decodeControlParameter(token)[0]); } // Window state definition: portalURL.setWindowState(). else if (token.startsWith(PREFIX + WINDOW_STATE)) { String[] decoded = decodeControlParameter(token); portalURL.setWindowState(decoded[0], new WindowState(decoded[1])); } // Portlet mode definition: portalURL.setPortletMode(). else if (token.startsWith(PREFIX + PORTLET_MODE)) { String[] decoded = decodeControlParameter(token); portalURL.setPortletMode(decoded[0], new PortletMode(decoded[1])); } // Portal URL parameter: portalURL.addParameter(). else if (token.startsWith(PREFIX + RENDER_PARAM)) { String value = null; if (st.hasMoreTokens()) { value = st.nextToken(); } //set the PortalURLParameter param = decodeParameter(token, value); portalURL.addParameter(param); } else if (token.startsWith(PREFIX + PRIVATE_RENDER_PARAM)) { String value = null; if (st.hasMoreTokens()) { value = st.nextToken(); } PortalURLParameter param = decodePublicParameter(token, value); if (param != null) { //set private (Resource) parameter in portalURL portalURL.getPrivateRenderParameters().put(param.getName(), param.getValues()); } } else if (token.startsWith(PREFIX + PUBLIC_RENDER_PARAM)) { String value = null; if (st.hasMoreTokens()) { value = st.nextToken(); } PortalURLParameter param = decodePublicParameter(token, value); if (param != null) { //set public parameter in portalURL portalURL.addPublicParameterCurrent(param.getName(), param.getValues()); } } } if (renderPath.length() > 0) { portalURL.setRenderPath(renderPath.toString()); } // Return the portal URL. return portalURL; } /** * Converts a portal URL to a URL string. * * @param portalURL the portal URL to convert. * @return a URL string representing the portal URL. */ public String toString(PortalURL portalURL) { final StringBuilder buffer = new StringBuilder(); // Append the server URI and the servlet path. //buffer.append(portalURL.getServletPath().startsWith("/") ? "" : "/").append(portalURL.getServletPath()); return buffer.append(StringUtils.substringAfterLast(portalURL.getServletPath(), "/")).append("?") .append(PORTLET_INFO).append("=").append(toPortletPathInfo(portalURL).replace('?', '&')).toString(); } /** * to porlet path info * * @param portalURL * @return */ private String toPortletPathInfo(PortalURL portalURL) { final StringBuffer buffer = new StringBuffer(); // Start the pathInfo with the path to the render URL (page). if (portalURL.getRenderPath() != null) { buffer.append(portalURL.getRenderPath()); } //Append the resource window definition, if it exists. if (portalURL.getResourceWindow() != null) { buffer.append("/"); buffer.append(PREFIX).append(RESOURCE).append(encodeCharacters(portalURL.getResourceWindow())); } // Append the action window definition, if it exists. if (portalURL.getActionWindow() != null) { buffer.append("/"); buffer.append(PREFIX).append(ACTION).append(encodeCharacters(portalURL.getActionWindow())); } if (portalURL.getResourceWindow() != null) { if (portalURL.getCacheability() != null) { buffer.append("/"); buffer.append(PREFIX).append(CACHE_LEVEL).append(encodeCharacters(portalURL.getCacheability())); } if (portalURL.getResourceID() != null) { buffer.append("/"); buffer.append(PREFIX).append(RESOURCE_ID).append(encodeCharacters(portalURL.getResourceID())); } } // Append portlet mode definitions. for (Iterator it = portalURL.getPortletModes().entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); buffer.append("/").append( encodeControlParameter(PORTLET_MODE, entry.getKey().toString(), entry.getValue().toString())); } // Append window state definitions. for (Iterator it = portalURL.getWindowStates().entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); buffer.append("/").append( encodeControlParameter(WINDOW_STATE, entry.getKey().toString(), entry.getValue().toString())); } // Append action and render parameters. StringBuilder query = new StringBuilder("?"); boolean firstParam = true; for (Iterator it = portalURL.getParameters().iterator(); it.hasNext();) { PortalURLParameter param = (PortalURLParameter) it.next(); // Encode action params in the query appended at the end of the URL. if (portalURL.getActionWindow() != null && portalURL.getActionWindow().equals(param.getWindowId()) || (portalURL.getResourceWindow() != null && portalURL.getResourceWindow().equals(param.getWindowId()))) { for (int i = 0; i < param.getValues().length; i++) { // FIX for PLUTO-247 if (firstParam) { firstParam = false; } else { query.append("&"); } query.append(encodeQueryParam(param.getName())).append("=") .append(encodeQueryParam(param.getValues()[i])); } } // Encode render params as a part of the URL. else if (param.getValues() != null && param.getValues().length > 0) { String valueString = encodeMultiValues(param.getValues()); if (valueString.length() > 0) { buffer.append("/") .append(encodeControlParameter(RENDER_PARAM, param.getWindowId(), param.getName())); buffer.append("/").append(valueString); } } } encode(buffer); if (portalURL.getResourceWindow() != null) { Map<String, String[]> privateParamList = portalURL.getPrivateRenderParameters(); if (privateParamList != null) { for (Iterator iter = privateParamList.keySet().iterator(); iter.hasNext();) { String paramname = (String) iter.next(); String[] tmp = privateParamList.get(paramname); String valueString = encodeMultiValues(tmp); if (valueString.length() > 0) { buffer.append("/").append(encodePublicParamname(PRIVATE_RENDER_PARAM, paramname)); buffer.append("/").append(valueString); } } } } Map<String, String[]> publicParamList = portalURL.getPublicParameters(); if (publicParamList != null) { for (Iterator iter = publicParamList.keySet().iterator(); iter.hasNext();) { String paramname = (String) iter.next(); String[] tmp = publicParamList.get(paramname); String valueString = encodeMultiValues(tmp); if (valueString.length() > 0) { buffer.append("/").append(encodePublicParamname(PUBLIC_RENDER_PARAM, paramname)); buffer.append("/").append(valueString); } } } // Construct the string representing the portal URL. // Fix for PLUTO-247 - check if query string contains parameters if (query.length() > 1) { return buffer.append(query).toString(); } // Construct the string representing the portal URL. return buffer.append(query).toString(); } public static void encode(StringBuffer url) { replaceChar(url, "|", "%7C"); replaceChar(url, "\"", "%22"); } private static void replaceChar(StringBuffer url, String character, String change) { boolean contains = url.toString().contains(character); while (contains) { int index = url.indexOf(character); url.deleteCharAt(index); url.insert(index, change, 0, 3); contains = url.toString().contains(character); } } private String encodeQueryParam(String param) { try { return URLEncoder.encode(param, "UTF-8"); } catch (UnsupportedEncodingException e) { // If this happens, we've got bigger problems. throw new RuntimeException(e); } } // Private Encoding/Decoding Methods --------------------------------------- /** * Encode a control parameter. * * @param type the type of the control parameter, which may be: * portlet mode, window state, or render parameter. * @param windowId the portlet window ID. * @param name the name to encode. */ private String encodeControlParameter(String type, String windowId, String name) { StringBuilder buffer = new StringBuilder(); buffer.append(PREFIX).append(type).append(encodeCharacters(windowId)).append(DELIM).append(name); return buffer.toString(); } private String encodePublicParamname(String type, String name) { StringBuilder buffer = new StringBuilder(); buffer.append(PREFIX).append(type).append(DELIM).append(name); return buffer.toString(); } /** * Encode a string array containing multiple values into a single string. * This method is used to encode multiple render parameter values. * * @param values the string array to encode. * @return a single string containing all the values. */ private String encodeMultiValues(String[] values) { StringBuilder buffer = new StringBuilder(); for (int i = 0; i < values.length; i++) { // Do not operate on the array reference String currentValue = values[i]; try { if (currentValue != null) currentValue = URLEncoder.encode(values[i], "UTF-8"); } catch (UnsupportedEncodingException e) { LOG.warn(e.getMessage(), e); } buffer.append(currentValue != null ? currentValue : ""); if (i + 1 < values.length) { buffer.append(VALUE_DELIM); } } return encodeCharacters(buffer.toString()); } /** * Encode special characters contained in the string value. * * @param string the string value to encode. * @return the encoded string. */ private String encodeCharacters(String string) { for (int i = 0; i < ENCODINGS.length; i++) { string = string.replace(ENCODINGS[i][0], ENCODINGS[i][1]); } return string; } /** * Decode a control parameter. * * @param control the control parameter to decode. * @return values a pair of decoded values. */ private String[] decodeControlParameter(String control) { String[] valuePair = new String[2]; control = control.substring((PREFIX + PORTLET_ID).length()); int index = control.indexOf(DELIM); if (index >= 0) { valuePair[0] = control.substring(0, index); valuePair[0] = decodeCharacters(valuePair[0]); if (index + 1 <= control.length()) { valuePair[1] = control.substring(index + 1); valuePair[1] = decodeCharacters(valuePair[1]); } else { valuePair[1] = ""; } } else { valuePair[0] = decodeCharacters(control); } return valuePair; } /** * Decode a name-value pair into a portal URL parameter. * * @param name the parameter name. * @param value the parameter value. * @return the decoded portal URL parameter. */ private PortalURLParameter decodeParameter(String name, String value) { if (LOG.isDebugEnabled()) { LOG.debug("Decoding parameter: name=" + name + ", value=" + value); } // Decode the name into window ID and parameter name. String noPrefix = name.substring((PREFIX + PORTLET_ID).length()); String windowId = noPrefix.substring(0, noPrefix.indexOf(DELIM)); String paramName = noPrefix.substring(noPrefix.indexOf(DELIM) + 1); // Decode special characters in window ID and parameter value. windowId = decodeCharacters(windowId); if (value != null) { value = decodeCharacters(value); } // Split multiple values into a value array. String[] paramValues = VALUE_DELIM_PATTERN.split(value); for (int i = 0; i < paramValues.length; i++) { try { paramValues[i] = URLDecoder.decode(paramValues[i], "UTF-8"); } catch (UnsupportedEncodingException e) { LOG.warn(e.getMessage(), e); } } // Construct portal URL parameter and return. return new PortalURLParameter(windowId, paramName, paramValues); } private PortalURLParameter decodePublicParameter(String name, String value) { if (LOG.isDebugEnabled()) { LOG.debug("Decoding parameter: name=" + name + ", value=" + value); } // // Decode the name into window ID and parameter name. String noPrefix = name.substring((PREFIX + PORTLET_ID).length()); String paramName = noPrefix.substring(noPrefix.indexOf(DELIM) + 1); // Decode special characters in parameter value. if (value != null) { value = decodeCharacters(value); } // Split multiple values into a value array. String[] paramValues = VALUE_DELIM_PATTERN.split(value); // Construct portal URL parameter and return. return new PortalURLParameter(null, paramName, paramValues); } /** * Decode special characters contained in the string value. * * @param string the string value to decode. * @return the decoded string. */ private String decodeCharacters(String string) { for (int i = 0; i < ENCODINGS.length; i++) { string = string.replace(ENCODINGS[i][1], ENCODINGS[i][0]); } return string; } }