001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.pluto.internal.impl;
018:
019: import java.io.BufferedReader;
020: import java.io.IOException;
021: import java.io.UnsupportedEncodingException;
022: import java.security.Principal;
023: import java.util.Collections;
024: import java.util.Enumeration;
025: import java.util.HashMap;
026: import java.util.HashSet;
027: import java.util.Iterator;
028: import java.util.Locale;
029: import java.util.Map;
030: import java.util.Set;
031: import java.util.Vector;
032:
033: import javax.portlet.PortalContext;
034: import javax.portlet.PortletContext;
035: import javax.portlet.PortletMode;
036: import javax.portlet.PortletRequest;
037: import javax.portlet.PortletSession;
038: import javax.portlet.WindowState;
039: import javax.servlet.RequestDispatcher;
040: import javax.servlet.ServletInputStream;
041: import javax.servlet.http.HttpServletRequest;
042: import javax.servlet.http.HttpServletRequestWrapper;
043: import javax.servlet.http.HttpSession;
044:
045: import org.apache.commons.logging.Log;
046: import org.apache.commons.logging.LogFactory;
047: import org.apache.pluto.PortletContainer;
048: import org.apache.pluto.PortletContainerException;
049: import org.apache.pluto.descriptors.common.SecurityRoleRefDD;
050: import org.apache.pluto.descriptors.portlet.PortletAppDD;
051: import org.apache.pluto.descriptors.portlet.PortletDD;
052: import org.apache.pluto.descriptors.portlet.SupportsDD;
053: import org.apache.pluto.descriptors.portlet.UserAttributeDD;
054: import org.apache.pluto.internal.InternalPortletRequest;
055: import org.apache.pluto.internal.InternalPortletWindow;
056: import org.apache.pluto.internal.PortletEntity;
057: import org.apache.pluto.util.ArgumentUtility;
058: import org.apache.pluto.util.Enumerator;
059: import org.apache.pluto.util.NamespaceMapper;
060: import org.apache.pluto.util.StringManager;
061: import org.apache.pluto.util.StringUtils;
062: import org.apache.pluto.util.impl.NamespaceMapperImpl;
063:
064: /**
065: * Abstract <code>javax.portlet.PortletRequest</code> implementation.
066: * This class also implements InternalPortletRequest.
067: *
068: */
069: public abstract class PortletRequestImpl extends
070: HttpServletRequestWrapper implements PortletRequest,
071: InternalPortletRequest {
072:
073: /**
074: * Logger.
075: */
076: private static final Log LOG = LogFactory
077: .getLog(PortletRequestImpl.class);
078:
079: private static final StringManager EXCEPTIONS = StringManager
080: .getManager(PortletRequestImpl.class.getPackage().getName());
081:
082: // Private Member Variables ------------------------------------------------
083:
084: /**
085: * The parent container within which this request was created.
086: */
087: private PortletContainer container;
088:
089: /**
090: * The portlet window which is the target of this portlet request.
091: */
092: private InternalPortletWindow internalPortletWindow;
093:
094: /**
095: * The PortletContext associated with this Request. This PortletContext must
096: * be initialized from within the <code>PortletServlet</code>.
097: */
098: private PortletContext portletContext;
099:
100: /**
101: * The PortalContext within which this request is occuring.
102: */
103: private PortalContext portalContext;
104:
105: /**
106: * The portlet session.
107: */
108: private PortletSession portletSession;
109:
110: /**
111: * Response content types.
112: */
113: private Vector contentTypes;
114:
115: /**
116: * TODO: javadoc
117: */
118: private final NamespaceMapper mapper = new NamespaceMapperImpl();
119:
120: /**
121: * FIXME: do we really need this?
122: * Flag indicating if the HTTP-Body has been accessed.
123: */
124: private boolean bodyAccessed = false;
125:
126: // Constructors ------------------------------------------------------------
127:
128: public PortletRequestImpl(
129: InternalPortletRequest internalPortletRequest) {
130: this (internalPortletRequest.getPortletContainer(),
131: internalPortletRequest.getInternalPortletWindow(),
132: internalPortletRequest.getHttpServletRequest());
133: }
134:
135: /**
136: * Creates a PortletRequestImpl instance.
137: *
138: * @param container the portlet container.
139: * @param internalPortletWindow the internal portlet window.
140: * @param servletRequest the underlying servlet request.
141: */
142: public PortletRequestImpl(PortletContainer container,
143: InternalPortletWindow internalPortletWindow,
144: HttpServletRequest servletRequest) {
145: super (servletRequest);
146: this .container = container;
147: this .internalPortletWindow = internalPortletWindow;
148: this .portalContext = container.getRequiredContainerServices()
149: .getPortalContext();
150: }
151:
152: // PortletRequest Impl -----------------------------------------------------
153:
154: /**
155: * Determine whether or not the specified WindowState is allowed for this
156: * portlet.
157: *
158: * @param state the state in question
159: * @return true if the state is allowed.
160: */
161: public boolean isWindowStateAllowed(WindowState state) {
162: for (Enumeration en = portalContext.getSupportedWindowStates(); en
163: .hasMoreElements();) {
164: if (en.nextElement().toString().equals(state.toString())) {
165: return true;
166: }
167: }
168: return false;
169: }
170:
171: public boolean isPortletModeAllowed(PortletMode mode) {
172: return (isPortletModeAllowedByPortlet(mode) && isPortletModeAllowedByPortal(mode));
173: }
174:
175: public PortletMode getPortletMode() {
176: return internalPortletWindow.getPortletMode();
177: }
178:
179: public WindowState getWindowState() {
180: return internalPortletWindow.getWindowState();
181: }
182:
183: public PortletSession getPortletSession() {
184: return getPortletSession(true);
185: }
186:
187: /**
188: * Returns the portlet session.
189: * <p/>
190: * Note that since portlet request instance is created everytime the portlet
191: * container receives an incoming request, the portlet session instance held
192: * by the request instance is also re-created for each incoming request.
193: * </p>
194: */
195: public PortletSession getPortletSession(boolean create) {
196: if (LOG.isDebugEnabled()) {
197: LOG.debug("Retreiving portlet session (create=" + create
198: + ")");
199: }
200: //
201: // It is critical that we don't retrieve the portlet session until the
202: // cross context dispatch has been completed. If we do then we risk
203: // having a cached version which is invalid for the context within
204: // which it exists.
205: //
206: if (portletContext == null) {
207: throw new IllegalStateException(EXCEPTIONS
208: .getString("error.session.illegalState"));
209: }
210: //
211: // We must make sure that if the session has been invalidated (perhaps
212: // through setMaxIntervalTimeout()) and the underlying request
213: // returns null that we no longer use the cached version.
214: // We have to check (ourselves) if the session has exceeded its max
215: // inactive interval. If so, we should invalidate the underlying
216: // HttpSession and recreate a new one (if the create flag is set to
217: // true) -- We just cannot depend on the implementation of
218: // javax.servlet.http.HttpSession!
219: //
220: HttpSession httpSession = getHttpServletRequest().getSession(
221: create);
222: if (httpSession != null) {
223: // HttpSession is not null does NOT mean that it is valid.
224: int maxInactiveInterval = httpSession
225: .getMaxInactiveInterval();
226: if (maxInactiveInterval >= 0) { // < 0 => Never expires.
227: long maxInactiveTime = httpSession
228: .getMaxInactiveInterval() * 1000L;
229: long currentInactiveTime = System.currentTimeMillis()
230: - httpSession.getLastAccessedTime();
231: if (currentInactiveTime > maxInactiveTime) {
232: if (LOG.isDebugEnabled()) {
233: LOG
234: .debug("The underlying HttpSession is expired and "
235: + "should be invalidated.");
236: }
237: httpSession.invalidate();
238: httpSession = getHttpServletRequest().getSession(
239: create);
240: }
241: }
242: }
243:
244: if (httpSession == null) {
245: if (LOG.isDebugEnabled()) {
246: LOG
247: .debug("The underlying HttpSession is not available: "
248: + "no session will be returned.");
249: }
250: return null;
251: }
252:
253: //
254: // If we reach here, we are sure that the underlying HttpSession is
255: // available. If we haven't created and cached a portlet session
256: // instance, we will create and cache one now.
257: //
258: if (portletSession == null) {
259: if (LOG.isDebugEnabled()) {
260: LOG.debug("Creating new portlet session...");
261: }
262: portletSession = new PortletSessionImpl(portletContext,
263: internalPortletWindow, httpSession);
264: }
265: return portletSession;
266: }
267:
268: public String getProperty(String name)
269: throws IllegalArgumentException {
270: ArgumentUtility.validateNotNull("propertyName", name);
271: String property = this .getHttpServletRequest().getHeader(name);
272: if (property == null) {
273: Map propertyMap = container.getRequiredContainerServices()
274: .getPortalCallbackService().getRequestProperties(
275: getHttpServletRequest(),
276: internalPortletWindow);
277:
278: if (propertyMap != null) {
279: String[] properties = (String[]) propertyMap.get(name);
280: if (properties != null && properties.length > 0) {
281: property = properties[0];
282: }
283: }
284: }
285: return property;
286: }
287:
288: public Enumeration getProperties(String name) {
289: ArgumentUtility.validateNotNull("propertyName", name);
290: Set v = new HashSet();
291: Enumeration props = this .getHttpServletRequest().getHeaders(
292: name);
293: if (props != null) {
294: while (props.hasMoreElements()) {
295: v.add(props.nextElement());
296: }
297: }
298:
299: // get properties from PropertyManager
300: Map map = container.getRequiredContainerServices()
301: .getPortalCallbackService().getRequestProperties(
302: getHttpServletRequest(), internalPortletWindow);
303:
304: if (map != null) {
305: String[] properties = (String[]) map.get(name);
306:
307: if (properties != null) {
308: // add properties to vector
309: for (int i = 0; i < properties.length; i++) {
310: v.add(properties[i]);
311: }
312: }
313: }
314:
315: return new Enumerator(v.iterator());
316: }
317:
318: public Enumeration getPropertyNames() {
319: Set v = new HashSet();
320:
321: // get properties from PropertyManager
322: Map map = container.getRequiredContainerServices()
323: .getPortalCallbackService().getRequestProperties(
324: getHttpServletRequest(), internalPortletWindow);
325:
326: if (map != null) {
327: v.addAll(map.keySet());
328: }
329:
330: // get properties from request header
331: Enumeration props = this .getHttpServletRequest()
332: .getHeaderNames();
333: if (props != null) {
334: while (props.hasMoreElements()) {
335: v.add(props.nextElement());
336: }
337: }
338:
339: return new Enumerator(v.iterator());
340: }
341:
342: public PortalContext getPortalContext() {
343: return container.getRequiredContainerServices()
344: .getPortalContext();
345: }
346:
347: public String getAuthType() {
348: return this .getHttpServletRequest().getAuthType();
349: }
350:
351: public String getContextPath() {
352: String contextPath = internalPortletWindow.getContextPath();
353: if ("/".equals(contextPath)) {
354: contextPath = "";
355: }
356: return contextPath;
357: }
358:
359: public String getRemoteUser() {
360: return this .getHttpServletRequest().getRemoteUser();
361: }
362:
363: public Principal getUserPrincipal() {
364: return this .getHttpServletRequest().getUserPrincipal();
365: }
366:
367: /**
368: * Determines whether a user is mapped to the specified role. As specified
369: * in PLT-20-3, we must reference the <security-role-ref> mappings
370: * within the deployment descriptor. If no mapping is available, then, and
371: * only then, do we check use the actual role name specified against the web
372: * application deployment descriptor.
373: *
374: * @param roleName the name of the role
375: * @return true if it is determined the user has the given role.
376: */
377: public boolean isUserInRole(String roleName) {
378: PortletEntity entity = internalPortletWindow.getPortletEntity();
379: PortletDD def = entity.getPortletDefinition();
380:
381: SecurityRoleRefDD ref = null;
382: Iterator refs = def.getSecurityRoleRefs().iterator();
383: while (refs.hasNext()) {
384: SecurityRoleRefDD r = (SecurityRoleRefDD) refs.next();
385: if (r.getRoleName().equals(roleName)) {
386: ref = r;
387: break;
388: }
389: }
390:
391: String link;
392: if (ref != null && ref.getRoleLink() != null) {
393: link = ref.getRoleLink();
394: } else {
395: link = roleName;
396: }
397:
398: return this .getHttpServletRequest().isUserInRole(link);
399: }
400:
401: public Object getAttribute(String name) {
402: ArgumentUtility.validateNotNull("attributeName", name);
403:
404: if (PortletRequest.USER_INFO.equals(name)) {
405: return createUserInfoMap();
406: }
407:
408: String encodedName = isNameReserved(name) ? name : mapper
409: .encode(internalPortletWindow.getId(), name);
410:
411: Object attribute = getHttpServletRequest().getAttribute(
412: encodedName);
413:
414: if (attribute == null) {
415: attribute = getHttpServletRequest().getAttribute(name);
416: }
417: return attribute;
418: }
419:
420: public Enumeration getAttributeNames() {
421: Enumeration attributes = this .getHttpServletRequest()
422: .getAttributeNames();
423:
424: Vector portletAttributes = new Vector();
425:
426: while (attributes.hasMoreElements()) {
427: String attribute = (String) attributes.nextElement();
428:
429: //Fix for PLUTO-369
430: String portletAttribute = isNameReserved(attribute) ? attribute
431: : mapper.decode(internalPortletWindow.getId(),
432: attribute);
433:
434: if (portletAttribute != null) { // it is in the portlet's namespace
435: portletAttributes.add(portletAttribute);
436: }
437: }
438:
439: return portletAttributes.elements();
440: }
441:
442: public Map createUserInfoMap() {
443:
444: Map userInfoMap = new HashMap();
445: try {
446:
447: PortletAppDD dd = container.getOptionalContainerServices()
448: .getPortletRegistryService()
449: .getPortletApplicationDescriptor(
450: internalPortletWindow.getContextPath());
451:
452: Map allMap = container.getOptionalContainerServices()
453: //PLUTO-388 fix:
454: //The PortletWindow is currently ignored in the implementing class
455: // See: org.apache.pluto.core.DefaultUserInfoService
456: .getUserInfoService().getUserInfo(this ,
457: this .internalPortletWindow);
458:
459: Iterator i = dd.getUserAttributes().iterator();
460: while (i.hasNext()) {
461: UserAttributeDD udd = (UserAttributeDD) i.next();
462: userInfoMap.put(udd.getName(), allMap
463: .get(udd.getName()));
464: }
465: } catch (PortletContainerException e) {
466: LOG.warn("Unable to retrieve user attribute map for user "
467: + getRemoteUser() + ". Returning null.");
468: return null;
469: }
470:
471: return Collections.unmodifiableMap(userInfoMap);
472: }
473:
474: public String getParameter(String name) {
475: ArgumentUtility.validateNotNull("parameterName", name);
476: String[] values = (String[]) baseGetParameterMap().get(name);
477: if (values != null && values.length > 0) {
478: return values[0];
479: } else {
480: return null;
481: }
482: }
483:
484: public Enumeration getParameterNames() {
485: return Collections.enumeration(baseGetParameterMap().keySet());
486: }
487:
488: public String[] getParameterValues(String name) {
489: ArgumentUtility.validateNotNull("parameterName", name);
490: String[] values = (String[]) baseGetParameterMap().get(name);
491: if (values != null) {
492: values = StringUtils.copy(values);
493: }
494: return values;
495: }
496:
497: public Map getParameterMap() {
498: return StringUtils.copyParameters(baseGetParameterMap());
499: }
500:
501: public boolean isSecure() {
502: return this .getHttpServletRequest().isSecure();
503: }
504:
505: public void setAttribute(String name, Object value) {
506: ArgumentUtility.validateNotNull("attributeName", name);
507: String encodedName = isNameReserved(name) ? name : mapper
508: .encode(internalPortletWindow.getId(), name);
509: if (value == null) {
510: removeAttribute(name);
511: } else {
512: getHttpServletRequest().setAttribute(encodedName, value);
513: }
514: }
515:
516: public void removeAttribute(String name) {
517: ArgumentUtility.validateNotNull("attributeName", name);
518: String encodedName = isNameReserved(name) ? name : mapper
519: .encode(internalPortletWindow.getId(), name);
520: getHttpServletRequest().removeAttribute(encodedName);
521: }
522:
523: public String getRequestedSessionId() {
524: return this .getHttpServletRequest().getRequestedSessionId();
525: }
526:
527: public boolean isRequestedSessionIdValid() {
528: if (LOG.isDebugEnabled()) {
529: LOG.debug(" ***** IsRequestedSessionIdValid? "
530: + getHttpServletRequest()
531: .isRequestedSessionIdValid());
532: }
533: return getHttpServletRequest().isRequestedSessionIdValid();
534: }
535:
536: public String getResponseContentType() {
537: Enumeration enumeration = getResponseContentTypes();
538: while (enumeration.hasMoreElements()) {
539: return (String) enumeration.nextElement();
540: }
541: return "text/html";
542: }
543:
544: public Enumeration getResponseContentTypes() {
545: if (contentTypes == null) {
546: contentTypes = new Vector();
547: PortletDD dd = internalPortletWindow.getPortletEntity()
548: .getPortletDefinition();
549: Iterator supports = dd.getSupports().iterator();
550: while (supports.hasNext()) {
551: SupportsDD sup = (SupportsDD) supports.next();
552: contentTypes.add(sup.getMimeType());
553: }
554: if (contentTypes.size() < 1) {
555: contentTypes.add("text/html");
556: }
557: }
558: return contentTypes.elements();
559: }
560:
561: public Locale getLocale() {
562: return this .getHttpServletRequest().getLocale();
563: }
564:
565: public Enumeration getLocales() {
566: return this .getHttpServletRequest().getLocales();
567: }
568:
569: public String getScheme() {
570: return this .getHttpServletRequest().getScheme();
571: }
572:
573: public String getServerName() {
574: return this .getHttpServletRequest().getServerName();
575: }
576:
577: public int getServerPort() {
578: return this .getHttpServletRequest().getServerPort();
579: }
580:
581: // Protected Methods -------------------------------------------------------
582:
583: /**
584: * The base method that returns the parameter map in this portlet request.
585: * All parameter-related methods call this base method. Subclasses may just
586: * overwrite this protected method to change behavior of all parameter-
587: * related methods.
588: *
589: * @return the base parameter map from which parameters are retrieved.
590: */
591: protected Map baseGetParameterMap() {
592: bodyAccessed = true;
593: return this .getHttpServletRequest().getParameterMap();
594: }
595:
596: protected void setBodyAccessed() {
597: bodyAccessed = true;
598: }
599:
600: // InternalPortletRequest Impl ---------------------------------------------
601:
602: public InternalPortletWindow getInternalPortletWindow() {
603: return internalPortletWindow;
604: }
605:
606: public PortletContainer getPortletContainer() {
607: return container;
608: }
609:
610: public HttpServletRequest getHttpServletRequest() {
611: return (HttpServletRequest) super .getRequest();
612: }
613:
614: public void init(PortletContext portletContext,
615: HttpServletRequest req) {
616: this .portletContext = portletContext;
617: setRequest(req);
618: }
619:
620: /**
621: * TODO: Implement this properly. Not required now
622: */
623: public void release() {
624: // TODO:
625: }
626:
627: // TODO: Additional Methods of HttpServletRequestWrapper -------------------
628:
629: public BufferedReader getReader()
630: throws UnsupportedEncodingException, IOException {
631: // the super class will ensure that a IllegalStateException is thrown
632: // if getInputStream() was called earlier
633: BufferedReader reader = getHttpServletRequest().getReader();
634: bodyAccessed = true;
635: return reader;
636: }
637:
638: public ServletInputStream getInputStream() throws IOException {
639: ServletInputStream stream = getHttpServletRequest()
640: .getInputStream();
641: bodyAccessed = true;
642: return stream;
643: }
644:
645: public RequestDispatcher getRequestDispatcher(String path) {
646: return getHttpServletRequest().getRequestDispatcher(path);
647: }
648:
649: /**
650: * TODO: why check bodyAccessed?
651: */
652: public void setCharacterEncoding(String encoding)
653: throws UnsupportedEncodingException {
654: if (bodyAccessed) {
655: throw new IllegalStateException(
656: "Cannot set character encoding "
657: + "after HTTP body is accessed.");
658: }
659: super .setCharacterEncoding(encoding);
660: }
661:
662: // Private Methods ---------------------------------------------------------
663:
664: /**
665: * Is this attribute name a reserved name (by the J2EE spec)?. Reserved
666: * names begin with "java." or "javax.".
667: *
668: * @return true if the name is reserved.
669: */
670: private boolean isNameReserved(String name) {
671: return name.startsWith("java.") || name.startsWith("javax.");
672: }
673:
674: private boolean isPortletModeAllowedByPortlet(PortletMode mode) {
675: if (isPortletModeMandatory(mode)) {
676: return true;
677: }
678:
679: PortletDD dd = internalPortletWindow.getPortletEntity()
680: .getPortletDefinition();
681:
682: Iterator mimes = dd.getSupports().iterator();
683: while (mimes.hasNext()) {
684: Iterator modes = ((SupportsDD) mimes.next())
685: .getPortletModes().iterator();
686: while (modes.hasNext()) {
687: String m = (String) modes.next();
688: if (m.equals(mode.toString())) {
689: return true;
690: }
691: }
692: }
693: return false;
694: }
695:
696: private boolean isPortletModeAllowedByPortal(PortletMode mode) {
697: Enumeration supportedModes = portalContext
698: .getSupportedPortletModes();
699: while (supportedModes.hasMoreElements()) {
700: if (supportedModes.nextElement().toString().equals(
701: (mode.toString()))) {
702: return true;
703: }
704: }
705: return false;
706: }
707:
708: private boolean isPortletModeMandatory(PortletMode mode) {
709: return PortletMode.VIEW.equals(mode)
710: || PortletMode.EDIT.equals(mode)
711: || PortletMode.HELP.equals(mode);
712: }
713: }
|