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