001    package org.tynamo.exceptionpage.services;
002    
003    import java.io.IOException;
004    import java.io.OutputStream;
005    
006    import org.apache.tapestry5.Link;
007    import org.apache.tapestry5.internal.services.LinkSource;
008    import org.apache.tapestry5.runtime.ComponentEventException;
009    import org.apache.tapestry5.services.ComponentClassResolver;
010    import org.apache.tapestry5.services.Request;
011    import org.apache.tapestry5.services.RequestExceptionHandler;
012    import org.apache.tapestry5.services.Response;
013    import org.tynamo.exceptionpage.ContextAwareException;
014    
015    public class ConfigurableRequestExceptionHandler implements RequestExceptionHandler {
016            private ExceptionHandler exceptionHandler;
017            private RequestExceptionHandler defaultRequestExceptionHandler;
018            private LinkSource linkSource;
019            private Response response;
020            private ComponentClassResolver componentClassResolver;
021            private Request request;
022    
023            public ConfigurableRequestExceptionHandler(RequestExceptionHandler requestExceptionHandler,
024                            ComponentClassResolver componentClassResolver, LinkSource linkSource, Request request, Response response,
025                            ExceptionHandler exceptionHandler) {
026                    defaultRequestExceptionHandler = requestExceptionHandler;
027                    this.componentClassResolver = componentClassResolver;
028                    this.linkSource = linkSource;
029                    this.request = request;
030                    this.response = response;
031                    this.exceptionHandler = exceptionHandler;
032            }
033    
034            protected Object[] formExceptionContext(Throwable exception) {
035                    if (exception instanceof ContextAwareException) return ((ContextAwareException) exception).getContext();
036                    if (exception.getMessage() == null) return new Object[0];
037                    return new Object[] { exception.getMessage() };
038            }
039    
040            public void handleRequestException(Throwable exception) throws IOException {
041                    Throwable cause = exception;
042                    // Throw away the wrapped exceptions first
043                    while (cause instanceof ComponentEventException) {
044                            if (cause.getCause() == null) break;
045                            cause = cause.getCause();
046                    }
047                    if (!exceptionHandler.getConfiguration().containsKey(cause.getClass())) {
048                            defaultRequestExceptionHandler.handleRequestException(exception);
049                            return;
050                    }
051    
052                    Link link = linkSource.createPageRenderLink(componentClassResolver.resolvePageClassNameToPageName(exceptionHandler.getConfiguration()
053                                    .get(cause.getClass()).getName()), false, formExceptionContext(cause));
054                    try {
055                            if (request.isXHR()) {
056                                    OutputStream os = response.getOutputStream("application/json;charset=UTF-8");
057                                    os.write(("{\"script\":\"window.location.replace('" + link.toAbsoluteURI() + "');\"}").getBytes());
058                                    os.close();
059                            } else response.sendRedirect(link);
060                    }
061                    // This could throw exceptions if this is already a render request, but it's
062                    // user's responsibility not to abuse the mechanism
063                    catch (Exception e) {
064                            // Nothing to do but delegate
065                            defaultRequestExceptionHandler.handleRequestException(exception);
066                    }
067            }
068    }