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}