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 com.caucho.portal.generic.context.ConnectionContext;
052:
053: import javax.portlet.PortletException;
054: import javax.portlet.PortletRequest;
055: import javax.portlet.PortletSecurityException;
056: import javax.portlet.PortletSession;
057: import java.io.BufferedReader;
058: import java.io.IOException;
059: import java.io.InputStream;
060: import java.io.OutputStream;
061: import java.io.PrintWriter;
062: import java.io.UnsupportedEncodingException;
063: import java.security.Principal;
064: import java.util.Enumeration;
065: import java.util.Locale;
066: import java.util.Set;
067: import java.util.logging.Level;
068: import java.util.logging.Logger;
069:
070: /**
071: * A PortletConnection is used to obtain {@link Action} and {@link Render}
072: * objects.
073: *
074: * For implementations that support only one portlet for each connection,
075: * the pattern of use is:
076: *
077: * <pre>
078: * Window window = ...;
079: * Portlet portlet = ...;
080: *
081: * Action action = connection.getAction(window);
082: *
083: * if (action != null) {
084: * try {
085: * if (action.isTarget())
086: * action.processAction(portlet);
087: * }
088: * finally {
089: * action.finish();
090: * }
091: * }
092: *
093: * Render render = connection.getRender(window);
094: *
095: * if (render != null) {
096: * try {
097: * render.render(portlet);
098: * }
099: * finally {
100: * render.finish();
101: * }
102: * }
103: *
104: * </pre>
105: *
106: * For implementations that support more than one portlet for each connection,
107: * each portlet is identified with a namespace. An optional Renderer is
108: * specified, it is used to obtain a Writer or OutputStream when the portlet
109: * requests it.
110: *
111: * <pre>
112: * Window window = ...;
113: * Portlet portlet = ...;
114: * String namespace = "...";
115: *
116: * Action action = connection.getAction(window, namespace);
117: *
118: * if (action != null) {
119: * try {
120: * if (action.isTarget())
121: * action.processAction(portlet);
122: * }
123: * finally {
124: * action.finish();
125: * }
126: * }
127: *
128: * Render render = connection.getRender(window, namespace);
129: *
130: * if (render != null) {
131: * try {
132: * render.render(portlet);
133: * }
134: * finally {
135: * render.finish();
136: * }
137: * }
138: *
139: * </pre>
140: *
141: * @see PortletConnection#getAction
142: * @see PortletConnection#getRender
143: * @see Action
144: * @see Render
145: *
146: */
147: abstract public class PortletConnection {
148: /**
149: * Name of the request attribute that stores the connection
150: */
151: public static final String PORTLET_CONNECTION = "com.caucho.portal.generic.PortletConnection";
152:
153: /**
154: * Return the connection that corresponds to the PortletRequest
155: */
156: public static PortletConnection getConnection(
157: PortletRequest portletRequest) {
158: return (PortletConnection) portletRequest
159: .getAttribute(PORTLET_CONNECTION);
160: }
161:
162: /**
163: * Return the Portal that corresponds to the PortletRequest, or null
164: * if there is not a current Portal or Connection.
165: */
166: public static Portal getPortal(PortletRequest portletRequest) {
167: PortletConnection connection = getConnection(portletRequest);
168:
169: if (connection == null)
170: return null;
171: else
172: return connection.getPortal();
173: }
174:
175: /**
176: * Return the Action that corresponds to the PortletRequest, or null
177: * if there is not a current Action or Connection.
178: */
179: public static Action getAction(PortletRequest portletRequest) {
180: PortletConnection connection = getConnection(portletRequest);
181:
182: if (connection == null)
183: return null;
184: else
185: return connection.getCurrentAction();
186: }
187:
188: /**
189: * Return the Render that corresponds to the PortletRequest, or null
190: * if there is not a current Render or Connection.
191: */
192: public static Render getRender(PortletRequest portletRequest) {
193: PortletConnection connection = getConnection(portletRequest);
194:
195: if (connection == null)
196: return null;
197: else
198: return connection.getCurrentRender();
199: }
200:
201: // --
202:
203: static final public Logger log = Logger
204: .getLogger(PortletConnection.class.getName());
205:
206: private static int _connectionCount = 10;
207:
208: private ConnectionContext _context;
209:
210: private String _connectionId;
211:
212: private Portal _portal;
213:
214: private boolean _connectionFailed;
215: private Exception _connectionFailedCause;
216:
217: protected PortletConnection() {
218: int id = _connectionCount++;
219: _connectionId = Integer.toString(id, Character.MAX_RADIX);
220: _context = new ConnectionContext(this );
221: }
222:
223: /**
224: * A unique identifier for this connection object, used for debugging
225: */
226: public String getId() {
227: return _connectionId;
228: }
229:
230: public void start(Portal portal, InvocationFactory invocationFactory) {
231: if (_portal != null)
232: throw new IllegalStateException("missing finish()?");
233:
234: _portal = portal;
235:
236: _context.start(invocationFactory);
237: }
238:
239: public void finish() {
240: _context.finish();
241: _portal = null;
242: _connectionFailedCause = null;
243: _connectionFailed = false;
244: }
245:
246: public Portal getPortal() {
247: return _portal;
248: }
249:
250: /**
251: * Used to indicate that the connection has failed. A connection fails if an
252: * unrecoverable error occurs.
253: */
254: public void setConnectionFailed() {
255: if (!_connectionFailed) {
256: PortletException ex = new PortletException(
257: "connection failed");
258: setConnectionFailed(ex);
259: }
260: }
261:
262: /**
263: * Used to indicate that the connection has failed. A connection fails if an
264: * unrecoverable error occurs.
265: */
266: public void setConnectionFailed(Exception ex) {
267: if (!_connectionFailed) {
268: _connectionFailed = true;
269: _connectionFailedCause = ex;
270:
271: log.log(Level.FINE, ex.toString(), ex);
272: }
273: }
274:
275: /**
276: * A connection fails if an unrecoverable error occurs.
277: */
278: public boolean isConnectionFailed() {
279: return _connectionFailed;
280: }
281:
282: /**
283: * Handle a constraint failure by sending some response to the client.
284: *
285: * @return false if the connection cannot handle the constraint failure.
286: */
287: abstract public boolean handleConstraintFailure(
288: Constraint constraint, int failureCode) throws IOException;
289:
290: /**
291: * Handle an exception by sending some response to the client.
292: *
293: * @return false if the connection cannot handle the constraint failure.
294: */
295: abstract public boolean handleException(Exception exception);
296:
297: /**
298: * Return true if the connection can guarantee integrity
299: * (preventing data tampering in the communication process).
300: */
301: abstract public boolean canGuaranteeIntegrity();
302:
303: /**
304: * Return true if the connection can guarantee confidentiality (preventing
305: * reading while in transit).
306: */
307: abstract public boolean canGuaranteeConfidentiality();
308:
309: /**
310: * Set an attribute for the current connection. Attributes are name/value
311: * pairs that are valid for the duration of one connection.
312: */
313: abstract public void setAttribute(String name, Object o);
314:
315: /**
316: * Get an attribute for the current connection. Attributes are name/value
317: * pairs that are valid for the duration of one connection.
318: */
319: abstract public Object getAttribute(String name);
320:
321: /**
322: * Remove an attribute for the current connection. Attributes are name/value
323: * pairs that are valid for the duration of one connection.
324: */
325: abstract public void removeAttribute(String name);
326:
327: /**
328: * Get a list of all attributes for the current connection. Attributes are
329: * name/value pairs that are valid for the duration of one connection.
330: *
331: * @return an Enumeration of String
332: */
333: abstract public Enumeration getAttributeNames();
334:
335: /**
336: * Return a {@link PortletSession} for the current client, or null if one is
337: * not available.
338: *
339: * A PortletSession once established will be consistently returned for a
340: * client on subsequent requests. Different clients will never have the same
341: * PortletSession.
342: *
343: * @param create, if true create a new session if one does not already exist
344: * for the client.
345: */
346: abstract public PortletSession getPortletSession(boolean create);
347:
348: /**
349: * Return the scheme portion of the url that was used to make the request.
350: *
351: * @see javax.portlet.PortletRequest#getScheme
352: */
353: abstract public String getScheme();
354:
355: /**
356: * Return the host name portion of the url that was used to make the request.
357: *
358: * @see javax.portlet.PortletRequest#getServerName
359: */
360: abstract public String getServerName();
361:
362: /**
363: * Return the port portion of the url that was used to make the request.
364: *
365: * @see javax.portlet.PortletRequest#getServerPort
366: */
367: abstract public int getServerPort();
368:
369: /**
370: * Return the path to the portal portion of the url that was used to make the
371: * request.
372: *
373: * @see javax.portlet.PortletRequest#getContextPath
374: */
375: abstract public String getContextPath();
376:
377: /**
378: * Return the authentication scheme used for the current request.
379: *
380: * @return PortletRequest.BASIC_AUTH, PortletRequest.CLIENT_CERT_AUTH,
381: * PortletRequest.DIGEST_AUTH, PortletRequest.FORM_AUTH, or a custom method.
382: *
383: * @see javax.portlet.PortletRequest#getAuthType
384: */
385: abstract public String getAuthType();
386:
387: /**
388: * Return true if the connection for the current request is secure, for
389: * example it uses HTTPS.
390: *
391: * @see javax.portlet.PortletRequest#isSecure
392: */
393: abstract public boolean isSecure();
394:
395: /**
396: * Return the session id that was supplied by the client for the current
397: * request.
398: *
399: * @see javax.portlet.PortletRequest#getRequestedSessionId
400: */
401: abstract public String getRequestedSessionId();
402:
403: /**
404: * Return true the session id that was supplied by the client for the current
405: * request is valid.
406: *
407: * @see javax.portlet.PortletRequest#isRequestedSessionIdValid
408: */
409: abstract public boolean isRequestedSessionIdValid();
410:
411: /**
412: * Return the identity of the remote user, null if the identity has not been
413: * established.
414: *
415: * @see javax.portlet.PortletRequest#getRemoteUser
416: */
417: abstract public String getRemoteUser();
418:
419: /**
420: * Return a {@link java.security.Principal} that contains the identity of
421: * the remote user, null if the identity has not been established.
422: *
423: * @see javax.portlet.PortletRequest#getUserPrincipal
424: */
425: abstract public Principal getUserPrincipal();
426:
427: /**
428: * Return true if the identity of remote user has been established and the
429: * user has been assigned the role.
430: *
431: * @see javax.portlet.PortletRequest#isUserInRole
432: */
433: abstract public boolean isUserInRole(String role);
434:
435: /**
436: * Return the value of the specified connection property as a String, null
437: * if the property was not provided by the request from the client.
438: *
439: * "properties" correspond to HTTP headers in the request for HTTP
440: * connections.
441: *
442: * @see javax.portlet.PortletRequest#getProperty
443: */
444: abstract public String getProperty(String propertyName);
445:
446: /**
447: * Return the values of the specified connection property as an array of
448: * Strings, null if the property was not provided by the request from the
449: * client.
450: *
451: * "properties" correspond to HTTP headers in the request for HTTP
452: * connections.
453: *
454: * @return an Enumeration of String
455: *
456: * @see javax.portlet.PortletRequest#getProperties
457: */
458: abstract public Enumeration getProperties(String propertyName);
459:
460: /**
461: * Return the names of available properties for the connection.
462: *
463: * "properties" correspond to HTTP headers in the request for HTTP
464: * connections.
465: *
466: * @return an Enumeration of String
467: *
468: * @see javax.portlet.PortletRequest#getPropertyNames
469: */
470: abstract public Enumeration getPropertyNames();
471:
472: /**
473: * Get the content types acceptable to the client. The returned Set
474: * is ordered, the most preferrable content types appear before the least
475: * preferred.
476: *
477: * A return of null or an empty Set indicates that the client content types
478: * cannot be determiend, and is treated as an indication that any locale is
479: * acceptable.
480: */
481: abstract public Set<String> getClientContentTypes();
482:
483: /**
484: * Get the locales acceptable to the client. The returned Set is ordered,
485: * the most preferrable locale appears before the least preferred. If the
486: * client supports all locales, then a Locale("","","") will be present in
487: * the returned Set.
488: *
489: * A return of null or an empty Set indicates that the client locales cannot
490: * be determiend, and is treated as an indication that any locale is
491: * acceptable.
492: */
493: abstract public Set<Locale> getClientLocales();
494:
495: /**
496: * Get the character encodings acceptable to the client. The returned Set is
497: * order, the most preferrable character encoding appears before the least
498: * preferred.
499: *
500: * A return of null or an empty Set indicates that the client character
501: * encodings cannot be determiend, and is treated as an indication that any
502: * locale is
503: * acceptable.
504: */
505: abstract public Set<String> getClientCharacterEncodings();
506:
507: /**
508: * Return the MIME type of the data supplied as the "body" of the request,
509: * null if not known.
510: *
511: * @see javax.portlet.ActionRequest#getContentType
512: */
513: abstract public String getSubmitContentType();
514:
515: /**
516: * Return the length of of the data supplied as the "body" of the request,
517: * -1 if not known.
518: *
519: * @see javax.portlet.ActionRequest#getContentLength
520: */
521: abstract public int getSubmitContentLength();
522:
523: /**
524: * Return the binary body of the current request.
525: *
526: * @throws IllegalStateException if getReader() has already been
527: * called for this connection.
528: *
529: * @throws IOException
530: *
531: * @see javax.portlet.ActionRequest#getPortletInputStream
532: */
533: abstract public InputStream getSubmitInputStream()
534: throws IOException, IllegalStateException;
535:
536: /**
537: * Override the character encoding used by the Reader obtained
538: * using {@link #getReader}. This method must be called prior to reading
539: * input using {@link #getReader} or {@link #getPortletInputStream}.
540: *
541: * @throws UnsupportedEncodingException
542: *
543: * @throws IllegalStateException if getReader() has already been called for
544: * this connection.
545: *
546: * @see javax.portlet.ActionRequest#setCharacterEncoding
547: */
548: abstract public void setSubmitCharacterEncoding(String enc)
549: throws UnsupportedEncodingException, IllegalStateException;
550:
551: /**
552: * Return the name of the character encoding that will be used by the Reader
553: * obtained using {@link #getReader}, null if none.
554: *
555: * @see javax.portlet.ActionRequest#getCharacterEncoding
556: */
557: abstract public String getSubmitCharacterEncoding();
558:
559: abstract public BufferedReader getSubmitReader()
560: throws UnsupportedEncodingException, IOException;
561:
562: /**
563: * Encode a url with any special encoding needed by the protocol,
564: * for example by adding a sesison id.
565: */
566: abstract public String encodeURL(String path);
567:
568: /**
569: * Resolve a url so that it makes a request to the portal
570: */
571: abstract public String resolveURL(String partialUrl);
572:
573: /**
574: * Resolve a url so that it makes a request to the portal with
575: * the specified level of security.
576: */
577: abstract public String resolveURL(String partialUrl,
578: boolean isSecure) throws PortletSecurityException;
579:
580: abstract public void sendRedirect(String location)
581: throws IllegalStateException, IOException;
582:
583: /**
584: * Set a property to be returned to the client.
585: *
586: * "properties" correspond to HTTP headers in the response for HTTP
587: * connections.
588: *
589: * @see javax.portlet.PortletResponse#setProperty
590: */
591: abstract public void setProperty(String name, String value);
592:
593: /**
594: * Add a value to a property to be returned to the client.
595: *
596: * "properties" correspond to HTTP headers in the response for HTTP
597: * connections.
598: *
599: * @see javax.portlet.PortletResponse#addProperty
600: */
601: abstract public void addProperty(String name, String value);
602:
603: /**
604: * Set the content type to use for the response.
605: */
606: abstract public void setContentType(String contentType);
607:
608: /**
609: * Return the content type established with setContentType(), or null if
610: * setContentType() has not been called.
611: */
612: abstract public String getContentType();
613:
614: /**
615: * Set the locale to use for the response.
616: */
617: abstract public void setLocale(Locale locale);
618:
619: /**
620: * Return the Locale established with setLocale(), or null if setLocale()
621: * has not been called.
622: */
623: abstract public Locale getLocale();
624:
625: abstract public void setBufferSize(int size);
626:
627: abstract public int getBufferSize();
628:
629: abstract public void flushBuffer() throws IOException;
630:
631: abstract public void resetBuffer();
632:
633: abstract public void reset();
634:
635: abstract public boolean isCommitted();
636:
637: /**
638: * @throws IllegalStatementException if the content type has not been set
639: * with setContentType.
640: */
641: abstract public OutputStream getOutputStream() throws IOException;
642:
643: abstract public String getCharacterEncoding();
644:
645: abstract public void setCharacterEncoding(String enc)
646: throws UnsupportedEncodingException;
647:
648: /**
649: * @throws IllegalStatementException if the content type has not been set
650: * with setContentType.
651: */
652: abstract public PrintWriter getWriter() throws IOException;
653:
654: /**
655: * Get an Action for a namespace.
656: * Return null if the action stage for the request is complete
657: * or there is some other reason that the window and it's children
658: * should not proceed further in the action stage.
659: *
660: * @throws PortletException if the namespace has already been seen in the
661: * actionstagephase of this connection
662: */
663: public Action getAction(Window window, String namespace)
664: throws PortletException, IOException {
665: return _context.getAction(window, namespace);
666: }
667:
668: /**
669: * Get the current Action object, established from a call to getRender().
670: */
671: public Action getCurrentAction() {
672: return _context.getCurrentAction();
673: }
674:
675: /**
676: * Get a Render for a namespace.
677: * Return null if there is some reason that the window and it's children
678: * should not be rendered.
679: *
680: * @throws PortletException if the namespace has already been seen in the
681: * render phase of this connection
682: */
683: public Render getRender(Window window, String namespace)
684: throws PortletException, IOException {
685: return _context.getRender(window, namespace);
686: }
687:
688: /**
689: * Get the current Render object, established from a call to getRender().
690: */
691: public Render getCurrentRender() {
692: return _context.getCurrentRender();
693: }
694:
695: /**
696: * Throw an exception if an error was encountered when using this conneciton.
697: */
698: public void checkForFailure() throws PortletException {
699: if (_connectionFailed) {
700: if (_connectionFailedCause == null)
701: throw new PortletException("connection failed");
702: else
703: throw new PortletException("connection failed: "
704: + _connectionFailedCause.toString(),
705: _connectionFailedCause);
706: }
707: }
708:
709: /**
710: * Used in derived classes during finish() to determine if
711: * the response is private
712: */
713: protected boolean isPrivate() {
714: return _context.isConnectionPrivate();
715: }
716:
717: /**
718: * Used in derived classes during finish() to determine
719: * the maximum expires time for the connection, derived classes use this
720: * value to send an expires timeout to the client.
721: * -1 means never expire, 0 means expire immediately, otherwise it is a
722: * number in seconds.
723: */
724: protected int getExpirationCache() {
725: return _context.getConnectionExpirationCache();
726: }
727:
728: }
|