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