1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
139 if(result.indexOf("&") >= 0) {
140 result = StringUtils.replace(result, "&", "&");
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
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 }