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.*;
052: import java.io.IOException;
053: import java.lang.reflect.Constructor;
054: import java.lang.reflect.Modifier;
055: import java.util.*;
056: import java.util.logging.Level;
057: import java.util.logging.Logger;
058:
059: abstract public class GenericWindow implements Window, PortletConfig {
060: static protected final Logger log = Logger
061: .getLogger(GenericWindow.class.getName());
062:
063: private String _namespace = "";
064: private String _portletName;
065: private Map<String, String> _initParamMap;
066: private int _expirationCache;
067: private boolean _isPrivate;
068: private Renderer _renderer;
069: private int _bufferSize;
070: private Set<Locale> _supportedLocales;
071: private GenericPortletPreferences _defaultPreferences;
072: private ResourceBundleFactory _resourceBundleFactory;
073: private String _errorPage;
074:
075: private PortletContext _portletContext;
076:
077: public GenericWindow() {
078: }
079:
080: /**
081: * The namespace is used to uniquely identify this usage of the portlet,
082: * the default is "" (the empty string).
083: *
084: * The namespace is important when using a portlet preferences store,
085: * and also has an effect on the encoding of parameters.
086: */
087: public void setNamespace(String namespace) {
088: _namespace = namespace;
089: }
090:
091: /**
092: * The default is the namespace.
093: */
094: public void setPortletName(String portletName) {
095: _portletName = portletName;
096: }
097:
098: /**
099: * Add an init-param for the portlet.
100: */
101: public void addInitParam(String name, String value) {
102: if (_initParamMap == null)
103: _initParamMap = new LinkedHashMap<String, String>();
104:
105: _initParamMap.put(name, value);
106: }
107:
108: public void addInitParam(NameValuePair nameValuePair) {
109: addInitParam(nameValuePair.getName(), nameValuePair.getValue());
110: }
111:
112: /**
113: * Set the default preferences.
114: */
115: public void setPortletPreferences(
116: GenericPortletPreferences defaultPreferences) {
117: _defaultPreferences = defaultPreferences;
118: }
119:
120: /**
121: * Enable caching of the response and set the expiration time in seconds. 0
122: * (the default) means do not cache, -1 means unlimited cach time, any other
123: * number is the number of seconds for which the response can be cached.
124: */
125: public void setExpirationCache(int expirationCache) {
126: _expirationCache = expirationCache;
127: }
128:
129: /**
130: * If true then the response is private, indicating that it contains
131: * information that should only be provided to the current client, default is
132: * false. Setting this to true has an effect on caching, if true then a
133: * cached value
134: * cannot be shared amongst different users and the effectiveness of caching
135: * is greatly reduced.
136: */
137: public void setPrivate(boolean isPrivate) {
138: _isPrivate = isPrivate;
139: }
140:
141: /**
142: * Add a supported locale, the default is to support all locales.
143: * This is an ordered list, those added first are more preferrable.
144: */
145: void addSupportedLocale(String locale) {
146: String language = "";
147: String country = "";
148: String variant = "";
149:
150: String[] split = locale.split("_", 3);
151: int len = split.length;
152:
153: if (len == 0) {
154: split = locale.split("-", 3);
155: len = split.length;
156: }
157:
158: if (len == 0)
159: throw new IllegalArgumentException(locale);
160:
161: language = split[0];
162: if (len > 0)
163: country = split[1];
164: if (len > 1)
165: country = split[2];
166:
167: if (_supportedLocales == null)
168: _supportedLocales = new LinkedHashSet<Locale>();
169:
170: _supportedLocales.add(new Locale(language, country, variant));
171: }
172:
173: /**
174: * Add supported locales with a comma separated list, the default is to
175: * support all locales. This is an ordered list, those added first are more
176: * preferrable.
177: */
178: void addSupportedLocales(String locales) {
179: String[] split = locales.split("\\s*,\\s*");
180: for (int i = 0; i < split.length; i++)
181: addSupportedLocale(split[i]);
182: }
183:
184: /**
185: * Set a resource bundle name, used to instantiate an instance of
186: * ResourceBundleFactory.
187: */
188: public void setResourceBundle(String name) {
189: ResourceBundleFactory resourceBundleFactory = new ResourceBundleFactory();
190:
191: resourceBundleFactory.setName(name);
192:
193: setResourceBundleFactory(resourceBundleFactory);
194: }
195:
196: public void setResourceBundleFactory(ResourceBundleFactory factory) {
197: if (_resourceBundleFactory != null)
198: throw new IllegalArgumentException(
199: "resource-bundle-factory already set");
200: }
201:
202: /**
203: * A Renderer wraps decorations around the portlet, see
204: * {@link AbstractRenderer}.
205: */
206: public void setRenderer(Renderer renderer) {
207: _renderer = renderer;
208: }
209:
210: /**
211: * An alternative to {@link #setRenderer(Renderer)}, specify the class
212: * name of an object to instantiate
213: */
214: public void setRendererClass(String className) {
215: setRenderer((Renderer) newInstance(Renderer.class, className));
216: }
217:
218: /**
219: * 0 disables buffering, -1 allows the portal to choose a
220: * buffer size, a positive number indicaets a minimum buffer size. Default is
221: * 0 unless `error-page' ({@link #setErrorPage(String)} has been used, then
222: * the default is -1.
223: */
224: public void setBufferSize(int bufferSize) {
225: _bufferSize = bufferSize;
226: }
227:
228: /**
229: * Specify a location to forward to if an exception or constraint failure
230: * occurs while rendering the portlet. The default behaviour is
231: * for an exception or constraint failure to propogate to the parent
232: * window, or if there is no parent then to the servlet container.
233: *
234: * If an exception occurs the following request attributes are set:
235: *
236: * <dl>
237: * <dt>com.caucho.portal.error.exception
238: * <dd>java.lang.Throwable
239: *
240: * <dt>com.caucho.portal.error.exception_type
241: * <dd>java.lang.Class
242: *
243: * <dt>com.caucho.portal.error.message
244: * <dd>java.lang.String
245: *
246: * <dt>javax.portlet.renderRequest
247: * <dd>javax.portlet.RenderRequest
248: *
249: * <dt>javax.portlet.renderResponse
250: * <dd>javax.portlet.RenderResponse
251: *
252: * <dt>javax.portlet.portletConfig
253: * <dd>javax.portlet.PortletConfig
254: *
255: * </dl>
256: *
257: * If a constraint failure occurs the following request attributes are set:
258: *
259: * <dl>
260: * <dt>com.caucho.portal.error.constraint
261: * <dd>com.caucho.portal.generic.Constraint
262: *
263: * <dt>com.caucho.portal.error.constraint_type
264: * <dd>java.lang.Class
265: *
266: * <dt>com.caucho.portal.error.status_code
267: * <dd>java.lang.Integer
268: *
269: * <dt>javax.portlet.renderRequest
270: * <dd>javax.portlet.RenderRequest
271: *
272: * <dt>javax.portlet.renderResponse
273: * <dd>javax.portlet.RenderResponse
274: *
275: * <dt>javax.portlet.portletConfig
276: * <dd>javax.portlet.PortletConfig
277: *
278: * </dl>
279: */
280: public void setErrorPage(String errorPage) {
281: _errorPage = errorPage;
282:
283: if (_bufferSize == 0)
284: _bufferSize = -1;
285: }
286:
287: public void init(PortletContext portletContext)
288: throws PortletException {
289: if (_portletContext != null)
290: throw new IllegalStateException(
291: "portlet-context already set!");
292:
293: _portletContext = portletContext;
294:
295: if (_portletName == null)
296: _portletName = _namespace == null ? "anonymous"
297: : _namespace;
298: }
299:
300: /**
301: * Instantiate a new instance of an object, performing checks
302: * for validity.
303: *
304: * The object that results from instantiating an instance of
305: * <code>className</code> must be an <code>instance of</code>
306: * <code>targetClass</code>.
307: *
308: * @param targetClass the class that the instantiated Object should be
309: * compatible with.
310: *
311: * @param className the String name of a class to use when instantiating the
312: * object.
313: *
314: * @return a new Object
315: */
316: protected Object newInstance(Class targetClass, String className)
317: throws IllegalArgumentException {
318: Class cl = null;
319:
320: ClassLoader loader = Thread.currentThread()
321: .getContextClassLoader();
322:
323: try {
324: cl = Class.forName(className, false, loader);
325: } catch (ClassNotFoundException e) {
326: }
327:
328: if (cl == null)
329: throw new IllegalArgumentException("`" + className
330: + "' is not a known class");
331:
332: if (!targetClass.isAssignableFrom(cl))
333: throw new IllegalArgumentException("'" + className
334: + "' must implement " + targetClass.getName());
335:
336: if (Modifier.isAbstract(cl.getModifiers()))
337: throw new IllegalArgumentException("'" + className
338: + "' must not be abstract.");
339:
340: if (!Modifier.isPublic(cl.getModifiers()))
341: throw new IllegalArgumentException("'" + className
342: + "' must be public.");
343:
344: Constructor[] constructors = cl.getDeclaredConstructors();
345:
346: Constructor zeroArg = null;
347: for (int i = 0; i < constructors.length; i++) {
348: if (constructors[i].getParameterTypes().length == 0) {
349: zeroArg = constructors[i];
350: break;
351: }
352: }
353:
354: if (zeroArg == null
355: || !Modifier.isPublic(zeroArg.getModifiers()))
356: throw new IllegalArgumentException("'" + className
357: + "' must have a public zero arg constructor");
358:
359: Object obj = null;
360:
361: try {
362: obj = cl.newInstance();
363: } catch (Exception ex) {
364: throw new IllegalArgumentException("error instantiating `"
365: + className + "': " + ex.toString(), ex);
366: }
367:
368: return obj;
369: }
370:
371: protected String getNamespace() {
372: return _namespace;
373: }
374:
375: /**
376: * {@inheritDoc}
377: */
378: public PortletContext getPortletContext() {
379: if (_portletContext == null)
380: throw new IllegalStateException("missing init()?");
381:
382: return _portletContext;
383: }
384:
385: /**
386: * {@inheritDoc}
387: */
388: public PortletConfig getPortletConfig() {
389: return this ;
390: }
391:
392: /**
393: * {@inheritDoc}
394: */
395: public String getPortletName() {
396: return _portletName;
397: }
398:
399: public String getInitParameter(String name) {
400: if (_initParamMap == null)
401: return null;
402: else
403: return _initParamMap.get(name);
404: }
405:
406: public Enumeration getInitParameterNames() {
407: if (_initParamMap == null)
408: return Collections.enumeration(Collections.EMPTY_LIST);
409: else
410: return Collections.enumeration(_initParamMap.keySet());
411: }
412:
413: /**
414: * {@inheritDoc}
415: */
416: public int getExpirationCache() {
417: return _expirationCache;
418: }
419:
420: /**
421: * {@inheritDoc}
422: */
423: public boolean isPrivate() {
424: return _isPrivate;
425: }
426:
427: /**
428: * {@inheritDoc}
429: *
430: * This implementation returns true.
431: */
432: public boolean isWindowStateAllowed(PortletRequest request,
433: WindowState windowState) {
434: // XXX: todo: support a window-states init-param like
435: // normal, minimized
436: return true;
437: }
438:
439: /**
440: * {@inheritDoc}
441: *
442: * This implementation returns true.
443: */
444: public boolean isPortletModeAllowed(PortletRequest request,
445: PortletMode portletMode) {
446: // todo: see getSupportedContentTypes()
447: return true;
448: }
449:
450: /**
451: * {@inheritDoc}
452: *
453: * This implementation returns null, which means that all content
454: * types are supported.
455: */
456: public Set<String> getSupportedContentTypes(PortletMode portletMode) {
457: // XXX: todo: support a content-type init-param like
458: // edit(text/html), view(text/html, application/pdf)
459: return null;
460: }
461:
462: /**
463: * {@inheritDoc}
464: *
465: * This implementation returns null, which means that all locales are
466: * supported.
467: */
468: public Set<Locale> getSupportedLocales() {
469: return _supportedLocales;
470: }
471:
472: /**
473: * {@inheritDoc}
474: *
475: * This implementation returns null.
476: */
477: public PortletPreferences getDefaultPreferences() {
478: return _defaultPreferences;
479: }
480:
481: /**
482: * {@inheritDoc}
483: */
484: public ArrayList<PreferencesValidator> getPreferencesValidators() {
485: if (_defaultPreferences != null)
486: return _defaultPreferences.getPreferencesValidators();
487: else
488: return null;
489: }
490:
491: /**
492: * {@inheritDoc}
493: *
494: * This implementation returns null.
495: */
496: public Map<String, String> getRoleRefMap() {
497: return null;
498: // todo
499: }
500:
501: /**
502: * {@inheritDoc}
503: *
504: * This implementation returns null.
505: */
506: public ArrayList<Constraint> getConstraints() {
507: return null;
508: // todo
509: }
510:
511: /**
512: * {@inheritDoc}
513: */
514: public Renderer getRenderer() {
515: return _renderer;
516: }
517:
518: /**
519: * {@inheritDoc}
520: *
521: * This implementation returns PortletMode.VIEW.
522: */
523: public PortletMode handlePortletModeFailure(PortletRequest request,
524: PortletMode notAllowed) {
525: return PortletMode.VIEW;
526: }
527:
528: /**
529: * {@inheritDoc}
530: *
531: * This implementation returns WindowState.NORMAL.
532: */
533: public WindowState handleWindowStateFailure(PortletRequest request,
534: WindowState notAllowed) {
535: return WindowState.NORMAL;
536: }
537:
538: /**
539: * {@inheritDoc}
540: *
541: * @see #setErrorPage(String)
542: */
543: public void handleConstraintFailure(RenderRequest request,
544: RenderResponse response, ConstraintFailureEvent event) {
545: if (_errorPage == null)
546: return;
547:
548: Object existingConstraint = request
549: .getAttribute("com.caucho.portal.error.constraint");
550:
551: Object existingConstraintType = request
552: .getAttribute("com.caucho.portal.error.constraint_type");
553:
554: Object existingStatusCode = request
555: .getAttribute("com.caucho.portal.error.status_code");
556:
557: Constraint constraint = event.getConstraint();
558: Class constraintType = constraint.getClass();
559: Integer statusCode = new Integer(event.getStatusCode());
560:
561: request.setAttribute("com.caucho.portal.error.constraint",
562: constraint);
563: request.setAttribute("com.caucho.portal.error.constraint_type",
564: constraintType);
565: request.setAttribute("com.caucho.portal.error.status_code",
566: statusCode);
567:
568: try {
569: if (handleErrorPage(request, response))
570: event.setHandled(false);
571: else
572: event.setHandled(true);
573: } finally {
574: if (existingConstraint == null) {
575: request
576: .removeAttribute("com.caucho.portal.error.constraint");
577: request
578: .removeAttribute("com.caucho.portal.error.constraint_type");
579: request
580: .removeAttribute("com.caucho.portal.error.status_code");
581: } else {
582: request.setAttribute(
583: "com.caucho.portal.error.constraint",
584: existingConstraint);
585:
586: request.setAttribute(
587: "com.caucho.portal.error.constraint_type",
588: existingConstraintType);
589:
590: request.setAttribute(
591: "com.caucho.portal.error.status_code",
592: existingStatusCode);
593: }
594: }
595: }
596:
597: /**
598: * {@inheritDoc}
599: *
600: * @see #setErrorPage(String)
601: */
602: public void handleException(RenderRequest request,
603: RenderResponse response, ExceptionEvent event) {
604: if (_errorPage == null)
605: return;
606:
607: Object existingException = request
608: .getAttribute("com.caucho.portal.error.exception");
609:
610: Object existingExceptionType = request
611: .getAttribute("com.caucho.portal.error.exception_type");
612:
613: Object existingMessage = request
614: .getAttribute("com.caucho.portal.error.message");
615:
616: Exception exception = event.getException();
617: Class exceptionType = exception.getClass();
618: String message = exception.getMessage();
619:
620: request.setAttribute("com.caucho.portal.error.exception",
621: exception);
622:
623: request.setAttribute("com.caucho.portal.error.exception_type",
624: exceptionType);
625:
626: request
627: .setAttribute("com.caucho.portal.error.message",
628: message);
629:
630: try {
631: if (handleErrorPage(request, response))
632: event.setHandled(false);
633: else
634: event.setHandled(true);
635: } finally {
636: if (existingException == null) {
637: request
638: .removeAttribute("com.caucho.portal.error.exception");
639: request
640: .removeAttribute("com.caucho.portal.error.exception_type");
641: request
642: .removeAttribute("com.caucho.portal.error.message");
643: } else {
644: request.setAttribute(
645: "com.caucho.portal.error.exception",
646: existingException);
647:
648: request.setAttribute(
649: "com.caucho.portal.error.exception_type",
650: existingExceptionType);
651:
652: request.setAttribute("com.caucho.portal.error.message",
653: existingMessage);
654: }
655: }
656: }
657:
658: private boolean handleErrorPage(RenderRequest request,
659: RenderResponse response) {
660: try {
661:
662: PortletRequestDispatcher dispatcher = getPortletContext()
663: .getRequestDispatcher(_errorPage);
664:
665: dispatcher.include(request, response);
666: } catch (Exception ex) {
667: log.log(Level.WARNING, ex.toString(), ex);
668: return false;
669: }
670:
671: return true;
672: }
673:
674: /**
675: * {@inheritDoc}
676: */
677: public ResourceBundle getResourceBundle(Locale locale) {
678: if (_resourceBundleFactory == null)
679: _resourceBundleFactory = new ResourceBundleFactory();
680:
681: return _resourceBundleFactory.getResourceBundle(locale);
682: }
683:
684: /**
685: * {@inheritDoc}
686: */
687: public int getBufferSize() {
688: return _bufferSize;
689: }
690:
691: abstract public void processAction(PortletConnection connection)
692: throws PortletException, IOException;
693:
694: abstract public void render(PortletConnection connection)
695: throws PortletException, IOException;
696:
697: public void destroy() {
698: }
699: }
|