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