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    }