Coverage Report - org.apache.turbine.Turbine
 
Classes in this File Line Coverage Branch Coverage Complexity
Turbine
78%
190/242
58%
40/68
2,767
 
 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  45
 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  33
     private static boolean firstInit = true;
 130  
 
 131  
     /**
 132  
      * The pipeline to use when processing requests.
 133  
      */
 134  33
     private static Pipeline pipeline = null;
 135  
 
 136  
     /** Whether init succeeded or not. */
 137  33
     private static Throwable initFailure = null;
 138  
 
 139  
     /**
 140  
      * Should initialization activities be performed during doGet() execution?
 141  
      */
 142  33
     private static boolean firstDoGet = true;
 143  
 
 144  
     /**
 145  
      * Keep all the properties of the web server in a convenient data
 146  
      * structure
 147  
      */
 148  33
     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  33
     private static Configuration configuration = null;
 168  
 
 169  
     /** Default Input encoding if the servlet container does not report an encoding */
 170  45
     private String inputEncoding = null;
 171  
 
 172  
     /** Logging class from commons.logging */
 173  33
     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  42
         synchronized (Turbine.class)
 187  
         {
 188  42
             super.init();
 189  42
             ServletConfig config = getServletConfig();
 190  
 
 191  42
             if (!firstInit)
 192  
             {
 193  1
                 log.info("Double initialization of Turbine was attempted!");
 194  1
                 return;
 195  
             }
 196  
             // executing init will trigger some static initializers, so we have
 197  
             // only one chance.
 198  41
             firstInit = false;
 199  
 
 200  
             try
 201  
             {
 202  41
                 ServletContext context = config.getServletContext();
 203  
 
 204  41
                 configure(config, context);
 205  
 
 206  41
                 TemplateService templateService = TurbineTemplate.getService();
 207  41
                 if (templateService == null)
 208  
                 {
 209  0
                     throw new TurbineException(
 210  
                             "No Template Service configured!");
 211  
                 }
 212  
 
 213  41
                 if (getRunDataService() == null)
 214  
                 {
 215  0
                     throw new TurbineException(
 216  
                             "No RunData Service configured!");
 217  
                 }
 218  
 
 219  
             }
 220  0
             catch (Exception e)
 221  
             {
 222  
                 // save the exception to complain loudly later :-)
 223  0
                 initFailure = e;
 224  0
                 log.fatal("Turbine: init() failed: ", e);
 225  0
                 throw new ServletException("Turbine: init() failed", e);
 226  41
             }
 227  
 
 228  41
             log.info("Turbine: init() Ready to Rumble!");
 229  41
         }
 230  41
     }
 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  41
         applicationRoot = findInitParameter(context, config,
 251  
                 TurbineConstants.APPLICATION_ROOT_KEY,
 252  
                 TurbineConstants.APPLICATION_ROOT_DEFAULT);
 253  
 
 254  41
         webappRoot = config.getServletContext().getRealPath("/");
 255  
         // log.info("Web Application root is " + webappRoot);
 256  
         // log.info("Application root is "     + applicationRoot);
 257  
 
 258  41
         if (applicationRoot == null || applicationRoot.equals(TurbineConstants.WEB_CONTEXT))
 259  
         {
 260  41
             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  41
         setApplicationRoot(applicationRoot);
 266  
 
 267  
         // Create any directories that need to be setup for
 268  
         // a running Turbine application.
 269  41
         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  41
         String confStyle = "unset";
 300  41
         String confPath= null;
 301  
         // first test
 302  41
         String confFile= findInitParameter(context, config,
 303  
                 TurbineConfig.CONFIGURATION_PATH_KEY,
 304  
                 null);
 305  41
         if (StringUtils.isNotEmpty(confFile))
 306  
         {
 307  2
             confStyle = "XML";
 308  
         } else // // second test
 309  
         {
 310  39
             confFile = findInitParameter(context, config,
 311  
                     TurbineConfig.PROPERTIES_PATH_KEY,
 312  
                                          null);
 313  39
             if (StringUtils.isNotEmpty((confFile)) )
 314  
             {
 315  39
                 confStyle = "Properties";
 316  
             }
 317  
         }
 318  
         // more tests ..
 319  
         // last test
 320  41
         if (confStyle.equals( "unset" ))
 321  
         {  // last resort
 322  0
              confFile = findInitParameter(context, config,
 323  
                     TurbineConfig.PROPERTIES_PATH_KEY,
 324  
                     TurbineConfig.PROPERTIES_PATH_DEFAULT);
 325  0
              confStyle = "Properties";
 326  
         }
 327  
         // now begin loading
 328  41
         if (!confStyle.equals( "unset" ))
 329  
         {
 330  41
              if (confStyle.equals( "XML" )) {
 331  2
                  if (confFile.startsWith( "/" ))
 332  
                  {
 333  0
                      confFile = confFile.substring( 1 ); // cft. RFC2396 should not start with a slash, if not absolute path
 334  
                  }
 335  2
                  DefaultConfigurationBuilder configurationBuilder = new DefaultConfigurationBuilder(confFile);
 336  2
                  confPath = new File(applicationRoot).toURI().toString();// relative base path used for this and child configuration files
 337  2
                  configurationBuilder.setBasePath(confPath);
 338  2
                  configuration = configurationBuilder.getConfiguration();
 339  2
              } else {
 340  39
                  confPath = getRealPath(confFile);
 341  
                  //configurationBuilder.setBasePath(getRealPath(getApplicationRoot()));
 342  39
                  configuration = new PropertiesConfiguration(confPath);
 343  
              }
 344  
         }
 345  
         //
 346  
         // Set up logging as soon as possible
 347  
         //
 348  41
         configureLogging();
 349  
 
 350  
         // Now report our successful configuration to the world
 351  41
         log.info("Loaded configuration (" + confStyle + ") from " + confFile + " (" + confPath + ") style: "+ configuration.toString());
 352  
 
 353  41
         setTurbineServletConfig(config);
 354  41
         setTurbineServletContext(context);
 355  
 
 356  41
         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  41
         configuration.setProperty(TurbineConstants.APPLICATION_ROOT_KEY, applicationRoot);
 364  41
         configuration.setProperty(TurbineConstants.WEBAPP_ROOT_KEY, webappRoot);
 365  
 
 366  
         // Get the default input encoding
 367  41
         inputEncoding = configuration.getString(
 368  
                 TurbineConstants.PARAMETER_ENCODING_KEY,
 369  
                 TurbineConstants.PARAMETER_ENCODING_DEFAULT);
 370  
 
 371  41
         if (log.isDebugEnabled())
 372  
         {
 373  41
             log.debug("Input Encoding has been set to " + inputEncoding);
 374  
         }
 375  
 
 376  41
         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  41
         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  41
             String descriptorPath =
 388  
                           configuration.getString(
 389  
                           "pipeline.default.descriptor",
 390  
                                           TurbinePipeline.CLASSIC_PIPELINE);
 391  
 
 392  41
         descriptorPath = getRealPath(descriptorPath);
 393  
 
 394  41
                   log.debug("Using descriptor path: " + descriptorPath);
 395  41
         Reader reader = new BufferedReader(new FileReader(descriptorPath));
 396  41
         XStream pipelineMapper = new XStream(new StaxDriver()); // does not require XPP3 library
 397  41
         pipeline = (Pipeline) pipelineMapper.fromXML(reader);
 398  
 
 399  41
                   log.debug("Initializing pipeline");
 400  
 
 401  41
                   pipeline.initialize();
 402  41
     }
 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  41
         String log4jFile = configuration.getString(TurbineConstants.LOG4J_CONFIG_FILE,
 412  
                 TurbineConstants.LOG4J_CONFIG_FILE_DEFAULT);
 413  
 
 414  41
         if (StringUtils.isNotEmpty(log4jFile) &&
 415  
                 !log4jFile.equalsIgnoreCase("none"))
 416  
         {
 417  41
             log4jFile = getRealPath(log4jFile);
 418  41
             boolean success = false;
 419  
 
 420  41
             if (log4jFile.endsWith(".xml"))
 421  
             {
 422  
                 // load XML type configuration
 423  
                 // NOTE: Only system property expansion available
 424  
                 try
 425  
                 {
 426  0
                     DOMConfigurator.configure(log4jFile);
 427  0
                     success = true;
 428  
                 }
 429  0
                 catch (FactoryConfigurationError e)
 430  
                 {
 431  0
                     System.err.println("Could not configure Log4J from configuration file "
 432  
                             + log4jFile + ": ");
 433  0
                     e.printStackTrace();
 434  0
                 }
 435  
             }
 436  
             else
 437  
             {
 438  
                 //
 439  
                 // Load the config file above into a Properties object and
 440  
                 // fix up the Application root
 441  
                 //
 442  41
                 Properties p = new Properties();
 443  41
                 FileInputStream fis = null;
 444  
 
 445  
                 try
 446  
                 {
 447  41
                     fis = new FileInputStream(log4jFile);
 448  41
                     p.load(fis);
 449  41
                     p.setProperty(TurbineConstants.APPLICATION_ROOT_KEY, getApplicationRoot());
 450  41
                     PropertyConfigurator.configure(p);
 451  41
                     success = true;
 452  
                 }
 453  0
                 catch (FileNotFoundException fnf)
 454  
                 {
 455  0
                     System.err.println("Could not open Log4J configuration file "
 456  
                             + log4jFile + ": ");
 457  0
                     fnf.printStackTrace();
 458  
                 }
 459  
                 finally
 460  
                 {
 461  41
                     if (fis != null)
 462  
                     {
 463  41
                         fis.close();
 464  
                     }
 465  
                 }
 466  
             }
 467  
 
 468  41
             if (success)
 469  
             {
 470  
                 // Rebuild our log object with a configured commons-logging
 471  41
                 log = LogFactory.getLog(this.getClass());
 472  41
                 log.info("Configured log4j from " + log4jFile);
 473  
             }
 474  
         }
 475  41
     }
 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  41
         String path = findInitParameter(context, config,
 497  
                                         TurbineConstants.LOGGING_ROOT_KEY,
 498  
                                         TurbineConstants.LOGGING_ROOT_DEFAULT);
 499  
 
 500  41
         File logDir = new File(getRealPath(path));
 501  41
         if (!logDir.exists())
 502  
         {
 503  
             // Create the logging directory
 504  0
             if (!logDir.mkdirs())
 505  
             {
 506  0
                 System.err.println("Cannot create directory for logs!");
 507  
             }
 508  
         }
 509  41
     }
 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  164
         String path = null;
 521  
 
 522  
         // Try the name as provided first.
 523  164
         boolean usingNamespace = name.startsWith(TurbineConstants.CONFIG_NAMESPACE);
 524  
         while (true)
 525  
         {
 526  283
             path = config.getInitParameter(name);
 527  283
             if (StringUtils.isEmpty(path))
 528  
             {
 529  238
                 path = context.getInitParameter(name);
 530  238
                 if (StringUtils.isEmpty(path))
 531  
                 {
 532  
                     // The named parameter didn't yield a value.
 533  238
                     if (usingNamespace)
 534  
                     {
 535  119
                         path = defaultValue;
 536  
                     }
 537  
                     else
 538  
                     {
 539  
                         // Try again using Turbine's namespace.
 540  119
                         name = TurbineConstants.CONFIG_NAMESPACE + '.' + name;
 541  119
                         usingNamespace = true;
 542  119
                         continue;
 543  
                     }
 544  
                 }
 545  
             }
 546  
             break;
 547  
         }
 548  
 
 549  164
         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  1
         synchronized (Turbine.class)
 561  
         {
 562  1
             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  1
                 saveServletInfo(data);
 570  
 
 571  
                 // Initialize services with the PipelineData instance
 572  1
                 TurbineServices services = (TurbineServices)TurbineServices.getInstance();
 573  
 
 574  1
                 for (Iterator<String> i = services.getServiceNames(); i.hasNext();)
 575  
                 {
 576  10
                         String serviceName = i.next();
 577  10
                         Object service = services.getService(serviceName);
 578  
 
 579  10
                         if (service instanceof Initable)
 580  
                         {
 581  
                                 try
 582  
                                 {
 583  10
                                                         ((Initable)service).init(data);
 584  
                                                 }
 585  0
                                 catch (InitializationException e)
 586  
                                 {
 587  0
                                         log.warn("Could not initialize Initable " + serviceName + " with PipelineData", e);
 588  10
                                                 }
 589  
                         }
 590  10
                 }
 591  
 
 592  
                 // Mark that we're done.
 593  1
                 firstDoGet = false;
 594  1
                 log.info("Turbine: first Request successful");
 595  
             }
 596  1
         }
 597  1
     }
 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  728
         return configuration;
 607  
     }
 608  
 
 609  
     /**
 610  
      * Return the server name.
 611  
      *
 612  
      * @return String server name
 613  
      */
 614  
     public static String getServerName()
 615  
     {
 616  1
         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  0
         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  2
         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  1
         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  0
         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  6
         if (serverData == null)
 675  
         {
 676  1
             String serverName
 677  
                     = configuration.getString(TurbineConstants.DEFAULT_SERVER_NAME_KEY);
 678  1
             if (serverName == null)
 679  
             {
 680  0
                 log.error("ServerData Information requested from Turbine before first request!");
 681  
             }
 682  
             else
 683  
             {
 684  1
                 log.info("ServerData Information retrieved from configuration.");
 685  
             }
 686  
             // Will be overwritten once the first request is run;
 687  1
             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  6
         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  41
         servletConfig = config;
 706  41
     }
 707  
 
 708  
     /**
 709  
      * Get the servlet config for this turbine webapp.
 710  
      *
 711  
      * @return ServletConfig
 712  
      */
 713  
     public static ServletConfig getTurbineServletConfig()
 714  
     {
 715  1
         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  41
         servletContext = context;
 726  41
     }
 727  
 
 728  
     /**
 729  
      * Get the servlet context for this turbine webapp.
 730  
      *
 731  
      * @return ServletContext
 732  
      */
 733  
     public static ServletContext getTurbineServletContext()
 734  
     {
 735  0
         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  45
         getServiceManager().shutdownServices();
 747  
 
 748  45
         firstInit = true;
 749  45
         firstDoGet = true;
 750  45
         log.info("Turbine: Done shutting down!");
 751  45
     }
 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  1
         PipelineData pipelineData = null;
 766  
 
 767  
         try
 768  
         {
 769  
             // Check to make sure that we started up properly.
 770  1
             if (initFailure != null)
 771  
             {
 772  0
                 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  1
             if (req.getCharacterEncoding() == null)
 779  
             {
 780  0
                 if (log.isDebugEnabled())
 781  
                 {
 782  0
                     log.debug("Changing Input Encoding to " + inputEncoding);
 783  
                 }
 784  
 
 785  
                 try
 786  
                 {
 787  0
                     req.setCharacterEncoding(inputEncoding);
 788  
                 }
 789  0
                 catch (UnsupportedEncodingException uee)
 790  
                 {
 791  0
                     log.warn("Could not change request encoding to " + inputEncoding, uee);
 792  0
                 }
 793  
             }
 794  
 
 795  
             // Get general RunData here...
 796  
             // Perform turbine specific initialization below.
 797  1
             pipelineData = getRunDataService().getRunData(req, res, getServletConfig());
 798  1
             Map<Class<?>, Object> runDataMap = new HashMap<Class<?>, Object>();
 799  1
             runDataMap.put(RunData.class, pipelineData);
 800  
             // put the data into the pipeline
 801  1
             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  1
             if (firstDoGet)
 807  
             {
 808  1
                 init(pipelineData);
 809  
             }
 810  
 
 811  
             // Stages of Pipeline implementation execution
 812  
                         // configurable via attached Valve implementations in a
 813  
                         // XML properties file.
 814  1
                         pipeline.invoke(pipelineData);
 815  
 
 816  
         }
 817  1
         catch (Exception e)
 818  
         {
 819  1
             handleException(pipelineData, res, e);
 820  
         }
 821  0
         catch (Throwable t)
 822  
         {
 823  0
             handleException(pipelineData, res, t);
 824  
         }
 825  
         finally
 826  
         {
 827  
             // Return the used RunData to the factory for recycling.
 828  1
             getRunDataService().putRunData((RunData)pipelineData);
 829  1
         }
 830  1
     }
 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  0
         doGet(req, res);
 845  0
     }
 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  0
         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  1
         RunData data = getRunData(pipelineData);
 875  
         // make sure that the stack trace makes it the log
 876  1
         log.error("Turbine.handleException: ", t);
 877  
 
 878  1
         String mimeType = "text/plain";
 879  
         try
 880  
         {
 881  
             // This is where we capture all exceptions and show the
 882  
             // Error Screen.
 883  1
             data.setStackTrace(ExceptionUtils.getStackTrace(t), t);
 884  
 
 885  
             // setup the screen
 886  1
             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  1
             if (data.getTemplateInfo() != null)
 892  
             {
 893  1
                 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  1
             data.setAction("");
 901  
 
 902  1
             PageLoader.getInstance().exec(pipelineData,
 903  
                     configuration.getString(TurbineConstants.PAGE_DEFAULT_KEY,
 904  
                             TurbineConstants.PAGE_DEFAULT_DEFAULT));
 905  
 
 906  0
             data.getResponse().setContentType(data.getContentType());
 907  0
             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  0
         catch (java.lang.NoSuchFieldError e)
 912  
         {
 913  
             try
 914  
             {
 915  0
                 data.getResponse().setContentType(mimeType);
 916  0
                 data.getResponse().setStatus(200);
 917  
             }
 918  0
             catch (Exception ignored)
 919  
             {
 920  
                 // ignore
 921  0
             }
 922  
 
 923  
             try
 924  
             {
 925  0
                                 data.getResponse().getWriter().print("java.lang.NoSuchFieldError: "
 926  
                         + "Please recompile all of your source code.");
 927  
             }
 928  0
             catch (IOException ignored)
 929  
             {
 930  
                 // ignore
 931  0
             }
 932  
 
 933  0
             log.error(data.getStackTrace(), e);
 934  
         }
 935  
         // Attempt to do *something* at this point...
 936  1
         catch (Throwable reallyScrewedNow)
 937  
         {
 938  1
             StringBuilder msg = new StringBuilder();
 939  1
             msg.append("Horrible Exception: ");
 940  1
             if (data != null)
 941  
             {
 942  1
                 msg.append(data.getStackTrace());
 943  
             }
 944  
             else
 945  
             {
 946  0
                 msg.append(t);
 947  
             }
 948  
             try
 949  
             {
 950  1
                 res.setContentType(mimeType);
 951  1
                 res.setStatus(200);
 952  1
                 res.getWriter().print(msg.toString());
 953  
             }
 954  0
             catch (Exception ignored)
 955  
             {
 956  
                 // ignore
 957  1
             }
 958  
 
 959  1
             log.error(reallyScrewedNow.getMessage(), reallyScrewedNow);
 960  0
         }
 961  1
     }
 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  1
         ServerData requestServerData = data.get(Turbine.class, ServerData.class);
 980  1
         serverData = (ServerData) requestServerData.clone();
 981  1
     }
 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  41
         applicationRoot = val;
 991  41
     }
 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  336
         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  294
         if (path.startsWith("/"))
 1018  
         {
 1019  172
             return new File(getApplicationRoot(), path.substring(1)).getAbsolutePath();
 1020  
         }
 1021  
 
 1022  122
         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  168
         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  1
         RunData data = null;
 1044  1
         data = (RunData)pipelineData;
 1045  1
         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  4
         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  43
         return (RunDataService) TurbineServices
 1066  
             .getInstance().getService(RunDataService.SERVICE_NAME);
 1067  
     }
 1068  
 }