FilterDefinition.java revision 1db3e7924216995afc2e042515938b5c93a7416a
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.collect.Iterators;
19import com.google.inject.Injector;
20import com.google.inject.Key;
21import com.google.inject.Scopes;
22import com.google.inject.spi.BindingTargetVisitor;
23import com.google.inject.spi.ProviderInstanceBinding;
24import com.google.inject.spi.ProviderWithExtensionVisitor;
25
26import java.util.Collections;
27import java.util.Enumeration;
28import java.util.HashMap;
29import java.util.Map;
30import java.util.Set;
31import java.util.concurrent.atomic.AtomicReference;
32
33import javax.servlet.Filter;
34import javax.servlet.FilterConfig;
35import javax.servlet.ServletContext;
36import javax.servlet.ServletException;
37import javax.servlet.http.HttpServletRequest;
38
39/**
40 * An internal representation of a filter definition against a particular URI pattern.
41 *
42 * @author dhanji@gmail.com (Dhanji R. Prasanna)
43 */
44class 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 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 = request.getRequestURI().substring(request.getContextPath().length());
156
157    if (shouldFilter(path)) {
158      return filter.get();
159    } else {
160      return null;
161    }
162  }
163
164  //VisibleForTesting
165  Filter getFilter() {
166    return filter.get();
167  }
168}
169