    001package org.apache.tapestry5.internal.services;
    002
    003import org.apache.tapestry5.services.*;
    004
    005import java.io.IOException;
    006
    007/**
    008 * Filter for the {@link org.apache.tapestry5.services.RequestHandler} pipeline used to intercept and report
    009 * exceptions.
    010 */
    011public 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}