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