    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;
    014
    015import org.apache.tapestry5.internal.ServletContextSymbolProvider;
    016import org.apache.tapestry5.internal.SingleKeySymbolProvider;
    017import org.apache.tapestry5.internal.TapestryAppInitializer;
    018import org.apache.tapestry5.internal.util.DelegatingSymbolProvider;
    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 *
    040 * The application is primarily configured via context-level init parameters.
    041 *
    042 * <dl>
    043 * <dt>tapestry.app-package</dt>
    044 * <dd>The application package (used to search for pages, components, etc.)</dd>
    045 * </dl>
    046 *
    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 the 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 combinedProvider = new DelegatingSymbolProvider(
    088                new SystemPropertiesSymbolProvider(),
    089                new SingleKeySymbolProvider(SymbolConstants.CONTEXT_PATH, context.getContextPath()),
    090                new ServletContextSymbolProvider(context),
    091                new SingleKeySymbolProvider(SymbolConstants.EXECUTION_MODE, "production"));
    092
    093        String executionMode = combinedProvider.valueForSymbol(SymbolConstants.EXECUTION_MODE);
    094
    095        TapestryAppInitializer appInitializer = new TapestryAppInitializer(logger, combinedProvider,
    096                filterName, executionMode);
    097
    098        appInitializer.addModules(provideExtraModuleDefs(context));
    099        appInitializer.addModules(provideExtraModuleClasses(context));
    100
    101        registry = appInitializer.createRegistry();
    102
    103        context.setAttribute(REGISTRY_CONTEXT_NAME, registry);
    104
    105        ServletApplicationInitializer ai = registry.getService("ServletApplicationInitializer",
    106                ServletApplicationInitializer.class);
    107
    108        ai.initializeApplication(context);
    109
    110        registry.performRegistryStartup();
    111
    112        handler = registry.getService("HttpServletRequestHandler", HttpServletRequestHandler.class);
    113
    114        init(registry);
    115
    116        appInitializer.announceStartup();
    117
    118        registry.cleanupThread();
    119    }
    120
    121    protected final FilterConfig getFilterConfig()
    122    {
    123        return config;
    124    }
    125
    126    /**
    127     * Invoked from {@link #init(FilterConfig)} after the Registry has been created, to allow any
    128     * additional
    129     * initialization to occur. This implementation does nothing, and my be overridden in subclasses.
    130     *
    131     * @param registry
    132     *         from which services may be extracted
    133     * @throws ServletException
    134     */
    135    protected void init(Registry registry) throws ServletException
    136    {
    137
    138    }
    139
    140    /**
    141     * Overridden in subclasses to provide additional module definitions beyond those normally
    142     * located. This
    143     * implementation returns an empty array.
    144     */
    145    protected ModuleDef[] provideExtraModuleDefs(ServletContext context)
    146    {
    147        return new ModuleDef[0];
    148    }
    149
    150    /**
    151     * Overridden in subclasses to provide additional module classes beyond those normally located. This implementation
    152     * returns an empty array.
    153     *
    154     * @since 5.3
    155     */
    156    protected Class[] provideExtraModuleClasses(ServletContext context)
    157    {
    158        return new Class[0];
    159    }
    160
    161    public final void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    162            throws IOException, ServletException
    163    {
    164        try
    165        {
    166            boolean handled = handler.service((HttpServletRequest) request,
    167                    (HttpServletResponse) response);
    168
    169            if (!handled)
    170            {
    171                chain.doFilter(request, response);
    172            }
    173        } finally
    174        {
    175            registry.cleanupThread();
    176        }
    177    }
    178
    179    /**
    180     * Shuts down and discards the registry. Invokes
    181     * {@link #destroy(org.apache.tapestry5.ioc.Registry)} to allow
    182     * subclasses to perform any shutdown logic, then shuts down the registry, and removes it from
    183     * the ServletContext.
    184     */
    185    public final void destroy()
    186    {
    187        destroy(registry);
    188
    189        registry.shutdown();
    190
    191        config.getServletContext().removeAttribute(REGISTRY_CONTEXT_NAME);
    192
    193        registry = null;
    194        config = null;
    195        handler = null;
    196    }
    197
    198    /**
    199     * Invoked from {@link #destroy()} to allow subclasses to add additional shutdown logic to the
    200     * filter. The Registry
    201     * will be shutdown after this call. This implementation does nothing, and may be overridden in
    202     * subclasses.
    203     *
    204     * @param registry
    205     */
    206    protected void destroy(Registry registry)
    207    {
    208
    209    }
    210}