001: package com.opensymphony.webwork.portlet.util;
002:
003: import java.io.UnsupportedEncodingException;
004: import java.net.URLEncoder;
005: import java.util.HashMap;
006: import java.util.Iterator;
007: import java.util.Map;
008: import java.util.StringTokenizer;
009:
010: import javax.portlet.PortletMode;
011: import javax.portlet.PortletSecurityException;
012: import javax.portlet.PortletURL;
013: import javax.portlet.RenderRequest;
014: import javax.portlet.RenderResponse;
015: import javax.portlet.WindowState;
016: import javax.servlet.jsp.JspException;
017:
018: import org.apache.commons.lang.StringUtils;
019: import org.apache.commons.logging.Log;
020: import org.apache.commons.logging.LogFactory;
021:
022: import com.opensymphony.webwork.portlet.PortletActionConstants;
023: import com.opensymphony.webwork.portlet.context.PortletActionContext;
024: import com.opensymphony.xwork.ActionContext;
025: import com.opensymphony.xwork.util.OgnlValueStack;
026: import com.opensymphony.xwork.util.TextParseUtil;
027:
028: /**
029: * Helper class for creating Portlet URLs. Portlet URLs are fundamentally different from regular
030: * servlet URLs since they never target the application itself; all requests go through the portlet
031: * container and must therefore be programatically constructed using the
032: * {@link javax.portlet.RenderResponse#createActionURL()} and
033: * {@link javax.portlet.RenderResponse#createRenderURL()} APIs.
034: *
035: * @author Nils-Helge Garli
036: */
037: public class PortletUrlHelper {
038: private static final Log LOG = LogFactory
039: .getLog(PortletUrlHelper.class);
040:
041: /**
042: * Default HTTP port (80).
043: */
044: private static final int DEFAULT_HTTP_PORT = 80;
045:
046: /**
047: * Default HTTPS port (443).
048: */
049: private static final int DEFAULT_HTTPS_PORT = 443;
050:
051: private static final String AMP = "&";
052:
053: /**
054: * Create a portlet URL with for the specified action and namespace.
055: *
056: * @param action The action the URL should invoke.
057: * @param namespace The namespace of the action to invoke.
058: * @param params The parameters of the URL.
059: * @param type The type of the url, either <tt>action</tt> or <tt>render</tt>
060: * @param mode The PortletMode of the URL.
061: * @param state The WindowState of the URL.
062: * @return The URL String.
063: */
064: public static String buildUrl(String action, String namespace,
065: Map params, String type, String mode, String state) {
066: return buildUrl(action, namespace, params, null, type, mode,
067: state, true, true);
068: }
069:
070: /**
071: * Create a portlet URL with for the specified action and namespace.
072: *
073: * @see #buildUrl(String, String, Map, String, String, String)
074: */
075: public static String buildUrl(String action, String namespace,
076: Map params, String scheme, String type, String portletMode,
077: String windowState, boolean includeContext,
078: boolean encodeResult) {
079: RenderRequest request = PortletActionContext.getRenderRequest();
080: RenderResponse response = PortletActionContext
081: .getRenderResponse();
082: LOG.debug("Creating url. Action = " + action + ", Namespace = "
083: + namespace + ", Type = " + type);
084: namespace = prependNamespace(namespace, portletMode);
085: if (StringUtils.isEmpty(portletMode)) {
086: portletMode = PortletActionContext.getRenderRequest()
087: .getPortletMode().toString();
088: }
089: String result = null;
090: int paramStartIndex = action.indexOf('?');
091: if (paramStartIndex > 0) {
092: String value = action;
093: action = value.substring(0, value.indexOf('?'));
094: String queryStr = value.substring(paramStartIndex + 1);
095: StringTokenizer tok = new StringTokenizer(queryStr, "&");
096: while (tok.hasMoreTokens()) {
097: String paramVal = tok.nextToken();
098: String key = paramVal.substring(0, paramVal
099: .indexOf('='));
100: String val = paramVal
101: .substring(paramVal.indexOf('=') + 1);
102: params.put(key, new String[] { val });
103: }
104: }
105: if (StringUtils.isNotEmpty(namespace)) {
106: StringBuffer sb = new StringBuffer();
107: sb.append(namespace);
108: if (!action.startsWith("/") && !namespace.endsWith("/")) {
109: sb.append("/");
110: }
111: action = sb.append(action).toString();
112: LOG.debug("Resulting actionPath: " + action);
113: }
114: params.put(PortletActionConstants.ACTION_PARAM,
115: new String[] { action });
116:
117: PortletURL url = null;
118: if ("action".equalsIgnoreCase(type)) {
119: LOG.debug("Creating action url");
120: url = response.createActionURL();
121: } else {
122: LOG.debug("Creating render url");
123: url = response.createRenderURL();
124: }
125:
126: params.put(PortletActionConstants.MODE_PARAM, portletMode);
127: url.setParameters(ensureParamsAreStringArrays(params));
128:
129: if ("HTTPS".equalsIgnoreCase(scheme)) {
130: try {
131: url.setSecure(true);
132: } catch (PortletSecurityException e) {
133: LOG.error("Cannot set scheme to https", e);
134: }
135: }
136: try {
137: url.setPortletMode(getPortletMode(request, portletMode));
138: url.setWindowState(getWindowState(request, windowState));
139: } catch (Exception e) {
140: LOG.error("Unable to set mode or state:" + e.getMessage(),
141: e);
142: }
143: result = url.toString();
144: // TEMP BUG-WORKAROUND FOR DOUBLE ESCAPING OF AMPERSAND
145: if (result.indexOf("&") >= 0) {
146: result = StringUtils.replace(result, "&", "&");
147: }
148: return result;
149:
150: }
151:
152: /**
153: *
154: * Prepend the namespace configuration for the specified namespace and PortletMode.
155: *
156: * @param namespace The base namespace.
157: * @param portletMode The PortletMode.
158: *
159: * @return prepended namespace.
160: */
161: private static String prependNamespace(String namespace,
162: String portletMode) {
163: StringBuffer sb = new StringBuffer();
164: PortletMode mode = PortletActionContext.getRenderRequest()
165: .getPortletMode();
166: if (StringUtils.isNotEmpty(portletMode)) {
167: mode = new PortletMode(portletMode);
168: }
169: String portletNamespace = PortletActionContext
170: .getPortletNamespace();
171: String modeNamespace = (String) PortletActionContext
172: .getModeNamespaceMap().get(mode);
173: LOG.debug("PortletNamespace: " + portletNamespace
174: + ", modeNamespace: " + modeNamespace);
175: if (StringUtils.isNotEmpty(portletNamespace)) {
176: sb.append(portletNamespace);
177: }
178: if (StringUtils.isNotEmpty(modeNamespace)) {
179: if (!modeNamespace.startsWith("/")) {
180: sb.append("/");
181: }
182: sb.append(modeNamespace);
183: }
184: if (StringUtils.isNotEmpty(namespace)) {
185: if (!namespace.startsWith("/")) {
186: sb.append("/");
187: }
188: sb.append(namespace);
189: }
190: LOG.debug("Resulting namespace: " + sb);
191: return sb.toString();
192: }
193:
194: /**
195: * Encode an url to a non webwork action resource, like stylesheet, image or
196: * servlet.
197: *
198: * @param value
199: * @return encoded url to non webwork action resources.
200: */
201: public static String buildResourceUrl(String value, Map params) {
202: StringBuffer sb = new StringBuffer();
203: // Relative URLs are not allowed in a portlet
204: if (!value.startsWith("/")) {
205: sb.append("/");
206: }
207: sb.append(value);
208: if (params != null && params.size() > 0) {
209: sb.append("?");
210: Iterator it = params.keySet().iterator();
211: while (it.hasNext()) {
212: String key = (String) it.next();
213: String val = (String) params.get(key);
214: sb.append(URLEncoder.encode(key)).append("=");
215: sb.append(URLEncoder.encode(val));
216: if (it.hasNext()) {
217: sb.append("&");
218: }
219: }
220: }
221: RenderResponse resp = PortletActionContext.getRenderResponse();
222: RenderRequest req = PortletActionContext.getRenderRequest();
223: return resp.encodeURL(req.getContextPath() + sb.toString());
224: }
225:
226: /**
227: * Will ensure that all entries in <code>params</code> are String arrays,
228: * as requried by the setParameters on the PortletURL.
229: *
230: * @param params The parameters to the URL.
231: * @return A Map with all parameters as String arrays.
232: */
233: public static Map ensureParamsAreStringArrays(Map params) {
234: Map result = null;
235: if (params != null) {
236: result = new HashMap(params.size());
237: Iterator it = params.keySet().iterator();
238: while (it.hasNext()) {
239: Object key = it.next();
240: Object val = params.get(key);
241: if (val instanceof String[]) {
242: result.put(key, val);
243: } else {
244: result.put(key, new String[] { val.toString() });
245: }
246: }
247: }
248: return result;
249: }
250:
251: /**
252: * Convert the given String to a WindowState object.
253: *
254: * @param portletReq The RenderRequest.
255: * @param windowState The WindowState as a String.
256: * @return The WindowState that mathces the <tt>windowState</tt> String, or if
257: * the Sring is blank, the current WindowState.
258: */
259: private static WindowState getWindowState(RenderRequest portletReq,
260: String windowState) {
261: WindowState state = portletReq.getWindowState();
262: if (StringUtils.isNotEmpty(windowState)) {
263: state = portletReq.getWindowState();
264: if ("maximized".equalsIgnoreCase(windowState)) {
265: state = WindowState.MAXIMIZED;
266: } else if ("normal".equalsIgnoreCase(windowState)) {
267: state = WindowState.NORMAL;
268: } else if ("minimized".equalsIgnoreCase(windowState)) {
269: state = WindowState.MINIMIZED;
270: }
271: }
272: if (state == null) {
273: state = WindowState.NORMAL;
274: }
275: return state;
276: }
277:
278: /**
279: * Convert the given String to a PortletMode object.
280: *
281: * @param portletReq The RenderRequest.
282: * @param portletMode The PortletMode as a String.
283: * @return The PortletMode that mathces the <tt>portletMode</tt> String, or if
284: * the Sring is blank, the current PortletMode.
285: */
286: private static PortletMode getPortletMode(RenderRequest portletReq,
287: String portletMode) {
288: PortletMode mode = portletReq.getPortletMode();
289:
290: if (StringUtils.isNotEmpty(portletMode)) {
291: mode = portletReq.getPortletMode();
292: if ("edit".equalsIgnoreCase(portletMode)) {
293: mode = PortletMode.EDIT;
294: } else if ("view".equalsIgnoreCase(portletMode)) {
295: mode = PortletMode.VIEW;
296: } else if ("help".equalsIgnoreCase(portletMode)) {
297: mode = PortletMode.HELP;
298: }
299: }
300: if (mode == null) {
301: mode = PortletMode.VIEW;
302: }
303: return mode;
304: }
305: }
|