FilterDefinition.java revision 71fe73ed4c6bcccbc6b71be1061435300b195c2f
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.inject.Injector; 19import com.google.inject.Key; 20import com.google.inject.Scopes; 21import com.google.inject.internal.util.Iterators; 22import com.google.inject.spi.BindingTargetVisitor; 23import com.google.inject.spi.ProviderInstanceBinding; 24import com.google.inject.spi.ProviderWithExtensionVisitor; 25 26import java.io.IOException; 27import java.util.Collections; 28import java.util.Enumeration; 29import java.util.HashMap; 30import java.util.Map; 31import java.util.Set; 32import java.util.concurrent.atomic.AtomicReference; 33import javax.servlet.Filter; 34import javax.servlet.FilterConfig; 35import javax.servlet.ServletContext; 36import javax.servlet.ServletException; 37import javax.servlet.ServletRequest; 38import javax.servlet.ServletResponse; 39import javax.servlet.http.HttpServletRequest; 40 41/** 42 * An internal representation of a filter definition against a particular URI pattern. 43 * 44 * @author dhanji@gmail.com (Dhanji R. Prasanna) 45 */ 46class FilterDefinition implements ProviderWithExtensionVisitor<FilterDefinition> { 47 private final String pattern; 48 private final Key<? extends Filter> filterKey; 49 private final UriPatternMatcher patternMatcher; 50 private final Map<String, String> initParams; 51 // set only if this was bound to an instance of a Filter. 52 private final Filter filterInstance; 53 54 // always set after init is called. 55 private final AtomicReference<Filter> filter = new AtomicReference<Filter>(); 56 57 public FilterDefinition(String pattern, Key<? extends Filter> filterKey, 58 UriPatternMatcher patternMatcher, Map<String, String> initParams, Filter filterInstance) { 59 this.pattern = pattern; 60 this.filterKey = filterKey; 61 this.patternMatcher = patternMatcher; 62 this.initParams = Collections.unmodifiableMap(new HashMap<String, String>(initParams)); 63 this.filterInstance = filterInstance; 64 } 65 66 public FilterDefinition get() { 67 return this; 68 } 69 70 public <B, V> V acceptExtensionVisitor(BindingTargetVisitor<B, V> visitor, 71 ProviderInstanceBinding<? extends B> binding) { 72 if(visitor instanceof ServletModuleTargetVisitor) { 73 if(filterInstance != null) { 74 return ((ServletModuleTargetVisitor<B, V>)visitor).visit( 75 new InstanceFilterBindingImpl(initParams, 76 pattern, 77 filterInstance, 78 patternMatcher)); 79 } else { 80 return ((ServletModuleTargetVisitor<B, V>)visitor).visit( 81 new LinkedFilterBindingImpl(initParams, 82 pattern, 83 filterKey, 84 patternMatcher)); 85 } 86 } else { 87 return visitor.visit(binding); 88 } 89 } 90 91 private boolean shouldFilter(String uri) { 92 return patternMatcher.matches(uri); 93 } 94 95 public void init(final ServletContext servletContext, Injector injector, 96 Set<Filter> initializedSoFar) throws ServletException { 97 98 // This absolutely must be a singleton, and so is only initialized once. 99 if (!Scopes.isSingleton(injector.getBinding(filterKey))) { 100 throw new ServletException("Filters must be bound as singletons. " 101 + filterKey + " was not bound in singleton scope."); 102 } 103 104 Filter filter = injector.getInstance(filterKey); 105 this.filter.set(filter); 106 107 // Only fire init() if this Singleton filter has not already appeared earlier 108 // in the filter chain. 109 if (initializedSoFar.contains(filter)) { 110 return; 111 } 112 113 //initialize our filter with the configured context params and servlet context 114 filter.init(new FilterConfig() { 115 public String getFilterName() { 116 return filterKey.toString(); 117 } 118 119 public ServletContext getServletContext() { 120 return servletContext; 121 } 122 123 public String getInitParameter(String s) { 124 return initParams.get(s); 125 } 126 127 public Enumeration getInitParameterNames() { 128 return Iterators.asEnumeration(initParams.keySet().iterator()); 129 } 130 }); 131 132 initializedSoFar.add(filter); 133 } 134 135 public void destroy(Set<Filter> destroyedSoFar) { 136 // filters are always singletons 137 Filter reference = filter.get(); 138 139 // Do nothing if this Filter was invalid (usually due to not being scoped 140 // properly), or was already destroyed. According to Servlet Spec: it is 141 // "out of service", and does not need to be destroyed. 142 // Also prevent duplicate destroys to the same singleton that may appear 143 // more than once on the filter chain. 144 if (null == reference || destroyedSoFar.contains(reference)) { 145 return; 146 } 147 148 try { 149 reference.destroy(); 150 } finally { 151 destroyedSoFar.add(reference); 152 } 153 } 154 155 public void doFilter(ServletRequest servletRequest, 156 ServletResponse servletResponse, FilterChainInvocation filterChainInvocation) 157 throws IOException, ServletException { 158 159 final String path = ((HttpServletRequest) servletRequest).getServletPath(); 160 161 if (shouldFilter(path)) { 162 filter.get() 163 .doFilter(servletRequest, servletResponse, filterChainInvocation); 164 165 } else { 166 //otherwise proceed down chain anyway 167 filterChainInvocation.doFilter(servletRequest, servletResponse); 168 } 169 } 170 171 //VisibleForTesting 172 Filter getFilter() { 173 return filter.get(); 174 } 175} 176