Package org.jasig.portal.channels.error

Source Code of org.jasig.portal.channels.error.CError

/**
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.jasig.portal.channels.error;

import java.io.PrintWriter;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.portal.ChannelCacheKey;
import org.jasig.portal.ChannelManager;
import org.jasig.portal.ChannelRuntimeData;
import org.jasig.portal.ChannelStaticData;
import org.jasig.portal.EntityIdentifier;
import org.jasig.portal.ICacheable;
import org.jasig.portal.IChannel;
import org.jasig.portal.ICharacterChannel;
import org.jasig.portal.IPrivileged;
import org.jasig.portal.IPrivilegedChannel;
import org.jasig.portal.IResetableChannel;
import org.jasig.portal.IUserPreferencesManager;
import org.jasig.portal.MediaManager;
import org.jasig.portal.PortalControlStructures;
import org.jasig.portal.PortalEvent;
import org.jasig.portal.PortalException;
import org.jasig.portal.ThemeStylesheetDescription;
import org.jasig.portal.channels.BaseChannel;
import org.jasig.portal.channels.error.error2xml.IThrowableToElement;
import org.jasig.portal.i18n.LocaleManager;
import org.jasig.portal.layout.IUserLayoutManager;
import org.jasig.portal.layout.node.IUserLayoutNodeDescription;
import org.jasig.portal.security.IAuthorizationPrincipal;
import org.jasig.portal.security.IPerson;
import org.jasig.portal.serialize.BaseMarkupSerializer;
import org.jasig.portal.serialize.OutputFormat;
import org.jasig.portal.serialize.XMLSerializer;
import org.jasig.portal.services.AuthorizationService;
import org.jasig.portal.spring.locator.ThrowableToElementLocator;
import org.jasig.portal.utils.XML;
import org.jasig.portal.utils.XSLT;
import org.w3c.dom.Document;
import org.xml.sax.ContentHandler;

/**
* CError is the error channel, also known as the null channel; it is designed
* to render in place of other channels when something goes wrong.
* <p>
* Possible conditions when CError is invoked are:
* <ul>
* <li>Channel has thrown a Throwable from one of the IChannel or
* IPrivilegedChannel methods.</li>
* <li>Channel has timed out on rendering and was terminated.</li>
* <li>uPortal has rejected a channel for some reason. In this case a general
* message is constructed by the portal.</li>
* </ul>
*
* @author Peter Kharchenko, pkharchenko@interactivebusiness.com
* @author andrew.petro@yale.edu
* @version $Revision: 19776 $ $Date: 2010-01-14 16:17:21 -0600 (Thu, 14 Jan 2010) $
* @since uPortal 2.5.  Prior to 2.5, CError existed only as org.jasig.portal.channels.CError.
* @deprecated All IChannel implementations should be migrated to portlets
*/
@Deprecated
public final class CError extends BaseChannel implements IPrivilegedChannel, ICacheable, ICharacterChannel {

    private static final Log log = LogFactory.getLog(CError.class);

    /**
     * An ErrorDocument representing the error about which we are reporting and
     * providing a source for XML to be rendered by our XSLT.
     */
    private ErrorDocument errorDocument = new ErrorDocument();
   
    /**
     * The channel instance that failed.
     */
    private IChannel targetChannel = null;

    /**
     * CError is a placeholder when it is taking the place of a channel that no
     * longer exists or that the user doesn't have permission to render. CError
     * is not a placeholder when it represents the failure of a channel that
     * actually tried to render.
     */
    private boolean placeHolder = false;

    /**
     * True if we should display the stack trace of the stored Throwable, if
     * any, at rendering.
     */
    private boolean showStackTrace = false;

    /**
     * The title of the stylesheet we should use to render.
     */
    private String ssTitle = null;

    private PortalControlStructures portcs;

    /**
     * The location of our our .ssl file.
     */
    private static final String sslLocation = "CError/CError.ssl";

    private static final MediaManager MEDIAMANAGER=MediaManager.getMediaManager(true);

