View Javadoc

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