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 }