View Javadoc

1   package org.apache.turbine;
2   
3   /* ====================================================================
4    * The Apache Software License, Version 1.1
5    *
6    * Copyright (c) 2001-2003 The Apache Software Foundation.  All rights
7    * reserved.
8    *
9    * Redistribution and use in source and binary forms, with or without
10   * modification, are permitted provided that the following conditions
11   * are met:
12   *
13   * 1. Redistributions of source code must retain the above copyright
14   *    notice, this list of conditions and the following disclaimer.
15   *
16   * 2. Redistributions in binary form must reproduce the above copyright
17   *    notice, this list of conditions and the following disclaimer in
18   *    the documentation and/or other materials provided with the
19   *    distribution.
20   *
21   * 3. The end-user documentation included with the redistribution,
22   *    if any, must include the following acknowledgment:
23   *       "This product includes software developed by the
24   *        Apache Software Foundation (http://www.apache.org/)."
25   *    Alternately, this acknowledgment may appear in the software itself,
26   *    if and wherever such third-party acknowledgments normally appear.
27   *
28   * 4. The names "Apache" and "Apache Software Foundation" and
29   *    "Apache Turbine" must not be used to endorse or promote products
30   *    derived from this software without prior written permission. For
31   *    written permission, please contact apache@apache.org.
32   *
33   * 5. Products derived from this software may not be called "Apache",
34   *    "Apache Turbine", nor may "Apache" appear in their name, without
35   *    prior written permission of the Apache Software Foundation.
36   *
37   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48   * SUCH DAMAGE.
49   * ====================================================================
50   *
51   * This software consists of voluntary contributions made by many
52   * individuals on behalf of the Apache Software Foundation.  For more
53   * information on the Apache Software Foundation, please see
54   * <http://www.apache.org/>.
55   */
56  
57  import java.io.File;
58  import java.io.FileInputStream;
59  import java.io.FileNotFoundException;
60  import java.io.IOException;
61  import java.util.HashMap;
62  import java.util.Map;
63  import java.util.Properties;
64  
65  import javax.servlet.ServletConfig;
66  import javax.servlet.ServletContext;
67  import javax.servlet.ServletException;
68  import javax.servlet.http.HttpServlet;
69  import javax.servlet.http.HttpServletRequest;
70  import javax.servlet.http.HttpServletResponse;
71  
72  import org.apache.commons.configuration.Configuration;
73  import org.apache.commons.configuration.ConfigurationFactory;
74  import org.apache.commons.configuration.PropertiesConfiguration;
75  
76  import org.apache.commons.lang.StringUtils;
77  
78  import org.apache.commons.lang.exception.ExceptionUtils;
79  
80  import org.apache.commons.logging.Log;
81  import org.apache.commons.logging.LogFactory;
82  
83  import org.apache.commons.xo.Mapper;
84  
85  import org.apache.log4j.PropertyConfigurator;
86  
87  import org.apache.turbine.modules.PageLoader;
88  import org.apache.turbine.pipeline.DefaultPipelineData;
89  import org.apache.turbine.pipeline.Pipeline;
90  import org.apache.turbine.pipeline.PipelineData;
91  import org.apache.turbine.pipeline.TurbinePipeline;
92  
93  import org.apache.turbine.services.ServiceManager;
94  import org.apache.turbine.services.TurbineServices;
95  import org.apache.turbine.services.avaloncomponent.AvalonComponentService;
96  import org.apache.turbine.services.component.ComponentService;
97  import org.apache.turbine.services.template.TemplateService;
98  import org.apache.turbine.services.template.TurbineTemplate;
99  import org.apache.turbine.services.rundata.RunDataService;
100 import org.apache.turbine.services.rundata.TurbineRunDataFacade;
101 
102 import org.apache.turbine.util.RunData;
103 import org.apache.turbine.util.ServerData;
104 import org.apache.turbine.util.TurbineConfig;
105 import org.apache.turbine.util.TurbineException;
106 import org.apache.turbine.util.uri.URIConstants;
107 
108 /***
109  * Turbine is the main servlet for the entire system. It is <code>final</code>
110  * because you should <i>not</i> ever need to subclass this servlet.  If you
111  * need to perform initialization of a service, then you should implement the
112  * Services API and let your code be initialized by it.
113  * If you need to override something in the <code>doGet()</code> or
114  * <code>doPost()</code> methods, edit the TurbineResources.properties file and
115  * specify your own classes there.
116  * <p>
117  * Turbine servlet recognizes the following initialization parameters.
118  * <ul>
119  * <li><code>properties</code> the path to TurbineResources.properties file
120  * used by the default implementation of <code>ResourceService</code>, relative
121  * to the application root.</li>
122  * <li><code>basedir</code> this parameter is used <strong>only</strong> if your
123  * application server does not support web applications, or the or does not
124  * support <code>ServletContext.getRealPath(String)</code> method correctly.
125  * You can use this parameter to specify the directory within the server's
126  * filesystem, that is the base of your web application.</li>
127  * </ul>
128  *
129  * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
130  * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a>
131  * @author <a href="mailto:greg@shwoop.com">Greg Ritter</a>
132  * @author <a href="mailto:john.mcnally@clearink.com">John D. McNally</a>
133  * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
134  * @author <a href="mailto:krzewski@e-point.pl">Rafal Krzewski</a>
135  * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
136  * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
137  * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
138  * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
139  * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
140  * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
141  * @author <a href="mailto:peter@courcoux.biz">Peter Courcoux</a>
142  * @version $Id: Turbine.java,v 1.51 2004/08/02 08:57:36 epugh Exp $
143  */
144 public class Turbine
145         extends HttpServlet
146         implements TurbineConstants
147 {
148     /***
149      * Name of path info parameter used to indicate the redirected stage of
150      * a given user's initial Turbine request
151      */
152     public static final String REDIRECTED_PATHINFO_NAME = "redirected";
153 
154     /*** The base directory key */
155     public static final String BASEDIR_KEY = "basedir";
156 
157     /***
158      * In certain situations the init() method is called more than once,
159      * somtimes even concurrently. This causes bad things to happen,
160      * so we use this flag to prevent it.
161      */
162     private static boolean firstInit = true;
163     
164 	/***
165 	 * The pipeline to use when processing requests.
166 	 */
167 	private static Pipeline pipeline = null;
168 
169     /*** Whether init succeeded or not. */
170     private static Throwable initFailure = null;
171 
172     /***
173      * Should initialization activities be performed during doGet() execution?
174      */
175     private static boolean firstDoGet = true;
176 
177     /***
178      * Keep all the properties of the web server in a convenient data
179      * structure
180      */
181     private static ServerData serverData = null;
182 
183     /*** The base from which the Turbine application will operate. */
184     private static String applicationRoot;
185 
186     /*** Servlet config for this Turbine webapp. */
187     private static ServletConfig servletConfig;
188 
189     /*** Servlet context for this Turbine webapp. */
190     private static ServletContext servletContext;
191 
192     /***
193      * The webapp root where the Turbine application
194      * is running in the servlet container.
195      * This might differ from the application root.
196      */
197     private static String webappRoot;
198 
199     /*** Our internal configuration object */
200     private static Configuration configuration = null;
201 
202     /*** A reference to the Template Service */
203     private TemplateService templateService = null;
204 
205     /*** A reference to the RunData Service */
206     private RunDataService rundataService = null;
207 
208     /*** Logging class from commons.logging */
209     private static Log log = LogFactory.getLog(Turbine.class);
210 
211     /***
212      * This init method will load the default resources from a
213      * properties file.
214      *
215      * This method is called by init(ServletConfig config)
216      *
217      * @exception ServletException a servlet exception.
218      */
219     public final void init() throws ServletException
220     {
221         synchronized (this.getClass())
222         {
223             super.init();
224             ServletConfig config = getServletConfig();
225 
226             if (!firstInit)
227             {
228                 log.info("Double initialization of Turbine was attempted!");
229                 return;
230             }
231             // executing init will trigger some static initializers, so we have
232             // only one chance.
233             firstInit = false;
234 
235             try
236             {
237                 ServletContext context = config.getServletContext();
238 
239                 configure(config, context);
240 
241                 templateService = TurbineTemplate.getService();
242                 rundataService = TurbineRunDataFacade.getService();
243 
244                 if (rundataService == null)
245                 {
246                     throw new TurbineException(
247                             "No RunData Service configured!");
248                 }
249 
250             }
251             catch (Exception e)
252             {
253                 // save the exception to complain loudly later :-)
254                 initFailure = e;
255                 log.fatal("Turbine: init() failed: ", e);
256                 throw new ServletException("Turbine: init() failed", e);
257             }
258             log.info("Turbine: init() Ready to Rumble!");
259         }
260     }
261 
262     /***
263      * Read the master configuration file in, configure logging
264      * and start up any early services.
265      *
266      * @param config The Servlet Configuration supplied by the container
267      * @param context The Servlet Context supplied by the container
268      *
269      * @throws Exception A problem occured while reading the configuration or performing early startup
270      */
271 
272     private void configure(ServletConfig config, ServletContext context)
273             throws Exception
274     {
275 
276         // Set the application root. This defaults to the webapp
277         // context if not otherwise set. This is to allow 2.1 apps
278         // to be developed from CVS. This feature will carry over
279         // into 3.0.
280         applicationRoot = findInitParameter(context, config,
281                 APPLICATION_ROOT_KEY,
282                 APPLICATION_ROOT_DEFAULT);
283 
284         webappRoot = config.getServletContext().getRealPath("/");
285         // log.info("Web Application root is " + webappRoot);
286         // log.info("Application root is "     + applicationRoot);
287 
288         if (applicationRoot == null || applicationRoot.equals(WEB_CONTEXT))
289         {
290             applicationRoot = webappRoot;
291             // log.info("got empty or 'webContext' Application root. Application root now: " + applicationRoot);
292         }
293 
294         // Set the applicationRoot for this webapp.
295         setApplicationRoot(applicationRoot);
296 
297         // Create any directories that need to be setup for
298         // a running Turbine application.
299         createRuntimeDirectories(context, config);
300 
301         //
302         // Now we run the Turbine configuration code. There are two ways
303         // to configure Turbine:
304         //
305         // a) By supplying an web.xml init parameter called "configuration"
306         //
307         // <init-param>
308         //   <param-name>configuration</param-name>
309         //   <param-value>/WEB-INF/conf/turbine.xml</param-value>
310         // </init-param>
311         //
312         // This loads an XML based configuration file.
313         //
314         // b) By supplying an web.xml init parameter called "properties"
315         //
316         // <init-param>
317         //   <param-name>properties</param-name>
318         //   <param-value>/WEB-INF/conf/TurbineResources.properties</param-value>
319         // </init-param>
320         //
321         // This loads a Properties based configuration file. Actually, these are
322         // extended properties as provided by commons-configuration
323         //
324         // If neither a) nor b) is supplied, Turbine will fall back to the
325         // known behaviour of loading a properties file called
326         // /WEB-INF/conf/TurbineResources.properties relative to the
327         // web application root.
328 
329         String confFile= findInitParameter(context, config, 
330                 TurbineConfig.CONFIGURATION_PATH_KEY, 
331                 null);
332 
333         String confPath;
334         String confStyle = "unset";
335 
336         if (StringUtils.isNotEmpty(confFile))
337         {
338             confPath = getRealPath(confFile);
339             ConfigurationFactory configurationFactory = new ConfigurationFactory(confPath);
340             configurationFactory.setBasePath(getApplicationRoot());
341             configuration = configurationFactory.getConfiguration();
342             confStyle = "XML";
343         }
344         else
345         {
346             confFile = findInitParameter(context, config,
347                     TurbineConfig.PROPERTIES_PATH_KEY,
348                     TurbineConfig.PROPERTIES_PATH_DEFAULT);
349 
350             confPath = getRealPath(confFile);
351 
352             // This should eventually be a Configuration
353             // interface so that service and app configuration
354             // can be stored anywhere.
355             configuration = (Configuration) new PropertiesConfiguration(confPath);
356             confStyle = "Properties";
357         }
358 
359 
360         //
361         // Set up logging as soon as possible
362         //
363         String log4jFile = configuration.getString(LOG4J_CONFIG_FILE,
364                                                    LOG4J_CONFIG_FILE_DEFAULT);
365 
366         log4jFile = getRealPath(log4jFile);
367 
368         //
369         // Load the config file above into a Properties object and
370         // fix up the Application root
371         //
372         Properties p = new Properties();
373         try
374         {
375             p.load(new FileInputStream(log4jFile));
376             p.setProperty(APPLICATION_ROOT_KEY, getApplicationRoot());
377             PropertyConfigurator.configure(p);
378 
379             //
380             // Rebuild our log object with a configured commons-logging
381             log = LogFactory.getLog(this.getClass());
382 
383             log.info("Configured log4j from " + log4jFile);
384         }
385         catch (FileNotFoundException fnf)
386         {
387             System.err.println("Could not open Log4J configuration file "
388                                + log4jFile + ": ");
389             fnf.printStackTrace();
390         }
391 
392         // Now report our successful configuration to the world
393         log.info("Loaded configuration  (" + confStyle + ") from " + confFile + " (" + confPath + ")");
394 
395         
396         setTurbineServletConfig(config);
397         setTurbineServletContext(context);
398 
399         getServiceManager().setApplicationRoot(applicationRoot);
400 
401         // We want to set a few values in the configuration so
402         // that ${variable} interpolation will work for
403         //
404         // ${applicationRoot}
405         // ${webappRoot}
406         configuration.setProperty(APPLICATION_ROOT_KEY, applicationRoot);
407         configuration.setProperty(WEBAPP_ROOT_KEY, webappRoot);
408 
409         
410 
411 		// Retrieve the pipeline class and then initialize it.  The pipeline
412         // handles the processing of a webrequest/response cycle.
413 	    Class pipelineClass =
414 		  Class.forName(
415 			  configuration.getString("pipeline.default", STANDARD_PIPELINE));
416   
417 		    log.debug("Using Pipeline: " + pipelineClass.getName());
418 	    // Turbine's standard Pipeline implementation uses
419 	    // descriptors to define what Valves are attached to it.
420 	    String descriptorPath =
421 		  	configuration.getString(
422 			  "pipeline.default.descriptor",
423 					  TurbinePipeline.CLASSIC_PIPELINE);
424   		  	descriptorPath = getRealPath(descriptorPath);
425   
426   		  	log.debug("Using descriptor path: " + descriptorPath);
427 	  	Mapper m = new Mapper();
428 	  	pipeline = (Pipeline) m.map(descriptorPath, pipelineClass.getName());
429 	  	log.debug("Initializing pipeline");
430 	  
431 	  	pipeline.initialize();
432 
433         
434         //
435         // Be sure, that our essential services get run early
436         //
437         configuration.setProperty(TurbineServices.SERVICE_PREFIX +
438                                   ComponentService.SERVICE_NAME + ".earlyInit",
439                                   Boolean.TRUE);
440 
441         configuration.setProperty(TurbineServices.SERVICE_PREFIX +
442                                   AvalonComponentService.SERVICE_NAME + ".earlyInit",
443                                   Boolean.TRUE);
444 
445         getServiceManager().setConfiguration(configuration);
446 
447         // Initialize the service manager. Services
448         // that have its 'earlyInit' property set to
449         // a value of 'true' will be started when
450         // the service manager is initialized.
451         getServiceManager().init();
452     }
453 
454     /***
455      * Create any directories that might be needed during
456      * runtime. Right now this includes:
457      *
458      * <ul>
459      *
460      * <li>The directory to write the log files to (relative to the
461      * web application root), or <code>null</code> for the default of
462      * <code>/logs</code>.  The directory is specified via the {@link
463      * TurbineConstants#LOGGING_ROOT} parameter.</li>
464      *
465      * </ul>
466      *
467      * @param context Global initialization parameters.
468      * @param config Initialization parameters specific to the Turbine
469      * servlet.
470      */
471     private static void createRuntimeDirectories(ServletContext context,
472                                                  ServletConfig config)
473     {
474         String path = findInitParameter(context, config,
475                                         LOGGING_ROOT_KEY,
476                                         LOGGING_ROOT_DEFAULT);
477 
478         File logDir = new File(getRealPath(path));
479         if (!logDir.exists())
480         {
481             // Create the logging directory
482             if (!logDir.mkdirs())
483             {
484                 System.err.println("Cannot create directory for logs!");
485             }
486         }
487     }
488 
489     /***
490      * Finds the specified servlet configuration/initialization
491      * parameter, looking first for a servlet-specific parameter, then
492      * for a global parameter, and using the provided default if not
493      * found.
494      */
495     protected static final String findInitParameter(ServletContext context,
496             ServletConfig config, String name, String defaultValue)
497     {
498         String path = null;
499 
500         // Try the name as provided first.
501         boolean usingNamespace = name.startsWith(CONFIG_NAMESPACE);
502         while (true)
503         {
504             path = config.getInitParameter(name);
505             if (StringUtils.isEmpty(path))
506             {
507                 path = context.getInitParameter(name);
508                 if (StringUtils.isEmpty(path))
509                 {
510                     // The named parameter didn't yield a value.
511                     if (usingNamespace)
512                     {
513                         path = defaultValue;
514                     }
515                     else
516                     {
517                         // Try again using Turbine's namespace.
518                         name = CONFIG_NAMESPACE + '.' + name;
519                         usingNamespace = true;
520                         continue;
521                     }
522                 }
523             }
524             break;
525         }
526 
527         return path;
528     }
529 
530     /***
531      * Return the current configuration with all keys included
532      *
533      * @return a Configuration Object
534      */
535     public static Configuration getConfiguration()
536     {
537         return configuration;
538     }
539 
540     /***
541      * Return the server name.
542      *
543      * @return String server name
544      */
545     public static String getServerName()
546     {
547         return getDefaultServerData().getServerName();
548     }
549 
550     /***
551      * Return the server scheme.
552      *
553      * @return String server scheme
554      */
555     public static String getServerScheme()
556     {
557         return getDefaultServerData().getServerScheme();
558     }
559 
560     /***
561      * Return the server port.
562      *
563      * @return String server port
564      */
565     public static String getServerPort()
566     {
567         return Integer.toString(getDefaultServerData().getServerPort());
568     }
569 
570     /***
571      * Get the script name. This is the initial script name.
572      * Actually this is probably not needed any more. I'll
573      * check. jvz.
574      *
575      * @return String initial script name.
576      */
577     public static String getScriptName()
578     {
579         return getDefaultServerData().getScriptName();
580     }
581 
582     /***
583      * Return the context path.
584      *
585      * @return String context path
586      */
587     public static String getContextPath()
588     {
589         return getDefaultServerData().getContextPath();
590     }
591 
592     /***
593      * Return all the Turbine Servlet information (Server Name, Port,
594      * Scheme in a ServerData structure. This is generated from the
595      * values set when initializing the Turbine and may not be correct
596      * if you're running in a clustered structure. This might be used
597      * if you need a DataURI and have no RunData object handy-
598      *
599      * @return An initialized ServerData object
600      */
601     public static ServerData getDefaultServerData()
602     {
603         if(serverData == null)
604         {
605             log.error("ServerData Information requested from Turbine before first request!");
606             // Will be overwritten once the first request is run;
607             serverData = new ServerData(null, URIConstants.HTTP_PORT,
608                     URIConstants.HTTP, null, null);
609         }
610         return serverData;
611     }
612 
613     /***
614      * Set the servlet config for this turbine webapp.
615      *
616      * @param config New servlet config
617      */
618     public static void setTurbineServletConfig(ServletConfig config)
619     {
620         servletConfig = config;
621     }
622 
623     /***
624      * Get the servlet config for this turbine webapp.
625      *
626      * @return ServletConfig
627      */
628     public static ServletConfig getTurbineServletConfig()
629     {
630         return servletConfig;
631     }
632 
633     /***
634      * Set the servlet context for this turbine webapp.
635      *
636      * @param context New servlet context.
637      */
638     public static void setTurbineServletContext(ServletContext context)
639     {
640         servletContext = context;
641     }
642 
643     /***
644      * Get the servlet context for this turbine webapp.
645      *
646      * @return ServletContext
647      */
648     public static ServletContext getTurbineServletContext()
649     {
650         return servletContext;
651     }
652 
653     /***
654      * The <code>Servlet</code> destroy method.  Invokes
655      * <code>ServiceBroker</code> tear down method.
656      */
657     public final void destroy()
658     {
659         // Shut down all Turbine Services.
660         getServiceManager().shutdownServices();
661         System.gc();
662 
663         firstInit = true;
664         firstDoGet = true;
665         log.info("Turbine: Done shutting down!");
666     }
667 
668     /***
669      * The primary method invoked when the Turbine servlet is executed.
670      *
671      * @param req Servlet request.
672      * @param res Servlet response.
673      * @exception IOException a servlet exception.
674      * @exception ServletException a servlet exception.
675      */
676     public final void doGet(HttpServletRequest req, HttpServletResponse res)
677             throws IOException, ServletException
678     {
679         // set to true if the request is to be redirected by the page
680         boolean requestRedirected = false;
681 
682         // Placeholder for the RunData object.
683         RunData data = null;
684         
685         PipelineData pipelineData = new DefaultPipelineData();
686         try
687         {
688             // Check to make sure that we started up properly.
689             if (initFailure != null)
690             {
691                 throw initFailure;
692             }
693             // If this is the first invocation, perform some
694             // initialization.  Certain services need RunData to initialize
695             // themselves.
696             if (firstDoGet)
697             {
698                 synchronized (Turbine.class)
699                 {
700                     // Store the context path for tools like ContentURI and
701                     // the UIManager that use webapp context path information
702                     // for constructing URLs.
703                     serverData = new ServerData(req);            
704 
705                     // Mark that we're done.
706                     firstDoGet = false;
707                     log.info("Turbine: first Request successful");
708                 }
709 
710             }            
711 
712             // Get general RunData here...
713             // Perform turbine specific initialization below.
714             data = rundataService.getRunData(req, res, getServletConfig());
715             Map runDataMap = new HashMap();
716             runDataMap.put(RunData.class, data);
717             // put the data into the pipeline
718             pipelineData.put(RunData.class, runDataMap);            
719 
720             // Stages of Pipeline implementation execution
721 			// configurable via attached Valve implementations in a
722 			// XML properties file.
723 			pipeline.invoke(pipelineData);
724   
725         }
726         catch (Exception e)
727         {
728             handleException(pipelineData, res, e);
729         }
730         catch (Throwable t)
731         {
732             handleException(pipelineData, res, t);
733         }
734         finally
735         {
736             // Return the used RunData to the factory for recycling.
737             rundataService.putRunData(data);
738         }
739     }
740 
741     /***
742      * In this application doGet and doPost are the same thing.
743      *
744      * @param req Servlet request.
745      * @param res Servlet response.
746      * @exception IOException a servlet exception.
747      * @exception ServletException a servlet exception.
748      */
749     public final void doPost(HttpServletRequest req, HttpServletResponse res)
750             throws IOException, ServletException
751     {
752         doGet(req, res);
753     }
754 
755     /***
756      * Return the servlet info.
757      *
758      * @return a string with the servlet information.
759      */
760     public final String getServletInfo()
761     {
762         return "Turbine Servlet";
763     }
764 
765     /***
766      * This method is about making sure that we catch and display
767      * errors to the screen in one fashion or another. What happens is
768      * that it will attempt to show the error using your user defined
769      * Error Screen. If that fails, then it will resort to just
770      * displaying the error and logging it all over the place
771      * including the servlet engine log file, the Turbine log file and
772      * on the screen.
773      *
774      * @param data A Turbine PipelineData object.
775      * @param res Servlet response.
776      * @param t The exception to report.
777      */
778     private final void handleException(PipelineData pipelineData, HttpServletResponse res,
779                                        Throwable t)
780     {
781         RunData data = (RunData)getRunData(pipelineData);
782         // make sure that the stack trace makes it the log
783         log.error("Turbine.handleException: ", t);
784 
785         String mimeType = "text/plain";
786         try
787         {
788             // This is where we capture all exceptions and show the
789             // Error Screen.
790             data.setStackTrace(ExceptionUtils.getStackTrace(t), t);
791 
792             // setup the screen
793             data.setScreen(configuration.getString(SCREEN_ERROR_KEY,
794                     SCREEN_ERROR_DEFAULT));
795 
796             // do more screen setup for template execution if needed
797             if (data.getTemplateInfo() != null)
798             {
799                 data.getTemplateInfo()
800                     .setScreenTemplate(configuration.getString(
801                             TEMPLATE_ERROR_KEY, TEMPLATE_ERROR_VM));
802             }
803 
804             // Make sure to not execute an action.
805             data.setAction("");
806 
807             PageLoader.getInstance().exec(pipelineData,
808                     configuration.getString(PAGE_DEFAULT_KEY,
809                             PAGE_DEFAULT_DEFAULT));
810 
811             data.getResponse().setContentType(data.getContentType());
812             data.getResponse().setStatus(data.getStatusCode());
813         }
814         // Catch this one because it occurs if some code hasn't been
815         // completely re-compiled after a change..
816         catch (java.lang.NoSuchFieldError e)
817         {
818             try
819             {
820                 data.getResponse().setContentType(mimeType);
821                 data.getResponse().setStatus(200);
822             }
823             catch (Exception ignored)
824             {
825             }
826 
827             try
828             {
829 				data.getResponse().getWriter().print("java.lang.NoSuchFieldError: "
830                         + "Please recompile all of your source code.");
831             }
832             catch (IOException ignored)
833             {
834             }
835 
836             log.error(data.getStackTrace(), e);
837         }
838         // Attempt to do *something* at this point...
839         catch (Throwable reallyScrewedNow)
840         {
841             StringBuffer msg = new StringBuffer();
842             msg.append("Horrible Exception: ");
843             if (data != null)
844             {
845                 msg.append(data.getStackTrace());
846             }
847             else
848             {
849                 msg.append(t);
850             }
851             try
852             {
853                 res.setContentType(mimeType);
854                 res.setStatus(200);
855                 res.getWriter().print(msg.toString());
856             }
857             catch (Exception ignored)
858             {
859             }
860 
861             log.error(reallyScrewedNow.getMessage(), reallyScrewedNow);
862         }
863     }
864 
865     /***
866      * Set the application root for the webapp.
867      *
868      * @param val New app root.
869      */
870     public static void setApplicationRoot(String val)
871     {
872         applicationRoot = val;
873     }
874 
875     /***
876      * Get the application root for this Turbine webapp. This
877      * concept was started in 3.0 and will allow an app to be
878      * developed from a standard CVS layout. With a simple
879      * switch the app will work fully within the servlet
880      * container for deployment.
881      *
882      * @return String applicationRoot
883      */
884     public static String getApplicationRoot()
885     {
886         return applicationRoot;
887     }
888 
889     /***
890      * Used to get the real path of configuration and resource
891      * information. This can be used by an app being
892      * developed in a standard CVS layout.
893      *
894      * @param path path translated to the application root
895      * @return the real path
896      */
897     public static String getRealPath(String path)
898     {
899         if (path.startsWith("/"))
900         {
901             path = path.substring(1);
902         }
903 
904         return new File(getApplicationRoot(), path).getAbsolutePath();
905     }
906 
907     /***
908      * Return an instance of the currently configured Service Manager
909      *
910      * @return A service Manager instance
911      */
912     private ServiceManager getServiceManager()
913     {
914         return TurbineServices.getInstance();
915     }
916     
917     /***
918      * Get a RunData from the pipelineData. Once RunData is replaced
919      * by PipelineData this should not be required. 
920      * @param pipelineData
921      * @return
922      */
923     private RunData getRunData(PipelineData pipelineData)
924     {
925         RunData data = null;
926         Map runDataMap = (Map) pipelineData.get(RunData.class);
927         data = (RunData)runDataMap.get(RunData.class);
928         return data;
929     }
930 }