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