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