    /**
     * Construct an uninitialized instance of the CError channel.
     */
    public CError() {
        // inject into our ErrorDocument the configured IThrowableToElement that
        // will translate from Throwables to XML that we can render

        try {
            IThrowableToElement throwableToElement = ThrowableToElementLocator.getThrowableToElement();
            this.errorDocument.setThrowableToElement(throwableToElement);
        }
        catch (Exception e) {
            // do not allow a Beans failure to break CError
            log.warn("Failed to retrieve mapping from throwables to Elements for CError rendering from the WebApplicationContext, the default mapping will be used.", e);
            // since our ErrorDocument has a default mapping from Throwables to Elements
            // we can fall back on that default by not doing anything.
        }
    }

    /**
     * Construct an instance of the Error channel representing a failure to
     * render of a particular subscribed channel for reason of having thrown a
     * Throwable.
     *
     * @param errorCode one of the static error codes of this class
     * @param throwable cause of failed channel's failure
     * @param channelSubscribeId identifies the failed channel
     * @param channelInstance the failed channel
     */
    public CError(ErrorCode errorCode, Throwable throwable, String channelSubscribeId, IChannel channelInstance) {
        this();
       
        if (log.isTraceEnabled()) {
            log.trace("CError(" + errorCode + ", throwable=[" + throwable
                    + "], chanSubId=" + channelSubscribeId
                    + ", channelInstance=[" + channelInstance + "]");
        }

       
        this.errorDocument.setChannelSubscribeId(channelSubscribeId);
        this.errorDocument.setThrowable(throwable);
        this.targetChannel = channelInstance;
        this.errorDocument.setCode(errorCode);

        if (log.isTraceEnabled()) {
            log.trace("Instantiated CError: " + this);
        }
    }

    /**
     * Instantiate a CError representing a particular channel's failure,
     * including a message and errorCode, but not a Throwable.
     *
     * @param errorCode one of the static error codes of this class
     * @param message describes error
     * @param channelSubscribeId identifies failed channel
     * @param channelInstance failed channel
     */
    public CError(ErrorCode errorCode, String message, String channelSubscribeId, IChannel channelInstance) {
        this();
       
        if (log.isTraceEnabled()) {
            log.trace("CError(" + errorCode + ", message=[" + message +
                    "], chanSubId=" + channelSubscribeId +
                    ", channelInstance=[" + channelInstance + "]");
        }
       
        this.errorDocument.setChannelSubscribeId(channelSubscribeId);
        this.targetChannel = channelInstance;
        this.errorDocument.setCode(errorCode);
        this.errorDocument.setMessage(message);
       
        if (log.isTraceEnabled()) {
            log.trace("Instantiated CError: " + this);
        }
    }

    /**
     * Instantiate a CError instance representing the failure of some particular
     * channel, including an error code, message, and the Throwable.
     *
     * @param errorCode one of the static error codes of this class
     * @param exception thrown by the failed channel
     * @param channelSubscribeId identifies failed channel
     * @param channelInstance the failed channel instance
     * @param message message describing failure
     */
    public CError(ErrorCode errorCode, Throwable exception, String channelSubscribeId, IChannel channelInstance, String message) {
        this(errorCode, exception, channelSubscribeId, channelInstance);
        this.errorDocument.setMessage(message);
       
        if (log.isTraceEnabled()) {
            log.trace("Instantiated CError: " + this);
        }
    }

    /**
     * Resets internal state of CError.
     *
     * @param errorCode new errorCode value
     * @param throwable new stored Throwable
     * @param channelSubscribeId new channelSubscribeId
     * @param channelInstance new failed channel
     * @param message new failure message
     */
    private void resetCError(ErrorCode errorCode, Throwable throwable, String channelSubscribeId, IChannel channelInstance, String message) {
        this.errorDocument.setCode(errorCode);
        this.errorDocument.setThrowable(throwable);
        this.errorDocument.setChannelSubscribeId(channelSubscribeId);

        this.targetChannel = channelInstance;

        this.errorDocument.setMessage(message);
       
        if (log.isTraceEnabled()) {
            log.trace("Reset CError to: " + this);
        }
    }

