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 }