View Javadoc

    1   /*
    2    * $Id: PortletUrlHelper.java 497293 2007-01-18 03:13:27Z mrdon $
    3    *
    4    * Licensed to the Apache Software Foundation (ASF) under one
    5    * or more contributor license agreements.  See the NOTICE file
    6    * distributed with this work for additional information
    7    * regarding copyright ownership.  The ASF licenses this file
    8    * to you under the Apache License, Version 2.0 (the
    9    * "License"); you may not use this file except in compliance
    10   * with the License.  You may obtain a copy of the License at
    11   *
    12   *  http://www.apache.org/licenses/LICENSE-2.0
    13   *
    14   * Unless required by applicable law or agreed to in writing,
    15   * software distributed under the License is distributed on an
    16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    17   * KIND, either express or implied.  See the License for the
    18   * specific language governing permissions and limitations
    19   * under the License.
    20   */
    21  package org.apache.struts2.portlet.util;
    22
    23  import java.io.UnsupportedEncodingException;
    24  import java.net.URLEncoder;
    25  import java.util.HashMap;
    26  import java.util.Iterator;
    27  import java.util.Map;
    28  import java.util.StringTokenizer;
    29
    30  import javax.portlet.PortletMode;
    31  import javax.portlet.PortletSecurityException;
    32  import javax.portlet.PortletURL;
    33  import javax.portlet.RenderRequest;
    34  import javax.portlet.RenderResponse;
    35  import javax.portlet.WindowState;
    36
    37  import org.apache.commons.logging.Log;
    38  import org.apache.commons.logging.LogFactory;
    39  import org.apache.struts2.StrutsException;
    40  import org.apache.struts2.portlet.PortletActionConstants;
    41  import org.apache.struts2.portlet.context.PortletActionContext;
    42  import com.opensymphony.xwork2.util.TextUtils;
    43
    44  /***
    45   * Helper class for creating Portlet URLs. Portlet URLs are fundamentally different from regular
    46   * servlet URLs since they never target the application itself; all requests go through the portlet
    47   * container and must therefore be programatically constructed using the
    48   * {@link javax.portlet.RenderResponse#createActionURL()} and
    49   * {@link javax.portlet.RenderResponse#createRenderURL()} APIs.
    50   *
    51   */
    52  public class PortletUrlHelper {
    53      public static final String ENCODING = "UTF-8";
    54
    55      private static final Log LOG = LogFactory.getLog(PortletUrlHelper.class);
    56
    57      /***
    58       * Create a portlet URL with for the specified action and namespace.
    59       *
    60       * @param action The action the URL should invoke.
    61       * @param namespace The namespace of the action to invoke.
    62       * @param params The parameters of the URL.
    63       * @param type The type of the url, either <tt>action</tt> or <tt>render</tt>
    64       * @param mode The PortletMode of the URL.
    65       * @param state The WindowState of the URL.
    66       * @return The URL String.
    67       */
    68      public static String buildUrl(String action, String namespace, Map params,
    69              String type, String mode, String state) {
    70          return buildUrl(action, namespace, params, null, type, mode, state,
    71                  true, true);
    72      }
    73
    74      /***
    75       * Create a portlet URL with for the specified action and namespace.
    76       *
    77       * @see #buildUrl(String, String, Map, String, String, String)
    78       */
    79      public static String buildUrl(String action, String namespace, Map params,
    80              String scheme, String type, String portletMode, String windowState,
    81              boolean includeContext, boolean encodeResult) {
    82          RenderRequest request = PortletActionContext.getRenderRequest();
    83          RenderResponse response = PortletActionContext.getRenderResponse();
    84          LOG.debug("Creating url. Action = " + action + ", Namespace = "
    85                  + namespace + ", Type = " + type);
    86          namespace = prependNamespace(namespace, portletMode);
    87          if (!TextUtils.stringSet(portletMode)) {
    88              portletMode = PortletActionContext.getRenderRequest().getPortletMode().toString();
    89          }
    90          String result = null;
    91          int paramStartIndex = action.indexOf('?');
    92          if (paramStartIndex > 0) {
    93              String value = action;
    94              action = value.substring(0, value.indexOf('?'));
    95              String queryStr = value.substring(paramStartIndex + 1);
    96              StringTokenizer tok = new StringTokenizer(queryStr, "&");
    97              while (tok.hasMoreTokens()) {
    98                  String paramVal = tok.nextToken();
    99                  String key = paramVal.substring(0, paramVal.indexOf('='));
    100                 String val = paramVal.substring(paramVal.indexOf('=') + 1);
    101                 params.put(key, new String[] { val });
    102             }
    103         }
    104         if (TextUtils.stringSet(namespace)) {
    105             StringBuffer sb = new StringBuffer();
    106             sb.append(namespace);
    107             if(!action.startsWith("/") && !namespace.endsWith("/")) {
    108                 sb.append("/");
    109             }
    110             action = sb.append(action).toString();
    111             LOG.debug("Resulting actionPath: " + action);
    112         }
    113         params.put(PortletActionConstants.ACTION_PARAM, new String[] { action });
    114
    115         PortletURL url = null;
    116         if ("action".equalsIgnoreCase(type)) {
    117             LOG.debug("Creating action url");
    118             url = response.createActionURL();
    119         } else {
    120             LOG.debug("Creating render url");
    121             url = response.createRenderURL();
    122         }
    123
    124         params.put(PortletActionConstants.MODE_PARAM, portletMode);
    125         url.setParameters(ensureParamsAreStringArrays(params));
    126
    127         if ("HTTPS".equalsIgnoreCase(scheme)) {
    128             try {
    129                 url.setSecure(true);
    130             } catch (PortletSecurityException e) {
    131                 LOG.error("Cannot set scheme to https", e);
    132             }
    133         }
    134         try {
    135             url.setPortletMode(getPortletMode(request, portletMode));
    136             url.setWindowState(getWindowState(request, windowState));
    137         } catch (Exception e) {
    138             LOG.error("Unable to set mode or state:" + e.getMessage(), e);
    139         }
    140         result = url.toString();
    141         // TEMP BUG-WORKAROUND FOR DOUBLE ESCAPING OF AMPERSAND
    142         if(result.indexOf("&amp;") >= 0) {
    143             result = result.replace("&amp;", "&");
    144         }
    145         return result;
    146
    147     }
    148
    149     /***
    150      *
    151      * Prepend the namespace configuration for the specified namespace and PortletMode.
    152      *
    153      * @param namespace The base namespace.
    154      * @param portletMode The PortletMode.
    155      *
    156      * @return prepended namespace.
    157      */
    158     private static String prependNamespace(String namespace, String portletMode) {
    159         StringBuffer sb = new StringBuffer();
    160         PortletMode mode = PortletActionContext.getRenderRequest().getPortletMode();
    161         if(TextUtils.stringSet(portletMode)) {
    162             mode = new PortletMode(portletMode);
    163         }
    164         String portletNamespace = PortletActionContext.getPortletNamespace();
    165         String modeNamespace = (String)PortletActionContext.getModeNamespaceMap().get(mode);
    166         LOG.debug("PortletNamespace: " + portletNamespace + ", modeNamespace: " + modeNamespace);
    167         if(TextUtils.stringSet(portletNamespace)) {
    168             sb.append(portletNamespace);
    169         }
    170         if(TextUtils.stringSet(modeNamespace)) {
    171             if(!modeNamespace.startsWith("/")) {
    172                 sb.append("/");
    173             }
    174             sb.append(modeNamespace);
    175         }
    176         if(TextUtils.stringSet(namespace)) {
    177             if(!namespace.startsWith("/")) {
    178                 sb.append("/");
    179             }
    180             sb.append(namespace);
    181         }
    182         LOG.debug("Resulting namespace: " + sb);
    183         return sb.toString();
    184     }
    185
    186     /***
    187      * Encode an url to a non Struts action resource, like stylesheet, image or
    188      * servlet.
    189      *
    190      * @param value
    191      * @return encoded url to non Struts action resources.
    192      */
    193     public static String buildResourceUrl(String value, Map params) {
    194         StringBuffer sb = new StringBuffer();
    195         // Relative URLs are not allowed in a portlet
    196         if (!value.startsWith("/")) {
    197             sb.append("/");
    198         }
    199         sb.append(value);
    200         if(params != null && params.size() > 0) {
    201             sb.append("?");
    202             Iterator it = params.keySet().iterator();
    203             try {
    204             while(it.hasNext()) {
    205                 String key = (String)it.next();
    206                 String val = (String)params.get(key);
    207
    208                 sb.append(URLEncoder.encode(key, ENCODING)).append("=");
    209                 sb.append(URLEncoder.encode(val, ENCODING));
    210                 if(it.hasNext()) {
    211                     sb.append("&");
    212                 }
    213             }
    214             } catch (UnsupportedEncodingException e) {
    215                 throw new StrutsException("Encoding "+ENCODING+" not found");
    216             }
    217         }
    218         RenderResponse resp = PortletActionContext.getRenderResponse();
    219         RenderRequest req = PortletActionContext.getRenderRequest();
    220         return resp.encodeURL(req.getContextPath() + sb.toString());
    221     }
    222
    223     /***
    224      * Will ensure that all entries in <code>params</code> are String arrays,
    225      * as requried by the setParameters on the PortletURL.
    226      *
    227      * @param params The parameters to the URL.
    228      * @return A Map with all parameters as String arrays.
    229      */
    230     public static Map ensureParamsAreStringArrays(Map params) {
    231         Map result = null;
    232         if (params != null) {
    233             result = new HashMap(params.size());
    234             Iterator it = params.keySet().iterator();
    235             while (it.hasNext()) {
    236                 Object key = it.next();
    237                 Object val = params.get(key);
    238                 if (val instanceof String[]) {
    239                     result.put(key, val);
    240                 } else {
    241                     result.put(key, new String[] { val.toString() });
    242                 }
    243             }
    244         }
    245         return result;
    246     }
    247
    248     /***
    249      * Convert the given String to a WindowState object.
    250      *
    251      * @param portletReq The RenderRequest.
    252      * @param windowState The WindowState as a String.
    253      * @return The WindowState that mathces the <tt>windowState</tt> String, or if
    254      * the Sring is blank, the current WindowState.
    255      */
    256     private static WindowState getWindowState(RenderRequest portletReq,
    257             String windowState) {
    258         WindowState state = portletReq.getWindowState();
    259         if (TextUtils.stringSet(windowState)) {
    260             state = portletReq.getWindowState();
    261             if ("maximized".equalsIgnoreCase(windowState)) {
    262                 state = WindowState.MAXIMIZED;
    263             } else if ("normal".equalsIgnoreCase(windowState)) {
    264                 state = WindowState.NORMAL;
    265             } else if ("minimized".equalsIgnoreCase(windowState)) {
    266                 state = WindowState.MINIMIZED;
    267             }
    268         }
    269         if(state == null) {
    270             state = WindowState.NORMAL;
    271         }
    272         return state;
    273     }
    274
    275     /***
    276      * Convert the given String to a PortletMode object.
    277      *
    278      * @param portletReq The RenderRequest.
    279      * @param portletMode The PortletMode as a String.
    280      * @return The PortletMode that mathces the <tt>portletMode</tt> String, or if
    281      * the Sring is blank, the current PortletMode.
    282      */
    283     private static PortletMode getPortletMode(RenderRequest portletReq,
    284             String portletMode) {
    285         PortletMode mode = portletReq.getPortletMode();
    286
    287         if (TextUtils.stringSet(portletMode)) {
    288             mode = portletReq.getPortletMode();
    289             if ("edit".equalsIgnoreCase(portletMode)) {
    290                 mode = PortletMode.EDIT;
    291             } else if ("view".equalsIgnoreCase(portletMode)) {
    292                 mode = PortletMode.VIEW;
    293             } else if ("help".equalsIgnoreCase(portletMode)) {
    294                 mode = PortletMode.HELP;
    295             }
    296         }
    297         if(mode == null) {
    298             mode = PortletMode.VIEW;
    299         }
    300         return mode;
    301     }
    302 }