001 /*******************************************************************************
002 * Copyright (C) PicoContainer Organization. All rights reserved.
003 * --------------------------------------------------------------------------
004 * The software in this package is published under the terms of the BSD style
005 * license a copy of which has been included with this distribution in the
006 * LICENSE.txt file.
007 ******************************************************************************/
008 package org.picocontainer.web;
009
010 import java.io.Serializable;
011
012 import javax.servlet.ServletContext;
013 import javax.servlet.ServletContextEvent;
014 import javax.servlet.ServletContextListener;
015 import javax.servlet.http.HttpSession;
016 import javax.servlet.http.HttpSessionEvent;
017 import javax.servlet.http.HttpSessionListener;
018
019 import org.picocontainer.DefaultPicoContainer;
020 import org.picocontainer.PicoCompositionException;
021 import org.picocontainer.PicoContainer;
022 import org.picocontainer.containers.EmptyPicoContainer;
023 import org.picocontainer.behaviors.Caching;
024 import org.picocontainer.behaviors.Storing;
025
026 /**
027 * Servlet listener class that hooks into the underlying servlet container and
028 * instantiates, assembles, starts, stores and disposes the appropriate pico
029 * containers when applications/sessions start/stop.
030 * <p>
031 * To use, simply add as a listener to the web.xml the listener-class
032 *
033 * <pre>
034 * <listener>
035 * <listener-class>org.picocontainer.web.PicoServletContainerListener</listener-class>
036 * </listener>
037 * </pre>
038 *
039 * </p>
040 * <p>
041 * The listener also requires a the class name of the
042 * {@link org.picocontainer.web.WebappComposer} as a context-param in web.xml:
043 *
044 * <pre>
045 * <context-param>
046 * <param-name>webapp-composer-class</param-name>
047 * <param-value>com.company.MyWebappComposer</param-value>
048 * </context-param>
049 * </pre>
050 *
051 * The composer will be used to compose the components for the different webapp
052 * scopes after the context has been initialised.
053 * </p>
054 *
055 * @author Joe Walnes
056 * @author Aslak Hellesøy
057 * @author Philipp Meier
058 * @author Paul Hammant
059 * @author Mauro Talevi
060 * @author Konstantin Pribluda
061 */
062 @SuppressWarnings("serial")
063 public class PicoServletContainerListener implements ServletContextListener, HttpSessionListener, Serializable {
064
065 public static final String WEBAPP_COMPOSER_CLASS = "webapp-composer-class";
066 private DefaultPicoContainer applicationContainer;
067 private DefaultPicoContainer sessionContainer;
068 private DefaultPicoContainer requestContainer;
069 private Storing sessionStoring;
070 private Storing requestStoring;
071 private boolean useCompositionClass;
072
073 /**
074 * Default constructor used in webapp containers
075 */
076 public PicoServletContainerListener() {
077 applicationContainer = new DefaultPicoContainer(new Caching(), makeParentContainer());
078 sessionStoring = new Storing();
079 sessionContainer = new DefaultPicoContainer(sessionStoring, applicationContainer);
080 requestStoring = new Storing();
081 requestContainer = new DefaultPicoContainer(requestStoring, sessionContainer);
082 useCompositionClass = true;
083 }
084
085 /**
086 * Creates a PicoServletContainerListener with dependencies injected
087 *
088 * @param applicationContainer the application-scoped container
089 * @param sessionContainer the session-scoped container
090 * @param requestContainer the request-scoped container
091 * @param sessionStoring the session storing behaviour
092 * @param requestStoring the request storing behaviour
093 */
094 public PicoServletContainerListener(DefaultPicoContainer applicationContainer,
095 DefaultPicoContainer sessionContainer, DefaultPicoContainer requestContainer, Storing sessionStoring,
096 Storing requestStoring) {
097 this.applicationContainer = applicationContainer;
098 this.sessionContainer = sessionContainer;
099 this.requestContainer = requestContainer;
100 this.sessionStoring = sessionStoring;
101 this.requestStoring = requestStoring;
102 useCompositionClass = false;
103 }
104
105 protected PicoContainer makeParentContainer() {
106 return new EmptyPicoContainer();
107 }
108
109 public void contextInitialized(final ServletContextEvent event) {
110
111 ServletContext context = event.getServletContext();
112 applicationContainer.setName("application");
113
114 context.setAttribute(ApplicationContainerHolder.class.getName(), new ApplicationContainerHolder(
115 applicationContainer));
116
117 sessionContainer.setName("session");
118 ThreadLocalLifecycleState sessionStateModel = new ThreadLocalLifecycleState();
119 sessionContainer.setLifecycleState(sessionStateModel);
120
121 context.setAttribute(SessionContainerHolder.class.getName(), new SessionContainerHolder(sessionContainer,
122 sessionStoring, sessionStateModel));
123
124 requestContainer.setName("request");
125 ThreadLocalLifecycleState requestStateModel = new ThreadLocalLifecycleState();
126 requestContainer.setLifecycleState(requestStateModel);
127
128 context.setAttribute(RequestContainerHolder.class.getName(), new RequestContainerHolder(requestContainer,
129 requestStoring, requestStateModel));
130
131 if (useCompositionClass) {
132 compose(loadComposer(context));
133 }
134 applicationContainer.start();
135 }
136
137 protected WebappComposer loadComposer(ServletContext context) {
138 String composerClassName = context.getInitParameter(WEBAPP_COMPOSER_CLASS);
139 try {
140 return (WebappComposer) Thread.currentThread().getContextClassLoader().loadClass(composerClassName)
141 .newInstance();
142 } catch (Exception e) {
143 throw new PicoCompositionException("Failed to load webapp composer class " + composerClassName
144 + ": ensure the context-param '" + WEBAPP_COMPOSER_CLASS + "' is configured in the web.xml.", e);
145 }
146 }
147
148 protected void compose(WebappComposer composer) {
149 composer.composeApplication(applicationContainer);
150 composer.composeSession(sessionContainer);
151 composer.composeRequest(requestContainer);
152 }
153
154 public void contextDestroyed(ServletContextEvent event) {
155 applicationContainer.stop();
156 applicationContainer.dispose();
157 }
158
159 public void sessionCreated(HttpSessionEvent event) {
160 HttpSession session = event.getSession();
161 ServletContext context = session.getServletContext();
162
163 SessionContainerHolder sch = (SessionContainerHolder) context.getAttribute(SessionContainerHolder.class
164 .getName());
165 ThreadLocalLifecycleState tlLifecycleState = sch.getLifecycleStateModel();
166 session.setAttribute(SessionStoreHolder.class.getName(), new SessionStoreHolder(sessionStoring
167 .resetCacheForThread(), tlLifecycleState.resetStateModelForThread()));
168
169 sessionContainer.start();
170 }
171
172 public void sessionDestroyed(HttpSessionEvent event) {
173 HttpSession session = event.getSession();
174 ServletContext context = session.getServletContext();
175
176 SessionStoreHolder ssh = (SessionStoreHolder) session.getAttribute(SessionStoreHolder.class.getName());
177
178 SessionContainerHolder sch = (SessionContainerHolder) context.getAttribute(SessionContainerHolder.class
179 .getName());
180 ThreadLocalLifecycleState tlLifecycleState = sch.getLifecycleStateModel();
181
182 sessionStoring.putCacheForThread(ssh.getStoreWrapper());
183 tlLifecycleState.putLifecycleStateModelForThread(ssh.getDefaultLifecycleState());
184
185 sessionContainer.stop();
186 sessionContainer.dispose();
187 sessionStoring.invalidateCacheForThread();
188 }
189
190 }