001: /*
002: * Copyright 2007 Tim Peierls
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.directwebremoting.guice;
017:
018: import java.util.ArrayList;
019: import java.util.List;
020:
021: import javax.servlet.ServletConfig;
022: import javax.servlet.ServletContext;
023: import javax.servlet.ServletException;
024:
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027: import org.directwebremoting.extend.AjaxFilterManager;
028: import org.directwebremoting.extend.Configurator;
029: import org.directwebremoting.extend.ConverterManager;
030: import org.directwebremoting.extend.CreatorManager;
031: import org.directwebremoting.servlet.DwrServlet;
032:
033: import com.google.inject.Inject;
034: import com.google.inject.Injector;
035: import com.google.inject.Key;
036:
037: import static org.directwebremoting.guice.DwrGuiceUtil.getInjector;
038: import static org.directwebremoting.guice.DwrGuiceUtil.popServletContext;
039: import static org.directwebremoting.guice.DwrGuiceUtil.pushServletContext;
040: import static org.directwebremoting.guice.ParamName.ACTIVE_REVERSE_AJAX_ENABLED;
041: import static org.directwebremoting.guice.ParamName.ALLOW_GET_FOR_SAFARI;
042: import static org.directwebremoting.guice.ParamName.ALLOW_SCRIPT_TAG_REMOTING;
043: import static org.directwebremoting.guice.ParamName.CLASSES;
044: import static org.directwebremoting.guice.ParamName.CROSS_DOMAIN_SESSION_SECURITY;
045: import static org.directwebremoting.guice.ParamName.DEBUG;
046: import static org.directwebremoting.guice.ParamName.DISCONNECTED_TIME;
047: import static org.directwebremoting.guice.ParamName.IGNORE_LAST_MODIFIED;
048: import static org.directwebremoting.guice.ParamName.MAX_CALL_COUNT;
049: import static org.directwebremoting.guice.ParamName.MAX_POLL_HITS_PER_SECOND;
050: import static org.directwebremoting.guice.ParamName.MAX_WAITING_THREADS;
051: import static org.directwebremoting.guice.ParamName.MAX_WAIT_AFTER_WRITE;
052: import static org.directwebremoting.guice.ParamName.NORMALIZE_INCLUDES_QUERY_STRING;
053: import static org.directwebremoting.guice.ParamName.OVERRIDE_PATH;
054: import static org.directwebremoting.guice.ParamName.POLL_AND_COMET_ENABLED;
055: import static org.directwebremoting.guice.ParamName.POST_STREAM_WAIT_TIME;
056: import static org.directwebremoting.guice.ParamName.PRE_STREAM_WAIT_TIME;
057: import static org.directwebremoting.guice.ParamName.SCRIPT_COMPRESSED;
058: import static org.directwebremoting.guice.ParamName.SCRIPT_SESSION_TIMEOUT;
059: import static org.directwebremoting.guice.ParamName.SESSION_COOKIE_NAME;
060: import static org.directwebremoting.guice.ParamName.WELCOME_FILES;
061: import static org.directwebremoting.impl.ContainerUtil.INIT_CUSTOM_CONFIGURATOR;
062:
063: /**
064: * An extension of the basic
065: * {@link org.directwebremoting.servlet.DwrServlet DwrServlet}
066: * that configures itself for dependency injection with Guice.
067: * Must be used in conjunction with {@link DwrGuiceServletContextListener}.
068: * @author Tim Peierls [tim at peierls dot net]
069: */
070: public class DwrGuiceServlet extends DwrServlet {
071: /**
072: * Copies DWR configuration values from the Guice bindings into
073: * {@code servletConfig} to make these values accessible to the
074: * standard DWR servlet configuration machinery.
075: */
076: @Override
077: public void init(ServletConfig servletConfig)
078: throws ServletException {
079: // Save this for later use by destroy.
080: this .servletContext = servletConfig.getServletContext();
081:
082: // Set the current context thread-locally so our internal classes can
083: // look up the Injector and use it in turn to look up further objects.
084: pushServletContext(this .servletContext);
085: try {
086: // Since ServletConfig is immutable, we use a modifiable
087: // decoration of the real servlet configuration and pass
088: // that to the init method of the superclass.
089: ModifiableServletConfig config = new ModifiableServletConfig(
090: servletConfig);
091:
092: // Apply settings configured at bind-time.
093: setInitParameters(config);
094:
095: // Use our internal manager classes to replace and delegate to
096: // any user-specified or default implementations, after adding
097: // additional creators and converters registered at bind-time.
098: configureDelegatedTypes(config);
099:
100: // Normal DwrServlet initialization happens here using the
101: // modified ServletConfig instead of the one we were passed.
102: super .init(config);
103:
104: // Objects with (non-global) application scope are initialized
105: // eagerly.
106: initApplicationScoped();
107: } finally {
108: // Clean up the ThreadLocal we just used.
109: popServletContext();
110: }
111: }
112:
113: /**
114: * Closes any {@code Closeable} application-scoped objects.
115: * IO exceptions are collected but ignored.
116: */
117: @Override
118: public void destroy() {
119: pushServletContext(this .servletContext);
120: try {
121: // Closeable objects with (non-global) application scope are closed.
122: List<Exception> exceptions = destroyApplicationScoped();
123:
124: super .destroy();
125:
126: for (Exception ex : exceptions) {
127: log.warn("During servlet shutdown", ex);
128: }
129: } finally {
130: popServletContext();
131: this .servletContext = null;
132: }
133: }
134:
135: /**
136: * Inject some values that might have been configured at bind-time.
137: * Override web.xml <init-param> settings in each case that injection
138: * is successful.
139: */
140: private void setInitParameters(ModifiableServletConfig config) {
141: Injector injector = getInjector();
142: InjectedConfig cfg = new InjectedConfig(config);
143: injector.injectMembers(cfg);
144: cfg.setParameters();
145: }
146:
147: private static class InjectedConfig {
148: InjectedConfig(ModifiableServletConfig config) {
149: this .config = config;
150: }
151:
152: void setParameter(ParamName paramName, Object value) {
153: if (value != null) {
154: config.setInitParameter(paramName.getName(), value
155: .toString());
156: }
157: }
158:
159: void setParameters() {
160: setParameter(ALLOW_GET_FOR_SAFARI,
161: allowGetForSafariButMakeForgeryEasier);
162: setParameter(CROSS_DOMAIN_SESSION_SECURITY,
163: crossDomainSessionSecurity);
164: setParameter(ALLOW_SCRIPT_TAG_REMOTING,
165: allowScriptTagRemoting);
166: setParameter(DEBUG, debug);
167: setParameter(SCRIPT_SESSION_TIMEOUT, scriptSessionTimeout);
168: setParameter(MAX_CALL_COUNT, maxCallCount);
169: setParameter(ACTIVE_REVERSE_AJAX_ENABLED,
170: activeReverseAjaxEnabled);
171: setParameter(MAX_WAIT_AFTER_WRITE, maxWaitAfterWrite);
172: setParameter(DISCONNECTED_TIME, disconnectedTime);
173: setParameter(POLL_AND_COMET_ENABLED, pollAndCometEnabled);
174: setParameter(MAX_WAITING_THREADS, maxWaitingThreads);
175: setParameter(MAX_POLL_HITS_PER_SECOND, maxPollHitsPerSecond);
176: setParameter(PRE_STREAM_WAIT_TIME, preStreamWaitTime);
177: setParameter(POST_STREAM_WAIT_TIME, postStreamWaitTime);
178: setParameter(IGNORE_LAST_MODIFIED, ignoreLastModified);
179: setParameter(SCRIPT_COMPRESSED, scriptCompressed);
180: setParameter(SESSION_COOKIE_NAME, sessionCookieName);
181: setParameter(WELCOME_FILES, welcomeFiles);
182: setParameter(NORMALIZE_INCLUDES_QUERY_STRING,
183: normalizeIncludesQueryString);
184: setParameter(OVERRIDE_PATH, overridePath);
185:
186: if (configurator != null) {
187: // InternalConfigurator knows how to look up the configurator
188: // instance again and delegate to it.
189: config.setInitParameter(INIT_CUSTOM_CONFIGURATOR,
190: InternalConfigurator.class.getName());
191: }
192:
193: if (classes != null) {
194: config.setInitParameter(CLASSES.getName(),
195: classListToString(classes));
196: }
197: }
198:
199: @Inject(optional=true)
200: @InitParam(ALLOW_GET_FOR_SAFARI)
201: Boolean allowGetForSafariButMakeForgeryEasier = null;
202: @Inject(optional=true)
203: @InitParam(CROSS_DOMAIN_SESSION_SECURITY)
204: Boolean crossDomainSessionSecurity = null;
205: @Inject(optional=true)
206: @InitParam(ALLOW_SCRIPT_TAG_REMOTING)
207: Boolean allowScriptTagRemoting = null;
208: @Inject(optional=true)
209: @InitParam(DEBUG)
210: Boolean debug = null;
211: @Inject(optional=true)
212: @InitParam(SCRIPT_SESSION_TIMEOUT)
213: Long scriptSessionTimeout = null;
214: @Inject(optional=true)
215: @InitParam(MAX_CALL_COUNT)
216: Integer maxCallCount = null;
217: @Inject(optional=true)
218: @InitParam(ACTIVE_REVERSE_AJAX_ENABLED)
219: Boolean activeReverseAjaxEnabled = null;
220: @Inject(optional=true)
221: @InitParam(MAX_WAIT_AFTER_WRITE)
222: Long maxWaitAfterWrite = null;
223: @Inject(optional=true)
224: @InitParam(DISCONNECTED_TIME)
225: Long disconnectedTime = null;
226: @Inject(optional=true)
227: @InitParam(POLL_AND_COMET_ENABLED)
228: Boolean pollAndCometEnabled = null;
229: @Inject(optional=true)
230: @InitParam(MAX_WAITING_THREADS)
231: Integer maxWaitingThreads = null;
232: @Inject(optional=true)
233: @InitParam(MAX_POLL_HITS_PER_SECOND)
234: Integer maxPollHitsPerSecond = null;
235: @Inject(optional=true)
236: @InitParam(PRE_STREAM_WAIT_TIME)
237: Long preStreamWaitTime = null;
238: @Inject(optional=true)
239: @InitParam(POST_STREAM_WAIT_TIME)
240: Long postStreamWaitTime = null;
241: @Inject(optional=true)
242: @InitParam(IGNORE_LAST_MODIFIED)
243: Boolean ignoreLastModified = null;
244: @Inject(optional=true)
245: @InitParam(SCRIPT_COMPRESSED)
246: Boolean scriptCompressed = null;
247: @Inject(optional=true)
248: @InitParam(SESSION_COOKIE_NAME)
249: String sessionCookieName = null;
250: @Inject(optional=true)
251: @InitParam(WELCOME_FILES)
252: String welcomeFiles = null;
253: @Inject(optional=true)
254: @InitParam(NORMALIZE_INCLUDES_QUERY_STRING)
255: Boolean normalizeIncludesQueryString = null;
256: @Inject(optional=true)
257: @InitParam(OVERRIDE_PATH)
258: String overridePath = null;
259:
260: @Inject(optional=true)
261: Configurator configurator = null;
262:
263: @Inject(optional=true)
264: @InitParam(CLASSES)
265: List<Class<?>> classes = null;
266:
267: private final ModifiableServletConfig config;
268: }
269:
270: private void configureDelegatedTypes(ModifiableServletConfig config) {
271: // Get the user-specified type names, if any, for CreatorManager
272: // and ConverterManager and stash them (thread-locally) so that
273: // InternalCreatorManager and InternalConverterManager can retrieve
274: // them in their parameterless constructors.
275:
276: InternalCreatorManager.setTypeName(config
277: .getInitParameter(INIT_CREATOR_MANAGER));
278: InternalConverterManager.setTypeName(config
279: .getInitParameter(INIT_CONVERTER_MANAGER));
280: InternalAjaxFilterManager.setTypeName(config
281: .getInitParameter(INIT_AJAX_FILTER_MANAGER));
282:
283: // Tell DWR to use our special delegating classes that know how to
284: // create delegates of the appropriate type by looking at the type
285: // names that we just stashed.
286:
287: config.setInitParameter(INIT_CREATOR_MANAGER,
288: InternalCreatorManager.class.getName());
289: config.setInitParameter(INIT_CONVERTER_MANAGER,
290: InternalConverterManager.class.getName());
291: config.setInitParameter(INIT_AJAX_FILTER_MANAGER,
292: InternalAjaxFilterManager.class.getName());
293: }
294:
295: private static void initApplicationScoped() {
296: Injector injector = getInjector();
297: for (Key<?> key : DwrScopes.APPLICATION.getKeysInScope()) {
298: // Eagerly create application-scoped object.
299: injector.getInstance(key);
300: }
301: }
302:
303: private static List<Exception> destroyApplicationScoped() {
304: final List<Exception> exceptions = new ArrayList<Exception>();
305: DwrScopes.APPLICATION
306: .closeAll(new ExceptionLoggingCloseableHandler(
307: exceptions));
308: return exceptions;
309: }
310:
311: static String classListToString(List<Class<?>> classList) {
312: StringBuilder buf = new StringBuilder();
313: int count = 0;
314: for (Class<?> cls : classList) {
315: if (count++ > 0) {
316: buf.append(", ");
317: }
318: buf.append(cls.getName());
319: }
320: return buf.toString();
321: }
322:
323: /**
324: * Used to stash context for later use by destroy().
325: */
326: private volatile ServletContext servletContext;
327:
328: /**
329: * The name DWR uses to look up a CreatorManager implementation class name
330: */
331: private static final String INIT_CREATOR_MANAGER = CreatorManager.class
332: .getName();
333:
334: /**
335: * The name DWR uses to look up a ConverterManager implementation class name
336: */
337: private static final String INIT_CONVERTER_MANAGER = ConverterManager.class
338: .getName();
339:
340: /**
341: * The name DWR uses to look up an AjaxFilterManager implementation class name
342: */
343: private static final String INIT_AJAX_FILTER_MANAGER = AjaxFilterManager.class
344: .getName();
345:
346: /**
347: * The log stream
348: */
349: private static final Log log = LogFactory
350: .getLog(DwrGuiceServlet.class);
351: }
|