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    }