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}