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 17 package com.google.inject.servlet; 18 19 import static com.google.common.base.Preconditions.checkState; 20 21 import com.google.common.collect.ImmutableList; 22 import com.google.inject.AbstractModule; 23 import com.google.inject.Key; 24 25 import java.util.Map; 26 27 import javax.servlet.Filter; 28 import javax.servlet.ServletContext; 29 import javax.servlet.http.HttpServlet; 30 31 /** 32 * Configures the servlet scopes and creates bindings for the servlet API 33 * objects so you can inject the request, response, session, etc. 34 * 35 * <p> 36 * You should subclass this module to register servlets and 37 * filters in the {@link #configureServlets()} method. 38 * 39 * @author crazybob@google.com (Bob Lee) 40 * @author dhanji@gmail.com (Dhanji R. Prasanna) 41 */ 42 public class ServletModule extends AbstractModule { 43 44 @Override configure()45 protected final void configure() { 46 checkState(filtersModuleBuilder == null, "Re-entry is not allowed."); 47 checkState(servletsModuleBuilder == null, "Re-entry is not allowed."); 48 filtersModuleBuilder = new FiltersModuleBuilder(binder()); 49 servletsModuleBuilder = new ServletsModuleBuilder(binder()); 50 try { 51 // Install common bindings (skipped if already installed). 52 install(new InternalServletModule()); 53 54 // Install local filter and servlet bindings. 55 configureServlets(); 56 } finally { 57 filtersModuleBuilder = null; 58 servletsModuleBuilder = null; 59 } 60 } 61 62 /** 63 * <h3>Servlet Mapping EDSL</h3> 64 * 65 * <p> Part of the EDSL builder language for configuring servlets 66 * and filters with guice-servlet. Think of this as an in-code replacement for web.xml. 67 * Filters and servlets are configured here using simple java method calls. Here is a typical 68 * example of registering a filter when creating your Guice injector: 69 * 70 * <pre> 71 * Guice.createInjector(..., new ServletModule() { 72 * 73 * {@literal @}Override 74 * protected void configureServlets() { 75 * <b>serve("*.html").with(MyServlet.class)</b> 76 * } 77 * } 78 * </pre> 79 * 80 * This registers a servlet (subclass of {@code HttpServlet}) called {@code MyServlet} to service 81 * any web pages ending in {@code .html}. You can also use a path-style syntax to register 82 * servlets: 83 * 84 * <pre> 85 * <b>serve("/my/*").with(MyServlet.class)</b> 86 * </pre> 87 * 88 * Every servlet (or filter) is required to be a singleton. If you cannot annotate the class 89 * directly, you should add a separate {@code bind(..).in(Singleton.class)} rule elsewhere in 90 * your module. Mapping a servlet that is bound under any other scope is an error. 91 * 92 * <p> 93 * <h4>Dispatch Order</h4> 94 * You are free to register as many servlets and filters as you like this way. They will 95 * be compared and dispatched in the order in which the filter methods are called: 96 * 97 * <pre> 98 * 99 * Guice.createInjector(..., new ServletModule() { 100 * 101 * {@literal @}Override 102 * protected void configureServlets() { 103 * filter("/*").through(MyFilter.class); 104 * filter("*.css").through(MyCssFilter.class); 105 * filter("*.jpg").through(new MyJpgFilter()); 106 * // etc.. 107 * 108 * serve("*.html").with(MyServlet.class); 109 * serve("/my/*").with(MyServlet.class); 110 * serve("*.jpg").with(new MyServlet()); 111 * // etc.. 112 * } 113 * } 114 * </pre> 115 * This will traverse down the list of rules in lexical order. For example, a url 116 * "{@code /my/file.js}" (after it runs through the matching filters) will first 117 * be compared against the servlet mapping: 118 * 119 * <pre> 120 * serve("*.html").with(MyServlet.class); 121 * </pre> 122 * And failing that, it will descend to the next servlet mapping: 123 * 124 * <pre> 125 * serve("/my/*").with(MyServlet.class); 126 * </pre> 127 * 128 * Since this rule matches, Guice Servlet will dispatch to {@code MyServlet}. These 129 * two mapping rules can also be written in more compact form using varargs syntax: 130 * 131 * <pre> 132 * serve(<b>"*.html", "/my/*"</b>).with(MyServlet.class); 133 * </pre> 134 * 135 * This way you can map several URI patterns to the same servlet. A similar syntax is 136 * also available for filter mappings. 137 * 138 * <p> 139 * <h4>Regular Expressions</h4> 140 * You can also map servlets (or filters) to URIs using regular expressions: 141 * <pre> 142 * <b>serveRegex("(.)*ajax(.)*").with(MyAjaxServlet.class)</b> 143 * </pre> 144 * 145 * This will map any URI containing the text "ajax" in it to {@code MyAjaxServlet}. Such as: 146 * <ul> 147 * <li>http://www.google.com/ajax.html</li> 148 * <li>http://www.google.com/content/ajax/index</li> 149 * <li>http://www.google.com/it/is_totally_ajaxian</li> 150 * </ul> 151 * 152 * 153 * <h3>Initialization Parameters</h3> 154 * 155 * Servlets (and filters) allow you to pass in init params 156 * using the {@code <init-param>} tag in web.xml. You can similarly pass in parameters to 157 * Servlets and filters registered in Guice-servlet using a {@link java.util.Map} of parameter 158 * name/value pairs. For example, to initialize {@code MyServlet} with two parameters 159 * ({@code name="Dhanji", site="google.com"}) you could write: 160 * 161 * <pre> 162 * Map<String, String> params = new HashMap<String, String>(); 163 * params.put("name", "Dhanji"); 164 * params.put("site", "google.com"); 165 * 166 * ... 167 * serve("/*").with(MyServlet.class, <b>params</b>) 168 * </pre> 169 * 170 * <p> 171 * <h3>Binding Keys</h3> 172 * 173 * You can also bind keys rather than classes. This lets you hide 174 * implementations with package-local visbility and expose them using 175 * only a Guice module and an annotation: 176 * 177 * <pre> 178 * ... 179 * filter("/*").through(<b>Key.get(Filter.class, Fave.class)</b>); 180 * </pre> 181 * 182 * Where {@code Filter.class} refers to the Servlet API interface and {@code Fave.class} is a 183 * custom binding annotation. Elsewhere (in one of your own modules) you can bind this 184 * filter's implementation: 185 * 186 * <pre> 187 * bind(Filter.class)<b>.annotatedWith(Fave.class)</b>.to(MyFilterImpl.class); 188 * </pre> 189 * 190 * See {@link com.google.inject.Binder} for more information on binding syntax. 191 * 192 * <p> 193 * <h3>Multiple Modules</h3> 194 * 195 * It is sometimes useful to capture servlet and filter mappings from multiple different 196 * modules. This is essential if you want to package and offer drop-in Guice plugins that 197 * provide servlet functionality. 198 * 199 * <p> 200 * Guice Servlet allows you to register several instances of {@code ServletModule} to your 201 * injector. The order in which these modules are installed determines the dispatch order 202 * of filters and the precedence order of servlets. For example, if you had two servlet modules, 203 * {@code RpcModule} and {@code WebServiceModule} and they each contained a filter that mapped 204 * to the same URI pattern, {@code "/*"}: 205 * 206 * <p> 207 * In {@code RpcModule}: 208 * <pre> 209 * filter("/*").through(RpcFilter.class); 210 * </pre> 211 * 212 * In {@code WebServiceModule}: 213 * <pre> 214 * filter("/*").through(WebServiceFilter.class); 215 * </pre> 216 * 217 * Then the order in which these filters are dispatched is determined by the order in which 218 * the modules are installed: 219 * 220 * <pre> 221 * <b>install(new WebServiceModule());</b> 222 * install(new RpcModule()); 223 * </pre> 224 * 225 * In the case shown above {@code WebServiceFilter} will run first. 226 * 227 * @since 2.0 228 */ configureServlets()229 protected void configureServlets() { 230 } 231 232 233 private FiltersModuleBuilder filtersModuleBuilder; 234 private ServletsModuleBuilder servletsModuleBuilder; 235 getFiltersModuleBuilder()236 private FiltersModuleBuilder getFiltersModuleBuilder() { 237 checkState(filtersModuleBuilder != null, 238 "This method can only be used inside configureServlets()"); 239 return filtersModuleBuilder; 240 } 241 getServletModuleBuilder()242 private ServletsModuleBuilder getServletModuleBuilder() { 243 checkState(servletsModuleBuilder != null, 244 "This method can only be used inside configureServlets()"); 245 return servletsModuleBuilder; 246 } 247 248 /** 249 * @param urlPattern Any Servlet-style pattern. examples: /*, /html/*, *.html, etc. 250 * @since 2.0 251 */ filter(String urlPattern, String... morePatterns)252 protected final FilterKeyBindingBuilder filter(String urlPattern, String... morePatterns) { 253 return getFiltersModuleBuilder() 254 .filter(ImmutableList.<String>builder().add(urlPattern).add(morePatterns).build()); 255 } 256 257 /** 258 * @param regex Any Java-style regular expression. 259 * @since 2.0 260 */ filterRegex(String regex, String... regexes)261 protected final FilterKeyBindingBuilder filterRegex(String regex, String... regexes) { 262 return getFiltersModuleBuilder() 263 .filterRegex(ImmutableList.<String>builder().add(regex).add(regexes).build()); 264 } 265 266 /** 267 * @param urlPattern Any Servlet-style pattern. examples: /*, /html/*, *.html, etc. 268 * @since 2.0 269 */ serve(String urlPattern, String... morePatterns)270 protected final ServletKeyBindingBuilder serve(String urlPattern, String... morePatterns) { 271 return getServletModuleBuilder() 272 .serve(ImmutableList.<String>builder().add(urlPattern).add(morePatterns).build()); 273 } 274 275 /** 276 * @param regex Any Java-style regular expression. 277 * @since 2.0 278 */ serveRegex(String regex, String... regexes)279 protected final ServletKeyBindingBuilder serveRegex(String regex, String... regexes) { 280 return getServletModuleBuilder() 281 .serveRegex(ImmutableList.<String>builder().add(regex).add(regexes).build()); 282 } 283 284 /** 285 * This method only works if you are using the {@linkplain GuiceServletContextListener} to 286 * create your injector. Otherwise, it returns null. 287 * @return The current servlet context. 288 * @since 3.0 289 */ getServletContext()290 protected final ServletContext getServletContext() { 291 return GuiceFilter.getServletContext(); 292 } 293 294 /** 295 * See the EDSL examples at {@link ServletModule#configureServlets()} 296 * 297 * @since 2.0 298 */ 299 public static interface FilterKeyBindingBuilder { through(Class<? extends Filter> filterKey)300 void through(Class<? extends Filter> filterKey); through(Key<? extends Filter> filterKey)301 void through(Key<? extends Filter> filterKey); 302 /** @since 3.0 */ through(Filter filter)303 void through(Filter filter); through(Class<? extends Filter> filterKey, Map<String, String> initParams)304 void through(Class<? extends Filter> filterKey, Map<String, String> initParams); through(Key<? extends Filter> filterKey, Map<String, String> initParams)305 void through(Key<? extends Filter> filterKey, Map<String, String> initParams); 306 /** @since 3.0 */ through(Filter filter, Map<String, String> initParams)307 void through(Filter filter, Map<String, String> initParams); 308 } 309 310 /** 311 * See the EDSL examples at {@link ServletModule#configureServlets()} 312 * 313 * @since 2.0 314 */ 315 public static interface ServletKeyBindingBuilder { with(Class<? extends HttpServlet> servletKey)316 void with(Class<? extends HttpServlet> servletKey); with(Key<? extends HttpServlet> servletKey)317 void with(Key<? extends HttpServlet> servletKey); 318 /** @since 3.0 */ with(HttpServlet servlet)319 void with(HttpServlet servlet); with(Class<? extends HttpServlet> servletKey, Map<String, String> initParams)320 void with(Class<? extends HttpServlet> servletKey, Map<String, String> initParams); with(Key<? extends HttpServlet> servletKey, Map<String, String> initParams)321 void with(Key<? extends HttpServlet> servletKey, Map<String, String> initParams); 322 /** @since 3.0 */ with(HttpServlet servlet, Map<String, String> initParams)323 void with(HttpServlet servlet, Map<String, String> initParams); 324 } 325 } 326