001: /* Copyright 2003 - 2005 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal.channels.portlet;
007:
008: import java.io.PrintWriter;
009: import java.io.StringWriter;
010: import java.util.Enumeration;
011: import java.util.HashMap;
012: import java.util.Iterator;
013: import java.util.List;
014: import java.util.Map;
015: import java.util.Properties;
016:
017: import javax.portlet.PortletMode;
018: import javax.portlet.PortletRequest;
019: import javax.portlet.PortletSession;
020: import javax.portlet.RenderResponse;
021: import javax.portlet.WindowState;
022: import javax.servlet.ServletConfig;
023: import javax.servlet.http.HttpServletRequest;
024: import javax.servlet.http.HttpServletResponse;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028: import org.apache.pluto.PortletContainer;
029: import org.apache.pluto.PortletContainerImpl;
030: import org.apache.pluto.PortletContainerServices;
031: import org.apache.pluto.om.entity.PortletEntity;
032: import org.apache.pluto.om.portlet.PortletDefinition;
033: import org.apache.pluto.om.window.PortletWindow;
034: import org.apache.pluto.services.information.DynamicInformationProvider;
035: import org.apache.pluto.services.information.InformationProviderAccess;
036: import org.apache.pluto.services.information.PortletActionProvider;
037: import org.apache.pluto.services.property.PropertyManager;
038: import org.apache.pluto.services.property.PropertyManagerService;
039: import org.jasig.portal.ChannelCacheKey;
040: import org.jasig.portal.ChannelDefinition;
041: import org.jasig.portal.ChannelRegistryStoreFactory;
042: import org.jasig.portal.ChannelRuntimeData;
043: import org.jasig.portal.ChannelRuntimeProperties;
044: import org.jasig.portal.ChannelStaticData;
045: import org.jasig.portal.ICacheable;
046: import org.jasig.portal.ICharacterChannel;
047: import org.jasig.portal.IDirectResponse;
048: import org.jasig.portal.IPrivileged;
049: import org.jasig.portal.PortalControlStructures;
050: import org.jasig.portal.PortalEvent;
051: import org.jasig.portal.PortalException;
052: import org.jasig.portal.container.PortletServlet;
053: import org.jasig.portal.container.om.common.ObjectIDImpl;
054: import org.jasig.portal.container.om.entity.PortletApplicationEntityImpl;
055: import org.jasig.portal.container.om.entity.PortletEntityImpl;
056: import org.jasig.portal.container.om.portlet.PortletApplicationDefinitionImpl;
057: import org.jasig.portal.container.om.portlet.PortletDefinitionImpl;
058: import org.jasig.portal.container.om.portlet.UserAttributeImpl;
059: import org.jasig.portal.container.om.portlet.UserAttributeListImpl;
060: import org.jasig.portal.container.om.window.PortletWindowImpl;
061: import org.jasig.portal.container.services.FactoryManagerServiceImpl;
062: import org.jasig.portal.container.services.PortletContainerEnvironmentImpl;
063: import org.jasig.portal.container.services.information.DynamicInformationProviderImpl;
064: import org.jasig.portal.container.services.information.InformationProviderServiceImpl;
065: import org.jasig.portal.container.services.information.PortletStateManager;
066: import org.jasig.portal.container.services.log.LogServiceImpl;
067: import org.jasig.portal.container.services.property.PropertyManagerServiceImpl;
068: import org.jasig.portal.container.servlet.DummyParameterRequestWrapper;
069: import org.jasig.portal.container.servlet.PortletAttributeRequestWrapper;
070: import org.jasig.portal.container.servlet.PortletParameterRequestWrapper;
071: import org.jasig.portal.container.servlet.ServletObjectAccess;
072: import org.jasig.portal.container.servlet.ServletRequestImpl;
073: import org.jasig.portal.layout.node.IUserLayoutChannelDescription;
074: import org.jasig.portal.properties.PropertiesManager;
075: import org.jasig.portal.security.IOpaqueCredentials;
076: import org.jasig.portal.security.IPerson;
077: import org.jasig.portal.security.ISecurityContext;
078: import org.jasig.portal.security.provider.NotSoOpaqueCredentials;
079: import org.jasig.portal.utils.NullOutputStream;
080: import org.jasig.portal.utils.SAXHelper;
081: import org.xml.sax.ContentHandler;
082:
083: /**
084: * A JSR 168 Portlet adapter that presents a portlet
085: * through the uPortal channel interface.
086: * <p>
087: * There is a related channel type called
088: * "Portlet Adapter" that is included with uPortal, so to use
089: * this channel, just select the "Portlet" type when publishing.
090: * </p>
091: * <p>
092: * Note: A portlet can specify the String "password" in the
093: * user attributes section of the portlet.xml. In this is done,
094: * this adapter will look for the user's cached password. If
095: * the user's password is being stored in memory by a caching
096: * security context, the adapter will consult the cache to fill the
097: * request for the attribute. If the user's password is not cached,
098: * <code>null</code> will be set for the attributes value.
099: * </p>
100: * @author Ken Weiner, kweiner@unicon.net
101: * @version $Revision: 42658 $
102: */
103: public class CPortletAdapter implements ICharacterChannel, IPrivileged,
104: ICacheable, IDirectResponse, IPortletAdaptor {
105:
106: protected final Log log = LogFactory.getLog(getClass());
107:
108: private static boolean portletContainerInitialized;
109: private static PortletContainer portletContainer;
110: private static ServletConfig servletConfig;
111: private static final ChannelCacheKey systemCacheKey;
112: private static final ChannelCacheKey instanceCacheKey;
113:
114: private static final String uniqueContainerName = PropertiesManager
115: .getProperty(
116: "org.jasig.portal.channels.portlet.CPortletAdapter.uniqueContainerName",
117: "Pluto-in-uPortal");
118:
119: // Publish parameters expected by this channel
120: private static final String portletDefinitionIdParamName = "portletDefinitionId";
121: public static final String portletPreferenceNamePrefix = "PORTLET.";
122:
123: private ChannelStaticData staticData = null;
124: private ChannelRuntimeData runtimeData = null;
125: private PortalControlStructures pcs = null;
126:
127: private boolean portletWindowInitialized = false;
128: private PortletWindow portletWindow = null;
129: private Map userInfo = null;
130: private boolean receivedEvent = false;
131: private boolean focused = false;
132: private PortletMode newPortletMode = null;
133: private long lastRenderTime = Long.MIN_VALUE;
134: private String expirationCache = null;
135: private WindowState newWindowState = null;
136: private PortletSession portletSession = null;
137:
138: private Map requestParams = null;
139:
140: static {
141: portletContainerInitialized = false;
142:
143: // Initialize cache keys
144: ChannelCacheKey key = new ChannelCacheKey();
145: key.setKeyScope(ChannelCacheKey.SYSTEM_KEY_SCOPE);
146: key.setKey("SYSTEM_SCOPE_KEY");
147: systemCacheKey = key;
148: key = new ChannelCacheKey();
149: key.setKeyScope(ChannelCacheKey.INSTANCE_KEY_SCOPE);
150: key.setKey("INSTANCE_SCOPE_KEY");
151: instanceCacheKey = key;
152: }
153:
154: /**
155: * Receive the servlet config from uPortal's PortalSessionManager servlet.
156: * Pluto needs access to this object from serveral places.
157: * @param config the servlet config
158: */
159: public static void setServletConfig(ServletConfig config) {
160: servletConfig = config;
161: }
162:
163: /**
164: *
165: * @throws PortalException
166: */
167: private synchronized static void initPortletContainer()
168: throws PortalException {
169: if (!portletContainerInitialized) {
170: portletContainerInitialized = true;
171: try {
172: PortletContainerEnvironmentImpl environment = new PortletContainerEnvironmentImpl();
173: LogServiceImpl logService = new LogServiceImpl();
174: FactoryManagerServiceImpl factorManagerService = new FactoryManagerServiceImpl();
175: InformationProviderServiceImpl informationProviderService = new InformationProviderServiceImpl();
176: PropertyManagerService propertyManagerService = new PropertyManagerServiceImpl();
177:
178: logService.init(servletConfig, null);
179: factorManagerService.init(servletConfig, null);
180: informationProviderService.init(servletConfig, null);
181:
182: environment.addContainerService(logService);
183: environment.addContainerService(factorManagerService);
184: environment
185: .addContainerService(informationProviderService);
186: environment.addContainerService(propertyManagerService);
187:
188: //Call added in case the context has been re-loaded
189: PortletContainerServices
190: .destroyReference(uniqueContainerName);
191: portletContainer = new PortletContainerImpl();
192: portletContainer.init(uniqueContainerName,
193: servletConfig, environment, new Properties());
194:
195: } catch (Exception e) {
196: String message = "Initialization of the portlet container failed.";
197: //log.error( message, e);
198: throw new PortalException(message, e);
199: }
200: }
201:
202: }
203:
204: protected void initPortletWindow() throws PortalException {
205:
206: try {
207: initPortletContainer();
208:
209: PortletContainerServices.prepare(uniqueContainerName);
210:
211: // Get the portlet definition Id which must be specified as a publish
212: // parameter. The syntax of the ID is [portlet-context-name].[portlet-name]
213: String portletDefinitionId = staticData
214: .getParameter(portletDefinitionIdParamName);
215: if (portletDefinitionId == null) {
216: throw new PortalException("Missing publish parameter '"
217: + portletDefinitionIdParamName + "'");
218: }
219:
220: // Create the PortletDefinition
221: PortletDefinitionImpl portletDefinition = (PortletDefinitionImpl) InformationProviderAccess
222: .getStaticProvider()
223: .getPortletDefinition(
224: ObjectIDImpl
225: .createFromString(portletDefinitionId));
226: if (portletDefinition == null) {
227: throw new PortalException(
228: "Unable to find portlet definition for ID '"
229: + portletDefinitionId + "'");
230: }
231: ChannelDefinition channelDefinition = ChannelRegistryStoreFactory
232: .getChannelRegistryStoreImpl()
233: .getChannelDefinition(
234: Integer.parseInt(staticData
235: .getChannelPublishId()));
236:
237: // Create the PortletApplicationEntity
238: final PortletApplicationEntityImpl portAppEnt = new PortletApplicationEntityImpl();
239: portAppEnt.setId(portletDefinition.getId().toString());
240: portAppEnt
241: .setPortletApplicationDefinition(portletDefinition
242: .getPortletApplicationDefinition());
243:
244: // Create the PortletEntity
245: PortletEntityImpl portletEntity = new PortletEntityImpl();
246: portletEntity.setId(staticData.getChannelPublishId());
247: portletEntity.setPortletDefinition(portletDefinition);
248: portletEntity.setChannelDefinition(channelDefinition);
249: portletEntity.setPortletApplicationEntity(portAppEnt);
250: portletEntity.setUserLayout(pcs.getUserPreferencesManager()
251: .getUserLayoutManager().getUserLayout());
252: portletEntity
253: .setChannelDescription((IUserLayoutChannelDescription) pcs
254: .getUserPreferencesManager()
255: .getUserLayoutManager().getNode(
256: staticData.getChannelSubscribeId()));
257: portletEntity.setPerson(staticData.getPerson());
258: portletEntity.loadPreferences();
259:
260: // Add the user information into the request See PLT.17.2.
261:
262: if (userInfo == null) {
263: UserAttributeListImpl userAttributeList = ((PortletApplicationDefinitionImpl) portletDefinition
264: .getPortletApplicationDefinition())
265: .getUserAttributes();
266:
267: // here we ask an overridable method to get the user attributes.
268: // you can extend CPortletAdapter to change the implementation of
269: // how we get user attributes. This whole initPortletWindow method
270: // is also overridable.
271: //
272: // Note that we will only call getUserInfo() once.
273: userInfo = getUserInfo(staticData, userAttributeList);
274: if (log.isTraceEnabled()) {
275: log.trace("For user [" + staticData.getPerson()
276: + "] got user info : [" + userInfo + "]");
277: }
278: }
279:
280: // Wrap the request
281: ServletRequestImpl wrappedRequest = new ServletRequestImpl(
282: pcs.getHttpServletRequest(),
283: staticData.getPerson(), portletDefinition
284: .getInitSecurityRoleRefSet());
285:
286: // Now create the PortletWindow and hold a reference to it
287: PortletWindowImpl pw = new PortletWindowImpl();
288: pw.setId(staticData.getChannelSubscribeId());
289: pw.setPortletEntity(portletEntity);
290: pw.setChannelRuntimeData(runtimeData);
291: pw.setHttpServletRequest(wrappedRequest);
292: portletWindow = pw;
293:
294: // Ask the container to load the portlet
295: synchronized (this ) {
296: portletContainer.portletLoad(portletWindow,
297: wrappedRequest, pcs.getHttpServletResponse());
298: }
299:
300: portletWindowInitialized = true;
301:
302: } catch (Exception e) {
303: String message = "Initialization of the portlet container failed.";
304: log.error(message, e);
305: throw new PortalException(message, e);
306: } finally {
307: PortletContainerServices.release();
308: }
309: }
310:
311: /**
312: * Sets channel runtime properties.
313: * @return channel runtime properties
314: */
315: public ChannelRuntimeProperties getRuntimeProperties() {
316: return new ChannelRuntimeProperties();
317: }
318:
319: /**
320: * React to portal events.
321: * Removes channel state from the channel state map when the session expires.
322: * @param ev a portal event
323: */
324: public void receiveEvent(PortalEvent ev) {
325:
326: try {
327: PortletContainerServices.prepare(uniqueContainerName);
328:
329: receivedEvent = true;
330:
331: switch (ev.getEventNumber()) {
332:
333: // Detect portlet mode changes
334:
335: // Cannot use the PortletActionProvider to change modes here. It uses
336: // PortletWindow information to store the changes and the window is
337: // not current at this point.
338:
339: case PortalEvent.EDIT_BUTTON_EVENT:
340: newPortletMode = PortletMode.EDIT;
341: break;
342: case PortalEvent.HELP_BUTTON_EVENT:
343: newPortletMode = PortletMode.HELP;
344: break;
345: case PortalEvent.ABOUT_BUTTON_EVENT:
346: // We might want to consider a custom ABOUT mode here
347: break;
348:
349: //Detect portlet window state changes
350: case PortalEvent.MINIMIZE_EVENT:
351: newWindowState = WindowState.MINIMIZED;
352: break;
353:
354: case PortalEvent.MAXIMIZE_EVENT:
355: newWindowState = WindowState.NORMAL;
356: break;
357:
358: case PortalEvent.DETACH_BUTTON_EVENT:
359: newWindowState = WindowState.MAXIMIZED;
360: break;
361:
362: //Detect end of session or portlet removed from layout
363: case PortalEvent.UNSUBSCRIBE:
364: //User is removing this portlet from their layout, remove all
365: //the preferences they have stored for it.
366: PortletEntityImpl pe = (PortletEntityImpl) portletWindow
367: .getPortletEntity();
368: try {
369: pe.removePreferences();
370: } catch (Exception e) {
371: log.error(e, e);
372: }
373:
374: case PortalEvent.SESSION_DONE:
375: // For both SESSION_DONE and UNSUBSCRIBE, we might want to
376: // release resources here if we need to
377:
378: PortletWindowImpl windowImpl = (PortletWindowImpl) portletWindow;
379:
380: try {
381: PortletStateManager.clearState(windowImpl);
382: } catch (IllegalStateException ise) {
383: //Ignore an illegal state when the PortletStateManager tries to
384: //access the session if it has already been destroyed.
385: if (log.isDebugEnabled()) {
386: log
387: .debug("IllegalStateException attempting to clear portlet state for windowImpl "
388: + windowImpl);
389: }
390: } catch (Exception e) {
391: // regardless of what went wrong clearing portlet state, need to continue the event handling workflow
392: // therefore, log error and ignore
393: log
394: .error("Exception attempting to clear portlet state for windowImpl "
395: + windowImpl);
396:
397: }
398:
399: // Invalidate portlet session
400: if (portletSession != null) {
401: try {
402: portletSession.invalidate();
403: } catch (Exception e) {
404: log.error(e, e);
405: }
406: }
407:
408: break;
409:
410: default:
411: break;
412: }
413: } finally {
414: PortletContainerServices.release();
415: }
416: }
417:
418: /**
419: * Sets the channel static data.
420: * @param sd the channel static data
421: * @throws org.jasig.portal.PortalException
422: */
423: public void setStaticData(ChannelStaticData sd)
424: throws PortalException {
425: staticData = sd;
426:
427: try {
428:
429: // Register this portlet's channel subscribe ID in the JNDI context
430: // this is probably not necessary since afaik nothing other than this
431: // portlet is reading this List. -andrew petro
432:
433: List portletIds = (List) sd.getJNDIContext().lookup(
434: "/portlet-ids");
435: portletIds.add(sd.getChannelSubscribeId());
436:
437: } catch (Exception e) {
438: throw new PortalException(
439: "Error accessing /portlet-ids JNDI context.", e);
440: }
441: }
442:
443: /**
444: * Sets the channel runtime data.
445: * @param rd the channel runtime data
446: * @throws org.jasig.portal.PortalException
447: */
448: public void setRuntimeData(ChannelRuntimeData rd)
449: throws PortalException {
450: runtimeData = rd;
451:
452: if (!this .portletWindowInitialized) {
453: this .initPortletWindow();
454: }
455:
456: try {
457: PortletContainerServices.prepare(uniqueContainerName);
458:
459: final PortletWindowImpl portletWindowimp = (PortletWindowImpl) portletWindow;
460: final PortletEntity portletEntity = portletWindow
461: .getPortletEntity();
462: final PortletDefinition portletDef = portletEntity
463: .getPortletDefinition();
464: final HttpServletRequest baseRequest = pcs
465: .getHttpServletRequest();
466:
467: HttpServletRequest wrappedRequest = new ServletRequestImpl(
468: baseRequest, staticData.getPerson(), portletDef
469: .getInitSecurityRoleRefSet());
470:
471: //Wrap the request to scope attributes to this portlet instance
472: wrappedRequest = new PortletAttributeRequestWrapper(
473: wrappedRequest);
474:
475: //Set up request attributes (user info, portal session, etc...)
476: setupRequestAttributes(wrappedRequest);
477:
478: // Put the current runtime data and wrapped request into the portlet window
479: portletWindowimp.setChannelRuntimeData(rd);
480: portletWindowimp.setHttpServletRequest(wrappedRequest);
481:
482: // Get the portlet url manager which will analyze the request parameters
483: DynamicInformationProvider dip = InformationProviderAccess
484: .getDynamicProvider(wrappedRequest);
485: PortletStateManager psm = ((DynamicInformationProviderImpl) dip)
486: .getPortletStateManager(portletWindow);
487: PortletActionProvider pap = dip
488: .getPortletActionProvider(portletWindow);
489:
490: //If portlet is rendering as root, change mode to maximized, otherwise minimized
491: if (!psm.isAction() && rd.isRenderingAsRoot()) {
492: if (WindowState.MINIMIZED.equals(newWindowState)) {
493: pap.changePortletWindowState(WindowState.MINIMIZED);
494: } else {
495: pap.changePortletWindowState(WindowState.MAXIMIZED);
496: }
497: } else if (newWindowState != null) {
498: pap.changePortletWindowState(newWindowState);
499: } else if (!psm.isAction()) {
500: pap.changePortletWindowState(WindowState.NORMAL);
501: }
502: newWindowState = null;
503:
504: //Check for a portlet mode change
505: if (newPortletMode != null) {
506: pap.changePortletMode(newPortletMode);
507: PortletStateManager.setMode(portletWindow,
508: newPortletMode);
509: }
510: newPortletMode = null;
511:
512: // Process action if this is the targeted channel and the URL is an action URL
513: if (rd.isTargeted() && psm.isAction()) {
514: //Create a sink to throw out and output (portlets can't output content during an action)
515: PrintWriter pw = new PrintWriter(new NullOutputStream());
516: HttpServletResponse wrappedResponse = ServletObjectAccess
517: .getStoredServletResponse(pcs
518: .getHttpServletResponse(), pw);
519:
520: try {
521: //See if a WindowState change was requested for an ActionURL
522: final String newWindowStateName = wrappedRequest
523: .getParameter(PortletStateManager.UP_WINDOW_STATE);
524: if (newWindowStateName != null) {
525: pap.changePortletWindowState(new WindowState(
526: newWindowStateName));
527: }
528:
529: HttpServletRequest wrappedPortletRequest = new PortletParameterRequestWrapper(
530: wrappedRequest);
531:
532: portletContainer.processPortletAction(
533: portletWindow, wrappedPortletRequest,
534: wrappedResponse);
535: } catch (Exception e) {
536: throw new PortalException(e);
537: }
538: }
539: } finally {
540: PortletContainerServices.release();
541: }
542: }
543:
544: /**
545: * Sets the portal control structures.
546: * @param pcs1 the portal control structures
547: * @throws org.jasig.portal.PortalException
548: */
549: public void setPortalControlStructures(PortalControlStructures pcs1)
550: throws PortalException {
551: this .pcs = pcs1;
552: }
553:
554: /**
555: * Output channel content to the portal as raw characters
556: * @param pw a print writer
557: */
558: public void renderCharacters(PrintWriter pw) throws PortalException {
559:
560: if (!portletWindowInitialized) {
561: initPortletWindow();
562: }
563:
564: try {
565: String markupString = getMarkup();
566: pw.print(markupString);
567: } catch (Exception e) {
568: throw new PortalException(e);
569: }
570: }
571:
572: /**
573: * Output channel content to the portal. This version of the
574: * render method is normally not used since this is a "character channel".
575: * @param out a sax document handler
576: */
577: public void renderXML(ContentHandler out) throws PortalException {
578:
579: if (!portletWindowInitialized) {
580: initPortletWindow();
581: }
582:
583: try {
584: String markupString = getMarkup();
585:
586: // Output content. This assumes that markupString
587: // is well-formed. Consider changing to a character
588: // channel when it becomes available. Until we use the
589: // character channel, these <div> tags will be necessary.
590: SAXHelper.outputContent(out, "<div>" + markupString
591: + "</div>");
592:
593: } catch (Exception e) {
594: throw new PortalException(e);
595: }
596: }
597:
598: /**
599: * This is where we do the real work of getting the markup.
600: * This is called from both renderXML() and renderCharacters().
601: * @return markup representing channel content
602: */
603: protected synchronized String getMarkup() throws PortalException {
604: try {
605: PortletContainerServices.prepare(uniqueContainerName);
606:
607: final PortletEntity portletEntity = portletWindow
608: .getPortletEntity();
609: final PortletDefinition portletDef = portletEntity
610: .getPortletDefinition();
611: final HttpServletRequest baseRequest = pcs
612: .getHttpServletRequest();
613:
614: HttpServletRequest wrappedRequest = new ServletRequestImpl(
615: baseRequest, staticData.getPerson(), portletDef
616: .getInitSecurityRoleRefSet());
617:
618: //Wrap the request to scope attributes to this portlet instance
619: wrappedRequest = new PortletAttributeRequestWrapper(
620: wrappedRequest);
621:
622: //Set up request attributes (user info, portal session, etc...)
623: setupRequestAttributes(wrappedRequest);
624:
625: final StringWriter sw = new StringWriter();
626: HttpServletResponse wrappedResponse = ServletObjectAccess
627: .getStoredServletResponse(pcs
628: .getHttpServletResponse(), new PrintWriter(
629: sw));
630:
631: //Use the parameters from the last request so the portlet maintains it's state
632: final ChannelRuntimeData rd = runtimeData;
633:
634: if (!rd.isTargeted() && requestParams != null) {
635: wrappedRequest = new DummyParameterRequestWrapper(
636: wrappedRequest, requestParams);
637: }
638: //Hide the request parameters if this portlet isn't targeted
639: else {
640: wrappedRequest = new PortletParameterRequestWrapper(
641: wrappedRequest);
642:
643: requestParams = wrappedRequest.getParameterMap();
644: }
645:
646: portletContainer.renderPortlet(portletWindow,
647: wrappedRequest, wrappedResponse);
648:
649: // Track PortletSession object
650: PortletSession ps = (PortletSession) wrappedRequest
651: .getAttribute(PortletServlet.SESSION_MONITOR_ATTRIBUTE);
652: if (ps != null) {
653: portletSession = ps;
654: }
655:
656: //Support for the portlet modifying it's cache timeout
657: final Map properties = PropertyManager
658: .getRequestProperties(portletWindow, wrappedRequest);
659: final String[] exprCacheTimeStr = (String[]) properties
660: .get(RenderResponse.EXPIRATION_CACHE);
661:
662: if (exprCacheTimeStr != null && exprCacheTimeStr.length > 0) {
663: try {
664: Integer.parseInt(exprCacheTimeStr[0]); //Check for valid number
665: expirationCache = exprCacheTimeStr[0];
666: } catch (NumberFormatException nfe) {
667: log.error(
668: "The specified RenderResponse.EXPIRATION_CACHE value of ("
669: + exprCacheTimeStr
670: + ") is not a number.", nfe);
671: throw nfe;
672: }
673: }
674:
675: //Keep track of the last time the portlet was successfully rendered
676: lastRenderTime = System.currentTimeMillis();
677:
678: //Return the content
679: return sw.toString();
680:
681: } catch (Throwable t) {
682: log.error(t, t);
683: throw new PortalException(t);
684: } finally {
685: PortletContainerServices.release();
686: }
687: }
688:
689: /**
690: * Generates a channel cache key. The key scope is currently set to be
691: * instance-wide.
692:
693: * @return the channel cache key
694: */
695: public ChannelCacheKey generateKey() {
696: //Special handling of 'guest' actually resulted in causing a great many
697: //issues in the guest view for which it was originally intended, and was
698: //not used elsewhere. Removed as a part of a fix for UP-1869
699: ChannelCacheKey cck = instanceCacheKey;
700: return cck;
701: }
702:
703: /**
704: * Determines whether the cached content for this channel is still valid.
705: * <p>
706: * Return <code>true</code> when:<br>
707: * <ol>
708: * <li>We have not just received an event</li>
709: * <li>No runtime parameters are sent to the channel</li>
710: * <li>The focus hasn't switched.</li>
711: * </ol>
712: * Otherwise, return <code>false</code>.
713: * <p>
714: * In other words, cache the content in all cases <b>except</b>
715: * for when a user clicks a channel button, a link or form button within the channel,
716: * or the <i>focus</i> or <i>unfocus</i> button.
717: * @param validity the validity object
718: * @return <code>true</code> if the cache is still valid, otherwise <code>false</code>
719: */
720: public boolean isCacheValid(Object validity) {
721:
722: PortletEntity pe = portletWindow.getPortletEntity();
723: PortletDefinition pd = pe.getPortletDefinition();
724:
725: //Expiration based caching support for the portlet.
726: String exprCacheTimeStr = pd.getExpirationCache();
727: try {
728: if (expirationCache != null)
729: exprCacheTimeStr = expirationCache;
730:
731: int exprCacheTime = Integer.parseInt(exprCacheTimeStr);
732:
733: if (exprCacheTime == 0) {
734: return false;
735: } else if (exprCacheTime > 0) {
736: if ((lastRenderTime + (exprCacheTime * 1000)) < System
737: .currentTimeMillis())
738: return false;
739: }
740: } catch (Exception e) {
741: if (log.isWarnEnabled()) {
742: String portletId = staticData
743: .getParameter(portletDefinitionIdParamName);
744: log.warn("Error parsing portlet expiration time ("
745: + exprCacheTimeStr + ") for portlet ("
746: + portletId + ").", e);
747: }
748: }
749:
750: // Determine if the channel focus has changed
751: boolean previouslyFocused = focused;
752: focused = runtimeData.isRenderingAsRoot();
753: boolean focusHasSwitched = focused != previouslyFocused;
754:
755: // Dirty cache only when we receive an event, one or more request params, or a change in focus
756: boolean cacheValid = !(receivedEvent
757: || runtimeData.isTargeted() || focusHasSwitched);
758:
759: receivedEvent = false;
760: return cacheValid;
761: }
762:
763: //***************************************************************
764: // IDirectResponse methods
765: //***************************************************************
766:
767: public synchronized void setResponse(HttpServletResponse response) {
768: try {
769: PortletContainerServices.prepare(uniqueContainerName);
770:
771: final PortletEntity portletEntity = portletWindow
772: .getPortletEntity();
773: final PortletDefinition portletDef = portletEntity
774: .getPortletDefinition();
775: final HttpServletRequest baseRequest = pcs
776: .getHttpServletRequest();
777:
778: HttpServletRequest wrappedRequest = new ServletRequestImpl(
779: baseRequest, staticData.getPerson(), portletDef
780: .getInitSecurityRoleRefSet());
781:
782: //Wrap the request to scope attributes to this portlet instance
783: wrappedRequest = new PortletAttributeRequestWrapper(
784: wrappedRequest);
785:
786: //Set up request attributes (user info, portal session, etc...)
787: setupRequestAttributes(wrappedRequest);
788:
789: //Hide the request parameters if this portlet isn't targeted
790: wrappedRequest = new PortletParameterRequestWrapper(
791: wrappedRequest);
792:
793: //Since the portlet is rendering through IDirectResponse change the window state to "exclusive"
794: DynamicInformationProvider dip = InformationProviderAccess
795: .getDynamicProvider(pcs.getHttpServletRequest());
796: PortletActionProvider pap = dip
797: .getPortletActionProvider(portletWindow);
798: pap.changePortletWindowState(new WindowState("exclusive"));
799:
800: HttpServletResponse wrappedResponse = new OutputStreamResponseWrapper(
801: response);
802:
803: //render the portlet
804: portletContainer.renderPortlet(portletWindow,
805: wrappedRequest, wrappedResponse);
806:
807: // Track PortletSession object
808: PortletSession ps = (PortletSession) wrappedRequest
809: .getAttribute(PortletServlet.SESSION_MONITOR_ATTRIBUTE);
810: if (ps != null) {
811: portletSession = ps;
812: }
813:
814: //Ensure all the data gets written out
815: wrappedResponse.flushBuffer();
816: } catch (Throwable t) {
817: log.error(t, t);
818: } finally {
819: PortletContainerServices.release();
820: }
821: }
822:
823: //***************************************************************
824: // Helper methods
825: //***************************************************************
826:
827: /**
828: * Retrieves the users password by iterating over
829: * the user's security contexts and returning the first
830: * available cached password.
831: *
832: * @param baseContext The security context to start looking for a password from.
833: * @return the users password
834: */
835: private String getPassword(ISecurityContext baseContext) {
836: String password = null;
837: IOpaqueCredentials oc = baseContext.getOpaqueCredentials();
838:
839: if (oc instanceof NotSoOpaqueCredentials) {
840: NotSoOpaqueCredentials nsoc = (NotSoOpaqueCredentials) oc;
841: password = nsoc.getCredentials();
842: }
843:
844: // If still no password, loop through subcontexts to find cached credentials
845: Enumeration en = baseContext.getSubContexts();
846: while (password == null && en.hasMoreElements()) {
847: ISecurityContext subContext = (ISecurityContext) en
848: .nextElement();
849: password = this .getPassword(subContext);
850: }
851:
852: return password;
853: }
854:
855: /**
856: * Adds the appropriate information to the request attributes of the portlet.
857: *
858: * This is an extension point. You can override this method to set other
859: * request attributes.
860: *
861: * @param request The request to add the attributes to
862: */
863: protected void setupRequestAttributes(
864: final HttpServletRequest request) {
865: //Add the user information map
866: request.setAttribute(PortletRequest.USER_INFO, userInfo);
867: }
868:
869: /**
870: * Get the Map of portlet user attribute names to portlet user attribute values.
871: *
872: * This is an extension point. You can extend CPortletAdapter and override this
873: * method to implement the particular user attribute Map creation strategy that
874: * you need to implement. Such strategies might rename uPortal
875: * user attributes to names that your particular portlet knows how to consume,
876: * transform the user attribute values to forms expected by your portlet, add
877: * additional attributes, convey a CAS proxy ticket or other security token.
878: * This extension point is the way to accomodate the particular user attributes
879: * particular portlets require.
880: *
881: * The default implementation of this method includes in the userInfo Map
882: * those uPortal IPerson attributes matching entries in the list of attributes the
883: * Portlet declared it wanted. Additionally, the default implementation copies
884: * the cached user password if the Portlet declares it wants the user attribute
885: * 'password'.
886: *
887: * @param staticData data associated with the particular instance of the portlet window for the particular
888: * user session
889: * @param userAttributes the user attributes requested by the Portlet
890: * @return a Map from portlet user attribute names to portlet user attribute values.
891: */
892: protected Map getUserInfo(ChannelStaticData staticData,
893: UserAttributeListImpl userAttributes) {
894:
895: final String PASSWORD_ATTR = "password";
896:
897: Map userInfo = new HashMap();
898: IPerson person = staticData.getPerson();
899: if (person.getSecurityContext().isAuthenticated()) {
900:
901: // for each attribute the Portlet requested
902: for (Iterator iter = userAttributes.iterator(); iter
903: .hasNext();) {
904: UserAttributeImpl userAttribute = (UserAttributeImpl) iter
905: .next();
906: String attName = userAttribute.getName();
907: String attValue = (String) person.getAttribute(attName);
908: if ((attValue == null || attValue.equals(""))
909: && attName.equals(PASSWORD_ATTR)) {
910: attValue = getPassword(person.getSecurityContext());
911: }
912: userInfo.put(attName, attValue);
913: }
914: }
915: return userInfo;
916: }
917: }
|