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