Home | History | Annotate | Download | only in servlet
      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