001    // Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5;
016    
017    import org.apache.tapestry5.internal.ServletContextSymbolProvider;
018    import org.apache.tapestry5.internal.TapestryAppInitializer;
019    import org.apache.tapestry5.ioc.Registry;
020    import org.apache.tapestry5.ioc.def.ModuleDef;
021    import org.apache.tapestry5.ioc.internal.services.SystemPropertiesSymbolProvider;
022    import org.apache.tapestry5.ioc.services.SymbolProvider;
023    import org.apache.tapestry5.services.HttpServletRequestHandler;
024    import org.apache.tapestry5.services.ServletApplicationInitializer;
025    import org.slf4j.Logger;
026    import org.slf4j.LoggerFactory;
027    
028    import javax.servlet.*;
029    import javax.servlet.http.HttpServletRequest;
030    import javax.servlet.http.HttpServletResponse;
031    import java.io.IOException;
032    
033    /**
034     * The TapestryFilter is responsible for intercepting all requests into the web application. It
035     * identifies the requests
036     * that are relevant to Tapestry, and lets the servlet container handle the rest. It is also
037     * responsible for
038     * initializing Tapestry.
039     * <p/>
040     * The application is primarily configured via context-level init parameters.
041     * <p/>
042     * <dl>
043     * <dt>tapestry.app-package</dt>
044     * <dd>The application package (used to search for pages, components, etc.)</dd>
045     * </dl>
046     * <p/>
047     * In addition, a JVM system property affects configuration: <code>tapestry.execution-mode</code>
048     * (with default value "production"). This property is a comma-separated list of execution modes.
049     * For each mode, an additional init parameter is checked for:
050     * <code>tapestry.<em>mode</em>-modules</code>; this is a comma-separated list of module class names
051     * to load. In this way, more precise control over the available modules can be obtained which is
052     * often needed during testing.
053     */
054    public class TapestryFilter implements Filter
055    {
056        private final Logger logger = LoggerFactory.getLogger(TapestryFilter.class);
057    
058        private FilterConfig config;
059    
060        private Registry registry;
061    
062        private HttpServletRequestHandler handler;
063    
064        /**
065         * Key under which that Tapestry IoC {@link org.apache.tapestry5.ioc.Registry} is stored in the
066         * ServletContext. This
067         * allows other code, beyond Tapestry, to obtain the Registry and, from it, any Tapestry
068         * services. Such code should
069         * be careful about invoking {@link org.apache.tapestry5.ioc.Registry#cleanupThread()}
070         * appropriately.
071         */
072        public static final String REGISTRY_CONTEXT_NAME = "org.apache.tapestry5.application-registry";
073    
074        /**
075         * Initializes the filter using the {@link TapestryAppInitializer}. The application name is the
076         * capitalization of
077         * the filter name (as specified in web.xml).
078         */
079        public final void init(FilterConfig filterConfig) throws ServletException
080        {
081            config = filterConfig;
082    
083            final ServletContext context = config.getServletContext();
084    
085            String filterName = config.getFilterName();
086    
087            SymbolProvider provider = new SymbolProvider()
088            {
089                SymbolProvider contextProvider = new ServletContextSymbolProvider(context);
090                SymbolProvider systemProvider = new SystemPropertiesSymbolProvider();
091    
092                public String valueForSymbol(String symbolName)
093                {
094                    String contextValue = contextProvider.valueForSymbol(symbolName);
095                    if (contextValue != null) return contextValue;
096    
097                    return systemProvider.valueForSymbol(symbolName);
098                }
099            };
100    
101            String executionMode = System.getProperty(SymbolConstants.EXECUTION_MODE, "production");
102    
103            TapestryAppInitializer appInitializer = new TapestryAppInitializer(logger, provider,
104                    filterName, executionMode);
105    
106            appInitializer.addModules(provideExtraModuleDefs(context));
107            appInitializer.addModules(provideExtraModuleClasses(context));
108    
109            registry = appInitializer.createRegistry();
110    
111            context.setAttribute(REGISTRY_CONTEXT_NAME, registry);
112    
113            ServletApplicationInitializer ai = registry.getService("ServletApplicationInitializer",
114                    ServletApplicationInitializer.class);
115    
116            ai.initializeApplication(filterConfig.getServletContext());
117    
118            registry.performRegistryStartup();
119    
120            handler = registry.getService("HttpServletRequestHandler", HttpServletRequestHandler.class);
121    
122            init(registry);
123    
124            appInitializer.announceStartup();
125        }
126    
127        protected final FilterConfig getFilterConfig()
128        {
129            return config;
130        }
131    
132        /**
133         * Invoked from {@link #init(FilterConfig)} after the Registry has been created, to allow any
134         * additional
135         * initialization to occur. This implementation does nothing, and my be overriden in subclasses.
136         *
137         * @param registry from which services may be extracted
138         * @throws ServletException
139         */
140        protected void init(Registry registry) throws ServletException
141        {
142    
143        }
144    
145        /**
146         * Overridden in subclasses to provide additional module definitions beyond those normally
147         * located. This
148         * implementation returns an empty array.
149         */
150        protected ModuleDef[] provideExtraModuleDefs(ServletContext context)
151        {
152            return new ModuleDef[0];
153        }
154    
155        /**
156         * Overriden in subclasses to provide additional module classes beyond those normally located. This implementation
157         * returns an empty array.
158         *
159         * @since 5.3
160         */
161        protected Class[] provideExtraModuleClasses(ServletContext context)
162        {
163            return new Class[0];
164        }
165    
166        public final void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
167                throws IOException, ServletException
168        {
169            try
170            {
171                boolean handled = handler.service((HttpServletRequest) request,
172                        (HttpServletResponse) response);
173    
174                if (!handled)
175                    chain.doFilter(request, response);
176            } finally
177            {
178                registry.cleanupThread();
179            }
180        }
181    
182        /**
183         * Shuts down and discards the registry. Invokes
184         * {@link #destroy(org.apache.tapestry5.ioc.Registry)} to allow
185         * subclasses to peform any shutdown logic, then shuts down the registry, and removes it from
186         * the ServletContext.
187         */
188        public final void destroy()
189        {
190            destroy(registry);
191    
192            registry.shutdown();
193    
194            config.getServletContext().removeAttribute(REGISTRY_CONTEXT_NAME);
195    
196            registry = null;
197            config = null;
198            handler = null;
199        }
200    
201        /**
202         * Invoked from {@link #destroy()} to allow subclasses to add additional shutdown logic to the
203         * filter. The Registry
204         * will be shutdown after this call. This implementation does nothing, and may be overridden in
205         * subclasses.
206         *
207         * @param registry
208         */
209        protected void destroy(Registry registry)
210        {
211    
212        }
213    }