View Javadoc

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