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}