    001    package org.apache.tapestry5.internal.services;
    002
    003    import org.apache.tapestry5.services.*;
    004
    005    import java.io.IOException;
    006
    007    /**
    008     * Filter for the {@link org.apache.tapestry5.services.RequestHandler} pipeline used to intercept and report
    009     * exceptions.
    010     */
    011    public class RequestErrorFilter implements RequestFilter
    012    {
    013        private final InternalRequestGlobals internalRequestGlobals;
    014        private final RequestExceptionHandler exceptionHandler;
    015
    016        public RequestErrorFilter(InternalRequestGlobals internalRequestGlobals, RequestExceptionHandler exceptionHandler)
    017        {
    018            this.internalRequestGlobals = internalRequestGlobals;
    019            this.exceptionHandler = exceptionHandler;
    020        }
    021
    022        public boolean service(Request request, Response response, RequestHandler handler) throws IOException
    023        {
    024            try
    025            {
    026                return handler.service(request, response);
    027            }
    028            catch (IOException ex)
    029            {
    030                // Pass it through.
    031                throw ex;
    032            }
    033            catch (Throwable ex)
    034            {
    035                // Most of the time, we've got exception linked up the kazoo ... but when ClassLoaders
    036                // get involved, things go screwy.  Exceptions when transforming classes can cause
    037                // a NoClassDefFoundError with no cause; here we're trying to link the cause back in.
    038                // TAPESTRY-2078
    039
    040                Throwable exceptionToReport = attachNewCause(ex, internalRequestGlobals.getClassLoaderException());
    041
    042                exceptionHandler.handleRequestException(exceptionToReport);
    043
    044                // We assume a reponse has been sent and there's no need to handle the request
    045                // further.
    046
    047                return true;
    048            }
    049        }
    050
    051        private Throwable attachNewCause(Throwable exception, Throwable underlyingCause)
    052        {
    053            if (underlyingCause == null) return exception;
    054
    055            Throwable current = exception;
    056
    057            while (current != null)
    058            {
    059
    060                if (current == underlyingCause) return exception;
    061
    062                Throwable cause = current.getCause();
    063
    064                // Often, exceptions report themselves as their own cause.
    065
    066                if (current == cause) break;
    067
    068                if (cause == null)
    069                {
    070
    071                    try
    072                    {
    073                        current.initCause(underlyingCause);
    074
    075                        return exception;
    076                    }
    077                    catch (IllegalStateException ex)
    078                    {
    079                        // TAPESTRY-2284: sometimes you just can't init the cause, and there's no way to
    080                        // find out without trying.
    081
    082                    }
    083                }
    084
    085                // Otherwise, continue working down the chain until we find a place where we can attach
    086
    087                current = cause;
    088            }
    089
    090            // Found no place to report the exeption, so report the underlying cause (and lose out
    091            // on all the other context).
    092
    093            return underlyingCause;
    094        }
    095    }