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}