0001: /* Copyright 2001 The JA-SIG Collaborative. All rights reserved.
0002: * See license distributed with this file and
0003: * available online at http://www.uportal.org/license.html
0004: */
0005:
0006: package org.jasig.portal;
0007:
0008: import java.io.IOException;
0009: import java.io.PrintWriter;
0010: import java.io.StringWriter;
0011: import java.util.Collections;
0012: import java.util.Enumeration;
0013: import java.util.Hashtable;
0014: import java.util.Map;
0015: import java.util.Properties;
0016:
0017: import javax.servlet.http.HttpServletRequest;
0018: import javax.servlet.http.HttpServletResponse;
0019: import javax.servlet.http.HttpSession;
0020: import javax.servlet.http.HttpSessionBindingEvent;
0021: import javax.servlet.http.HttpSessionBindingListener;
0022: import javax.xml.transform.ErrorListener;
0023: import javax.xml.transform.Transformer;
0024: import javax.xml.transform.TransformerException;
0025: import javax.xml.transform.sax.SAXResult;
0026: import javax.xml.transform.sax.TransformerHandler;
0027:
0028: import org.apache.commons.logging.Log;
0029: import org.apache.commons.logging.LogFactory;
0030: import org.jasig.portal.car.CarResources;
0031: import org.jasig.portal.container.services.information.PortletStateManager;
0032: import org.jasig.portal.events.EventPublisherLocator;
0033: import org.jasig.portal.events.support.UserSessionCreatedPortalEvent;
0034: import org.jasig.portal.events.support.UserSessionDestroyedPortalEvent;
0035: import org.jasig.portal.i18n.LocaleManager;
0036: import org.jasig.portal.layout.IUserLayoutManager;
0037: import org.jasig.portal.layout.TransientUserLayoutManagerWrapper;
0038: import org.jasig.portal.layout.UserLayoutParameterProcessor;
0039: import org.jasig.portal.layout.alm.IALFolderDescription;
0040: import org.jasig.portal.layout.alm.IAggregatedUserLayoutManager;
0041: import org.jasig.portal.layout.node.IUserLayoutNodeDescription;
0042: import org.jasig.portal.properties.PropertiesManager;
0043: import org.jasig.portal.security.IPermission;
0044: import org.jasig.portal.security.IPerson;
0045: import org.jasig.portal.serialize.BaseMarkupSerializer;
0046: import org.jasig.portal.serialize.CachingSerializer;
0047: import org.jasig.portal.serialize.OutputFormat;
0048: import org.jasig.portal.serialize.XMLSerializer;
0049: import org.jasig.portal.services.GroupService;
0050: import org.jasig.portal.tools.versioning.Version;
0051: import org.jasig.portal.tools.versioning.VersionsManager;
0052: import org.jasig.portal.utils.MovingAverage;
0053: import org.jasig.portal.utils.MovingAverageSample;
0054: import org.jasig.portal.utils.ResourceLoader;
0055: import org.jasig.portal.utils.SAX2BufferImpl;
0056: import org.jasig.portal.utils.SAX2DuplicatingFilterImpl;
0057: import org.jasig.portal.utils.SoftHashMap;
0058: import org.jasig.portal.utils.URLUtil;
0059: import org.jasig.portal.utils.XSLT;
0060: import org.xml.sax.ContentHandler;
0061: import org.xml.sax.XMLReader;
0062:
0063: import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger;
0064:
0065: /**
0066: * A class handling holding all user state information. The class is also reponsible for
0067: * request processing and orchestrating the entire rendering procedure.
0068: * (this is a replacement for the good old LayoutBean class)
0069: * @author Peter Kharchenko {@link <a href="mailto:pkharchenko@interactivebusiness.com"">pkharchenko@interactivebusiness.com"</a>}
0070: * @version $Revision: 42516 $
0071: */
0072: public class UserInstance implements HttpSessionBindingListener {
0073:
0074: private static final Log log = LogFactory
0075: .getLog(UserInstance.class);
0076:
0077: public static final int guestUserId = 1;
0078:
0079: // To debug structure and/or theme transformations, set these to true
0080: // and the XML fed to those transformations will be printed to the log.
0081: private static final boolean logXMLBeforeStructureTransformation = PropertiesManager
0082: .getPropertyAsBoolean(UserInstance.class.getName()
0083: + ".log_xml_before_structure_transformation");
0084: private static final boolean logXMLBeforeThemeTransformation = PropertiesManager
0085: .getPropertyAsBoolean(UserInstance.class.getName()
0086: + ".log_xml_before_theme_transformation");;
0087:
0088: // manages layout and preferences
0089: private IUserPreferencesManager uPreferencesManager;
0090: // manages channel instances and channel rendering
0091: private ChannelManager channelManager;
0092: // manages locale
0093: private LocaleManager localeManager;
0094:
0095: // contains information relating client names to media and mime types
0096: private static final MediaManager MEDIAMANAGER = MediaManager
0097: .getMediaManager();
0098:
0099: // system profile mapper standalone instance
0100: private StandaloneChannelRenderer p_browserMapper = null;
0101:
0102: // lock preventing concurrent rendering
0103: private Object p_rendering_lock;
0104:
0105: // global rendering cache
0106: public static final boolean CACHE_ENABLED = PropertiesManager
0107: .getPropertyAsBoolean("org.jasig.portal.UserInstance.cache_enabled");
0108: private static final int SYSTEM_XSLT_CACHE_MIN_SIZE = PropertiesManager
0109: .getPropertyAsInt("org.jasig.portal.UserInstance.system_xslt_cache_min_size");
0110: private static final int SYSTEM_CHARACTER_BLOCK_CACHE_MIN_SIZE = PropertiesManager
0111: .getPropertyAsInt("org.jasig.portal.UserInstance.system_character_block_cache_min_size");
0112: public static final boolean CHARACTER_CACHE_ENABLED = PropertiesManager
0113: .getPropertyAsBoolean("org.jasig.portal.UserInstance.character_cache_enabled");
0114:
0115: // a string that will be used to designate user layout root node in .uP files
0116: public static final String USER_LAYOUT_ROOT_NODE = IALFolderDescription.ROOT_FOLDER_ID;
0117:
0118: private static final String WORKER_PROPERTIES_FILE_NAME = "/properties/worker.properties";
0119: private static Properties workerProperties;
0120:
0121: // string that defines which character set to use for content
0122: private static final String CHARACTER_SET = "UTF-8";
0123:
0124: private static final Map systemCache = Collections
0125: .synchronizedMap(new SoftHashMap(SYSTEM_XSLT_CACHE_MIN_SIZE));
0126: private static final Map systemCharacterCache = Collections
0127: .synchronizedMap(new SoftHashMap(
0128: SYSTEM_CHARACTER_BLOCK_CACHE_MIN_SIZE));
0129:
0130: protected IPerson person;
0131:
0132: // Metric counters
0133: public static final AtomicInteger userSessions = new AtomicInteger();
0134: private static final MovingAverage renderTimes = new MovingAverage();
0135: public static MovingAverageSample lastRender = new MovingAverageSample();
0136:
0137: public UserInstance(IPerson person) {
0138: this .person = person;
0139: }
0140:
0141: /**
0142: * Prepares for and initates the rendering cycle.
0143: * @param req the servlet request object
0144: * @param res the servlet response object
0145: */
0146: public void writeContent(HttpServletRequest req,
0147: HttpServletResponse res) throws PortalException {
0148: // instantiate user layout manager and check to see if the profile mapping has been established
0149: if (p_browserMapper != null) {
0150: try {
0151: p_browserMapper.prepare(req);
0152: } catch (Exception e) {
0153: throw new PortalException(e);
0154: }
0155: }
0156:
0157: // instantiate locale manager (uPortal i18n)
0158: if (localeManager == null) {
0159: localeManager = new LocaleManager(person, req
0160: .getHeader("Accept-Language"));
0161: }
0162:
0163: if (uPreferencesManager == null
0164: || uPreferencesManager.isUserAgentUnmapped()) {
0165: uPreferencesManager = new UserPreferencesManager(req, this
0166: .getPerson(), localeManager);
0167: } else {
0168: // p_browserMapper is no longer needed
0169: p_browserMapper = null;
0170: }
0171:
0172: if (uPreferencesManager.isUserAgentUnmapped()) {
0173: // unmapped browser
0174: if (p_browserMapper == null) {
0175: p_browserMapper = new org.jasig.portal.channels.CSelectSystemProfile();
0176: p_browserMapper.initialize(new Hashtable(),
0177: "CSelectSystemProfile", true, true, false,
0178: 10000, getPerson());
0179: }
0180: try {
0181: p_browserMapper.render(req, res);
0182: } catch (PortalException pe) {
0183: throw new PortalException(pe);
0184: } catch (Throwable t) {
0185: // something went wrong trying to show CSelectSystemProfileChannel
0186: log
0187: .error("UserInstance::writeContent() : CSelectSystemProfileChannel.render() threw: "
0188: + t);
0189: throw new PortalException(
0190: "CSelectSystemProfileChannel.render() threw: "
0191: + t);
0192: }
0193: // don't go any further!
0194: return;
0195: }
0196:
0197: // if we got to this point, we can proceed with the rendering
0198: if (channelManager == null) {
0199: channelManager = new ChannelManager(uPreferencesManager);
0200: uPreferencesManager.getUserLayoutManager()
0201: .addLayoutEventListener(channelManager);
0202: p_rendering_lock = new Object();
0203: }
0204:
0205: //If the request is for a portlet and an action request run that portlet's processAction
0206: final boolean didAction = this .processPortletActionIfNecessary(
0207: req, res);
0208:
0209: //If an action was performed a redirect will be issued to the HttpResponse so this request won't render anything
0210: if (didAction)
0211: return;
0212:
0213: // Begin the rendering process
0214: renderState(req, res, this .channelManager, this .localeManager,
0215: uPreferencesManager, p_rendering_lock);
0216: }
0217:
0218: /**
0219: * Determines if this request is triggering a portlet's processAction method,
0220: * and if so, starts the rendering cycle early which has the effect of
0221: * feeding the portlet control structures and channel runtime data to the
0222: * portlet adapter so that the portlet's process action
0223: * method gets a chance to complete before we continue.
0224: * This allows portlets to affect things like window states and be able to
0225: * redirect to another web page as required by the portlet specification.
0226: * @param req the http servlet request
0227: * @param res the http servlet response
0228: */
0229: protected boolean processPortletActionIfNecessary(
0230: HttpServletRequest req, HttpServletResponse res) {
0231: if (this .channelManager != null) {
0232: boolean isPortletAction = (new Boolean(req
0233: .getParameter(PortletStateManager.ACTION)))
0234: .booleanValue();
0235: if (isPortletAction) {
0236: try {
0237: this .channelManager.startRenderingCycle(req, res,
0238: new UPFileSpec(req));
0239: } finally {
0240: this .channelManager.finishedRenderingCycle();
0241: }
0242: }
0243:
0244: return isPortletAction;
0245: }
0246:
0247: return false;
0248: }
0249:
0250: /**
0251: * <code>renderState</code> method orchestrates the rendering pipeline.
0252: * @param req the <code>HttpServletRequest</code>
0253: * @param res the <code>HttpServletResponse</code>
0254: * @param channelManager the <code>ChannelManager</code> instance
0255: * @param upm the <code>IUserPreferencesManager</code> instance
0256: * @param rendering_lock a lock for rendering on a single user
0257: * @exception PortalException if an error occurs
0258: */
0259: public void renderState(HttpServletRequest req,
0260: HttpServletResponse res, ChannelManager channelManager,
0261: LocaleManager localeManager, IUserPreferencesManager upm,
0262: Object rendering_lock) throws PortalException {
0263: uPreferencesManager = upm;
0264: // process possible worker dispatch
0265: if (!processWorkerDispatch(req, res, channelManager)) {
0266: final long startTime = System.currentTimeMillis();
0267: synchronized (rendering_lock) {
0268: // This function does ALL the content gathering/presentation work.
0269: // The following filter sequence is processed:
0270: // userLayoutXML (in UserPreferencesManager)
0271: // |
0272: // incorporate StructureAttributes
0273: // |
0274: // Structure transformation
0275: // + (buffering step)
0276: // ChannelRendering Buffer
0277: // |
0278: // ThemeAttributesIncorporation Filter
0279: // |
0280: // Theme Transformation
0281: // |
0282: // ChannelIncorporation filter
0283: // |
0284: // Serializer (XHTML/WML/HTML/etc.)
0285: // |
0286: // JspWriter
0287: //
0288:
0289: try {
0290:
0291: // call layout manager to process all user-preferences-related request parameters
0292: // this will update UserPreference object contained by UserPreferencesManager, so that
0293: // appropriate attribute incorporation filters and parameter tables can be constructed.
0294: uPreferencesManager
0295: .processUserPreferencesParameters(req);
0296:
0297: // determine uPElement (optimistic prediction) --begin
0298: // We need uPElement for ChannelManager.setReqNRes() call. That call will distribute uPElement
0299: // to Privileged channels. We assume that Privileged channels are smart enough not to delete
0300: // themselves in the detach mode !
0301:
0302: // In general transformations will start at the userLayoutRoot node, unless
0303: // we are rendering something in a detach mode.
0304: IUserLayoutNodeDescription rElement = null;
0305: // see if an old detach target exists in the servlet path
0306:
0307: UPFileSpec upfs = new UPFileSpec(req);
0308: String rootNodeId = upfs.getMethodNodeId();
0309: if (rootNodeId == null) {
0310: rootNodeId = USER_LAYOUT_ROOT_NODE;
0311: }
0312:
0313: // give channels the current locale manager
0314: channelManager.setLocaleManager(localeManager);
0315:
0316: // see if a new root target has been specified
0317: String newRootNodeId = req
0318: .getParameter("uP_detach_target");
0319:
0320: // set optimistic uPElement value
0321: UPFileSpec uPElement = new UPFileSpec(
0322: PortalSessionManager.INTERNAL_TAG_VALUE,
0323: UPFileSpec.RENDER_METHOD, rootNodeId, null,
0324: null);
0325:
0326: if (newRootNodeId != null) {
0327: // set a new root
0328: uPElement.setMethodNodeId(newRootNodeId);
0329: }
0330:
0331: IUserLayoutManager ulm = uPreferencesManager
0332: .getUserLayoutManager();
0333:
0334: // process events that have to be handed directly to the userPreferencesManager.
0335: // (examples of such events are "remove channel", "minimize channel", etc.
0336: // basically things that directly affect the userLayout structure)
0337: try {
0338: UserPreferences userPrefs = uPreferencesManager
0339: .getUserPreferences();
0340: UserLayoutParameterProcessor processor = new UserLayoutParameterProcessor(
0341: ulm, userPrefs);
0342: processor.processUserLayoutParameters(req, res,
0343: channelManager, person);
0344: } catch (PortalException pe) {
0345: log.error(
0346: "Failure processing user parameters.",
0347: pe);
0348: }
0349:
0350: // determine uPElement (optimistic prediction) --end
0351:
0352: // set up the channel manager
0353:
0354: channelManager.startRenderingCycle(req, res,
0355: uPElement);
0356:
0357: // after this point the layout is determined
0358:
0359: UserPreferences userPreferences = uPreferencesManager
0360: .getUserPreferences();
0361: StructureStylesheetDescription ssd = uPreferencesManager
0362: .getStructureStylesheetDescription();
0363: ThemeStylesheetDescription tsd = uPreferencesManager
0364: .getThemeStylesheetDescription();
0365:
0366: // verify upElement and determine rendering root --begin
0367: if (newRootNodeId != null
0368: && (!newRootNodeId.equals(rootNodeId))) {
0369: // see if the new detach traget is valid
0370: try {
0371: rElement = ulm.getNode(newRootNodeId);
0372: } catch (PortalException e) {
0373: rElement = null;
0374: }
0375:
0376: if (rElement != null) {
0377: // valid new root id was specified. need to redirect
0378: // peterk: should we worry about forwarding
0379: // parameters here ? or those passed with detach
0380: // always get sacked ?
0381: // Andreas: Forwarding parameters with detach
0382: // are not lost anymore with the URLUtil class.
0383:
0384: // Skip the uP_detach_target parameter since
0385: // it has already been processed
0386: String[] skipParams = new String[] { "uP_detach_target" };
0387:
0388: try {
0389: URLUtil.redirect(req, res,
0390: newRootNodeId, true,
0391: skipParams, CHARACTER_SET);
0392: } catch (PortalException pe) {
0393: log
0394: .error(
0395: "UserInstance::renderState() : PortalException occurred while redirecting",
0396: pe);
0397: }
0398: return;
0399: }
0400: }
0401: // else ignore new id, proceed with the old root target (or the lack of such)
0402: if (rootNodeId != null) {
0403: // LogService.log(LogService.DEBUG,"UserInstance::renderState() : uP_detach_target=\""+rootNodeId+"\".");
0404: try {
0405: rElement = ulm.getNode(rootNodeId);
0406: } catch (PortalException e) {
0407: rElement = null;
0408: }
0409: }
0410: // if we haven't found root node so far, set it to the userLayoutRoot
0411: if (rElement == null) {
0412: rootNodeId = USER_LAYOUT_ROOT_NODE;
0413: }
0414:
0415: // update the render target
0416: uPElement.setMethodNodeId(rootNodeId);
0417:
0418: // inform channel manager about the new uPElement value
0419: channelManager.setUPElement(uPElement);
0420: // verify upElement and determine rendering root --begin
0421:
0422: // Disable page caching
0423: res.setHeader("pragma", "no-cache");
0424: res.setHeader("Cache-Control",
0425: "no-cache, max-age=0, must-revalidate");
0426: res.setDateHeader("Expires", 0);
0427: // set the response mime type
0428: res.setContentType(tsd.getMimeType() + "; charset="
0429: + CHARACTER_SET);
0430: // obtain the writer - res.getWriter() must occur after res.setContentType()
0431: PrintWriter out = res.getWriter();
0432: // get a serializer appropriate for the target media
0433: BaseMarkupSerializer markupSerializer = MEDIAMANAGER
0434: .getSerializerByName(tsd
0435: .getSerializerName(), out);
0436: // set up the serializer
0437: markupSerializer.asContentHandler();
0438: // see if we can use character caching
0439: boolean ccaching = (CHARACTER_CACHE_ENABLED && (markupSerializer instanceof CachingSerializer));
0440: channelManager.setCharacterCaching(ccaching);
0441: // pass along the serializer name
0442: channelManager.setSerializerName(tsd
0443: .getSerializerName());
0444: // initialize ChannelIncorporationFilter
0445: // ChannelIncorporationFilter cif = new ChannelIncorporationFilter(markupSerializer, channelManager); // this should be slightly faster then the ccaching version, may be worth adding support later
0446: CharacterCachingChannelIncorporationFilter cif = new CharacterCachingChannelIncorporationFilter(
0447: markupSerializer,
0448: channelManager,
0449: UserInstance.CACHE_ENABLED
0450: && UserInstance.CHARACTER_CACHE_ENABLED);
0451:
0452: String cacheKey = null;
0453: boolean output_produced = false;
0454: if (UserInstance.CACHE_ENABLED) {
0455: boolean ccache_exists = false;
0456: // obtain the cache key
0457: cacheKey = constructCacheKey(this .getPerson(),
0458: rootNodeId);
0459: if (ccaching) {
0460: // obtain character cache
0461: CharacterCacheEntry cCache = (CharacterCacheEntry) this .systemCharacterCache
0462: .get(cacheKey);
0463: if (cCache != null
0464: && cCache.channelIds != null
0465: && cCache.systemBuffers != null) {
0466: ccache_exists = true;
0467: if (log.isDebugEnabled())
0468: log
0469: .debug("UserInstance::renderState() : retreived transformation character block cache for a key \""
0470: + cacheKey + "\"");
0471: // start channel threads
0472: for (int i = 0; i < cCache.channelIds
0473: .size(); i++) {
0474: String channelSubscribeId = (String) cCache.channelIds
0475: .get(i);
0476: if (channelSubscribeId != null) {
0477: try {
0478: channelManager
0479: .startChannelRendering(channelSubscribeId);
0480: } catch (PortalException e) {
0481: log
0482: .error("UserInstance::renderState() : unable to start rendering channel (subscribeId=\""
0483: + channelSubscribeId
0484: + "\", user="
0485: + person
0486: .getID()
0487: + " layoutId="
0488: + uPreferencesManager
0489: .getCurrentProfile()
0490: .getLayoutId()
0491: + e
0492: .getCause()
0493: .toString());
0494: }
0495: } else {
0496: log
0497: .error("UserInstance::renderState() : channel entry "
0498: + Integer
0499: .toString(i)
0500: + " in character cache is invalid (user="
0501: + person
0502: .getID()
0503: + ")!");
0504: }
0505: }
0506: channelManager
0507: .commitToRenderingChannelSet();
0508:
0509: // go through the output loop
0510: int ccsize = cCache.systemBuffers
0511: .size();
0512: if (cCache.channelIds.size() != ccsize - 1) {
0513: log
0514: .error("UserInstance::renderState() : channelIds character cache has invalid size ! "
0515: + "UserInstance::renderState() : ccache contains "
0516: + cCache.systemBuffers
0517: .size()
0518: + " system buffers and "
0519: + cCache.channelIds
0520: .size()
0521: + " channel entries");
0522:
0523: }
0524: CachingSerializer cSerializer = (CachingSerializer) markupSerializer;
0525: cSerializer.setDocumentStarted(true);
0526:
0527: for (int sb = 0; sb < ccsize - 1; sb++) {
0528: cSerializer
0529: .printRawCharacters((String) cCache.systemBuffers
0530: .get(sb));
0531: if (log.isDebugEnabled()) {
0532: log
0533: .debug("----------printing frame piece "
0534: + Integer
0535: .toString(sb));
0536: log
0537: .debug((String) cCache.systemBuffers
0538: .get(sb));
0539: }
0540:
0541: // get channel output
0542: String channelSubscribeId = (String) cCache.channelIds
0543: .get(sb);
0544: channelManager.outputChannel(
0545: channelSubscribeId,
0546: markupSerializer);
0547: }
0548:
0549: // print out the last block
0550:
0551: cSerializer
0552: .printRawCharacters((String) cCache.systemBuffers
0553: .get(ccsize - 1));
0554: if (log.isDebugEnabled()) {
0555: log
0556: .debug("----------printing frame piece "
0557: + Integer
0558: .toString(ccsize - 1));
0559: log
0560: .debug((String) cCache.systemBuffers
0561: .get(ccsize - 1));
0562: }
0563:
0564: cSerializer.flush();
0565: output_produced = true;
0566: }
0567: }
0568: // if this failed, try XSLT cache
0569: if ((!ccaching) || (!ccache_exists)) {
0570: // obtain XSLT cache
0571:
0572: SAX2BufferImpl cachedBuffer = (SAX2BufferImpl) this .systemCache
0573: .get(cacheKey);
0574: if (cachedBuffer != null) {
0575: // replay the buffer to channel incorporation filter
0576: if (log.isDebugEnabled())
0577: log
0578: .debug("UserInstance::renderState() : retreived XSLT transformation cache for a key \""
0579: + cacheKey + "\"");
0580: // attach rendering buffer downstream of the cached buffer
0581: ChannelRenderingBuffer crb = new ChannelRenderingBuffer(
0582: (XMLReader) cachedBuffer,
0583: channelManager, ccaching);
0584: // attach channel incorporation filter downstream of the channel rendering buffer
0585: cif.setParent(crb);
0586: crb.setOutputAtDocumentEnd(true);
0587: cachedBuffer
0588: .outputBuffer((ContentHandler) crb);
0589:
0590: output_produced = true;
0591: }
0592: }
0593: }
0594: // fallback on the regular rendering procedure
0595: if (!output_produced) {
0596:
0597: // obtain transformer handlers for both structure and theme stylesheets
0598: TransformerHandler ssth = XSLT
0599: .getTransformerHandler(ResourceLoader
0600: .getResourceAsURL(
0601: UserInstance.class,
0602: ssd.getStylesheetURI())
0603: .toString());
0604: TransformerHandler tsth = XSLT
0605: .getTransformerHandler(tsd
0606: .getStylesheetURI(),
0607: localeManager.getLocales(),
0608: this );
0609:
0610: // obtain transformer references from the handlers
0611: Transformer sst = ssth.getTransformer();
0612: sst.setErrorListener(cErrListener);
0613: Transformer tst = tsth.getTransformer();
0614: tst.setErrorListener(cErrListener);
0615:
0616: // initialize ChannelRenderingBuffer and attach it downstream of the structure transformer
0617: ChannelRenderingBuffer crb = new ChannelRenderingBuffer(
0618: channelManager, ccaching);
0619: ssth.setResult(new SAXResult(crb));
0620:
0621: // determine and set the stylesheet params
0622: // prepare .uP element and detach flag to be passed to the stylesheets
0623: // Including the context path in front of uPElement is necessary for phone.com browsers to work
0624: sst.setParameter("baseActionURL", uPElement
0625: .getUPFile());
0626: // construct idempotent version of the uPElement
0627: UPFileSpec uPIdempotentElement = new UPFileSpec(
0628: uPElement);
0629: uPIdempotentElement
0630: .setTagId(PortalSessionManager.IDEMPOTENT_URL_TAG);
0631: sst.setParameter("baseIdempotentActionURL",
0632: uPElement.getUPFile());
0633:
0634: Hashtable supTable = userPreferences
0635: .getStructureStylesheetUserPreferences()
0636: .getParameterValues();
0637: for (Enumeration e = supTable.keys(); e
0638: .hasMoreElements();) {
0639: String pName = (String) e.nextElement();
0640: String pValue = (String) supTable
0641: .get(pName);
0642: if (log.isDebugEnabled())
0643: log
0644: .debug("UserInstance::renderState() : setting sparam \""
0645: + pName
0646: + "\"=\""
0647: + pValue + "\".");
0648: sst.setParameter(pName, pValue);
0649: }
0650:
0651: // all the parameters are set up, fire up structure transformation
0652:
0653: // filter to fill in channel/folder attributes for the "structure" transformation.
0654: StructureAttributesIncorporationFilter saif = new StructureAttributesIncorporationFilter(
0655: ssth,
0656: userPreferences
0657: .getStructureStylesheetUserPreferences());
0658:
0659: // This is a debug statement that will print out XML incoming to the
0660: // structure transformation to a log file serializer to a printstream
0661: StringWriter dbwr1 = null;
0662: OutputFormat outputFormat = null;
0663: if (logXMLBeforeStructureTransformation) {
0664: dbwr1 = new StringWriter();
0665: outputFormat = new OutputFormat();
0666: outputFormat.setIndenting(true);
0667: XMLSerializer dbser1 = new XMLSerializer(
0668: dbwr1, outputFormat);
0669: SAX2DuplicatingFilterImpl dupl1 = new SAX2DuplicatingFilterImpl(
0670: ssth, dbser1);
0671: dupl1.setParent(saif);
0672: }
0673:
0674: // if operating in the detach mode, need wrap everything
0675: // in a document node and a <layout_fragment> node
0676: boolean detachMode = !rootNodeId
0677: .equals(USER_LAYOUT_ROOT_NODE);
0678: if (detachMode) {
0679: saif.startDocument();
0680: saif
0681: .startElement(
0682: "",
0683: "layout_fragment",
0684: "layout_fragment",
0685: new org.xml.sax.helpers.AttributesImpl());
0686:
0687: // emptyt.transform(new DOMSource(rElement),new SAXResult(new ChannelSAXStreamFilter((ContentHandler)saif)));
0688: if (rElement == null) {
0689: ulm
0690: .getUserLayout(new ChannelSAXStreamFilter(
0691: (ContentHandler) saif));
0692: } else {
0693: ulm.getUserLayout(rElement.getId(),
0694: new ChannelSAXStreamFilter(
0695: (ContentHandler) saif));
0696: }
0697:
0698: saif.endElement("", "layout_fragment",
0699: "layout_fragment");
0700: saif.endDocument();
0701: } else {
0702: if (rElement == null) {
0703: ulm
0704: .getUserLayout((ContentHandler) saif);
0705: } else {
0706: ulm.getUserLayout(rElement.getId(),
0707: (ContentHandler) saif);
0708: }
0709: // emptyt.transform(new DOMSource(rElement),new SAXResult((ContentHandler)saif));
0710: }
0711: // all channels should be rendering now
0712:
0713: // Debug piece to print out the recorded pre-structure transformation XML
0714: if (logXMLBeforeStructureTransformation) {
0715: if (log.isDebugEnabled())
0716: log
0717: .debug("UserInstance::renderState() : XML incoming to the structure transformation :\n\n"
0718: + dbwr1.toString()
0719: + "\n\n");
0720: }
0721:
0722: // prepare for the theme transformation
0723:
0724: // set up of the parameters
0725: tst.setParameter("baseActionURL", uPElement
0726: .getUPFile());
0727: tst.setParameter("baseIdempotentActionURL",
0728: uPIdempotentElement.getUPFile());
0729:
0730: Hashtable tupTable = userPreferences
0731: .getThemeStylesheetUserPreferences()
0732: .getParameterValues();
0733: for (Enumeration e = tupTable.keys(); e
0734: .hasMoreElements();) {
0735: String pName = (String) e.nextElement();
0736: String pValue = (String) tupTable
0737: .get(pName);
0738: if (log.isDebugEnabled())
0739: log
0740: .debug("UserInstance::renderState() : setting tparam \""
0741: + pName
0742: + "\"=\""
0743: + pValue + "\".");
0744: tst.setParameter(pName, pValue);
0745: }
0746:
0747: VersionsManager versionsManager = VersionsManager
0748: .getInstance();
0749: Version[] versions = versionsManager
0750: .getVersions();
0751:
0752: for (Version version : versions) {
0753: String paramName = "version-"
0754: + version.getFname();
0755: tst.setParameter(paramName, version
0756: .dottedTriple());
0757: }
0758:
0759: // the uP_productAndVersion stylesheet parameter is deprecated
0760: // instead use the more generic "version-UP_VERSION" generated from the
0761: // framework's functional name when all versions are pulled immediately
0762: // above.
0763: Version uPortalVersion = versionsManager
0764: .getVersion(IPermission.PORTAL_FRAMEWORK);
0765: tst
0766: .setParameter("uP_productAndVersion",
0767: "uPortal "
0768: + uPortalVersion
0769: .dottedTriple());
0770:
0771: // tst.setParameter("locale", localeManager.getLocaleFromSessionParameter());
0772:
0773: // initialize a filter to fill in channel attributes for the "theme" (second) transformation.
0774: // attach it downstream of the channel rendering buffer
0775: ThemeAttributesIncorporationFilter taif = new ThemeAttributesIncorporationFilter(
0776: (XMLReader) crb,
0777: userPreferences
0778: .getThemeStylesheetUserPreferences());
0779: // attach theme transformation downstream of the theme attribute incorporation filter
0780: taif.setAllHandlers(tsth);
0781:
0782: // This is a debug statement that will print out XML incoming to the
0783: // theme transformation to a log file serializer to a printstream
0784: StringWriter dbwr2 = null;
0785: if (logXMLBeforeThemeTransformation) {
0786: dbwr2 = new StringWriter();
0787: XMLSerializer dbser2 = new XMLSerializer(
0788: dbwr2, outputFormat);
0789: SAX2DuplicatingFilterImpl dupl2 = new SAX2DuplicatingFilterImpl(
0790: tsth, dbser2);
0791: dupl2.setParent(taif);
0792: }
0793:
0794: if (UserInstance.CACHE_ENABLED && !ccaching) {
0795: // record cache
0796: // attach caching buffer downstream of the theme transformer
0797: SAX2BufferImpl newCache = new SAX2BufferImpl();
0798: tsth.setResult(new SAXResult(newCache));
0799:
0800: // attach channel incorporation filter downstream of the caching buffer
0801: cif.setParent(newCache);
0802:
0803: systemCache.put(cacheKey, newCache);
0804: newCache.setOutputAtDocumentEnd(true);
0805: if (log.isDebugEnabled())
0806: log
0807: .debug("UserInstance::renderState() : recorded transformation cache with key \""
0808: + cacheKey + "\"");
0809: } else {
0810: // attach channel incorporation filter downstream of the theme transformer
0811: tsth.setResult(new SAXResult(cif));
0812: }
0813: // fire up theme transformation
0814: crb.stopBuffering();
0815: crb.outputBuffer();
0816: crb.clearBuffer();
0817:
0818: // Debug piece to print out the recorded pre-theme transformation XML
0819: if (logXMLBeforeThemeTransformation
0820: && log.isDebugEnabled()) {
0821: log
0822: .debug("UserInstance::renderState() : XML incoming to the theme transformation :\n\n"
0823: + dbwr2.toString() + "\n\n");
0824: }
0825:
0826: if (UserInstance.CACHE_ENABLED && ccaching) {
0827: // save character block cache
0828: CharacterCacheEntry ce = new CharacterCacheEntry();
0829: ce.systemBuffers = cif
0830: .getSystemCCacheBlocks();
0831: ce.channelIds = cif.getChannelIdBlocks();
0832: if (ce.systemBuffers == null
0833: || ce.channelIds == null) {
0834: log
0835: .error("UserInstance::renderState() : CharacterCachingChannelIncorporationFilter returned invalid cache entries!");
0836: } else {
0837: // record cache
0838: systemCharacterCache.put(cacheKey, ce);
0839: if (log.isDebugEnabled()) {
0840: log
0841: .debug("UserInstance::renderState() : recorded transformation character block cache with key \""
0842: + cacheKey + "\"");
0843:
0844: log
0845: .debug("Printing transformation cache system blocks:");
0846: for (int i = 0; i < ce.systemBuffers
0847: .size(); i++) {
0848: log.debug("----------piece "
0849: + Integer.toString(i));
0850: log
0851: .debug((String) ce.systemBuffers
0852: .get(i));
0853: }
0854: log
0855: .debug("Printing transformation cache channel IDs:");
0856: for (int i = 0; i < ce.channelIds
0857: .size(); i++) {
0858: log
0859: .debug("----------channel entry "
0860: + Integer
0861: .toString(i));
0862: log
0863: .debug((String) ce.channelIds
0864: .get(i));
0865: }
0866: }
0867: }
0868: }
0869:
0870: }
0871: // signal the end of the rendering round
0872: channelManager.finishedRenderingCycle();
0873: } catch (PortalException pe) {
0874: throw pe;
0875: } catch (Exception e) {
0876: throw new PortalException(e);
0877: } finally {
0878: lastRender = renderTimes.add(System
0879: .currentTimeMillis()
0880: - startTime);
0881: }
0882: }
0883: }
0884: }
0885:
0886: /**
0887: * Class providing exposure to causal exception information for exceptions
0888: * incurred during transformation.
0889: *
0890: * @author Mark Boyd
0891: *
0892: */
0893: private static class TransformErrorListener implements
0894: ErrorListener {
0895:
0896: public void error(TransformerException te)
0897: throws TransformerException {
0898: log.error("An error occurred during transforamtion.", te);
0899: }
0900:
0901: public void fatalError(TransformerException te)
0902: throws TransformerException {
0903: log.error("A fatal error occurred during transforamtion.",
0904: te);
0905: }
0906:
0907: public void warning(TransformerException te)
0908: throws TransformerException {
0909: log.error("A warning occurred during transforamtion.", te);
0910: }
0911: }
0912:
0913: /**
0914: * Listener that exposes full causal information when exceptions occur
0915: * during transformation.
0916: */
0917: private static final TransformErrorListener cErrListener = new TransformErrorListener();
0918:
0919: private String constructCacheKey(IPerson person, String rootNodeId)
0920: throws PortalException {
0921: StringBuffer sbKey = new StringBuffer(1024);
0922: sbKey.append(rootNodeId).append(",");
0923: sbKey.append(uPreferencesManager.getUserPreferences()
0924: .getCacheKey());
0925: sbKey.append(uPreferencesManager.getUserLayoutManager()
0926: .getCacheKey());
0927: return sbKey.toString();
0928: }
0929:
0930: /**
0931: * Gets the person object from the session. Null is returned if
0932: * no person is logged in
0933: * @return the person object, null if no person is logged in
0934: */
0935: public IPerson getPerson() {
0936: return this .person;
0937: }
0938:
0939: /**
0940: * Gets the preferences manager object from the session.
0941: * @return the UserPreferencesManager object, null if no person is logged in
0942: */
0943: public IUserPreferencesManager getPreferencesManager() {
0944: return this .uPreferencesManager;
0945: }
0946:
0947: /**
0948: * This notifies UserInstance that it has been unbound from the session.
0949: * Method triggers cleanup in ChannelManager.
0950: *
0951: * @param bindingEvent an <code>HttpSessionBindingEvent</code> value
0952: */
0953: public void valueUnbound(HttpSessionBindingEvent bindingEvent) {
0954: if (channelManager != null) {
0955: channelManager.finishedSession();
0956: if (uPreferencesManager != null)
0957: uPreferencesManager.getUserLayoutManager()
0958: .removeLayoutEventListener(channelManager);
0959: }
0960: if (uPreferencesManager != null) {
0961: uPreferencesManager.finishedSession(bindingEvent);
0962: try {
0963: IUserLayoutManager ulm = uPreferencesManager
0964: .getUserLayoutManager();
0965: if (ulm instanceof TransientUserLayoutManagerWrapper)
0966: ulm = ((TransientUserLayoutManagerWrapper) ulm)
0967: .getOriginalLayoutManager();
0968: if (!(ulm instanceof IAggregatedUserLayoutManager))
0969: ulm.saveUserLayout();
0970: } catch (Exception e) {
0971: log
0972: .error(
0973: "UserInstance::valueUnbound(): Error occured while saving the user layout ",
0974: e);
0975: }
0976: }
0977: // Record the destruction of the session
0978: EventPublisherLocator.getApplicationEventPublisher()
0979: .publishEvent(
0980: new UserSessionDestroyedPortalEvent(this ,
0981: person));
0982: userSessions.decrementAndGet();
0983: GroupService.finishedSession(person);
0984: }
0985:
0986: /**
0987: * Notifies UserInstance that it has been bound to a session.
0988: *
0989: * @param bindingEvent a <code>HttpSessionBindingEvent</code> value
0990: */
0991: public void valueBound(HttpSessionBindingEvent bindingEvent) {
0992: // Record the creation of the session
0993: EventPublisherLocator
0994: .getApplicationEventPublisher()
0995: .publishEvent(
0996: new UserSessionCreatedPortalEvent(this , person));
0997: userSessions.incrementAndGet();
0998: }
0999:
1000: private IAggregatedUserLayoutManager getAggregatedLayoutManager(
1001: IUserLayoutManager ulm) throws PortalException {
1002: if (ulm instanceof TransientUserLayoutManagerWrapper)
1003: ulm = ((TransientUserLayoutManagerWrapper) ulm)
1004: .getOriginalLayoutManager();
1005: if (ulm instanceof IAggregatedUserLayoutManager)
1006: return (IAggregatedUserLayoutManager) ulm;
1007: return null;
1008: }
1009:
1010: /**
1011: * A method will determine if current request is a worker dispatch, and if so process it appropriatly
1012: * @param req the <code>HttpServletRequest</code>
1013: * @param res the <code>HttpServletResponse</code>
1014: * @param cm the <code>ChannelManager</code> instance
1015: * @return a <code>boolean</code> value
1016: * @exception PortalException if an error occurs
1017: */
1018: protected boolean processWorkerDispatch(HttpServletRequest req,
1019: HttpServletResponse res, ChannelManager cm)
1020: throws PortalException {
1021:
1022: HttpSession session = req.getSession(false);
1023: if (session != null) {
1024: // determine uPFile
1025: try {
1026: UPFileSpec upfs = new UPFileSpec(req);
1027: // is this a worker method ?
1028: if (upfs.getMethod() != null
1029: && upfs.getMethod().equals(
1030: UPFileSpec.WORKER_URL_ELEMENT)) {
1031: // this is a worker dispatch, process it
1032: // determine worker type
1033:
1034: String workerName = upfs.getMethodNodeId();
1035:
1036: if (workerName != null) {
1037: if (UserInstance.workerProperties == null) {
1038: // load worker properties
1039: try {
1040: UserInstance.workerProperties = ResourceLoader
1041: .getResourceAsProperties(
1042: UserInstance.class,
1043: WORKER_PROPERTIES_FILE_NAME);
1044: } catch (IOException ioe) {
1045: log
1046: .error(
1047: "UserInstance::processWorkerDispatch() : Unable to load worker.properties file. ",
1048: ioe);
1049: }
1050: // now add in component archive declared workers
1051: CarResources cRes = CarResources
1052: .getInstance();
1053: cRes
1054: .getWorkers(UserInstance.workerProperties);
1055: }
1056:
1057: String dispatchClassName = UserInstance.workerProperties
1058: .getProperty(workerName);
1059: if (dispatchClassName == null) {
1060: throw new PortalException(
1061: "UserInstance::processWorkerDispatch() : Unable to find processing class for the worker type \""
1062: + workerName
1063: + "\". Please check worker.properties");
1064: } else {
1065: // try to instantiate a worker class
1066: try {
1067: Object obj = CarResources.getInstance()
1068: .getClassLoader().loadClass(
1069: dispatchClassName)
1070: .newInstance();
1071: IWorkerRequestProcessor wrp = (IWorkerRequestProcessor) obj;
1072: // invoke processor
1073: try {
1074: wrp
1075: .processWorkerDispatch(new PortalControlStructures(
1076: req, res, cm,
1077: uPreferencesManager));
1078: } catch (PortalException pe) {
1079: throw pe;
1080: } catch (RuntimeException re) {
1081: throw new PortalException(re);
1082: }
1083: } catch (ClassNotFoundException cnfe) {
1084: throw new PortalException(
1085: "UserInstance::processWorkerDispatch() : Unable to find processing class (\""
1086: + dispatchClassName
1087: + "\") for the worker type \""
1088: + workerName
1089: + "\". Please check worker.properties",
1090: cnfe);
1091: } catch (InstantiationException ie) {
1092: throw new PortalException(
1093: "UserInstance::processWorkerDispatch() : Unable to instantiate processing class (\""
1094: + dispatchClassName
1095: + "\") for the worker type \""
1096: + workerName
1097: + "\". Please check worker.properties",
1098: ie);
1099: } catch (IllegalAccessException iae) {
1100: throw new PortalException(
1101: "UserInstance::processWorkerDispatch() : Unable to access processing class (\""
1102: + dispatchClassName
1103: + "\") for the worker type \""
1104: + workerName
1105: + "\". Please check worker.properties",
1106: iae);
1107: }
1108: }
1109: } else {
1110: throw new PortalException(
1111: "UserInstance::processWorkerDispatch() : Unable to determine worker type. uPFile=\""
1112: + upfs.getUPFile() + "\".");
1113: }
1114:
1115: return true;
1116: } else {
1117: return false;
1118: }
1119: } catch (IndexOutOfBoundsException iobe) {
1120: // ill-constructed URL
1121: return false;
1122: }
1123: }
1124: // will never get here
1125: return false;
1126: }
1127:
1128: }
|