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