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 }