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 }