001 // Copyright 2004, 2005 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.tapestry;
016
017 import java.io.IOException;
018 import java.util.Locale;
019
020 import javax.servlet.ServletConfig;
021 import javax.servlet.ServletContext;
022 import javax.servlet.ServletException;
023 import javax.servlet.http.HttpServlet;
024 import javax.servlet.http.HttpServletRequest;
025 import javax.servlet.http.HttpServletResponse;
026
027 import org.apache.commons.logging.Log;
028 import org.apache.commons.logging.LogFactory;
029 import org.apache.hivemind.ClassResolver;
030 import org.apache.hivemind.ErrorHandler;
031 import org.apache.hivemind.Registry;
032 import org.apache.hivemind.Resource;
033 import org.apache.hivemind.impl.DefaultClassResolver;
034 import org.apache.hivemind.impl.RegistryBuilder;
035 import org.apache.hivemind.impl.StrictErrorHandler;
036 import org.apache.hivemind.impl.XmlModuleDescriptorProvider;
037 import org.apache.hivemind.util.ContextResource;
038 import org.apache.tapestry.services.ApplicationInitializer;
039 import org.apache.tapestry.services.ServletRequestServicer;
040 import org.apache.tapestry.util.exception.ExceptionAnalyzer;
041
042 /**
043 * Links a servlet container with a Tapestry application. The servlet init parameter
044 * <code>org.apache.tapestry.application-specification</code> should be set to the complete
045 * resource path (within the classpath) to the application specification, i.e.,
046 * <code>/com/foo/bar/MyApp.application</code>. As of release 4.0, this servlet will also create
047 * a HiveMind Registry and manage it.
048 *
049 * @author Howard Lewis Ship
050 * @see org.apache.tapestry.services.ApplicationInitializer
051 * @see org.apache.tapestry.services.ServletRequestServicer
052 */
053
054 public class ApplicationServlet extends HttpServlet
055 {
056 private static final long serialVersionUID = -8046042689991538059L;
057
058 /**
059 * Prefix used to store the HiveMind Registry into the ServletContext. This string is suffixed
060 * with the servlet name (in case multiple Tapestry applications are executing within a single
061 * web application).
062 *
063 * @since 4.0
064 */
065
066 private static final String REGISTRY_KEY_PREFIX = "org.apache.tapestry.Registry:";
067
068 private static final Log LOG = LogFactory.getLog(ApplicationServlet.class);
069
070 /**
071 * Invokes {@link #doService(HttpServletRequest, HttpServletResponse)}.
072 *
073 * @since 1.0.6
074 */
075
076 public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException,
077 ServletException
078 {
079 doService(request, response);
080 }
081
082 /**
083 * @since 2.3
084 */
085
086 private ClassResolver _resolver;
087
088 /**
089 * The key used to store the registry into the ServletContext.
090 *
091 * @since 4.0
092 */
093
094 private String _registryKey;
095
096 /**
097 * @since 4.0
098 */
099
100 private Registry _registry;
101
102 /**
103 * @since 4.0
104 */
105 private ServletRequestServicer _requestServicer;
106
107 /**
108 * Handles the GET and POST requests. Performs the following:
109 * <ul>
110 * <li>Construct a {@link RequestContext}
111 * <li>Invoke {@link #getEngine(RequestContext)}to get or create the {@link IEngine}
112 * <li>Invoke {@link IEngine#service(RequestContext)}on the application
113 * </ul>
114 */
115
116 protected void doService(HttpServletRequest request, HttpServletResponse response)
117 throws IOException, ServletException
118 {
119 try
120 {
121 _registry.setupThread();
122
123 _requestServicer.service(request, response);
124 }
125 catch (ServletException ex)
126 {
127 log("ServletException", ex);
128
129 show(ex);
130
131 // Rethrow it.
132
133 throw ex;
134 }
135 catch (IOException ex)
136 {
137 log("IOException", ex);
138
139 show(ex);
140
141 // Rethrow it.
142
143 throw ex;
144 }
145 finally
146 {
147 _registry.cleanupThread();
148 }
149 }
150
151 protected void show(Exception ex)
152 {
153 System.err.println("\n\n**********************************************************\n\n");
154
155 new ExceptionAnalyzer().reportException(ex, System.err);
156
157 System.err.println("\n**********************************************************\n");
158
159 }
160
161 /**
162 * Invokes {@link #doService(HttpServletRequest, HttpServletResponse)}.
163 */
164
165 public void doPost(HttpServletRequest request, HttpServletResponse response)
166 throws IOException, ServletException
167 {
168 doService(request, response);
169 }
170
171 /**
172 * Reads the application specification when the servlet is first initialized. All
173 * {@link IEngine engine instances}will have access to the specification via the servlet.
174 *
175 * @see #constructApplicationSpecification()
176 * @see #createResourceResolver()
177 */
178
179 public void init(ServletConfig config) throws ServletException
180 {
181 String name = config.getServletName();
182
183 _registryKey = REGISTRY_KEY_PREFIX + name;
184
185 long startTime = System.currentTimeMillis();
186 long elapsedToRegistry = 0;
187
188 super.init(config);
189
190 _resolver = createClassResolver();
191
192 try
193 {
194 _registry = constructRegistry(config);
195
196 elapsedToRegistry = System.currentTimeMillis() - startTime;
197
198 initializeApplication();
199
200 config.getServletContext().setAttribute(_registryKey, _registry);
201 }
202 catch (Exception ex)
203 {
204 show(ex);
205
206 throw new ServletException(TapestryMessages.servletInitFailure(ex), ex);
207 }
208
209 long elapsedOverall = System.currentTimeMillis() - startTime;
210
211 LOG.info(TapestryMessages.servletInit(name, elapsedToRegistry, elapsedOverall));
212 }
213
214 /**
215 * Invoked from {@link #init(ServletConfig)}to create a resource resolver for the servlet
216 * (which will utlimately be shared and used through the application).
217 * <p>
218 * This implementation constructs a {@link DefaultResourceResolver}, subclasses may provide a
219 * different implementation.
220 *
221 * @see #getResourceResolver()
222 * @since 2.3
223 */
224
225 protected ClassResolver createClassResolver()
226 {
227 return new DefaultClassResolver();
228 }
229
230 /**
231 * Invoked from {@link #init(ServletConfig)}to construct the Registry to be used by the
232 * application.
233 * <p>
234 * This looks in the standard places (on the classpath), but also in the WEB-INF/name and
235 * WEB-INF folders (where name is the name of the servlet).
236 *
237 * @since 4.0
238 */
239 protected Registry constructRegistry(ServletConfig config)
240 {
241 ErrorHandler errorHandler = constructErrorHandler(config);
242
243 RegistryBuilder builder = new RegistryBuilder(errorHandler);
244
245 builder.addModuleDescriptorProvider(new XmlModuleDescriptorProvider(_resolver));
246
247 String name = config.getServletName();
248 ServletContext context = config.getServletContext();
249
250 addModuleIfExists(builder, context, "/WEB-INF/" + name + "/hivemodule.xml");
251 addModuleIfExists(builder, context, "/WEB-INF/hivemodule.xml");
252
253 return builder.constructRegistry(Locale.getDefault());
254 }
255
256 /**
257 * Invoked by {@link #constructRegistry(ServletConfig)} to create and return an
258 * {@link ErrorHandler} instance to be used when constructing the Registry (and then to handle
259 * any runtime exceptions). This implementation returns a new instance of
260 * {@link org.apache.hivemind.impl.StrictErrorHandler}.
261 *
262 * @since 4.0
263 */
264 protected ErrorHandler constructErrorHandler(ServletConfig config)
265 {
266 return new StrictErrorHandler();
267 }
268
269 /**
270 * Looks for a file in the servlet context; if it exists, it is expected to be a HiveMind module
271 * descriptor, and is added to the builder.
272 *
273 * @since 4.0
274 */
275
276 protected void addModuleIfExists(RegistryBuilder builder, ServletContext context, String path)
277 {
278 Resource r = new ContextResource(context, path);
279
280 if (r.getResourceURL() == null)
281 return;
282
283 builder.addModuleDescriptorProvider(new XmlModuleDescriptorProvider(_resolver, r));
284 }
285
286 /**
287 * Invoked from {@link #init(ServletConfig)}, after the registry has been constructed, to
288 * bootstrap the application via the <code>tapestry.MasterApplicationInitializer</code>
289 * service.
290 *
291 * @since 4.0
292 */
293 protected void initializeApplication()
294 {
295 ApplicationInitializer ai = (ApplicationInitializer) _registry.getService(
296 "tapestry.init.MasterInitializer",
297 ApplicationInitializer.class);
298
299 ai.initialize(this);
300
301 _registry.cleanupThread();
302
303 _requestServicer = (ServletRequestServicer) _registry.getService(
304 "tapestry.request.ServletRequestServicer",
305 ServletRequestServicer.class);
306 }
307
308 /**
309 * Shuts down the registry (if it exists).
310 *
311 * @since 4.0
312 */
313 public void destroy()
314 {
315 getServletContext().removeAttribute(_registryKey);
316
317 if (_registry != null)
318 {
319 _registry.shutdown();
320 _registry = null;
321 }
322 }
323 }