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}