    public void setPortalControlStructures(PortalControlStructures pcs) {
        this.portcs = pcs;
    }
   
    public void receiveEvent(PortalEvent ev) {
        if (targetChannel != null) {
            if (this.targetChannel instanceof IPrivileged) {
                ((IPrivileged) this.targetChannel).setPortalControlStructures(this.portcs);
            }

            // propagate the portal events to the normal channel
            targetChannel.receiveEvent(ev);
        }
        super.receiveEvent(ev);
    }


    /*
     * This is so CError can be used by getUserLayout() as a placeholder for
     * channels that have either been deleted from the portal database or the
     * users permission to use the channel has been removed (permanently or
     * temporarily).
     */
    public void setStaticData(ChannelStaticData sd) {
        if (log.isTraceEnabled()) {
            log.trace("setStaticData(" + sd + ")");
        }
        if (sd == null) {
            log.error("ChannelStaticData argument to setStaticData() illegally null.");
            return;
        }
       
        try {
            this.errorDocument.setMessage(sd.getParameter("CErrorMessage"));
            this.errorDocument.setChannelSubscribeId(sd.getParameter("CErrorChanId"));
           
            final String value = sd.getParameter("CErrorErrorId");
            if (value != null) {
                this.errorDocument.setCode(ErrorCode.codeForInt(Integer.parseInt(value)));
            }
            this.placeHolder = true; // Should only get here if we are a
                                            // "normal channel"
        }
        catch (Throwable t) {
            log.error("Error setting static data of CError instance", t);
        }
    }
   
    protected void renderChannel(IChannel channel, ContentHandler contentHandler, PrintWriter printWriter) throws Exception {
        if (contentHandler != null) {
            channel.renderXML(contentHandler);
        }
        else {
            if (channel instanceof ICharacterChannel) {
                ((ICharacterChannel) channel).renderCharacters(printWriter);
            }
            else {
                final IUserPreferencesManager userPreferencesManager = this.portcs.getUserPreferencesManager();
                final ThemeStylesheetDescription tsd = userPreferencesManager.getThemeStylesheetDescription();
                final String serializerName = tsd.getSerializerName();
               
                final BaseMarkupSerializer serOut = MEDIAMANAGER.getSerializerByName(serializerName, printWriter);
                channel.renderXML(serOut);
            }
        }
    }
   
