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