001    package org.picocontainer.web;
002    
003    import java.io.IOException;
004    
005    import javax.servlet.Filter;
006    import javax.servlet.FilterChain;
007    import javax.servlet.FilterConfig;
008    import javax.servlet.ServletException;
009    import javax.servlet.ServletRequest;
010    import javax.servlet.ServletResponse;
011    import javax.servlet.http.HttpServletRequest;
012    
013    import org.picocontainer.PicoContainer;
014    import org.picocontainer.PicoCompositionException;
015    
016    /**
017    *<p>
018    * ServletContainerProxyFilter is a Filter which delegates to any Filter which is registered in a PicoContainer,
019    * in any of the web scopes: context, session or request.
020    * This form of delegation is particularly useful as it brings dependency injections to filters.
021    * In fact the delegate filter may be implemented via any form of dependency injection.
022    *</p>
023    *
024    *<p>The delegate Filter must be registered via the <code>delegate-key</code> or <code>delegate-class</code>
025    * init-params of this Filter.  
026    *</p>
027    * 
028    *<p>The initialization is done lazily, using the <code>init-type</code> init-param
029    * to control it.  Allowed values are:
030    * <ul>
031    *   <li>"context": will call init() on the filter only once</li>
032    *   <li>"request": will re-init it at every request</li>
033    *   <li>"never": will never init it</li>
034    *</ul>
035    *The default is "context".
036    * </p>
037    * 
038    * <p>The lookup in the PicoContainer is by default done for each request, but you
039    * can control that behaviour with the <code>lookup-only-once</code> init-param.
040    * If set to "true", ServletContainerProxyFilter will only lookup your delegate filter
041    * at the first request.
042    * </p>
043    * 
044    * <p><b>Note</b>: Be aware that any dependency on your filter, in this setup, will stay
045    * referenced by your filter for its whole lifetime, even though this dependency
046    * might have been set up at request level in your composer!
047    * </p>
048    *
049    * @author Gr&eacute;gory Joseph
050    * @author Mauro Talevi
051    */
052    public class ServletContainerProxyFilter implements Filter {
053    
054        private static final String CONTEXT_INIT_TYPE = "context";
055        private static final String REQUEST_INIT_TYPE = "request";
056    
057        private String initType;
058        private boolean lookupOnlyOnce;
059        private FilterConfig filterConfig;
060        private Filter delegate;
061    
062        public void init(FilterConfig filterConfig) throws ServletException {
063            this.filterConfig = filterConfig;
064            initType = filterConfig.getInitParameter("init-type");        
065            if ( initType == null ){
066                initType = CONTEXT_INIT_TYPE;
067            }
068            lookupOnlyOnce = Boolean.valueOf(filterConfig.getInitParameter("lookup-only-once"));
069        }
070    
071        public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
072            if (delegate == null || !lookupOnlyOnce) {            
073                lookupDelegate((HttpServletRequest) request);
074                if (initType.equals(CONTEXT_INIT_TYPE) ) {
075                    initDelegate();
076                }
077            }
078            if (initType.equals(REQUEST_INIT_TYPE)) {
079                initDelegate();
080            }
081            delegate.doFilter(request, response, filterChain);
082        }
083    
084        public void destroy() {
085            if (delegate != null) {
086                delegate.destroy();
087            }
088        }
089        
090        protected void initDelegate() throws ServletException {
091            if (delegate == null) {
092                throw new IllegalStateException("Delegate filter was not set up");
093            }
094            delegate.init(filterConfig);
095        }
096        
097        /**
098         * Looks up delegate Filter in PicoContainer found in any of the web scopes.
099         * 
100         * @param request the HttpServletRequest used to find the PicoContainer
101         * @throws PicoCompositionException if the delegate Filter cannot be found
102         */
103        protected void lookupDelegate(HttpServletRequest request) {
104            PicoContainer pico = PicoServletContainerFilter.ServletFilter.getRequestContainerForThread();
105            String delegateClassName = filterConfig.getInitParameter("delegate-class");
106            String delegateKey = filterConfig.getInitParameter("delegate-key");
107            if (delegateClassName != null) {
108                try {
109                    Class<?> delegateClass = getClassLoader().loadClass(delegateClassName);
110                    delegate = (Filter) pico.getComponent(delegateClass);
111                } catch (ClassNotFoundException e) {
112                    throw new PicoCompositionException("Cannot load " + delegateClassName, e);
113                }
114            } else if (delegateKey != null) {
115                delegate = (Filter) pico.getComponent(delegateKey);
116            } else {
117                throw new PicoCompositionException("You must specify one of delegate-class or delegate-key in the filter config");
118            }
119    
120            if (delegate == null) {
121                throw new PicoCompositionException("Cannot find delegate for class " + delegateClassName + " or key "+ delegateKey);
122            }
123        }
124    
125        private ClassLoader getClassLoader() {
126            return this.getClass().getClassLoader();
127        }
128        
129    
130    }