0001: /* Copyright 2001, 2004 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.io.UnsupportedEncodingException;
0012: import java.util.ArrayList;
0013: import java.util.Collections;
0014: import java.util.Enumeration;
0015: import java.util.HashMap;
0016: import java.util.HashSet;
0017: import java.util.Hashtable;
0018: import java.util.Iterator;
0019: import java.util.Map;
0020: import java.util.Set;
0021: import java.util.WeakHashMap;
0022:
0023: import javax.naming.Context;
0024: import javax.naming.InitialContext;
0025: import javax.naming.NamingException;
0026: import javax.servlet.http.HttpServletRequest;
0027: import javax.servlet.http.HttpServletResponse;
0028: import javax.servlet.http.HttpSession;
0029:
0030: import org.apache.commons.logging.Log;
0031: import org.apache.commons.logging.LogFactory;
0032: import org.jasig.portal.channels.CSecureInfo;
0033: import org.jasig.portal.channels.error.CError;
0034: import org.jasig.portal.channels.error.ErrorCode;
0035: import org.jasig.portal.channels.support.IDynamicChannelTitleRenderer;
0036: import org.jasig.portal.events.EventPublisherLocator;
0037: import org.jasig.portal.events.support.ChannelInstanciatedInLayoutPortalEvent;
0038: import org.jasig.portal.events.support.ChannelRenderedInLayoutPortalEvent;
0039: import org.jasig.portal.events.support.ChannelTargetedInLayoutPortalEvent;
0040: import org.jasig.portal.i18n.LocaleManager;
0041: import org.jasig.portal.layout.IUserLayout;
0042: import org.jasig.portal.layout.IUserLayoutManager;
0043: import org.jasig.portal.layout.LayoutEvent;
0044: import org.jasig.portal.layout.LayoutEventListener;
0045: import org.jasig.portal.layout.LayoutMoveEvent;
0046: import org.jasig.portal.layout.node.IUserLayoutChannelDescription;
0047: import org.jasig.portal.layout.node.IUserLayoutNodeDescription;
0048: import org.jasig.portal.properties.PropertiesManager;
0049: import org.jasig.portal.security.IAuthorizationPrincipal;
0050: import org.jasig.portal.serialize.CachingSerializer;
0051: import org.jasig.portal.services.AuthorizationService;
0052: import org.jasig.portal.utils.SAX2BufferImpl;
0053: import org.jasig.portal.utils.SetCheckInSemaphore;
0054: import org.jasig.portal.utils.SoftHashMap;
0055: import org.xml.sax.ContentHandler;
0056:
0057: import tyrex.naming.MemoryContext;
0058: import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicLong;
0059:
0060: /**
0061: * ChannelManager shall have the burden of squeezing content out of channels.
0062: * <p>
0063: * Validation and timeouts, these two are needed for smooth operation of the portal
0064: * sometimes channels will timeout with information retreival then the content should
0065: * be skipped.
0066: *
0067: * @author Peter Kharchenko, pkharchenko@unicon.net
0068: * @version $Revision: 42516 $
0069: */
0070: public class ChannelManager implements LayoutEventListener {
0071: private static final Log log = LogFactory
0072: .getLog(ChannelManager.class);
0073:
0074: private IUserPreferencesManager upm;
0075: private PortalControlStructures pcs;
0076:
0077: private Hashtable channelTable;
0078: private Hashtable rendererTable;
0079: private Map channelCacheTable;
0080:
0081: private String channelTarget;
0082: //Using a ThreadLocal to store the target parameters to allow
0083: //multiple threads to be accessing this class in parallel without
0084: //overwriting the others parameters.
0085: private static ThreadLocal targetParamsLocal = new ThreadLocal();
0086: private BrowserInfo binfo;
0087: private LocaleManager lm;
0088:
0089: private Context portalContext;
0090: private Context channelContext;
0091:
0092: // inter-channel communication tables
0093: private HashMap iccTalkers;
0094: private HashMap iccListeners;
0095:
0096: // a set of channels requested for rendering, but
0097: // awaiting rendering set commit due to inter-channel
0098: // communication
0099: private Set pendingChannels;
0100: private boolean groupedRendering;
0101:
0102: private IAuthorizationPrincipal ap;
0103:
0104: // Metrics
0105: public static final AtomicLong activeRenderers = new AtomicLong();
0106: public static AtomicLong maxRenderThreads = new AtomicLong();
0107:
0108: private String serializerName;
0109:
0110: /** Factory used to build all channel renderer objects. */
0111: private static final IChannelRendererFactory cChannelRendererFactory = ChannelRendererFactory
0112: .newInstance(ChannelManager.class.getName(),
0113: activeRenderers, maxRenderThreads);
0114:
0115: public UPFileSpec uPElement;
0116:
0117: // global channel rendering cache
0118: public static final int SYSTEM_CHANNEL_CACHE_MIN_SIZE = 50; // this should be in a file somewhere
0119:
0120: public static final Map systemCache = new SoftHashMap(
0121: SYSTEM_CHANNEL_CACHE_MIN_SIZE);
0122:
0123: public static final String channelAddressingPathElement = "channel";
0124: private static boolean useAnchors = PropertiesManager
0125: .getPropertyAsBoolean(
0126: "org.jasig.portal.ChannelManager.use_anchors",
0127: false);
0128: private Set repeatRenderings = new HashSet();
0129: private boolean ccaching = false;
0130:
0131: public ChannelManager() {
0132: channelTable = new Hashtable();
0133: rendererTable = new Hashtable();
0134: iccTalkers = new HashMap();
0135: iccListeners = new HashMap();
0136: channelCacheTable = Collections
0137: .synchronizedMap(new WeakHashMap());
0138: groupedRendering = false;
0139: }
0140:
0141: /**
0142: * Creates a new <code>ChannelManager</code> instance.
0143: *
0144: * @param request a <code>HttpServletRequest</code> value
0145: * @param response a <code>HttpServletResponse</code> value
0146: * @param manager an <code>IUserPreferencesManager</code> value
0147: * @param uPElement an <code>UPFileSpec</code> that includes a tag number.
0148: */
0149: public ChannelManager(HttpServletRequest request,
0150: HttpServletResponse response,
0151: IUserPreferencesManager manager, UPFileSpec uPElement) {
0152: this (manager);
0153:
0154: this .startRenderingCycle(request, response, uPElement);
0155: }
0156:
0157: /**
0158: * Creates a new <code>ChannelManager</code> instance.
0159: *
0160: * @param manager an <code>IUserPreferencesManager</code> value
0161: */
0162: public ChannelManager(IUserPreferencesManager manager) {
0163: this ();
0164: this .upm = manager;
0165: pcs = new PortalControlStructures();
0166: pcs.setUserPreferencesManager(manager);
0167: pcs.setChannelManager(this );
0168: }
0169:
0170: /**
0171: * Directly places a channel instance into the hashtable of active channels.
0172: * This is designed to be used by the error channel only.
0173: */
0174:
0175: public void setChannelInstance(String channelSubscribeId,
0176: IChannel channelInstance) {
0177: if (channelTable.get(channelSubscribeId) != null) {
0178: channelTable.remove(channelSubscribeId);
0179: }
0180: channelTable.put(channelSubscribeId, channelInstance);
0181: }
0182:
0183: /**
0184: * A method to notify <code>ChannelManager</code> that the channel set for
0185: * the current rendering cycle is complete.
0186: * Note: This information is used to identify relevant channel communication dependencies
0187: */
0188: public void commitToRenderingChannelSet() {
0189: if (groupedRendering) {
0190: // separate out the dependency group in s0
0191:
0192: HashSet s0 = new HashSet();
0193: Set children;
0194:
0195: if (pendingChannels.contains(channelTarget)) {
0196: s0.add(channelTarget);
0197: pendingChannels.remove(channelTarget);
0198: children = getListeningChannels(channelTarget);
0199: if (children != null && !children.isEmpty()) {
0200: children.retainAll(pendingChannels);
0201: while (!children.isEmpty()) {
0202: // move to the next generation
0203: HashSet newChildren = new HashSet();
0204: for (Iterator ci = children.iterator(); ci
0205: .hasNext();) {
0206: String childId = (String) ci.next();
0207: s0.add(childId);
0208: pendingChannels.remove(childId);
0209: Set currentChildren = getListeningChannels(childId);
0210: if (currentChildren != null) {
0211: newChildren.addAll(currentChildren);
0212: }
0213: }
0214: newChildren.retainAll(pendingChannels);
0215: children = newChildren;
0216: }
0217: }
0218: }
0219:
0220: // now s0 group must be synchronized at renderXML(), while the remaining pendingChildren can be rendered freely
0221: SetCheckInSemaphore s0semaphore = new SetCheckInSemaphore(
0222: new HashSet(s0));
0223: for (Iterator gi = s0.iterator(); gi.hasNext();) {
0224: String channelSubscribeId = (String) gi.next();
0225: IChannelRenderer cr = (IChannelRenderer) rendererTable
0226: .get(channelSubscribeId);
0227: cr.startRendering(s0semaphore, channelSubscribeId);
0228: }
0229:
0230: for (Iterator oi = pendingChannels.iterator(); oi.hasNext();) {
0231: String channelSubscribeId = (String) oi.next();
0232: IChannelRenderer cr = (IChannelRenderer) rendererTable
0233: .get(channelSubscribeId);
0234: cr.startRendering();
0235: }
0236: }
0237: }
0238:
0239: /**
0240: * Clean up after a rendering round.
0241: */
0242: public void finishedRenderingCycle() {
0243: // clean up
0244: for (Enumeration enumeration = rendererTable.elements(); enumeration
0245: .hasMoreElements();) {
0246: ChannelRenderer channelRenderer = (ChannelRenderer) enumeration
0247: .nextElement();
0248: try {
0249: /*
0250: * For well behaved, finished channel renderers, killing doesn't do
0251: * anything.
0252: *
0253: * For runaway, not-finished channel renderers, killing instructs them to
0254: * stop trying to render because at this point we can't use the
0255: * results of their rendering anyway. Furthermore, the current
0256: * actual implementation
0257: * of kill is for channel renderers to kill runaway threads.
0258: */
0259: channelRenderer.kill();
0260: } catch (Throwable t) {
0261: /*
0262: * We're trying to clean up. A particular thread renderer we've asked
0263: * to please die has failed to die in some potentially horrible way.
0264: * This is unfortunate, but the best thing we can do about it is log
0265: * the problem and then go on and ask the other ChannelRenderers to
0266: * clean up. If this one won't clean up properly, maybe at least some
0267: * of the others will clean up. By catching Throwable and handling
0268: * it in this way, we prevent any particular ChannelRenderer's failure
0269: * from blocking our asking other ChannelRenderers to clean up.
0270: */
0271: log.error(
0272: "Error cleaning up runaway channel renderer: ["
0273: + channelRenderer + "]", t);
0274: }
0275:
0276: }
0277: rendererTable.clear();
0278: clearRepeatedRenderings();
0279: targetParamsLocal.set(null);
0280: //To ensure the old request and response objects are not used by the next
0281: //request clear them at the end of the processing cycle.
0282: this .pcs.setHttpServletRequest(null);
0283: this .pcs.setHttpServletResponse(null);
0284: pendingChannels = new HashSet();
0285: groupedRendering = false;
0286: }
0287:
0288: /**
0289: * Handle end-of-session cleanup
0290: *
0291: */
0292: public void finishedSession() {
0293: this .finishedRenderingCycle();
0294:
0295: // send SESSION_DONE event to all the channels
0296: PortalEvent ev = PortalEvent.SESSION_DONE_EVENT;
0297: for (Enumeration enum1 = channelTable.elements(); enum1
0298: .hasMoreElements();) {
0299: IChannel ch = (IChannel) enum1.nextElement();
0300: if (ch != null) {
0301: try {
0302: ch.receiveEvent(ev);
0303: } catch (Exception e) {
0304: log.error(
0305: "Error sending session done event to channel "
0306: + ch, e);
0307: }
0308: }
0309: }
0310:
0311: // we dont' really need to clean anything here,
0312: // since the entire session will be destroyed
0313: //channelCacheTable.clear();
0314: //channelTable.clear()
0315: }
0316:
0317: /**
0318: * Outputs a channel in to a given content handler.
0319: * If the current rendering cycle is targeting character
0320: * cache output, and the content handler passed to the method
0321: * is an instance of <code>CachingSerializer</code>, the method
0322: * will take care of character cache compilation and store cache
0323: * in the tables.
0324: *
0325: * @param channelSubscribeId a <code>String</code> value
0326: * @param contentHandler a <code>ContentHandler</code> value
0327: */
0328: public void outputChannel(String channelSubscribeId,
0329: ContentHandler contentHandler) {
0330: // Set the subscribeId as the achorId for an anchoring serializer
0331: if (useAnchors
0332: && contentHandler instanceof IAnchoringSerializer) {
0333: IAnchoringSerializer as = (IAnchoringSerializer) contentHandler;
0334: as.startAnchoring(channelSubscribeId);
0335: }
0336:
0337: // obtain IChannelRenderer
0338: IChannelRenderer cr = (IChannelRenderer) rendererTable
0339: .get(channelSubscribeId);
0340: if (cr == null) {
0341: // channel rendering wasn't started ?
0342: try {
0343: cr = startChannelRendering(channelSubscribeId);
0344: } catch (PortalException pe) {
0345: // record, and go on
0346: log
0347: .error(
0348: "ChannelManager::outputChannel() : Encountered a portal exception while trying to start channel rendering! :",
0349: pe);
0350: }
0351: }
0352:
0353: // complete rendering and check status
0354: int renderingStatus = -1;
0355: try {
0356: renderingStatus = cr.completeRendering();
0357: } catch (Throwable t) {
0358: handleRenderingError(
0359: channelSubscribeId,
0360: contentHandler,
0361: t,
0362: renderingStatus,
0363: "encountered problem while trying to complete rendering",
0364: "IChannelRenderer.completeRendering() threw", false);
0365: return;
0366: }
0367:
0368: if (renderingStatus == IChannelRenderer.RENDERING_SUCCESSFUL) {
0369: // obtain content
0370: if (contentHandler instanceof CachingSerializer
0371: && this .isCharacterCaching()) {
0372: CachingSerializer cs = (CachingSerializer) contentHandler;
0373: // need to get characters
0374: String characterContent = cr.getCharacters();
0375: if (characterContent == null) {
0376: // obtain a SAX Buffer content then
0377: SAX2BufferImpl bufferedContent = cr.getBuffer();
0378: if (bufferedContent != null) {
0379: // translate SAX Buffer into the character version
0380: try {
0381: if (!cs.startCaching()) {
0382: log
0383: .error("ChannelManager::outputChannel() : unable to restart character cache while compiling character cache for channel \""
0384: + channelSubscribeId
0385: + "\" !");
0386: }
0387: // dump SAX buffer into the serializer
0388: bufferedContent
0389: .outputBuffer(contentHandler);
0390: // extract compiled character cache
0391: if (cs.stopCaching()) {
0392: try {
0393: characterContent = cs.getCache();
0394: log.debug("outputChannel 2: "
0395: + characterContent);
0396: if (characterContent != null) {
0397: // save compiled character cache
0398: cr
0399: .setCharacterCache(characterContent);
0400: } else {
0401: log
0402: .error("ChannelManager::outputChannel() : character caching serializer returned NULL character cache for channel \""
0403: + channelSubscribeId
0404: + "\" !");
0405: }
0406: } catch (UnsupportedEncodingException e) {
0407: log
0408: .error(
0409: "ChannelManager::outputChannel() :unable to compile character cache for channel \""
0410: + channelSubscribeId
0411: + "\"! Invalid encoding specified.",
0412: e);
0413: } catch (IOException ioe) {
0414: log
0415: .error(
0416: "ChannelManager::outputChannel() :IO exception occurred while compiling character cache for channel \""
0417: + channelSubscribeId
0418: + "\" !",
0419: ioe);
0420: }
0421: } else {
0422: log
0423: .error("ChannelManager::outputChannel() : unable to reset cache state while compiling character cache for channel \""
0424: + channelSubscribeId
0425: + "\" ! Serializer was not caching when it should've been ! Partial output possible!");
0426: return;
0427: }
0428: } catch (IOException ioe) {
0429: handleRenderingError(
0430: channelSubscribeId,
0431: contentHandler,
0432: ioe,
0433: renderingStatus,
0434: "encountered a problem compiling channel character content",
0435: "Encountered IO exception while trying to output channel content SAX to the character caching serializer",
0436: true);
0437: return;
0438: } catch (org.xml.sax.SAXException se) {
0439: handleRenderingError(
0440: channelSubscribeId,
0441: contentHandler,
0442: se,
0443: renderingStatus,
0444: "encountered a problem compiling channel character content",
0445: "Encountered SAX exception while trying to output channel content SAX to the character caching serializer",
0446: true);
0447: return;
0448: }
0449:
0450: } else {
0451: handleRenderingError(
0452: channelSubscribeId,
0453: contentHandler,
0454: null,
0455: renderingStatus,
0456: "unable to obtain channel rendering",
0457: "IChannelRenderer.getBuffer() returned null",
0458: false);
0459: return;
0460: }
0461: } else { // non-null characterContent case
0462: // output character content
0463: try {
0464: cs.printRawCharacters(characterContent);
0465: // LogService.log(LogService.DEBUG,"------ channel "+channelSubscribeId+" character block (retrieved):");
0466: // LogService.log(LogService.DEBUG,characterContent);
0467: } catch (IOException ioe) {
0468: if (log.isDebugEnabled())
0469: log
0470: .debug(
0471: "ChannelManager::outputChannel() : "
0472: + "exception thrown while trying to output character "
0473: + "cache for channelSubscribeId="
0474: + "\""
0475: + channelSubscribeId
0476: + "\"", ioe);
0477: }
0478: }
0479: } else { // regular serializer case
0480: // need to output straight
0481: SAX2BufferImpl bufferedContent = cr.getBuffer();
0482: if (bufferedContent != null) {
0483: try {
0484: // output to the serializer
0485: ChannelSAXStreamFilter custodian = new ChannelSAXStreamFilter(
0486: contentHandler);
0487: bufferedContent.outputBuffer(custodian);
0488: } catch (Exception e) {
0489: log
0490: .error(
0491: "ChannelManager::outputChannel() : encountered an exception while trying to output SAX2 content of channel \""
0492: + channelSubscribeId
0493: + "\" to a regular serializer. Partial output possible !",
0494: e);
0495: return;
0496: }
0497: } else {
0498: handleRenderingError(
0499: channelSubscribeId,
0500: contentHandler,
0501: null,
0502: renderingStatus,
0503: "unable to obtain channel rendering",
0504: "IChannelRenderer.getBuffer() returned null",
0505: false);
0506: return;
0507: }
0508: }
0509:
0510: // Reset the anchorId for an anchoring serializer
0511: if (useAnchors
0512: && contentHandler instanceof IAnchoringSerializer) {
0513: IAnchoringSerializer as = (IAnchoringSerializer) contentHandler;
0514: as.stopAnchoring();
0515: }
0516:
0517: // Obtain the channel description
0518: IUserLayoutChannelDescription channelDesc = null;
0519: try {
0520: channelDesc = (IUserLayoutChannelDescription) upm
0521: .getUserLayoutManager().getNode(
0522: channelSubscribeId);
0523: } catch (PortalException pe) {
0524: // Just log exception
0525: log.warn(pe, pe);
0526: }
0527:
0528: // Tell the StatsRecorder that this channel has rendered
0529: EventPublisherLocator.getApplicationEventPublisher()
0530: .publishEvent(
0531: new ChannelRenderedInLayoutPortalEvent(
0532: this , upm.getPerson(), upm
0533: .getCurrentProfile(),
0534: channelDesc));
0535: } else {
0536: handleRenderingError(channelSubscribeId, contentHandler,
0537: null, renderingStatus, "unsuccessful rendering",
0538: "unsuccessful rendering", false);
0539: return;
0540: }
0541: }
0542:
0543: /**
0544: * Check if repeated rendering has been attempted for a given channel.
0545: *
0546: * @param channelSubscribeId a <code>String</code> value
0547: * @return a <code>boolean</code> value
0548: */
0549: private boolean isRepeatedRenderingAttempt(String channelSubscribeId) {
0550: return repeatRenderings.contains(channelSubscribeId);
0551: }
0552:
0553: /**
0554: * Register a repeated rendering attempt for a particular channel.
0555: *
0556: * @param channelSubscribeId a <code>String</code> value
0557: */
0558: private void setRepeatedRenderingAttempt(String channelSubscribeId) {
0559: repeatRenderings.add(channelSubscribeId);
0560: }
0561:
0562: /**
0563: * Clear information about repeated rendering attempts.
0564: *
0565: */
0566: private void clearRepeatedRenderings() {
0567: repeatRenderings.clear();
0568: }
0569:
0570: /**
0571: * Handles rendering output errors by replacing a channel instance with that of an error channel.
0572: * (or giving up if the error channel is failing as well)
0573: *
0574: * @param channelSubscribeId a <code>String</code> value
0575: * @param contentHandler a <code>ContentHandler</code> value
0576: * @param t a <code>Throwable</code> value
0577: * @param renderingStatus an <code>int</code> value
0578: * @param commonMessage a <code>String</code> value
0579: * @param technicalMessage a <code>String</code> value
0580: * @param partialOutput a <code>boolean</code> value
0581: */
0582: private void handleRenderingError(String channelSubscribeId,
0583: ContentHandler contentHandler, Throwable t,
0584: int renderingStatus, String commonMessage,
0585: String technicalMessage, boolean partialOutput) {
0586: try {
0587: if (isRepeatedRenderingAttempt(channelSubscribeId)) {
0588: // this means that the error channel has failed :(
0589: String message = "ChannelManager::handleRenderingError() : Unable to handle a rendering error through error channel.";
0590: if (t != null) {
0591: if (t instanceof InternalPortalException) {
0592: InternalPortalException ipe = (InternalPortalException) t;
0593: Throwable e = ipe.getCause();
0594: message = message
0595: + " Error channel (channelSubscribeId=\""
0596: + channelSubscribeId
0597: + "\") has thrown the following exception: "
0598: + e.toString()
0599: + " Partial output possible !";
0600: log
0601: .fatal(
0602: "CError threw exception. Please fix CError immediately!",
0603: e);
0604: } else {
0605: message = message
0606: + " An following exception encountered while trying to render the error channel for channelSubscribeId=\""
0607: + channelSubscribeId + "\": "
0608: + t.toString();
0609: log
0610: .fatal(
0611: "CError threw exception. Please fix CError immediately!",
0612: t);
0613: }
0614: } else {
0615: // check status
0616: message = message + " channelRenderingStatus=";
0617:
0618: switch (renderingStatus) {
0619: case IChannelRenderer.RENDERING_SUCCESSFUL:
0620: message += "successful";
0621: break;
0622: case IChannelRenderer.RENDERING_FAILED:
0623: message += "failed";
0624: break;
0625: case IChannelRenderer.RENDERING_TIMED_OUT:
0626: message += "timed out";
0627: break;
0628: default:
0629: message += "UNKNOWN CODE: " + renderingStatus;
0630: break;
0631: }
0632: }
0633: message = message + " " + technicalMessage;
0634: log.error(message);
0635: } else {
0636: // first check for an exception
0637: if (t != null) {
0638: if (t instanceof InternalPortalException) {
0639: InternalPortalException ipe = (InternalPortalException) t;
0640: Throwable channelException = ipe.getCause();
0641: replaceWithErrorChannel(channelSubscribeId,
0642: ErrorCode.RENDER_TIME_EXCEPTION,
0643: channelException, technicalMessage,
0644: true);
0645: } else {
0646: replaceWithErrorChannel(channelSubscribeId,
0647: ErrorCode.RENDER_TIME_EXCEPTION, t,
0648: technicalMessage, true);
0649: }
0650: } else {
0651: if (renderingStatus == IChannelRenderer.RENDERING_TIMED_OUT) {
0652: replaceWithErrorChannel(channelSubscribeId,
0653: ErrorCode.TIMEOUT_EXCEPTION, t,
0654: technicalMessage, true);
0655: } else {
0656: replaceWithErrorChannel(channelSubscribeId,
0657: ErrorCode.GENERAL_ERROR, t,
0658: technicalMessage, true);
0659: }
0660: }
0661:
0662: // remove channel renderer
0663: rendererTable.remove(channelSubscribeId);
0664: // re-try render
0665: if (!partialOutput) {
0666: setRepeatedRenderingAttempt(channelSubscribeId);
0667: outputChannel(channelSubscribeId, contentHandler);
0668: }
0669: }
0670: } finally {
0671: // Set the subscribeId as the achorId for an anchoring serializer
0672: if (useAnchors
0673: && contentHandler instanceof IAnchoringSerializer) {
0674: IAnchoringSerializer as = (IAnchoringSerializer) contentHandler;
0675: as.stopAnchoring();
0676: }
0677: }
0678: }
0679:
0680: /**
0681: * A helper method to replace all occurences of a given channel instance
0682: * with that of an error channel.
0683: *
0684: * @param channelSubscribeId a <code>String</code> value
0685: * @param errorCode an ErrorCode
0686: * @param t a <code>Throwable</code> an exception that caused the problem
0687: * @param message a <code>String</code> an optional message to pass to the error channel
0688: * @param setRuntimeData a <code>boolean</code> wether the method should also set the ChannelRuntimeData for the newly instantiated error channel
0689: * @return an <code>IChannel</code> value of an error channel instance
0690: */
0691: private IChannel replaceWithErrorChannel(String channelSubscribeId,
0692: ErrorCode errorCode, Throwable t, String message,
0693: boolean setRuntimeData) {
0694: // get and delete old channel instance
0695: IChannel oldInstance = (IChannel) channelTable
0696: .get(channelSubscribeId);
0697: if (log.isWarnEnabled())
0698: log.warn("Replacing channel [" + oldInstance
0699: + "], which had subscribeId [" + channelSubscribeId
0700: + "] with error channel because of error code "
0701: + errorCode + " message: " + message
0702: + " and throwable [" + t + "]", t);
0703:
0704: channelTable.remove(channelSubscribeId);
0705: rendererTable.remove(channelSubscribeId);
0706:
0707: CError errorChannel = new CError(errorCode, t,
0708: channelSubscribeId, oldInstance, message);
0709: if (setRuntimeData) {
0710: ChannelRuntimeData rd = new ChannelRuntimeData();
0711: rd.setBrowserInfo(binfo);
0712: if (lm != null) {
0713: rd.setLocales(lm.getLocales());
0714: }
0715: rd.setRemoteAddress(pcs.getHttpServletRequest()
0716: .getRemoteAddr());
0717: rd.setHttpRequestMethod(pcs.getHttpServletRequest()
0718: .getMethod());
0719: UPFileSpec up = new UPFileSpec(uPElement);
0720: up.setTargetNodeId(channelSubscribeId);
0721: rd.setUPFile(up);
0722: try {
0723: errorChannel.setRuntimeData(rd);
0724: errorChannel.setPortalControlStructures(pcs);
0725: } catch (Throwable e) {
0726:
0727: log
0728: .error(
0729: "Encountered an exception while trying to set runtime data or portal control structures on the error channel!",
0730: e);
0731: }
0732: }
0733: channelTable.put(channelSubscribeId, errorChannel);
0734: return errorChannel;
0735: }
0736:
0737: /**
0738: * A helper method to replace all occurences of a secure channel instance
0739: * with that of a secure information channel.
0740: *
0741: * @param channelSubscribeId a <code>String</code> value
0742: * @param setRuntimeData a <code>boolean</code> wether the method should also set the ChannelRuntimeData for the newly instantiated secure info channel
0743: * @return an <code>IChannel</code> value of a secure info channel instance
0744: */
0745: private IChannel replaceWithSecureInfoChannel(
0746: String channelSubscribeId, boolean setRuntimeData) {
0747: // get and delete old channel instance
0748: IChannel oldInstance = (IChannel) channelTable
0749: .get(channelSubscribeId);
0750: channelTable.remove(channelSubscribeId);
0751: rendererTable.remove(channelSubscribeId);
0752:
0753: CSecureInfo secureInfoChannel = new CSecureInfo(
0754: channelSubscribeId, oldInstance);
0755: if (setRuntimeData) {
0756: ChannelRuntimeData rd = new ChannelRuntimeData();
0757: rd.setBrowserInfo(binfo);
0758: if (lm != null) {
0759: rd.setLocales(lm.getLocales());
0760: }
0761: rd.setHttpRequestMethod(pcs.getHttpServletRequest()
0762: .getMethod());
0763: rd.setRemoteAddress(pcs.getHttpServletRequest()
0764: .getRemoteAddr());
0765: UPFileSpec up = new UPFileSpec(uPElement);
0766: up.setTargetNodeId(channelSubscribeId);
0767: rd.setUPFile(up);
0768: try {
0769: secureInfoChannel.setRuntimeData(rd);
0770: secureInfoChannel.setPortalControlStructures(pcs);
0771: } catch (Throwable e) {
0772: log
0773: .error(
0774: "Encountered an exception while trying to set runtime data or portal control structures on the secure info channel!",
0775: e);
0776: }
0777: }
0778: channelTable.put(channelSubscribeId, secureInfoChannel);
0779: return secureInfoChannel;
0780: }
0781:
0782: /**
0783: * <code>getChannelContext</code> generates a JNDI context that
0784: * will be passed to the regular channels. The context is pieced
0785: * together from the parts of the global portal context.
0786: *
0787: * @param portalContext uPortal JNDI context
0788: * @param sessionId current session id
0789: * @param userId id of a current user
0790: * @param layoutId id of the layout used by the user
0791: * @return a channel <code>InitialContext</code> value
0792: */
0793: private static Context getChannelJndiContext(Context portalContext,
0794: String sessionId, String userId, String layoutId)
0795: throws NamingException {
0796: // create a new InitialContext
0797: Context cic = new MemoryContext(new Hashtable());
0798: // get services context
0799: Context servicesContext = (Context) portalContext
0800: .lookup("services");
0801: // get channel-ids context
0802: Context channel_idsContext = (Context) portalContext
0803: .lookup("users/" + userId + "/layouts/" + layoutId
0804: + "/channel-ids");
0805: // get channel-obj context
0806: Context channel_objContext = (Context) portalContext
0807: .lookup("users/" + userId + "/sessions/" + sessionId
0808: + "/channel-obj");
0809:
0810: cic.bind("services", servicesContext);
0811: cic.bind("channel-ids", channel_idsContext);
0812: cic.bind("channel-obj", channel_objContext);
0813: cic.bind("portlet-ids", new ArrayList());
0814:
0815: return cic;
0816: }
0817:
0818: /**
0819: * Get the uPortal JNDI context
0820: * @return uPortal initial JNDI context
0821: * @exception NamingException
0822: */
0823: private static Context getPortalContext() throws NamingException {
0824: Hashtable environment = new Hashtable(5);
0825: // Set up the path
0826: environment.put(Context.INITIAL_CONTEXT_FACTORY,
0827: "org.jasig.portal.jndi.PortalInitialContextFactory");
0828: Context ctx = new InitialContext(environment);
0829: return (ctx);
0830: }
0831:
0832: /**
0833: * Instantiates a channel given just the channel subscribe Id.
0834: *
0835: * @param channelSubscribeId a channel instance Id in the userLayout
0836: * @return an <code>IChannel</code> object
0837: */
0838: public IChannel instantiateChannel(String channelSubscribeId)
0839: throws PortalException {
0840: if (channelTable.get(channelSubscribeId) != null) {
0841: // reinstantiation
0842: channelTable.remove(channelSubscribeId);
0843: }
0844: // get channel information from the user layout manager
0845: IUserLayoutChannelDescription channel = (IUserLayoutChannelDescription) upm
0846: .getUserLayoutManager().getNode(channelSubscribeId);
0847: if (channel != null)
0848: return instantiateChannel(channel);
0849: else
0850: return null;
0851: }
0852:
0853: private IChannel instantiateChannel(IUserLayoutChannelDescription cd)
0854: throws PortalException {
0855: IChannel ch = null;
0856: String channelSubscribeId = cd.getChannelSubscribeId();
0857: String channelPublishId = cd.getChannelPublishId();
0858: // check if the user has permissions to instantiate this channel
0859: if (ap == null) {
0860: EntityIdentifier ei = this .pcs.getUserPreferencesManager()
0861: .getPerson().getEntityIdentifier();
0862: ap = AuthorizationService.instance().newPrincipal(
0863: ei.getKey(), ei.getType());
0864: }
0865:
0866: if (ap.canRender(Integer.parseInt(channelPublishId))) {
0867:
0868: // Instantiate the channel and notify the StatsRecorder
0869:
0870: HttpServletRequest sr = this .pcs.getHttpServletRequest();
0871: if (sr == null) {
0872: ch = new CError(
0873: ErrorCode.GENERAL_ERROR,
0874: "Unable to get SessionId. getHttpServletRequest returned null.",
0875: channelSubscribeId, null);
0876: } else {
0877: // getSession() true is apparently required to support cross-context sessions
0878: // under Tomcat. See UP-1320
0879: HttpSession hs = sr.getSession(true);
0880: if (hs == null) {
0881: ch = new CError(
0882: ErrorCode.GENERAL_ERROR,
0883: "Unable to get SessionId. getSession returned null.",
0884: channelSubscribeId, null);
0885: } else {
0886: String id = hs.getId();
0887: if (id == null) {
0888: ch = new CError(
0889: ErrorCode.GENERAL_ERROR,
0890: "Unable to get SessionId. getId returned null.",
0891: channelSubscribeId, null);
0892: } else {
0893:
0894: ch = ChannelFactory.instantiateLayoutChannel(
0895: cd, id);
0896:
0897: if (ch == null) {
0898: throw new IllegalStateException(
0899: "ChannelFactory returned null on request to instantiate layout channel with id ["
0900: + id
0901: + "] and description ["
0902: + cd + "]");
0903: }
0904:
0905: EventPublisherLocator
0906: .getApplicationEventPublisher()
0907: .publishEvent(
0908: new ChannelInstanciatedInLayoutPortalEvent(
0909: this ,
0910: upm.getPerson(),
0911: upm.getCurrentProfile(),
0912: cd));
0913:
0914: // Create and stuff the channel static data
0915: ChannelStaticData sd = new ChannelStaticData(cd
0916: .getParameterMap(), upm
0917: .getUserLayoutManager());
0918: sd.setChannelSubscribeId(channelSubscribeId);
0919: sd.setTimeout(cd.getTimeout());
0920: sd.setPerson(upm.getPerson());
0921: sd.setJNDIContext(channelContext);
0922: sd.setICCRegistry(new ICCRegistry(this ,
0923: channelSubscribeId));
0924: sd
0925: .setChannelPublishId(cd
0926: .getChannelPublishId());
0927: sd.setSerializerName(serializerName);
0928:
0929: ch.setStaticData(sd);
0930: }
0931: }
0932: }
0933:
0934: } else {
0935: // user is not authorized to instantiate this channel
0936: // create an instance of an error channel instead
0937: ch = new CError(
0938: ErrorCode.CHANNEL_AUTHORIZATION_EXCEPTION,
0939: "You don't have authorization to render this channel.",
0940: channelSubscribeId, null);
0941: }
0942:
0943: channelTable.put(channelSubscribeId, ch);
0944: return ch;
0945: }
0946:
0947: /**
0948: * Passes a layout-level event to a channel.
0949: * @param channelSubscribeId the channel subscribe id
0950: * @param le the portal event
0951: */
0952: public void passPortalEvent(HttpServletRequest req,
0953: HttpServletResponse res, String channelSubscribeId,
0954: PortalEvent le) {
0955: //The latest portlet req/res are required in the portal control structures
0956: //for events because of portlet adapter requirements regarding
0957: //stats collection.
0958: pcs.setHttpServletRequest(req);
0959: pcs.setHttpServletResponse(res);
0960:
0961: IChannel ch = (IChannel) channelTable.get(channelSubscribeId);
0962:
0963: if (ch != null) {
0964: //Ensure the channel (if IPrivileged) has the latest control
0965: //structures object before recieving the event.
0966: if ((ch instanceof IPrivileged)) {
0967: try {
0968: ((IPrivileged) ch).setPortalControlStructures(pcs);
0969: } catch (Exception e) {
0970: log
0971: .warn(
0972: "ChannelManager::passPortalEvent() : ChannelManager threw exception while trying to set portalControlStructures",
0973: e);
0974: }
0975: }
0976:
0977: try {
0978: ch.receiveEvent(le);
0979: } catch (Exception e) {
0980: log.error("Error sending layout event " + le
0981: + " to channel " + ch, e);
0982: }
0983: } else {
0984: log
0985: .error("ChannelManager::passPortalEvent() : trying to pass an event to a channel that is not in cache. (channel=\""
0986: + channelSubscribeId + "\")");
0987: }
0988: }
0989:
0990: /**
0991: * Determine target channel and pass corresponding
0992: * actions/params to that channel
0993: * @param req the <code>HttpServletRequest</code>
0994: */
0995: private void processRequestChannelParameters(HttpServletRequest req) {
0996: // clear the previous settings
0997: channelTarget = null;
0998: targetParamsLocal.set(new Hashtable());
0999:
1000: // see if this is targeted at an fname channel. if so then it takes
1001: // precedence. This is done so that a baseActionURL can be used for
1002: // the basis of an fname targeted channel with the fname query parm
1003: // appended to direct all query parms to the fname channel
1004: String fname = req.getParameter(Constants.FNAME_PARAM);
1005:
1006: if (fname != null) {
1007: // need to get to wrapper for obtaining a subscribe id
1008: IUserLayoutManager ulm = upm.getUserLayoutManager();
1009: // get a subscribe id for the fname
1010: try {
1011: channelTarget = ulm.getSubscribeId(fname);
1012: } catch (PortalException pe) {
1013: log
1014: .error(
1015: "ChannelManager::processRequestChannelParameters(): Unable to get subscribe ID for fname="
1016: + fname, pe);
1017: }
1018: }
1019: if (channelTarget == null) {
1020: // check if the uP_channelTarget parameter has been passed
1021: channelTarget = req.getParameter("uP_channelTarget");
1022: }
1023: if (channelTarget == null) {
1024: // determine target channel id
1025: UPFileSpec upfs = new UPFileSpec(req);
1026: channelTarget = upfs.getTargetNodeId();
1027: if (log.isDebugEnabled())
1028: log
1029: .debug("ChannelManager::processRequestChannelParameters() : "
1030: + "channelTarget=\""
1031: + channelTarget
1032: + "\".");
1033: }
1034:
1035: if (channelTarget != null) {
1036: // Obtain the channel description
1037: IUserLayoutChannelDescription channelDesc = null;
1038: try {
1039: channelDesc = (IUserLayoutChannelDescription) upm
1040: .getUserLayoutManager().getNode(channelTarget);
1041: } catch (PortalException pe) {
1042: // Do nothing
1043: }
1044:
1045: // Tell StatsRecorder that a user has interacted with the channel
1046: EventPublisherLocator.getApplicationEventPublisher()
1047: .publishEvent(
1048: new ChannelTargetedInLayoutPortalEvent(
1049: this , upm.getPerson(), upm
1050: .getCurrentProfile(),
1051: channelDesc));
1052:
1053: // process parameters
1054: Enumeration en = req.getParameterNames();
1055: if (en != null) {
1056: if (en.hasMoreElements()) {
1057: // only do grouped rendering if there are some parameters passed
1058: // to the target channel.
1059: // detect if channel target talks to other channels
1060: groupedRendering = hasListeningChannels(channelTarget);
1061: }
1062: Map targetParams = (Map) targetParamsLocal.get();
1063: while (en.hasMoreElements()) {
1064: String pName = (String) en.nextElement();
1065: if (!pName.equals("uP_channelTarget")
1066: && !pName.equals("uP_fname")) {
1067: Object[] val = (Object[]) req
1068: .getParameterValues(pName);
1069: if (val == null) {
1070: val = ((IRequestParamWrapper) req)
1071: .getObjectParameterValues(pName);
1072: }
1073: targetParams.put(pName, val);
1074: }
1075: }
1076: }
1077:
1078: IChannel chObj;
1079: if ((chObj = (IChannel) channelTable.get(channelTarget)) == null) {
1080: try {
1081: chObj = instantiateChannel(channelTarget);
1082: } catch (Throwable e) {
1083: if (upm.getPerson().isGuest() == true) {
1084: // We get this alot when people's sessions have timed out and they get directed
1085: // to the guest page. Changed to WARN because there might be a need to note this
1086: // to diagnose problems with the guest layout.
1087:
1088: log
1089: .warn("ChannelManager::processRequestChannelParameters() : unable to pass find/create an instance of a channel. Bogus Id ? ! (id=\""
1090: + channelTarget + "\").");
1091: } else {
1092: log
1093: .error(
1094: "ChannelManager::processRequestChannelParameters() : unable to pass find/create an instance of a channel. Bogus Id ? ! (id=\""
1095: + channelTarget
1096: + "\" uid=\""
1097: + upm.getPerson()
1098: .getID()
1099: + "\").", e);
1100: }
1101: chObj = replaceWithErrorChannel(channelTarget,
1102: ErrorCode.SET_STATIC_DATA_EXCEPTION, e,
1103: null, false);
1104: }
1105: }
1106:
1107: // Check if the channel is an IPrivilegedChannel, and if it is,
1108: // pass portal control structures and runtime data.
1109: if (chObj != null && (chObj instanceof IPrivileged)) {
1110: chObj = feedPortalControlStructuresToChannel(chObj, pcs);
1111: chObj = feedRuntimeDataToChannel(chObj, req);
1112: }
1113: }
1114: }
1115:
1116: private IChannel feedPortalControlStructuresToChannel(
1117: IChannel chObj, PortalControlStructures pcs) {
1118: IPrivileged prvChanObj = (IPrivileged) chObj;
1119: try {
1120: prvChanObj.setPortalControlStructures(pcs);
1121: } catch (Exception e) {
1122: chObj = replaceWithErrorChannel(channelTarget,
1123: ErrorCode.SET_PCS_EXCEPTION, e, null, false);
1124: prvChanObj = (IPrivileged) chObj;
1125:
1126: // set portal control structures
1127: try {
1128: prvChanObj.setPortalControlStructures(pcs);
1129: } catch (Exception e2) {
1130: // things are looking bad for our hero
1131: StringWriter sw = new StringWriter();
1132: e2.printStackTrace(new PrintWriter(sw));
1133: sw.flush();
1134: log.error("Error channel threw ! ", e2);
1135: }
1136: }
1137: return chObj;
1138: }
1139:
1140: private IChannel feedRuntimeDataToChannel(IChannel chObj,
1141: HttpServletRequest req) {
1142: try {
1143: ChannelRuntimeData rd = new ChannelRuntimeData();
1144: rd.setParameters((Map) targetParamsLocal.get());
1145: String qs = pcs.getHttpServletRequest().getQueryString();
1146: if (qs != null && qs.indexOf("=") == -1)
1147: rd.setKeywords(qs);
1148: rd.setBrowserInfo(binfo);
1149: if (lm != null) {
1150: rd.setLocales(lm.getLocales());
1151: }
1152: rd.setHttpRequestMethod(pcs.getHttpServletRequest()
1153: .getMethod());
1154: rd.setRemoteAddress(req.getRemoteAddr());
1155: UPFileSpec up = new UPFileSpec(uPElement);
1156: up.setTargetNodeId(channelTarget);
1157: rd.setUPFile(up);
1158:
1159: // Check if channel is rendering as the root element of the layout
1160: String userLayoutRoot = upm.getUserPreferences()
1161: .getStructureStylesheetUserPreferences()
1162: .getParameterValue("userLayoutRoot");
1163: if (userLayoutRoot != null
1164: && !userLayoutRoot
1165: .equals(IUserLayout.ROOT_NODE_NAME)) {
1166: rd.setRenderingAsRoot(true);
1167: }
1168:
1169: // Indicate that this channel is targeted
1170: rd.setTargeted(true);
1171:
1172: // Finally, feed runtime data to channel
1173: chObj.setRuntimeData(rd);
1174: } catch (Exception e) {
1175: chObj = replaceWithErrorChannel(channelTarget,
1176: ErrorCode.SET_RUNTIME_DATA_EXCEPTION, e, null, true);
1177: }
1178: return chObj;
1179: }
1180:
1181: /**
1182: * Obtain an instance of a channel.
1183: *
1184: * @param channelSubscribeId a <code>String</code> value
1185: * @return an <code>IChannel</code> object
1186: */
1187: public IChannel getChannelInstance(String channelSubscribeId) {
1188: IChannel ch = (IChannel) channelTable.get(channelSubscribeId);
1189: if (ch == null) {
1190: try {
1191: ch = instantiateChannel(channelSubscribeId);
1192: } catch (Throwable e) {
1193: log.warn(e, e);
1194: return null;
1195: }
1196: }
1197: return ch;
1198: }
1199:
1200: /**
1201: * Removes channel instance from the internal caches.
1202: *
1203: * @param channelSubscribeId a <code>String</code> value
1204: */
1205: public void removeChannel(String channelSubscribeId) {
1206: IChannel ch = (IChannel) channelTable.get(channelSubscribeId);
1207: if (ch != null) {
1208: channelCacheTable.remove(ch);
1209: try {
1210: ch.receiveEvent(PortalEvent.UNSUBSCRIBE_EVENT);
1211: } catch (Exception e) {
1212: log.error(e, e);
1213: }
1214: channelTable.remove(ch);
1215: if (log.isDebugEnabled())
1216: log.debug("ChannelManager::removeChannel(): "
1217: + "removed channel with subscribe id="
1218: + channelSubscribeId);
1219: }
1220: }
1221:
1222: /**
1223: * Signals the start of a new rendering cycle.
1224: *
1225: * @param request a <code>HttpServletRequest</code> value
1226: * @param response a <code>HttpServletResponse</code> value
1227: * @param uPElement an <code>UPFileSpec</code> value
1228: */
1229: public void startRenderingCycle(HttpServletRequest request,
1230: HttpServletResponse response, UPFileSpec uPElement) {
1231: this .pcs.setHttpServletRequest(request);
1232: this .pcs.setHttpServletResponse(response);
1233: this .binfo = new BrowserInfo(request);
1234: this .uPElement = uPElement;
1235: rendererTable.clear();
1236:
1237: // check portal JNDI context
1238: if (portalContext == null) {
1239: try {
1240: portalContext = getPortalContext();
1241: } catch (NamingException ne) {
1242: log.error(ne, ne);
1243: }
1244: }
1245: // construct a channel context
1246: if (channelContext == null) {
1247: try {
1248: channelContext = getChannelJndiContext(portalContext,
1249: request.getSession(false).getId(), Integer
1250: .toString(this .pcs
1251: .getUserPreferencesManager()
1252: .getPerson().getID()), Integer
1253: .toString(this .pcs
1254: .getUserPreferencesManager()
1255: .getCurrentProfile()
1256: .getLayoutId()));
1257: } catch (NamingException ne) {
1258: log.error(ne, ne);
1259: }
1260: }
1261: processRequestChannelParameters(request);
1262: }
1263:
1264: /**
1265: * Specifies if this particular rendering cycle is using
1266: * character caching.
1267: *
1268: * @return a <code>boolean</code> value
1269: */
1270: public boolean isCharacterCaching() {
1271: return this .ccaching;
1272: }
1273:
1274: /**
1275: * Specify that the current rendering cycle should be
1276: * using (or not) character caching.
1277: *
1278: * @param setting a <code>boolean</code> value
1279: */
1280: public void setCharacterCaching(boolean setting) {
1281: this .ccaching = setting;
1282: }
1283:
1284: /**
1285: * Specify <code>UPFileSpec</code> object that will be
1286: * used to construct file portion of the context path
1287: * in the auto-generated URLs, also known as the baseActionURL.
1288: *
1289: * @param uPElement an <code>UPFileSpec</code> value
1290: */
1291: public void setUPElement(UPFileSpec uPElement) {
1292: this .uPElement = uPElement;
1293: }
1294:
1295: /**
1296: * Specify <code>IUserPreferencesManager</code> to use.
1297: *
1298: * @param m an <code>IUserPreferencesManager</code> value
1299: */
1300: public void setUserPreferencesManager(IUserPreferencesManager m) {
1301: upm = m;
1302: }
1303:
1304: /**
1305: * Initiate channel rendering cycle.
1306: *
1307: * @param channelSubscribeId a <code>String</code> value
1308: * @return a <code>IChannelRenderer</code> value
1309: * @exception PortalException if an error occurs
1310: */
1311: public IChannelRenderer startChannelRendering(
1312: String channelSubscribeId) throws PortalException {
1313: return startChannelRendering(channelSubscribeId, false);
1314: }
1315:
1316: /**
1317: * Initiate channel rendering cycle, possibly disabling timeout.
1318: *
1319: * @param channelSubscribeId a <code>String</code> value
1320: * @param noTimeout a <code>boolean</code> value specifying if the
1321: * time out rendering control should be disabled.
1322: * @return a <code>IChannelRenderer</code> value
1323: * @exception PortalException if an error occurs
1324: */
1325: private IChannelRenderer startChannelRendering(
1326: String channelSubscribeId, boolean noTimeout)
1327: throws PortalException {
1328: // see if the channel has already been instantiated
1329: // see if the channel is cached
1330: IChannel ch;
1331: long timeOut = 0;
1332:
1333: IUserLayoutNodeDescription node = upm.getUserLayoutManager()
1334: .getNode(channelSubscribeId);
1335: if (!(node instanceof IUserLayoutChannelDescription)) {
1336: throw new PortalException("\"" + channelSubscribeId
1337: + "\" is not a channel node !");
1338: }
1339:
1340: IUserLayoutChannelDescription channel = (IUserLayoutChannelDescription) node;
1341: timeOut = channel.getTimeout();
1342:
1343: ch = (IChannel) channelTable.get(channelSubscribeId);
1344:
1345: // replace channels that are specified as needing to be
1346: // rendered securely with CSecureInfo.
1347: if (!pcs.getHttpServletRequest().isSecure()
1348: && channel.isSecure()) {
1349: if (ch == null || !(ch instanceof CSecureInfo)) {
1350: ch = replaceWithSecureInfoChannel(channelSubscribeId,
1351: false);
1352: }
1353: } else {
1354: // A secure channel may not have been able to render at one
1355: // time but now it can, create its instance to replace the
1356: // cached CSecureInfo entry.
1357: if (ch == null || ch instanceof CSecureInfo) {
1358: try {
1359: ch = instantiateChannel(channel);
1360: } catch (Throwable e) {
1361: ch = replaceWithErrorChannel(channelSubscribeId,
1362: ErrorCode.SET_STATIC_DATA_EXCEPTION, e,
1363: null, false);
1364: }
1365: }
1366: }
1367:
1368: ChannelRuntimeData rd = null;
1369:
1370: if (!channelSubscribeId.equals(channelTarget)) {
1371: if ((ch instanceof IPrivileged)) {
1372: // send the control structures
1373: try {
1374: ((IPrivileged) ch).setPortalControlStructures(pcs);
1375: } catch (Exception e) {
1376: ch = replaceWithErrorChannel(channelSubscribeId,
1377: ErrorCode.SET_PCS_EXCEPTION, e, null, false);
1378: channelTable.remove(ch);
1379:
1380: // set portal control structures for the error channel
1381: try {
1382: ((IPrivileged) ch)
1383: .setPortalControlStructures(pcs);
1384: } catch (Exception e2) {
1385: // things are looking bad for our hero
1386:
1387: log
1388: .error(
1389: "ChannelManager::outputChannels : Error channel threw ! ",
1390: e2);
1391: }
1392: }
1393: }
1394: rd = new ChannelRuntimeData();
1395: rd.setBrowserInfo(binfo);
1396: if (lm != null) {
1397: rd.setLocales(lm.getLocales());
1398: }
1399: rd.setHttpRequestMethod(pcs.getHttpServletRequest()
1400: .getMethod());
1401: rd.setRemoteAddress(pcs.getHttpServletRequest()
1402: .getRemoteAddr());
1403:
1404: UPFileSpec up = new UPFileSpec(uPElement);
1405: up.setTargetNodeId(channelSubscribeId);
1406: rd.setUPFile(up);
1407:
1408: } else {
1409: // set up runtime data that will be passed to the IChannelRenderer
1410: if (!(ch instanceof IPrivileged)) {
1411: rd = new ChannelRuntimeData();
1412: rd.setTargeted(true);
1413: rd.setParameters((Map) targetParamsLocal.get());
1414: String qs = pcs.getHttpServletRequest()
1415: .getQueryString();
1416: if (qs != null && qs.indexOf("=") == -1)
1417: rd.setKeywords(qs);
1418: rd.setBrowserInfo(binfo);
1419: if (lm != null) {
1420: rd.setLocales(lm.getLocales());
1421: }
1422: rd.setHttpRequestMethod(pcs.getHttpServletRequest()
1423: .getMethod());
1424: rd.setRemoteAddress(pcs.getHttpServletRequest()
1425: .getRemoteAddr());
1426:
1427: UPFileSpec up = new UPFileSpec(uPElement);
1428: up.setTargetNodeId(channelSubscribeId);
1429: rd.setUPFile(up);
1430: }
1431: }
1432:
1433: // Check if channel is rendering as the root element of the layout
1434:
1435: UserPreferences tempUserPref = upm.getUserPreferences();
1436: StructureStylesheetUserPreferences tempSSUP = tempUserPref
1437: .getStructureStylesheetUserPreferences();
1438: String userLayoutRoot = tempSSUP
1439: .getParameterValue("userLayoutRoot");
1440: if (rd != null && userLayoutRoot != null
1441: && !userLayoutRoot.equals(IUserLayout.ROOT_NODE_NAME)) {
1442: rd.setRenderingAsRoot(true);
1443: }
1444:
1445: // Build a new channel renderer instance.
1446: IChannelRenderer cr = cChannelRendererFactory.newInstance(ch,
1447: rd, pcs);
1448:
1449: cr.setCharacterCacheable(this .isCharacterCaching());
1450: if (ch instanceof ICacheable) {
1451: cr.setCacheTables(this .channelCacheTable);
1452: }
1453:
1454: if (noTimeout) {
1455: cr.setTimeout(0);
1456: } else {
1457: cr.setTimeout(timeOut);
1458: }
1459:
1460: if (groupedRendering
1461: && (isListeningToChannels(channelSubscribeId) || channelSubscribeId
1462: .equals(channelTarget))) {
1463: // channel might depend on the target channel
1464: pendingChannels.add(channelSubscribeId); // defer rendering start
1465: } else {
1466: cr.startRendering();
1467: }
1468: rendererTable.put(channelSubscribeId, cr);
1469:
1470: return cr;
1471: }
1472:
1473: synchronized void registerChannelDependency(
1474: String listenerChannelSubscribeId,
1475: String talkerChannelSubscribeId) {
1476: Set talkers = (Set) iccListeners
1477: .get(listenerChannelSubscribeId);
1478: if (talkers == null) {
1479: talkers = new HashSet();
1480: iccListeners.put(listenerChannelSubscribeId, talkers);
1481: }
1482: talkers.add(talkerChannelSubscribeId);
1483:
1484: Set listeners = (Set) iccTalkers.get(talkerChannelSubscribeId);
1485: if (listeners == null) {
1486: listeners = new HashSet();
1487: iccTalkers.put(talkerChannelSubscribeId, listeners);
1488: }
1489: listeners.add(listenerChannelSubscribeId);
1490: }
1491:
1492: private Set getListeningChannels(String talkerChannelSubscribeId) {
1493: return (Set) iccTalkers.get(talkerChannelSubscribeId);
1494: }
1495:
1496: private boolean isListeningToChannels(
1497: String listenerChannelSubscribeId) {
1498: return (iccListeners.get(listenerChannelSubscribeId) != null);
1499: }
1500:
1501: private boolean hasListeningChannels(String talkerChannelSubscribeId) {
1502: return (iccTalkers.get(talkerChannelSubscribeId) != null);
1503: }
1504:
1505: synchronized void removeChannelDependency(
1506: String listenerChannelSubscribeId,
1507: String talkerChannelSubscribeId) {
1508: Set talkers = (Set) iccListeners
1509: .get(listenerChannelSubscribeId);
1510: if (talkers != null) {
1511: talkers.remove(talkerChannelSubscribeId);
1512: if (talkers.isEmpty()) {
1513: iccListeners.remove(listenerChannelSubscribeId);
1514: }
1515: }
1516:
1517: Set listeners = (Set) iccTalkers.get(talkerChannelSubscribeId);
1518: if (listeners != null) {
1519: listeners.remove(listenerChannelSubscribeId);
1520: if (listeners.isEmpty()) {
1521: iccTalkers.remove(talkerChannelSubscribeId);
1522: }
1523: }
1524: }
1525:
1526: /**
1527: * Determines whether or not anchors should be
1528: * inserted at the end of URLS within channels.
1529: * These anchors typically tell a browser to
1530: * position itself with the channel in-view after
1531: * a link is clicked or a form is submitted.
1532: * @return <code>true</code> if use of anchors is enabled, otherwise <code>false</code>
1533: */
1534: public static boolean isUseAnchors() {
1535: return ChannelManager.useAnchors;
1536: }
1537:
1538: public String getChannelTarget() {
1539: return channelTarget;
1540: }
1541:
1542: // LayoutEventListener interface implementation
1543: public void channelAdded(LayoutEvent ev) {
1544: }
1545:
1546: public void channelUpdated(LayoutEvent ev) {
1547: }
1548:
1549: public void channelMoved(LayoutMoveEvent ev) {
1550: }
1551:
1552: public void channelDeleted(LayoutMoveEvent ev) {
1553: this .removeChannel(ev.getNodeDescription().getId());
1554: }
1555:
1556: public void folderAdded(LayoutEvent ev) {
1557: }
1558:
1559: public void folderUpdated(LayoutEvent ev) {
1560: }
1561:
1562: public void folderMoved(LayoutMoveEvent ev) {
1563: }
1564:
1565: public void folderDeleted(LayoutMoveEvent ev) {
1566: }
1567:
1568: public void layoutLoaded() {
1569: }
1570:
1571: public void layoutSaved() {
1572: }
1573:
1574: public void setLocaleManager(LocaleManager lm) {
1575: this .lm = lm;
1576: }
1577:
1578: /**
1579: * Get the dynamic channel title for a given channelSubscribeID.
1580: * Returns null if no dynamic channel (the rendering infrastructure
1581: * calling this method should fall back on a default title when this
1582: * method returns null).
1583: * @since uPortal 2.5.1
1584: * @param channelSubscribeId
1585: * @throws IllegalArgumentException if channelSubcribeId is null
1586: * @throws IllegalStateException if
1587: */
1588: public String getChannelTitle(String channelSubscribeId) {
1589:
1590: if (log.isTraceEnabled()) {
1591: log
1592: .trace("ChannelManager getting dynamic title for channel with subscribe id="
1593: + channelSubscribeId);
1594: }
1595:
1596: // obtain IChannelRenderer
1597: Object channelRenderer = rendererTable.get(channelSubscribeId);
1598:
1599: // default to null (no dynamic channel title.
1600: String channelTitle = null;
1601:
1602: // dynamic channel title support is not in IChannelRenderer itself because
1603: // that would have required a change to the IChannelRenderer interface
1604: if (channelRenderer instanceof IDynamicChannelTitleRenderer) {
1605:
1606: IDynamicChannelTitleRenderer channelTitleRenderer = (IDynamicChannelTitleRenderer) channelRenderer;
1607: channelTitle = channelTitleRenderer.getChannelTitle();
1608:
1609: if (log.isTraceEnabled()) {
1610: log
1611: .trace("ChannelManager reports that dynamic title for channel with subscribe id="
1612: + channelSubscribeId
1613: + " is ["
1614: + channelTitle + "].");
1615: }
1616: }
1617:
1618: return channelTitle;
1619:
1620: }
1621:
1622: public String getSubscribeId(String fname) throws PortalException {
1623: IUserLayoutManager ulm = upm.getUserLayoutManager();
1624: return ulm.getSubscribeId(fname);
1625: }
1626:
1627: /**
1628: * Sets the serializer name.
1629: * @return serializerName
1630: */
1631: public String getSerializerName() {
1632: return serializerName;
1633: }
1634:
1635: /**
1636: * Setter method for the serializer name.
1637: * @param serializerName
1638: */
1639: public void setSerializerName(String serializerName) {
1640: this.serializerName = serializerName;
1641: }
1642: }
|