    /**
     * Logic common to both renderXml and renderCharacters for handling a request for an errored out channel.
     */
    protected void doCommonErrorHandling(ContentHandler contentHandler, PrintWriter printWriter) {
        // runtime data processing needs to be done here, otherwise replaced
        // channel will get duplicated setRuntimeData() calls

        log.trace("Entering doCommonErrorHandling()");

        final String channelSubscribeId = this.errorDocument.getChannelSubscribeId();
        if (channelSubscribeId != null) {
           
            final String chFate = this.runtimeData.getParameter("action");
            if (log.isDebugEnabled()) {
                log.debug("Channel fate is [" + chFate + "] for chanSubscribeId=" + channelSubscribeId);
            }
           
            if (chFate != null) {
                // a fate has been chosen
                if (chFate.equals("retry")) {
                    try {
                        // clean things up for the channel
                        if (this.targetChannel instanceof IResetableChannel) {
                            if (this.targetChannel instanceof IPrivileged) {
                                ((IPrivileged) this.targetChannel).setPortalControlStructures(this.portcs);
                            }
                           
                            this.targetChannel.setRuntimeData(this.runtimeData);

                            ((IResetableChannel) this.targetChannel).prepareForRefresh();
                        }

                        ChannelRuntimeData crd = (ChannelRuntimeData) this.runtimeData.clone();
                        crd.clear(); // Remove parameters

                        if (this.targetChannel instanceof IPrivileged) {
                            ((IPrivileged) this.targetChannel).setPortalControlStructures(this.portcs);
                        }
                       
                        this.targetChannel.setRuntimeData(crd);
                       
                        final ChannelManager cm = this.portcs.getChannelManager();
                        cm.setChannelInstance(channelSubscribeId, this.targetChannel);
                       
                        this.renderChannel(this.targetChannel, contentHandler, printWriter);

                        return;
                    }
                    catch (Exception e) {
                        // if any of the above didn't work, fall back to the error channel
                        resetCError(ErrorCode.SET_RUNTIME_DATA_EXCEPTION, e, channelSubscribeId, this.targetChannel, "Channel failed a refresh attempt.");
                    }
                }
                else if (chFate.equals("restart")) {
                    final ChannelManager cm = this.portcs.getChannelManager();

                    try {
                        //Clean things up for the channel
                        if (this.targetChannel instanceof IResetableChannel) {
                            if (this.targetChannel instanceof IPrivileged) {
                                ((IPrivileged) this.targetChannel).setPortalControlStructures(this.portcs);
                            }
                           
                            this.targetChannel.setRuntimeData(this.runtimeData);

                            ((IResetableChannel) this.targetChannel).prepareForReset();
                        }

                        final ChannelRuntimeData crd = (ChannelRuntimeData) this.runtimeData.clone();
                        crd.clear();
                       
                        if ((this.targetChannel = cm.instantiateChannel(this.portcs.getHttpServletRequest(), this.portcs.getHttpServletResponse(), channelSubscribeId)) == null) {
                            resetCError(ErrorCode.GENERAL_ERROR, null, channelSubscribeId, null, "Channel failed to reinstantiate!");
                        }
                        else {
                            try {
                                if (this.targetChannel instanceof IPrivileged) {
                                    ((IPrivileged) this.targetChannel).setPortalControlStructures(this.portcs);
                                }
                               
                                this.targetChannel.setRuntimeData(crd);
                               
                                this.renderChannel(this.targetChannel, contentHandler, printWriter);
                               
                                return;
                            }
                            catch (Exception e) {
                                // if any of the above didn't work, fall back to the error channel
                                resetCError(ErrorCode.SET_RUNTIME_DATA_EXCEPTION, e, channelSubscribeId, this.targetChannel, "Channel failed a reload attempt.");
                                cm.setChannelInstance(channelSubscribeId, this);
                                log.error("an error occurred during channel reinitialization.", e);
                            }
                        }
                    }
                    catch (Exception e) {
                        resetCError(ErrorCode.GENERAL_ERROR, e, channelSubscribeId, null, "Channel failed to reinstantiate!");
                        log.error("an error occurred during channel reinstantiation.", e);
                    }
                }
                else if (chFate.equals("toggle_stack_trace")) {
                    this.showStackTrace = !this.showStackTrace;
                }
            }
        }

        // if channel's render method was to be called, we would've returned by now
        if (contentHandler != null) {
            localRenderXML(contentHandler);
        }
        else {
            BaseMarkupSerializer serOut = null;
            try {
                final IUserPreferencesManager userPreferencesManager = this.portcs.getUserPreferencesManager();
                final ThemeStylesheetDescription tsd = userPreferencesManager.getThemeStylesheetDescription();
                serOut = MEDIAMANAGER.getSerializerByName(tsd.getSerializerName(), printWriter);
            }
            catch (Exception e) {
                log.error("unable to obtain proper markup serializer : ", e);
            }

            if (serOut == null) {
                // default to XML serializer
                final OutputFormat frmt = new OutputFormat("XML", "UTF-8", true);
                serOut = new XMLSerializer(printWriter, frmt);
            }

            localRenderXML(serOut);
        }
    }

    public void renderXML(ContentHandler out) {
        this.doCommonErrorHandling(out, null);
    }

