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.IOException;
011 import java.io.Serializable;
012 import java.io.ByteArrayOutputStream;
013 import java.io.ObjectOutputStream;
014 import java.lang.reflect.Type;
015
016 import javax.servlet.Filter;
017 import javax.servlet.FilterChain;
018 import javax.servlet.FilterConfig;
019 import javax.servlet.ServletContext;
020 import javax.servlet.ServletException;
021 import javax.servlet.ServletRequest;
022 import javax.servlet.ServletResponse;
023 import javax.servlet.http.HttpServletRequest;
024 import javax.servlet.http.HttpSession;
025 import javax.servlet.http.HttpServletResponse;
026
027 import org.picocontainer.DefaultPicoContainer;
028 import org.picocontainer.MutablePicoContainer;
029 import org.picocontainer.Characteristics;
030 import org.picocontainer.PicoContainer;
031 import org.picocontainer.PicoCompositionException;
032 import org.picocontainer.lifecycle.DefaultLifecycleState;
033 import org.picocontainer.adapters.AbstractAdapter;
034 import com.thoughtworks.xstream.XStream;
035 import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;
036
037 @SuppressWarnings("serial")
038 public abstract class PicoServletContainerFilter implements Filter, Serializable {
039
040 private boolean exposeServletInfrastructure;
041 private boolean isStateless;
042 private boolean printSessionSize;
043 private boolean debug = false;
044
045 public void init(FilterConfig filterConfig) throws ServletException {
046 ServletContext context = filterConfig.getServletContext();
047 ScopedContainers scopedContainers = getScopedContainers(context);
048 setAppContainer(scopedContainers.getApplicationContainer());
049
050 isStateless = Boolean.parseBoolean(context.getInitParameter(PicoServletContainerListener.STATELESS_WEBAPP));
051 printSessionSize = Boolean.parseBoolean(context.getInitParameter(PicoServletContainerListener.PRINT_SESSION_SIZE));
052
053 String exposeServletInfrastructureString = filterConfig.getInitParameter("exposeServletInfrastructure");
054 if (exposeServletInfrastructureString == null || Boolean.parseBoolean(exposeServletInfrastructureString)) {
055 exposeServletInfrastructure = true;
056 }
057
058 scopedContainers.getRequestContainer().as(Characteristics.NO_CACHE).addAdapter(new HttpSessionInjector());
059 scopedContainers.getRequestContainer().as(Characteristics.NO_CACHE).addAdapter(new HttpServletRequestInjector());
060 scopedContainers.getRequestContainer().as(Characteristics.NO_CACHE).addAdapter(new HttpServletResponseInjector());
061
062 initAdditionalScopedComponents(scopedContainers.getSessionContainer(), scopedContainers.getRequestContainer());
063
064 }
065
066 public void destroy() {
067 }
068
069 private ScopedContainers getScopedContainers(ServletContext context) {
070 return (ScopedContainers) context.getAttribute(ScopedContainers.class.getName());
071 }
072
073 protected void initAdditionalScopedComponents(MutablePicoContainer sessionContainer, MutablePicoContainer reqContainer) {
074 }
075
076 public static Object getRequestComponentForThread(Class<?> type) {
077 MutablePicoContainer requestContainer = ServletFilter.currentRequestContainer.get();
078 MutablePicoContainer container = new DefaultPicoContainer(requestContainer);
079 container.addComponent(type);
080 return container.getComponent(type);
081 }
082
083 public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {
084
085 HttpServletRequest servletRequest = (HttpServletRequest) req;
086 HttpSession sess = servletRequest.getSession();
087 if (exposeServletInfrastructure) {
088 currentSession.set(sess);
089 currentRequest.set(req);
090 currentResponse.set(resp);
091 }
092
093 ScopedContainers scopedContainers = getScopedContainers(sess.getServletContext());
094
095 SessionStoreHolder ssh = null;
096 if (!isStateless) {
097
098 ssh = (SessionStoreHolder) sess.getAttribute(SessionStoreHolder.class.getName());
099 if (ssh == null) {
100 if (scopedContainers.getSessionContainer().getComponentAdapters().size() > 0) {
101 throw new PicoContainerWebException("Session not setup correctly. There are components registered " +
102 "at the session level, but no working container to host them");
103 }
104 ssh = new SessionStoreHolder(scopedContainers.getSessionStoring().getCacheForThread(), new DefaultLifecycleState());
105 }
106
107 scopedContainers.getSessionStoring().putCacheForThread(ssh.getStoreWrapper());
108 scopedContainers.getSessionState().putLifecycleStateModelForThread(ssh.getLifecycleState());
109
110 }
111 scopedContainers.getRequestStoring().resetCacheForThread();
112 scopedContainers.getRequestState().resetStateModelForThread();
113
114 scopedContainers.getRequestContainer().start();
115
116 setAppContainer(scopedContainers.getApplicationContainer());
117 if (!isStateless) {
118 setSessionContainer(scopedContainers.getSessionContainer());
119 }
120 setRequestContainer(scopedContainers.getRequestContainer());
121
122 containersSetupForRequest(scopedContainers.getApplicationContainer(), scopedContainers.getSessionContainer(), scopedContainers.getRequestContainer(), req, resp);
123
124 filterChain.doFilter(req, resp);
125
126 setAppContainer(null);
127 if (!isStateless) {
128 setSessionContainer(null);
129 }
130 setRequestContainer(null);
131
132 scopedContainers.getRequestContainer().stop();
133 scopedContainers.getRequestContainer().dispose();
134
135 if (!isStateless) {
136 if (printSessionSize) {
137 printSessionSizeDetailsForDebugging(ssh);
138 }
139 sess.setAttribute(SessionStoreHolder.class.getName(), ssh);
140 }
141 scopedContainers.getRequestStoring().invalidateCacheForThread();
142 scopedContainers.getRequestState().invalidateStateModelForThread();
143
144 if (!isStateless) {
145 scopedContainers.getSessionStoring().invalidateCacheForThread();
146 scopedContainers.getSessionState().invalidateStateModelForThread();
147 }
148
149 if (exposeServletInfrastructure) {
150 currentSession.set(null);
151 currentRequest.set(null);
152 currentResponse.set(null);
153 }
154
155 }
156
157 private void printSessionSizeDetailsForDebugging(SessionStoreHolder ssh) throws IOException {
158 if ( debug ){
159 ByteArrayOutputStream baos = new ByteArrayOutputStream();
160 ObjectOutputStream oos = new ObjectOutputStream(baos);
161 oos.writeObject(ssh);
162 oos.close();
163 baos.close();
164 String xml = new XStream(new PureJavaReflectionProvider()).toXML(ssh);
165 int bytes = baos.toByteArray().length;
166 System.out.println("** Session written (" + bytes + " bytes), xml representation= " + xml);
167 }
168 }
169
170 protected void containersSetupForRequest(MutablePicoContainer appcontainer, MutablePicoContainer sessionContainer,
171 MutablePicoContainer requestContainer, ServletRequest req, ServletResponse resp) {
172 }
173
174 private static ThreadLocal<HttpSession> currentSession = new ThreadLocal<HttpSession>();
175 private static ThreadLocal<ServletRequest> currentRequest = new ThreadLocal<ServletRequest>();
176 private static ThreadLocal<ServletResponse> currentResponse = new ThreadLocal<ServletResponse>();
177
178 protected abstract void setAppContainer(MutablePicoContainer container);
179
180 protected abstract void setSessionContainer(MutablePicoContainer container);
181
182 protected abstract void setRequestContainer(MutablePicoContainer container);
183
184 public static class ServletFilter extends PicoServletContainerFilter {
185
186 private static ThreadLocal<MutablePicoContainer> currentAppContainer = new ThreadLocal<MutablePicoContainer>();
187 private static ThreadLocal<MutablePicoContainer> currentSessionContainer = new ThreadLocal<MutablePicoContainer>();
188 private static ThreadLocal<MutablePicoContainer> currentRequestContainer = new ThreadLocal<MutablePicoContainer>();
189
190 protected void setAppContainer(MutablePicoContainer container) {
191 if (currentRequestContainer == null) {
192 currentRequestContainer = new ThreadLocal<MutablePicoContainer>();
193 }
194 currentAppContainer.set(container);
195 }
196
197 protected void setRequestContainer(MutablePicoContainer container) {
198 if (currentRequestContainer == null) {
199 currentRequestContainer = new ThreadLocal<MutablePicoContainer>();
200 }
201 currentRequestContainer.set(container);
202 }
203
204 protected void setSessionContainer(MutablePicoContainer container) {
205 if (currentSessionContainer == null) {
206 currentSessionContainer = new ThreadLocal<MutablePicoContainer>();
207 }
208 currentSessionContainer.set(container);
209 }
210 }
211
212 public static class HttpSessionInjector extends AbstractAdapter {
213
214 public HttpSessionInjector() {
215 super(HttpSession.class, HttpSession.class);
216 }
217
218 public Object getComponentInstance(PicoContainer picoContainer, Type type) throws PicoCompositionException {
219 return currentSession.get();
220 }
221
222 public void verify(PicoContainer picoContainer) throws PicoCompositionException {
223 }
224
225 public String getDescriptor() {
226 return "HttpSessionInjector";
227 }
228 }
229
230 public static class HttpServletRequestInjector extends AbstractAdapter {
231
232 public HttpServletRequestInjector() {
233 super(HttpServletRequest.class, HttpServletRequest.class);
234 }
235
236 public Object getComponentInstance(PicoContainer picoContainer, Type type) throws PicoCompositionException {
237 return currentRequest.get();
238 }
239
240 public void verify(PicoContainer picoContainer) throws PicoCompositionException {
241 }
242
243 public String getDescriptor() {
244 return "HttpServletRequestInjector";
245 }
246 }
247
248 public static class HttpServletResponseInjector extends AbstractAdapter {
249
250 public HttpServletResponseInjector() {
251 super(HttpServletResponse.class, HttpServletResponse.class);
252 }
253
254 public Object getComponentInstance(PicoContainer picoContainer, Type type) throws PicoCompositionException {
255 return currentResponse.get();
256 }
257
258 public void verify(PicoContainer picoContainer) throws PicoCompositionException {
259 }
260
261 public String getDescriptor() {
262 return "HttpServletResponseInjector";
263 }
264 }
265 }