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 }