    private void localRenderXML(ContentHandler out) {
        // note: this method should be made very robust. Optimally, it should
        // not rely on XSLT to do the job. That means that mime-type dependent
        // output should be generated directly within the method.
        // For now, we'll just do it the usual way.

        if (log.isTraceEnabled()) {
            log.trace("Entering localRenderXML() for CError " + this);
        }

        final String channelSubscribeId = this.errorDocument.getChannelSubscribeId();

        final IUserPreferencesManager userPreferencesManager = this.portcs.getUserPreferencesManager();
        if (channelSubscribeId != null) {
            try {
                final IUserLayoutManager userLayoutManager = userPreferencesManager.getUserLayoutManager();
                final IUserLayoutNodeDescription channelNode = userLayoutManager.getNode(channelSubscribeId);
                this.errorDocument.setChannelName(channelNode.getName());
            }
            catch (Throwable t) {
                log.error("Error determining name of channel with subscribe id [" + channelSubscribeId + "]", t);
            }
        }

        // defaults to refresh and reload not allowed.
        final RefreshPolicy policy;
        if (channelSubscribeId != null) {
            policy = computeRefreshPolicy();
        }
        else {
            policy = new RefreshPolicy();
        }

        // Decide whether to render a friendly or detailed screen
        this.ssTitle = "friendly";
        try {
            final AuthorizationService authService = AuthorizationService.instance();
            final IPerson person = userPreferencesManager.getPerson();
            final EntityIdentifier ei = person.getEntityIdentifier();
            final IAuthorizationPrincipal ap = authService.newPrincipal(ei.getKey(), ei.getType());
           
            if (ap.hasPermission(SupportedPermissions.OWNER, SupportedPermissions.VIEW_ACTIVITY, SupportedPermissions.DETAILS_TARGET)) {
                this.ssTitle = "detailed";
            }
        }
        catch (Throwable t) {
            log.error("Exception checking whether user authorized to view detailed CError view.  Defaulting to friendly view.", t);
        }

        if (log.isTraceEnabled()) {
            log.trace("SSL title is " + this.ssTitle);
        }

        final Document doc = this.errorDocument.getDocument();

        if (log.isWarnEnabled()) {
            try {
                log.warn("ErrorDocument XML is \n" + XML.serializeNode(doc));
            }
            catch (Exception e) {
                log.error("Failed to write error document XML to logger.", e);
            }
        }

        try {
            XSLT xslt = XSLT.getTransformer(this, this.runtimeData.getLocales());
            xslt.setXML(doc);
            xslt.setXSL(sslLocation, this.ssTitle, this.runtimeData.getBrowserInfo());
            xslt.setTarget(out);
            xslt.setStylesheetParameter("baseActionURL", this.runtimeData.getBaseActionURL());
            xslt.setStylesheetParameter("showStackTrace", String.valueOf(this.showStackTrace));
            xslt.setStylesheetParameter("allowRefresh", Boolean.toString(policy.allowRefresh));
            xslt.setStylesheetParameter("allowReinstantiation", Boolean.toString(policy.allowReinstantiation));
            xslt.transform();
        }
        catch (Exception e) {
            log.error("Things are bad. Error channel threw Exception rendering its stylesheet.", e);
        }
    }

    public ChannelCacheKey generateKey() {
        // check if either restart or refresh command has been given, otherwise
        // generate key
        if (this.runtimeData != null && this.runtimeData.getParameter("action") != null) {
            return null;
        }

        ChannelCacheKey k = new ChannelCacheKey();
        StringBuilder sbKey = new StringBuilder(1024);

        // assume that errors can be cached system-wide
        k.setKeyScope(ChannelCacheKey.SYSTEM_KEY_SCOPE);

        sbKey.append(this.getClass().getName()).append(": errorDocument=").append(this.errorDocument);
        sbKey.append(" strace=").append(Boolean.toString(this.showStackTrace));
        sbKey.append(", mode=").append(this.ssTitle);
        sbKey.append(", locales=").append(LocaleManager.stringValueOf(this.runtimeData.getLocales()));
        k.setKey(sbKey.toString());
        return k;
    }

