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