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
015package org.apache.tapestry5;
016
017import org.apache.tapestry5.internal.ServletContextSymbolProvider;
018import org.apache.tapestry5.internal.TapestryAppInitializer;
019import org.apache.tapestry5.ioc.Registry;
020import org.apache.tapestry5.ioc.def.ModuleDef;
021import org.apache.tapestry5.ioc.internal.services.SystemPropertiesSymbolProvider;
022import org.apache.tapestry5.ioc.services.SymbolProvider;
023import org.apache.tapestry5.services.HttpServletRequestHandler;
024import org.apache.tapestry5.services.ServletApplicationInitializer;
025import org.slf4j.Logger;
026import org.slf4j.LoggerFactory;
027
028import javax.servlet.*;
029import javax.servlet.http.HttpServletRequest;
030import javax.servlet.http.HttpServletResponse;
031import 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 */
054public 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}