001: /*
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution, if
019: * any, must include the following acknowlegement:
020: * "This product includes software developed by the
021: * Caucho Technology (http://www.caucho.com/)."
022: * Alternately, this acknowlegement may appear in the software itself,
023: * if and wherever such third-party acknowlegements normally appear.
024: *
025: * 4. The names "Hessian", "Resin", and "Caucho" must not be used to
026: * endorse or promote products derived from this software without prior
027: * written permission. For written permission, please contact
028: * info@caucho.com.
029: *
030: * 5. Products derived from this software may not be called "Resin"
031: * nor may "Resin" appear in their names without prior written
032: * permission of Caucho Technology.
033: *
034: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
035: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
036: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
037: * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
038: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
039: * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
040: * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
041: * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
042: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
043: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
044: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
045: *
046: * @author Sam
047: */
048:
049: package com.caucho.portal.generic;
050:
051: import javax.portlet.PortletMode;
052: import javax.portlet.WindowState;
053: import java.util.HashSet;
054: import java.util.Iterator;
055: import java.util.LinkedHashMap;
056: import java.util.Map;
057: import java.util.Set;
058: import java.util.logging.Level;
059: import java.util.logging.Logger;
060:
061: /**
062: */
063: public class MapBasedInvocationFactory implements InvocationFactory,
064: Cloneable {
065: static protected final Logger log = Logger
066: .getLogger(MapBasedInvocationFactory.class.getName());
067:
068: private String _reservedNamespace = "__";
069: private String _actionTargetParameterName = "A";
070: private String _windowStateParameterName = "W";
071: private String _portletModeParameterName = "M";
072:
073: private LinkedHashMap<String, MapBasedInvocation> _invocationMap = new LinkedHashMap<String, MapBasedInvocation>();
074:
075: private String _actionNamespace;
076:
077: private Set<WindowState> _windowStatesUsed;
078: private Set<PortletMode> _portletModesUsed;
079:
080: // this class is not required to be thread-safe
081: private StringBuffer _buffer = new StringBuffer(256);
082:
083: /**
084: * The reserved namespace is used to mark parameters that have special
085: * meaning to the portal. The specification suggests "javax.portal.", which
086: * is rather long so the default is "__".
087: *
088: * This implementation also uses the reserved namespace as a prefix to
089: * parameter names.
090: */
091: public void setReservedNamespace(String namespace) {
092: _reservedNamespace = namespace;
093: }
094:
095: /**
096: * The reserved namespace is used to mark parameters that have special
097: * meaning to the portal.
098: */
099: public String getReservedNamespace() {
100: return _reservedNamespace;
101: }
102:
103: /**
104: * The name of the parameter to use to store the namespace of
105: * the target of an action.
106: */
107: public void setActionTargetParameterName(String name) {
108: _actionTargetParameterName = name;
109: }
110:
111: /**
112: * The name of the parameter to use to store the namespace of
113: * the target of an action.
114: */
115: public String getActionTargetParameterName() {
116: return _actionTargetParameterName;
117: }
118:
119: /**
120: * The name of the parameter to use to store the window state.
121: */
122: public void setWindowStateParameterName(String name) {
123: _windowStateParameterName = name;
124: }
125:
126: /**
127: * The name of the parameter to use to store the window state.
128: */
129: public String getWindowStateParameterName() {
130: return _windowStateParameterName;
131: }
132:
133: /**
134: * The name of the parameter to use to store the portlet mode.
135: */
136: public void setPortletModeParameterName(String name) {
137: _portletModeParameterName = name;
138: }
139:
140: /**
141: * The name of the parameter to use to store the portlet mode.
142: */
143: public String getPortletModeParameterName() {
144: return _portletModeParameterName;
145: }
146:
147: public void start(Map<String, String[]> rawParameters) {
148: if (rawParameters != null)
149: decodeRawParameters(rawParameters);
150: }
151:
152: public void finish() {
153: _windowStatesUsed = null;
154: _portletModesUsed = null;
155:
156: _actionNamespace = null;
157: _invocationMap.clear();
158: }
159:
160: public boolean isActionTarget(String namespace) {
161: return namespace == _actionNamespace;
162: }
163:
164: /**
165: * The actionNamespace and actionMap are null in the returned clone
166: * The invocation that matches the passed namespace will not have any
167: * parameters.
168: */
169: protected MapBasedInvocationFactory clone(String namespace) {
170: try {
171: MapBasedInvocationFactory clone = (MapBasedInvocationFactory) super
172: .clone();
173:
174: clone._windowStatesUsed = null;
175: clone._portletModesUsed = null;
176: clone._actionNamespace = null;
177:
178: clone._invocationMap = new LinkedHashMap<String, MapBasedInvocation>();
179:
180: Iterator<Map.Entry<String, MapBasedInvocation>> iter = _invocationMap
181: .entrySet().iterator();
182:
183: while (iter.hasNext()) {
184: Map.Entry<String, MapBasedInvocation> entry = iter
185: .next();
186: String invocationNamespace = entry.getKey();
187: MapBasedInvocation invocation = entry.getValue();
188:
189: boolean keepParameters = !namespace
190: .equals(invocationNamespace);
191:
192: clone._invocationMap.put(invocationNamespace,
193: invocation.clone(keepParameters));
194: }
195:
196: return clone;
197: } catch (CloneNotSupportedException ex) {
198: throw new RuntimeException(ex);
199: }
200: }
201:
202: protected void decodeRawParameters(Map<String, String[]> raw) {
203: _buffer.setLength(0);
204: StringBuffer buf = _buffer;
205:
206: buf.append(_reservedNamespace);
207: int len = buf.length();
208:
209: buf.append(_reservedNamespace);
210: buf.append(_actionTargetParameterName);
211: String actionTargetParameterName = buf.toString();
212:
213: buf.setLength(len);
214: buf.append(_windowStateParameterName);
215: String windowStateParameterName = buf.toString();
216:
217: buf.setLength(len);
218: buf.append(_portletModeParameterName);
219: String portletModeParameterName = buf.toString();
220:
221: MapBasedInvocation invocation = null;
222:
223: // first, determine the target of the action, if any.
224: String[] action = raw.get(actionTargetParameterName);
225: _actionNamespace = action == null || action.length == 0 ? null
226: : action[0];
227:
228: if (_actionNamespace != null)
229: getInvocation(_actionNamespace);
230:
231: // iterate the parameters.
232: // parameters that begin with the _reservedNamespace are render
233: // parameters.
234: // parameters that do not begin with the _reservedNamespace are action
235: // parameters, and belong to the Invocation for _actionNamespace.
236:
237: Iterator<Map.Entry<String, String[]>> iter = raw.entrySet()
238: .iterator();
239:
240: while (iter.hasNext()) {
241: Map.Entry<String, String[]> entry = iter.next();
242: String key = entry.getKey();
243: String[] values = entry.getValue();
244:
245: String namespace = null;
246:
247: if (key.startsWith(_reservedNamespace)) {
248:
249: int keyLen = key.length();
250:
251: int st = _reservedNamespace.length();
252: int nd = key.indexOf('.', st);
253: if (nd > -1) {
254: namespace = key.substring(st, nd);
255: nd++;
256: } else {
257: if (log.isLoggable(Level.FINE)
258: && !key.equals(actionTargetParameterName))
259: log.fine("unusable raw parameter name `" + key
260: + "'");
261: continue;
262: }
263:
264: if (nd < keyLen)
265: key = key.substring(nd);
266: else {
267: if (log.isLoggable(Level.FINE))
268: log.fine("unusable . raw parameter name `"
269: + key + "'");
270: continue;
271: }
272: } else {
273: if (_actionNamespace == null) {
274: log.finer("unusable raw action parameter name `"
275: + key + "'");
276: continue;
277: } else
278: namespace = _actionNamespace;
279: }
280:
281: if (invocation == null
282: || !invocation.getNamespace().equals(namespace))
283: invocation = getInvocation(namespace);
284:
285: if (windowStateParameterName.equals(key)) {
286: invocation.setWindowStateName(values);
287: } else if (portletModeParameterName.equals(key)) {
288: invocation.setPortletModeName(values);
289: } else {
290: invocation.getParameterMap().put(key, values);
291: }
292: }
293:
294: }
295:
296: public String getURL() {
297: _buffer.setLength(0);
298: StringBuffer buf = _buffer;
299:
300: StringBuffer url = _buffer;
301:
302: if (_actionNamespace != null)
303: appendReserved(url, null, _actionTargetParameterName,
304: _actionNamespace);
305:
306: Iterator<Map.Entry<String, MapBasedInvocation>> iter = _invocationMap
307: .entrySet().iterator();
308:
309: // "view" and "normal" are defaults. They only to to be included
310: // in the url if:
311: // 1) some namespace uses view or normal and has no parameters
312: // 2) some other namespace uses a different portlet mode/window state
313: //
314: // They only need to be included once, the purpose for including them is so
315: // that on a subsequent request the windowStatesUsed and portletModesUsed
316: // are correct.
317:
318: String viewPortletModeNamespace = null;
319: String normalWindowStateNamespace = null;
320: boolean needViewPortletMode = false;
321: boolean needNormalWindowState = false;
322:
323: boolean sawActionNamespace = false;
324:
325: while (iter.hasNext()) {
326: Map.Entry<String, MapBasedInvocation> entry = iter.next();
327: String namespace = entry.getKey();
328: MapBasedInvocation invocation = entry.getValue();
329:
330: // when the namespace is the target of an action, the parameter
331: // names are not encoded
332: String paramNamespace = namespace == _actionNamespace ? null
333: : namespace;
334:
335: PortletMode portletMode = invocation.getPortletMode();
336: WindowState windowState = invocation.getWindowState();
337: Map<String, String[]> parameterMap = invocation._parameterMap;
338:
339: boolean hasParameters = parameterMap != null
340: && !parameterMap.isEmpty();
341:
342: if (portletMode == PortletMode.VIEW) {
343: if (viewPortletModeNamespace == null && !hasParameters)
344: viewPortletModeNamespace = namespace;
345: } else {
346: needViewPortletMode = true;
347:
348: String key = _portletModeParameterName;
349: String value = portletMode.toString();
350:
351: appendReserved(url, namespace, key, value);
352: }
353:
354: if (windowState == WindowState.NORMAL) {
355: if (normalWindowStateNamespace == null
356: && !hasParameters)
357: normalWindowStateNamespace = namespace;
358: } else {
359: needNormalWindowState = true;
360:
361: String key = _windowStateParameterName;
362: String value = windowState.toString();
363:
364: appendReserved(url, namespace, key, value);
365: }
366:
367: if (parameterMap != null && !parameterMap.isEmpty()) {
368: Iterator<Map.Entry<String, String[]>> paramIter = parameterMap
369: .entrySet().iterator();
370:
371: while (paramIter.hasNext()) {
372: Map.Entry<String, String[]> paramEntry = paramIter
373: .next();
374:
375: String paramKey = paramEntry.getKey();
376: String[] paramValues = paramEntry.getValue();
377:
378: if (paramValues == null || paramValues.length == 0)
379: continue;
380:
381: appendParameter(url, paramNamespace, paramKey,
382: paramValues);
383: }
384: }
385:
386: } // iterate invocationMap
387:
388: if (needViewPortletMode && viewPortletModeNamespace != null) {
389: String key = _portletModeParameterName;
390:
391: if (viewPortletModeNamespace == _actionNamespace)
392: viewPortletModeNamespace = null;
393:
394: appendReserved(url, viewPortletModeNamespace, key, "view");
395: }
396:
397: if (needNormalWindowState && normalWindowStateNamespace != null) {
398: String key = _windowStateParameterName;
399:
400: if (normalWindowStateNamespace == _actionNamespace)
401: normalWindowStateNamespace = null;
402:
403: appendReserved(url, normalWindowStateNamespace, key,
404: "normal");
405: }
406:
407: return url.toString();
408: }
409:
410: private void appendReserved(StringBuffer url, String namespace,
411: String key, String value) {
412: url.append(url.length() == 0 ? '?' : '&');
413:
414: url.append(_reservedNamespace);
415:
416: if (namespace != null) {
417: url.append(namespace);
418: url.append('.');
419: }
420:
421: url.append(_reservedNamespace);
422: url.append(key);
423: url.append('=');
424: HttpUtil.encode(value, url);
425: }
426:
427: private void appendParameter(StringBuffer url, String namespace,
428: String key, String values[]) {
429: for (int i = 0; i < values.length; i++) {
430: url.append(url.length() == 0 ? '?' : '&');
431:
432: if (namespace != null) {
433: url.append(_reservedNamespace);
434: url.append(namespace);
435: url.append('.');
436: }
437:
438: HttpUtil.encode(key, url);
439: url.append('=');
440: HttpUtil.encode(values[i], url);
441: }
442: }
443:
444: /**
445: * @return a Set of all WindowState's used.
446: */
447: public Set<WindowState> getWindowStatesUsed() {
448: if (_windowStatesUsed == null) {
449: _windowStatesUsed = new HashSet<WindowState>();
450:
451: if (_invocationMap != null) {
452: Iterator<Map.Entry<String, MapBasedInvocation>> iter = _invocationMap
453: .entrySet().iterator();
454:
455: while (iter.hasNext()) {
456: Map.Entry<String, MapBasedInvocation> entry = iter
457: .next();
458: _windowStatesUsed.add(entry.getValue()
459: .getWindowState());
460: }
461: }
462: }
463:
464: if (_windowStatesUsed.isEmpty())
465: _windowStatesUsed.add(WindowState.NORMAL);
466:
467: return _windowStatesUsed;
468: }
469:
470: private void addWindowStateUsed(WindowState windowState) {
471: if (_windowStatesUsed != null)
472: _windowStatesUsed.add(windowState);
473: }
474:
475: /**
476: * @return a Set of all PortletMode's used.
477: */
478: public Set<PortletMode> getPortletModesUsed() {
479: if (_portletModesUsed == null) {
480: _portletModesUsed = new HashSet<PortletMode>();
481:
482: if (_invocationMap != null) {
483: Iterator<Map.Entry<String, MapBasedInvocation>> iter = _invocationMap
484: .entrySet().iterator();
485:
486: while (iter.hasNext()) {
487: Map.Entry<String, MapBasedInvocation> entry = iter
488: .next();
489: _portletModesUsed.add(entry.getValue()
490: .getPortletMode());
491: }
492: }
493: }
494:
495: if (_portletModesUsed.isEmpty())
496: _portletModesUsed.add(PortletMode.VIEW);
497:
498: return _portletModesUsed;
499: }
500:
501: private void addPortletModeUsed(PortletMode portletMode) {
502: if (_portletModesUsed != null)
503: _portletModesUsed.add(portletMode);
504: }
505:
506: /**
507: * Return a Invocation appropriate for the passed namespace.
508: */
509: public MapBasedInvocation getInvocation(String namespace) {
510: if (namespace == null)
511: throw new NullPointerException("namespace cannot be null");
512:
513: MapBasedInvocation invocation = _invocationMap.get(namespace);
514:
515: if (invocation == null) {
516: invocation = new MapBasedInvocation(this );
517:
518: invocation.start(namespace);
519: _invocationMap.put(namespace, invocation);
520: }
521:
522: return invocation;
523: }
524:
525: protected InvocationURL createActionURL(String namespace) {
526: MapBasedInvocationFactory clone = clone(namespace);
527: clone._actionNamespace = namespace;
528: return new MapBasedInvocationURL(clone, namespace);
529: }
530:
531: protected InvocationURL createRenderURL(String namespace) {
532: MapBasedInvocationFactory clone = clone(namespace);
533: clone._actionNamespace = null;
534: return new MapBasedInvocationURL(clone, namespace);
535: }
536:
537: public String toString() {
538: return "[MapBasedInvocationFactory actionNamespace="
539: + _actionNamespace + " invocationMap=" + _invocationMap
540: + "]";
541: }
542:
543: private static class MapBasedInvocation implements Invocation,
544: Cloneable {
545: private MapBasedInvocationFactory _factory;
546:
547: private String _namespace;
548: private WindowState _windowState = WindowState.NORMAL;
549: private PortletMode _portletMode = PortletMode.VIEW;
550: private Map<String, String[]> _parameterMap;
551:
552: public MapBasedInvocation(MapBasedInvocationFactory factory) {
553: _factory = factory;
554: }
555:
556: public void start(String namespace) {
557: if (_namespace != null)
558: throw new IllegalStateException("missing finish()?");
559:
560: _namespace = namespace;
561: }
562:
563: public void finish() {
564: _windowState = WindowState.NORMAL;
565: _portletMode = PortletMode.VIEW;
566: if (_parameterMap != null)
567: _parameterMap.clear();
568: _namespace = null;
569: }
570:
571: public MapBasedInvocation clone(boolean keepParameters) {
572: try {
573: MapBasedInvocation clone = (MapBasedInvocation) super
574: .clone();
575:
576: if (keepParameters && _parameterMap != null)
577: clone._parameterMap = new LinkedHashMap<String, String[]>(
578: _parameterMap);
579: else
580: clone._parameterMap = null;
581:
582: return clone;
583: } catch (CloneNotSupportedException ex) {
584: throw new RuntimeException(ex);
585: }
586: }
587:
588: public String getNamespace() {
589: return _namespace;
590: }
591:
592: public boolean isActionTarget() {
593: return _factory.isActionTarget(_namespace);
594: }
595:
596: public Map<String, String[]> getParameterMap() {
597: if (_parameterMap == null)
598: _parameterMap = new LinkedHashMap<String, String[]>();
599:
600: return _parameterMap;
601: }
602:
603: public Map<String, String[]> releaseParameterMap() {
604: Map<String, String[]> map = getParameterMap();
605: _parameterMap = null;
606: return map;
607: }
608:
609: public WindowState getWindowState() {
610: return _windowState;
611: }
612:
613: public void setWindowState(WindowState windowState) {
614: _windowState = windowState == null ? WindowState.NORMAL
615: : windowState;
616: _factory.addWindowStateUsed(_windowState);
617: }
618:
619: void setWindowStateName(String[] values) {
620: String windowStateName = values == null
621: || values.length == 0 ? null : values[0];
622:
623: if (windowStateName == null)
624: setWindowState(WindowState.NORMAL);
625: else if (windowStateName.equals("normal"))
626: setWindowState(WindowState.NORMAL);
627: else if (windowStateName.equals("minimized"))
628: setWindowState(WindowState.MINIMIZED);
629: else if (windowStateName.equals("maximized"))
630: setWindowState(WindowState.MAXIMIZED);
631: else
632: setWindowState(new WindowState(windowStateName));
633: }
634:
635: public PortletMode getPortletMode() {
636: return _portletMode;
637: }
638:
639: public void setPortletMode(PortletMode portletMode) {
640: _portletMode = portletMode == null ? PortletMode.VIEW
641: : portletMode;
642: _factory.addPortletModeUsed(_portletMode);
643: }
644:
645: void setPortletModeName(String[] values) {
646: String portletModeName = values == null
647: || values.length == 0 ? null : values[0];
648:
649: if (portletModeName == null)
650: setPortletMode(PortletMode.VIEW);
651: else if (portletModeName.equals("view"))
652: setPortletMode(PortletMode.VIEW);
653: else if (portletModeName.equals("edit"))
654: setPortletMode(PortletMode.EDIT);
655: else if (portletModeName.equals("help"))
656: setPortletMode(PortletMode.HELP);
657: else
658: setPortletMode(new PortletMode(portletModeName));
659: }
660:
661: public InvocationURL createActionURL() {
662: return _factory.createActionURL(_namespace);
663: }
664:
665: public InvocationURL createRenderURL() {
666: return _factory.createRenderURL(_namespace);
667: }
668:
669: public String toString() {
670: return "[MapBasedInvocationFactory " + " namespace="
671: + _namespace + " windowState=" + _windowState
672: + " portletMode=" + _portletMode + " parameters="
673: + _parameterMap + "]";
674:
675: }
676: }
677:
678: static class MapBasedInvocationURL extends InvocationURL {
679: MapBasedInvocationFactory _factory;
680:
681: MapBasedInvocationURL(MapBasedInvocationFactory factory,
682: String namespace) {
683: super (factory, namespace);
684: _factory = factory;
685: }
686:
687: public String getURL() {
688: return _factory.getURL();
689: }
690:
691: }
692: }
|