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