    public boolean isCacheValid(Object validity) {
        return true;
    }

    public void renderCharacters(PrintWriter out) throws PortalException {
        this.doCommonErrorHandling(null, out);
    }
   
    /**
     * Compute the refresh policy.
     * Assumes channel subcribe ID is not null, since in that case there is no
     * question about the policy - you cannot reinstantiate or refresh
     * unknown channels.
     * @return a RefreshPolicy suitable to our state.
     */
    private RefreshPolicy computeRefreshPolicy() {
        log.trace("entering computeRefreshPolicy()");
        RefreshPolicy policy = new RefreshPolicy();
       
        if (this.placeHolder) {
            // We are just displaying a message.
            // No channel to refresh or reload
            policy.allowRefresh = false;
            policy.allowReinstantiation = false;
            if (log.isTraceEnabled())
                log.trace("policy is [" + policy
                        + "] because we are a placeholder.");
        } else {
            // allow the PortalException, if any, to configure refresh and
            // reload
            Throwable errorThrowable = this.errorDocument.getThrowable();
            if (errorThrowable != null
                    && errorThrowable instanceof PortalException) {

                PortalException portalException = (PortalException) errorThrowable;

                policy.allowRefresh = portalException.isRefreshable();
                policy.allowReinstantiation = portalException.isReinstantiable();
               
                if (log.isTraceEnabled()){
                    log.trace("PortalException [" + portalException +
                            "] implied refresh policy [" + policy + "]");
                }
            }
        }

        // allow the ErrorCode to veto refresh
        if (policy.allowRefresh) {
            ErrorCode code = this.errorDocument.getCode();
            if (!code.isRefreshAllowed()) {
                policy.allowRefresh = false;
                if (log.isTraceEnabled())
                    log.trace("ErrorCode " + code + " vetoed allowing refresh.");
            }
        }
       
        if (log.isTraceEnabled())
            log.trace("computed refresh plolicy: " + policy);
       
        return policy;
    }
   
    /**
     * Class to represent policy about whether channel refresh and
     * reinstantiation is allowed.
     */
    private class RefreshPolicy{
        /**
         * Whether refreshing the channel is allowed.
         */
        boolean allowRefresh = true;
       
        /**
         * Whether reloading the channel is allowed.
         */
        boolean allowReinstantiation = true;
       
        public String toString() {
            return "refresh=" + this.allowRefresh
                + " reinstantiate=" + this.allowReinstantiation;
        }
    }
   
    /**
     * @return Returns the errorDocument.
     */
    public ErrorDocument getErrorDocument() {
        return this.errorDocument;
    }
   
    /**
     * @param errorDocument The errorDocument to set.
     */
    public void setErrorDocument(ErrorDocument errorDocument) {
        this.errorDocument = errorDocument;
    }
   
    /**
     * Returns true iff this CError instance is acting as a placeholder.
     * @return Returns true iff this CError instance is acting as a placeholder.
     */
    boolean isPlaceHolder() {
        return this.placeHolder;
    }
   
    /**
     * Configure this CError instance to act as a placeholder.  In placeholder
     * mode, we do not present refresh and restart controls.  Instead, we
     * display a message about why we have taken the place of a channel -
     * perhaps because the user is not authorized to view the channel or
     * because the channel no longer exists.
     * @param placeHolder true to suppress refresh and renew controls, false otherwise
     */
    void setPlaceHolder(boolean placeHolder) {
        this.placeHolder = placeHolder;
    }
   
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append(getClass().getName());
        sb.append(" errorDocument:[").append(this.errorDocument).append("]");
        sb.append(" placeholder:").append(this.placeHolder);
        sb.append(" showStackTrace:").append(this.showStackTrace);
        sb.append(" sslTitle:[").append(this.ssTitle).append("]");
        return sb.toString();
    }
}
TOP

Related Classes of org.jasig.portal.channels.error.CError

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.