001: /* Copyright 2001, 2004 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal.channels.error;
007:
008: import java.io.PrintWriter;
009: import org.jasig.portal.ChannelCacheKey;
010: import org.jasig.portal.ChannelManager;
011: import org.jasig.portal.ChannelRuntimeData;
012: import org.jasig.portal.ChannelStaticData;
013: import org.jasig.portal.EntityIdentifier;
014: import org.jasig.portal.ICacheable;
015: import org.jasig.portal.IChannel;
016: import org.jasig.portal.MediaManager;
017: import org.jasig.portal.PortalEvent;
018: import org.jasig.portal.serialize.OutputFormat;
019: import org.jasig.portal.serialize.XMLSerializer;
020: import org.jasig.portal.serialize.BaseMarkupSerializer;
021: import org.jasig.portal.ThemeStylesheetDescription;
022: import org.jasig.portal.ICharacterChannel;
023: import org.jasig.portal.IPrivilegedChannel;
024: import org.jasig.portal.PortalControlStructures;
025: import org.jasig.portal.PortalException;
026: import org.jasig.portal.channels.BaseChannel;
027: import org.jasig.portal.channels.error.error2xml.IThrowableToElement;
028: import org.jasig.portal.i18n.LocaleManager;
029: import org.jasig.portal.security.IAuthorizationPrincipal;
030: import org.jasig.portal.services.AuthorizationService;
031: import org.jasig.portal.spring.PortalApplicationContextFacade;
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.jasig.portal.utils.XML;
035: import org.jasig.portal.utils.XSLT;
036: import org.springframework.beans.BeansException;
037: import org.w3c.dom.Document;
038: import org.xml.sax.ContentHandler;
039:
040: /**
041: * CError is the error channel, also known as the null channel; it is designed
042: * to render in place of other channels when something goes wrong.
043: * <p>
044: * Possible conditions when CError is invoked are:
045: * <ul>
046: * <li>Channel has thrown a Throwable from one of the IChannel or
047: * IPrivilegedChannel methods.</li>
048: * <li>Channel has timed out on rendering and was terminated.</li>
049: * <li>uPortal has rejected a channel for some reason. In this case a general
050: * message is constructed by the portal.</li>
051: * </ul>
052: *
053: * @author Peter Kharchenko, pkharchenko@interactivebusiness.com
054: * @author andrew.petro@yale.edu
055: * @version $Revision: 36781 $ $Date: 2007-01-24 10:35:24 -0700 (Wed, 24 Jan 2007) $
056: * @since uPortal 2.5. Prior to 2.5, CError existed only as org.jasig.portal.channels.CError.
057: */
058: public final class CError extends BaseChannel implements
059: IPrivilegedChannel, ICacheable, ICharacterChannel {
060:
061: private static final Log log = LogFactory.getLog(CError.class);
062:
063: /**
064: * An ErrorDocument representing the error about which we are reporting and
065: * providing a source for XML to be rendered by our XSLT.
066: */
067: private ErrorDocument errorDocument = new ErrorDocument();
068:
069: /**
070: * The channel instance that failed.
071: */
072: private IChannel the_channel = null;
073:
074: /**
075: * CError is a placeholder when it is taking the place of a channel that no
076: * longer exists or that the user doesn't have permission to render. CError
077: * is not a placeholder when it represents the failure of a channel that
078: * actually tried to render.
079: */
080: private boolean placeHolder = false;
081:
082: /**
083: * True if we should display the stack trace of the stored Throwable, if
084: * any, at rendering.
085: */
086: private boolean showStackTrace = false;
087:
088: /**
089: * The title of the stylesheet we should use to render.
090: */
091: private String ssTitle = null;
092:
093: private PortalControlStructures portcs;
094:
095: /**
096: * The location of our our .ssl file.
097: */
098: private static final String sslLocation = "CError/CError.ssl";
099:
100: private static final MediaManager MEDIAMANAGER = MediaManager
101: .getMediaManager(true);
102:
103: /**
104: * Construct an uninitialized instance of the CError channel.
105: */
106: public CError() {
107:
108: // inject into our ErrorDocument the configured IThrowableToElement that
109: // will translate from Throwables to XML that we can render
110:
111: try {
112: IThrowableToElement throwableToElement = (IThrowableToElement) PortalApplicationContextFacade
113: .getPortalApplicationContext().getBean(
114: "throwableToElement",
115: IThrowableToElement.class);
116:
117: this .errorDocument
118: .setThrowableToElement(throwableToElement);
119: } catch (BeansException be) {
120: // do not allow a Beans failure to break CError
121: log
122: .error(
123: "Error retrieving the mapping from throwables to Elements for CError rendering.",
124: be);
125: // since our ErrorDocument has a default mapping from Throwables to Elements
126: // we can fall back on that default by not doing anything.
127: }
128:
129: }
130:
131: /**
132: * Construct an instance of the Error channel representing a failure to
133: * render of a particular subscribed channel for reason of having thrown a
134: * Throwable.
135: *
136: * @param errorCode -
137: * one of the static error codes of this class
138: * @param throwable -
139: * cause of failed channel's failure
140: * @param channelSubscribeId -
141: * identifies the failed channel
142: * @param channelInstance -
143: * the failed channel
144: */
145: public CError(ErrorCode errorCode, Throwable throwable,
146: String channelSubscribeId, IChannel channelInstance) {
147:
148: this ();
149:
150: if (log.isTraceEnabled()) {
151: log.trace("CError(" + errorCode + ", throwable=["
152: + throwable + "], chanSubId=" + channelSubscribeId
153: + ", channelInstance=[" + channelInstance + "]");
154: }
155:
156: this .errorDocument.setChannelSubscribeId(channelSubscribeId);
157: this .errorDocument.setThrowable(throwable);
158: this .the_channel = channelInstance;
159: this .errorDocument.setCode(errorCode);
160:
161: if (log.isTraceEnabled()) {
162: log.trace("Instantiated CError: " + this );
163: }
164:
165: }
166:
167: /**
168: * Instantiate a CError representing a particular channel's failure,
169: * including a message and errorCode, but not a Throwable.
170: *
171: * @param errorCode -
172: * one of the static error codes of this class
173: * @param message -
174: * describes error
175: * @param channelSubscribeId -
176: * identifies failed channel
177: * @param channelInstance -
178: * failed channel
179: */
180: public CError(ErrorCode errorCode, String message,
181: String channelSubscribeId, IChannel channelInstance) {
182:
183: this ();
184:
185: if (log.isTraceEnabled())
186: log.trace("CError(" + errorCode + ", message=[" + message
187: + "], chanSubId=" + channelSubscribeId
188: + ", channelInstance=[" + channelInstance + "]");
189:
190: this .errorDocument.setChannelSubscribeId(channelSubscribeId);
191: this .the_channel = channelInstance;
192: this .errorDocument.setCode(errorCode);
193: this .errorDocument.setMessage(message);
194:
195: if (log.isTraceEnabled())
196: log.trace("Instantiated CError: " + this );
197: }
198:
199: /**
200: * Instantiate a CError instance representing the failure of some particular
201: * channel, including an error code, message, and the Throwable.
202: *
203: * @param errorCode -
204: * one of the static error codes of this class
205: * @param exception -
206: * thrown by the failed channel
207: * @param channelSubscribeId -
208: * identifies failed channel
209: * @param channelInstance -
210: * the failed channel instance
211: * @param message -
212: * message describing failure
213: */
214: public CError(ErrorCode errorCode, Throwable exception,
215: String channelSubscribeId, IChannel channelInstance,
216: String message) {
217:
218: this (errorCode, exception, channelSubscribeId, channelInstance);
219: this .errorDocument.setMessage(message);
220:
221: if (log.isTraceEnabled())
222: log.trace("Instantiated CError: " + this );
223: }
224:
225: /**
226: * Resets internal state of CError.
227: *
228: * @param errorCode -
229: * new errorCode value
230: * @param throwable -
231: * new stored Throwable
232: * @param channelSubscribeId -
233: * new channelSubscribeId
234: * @param channelInstance -
235: * new failed channel
236: * @param message -
237: * new failure message
238: */
239: private void resetCError(ErrorCode errorCode, Throwable throwable,
240: String channelSubscribeId, IChannel channelInstance,
241: String message) {
242:
243: this .errorDocument.setCode(errorCode);
244: this .errorDocument.setThrowable(throwable);
245: this .errorDocument.setChannelSubscribeId(channelSubscribeId);
246:
247: this .the_channel = channelInstance;
248:
249: this .errorDocument.setMessage(message);
250:
251: if (log.isTraceEnabled())
252: log.trace("Reset CError to: " + this );
253: }
254:
255: public void setPortalControlStructures(PortalControlStructures pcs) {
256: this .portcs = pcs;
257: }
258:
259: public void receiveEvent(PortalEvent ev) {
260: if (the_channel != null) {
261: // propagate the portal events to the normal channel
262: the_channel.receiveEvent(ev);
263: }
264: super .receiveEvent(ev);
265: }
266:
267: /*
268: * This is so CError can be used by getUserLayout() as a placeholder for
269: * channels that have either been deleted from the portal database or the
270: * users permission to use the channel has been removed (permanently or
271: * temporarily).
272: */
273: public void setStaticData(ChannelStaticData sd) {
274: if (log.isTraceEnabled())
275: log.trace("setStaticData(" + sd + ")");
276:
277: try {
278: if (sd == null) {
279: log
280: .error("ChannelStaticData argument to setStaticData() illegally null.");
281: return;
282: }
283: this .errorDocument.setMessage(sd
284: .getParameter("CErrorMessage"));
285: this .errorDocument.setChannelSubscribeId(sd
286: .getParameter("CErrorChanId"));
287: String value = sd.getParameter("CErrorErrorId");
288: if (value != null) {
289: this .errorDocument.setCode(ErrorCode.codeForInt(Integer
290: .parseInt(value)));
291: }
292: this .placeHolder = true; // Should only get here if we are a
293: // "normal channel"
294: } catch (Throwable t) {
295: log
296: .error(
297: "Error setting static data of CError instance",
298: t);
299: }
300: }
301:
302: public void renderXML(ContentHandler out) {
303: // runtime data processing needs to be done here, otherwise replaced
304: // channel will get duplicated setRuntimeData() calls
305:
306: log.trace("Entering renderXML()");
307:
308: String channelSubscribeId = this .errorDocument
309: .getChannelSubscribeId();
310:
311: if (channelSubscribeId != null) {
312: String chFate = this .runtimeData.getParameter("action");
313: log.debug("Channel fate is [" + chFate
314: + "] for chanSubscribeId=" + channelSubscribeId);
315: if (chFate != null) {
316: // a fate has been chosen
317: if (chFate.equals("retry")) {
318: // clean things up for the channel
319: ChannelRuntimeData crd = (ChannelRuntimeData) this .runtimeData
320: .clone();
321: crd.clear(); // Remove parameters
322: try {
323: if (this .the_channel instanceof IPrivilegedChannel)
324: ((IPrivilegedChannel) this .the_channel)
325: .setPortalControlStructures(this .portcs);
326: this .the_channel.setRuntimeData(crd);
327: ChannelManager cm = this .portcs
328: .getChannelManager();
329: cm.setChannelInstance(channelSubscribeId,
330: this .the_channel);
331: this .the_channel.renderXML(out);
332: return;
333: } catch (Exception e) {
334: // if any of the above didn't work, fall back to the
335: // error channel
336: resetCError(
337: ErrorCode.SET_RUNTIME_DATA_EXCEPTION,
338: e, channelSubscribeId,
339: this .the_channel,
340: "Channel failed a refresh attempt.");
341: }
342: } else if (chFate.equals("restart")) {
343:
344: ChannelManager cm = this .portcs.getChannelManager();
345:
346: ChannelRuntimeData crd = (ChannelRuntimeData) this .runtimeData
347: .clone();
348: crd.clear();
349: try {
350: if ((this .the_channel = cm
351: .instantiateChannel(channelSubscribeId)) == null) {
352: resetCError(ErrorCode.GENERAL_ERROR, null,
353: channelSubscribeId, null,
354: "Channel failed to reinstantiate!");
355: } else {
356: try {
357: if (this .the_channel instanceof IPrivilegedChannel) {
358: ((IPrivilegedChannel) this .the_channel)
359: .setPortalControlStructures(this .portcs);
360: }
361: this .the_channel.setRuntimeData(crd);
362: this .the_channel.renderXML(out);
363: return;
364: } catch (Exception e) {
365: // if any of the above didn't work, fall back to
366: // the error channel
367: resetCError(
368: ErrorCode.SET_RUNTIME_DATA_EXCEPTION,
369: e, channelSubscribeId,
370: this .the_channel,
371: "Channel failed a reload attempt.");
372: cm.setChannelInstance(
373: channelSubscribeId, this );
374: log
375: .error(
376: "CError::setRuntimeData() : "
377: + "an error occurred during channel reinitialization. ",
378: e);
379: }
380: }
381: } catch (Exception e) {
382: resetCError(ErrorCode.GENERAL_ERROR, e,
383: channelSubscribeId, null,
384: "Channel failed to reinstantiate!");
385: log
386: .error(
387: "CError::setRuntimeData() : "
388: + "an error occurred during channel reinstantiation. ",
389: e);
390: }
391: } else if (chFate.equals("toggle_stack_trace")) {
392: this .showStackTrace = !this .showStackTrace;
393: }
394: }
395: }
396: // if channel's render XML method was to be called, we would've returned
397: // by now
398: localRenderXML(out);
399: }
400:
401: private void localRenderXML(ContentHandler out) {
402: // note: this method should be made very robust. Optimally, it should
403: // not rely on XSLT to do the job. That means that mime-type dependent
404: // output should be generated directly within the method.
405: // For now, we'll just do it the usual way.
406:
407: if (log.isTraceEnabled())
408: log.trace("Entering localRenderXML() for CError " + this );
409:
410: String channelSubscribeId = this .errorDocument
411: .getChannelSubscribeId();
412:
413: if (channelSubscribeId != null) {
414:
415: try {
416: this .errorDocument.setChannelName(this .portcs
417: .getUserPreferencesManager()
418: .getUserLayoutManager().getNode(
419: channelSubscribeId).getName());
420: } catch (Throwable t) {
421: log.error(
422: "Error determining name of channel with subscribe id ["
423: + channelSubscribeId + "]", t);
424: }
425: }
426:
427: // defaults to refresh and reload not allowed.
428: RefreshPolicy policy = new RefreshPolicy();
429: if (channelSubscribeId != null)
430: policy = computeRefreshPolicy();
431:
432: // Decide whether to render a friendly or detailed screen
433: this .ssTitle = "friendly";
434: try {
435: AuthorizationService authService = AuthorizationService
436: .instance();
437: EntityIdentifier ei = this .portcs
438: .getUserPreferencesManager().getPerson()
439: .getEntityIdentifier();
440: IAuthorizationPrincipal ap = authService.newPrincipal(ei
441: .getKey(), ei.getType());
442: if (ap.hasPermission(SupportedPermissions.OWNER,
443: SupportedPermissions.VIEW_ACTIVITY,
444: SupportedPermissions.DETAILS_TARGET))
445: this .ssTitle = "detailed";
446: } catch (Throwable t) {
447: log
448: .error(
449: "Exception checking whether user authorized to view "
450: + "detailed CError view. Defaulting to friendly view.",
451: t);
452: }
453:
454: log.trace("SSL title is " + this .ssTitle);
455:
456: Document doc = this .errorDocument.getDocument();
457:
458: if (log.isWarnEnabled()) {
459: try {
460: // java.io.StringWriter outString = new java.io.StringWriter();
461: // org.apache.xml.serialize.OutputFormat format =
462: // new org.apache.xml.serialize.OutputFormat();
463: // format.setOmitXMLDeclaration(true);
464: // format.setIndenting(true);
465: // org.apache.xml.serialize.XMLSerializer xsl =
466: // new org.apache.xml.serialize.XMLSerializer(outString, format);
467: // xsl.serialize(doc);
468: log.warn("ErrorDocument XML is \n"
469: + XML.serializeNode(doc));
470: } catch (Exception e) {
471: log.error(e, e);
472: }
473: }
474:
475: try {
476: XSLT xslt = XSLT.getTransformer(this , this .runtimeData
477: .getLocales());
478: xslt.setXML(doc);
479: xslt.setXSL(sslLocation, this .ssTitle, this .runtimeData
480: .getBrowserInfo());
481: xslt.setTarget(out);
482: xslt.setStylesheetParameter("baseActionURL",
483: this .runtimeData.getBaseActionURL());
484: xslt.setStylesheetParameter("showStackTrace", String
485: .valueOf(this .showStackTrace));
486: xslt.setStylesheetParameter("allowRefresh", Boolean
487: .toString(policy.allowRefresh));
488: xslt.setStylesheetParameter("allowReinstantiation", Boolean
489: .toString(policy.allowReinstantiation));
490: xslt.transform();
491: } catch (Exception e) {
492: log
493: .error(
494: "CError::renderXML() : Things are bad. "
495: + "Error channel threw Exception rendering its stylesheet.",
496: e);
497: }
498: }
499:
500: public ChannelCacheKey generateKey() {
501: // check if either restart or refresh command has been given, otherwise
502: // generate key
503: if (this .runtimeData != null
504: && this .runtimeData.getParameter("action") != null) {
505: return null;
506: }
507:
508: ChannelCacheKey k = new ChannelCacheKey();
509: StringBuffer sbKey = new StringBuffer(1024);
510:
511: // assume that errors can be cached system-wide
512: k.setKeyScope(ChannelCacheKey.SYSTEM_KEY_SCOPE);
513:
514: sbKey
515: .append(
516: "org.jasig.portal.channels.CError: errorDocument=")
517: .append(this .errorDocument).append(" strace=").append(
518: Boolean.toString(this .showStackTrace));
519: sbKey.append(", mode=").append(this .ssTitle);
520: sbKey.append(", locales=").append(
521: LocaleManager.stringValueOf(this .runtimeData
522: .getLocales()));
523: k.setKey(sbKey.toString());
524: return k;
525: }
526:
527: public boolean isCacheValid(Object validity) {
528: return true;
529: }
530:
531: public void renderCharacters(PrintWriter out)
532: throws PortalException {
533: // runtime data processing needs to be done here, otherwise replaced
534: // channel will get duplicated setRuntimeData() calls
535:
536: String channelSubscribeId = this .errorDocument
537: .getChannelSubscribeId();
538:
539: if (channelSubscribeId != null) {
540: String chFate = this .runtimeData.getParameter("action");
541: if (chFate != null) {
542: // a fate has been chosen
543: if (chFate.equals("retry")) {
544: log
545: .debug("CError:renderCharacters() : going for retry");
546: // clean things up for the channel
547: ChannelRuntimeData crd = (ChannelRuntimeData) this .runtimeData
548: .clone();
549: crd.clear(); // Remove parameters
550: try {
551: if (this .the_channel instanceof IPrivilegedChannel)
552: ((IPrivilegedChannel) this .the_channel)
553: .setPortalControlStructures(this .portcs);
554: this .the_channel.setRuntimeData(crd);
555: ChannelManager cm = this .portcs
556: .getChannelManager();
557: cm.setChannelInstance(channelSubscribeId,
558: this .the_channel);
559: if (this .the_channel instanceof ICharacterChannel) {
560: ((ICharacterChannel) this .the_channel)
561: .renderCharacters(out);
562: } else {
563: ThemeStylesheetDescription tsd = this .portcs
564: .getUserPreferencesManager()
565: .getThemeStylesheetDescription();
566: BaseMarkupSerializer serOut = MEDIAMANAGER
567: .getSerializerByName(tsd
568: .getSerializerName(), out);
569: this .the_channel.renderXML(serOut);
570: }
571: return;
572: } catch (Exception e) {
573: // if any of the above didn't work, fall back to the
574: // error channel
575: resetCError(
576: ErrorCode.SET_RUNTIME_DATA_EXCEPTION,
577: e, channelSubscribeId,
578: this .the_channel,
579: "Channel failed a refresh attempt.");
580: }
581: } else if (chFate.equals("restart")) {
582: log
583: .debug("CError:renderCharacters() : going for reinstantiation");
584:
585: ChannelManager cm = this .portcs.getChannelManager();
586:
587: ChannelRuntimeData crd = (ChannelRuntimeData) this .runtimeData
588: .clone();
589: crd.clear();
590: try {
591: this .the_channel = cm
592: .instantiateChannel(channelSubscribeId);
593: if (this .the_channel == null) {
594: resetCError(ErrorCode.GENERAL_ERROR, null,
595: channelSubscribeId, null,
596: "Channel failed to reinstantiate!");
597: } else {
598: try {
599: if (this .the_channel instanceof IPrivilegedChannel) {
600: ((IPrivilegedChannel) this .the_channel)
601: .setPortalControlStructures(this .portcs);
602: }
603: this .the_channel.setRuntimeData(crd);
604: if (this .the_channel instanceof ICharacterChannel) {
605: ((ICharacterChannel) this .the_channel)
606: .renderCharacters(out);
607: } else {
608: ThemeStylesheetDescription tsd = this .portcs
609: .getUserPreferencesManager()
610: .getThemeStylesheetDescription();
611: BaseMarkupSerializer serOut = MEDIAMANAGER
612: .getSerializerByName(
613: tsd
614: .getSerializerName(),
615: out);
616: this .the_channel.renderXML(serOut);
617: }
618: return;
619: } catch (Exception e) {
620: // if any of the above didn't work, fall back to
621: // the error channel
622: resetCError(
623: ErrorCode.SET_RUNTIME_DATA_EXCEPTION,
624: e, channelSubscribeId,
625: this .the_channel,
626: "Channel failed a reload attempt.");
627: cm.setChannelInstance(
628: channelSubscribeId, this );
629: log
630: .error(
631: "CError::renderCharacters() : an error occurred "
632: + "during channel reinitialization.",
633: e);
634: }
635: }
636: } catch (Exception e) {
637: resetCError(ErrorCode.GENERAL_ERROR, e,
638: channelSubscribeId, null,
639: "Channel failed to reinstantiate!");
640: log.error(
641: "CError::renderCharacters() : an error occurred during "
642: + "channel reinstantiation. ",
643: e);
644: }
645: } else if (chFate.equals("toggle_stack_trace")) {
646: this .showStackTrace = !this .showStackTrace;
647: }
648: }
649: }
650: // if channel's render XML method was to be called, we would've returned
651: // by now
652: BaseMarkupSerializer serOut = null;
653: try {
654: ThemeStylesheetDescription tsd = this .portcs
655: .getUserPreferencesManager()
656: .getThemeStylesheetDescription();
657: serOut = MEDIAMANAGER.getSerializerByName(tsd
658: .getSerializerName(), out);
659: } catch (Exception e) {
660: log
661: .error(
662: "CError::renderCharacters() : "
663: + "unable to obtain proper markup serializer : ",
664: e);
665: }
666:
667: if (serOut == null) {
668: // default to XML serializer
669: OutputFormat frmt = new OutputFormat("XML", "UTF-8", true);
670: serOut = new XMLSerializer(out, frmt);
671: }
672:
673: localRenderXML(serOut);
674: }
675:
676: /**
677: * Compute the refresh policy.
678: * Assumes channel subcribe ID is not null, since in that case there is no
679: * question about the policy - you cannot reinstantiate or refresh
680: * unknown channels.
681: * @return a RefreshPolicy suitable to our state.
682: */
683: private RefreshPolicy computeRefreshPolicy() {
684: log.trace("entering computeRefreshPolicy()");
685: RefreshPolicy policy = new RefreshPolicy();
686:
687: if (this .placeHolder) {
688: // We are just displaying a message.
689: // No channel to refresh or reload
690: policy.allowRefresh = false;
691: policy.allowReinstantiation = false;
692: if (log.isTraceEnabled())
693: log.trace("policy is [" + policy
694: + "] because we are a placeholder.");
695: } else {
696: // allow the PortalException, if any, to configure refresh and
697: // reload
698: Throwable errorThrowable = this .errorDocument
699: .getThrowable();
700: if (errorThrowable != null
701: && errorThrowable instanceof PortalException) {
702:
703: PortalException portalException = (PortalException) errorThrowable;
704:
705: policy.allowRefresh = portalException.isRefreshable();
706: policy.allowReinstantiation = portalException
707: .isReinstantiable();
708:
709: if (log.isTraceEnabled()) {
710: log.trace("PortalException [" + portalException
711: + "] implied refresh policy [" + policy
712: + "]");
713: }
714: }
715: }
716:
717: // allow the ErrorCode to veto refresh
718: if (policy.allowRefresh) {
719: ErrorCode code = this .errorDocument.getCode();
720: if (!code.isRefreshAllowed()) {
721: policy.allowRefresh = false;
722: if (log.isTraceEnabled())
723: log.trace("ErrorCode " + code
724: + " vetoed allowing refresh.");
725: }
726: }
727:
728: if (log.isTraceEnabled())
729: log.trace("computed refresh plolicy: " + policy);
730:
731: return policy;
732: }
733:
734: /**
735: * Class to represent policy about whether channel refresh and
736: * reinstantiation is allowed.
737: */
738: private class RefreshPolicy {
739: /**
740: * Whether refreshing the channel is allowed.
741: */
742: boolean allowRefresh = true;
743:
744: /**
745: * Whether reloading the channel is allowed.
746: */
747: boolean allowReinstantiation = true;
748:
749: public String toString() {
750: return "refresh=" + this .allowRefresh + " reinstantiate="
751: + this .allowReinstantiation;
752: }
753: }
754:
755: /**
756: * @return Returns the errorDocument.
757: */
758: public ErrorDocument getErrorDocument() {
759: return this .errorDocument;
760: }
761:
762: /**
763: * @param errorDocument The errorDocument to set.
764: */
765: public void setErrorDocument(ErrorDocument errorDocument) {
766: this .errorDocument = errorDocument;
767: }
768:
769: /**
770: * Returns true iff this CError instance is acting as a placeholder.
771: * @return Returns true iff this CError instance is acting as a placeholder.
772: */
773: boolean isPlaceHolder() {
774: return this .placeHolder;
775: }
776:
777: /**
778: * Configure this CError instance to act as a placeholder. In placeholder
779: * mode, we do not present refresh and restart controls. Instead, we
780: * display a message about why we have taken the place of a channel -
781: * perhaps because the user is not authorized to view the channel or
782: * because the channel no longer exists.
783: * @param placeHolder true to suppress refresh and renew controls, false otherwise
784: */
785: void setPlaceHolder(boolean placeHolder) {
786: this .placeHolder = placeHolder;
787: }
788:
789: public String toString() {
790: StringBuffer sb = new StringBuffer();
791: sb.append(getClass().getName());
792: sb.append(" errorDocument:[").append(this .errorDocument)
793: .append("]");
794: sb.append(" placeholder:").append(this .placeHolder);
795: sb.append(" showStackTrace:").append(this .showStackTrace);
796: sb.append(" sslTitle:[").append(this .ssTitle).append("]");
797: return sb.toString();
798: }
799: }
|