View Javadoc

1   /*
2    * Copyright 2007 Tim Peierls
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 org.directwebremoting.guice;
17  
18  import com.google.inject.Binding;
19  import com.google.inject.Inject;
20  import com.google.inject.Injector;
21  import com.google.inject.Key;
22  import com.google.inject.name.Named;
23  
24  import java.util.ArrayList;
25  import java.util.List;
26  import static java.util.Map.Entry;
27  
28  import javax.servlet.ServletConfig;
29  import javax.servlet.ServletContext;
30  import javax.servlet.ServletException;
31  
32  import org.directwebremoting.extend.AjaxFilterManager;
33  import org.directwebremoting.extend.Configurator;
34  import org.directwebremoting.extend.ConverterManager;
35  import org.directwebremoting.extend.CreatorManager;
36  import org.directwebremoting.servlet.DwrServlet;
37  import org.directwebremoting.util.Logger;
38  
39  import static org.directwebremoting.guice.DwrGuiceUtil.getInjector;
40  import static org.directwebremoting.guice.DwrGuiceUtil.popServletContext;
41  import static org.directwebremoting.guice.DwrGuiceUtil.pushServletContext;
42  import static org.directwebremoting.guice.ParamName.*;
43  import static org.directwebremoting.impl.ContainerUtil.INIT_CUSTOM_CONFIGURATOR;
44  
45  /**
46   * An extension of the basic 
47   * {@link org.directwebremoting.servlet.DwrServlet DwrServlet} 
48   * that configures itself for dependency injection with Guice. 
49   * Must be used in conjunction with
50   * {@link org.directwebremoting.guice.DwrGuiceServletContextFilter DwrGuiceServletContextFilter}.
51   * @author Tim Peierls [tim at peierls dot net]
52   */
53  public class DwrGuiceServlet extends DwrServlet 
54  {
55      /**
56       * Copies DWR configuration values from the Guice bindings into 
57       * {@code servletConfig} to make these values accessible to the 
58       * standard DWR servlet configuration machinery.
59       */
60      @Override public void init(ServletConfig servletConfig) throws ServletException
61      {
62          // Save this for later use by destroy.
63          this.servletContext = servletConfig.getServletContext();
64          
65          // Set the current context thread-locally so our internal classes can
66          // look up the Injector and use it in turn to look up further objects.
67          pushServletContext(this.servletContext);
68          try
69          {      
70              // Since ServletConfig is immutable, we use a modifiable 
71              // decoration of the real servlet configuration and pass 
72              // that to the init method of the superclass.
73              ModifiableServletConfig config = new ModifiableServletConfig(servletConfig);
74          
75              // Apply settings configured at bind-time.
76              setInitParameters(config);
77  
78              // Use our internal manager classes to replace and delegate to
79              // any user-specified or default implementations, after adding
80              // additional creators and converters registered at bind-time.
81              configureDelegatedTypes(config);
82              
83              // Normal DwrServlet initialization happens here using the
84              // modified ServletConfig instead of the one we were passed.
85              super.init(config);
86  
87              // Objects with (non-global) application scope are initialized
88              // eagerly.
89              initApplicationScoped();            
90          }
91          finally
92          {
93              // Clean up the ThreadLocal we just used.
94              popServletContext();
95          }
96      }
97      
98      /**
99       * Closes any {@code Closeable} application-scoped objects.
100      * IO exceptions are collected but ignored.
101      */
102     @Override public void destroy()
103     {
104         pushServletContext(this.servletContext);
105         try
106         {            
107             // Closeable objects with (non-global) application scope are closed.
108             List<Exception> exceptions = destroyApplicationScoped();
109             
110             super.destroy();
111             
112             for (Exception ex : exceptions)
113             {
114                 log.warn("During servlet shutdown", ex);
115             }
116         }
117         finally
118         {
119             popServletContext();
120             this.servletContext = null;
121         }
122     }
123     
124     /**
125      * Inject some values that might have been configured at bind-time.
126      * Override web.xml <init-param> settings in each case that injection
127      * is successful.
128      */
129     private void setInitParameters(ModifiableServletConfig config)
130     {
131         Injector injector = getInjector();
132         InjectedConfig cfg = new InjectedConfig(config);
133         injector.injectMembers(cfg);
134         cfg.setParameters();
135     }
136     
137     private static class InjectedConfig
138     {
139         InjectedConfig(ModifiableServletConfig config)
140         {
141             this.config = config;
142         }
143         
144         void setParameter(ParamName paramName, Object value)
145         {
146             if (value != null)
147             {
148                 config.setInitParameter(paramName.getName(), value.toString());
149             }
150         }
151         
152         void setParameters()
153         {
154             setParameter(ALLOW_GET_FOR_SAFARI,            allowGetForSafariButMakeForgeryEasier);
155             setParameter(CROSS_DOMAIN_SESSION_SECURITY,   crossDomainSessionSecurity);
156             setParameter(ALLOW_SCRIPT_TAG_REMOTING,       allowScriptTagRemoting);
157             setParameter(DEBUG,                           debug);
158             setParameter(SCRIPT_SESSION_TIMEOUT,          scriptSessionTimeout);
159             setParameter(MAX_CALL_COUNT,                  maxCallCount);
160             setParameter(ACTIVE_REVERSE_AJAX_ENABLED,     activeReverseAjaxEnabled);
161             setParameter(MAX_WAIT_AFTER_WRITE,            maxWaitAfterWrite);
162             setParameter(DISCONNECTED_TIME,               disconnectedTime);
163             setParameter(POLL_AND_COMET_ENABLED,          pollAndCometEnabled);
164             setParameter(MAX_WAITING_THREADS,             maxWaitingThreads);
165             setParameter(MAX_POLL_HITS_PER_SECOND,        maxPollHitsPerSecond);
166             setParameter(PRE_STREAM_WAIT_TIME,            preStreamWaitTime);
167             setParameter(POST_STREAM_WAIT_TIME,           postStreamWaitTime);
168             setParameter(IGNORE_LAST_MODIFIED,            ignoreLastModified);
169             setParameter(SCRIPT_COMPRESSED,               scriptCompressed);
170             setParameter(SESSION_COOKIE_NAME,             sessionCookieName);
171             setParameter(WELCOME_FILES,                   welcomeFiles);
172             setParameter(NORMALIZE_INCLUDES_QUERY_STRING, normalizeIncludesQueryString);
173             setParameter(OVERRIDE_PATH,                   overridePath);
174 
175             if (configurator != null) 
176             {
177                 // InternalConfigurator knows how to look up the configurator
178                 // instance again and delegate to it.
179                 config.setInitParameter(INIT_CUSTOM_CONFIGURATOR, InternalConfigurator.class.getName());
180             }
181 
182             if (classes != null) 
183             {
184                 config.setInitParameter(CLASSES.getName(), classListToString(classes));
185             }
186         }
187         
188         @Inject(optional=true) @InitParam(ALLOW_GET_FOR_SAFARI)            Boolean allowGetForSafariButMakeForgeryEasier = null;
189         @Inject(optional=true) @InitParam(CROSS_DOMAIN_SESSION_SECURITY)   Boolean crossDomainSessionSecurity = null;
190         @Inject(optional=true) @InitParam(ALLOW_SCRIPT_TAG_REMOTING)       Boolean allowScriptTagRemoting = null;
191         @Inject(optional=true) @InitParam(DEBUG)                           Boolean debug = null;
192         @Inject(optional=true) @InitParam(SCRIPT_SESSION_TIMEOUT)          Long    scriptSessionTimeout = null;
193         @Inject(optional=true) @InitParam(MAX_CALL_COUNT)                  Integer maxCallCount = null;
194         @Inject(optional=true) @InitParam(ACTIVE_REVERSE_AJAX_ENABLED)     Boolean activeReverseAjaxEnabled = null;
195         @Inject(optional=true) @InitParam(MAX_WAIT_AFTER_WRITE)            Long    maxWaitAfterWrite = null;
196         @Inject(optional=true) @InitParam(DISCONNECTED_TIME)               Long    disconnectedTime = null;
197         @Inject(optional=true) @InitParam(POLL_AND_COMET_ENABLED)          Boolean pollAndCometEnabled = null;
198         @Inject(optional=true) @InitParam(MAX_WAITING_THREADS)             Integer maxWaitingThreads = null;
199         @Inject(optional=true) @InitParam(MAX_POLL_HITS_PER_SECOND)        Integer maxPollHitsPerSecond = null;
200         @Inject(optional=true) @InitParam(PRE_STREAM_WAIT_TIME)            Long    preStreamWaitTime = null;
201         @Inject(optional=true) @InitParam(POST_STREAM_WAIT_TIME)           Long    postStreamWaitTime = null;
202         @Inject(optional=true) @InitParam(IGNORE_LAST_MODIFIED)            Boolean ignoreLastModified = null;
203         @Inject(optional=true) @InitParam(SCRIPT_COMPRESSED)               Boolean scriptCompressed = null;
204         @Inject(optional=true) @InitParam(SESSION_COOKIE_NAME)             String  sessionCookieName = null;
205         @Inject(optional=true) @InitParam(WELCOME_FILES)                   String  welcomeFiles = null;
206         @Inject(optional=true) @InitParam(NORMALIZE_INCLUDES_QUERY_STRING) Boolean normalizeIncludesQueryString = null;
207         @Inject(optional=true) @InitParam(OVERRIDE_PATH)                   String  overridePath = null;
208         
209         @Inject(optional=true) Configurator configurator = null;
210         
211         @Inject(optional=true) @InitParam(CLASSES) List<Class> classes = null;
212         
213         private final ModifiableServletConfig config;
214     }
215     
216 
217     private void configureDelegatedTypes(ModifiableServletConfig config)
218     {
219         // Get the user-specified type names, if any, for CreatorManager 
220         // and ConverterManager and stash them (thread-locally) so that 
221         // InternalCreatorManager and InternalConverterManager can retrieve 
222         // them in their parameterless constructors.
223         
224         InternalCreatorManager.setTypeName(config.getInitParameter(INIT_CREATOR_MANAGER));
225         InternalConverterManager.setTypeName(config.getInitParameter(INIT_CONVERTER_MANAGER));
226         InternalAjaxFilterManager.setTypeName(config.getInitParameter(INIT_AJAX_FILTER_MANAGER));
227         
228         // Tell DWR to use our special delegating classes that know how to 
229         // create delegates of the appropriate type by looking at the type 
230         // names that we just stashed.
231         
232         config.setInitParameter(INIT_CREATOR_MANAGER, InternalCreatorManager.class.getName());
233         config.setInitParameter(INIT_CONVERTER_MANAGER, InternalConverterManager.class.getName());
234         config.setInitParameter(INIT_AJAX_FILTER_MANAGER, InternalAjaxFilterManager.class.getName());
235     }
236     
237     
238     private static void initApplicationScoped()
239     {
240         Injector injector = getInjector();
241         for (Key<?> key : DwrScopes.APPLICATION.getKeysInScope())
242         {
243             // Eagerly create application-scoped object.
244             injector.getInstance(key);
245         }
246     }
247     
248     private static List<Exception> destroyApplicationScoped()
249     {
250         final List<Exception> exceptions = new ArrayList<Exception>();
251         DwrScopes.APPLICATION.closeAll(new ExceptionLoggingCloseableHandler(exceptions));
252         return exceptions;
253     }
254 
255 
256     static String classListToString(List<Class> classList)
257     {
258         StringBuilder buf = new StringBuilder();
259         int count = 0;
260         for (Class cls : classList)
261         {
262             if (count++ > 0) buf.append(", ");
263             buf.append(cls.getName());
264         }
265         return buf.toString();
266     }
267 
268     
269     /**
270      * Used to stash context for later use by destroy().
271      */
272     private volatile ServletContext servletContext;
273     
274     
275     /** 
276      * The name DWR uses to look up a CreatorManager implementation class name 
277      */
278     private static final String INIT_CREATOR_MANAGER = CreatorManager.class.getName();   
279     
280     /** 
281      * The name DWR uses to look up a ConverterManager implementation class name 
282      */
283     private static final String INIT_CONVERTER_MANAGER = ConverterManager.class.getName();
284     
285     /** 
286      * The name DWR uses to look up an AjaxFilterManager implementation class name 
287      */
288     private static final String INIT_AJAX_FILTER_MANAGER = AjaxFilterManager.class.getName();
289 
290     /**
291      * The log stream
292      */
293     private static final Logger log = Logger.getLogger(DwrGuiceServlet.class);
294 }