/* * Copyright 2007 Tim Peierls * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.directwebremoting.guice; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import org.directwebremoting.extend.AjaxFilterManager; import org.directwebremoting.extend.ConverterManager; import org.directwebremoting.extend.CreatorManager; import org.directwebremoting.servlet.DwrServlet; import org.directwebremoting.util.FakeServletConfig; import com.google.inject.Injector; import com.google.inject.Key; import static org.directwebremoting.guice.util.ContextCloseHandlers.*; /** * An extension of the basic * {@link org.directwebremoting.servlet.DwrServlet DwrServlet} * that configures itself for dependency injection with Guice. * Must be used in conjunction with {@link DwrGuiceServletContextListener}. * @author Tim Peierls [tim at peierls dot net] */ public class DwrGuiceServlet extends DwrServlet { /** * Copies DWR configuration values from the Guice bindings into * {@code servletConfig} to make these values accessible to the * standard DWR servlet configuration machinery. */ @Override public void init(final ServletConfig servletConfig) throws ServletException { // Save this for later use by destroy. this.servletContext = servletConfig.getServletContext(); // Set the current context thread-locally so our internal classes can // look up the Injector and use it in turn to look up further objects. try { DwrGuiceUtil.withServletContext(this.servletContext, new Callable<Void>() { public Void call() throws ServletException { // Since ServletConfig is immutable, we use a modifiable // decoration of the real servlet configuration and pass // that to the init method of the superclass. FakeServletConfig config = new FakeServletConfig(servletConfig); // Apply settings configured at bind-time. setInitParameters(config); // Use our internal manager classes to replace and delegate to // any user-specified or default implementations, after adding // additional creators and converters registered at bind-time. configureDelegatedTypes(config); // Normal DwrServlet initialization happens here using the // modified ServletConfig instead of the one we were passed. DwrGuiceServlet.super.init(config); // Objects with (non-global) application scope are initialized // eagerly. initApplicationScoped(); return null; } }); } catch (ServletException e) { throw e; } catch (Exception e) { // Can't happen: throw new AssertionError("unexpected exception: " + e); } } /** * Closes any {@code Closeable} application-scoped objects. * IO exceptions are collected but ignored. */ @Override public void destroy() { ServletContext localContext = this.servletContext; this.servletContext = null; DwrGuiceUtil.withServletContext(localContext, new Runnable() { public void run() { // Closeable objects with (non-global) application scope are closed. List<Exception> exceptions = destroyApplicationScoped(); DwrGuiceServlet.super.destroy(); for (Exception ex : exceptions) { log.warn("During servlet shutdown", ex); } } }); } /** * Inject some values that might have been configured at bind-time. * Override web.xml <init-param> settings in each case that injection * is successful. */ private void setInitParameters(FakeServletConfig config) { InjectedConfig cfg = new InjectedConfig(config); DwrGuiceUtil.getInjector().injectMembers(cfg); cfg.setParameters(); } private void configureDelegatedTypes(FakeServletConfig config) { // Get the user-specified type names, if any, for CreatorManager // and ConverterManager and stash them (thread-locally) so that // InternalCreatorManager and InternalConverterManager can retrieve // them in their parameterless constructors. InternalCreatorManager.setTypeName(config.getInitParameter(INIT_CREATOR_MANAGER)); InternalConverterManager.setTypeName(config.getInitParameter(INIT_CONVERTER_MANAGER)); InternalAjaxFilterManager.setTypeName(config.getInitParameter(INIT_AJAX_FILTER_MANAGER)); // Tell DWR to use our special delegating classes that know how to // create delegates of the appropriate type by looking at the type // names that we just stashed. config.setInitParameter(INIT_CREATOR_MANAGER, InternalCreatorManager.class.getName()); config.setInitParameter(INIT_CONVERTER_MANAGER, InternalConverterManager.class.getName()); config.setInitParameter(INIT_AJAX_FILTER_MANAGER, InternalAjaxFilterManager.class.getName()); } private static void initApplicationScoped() { Injector injector = DwrGuiceUtil.getInjector(); for (Key<?> key : DwrScopes.APPLICATION.getKeysInScope()) { // Eagerly create application-scoped object. injector.getInstance(key); } } private static List<Exception> destroyApplicationScoped() { final List<Exception> exceptions = new ArrayList<Exception>(); DwrScopes.APPLICATION.closeAll(newExceptionLoggingCloseableHandler(exceptions)); return exceptions; } /** * Used to stash context for later use by destroy(). */ private volatile ServletContext servletContext; /** * The name DWR uses to look up a CreatorManager implementation class name */ private static final String INIT_CREATOR_MANAGER = CreatorManager.class.getName(); /** * The name DWR uses to look up a ConverterManager implementation class name */ private static final String INIT_CONVERTER_MANAGER = ConverterManager.class.getName(); /** * The name DWR uses to look up an AjaxFilterManager implementation class name */ private static final String INIT_AJAX_FILTER_MANAGER = AjaxFilterManager.class.getName(); /** * The log stream */ private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog (DwrGuiceServlet.class); }