FilterChainInvocation.java revision fee2d237bd4d6f89ed510861a25d4f3eafa8f1d1
1/** 2 * Copyright (C) 2008 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package com.google.inject.servlet; 17 18import com.google.common.base.Throwables; 19import com.google.common.collect.ImmutableSet; 20import com.google.common.collect.Lists; 21 22import java.io.IOException; 23import java.util.List; 24import java.util.Set; 25 26import javax.servlet.FilterChain; 27import javax.servlet.ServletException; 28import javax.servlet.ServletRequest; 29import javax.servlet.ServletResponse; 30import javax.servlet.http.HttpServletRequest; 31import javax.servlet.http.HttpServletResponse; 32 33/** 34 * A Filter chain impl which basically passes itself to the "current" filter and iterates the chain 35 * on {@code doFilter()}. Modeled on something similar in Apache Tomcat. 36 * 37 * Following this, it attempts to dispatch to guice-servlet's registered servlets using the 38 * ManagedServletPipeline. 39 * 40 * And the end, it proceeds to the web.xml (default) servlet filter chain, if needed. 41 * 42 * @author Dhanji R. Prasanna 43 * @since 1.0 44 */ 45class FilterChainInvocation implements FilterChain { 46 47 private static final Set<String> SERVLET_INTERNAL_METHODS = ImmutableSet.of( 48 FilterDefinition.class.getName() + ".doFilter", 49 FilterChainInvocation.class.getName() + ".doFilter"); 50 51 private final FilterDefinition[] filterDefinitions; 52 private final FilterChain proceedingChain; 53 private final ManagedServletPipeline servletPipeline; 54 55 //state variable tracks current link in filterchain 56 private int index = -1; 57 // whether or not we've caught an exception & cleaned up stack traces 58 private boolean cleanedStacks = false; 59 60 public FilterChainInvocation(FilterDefinition[] filterDefinitions, 61 ManagedServletPipeline servletPipeline, FilterChain proceedingChain) { 62 63 this.filterDefinitions = filterDefinitions; 64 this.servletPipeline = servletPipeline; 65 this.proceedingChain = proceedingChain; 66 } 67 68 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse) 69 throws IOException, ServletException { 70 index++; 71 72 GuiceFilter.Context previous = GuiceFilter.localContext.get(); 73 HttpServletRequest request = (HttpServletRequest) servletRequest; 74 HttpServletResponse response = (HttpServletResponse) servletResponse; 75 HttpServletRequest originalRequest 76 = (previous != null) ? previous.getOriginalRequest() : request; 77 GuiceFilter.localContext.set(new GuiceFilter.Context(originalRequest, request, response)); 78 try { 79 //dispatch down the chain while there are more filters 80 if (index < filterDefinitions.length) { 81 filterDefinitions[index].doFilter(servletRequest, servletResponse, this); 82 } else { 83 //we've reached the end of the filterchain, let's try to dispatch to a servlet 84 final boolean serviced = servletPipeline.service(servletRequest, servletResponse); 85 86 //dispatch to the normal filter chain only if one of our servlets did not match 87 if (!serviced) { 88 proceedingChain.doFilter(servletRequest, servletResponse); 89 } 90 } 91 } catch (Throwable t) { 92 // Only clean on the first pass through -- one exception deep in a filter 93 // will propogate upward & hit this catch clause multiple times. We don't 94 // want to iterate through the stack elements for every filter. 95 if (!cleanedStacks) { 96 cleanedStacks = true; 97 pruneStacktrace(t); 98 } 99 Throwables.propagateIfInstanceOf(t, ServletException.class); 100 Throwables.propagateIfInstanceOf(t, IOException.class); 101 throw Throwables.propagate(t); 102 } finally { 103 GuiceFilter.localContext.set(previous); 104 } 105 } 106 107 /** 108 * Removes stacktrace elements related to AOP internal mechanics from the 109 * throwable's stack trace and any causes it may have. 110 */ 111 private void pruneStacktrace(Throwable throwable) { 112 for (Throwable t = throwable; t != null; t = t.getCause()) { 113 StackTraceElement[] stackTrace = t.getStackTrace(); 114 List<StackTraceElement> pruned = Lists.newArrayList(); 115 for (StackTraceElement element : stackTrace) { 116 String name = element.getClassName() + "." + element.getMethodName(); 117 if (!SERVLET_INTERNAL_METHODS.contains(name)) { 118 pruned.add(element); 119 } 120 } 121 t.setStackTrace(pruned.toArray(new StackTraceElement[pruned.size()])); 122 } 123 } 124} 125