    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}