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 015package org.apache.tapestry; 016 017import java.io.IOException; 018import java.util.Locale; 019 020import javax.servlet.ServletConfig; 021import javax.servlet.ServletContext; 022import javax.servlet.ServletException; 023import javax.servlet.http.HttpServlet; 024import javax.servlet.http.HttpServletRequest; 025import javax.servlet.http.HttpServletResponse; 026 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029import org.apache.hivemind.ClassResolver; 030import org.apache.hivemind.ErrorHandler; 031import org.apache.hivemind.Registry; 032import org.apache.hivemind.Resource; 033import org.apache.hivemind.impl.DefaultClassResolver; 034import org.apache.hivemind.impl.RegistryBuilder; 035import org.apache.hivemind.impl.StrictErrorHandler; 036import org.apache.hivemind.impl.XmlModuleDescriptorProvider; 037import org.apache.hivemind.util.ContextResource; 038import org.apache.tapestry.services.ApplicationInitializer; 039import org.apache.tapestry.services.ServletRequestServicer; 040import 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 054public 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}