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 }