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