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