001: /*
002: * @author <a href="mailto:novotny@gridsphere.org">Jason Novotny</a>
003: * @version $Id: PortletURLImpl.java 6385 2007-10-25 14:02:26Z wehrens $
004: */
005: package org.gridsphere.portlet.impl;
006:
007: import org.gridsphere.portlet.service.spi.PortletServiceFactory;
008: import org.gridsphere.services.core.portal.PortalConfigService;
009:
010: import javax.portlet.*;
011: import javax.servlet.http.HttpServletRequest;
012: import javax.servlet.http.HttpServletResponse;
013: import java.io.UnsupportedEncodingException;
014: import java.net.URLEncoder;
015: import java.util.*;
016:
017: /**
018: * The <CODE>PortletURL</CODE> interface represents a URL
019: * that reference the portlet itself.
020: * <p/>
021: * A PortletURL is created through the <CODE>RenderResponse</CODE>.
022: * Parameters, a portlet mode, a window state and a security level
023: * can be added to <CODE>PortletURL</CODE> objects. The PortletURL
024: * must be converted to a String in order to embed it into
025: * the markup generated by the portlet.
026: * <p/>
027: * There are two types of PortletURLs:
028: * <ul>
029: * <li>Action URLs, they are created with <CODE>RenderResponse.createActionURL</CODE>, and
030: * trigger an action request followed by a render request.
031: * <li>Render URLs, they are created with <CODE>RenderResponse.createRenderURL</CODE>, and
032: * trigger a render request.
033: * </ul>
034: * <p/>
035: * The string reprensentation of a PortletURL does not need to be a valid
036: * URL at the time the portlet is generating its content. It may contain
037: * special tokens that will be converted to a valid URL, by the portal,
038: * before the content is returned to the client.
039: */
040: public class PortletURLImpl implements PortletURL {
041:
042: private HttpServletResponse res = null;
043: private HttpServletRequest req = null;
044: private boolean isSecure = false;
045: private Map<String, Object> store = null;
046: private boolean redirect = false;
047: private boolean encoding = true;
048: private boolean isRender = false;
049: private String label = null;
050:
051: private String layout = null;
052:
053: private PortalConfigService configService = null;
054:
055: private PortletMode mode = null;
056: private WindowState state = null;
057:
058: protected PortletURLImpl() {
059: }
060:
061: /**
062: * Constructs a PortletURL from a servlet request and response
063: *
064: * @param req the servlet request
065: * @param res the servlet response
066: * @param isRender true if this is a render url, false if an action url
067: */
068: public PortletURLImpl(HttpServletRequest req,
069: HttpServletResponse res, boolean isRender) {
070: this .store = new HashMap<String, Object>();
071: this .res = res;
072: this .req = req;
073:
074: this .isSecure = req.isSecure();
075: this .isRender = isRender;
076: configService = (PortalConfigService) PortletServiceFactory
077: .createPortletService(PortalConfigService.class, true);
078: }
079:
080: /**
081: * Indicates the window state the portlet should be in, if this
082: * portlet URL triggers a request.
083: * <p/>
084: * A URL can not have more than one window state attached to it.
085: * If more than one window state is set only the last one set
086: * is attached to the URL.
087: *
088: * @param windowState the portlet window state
089: * @throws WindowStateException if the portlet cannot switch to this state,
090: * because the portal does not support this state, the portlet has not
091: * declared in its deployment descriptor that it supports this state, or the current
092: * user is not allowed to switch to this state.
093: * The <code>PortletRequest.isWindowStateAllowed()</code> method can be used
094: * to check if the portlet can set a given window state.
095: * @see PortletRequest#isWindowStateAllowed
096: */
097: public void setWindowState(WindowState windowState)
098: throws WindowStateException {
099: if (windowState == null)
100: throw new IllegalArgumentException(
101: "Window state cannot be null");
102: boolean isSupported = false;
103: PortalContext context = (PortalContext) req
104: .getAttribute(SportletProperties.PORTAL_CONTEXT);
105: Enumeration e = context.getSupportedWindowStates();
106: while (e.hasMoreElements()) {
107: WindowState supported = (WindowState) e.nextElement();
108: if (supported.equals(windowState)) {
109: isSupported = true;
110: break;
111: }
112: }
113:
114: if (isSupported) {
115: state = windowState;
116: } else {
117: throw new WindowStateException("Illegal window state",
118: windowState);
119: }
120:
121: }
122:
123: /**
124: * Indicates the portlet mode the portlet must be in, if this
125: * portlet URL triggers a request.
126: * <p/>
127: * A URL can not have more than one portlet mode attached to it.
128: * If more than one portlet mode is set only the last one set
129: * is attached to the URL.
130: *
131: * @param portletMode the portlet mode
132: * @throws PortletModeException if the portlet cannot switch to this mode,
133: * because the portal does not support this mode, the portlet has not
134: * declared in its deployment descriptor that it supports this mode for the current markup,
135: * or the current user is not allowed to switch to this mode.
136: * The <code>PortletRequest.isPortletModeAllowed()</code> method can be used
137: * to check if the portlet can set a given portlet mode.
138: * @see PortletRequest#isPortletModeAllowed
139: */
140: public void setPortletMode(PortletMode portletMode)
141: throws PortletModeException {
142: if (portletMode == null)
143: throw new IllegalArgumentException(
144: "Portlet mode cannot be null");
145: Set allowedModes = (Set) req
146: .getAttribute(SportletProperties.ALLOWED_MODES);
147: if (allowedModes.contains(portletMode.toString())) {
148: // hack to handle config mode
149: if (portletMode.toString().equals("config"))
150: portletMode = new PortletMode("configure");
151: mode = portletMode;
152: } else {
153: throw new PortletModeException("Illegal portlet mode",
154: portletMode);
155: }
156: }
157:
158: /**
159: * Sets the given String parameter to this URL.
160: * <p/>
161: * This method replaces all parameters with the given key.
162: * <p/>
163: * The <code>PortletURL</code> implementation 'x-www-form-urlencoded' encodes
164: * all parameter names and values. Developers should not encode them.
165: * <p/>
166: * A portlet container may prefix the attribute names internally
167: * in order to preserve a unique namespace for the portlet.
168: *
169: * @param name the parameter name
170: * @param value the parameter value
171: * @throws IllegalArgumentException if name or value are <code>null</code>.
172: */
173: public void setParameter(String name, String value) {
174: if ((name == null) || !(name instanceof String))
175: throw new IllegalArgumentException(
176: "name must be a non-null string");
177: if (value == null)
178: throw new IllegalArgumentException("value is NULL");
179: if (isRender) {
180: store.put(SportletProperties.RENDER_PARAM_PREFIX + name,
181: value);
182: } else {
183: store.put(name, value);
184: }
185: }
186:
187: /**
188: * Sets the given String array parameter to this URL.
189: * <p/>
190: * This method replaces all parameters with the given key.
191: * <p/>
192: * The <code>PortletURL</code> implementation 'x-www-form-urlencoded' encodes
193: * all parameter names and values. Developers should not encode them.
194: * <p/>
195: * A portlet container may prefix the attribute names internally
196: * in order to preserve a unique namespace for the portlet.
197: *
198: * @param name the parameter name
199: * @param values the parameter values
200: * @throws IllegalArgumentException if name or values are <code>null</code>.
201: */
202: public void setParameter(String name, String[] values) {
203: if ((name == null) || !(name instanceof String))
204: throw new IllegalArgumentException(
205: "name must be a non-null string");
206: if (values == null)
207: throw new IllegalArgumentException("values is NULL");
208: if (values.length == 0)
209: throw new IllegalArgumentException("values is NULL");
210:
211: if (isRender) {
212: store.put(SportletProperties.RENDER_PARAM_PREFIX + name,
213: values);
214: } else {
215: store.put(name, values);
216: }
217: }
218:
219: /**
220: * Sets a parameter map for this URL.
221: * <p/>
222: * All previously set parameters are cleared.
223: * <p/>
224: * The <code>PortletURL</code> implementation 'x-www-form-urlencoded' encodes
225: * all parameter names and values. Developers should not encode them.
226: * <p/>
227: * A portlet container may prefix the attribute names internally,
228: * in order to preserve a unique namespace for the portlet.
229: *
230: * @param parameters Map containing parameter names for
231: * the render phase as
232: * keys and parameter values as map
233: * values. The keys in the parameter
234: * map must be of type String. The values
235: * in the parameter map must be of type
236: * String array (<code>String[]</code>).
237: * @throws IllegalArgumentException if parameters is <code>null</code>, if
238: * any of the key/values in the Map are <code>null</code>,
239: * if any of the keys is not a String, or if any of
240: * the values is not a String array.
241: */
242: public void setParameters(java.util.Map parameters) {
243: if (parameters == null) {
244: throw new IllegalArgumentException("parameters is NULL");
245: }
246: // All previously set parameters are cleared.
247: this .store = new HashMap<String, Object>();
248: for (Object key : parameters.keySet()) {
249: if (key == null)
250: throw new IllegalArgumentException(
251: "a parameters key is NULL");
252: if (key instanceof String) {
253: Object values = parameters.get(key);
254: if (values == null)
255: throw new IllegalArgumentException(
256: "a parameters value is NULL");
257: if (!(values instanceof String[])) {
258: throw new IllegalArgumentException(
259: "a parameters value element must be a string array");
260: }
261: this .setParameter((String) key, (String[]) values);
262: } else {
263: throw new IllegalArgumentException(
264: "parameter key must be a string");
265: }
266:
267: }
268: }
269:
270: public void setAction(String action) {
271: store.put(SportletProperties.DEFAULT_PORTLET_ACTION, action);
272: }
273:
274: public void setRender(String render) {
275: store.put(SportletProperties.DEFAULT_PORTLET_RENDER, render);
276: }
277:
278: /**
279: * Sets a label for this link, which will overwrite the component id
280: *
281: * @param label the link label
282: */
283: public void setLabel(String label) {
284: this .label = label;
285: }
286:
287: /**
288: * Sets the layout id that identifies a layout descriptor to target
289: *
290: * @param layout the layout id that identifies a layout descriptor to target
291: */
292: public void setLayout(String layout) {
293: this .layout = layout;
294: }
295:
296: /**
297: * Indicated the security setting for this URL.
298: * <p/>
299: * Secure set to <code>true</code> indicates that the portlet requests
300: * a secure connection between the client and the portlet window for
301: * this URL. Secure set to <code>false</code> indicates that the portlet
302: * does not need a secure connection for this URL. If the security is not
303: * set for a URL, it will stay the same as the current request.
304: *
305: * @param secure true, if portlet requests to have a secure connection
306: * between its portlet window and the client; false, if
307: * the portlet does not require a secure connection.
308: * @throws PortletSecurityException if the run-time environment does
309: * not support the indicated setting
310: */
311: public void setSecure(boolean secure)
312: throws PortletSecurityException {
313: this .isSecure = secure;
314: }
315:
316: public void setEncoding(boolean encoding) {
317: this .encoding = encoding;
318: }
319:
320: public boolean isEncoding() {
321: return encoding;
322: }
323:
324: /**
325: * Returns the portlet URL string representation to be embedded in the
326: * markup.<br>
327: * Note that the returned String may not be a valid URL, as it may
328: * be rewritten by the portal/portlet-container before returning the
329: * markup to the client.
330: *
331: * @return the encoded URL as a string
332: */
333: public String toString() {
334: StringBuffer s = new StringBuffer();
335: String port = null;
336: if (req.isSecure()
337: || isSecure
338: || (req.getAttribute(SportletProperties.SSL_REQUIRED) != null)) {
339: s.append("https://");
340: port = configService
341: .getProperty(PortalConfigService.PORTAL_SECURE_PORT);
342: } else {
343: s.append("http://");
344: port = configService
345: .getProperty(PortalConfigService.PORTAL_PORT);
346: }
347: String hostname = configService
348: .getProperty(PortalConfigService.PORTAL_HOST);
349:
350: if (hostname == null || hostname.equals(""))
351: hostname = req.getServerName();
352: s.append(hostname);
353: if (!port.equals("80") && (!port.equals("443"))) {
354: s.append(":");
355: s.append((!port.equals("")) ? port : String.valueOf(req
356: .getServerPort()));
357: }
358:
359: // if underlying window state is floating then set it in the URI
360: if (req.getAttribute(SportletProperties.FLOAT_STATE) != null) {
361: store.put(SportletProperties.PORTLET_WINDOW, "FLOATING");
362: }
363: String contextPath = "/"
364: + configService.getProperty("gridsphere.deploy");
365: // handle ROOT context
366: if (contextPath.equals("/"))
367: contextPath = "";
368: String servletPath = "/"
369: + configService.getProperty("gridsphere.context");
370: //String servletPath = req.getServletPath();
371: String url = contextPath + servletPath;
372:
373: String cid = (String) req
374: .getAttribute(SportletProperties.COMPONENT_ID);
375: /*
376: This bit of jiggery is here only for the LayoutManager portlet currently.
377: A special param SportletProperties.EXTRA_QUERY_INFO can be used to stuff
378: some extra params into every portal generated url
379: */
380: String extraQuery = (String) req
381: .getAttribute(SportletProperties.EXTRA_QUERY_INFO);
382: if (extraQuery != null) {
383: StringTokenizer st = new StringTokenizer(extraQuery, "&");
384: while (st.hasMoreTokens()) {
385: String cmd = (String) st.nextElement();
386: //System.err.println("cmd= " + cmd);
387:
388: if (cmd.startsWith(SportletProperties.COMPONENT_ID)) {
389: store.put(SportletProperties.COMPONENT_ID_2, cid);
390: cid = cmd.substring(SportletProperties.COMPONENT_ID
391: .length() + 1);
392: } else if (cmd
393: .startsWith(SportletProperties.DEFAULT_PORTLET_ACTION)) {
394: String action = (String) store
395: .get(SportletProperties.DEFAULT_PORTLET_ACTION);
396: store
397: .put(
398: SportletProperties.DEFAULT_PORTLET_ACTION_2,
399: action);
400: store
401: .put(
402: SportletProperties.DEFAULT_PORTLET_ACTION,
403: cmd
404: .substring(SportletProperties.DEFAULT_PORTLET_ACTION
405: .length() + 1));
406:
407: }
408: }
409: }
410:
411: String layoutId = layout;
412: if (layoutId == null) {
413: layoutId = (String) req
414: .getAttribute(SportletProperties.LAYOUT_PAGE);
415: }
416: if (layoutId != null) {
417: //System.err.println("layoutId=" + layoutId);
418: url += "/" + layoutId;
419: //String compVar = (String)req.getAttribute(SportletProperties.COMPONENT_ID_VAR);
420: //if (compVar == null) compVar = SportletProperties.COMPONENT_ID;
421:
422: /*
423: // if a label exists, use it instead
424: if (label != null) {
425: cid = label;
426: } else{
427: cid = (String)req.getAttribute(SportletProperties.COMPONENT_ID);
428: }
429: */
430: // if a label exists, use it instead
431: if (label != null)
432: cid = label;
433:
434: if (cid != null) {
435: url += "/" + cid;
436: if (mode != null)
437: url += "/m/" + mode.toString();
438: if (state != null)
439: url += "/s/" + state.toString();
440: String action = (String) store
441: .get(SportletProperties.DEFAULT_PORTLET_ACTION);
442: if (action != null) {
443: store
444: .remove(SportletProperties.DEFAULT_PORTLET_ACTION);
445: url += "/a/" + action;
446: }
447: String render = (String) store
448: .get(SportletProperties.DEFAULT_PORTLET_RENDER);
449: if (render != null) {
450: store
451: .remove(SportletProperties.DEFAULT_PORTLET_RENDER);
452: url += "/r/" + render;
453: }
454: }
455: //System.err.println("url=" + layoutId);
456: }
457: ///////////// JASON ADDED ABOVE
458: Set set = store.keySet();
459: if (!set.isEmpty()) {
460: // add question mark
461: url += "?";
462:
463: Iterator it = set.iterator();
464: boolean firstParam = true;
465: try {
466: while (it.hasNext()) {
467: if (!firstParam)
468: url += "&";
469: String name = (String) it.next();
470:
471: String encname = null;
472: encname = URLEncoder.encode(name, "UTF-8");
473:
474: Object val = store.get(name);
475: if (val instanceof String[]) {
476: String[] vals = (String[]) val;
477: for (int j = 0; j < vals.length - 1; j++) {
478: String encvalue = URLEncoder.encode(
479: vals[j], "UTF-8");
480: url += encname + "=" + encvalue + "&";
481: }
482: String encvalue = URLEncoder.encode(
483: vals[vals.length - 1], "UTF-8");
484: url += encname + "=" + encvalue;
485: } else if (val instanceof String) {
486: String aval = (String) store.get(name);
487: if ((aval != null) && (!aval.equals(""))) {
488: String encvalue = URLEncoder.encode(aval,
489: "UTF-8");
490: url += encname + "=" + encvalue;
491: } else {
492: url += encname;
493: }
494: }
495: firstParam = false;
496: }
497:
498: } catch (UnsupportedEncodingException e) {
499: System.err.println("Unable to support UTF-8 encoding!");
500: }
501:
502: if (encoding) {
503: if (redirect) {
504: url = res.encodeRedirectURL(url);
505: } else {
506: url = res.encodeURL(url);
507: }
508: }
509: }
510: s.append(url);
511: //System.err.println("created URL= " + s.toString());
512: return s.toString();
513: }
514:
515: }
|