GuiceFilter.java revision b7a02b02d81c830d148355c90bc309bcd66fb592
1/** 2 * Copyright (C) 2006 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 17package com.google.inject.servlet; 18 19import com.google.inject.Inject; 20import com.google.inject.OutOfScopeException; 21 22import java.io.IOException; 23import java.lang.ref.WeakReference; 24import java.util.logging.Logger; 25 26import javax.servlet.Filter; 27import javax.servlet.FilterChain; 28import javax.servlet.FilterConfig; 29import javax.servlet.ServletContext; 30import javax.servlet.ServletException; 31import javax.servlet.ServletRequest; 32import javax.servlet.ServletResponse; 33import javax.servlet.http.HttpServletRequest; 34import javax.servlet.http.HttpServletResponse; 35 36/** 37 * <p> 38 * Apply this filter in web.xml above all other filters (typically), to all requests where you plan 39 * to use servlet scopes. This is also needed in order to dispatch requests to injectable filters 40 * and servlets: 41 * <pre> 42 * <filter> 43 * <filter-name>guiceFilter</filter-name> 44 * <filter-class><b>com.google.inject.servlet.GuiceFilter</b></filter-class> 45 * </filter> 46 * 47 * <filter-mapping> 48 * <filter-name>guiceFilter</filter-name> 49 * <url-pattern>/*</url-pattern> 50 * </filter-mapping> 51 * </pre> 52 * 53 * This filter must appear before every filter that makes use of Guice injection or servlet 54 * scopes functionality. Typically, you will only register this filter in web.xml and register 55 * any other filters (and servlets) using a {@link ServletModule}. 56 * 57 * @author crazybob@google.com (Bob Lee) 58 * @author dhanji@gmail.com (Dhanji R. Prasanna) 59 */ 60public class GuiceFilter implements Filter { 61 static final ThreadLocal<Context> localContext = new ThreadLocal<Context>(); 62 static volatile FilterPipeline pipeline = new DefaultFilterPipeline(); 63 64 /** 65 * We allow both the static and dynamic versions of the pipeline to exist. 66 */ 67 @Inject 68 private final FilterPipeline injectedPipeline = null; 69 70 /** Used to inject the servlets configured via {@link ServletModule} */ 71 static volatile WeakReference<ServletContext> servletContext = 72 new WeakReference<ServletContext>(null); 73 74 private static final String MULTIPLE_INJECTORS_WARNING = 75 "Multiple Servlet injectors detected. This is a warning " 76 + "indicating that you have more than one " 77 + GuiceFilter.class.getSimpleName() + " running " 78 + "in your web application. If this is deliberate, you may safely " 79 + "ignore this message. If this is NOT deliberate however, " 80 + "your application may not work as expected."; 81 82 //VisibleForTesting 83 @Inject 84 static void setPipeline(FilterPipeline pipeline) { 85 86 // This can happen if you create many injectors and they all have their own 87 // servlet module. This is legal, caveat a small warning. 88 if (GuiceFilter.pipeline instanceof ManagedFilterPipeline) { 89 Logger.getLogger(GuiceFilter.class.getName()).warning(MULTIPLE_INJECTORS_WARNING); 90 } 91 92 // We overwrite the default pipeline 93 GuiceFilter.pipeline = pipeline; 94 } 95 96 //VisibleForTesting 97 static void reset() { 98 pipeline = new DefaultFilterPipeline(); 99 } 100 101 public void doFilter(ServletRequest servletRequest, 102 ServletResponse servletResponse, FilterChain filterChain) 103 throws IOException, ServletException { 104 105 Context previous = localContext.get(); 106 107 // Prefer the injected pipeline, but fall back on the static one for web.xml users. 108 FilterPipeline filterPipeline = null != injectedPipeline ? injectedPipeline : pipeline; 109 110 try { 111 localContext.set(new Context((HttpServletRequest) servletRequest, 112 (HttpServletResponse) servletResponse)); 113 114 //dispatch across the servlet pipeline, ensuring web.xml's filterchain is honored 115 filterPipeline.dispatch(servletRequest, servletResponse, filterChain); 116 117 } finally { 118 localContext.set(previous); 119 } 120 } 121 122 static HttpServletRequest getRequest() { 123 return getContext().getRequest(); 124 } 125 126 static HttpServletResponse getResponse() { 127 return getContext().getResponse(); 128 } 129 130 static ServletContext getServletContext() { 131 return servletContext.get(); 132 } 133 134 static Context getContext() { 135 Context context = localContext.get(); 136 if (context == null) { 137 throw new OutOfScopeException("Cannot access scoped object. Either we" 138 + " are not currently inside an HTTP Servlet request, or you may" 139 + " have forgotten to apply " + GuiceFilter.class.getName() 140 + " as a servlet filter for this request."); 141 } 142 return context; 143 } 144 145 static class Context { 146 147 final HttpServletRequest request; 148 final HttpServletResponse response; 149 150 Context(HttpServletRequest request, HttpServletResponse response) { 151 this.request = request; 152 this.response = response; 153 } 154 155 HttpServletRequest getRequest() { 156 return request; 157 } 158 159 HttpServletResponse getResponse() { 160 return response; 161 } 162 } 163 164 public void init(FilterConfig filterConfig) throws ServletException { 165 final ServletContext servletContext = filterConfig.getServletContext(); 166 167 // Store servlet context in a weakreference, for injection 168 GuiceFilter.servletContext = new WeakReference<ServletContext>(servletContext); 169 170 // In the default pipeline, this is a noop. However, if replaced 171 // by a managed pipeline, a lazy init will be triggered the first time 172 // dispatch occurs. 173 FilterPipeline filterPipeline = null != injectedPipeline ? injectedPipeline : pipeline; 174 filterPipeline.initPipeline(servletContext); 175 } 176 177 public void destroy() { 178 179 try { 180 // Destroy all registered filters & servlets in that order 181 FilterPipeline filterPipeline = null != injectedPipeline ? injectedPipeline : pipeline; 182 filterPipeline.destroyPipeline(); 183 184 } finally { 185 reset(); 186 servletContext.clear(); 187 } 188 } 189} 190