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}