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