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  185
     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  43
         configuration.setProperty(TurbineConstants.LOCALE_DEFAULT_CHARSET_KEY, inputEncoding);
 379  
 
 380  43
         if (log.isDebugEnabled())
 381  
         {
 382  43
             log.debug("Input Encoding has been set to " + inputEncoding);
 383  
         }
 384  
         
 385  43
         getServiceManager().setConfiguration(configuration);
 386  
 
 387  
         // Initialize the service manager. Services
 388  
         // that have its 'earlyInit' property set to
 389  
         // a value of 'true' will be started when
 390  
         // the service manager is initialized.
 391  43
         getServiceManager().init();
 392  
 
 393  
         // Retrieve the pipeline class and then initialize it.  The pipeline
 394  
         // handles the processing of a webrequest/response cycle.
 395  43
             String descriptorPath =
 396  
                           configuration.getString(
 397  
                           "pipeline.default.descriptor",
 398  
                                           TurbinePipeline.CLASSIC_PIPELINE);
 399  
 
 400  43
         if (log.isDebugEnabled())
 401  
         {
 402  43
             log.debug("Using descriptor path: " + descriptorPath);
 403  
         }
 404  
 
 405  
         // context resource path has to begin with slash, cft. context,getResource
 406  43
         if (!descriptorPath.startsWith( "/" )) {
 407  20
                 descriptorPath  = "/" + descriptorPath;
 408  
         }
 409  
 
 410  43
         InputStream reader = context.getResourceAsStream(descriptorPath);
 411  43
         JAXBContext jaxb = JAXBContext.newInstance(TurbinePipeline.class);
 412  43
         Unmarshaller unmarshaller = jaxb.createUnmarshaller();
 413  43
         pipeline = (Pipeline) unmarshaller.unmarshal(reader);
 414  43
         IOUtils.closeQuietly(reader);
 415  
 
 416  43
                   log.debug("Initializing pipeline");
 417  
 
 418  43
                   pipeline.initialize();
 419  39
     }
 420  
 
 421  
     /**
 422  
      * Configure the logging facilities of Turbine
 423  
      *
 424  
      * @throws IOException if the configuration file handling fails.
 425  
      */
 426  
     protected void configureLogging() throws IOException
 427  
     {
 428  43
         String log4jFile = configuration.getString(TurbineConstants.LOG4J_CONFIG_FILE,
 429  
                 TurbineConstants.LOG4J_CONFIG_FILE_DEFAULT);
 430  
 
 431  43
         if (StringUtils.isNotEmpty(log4jFile) &&
 432  
                 !log4jFile.equalsIgnoreCase("none"))
 433  
         {
 434  43
             log4jFile = getRealPath(log4jFile);
 435  43
             boolean success = false;
 436  
 
 437  43
             if (log4jFile.endsWith(".xml"))
 438  
             {
 439  
                 // load XML type configuration
 440  
                 // NOTE: Only system property expansion available
 441  
                 try
 442  
                 {
 443  0
                     DOMConfigurator.configure(log4jFile);
 444  0
                     success = true;
 445  
                 }
 446  0
                 catch (FactoryConfigurationError e)
 447  
                 {
 448  0
                     System.err.println("Could not configure Log4J from configuration file "
 449  
                             + log4jFile + ": ");
 450  0
                     e.printStackTrace();
 451  0
                 }
 452  
             }
 453  
             else
 454  
             {
 455  
                 //
 456  
                 // Load the config file above into a Properties object and
 457  
                 // fix up the Application root
 458  
                 //
 459  43
                 Properties p = new Properties();
 460  43
                 FileInputStream fis = null;
 461  
 
 462  
                 try
 463  
                 {
 464  43
                     fis = new FileInputStream(log4jFile);
 465  43
                     p.load(fis);
 466  43
                     p.setProperty(TurbineConstants.APPLICATION_ROOT_KEY, getApplicationRoot());
 467  43
                     PropertyConfigurator.configure(p);
 468  43
                     success = true;
 469  
                 }
 470  0
                 catch (FileNotFoundException fnf)
 471  
                 {
 472  0
                     System.err.println("Could not open Log4J configuration file "
 473  
                             + log4jFile + ": ");
 474  0
                     fnf.printStackTrace();
 475  
                 }
 476  
                 finally
 477  
                 {
 478  43
                     if (fis != null)
 479  
                     {
 480  43
                         fis.close();
 481  
                     }
 482  
                 }
 483  
             }
 484  
 
 485  43
             if (success)
 486  
             {
 487  
                 // Rebuild our log object with a configured commons-logging
 488  43
                 log = LogFactory.getLog(this.getClass());
 489  43
                 log.info("Configured log4j from " + log4jFile);
 490  
             }
 491  
         }
 492  43
     }
 493  
     /**
 494  
      * Create any directories that might be needed during
 495  
      * runtime. Right now this includes:
 496  
      *
 497  
      * <ul>
 498  
      *
 499  
      * <li>The directory to write the log files to (relative to the
 500  
      * web application root), or <code>null</code> for the default of
 501  
      * <code>/logs</code>.  The directory is specified via the {@link
 502  
      * TurbineConstants#LOGGING_ROOT_KEY} parameter.</li>
 503  
      *
 504  
      * </ul>
 505  
      *
 506  
      * @param context Global initialization parameters.
 507  
      * @param config Initialization parameters specific to the Turbine
 508  
      * servlet.
 509  
      */
 510  
     protected void createRuntimeDirectories(ServletContext context,
 511  
                                                  ServletConfig config)
 512  
     {
 513  43
         String path = findInitParameter(context, config,
 514  
                                         TurbineConstants.LOGGING_ROOT_KEY,
 515  
                                         TurbineConstants.LOGGING_ROOT_DEFAULT);
 516  
 
 517  43
         File logDir = new File(getRealPath(path));
 518  43
         if (!logDir.exists())
 519  
         {
 520  
             // Create the logging directory
 521  0
             if (!logDir.mkdirs())
 522  
             {
 523  0
                 System.err.println("Cannot create directory for logs!");
 524  
             }
 525  
         }
 526  43
     }
 527  
 
 528  
     /**
 529  
      * Finds the specified servlet configuration/initialization
 530  
      * parameter, looking first for a servlet-specific parameter, then
 531  
      * for a global parameter, and using the provided default if not
 532  
      * found.
 533  
      */
 534  
     protected String findInitParameter(ServletContext context,
 535  
             ServletConfig config, String name, String defaultValue)
 536  
     {
 537  172
         String path = null;
 538  
 
 539  
         // Try the name as provided first.
 540  172
         boolean usingNamespace = name.startsWith(TurbineConstants.CONFIG_NAMESPACE);
 541  
         while (true)
 542  
         {
 543  297
             path = config.getInitParameter(name);
 544  297
             if (StringUtils.isEmpty(path))
 545  
             {
 546  250
                 path = context.getInitParameter(name);
 547  250
                 if (StringUtils.isEmpty(path))
 548  
                 {
 549  
                     // The named parameter didn't yield a value.
 550  250
                     if (usingNamespace)
 551  
                     {
 552  125
                         path = defaultValue;
 553  
                     }
 554  
                     else
 555  
                     {
 556  
                         // Try again using Turbine's namespace.
 557  125
                         name = TurbineConstants.CONFIG_NAMESPACE + '.' + name;
 558  125
                         usingNamespace = true;
 559  125
                         continue;
 560  
                     }
 561  
                 }
 562  
             }
 563  
             break;
 564  
         }
 565  
 
 566  172
         return path;
 567  
     }
 568  
 
 569  
     /**
 570  
      * Initializes the services which need <code>PipelineData</code> to
 571  
      * initialize themselves (post startup).
 572  
      *
 573  
      * @param data The first <code>GET</code> request.
 574  
      */
 575  
     public void init(PipelineData data)
 576  
     {
 577  1
         synchronized (Turbine.class)
 578  
         {
 579  1
             if (firstDoGet)
 580  
             {
 581  
                 // All we want to do here is save some servlet
 582  
                 // information so that services and processes
 583  
                 // that don't have direct access to a RunData
 584  
                 // object can still know something about
 585  
                 // the servlet environment.
 586  1
                 saveServletInfo(data);
 587  
 
 588  
                 // Initialize services with the PipelineData instance
 589  1
                 TurbineServices services = (TurbineServices)getServiceManager();
 590  
 
 591  1
                 for (Iterator<String> i = services.getServiceNames(); i.hasNext();)
 592  
                 {
 593  10
                         String serviceName = i.next();
 594  10
                         Object service = services.getService(serviceName);
 595  
 
 596  10
                         if (service instanceof Initable)
 597  
                         {
 598  
                                 try
 599  
                                 {
 600  10
                                                         ((Initable)service).init(data);
 601  
                                                 }
 602  0
                                 catch (InitializationException e)
 603  
                                 {
 604  0
                                         log.warn("Could not initialize Initable " + serviceName + " with PipelineData", e);
 605  10
                                                 }
 606  
                         }
 607  10
                 }
 608  
 
 609  
                 // Mark that we're done.
 610  1
                 firstDoGet = false;
 611  1
                 log.info("Turbine: first Request successful");
 612  
             }
 613  1
         }
 614  1
     }
 615  
 
 616  
     /**
 617  
      * Return the current configuration with all keys included
 618  
      *
 619  
      * @return a Configuration Object
 620  
      */
 621  
     public static Configuration getConfiguration()
 622  
     {
 623  769
         return configuration;
 624  
     }
 625  
 
 626  
     /**
 627  
      * Return the server name.
 628  
      *
 629  
      * @return String server name
 630  
      */
 631  
     public static String getServerName()
 632  
     {
 633  1
         return getDefaultServerData().getServerName();
 634  
     }
 635  
 
 636  
     /**
 637  
      * Return the server scheme.
 638  
      *
 639  
      * @return String server scheme
 640  
      */
 641  
     public static String getServerScheme()
 642  
     {
 643  0
         return getDefaultServerData().getServerScheme();
 644  
     }
 645  
 
 646  
     /**
 647  
      * Return the server port.
 648  
      *
 649  
      * @return String server port
 650  
      */
 651  
     public static String getServerPort()
 652  
     {
 653  2
         return Integer.toString(getDefaultServerData().getServerPort());
 654  
     }
 655  
 
 656  
     /**
 657  
      * Get the script name. This is the initial script name.
 658  
      * Actually this is probably not needed any more. I'll
 659  
      * check. jvz.
 660  
      *
 661  
      * @return String initial script name.
 662  
      */
 663  
     public static String getScriptName()
 664  
     {
 665  1
         return getDefaultServerData().getScriptName();
 666  
     }
 667  
 
 668  
     /**
 669  
      * Return the context path.
 670  
      *
 671  
      * @return String context path
 672  
      */
 673  
     public static String getContextPath()
 674  
     {
 675  0
         return getDefaultServerData().getContextPath();
 676  
     }
 677  
 
 678  
     /**
 679  
      * Return all the Turbine Servlet information (Server Name, Port,
 680  
      * Scheme in a ServerData structure. This is generated from the
 681  
      * values set when initializing the Turbine and may not be correct
 682  
      * if you're running in a clustered structure. You can provide default
 683  
      * values in your configuration for cases where access is requied before
 684  
      * your application is first accessed by a user.  This might be used
 685  
      * if you need a DataURI and have no RunData object handy.
 686  
      *
 687  
      * @return An initialized ServerData object
 688  
      */
 689  
     public static ServerData getDefaultServerData()
 690  
     {
 691  6
         if (serverData == null)
 692  
         {
 693  1
             String serverName
 694  
                     = configuration.getString(TurbineConstants.DEFAULT_SERVER_NAME_KEY);
 695  1
             if (serverName == null)
 696  
             {
 697  0
                 log.error("ServerData Information requested from Turbine before first request!");
 698  
             }
 699  
             else
 700  
             {
 701  1
                 log.info("ServerData Information retrieved from configuration.");
 702  
             }
 703  
             // Will be overwritten once the first request is run;
 704  1
             serverData = new ServerData(serverName,
 705  
                     configuration.getInt(TurbineConstants.DEFAULT_SERVER_PORT_KEY,
 706  
                             URIConstants.HTTP_PORT),
 707  
                     configuration.getString(TurbineConstants.DEFAULT_SERVER_SCHEME_KEY,
 708  
                             URIConstants.HTTP),
 709  
                     configuration.getString(TurbineConstants.DEFAULT_SCRIPT_NAME_KEY),
 710  
                     configuration.getString(TurbineConstants.DEFAULT_CONTEXT_PATH_KEY));
 711  
         }
 712  6
         return serverData;
 713  
     }
 714  
 
 715  
     /**
 716  
      * Set the servlet config for this turbine webapp.
 717  
      *
 718  
      * @param config New servlet config
 719  
      */
 720  
     public static void setTurbineServletConfig(ServletConfig config)
 721  
     {
 722  43
         servletConfig = config;
 723  43
     }
 724  
 
 725  
     /**
 726  
      * Get the servlet config for this turbine webapp.
 727  
      *
 728  
      * @return ServletConfig
 729  
      */
 730  
     public static ServletConfig getTurbineServletConfig()
 731  
     {
 732  39
         return servletConfig;
 733  
     }
 734  
 
 735  
     /**
 736  
      * Set the servlet context for this turbine webapp.
 737  
      *
 738  
      * @param context New servlet context.
 739  
      */
 740  
     public static void setTurbineServletContext(ServletContext context)
 741  
     {
 742  43
         servletContext = context;
 743  43
     }
 744  
 
 745  
     /**
 746  
      * Get the servlet context for this turbine webapp.
 747  
      *
 748  
      * @return ServletContext
 749  
      */
 750  
     public static ServletContext getTurbineServletContext()
 751  
     {
 752  0
         return servletContext;
 753  
     }
 754  
 
 755  
     /**
 756  
      * The <code>Servlet</code> destroy method.  Invokes
 757  
      * <code>ServiceBroker</code> tear down method.
 758  
      */
 759  
     @Override
 760  
     public void destroy()
 761  
     {
 762  
         // Shut down all Turbine Services.
 763  46
         getServiceManager().shutdownServices();
 764  
 
 765  46
         firstInit = true;
 766  46
         firstDoGet = true;
 767  46
         log.info("Turbine: Done shutting down!");
 768  46
     }
 769  
 
 770  
     /**
 771  
      * The primary method invoked when the Turbine servlet is executed.
 772  
      *
 773  
      * @param req Servlet request.
 774  
      * @param res Servlet response.
 775  
      * @throws IOException a servlet exception.
 776  
      * @throws ServletException a servlet exception.
 777  
      */
 778  
     @Override
 779  
     public void doGet(HttpServletRequest req, HttpServletResponse res)
 780  
             throws IOException, ServletException
 781  
     {
 782  1
         PipelineData pipelineData = null;
 783  
 
 784  
         try
 785  
         {
 786  
             // Check to make sure that we started up properly.
 787  1
             if (initFailure != null)
 788  
             {
 789  0
                 throw initFailure;
 790  
             }
 791  
 
 792  
             //
 793  
             // If the servlet container gives us no clear indication about the
 794  
             // Encoding of the contents, set it to our default value.
 795  1
             if (req.getCharacterEncoding() == null)
 796  
             {
 797  0
                 if (log.isDebugEnabled())
 798  
                 {
 799  0
                     log.debug("Changing Input Encoding to " + inputEncoding);
 800  
                 }
 801  
 
 802  
                 try
 803  
                 {
 804  0
                     req.setCharacterEncoding(inputEncoding);
 805  
                 }
 806  0
                 catch (UnsupportedEncodingException uee)
 807  
                 {
 808  0
                     log.warn("Could not change request encoding to " + inputEncoding, uee);
 809  0
                 }
 810  
             }
 811  
 
 812  
             // Get general RunData here...
 813  
             // Perform turbine specific initialization below.
 814  1
             pipelineData = getRunDataService().getRunData(req, res, getServletConfig());
 815  1
             Map<Class<?>, Object> runDataMap = new HashMap<Class<?>, Object>();
 816  1
             runDataMap.put(RunData.class, pipelineData);
 817  
             // put the data into the pipeline
 818  1
             pipelineData.put(RunData.class, runDataMap);
 819  
 
 820  
             // If this is the first invocation, perform some
 821  
             // initialization.  Certain services need RunData to initialize
 822  
             // themselves.
 823  1
             if (firstDoGet)
 824  
             {
 825  1
                 init(pipelineData);
 826  
             }
 827  
 
 828  
             // Stages of Pipeline implementation execution
 829  
                         // configurable via attached Valve implementations in a
 830  
                         // XML properties file.
 831  1
                         pipeline.invoke(pipelineData);
 832  
 
 833  
         }
 834  1
         catch (Exception e)
 835  
         {
 836  1
             handleException(pipelineData, res, e);
 837  
         }
 838  0
         catch (Throwable t)
 839  
         {
 840  0
             handleException(pipelineData, res, t);
 841  
         }
 842  
         finally
 843  
         {
 844  
             // Return the used RunData to the factory for recycling.
 845  1
             getRunDataService().putRunData((RunData)pipelineData);
 846  1
         }
 847  1
     }
 848  
 
 849  
     /**
 850  
      * In this application doGet and doPost are the same thing.
 851  
      *
 852  
      * @param req Servlet request.
 853  
      * @param res Servlet response.
 854  
      * @throws IOException a servlet exception.
 855  
      * @throws ServletException a servlet exception.
 856  
      */
 857  
     @Override
 858  
     public void doPost(HttpServletRequest req, HttpServletResponse res)
 859  
             throws IOException, ServletException
 860  
     {
 861  0
         doGet(req, res);
 862  0
     }
 863  
 
 864  
     /**
 865  
      * Return the servlet info.
 866  
      *
 867  
      * @return a string with the servlet information.
 868  
      */
 869  
     @Override
 870  
     public String getServletInfo()
 871  
     {
 872  0
         return "Turbine Servlet";
 873  
     }
 874  
 
 875  
     /**
 876  
      * This method is about making sure that we catch and display
 877  
      * errors to the screen in one fashion or another. What happens is
 878  
      * that it will attempt to show the error using your user defined
 879  
      * Error Screen. If that fails, then it will resort to just
 880  
      * displaying the error and logging it all over the place
 881  
      * including the servlet engine log file, the Turbine log file and
 882  
      * on the screen.
 883  
      *
 884  
      * @param pipelineData A Turbine PipelineData object.
 885  
      * @param res Servlet response.
 886  
      * @param t The exception to report.
 887  
      */
 888  
     protected void handleException(PipelineData pipelineData, HttpServletResponse res,
 889  
                                        Throwable t)
 890  
     {
 891  1
         RunData data = (RunData) pipelineData;
 892  
         // make sure that the stack trace makes it the log
 893  1
         log.error("Turbine.handleException: ", t);
 894  
 
 895  1
         String mimeType = "text/plain";
 896  
         try
 897  
         {
 898  
             // This is where we capture all exceptions and show the
 899  
             // Error Screen.
 900  1
             data.setStackTrace(ExceptionUtils.getStackTrace(t), t);
 901  
 
 902  
             // setup the screen
 903  1
             data.setScreen(configuration.getString(
 904  
                     TurbineConstants.SCREEN_ERROR_KEY,
 905  
                     TurbineConstants.SCREEN_ERROR_DEFAULT));
 906  
 
 907  
             // do more screen setup for template execution if needed
 908  1
             if (data.getTemplateInfo() != null)
 909  
             {
 910  1
                 data.getTemplateInfo()
 911  
                     .setScreenTemplate(configuration.getString(
 912  
                             TurbineConstants.TEMPLATE_ERROR_KEY,
 913  
                             TurbineConstants.TEMPLATE_ERROR_VM));
 914  
             }
 915  
 
 916  
             // Make sure to not execute an action.
 917  1
             data.setAction("");
 918  
 
 919  1
             PageLoader.getInstance().exec(pipelineData,
 920  
                     configuration.getString(TurbineConstants.PAGE_DEFAULT_KEY,
 921  
                             TurbineConstants.PAGE_DEFAULT_DEFAULT));
 922  
 
 923  0
             data.getResponse().setContentType(data.getContentType());
 924  0
             data.getResponse().setStatus(data.getStatusCode());
 925  
         }
 926  
         // Catch this one because it occurs if some code hasn't been
 927  
         // completely re-compiled after a change..
 928  0
         catch (java.lang.NoSuchFieldError e)
 929  
         {
 930  
             try
 931  
             {
 932  0
                 data.getResponse().setContentType(mimeType);
 933  0
                 data.getResponse().setStatus(200);
 934  
             }
 935  0
             catch (Exception ignored)
 936  
             {
 937  
                 // ignore
 938  0
             }
 939  
 
 940  
             try
 941  
             {
 942  0
                                 data.getResponse().getWriter().print("java.lang.NoSuchFieldError: "
 943  
                         + "Please recompile all of your source code.");
 944  
             }
 945  0
             catch (IOException ignored)
 946  
             {
 947  
                 // ignore
 948  0
             }
 949  
 
 950  0
             log.error(data.getStackTrace(), e);
 951  
         }
 952  
         // Attempt to do *something* at this point...
 953  1
         catch (Throwable reallyScrewedNow)
 954  
         {
 955  1
             StringBuilder msg = new StringBuilder();
 956  1
             msg.append("Horrible Exception: ");
 957  1
             if (data != null)
 958  
             {
 959  1
                 msg.append(data.getStackTrace());
 960  
             }
 961  
             else
 962  
             {
 963  0
                 msg.append(t);
 964  
             }
 965  
             try
 966  
             {
 967  1
                 res.setContentType(mimeType);
 968  1
                 res.setStatus(200);
 969  1
                 res.getWriter().print(msg.toString());
 970  
             }
 971  1
             catch (Exception ignored)
 972  
             {
 973  
                 // ignore
 974  0
             }
 975  
 
 976  1
             log.error(reallyScrewedNow.getMessage(), reallyScrewedNow);
 977  0
         }
 978  1
     }
 979  
 
 980  
     /**
 981  
      * Save some information about this servlet so that
 982  
      * it can be utilized by object instances that do not
 983  
      * have direct access to PipelineData.
 984  
      *
 985  
      * @param data Turbine request data
 986  
      */
 987  
     public static synchronized void saveServletInfo(PipelineData data)
 988  
     {
 989  
         // Store the context path for tools like ContentURI and
 990  
         // the UIManager that use webapp context path information
 991  
         // for constructing URLs.
 992  
 
 993  
         //
 994  
         // Bundle all the information above up into a convenient structure
 995  
         //
 996  1
         ServerData requestServerData = data.get(Turbine.class, ServerData.class);
 997  1
         serverData = (ServerData) requestServerData.clone();
 998  1
     }
 999  
 
 1000  
     /**
 1001  
      * Set the application root for the webapp.
 1002  
      *
 1003  
      * @param val New app root.
 1004  
      */
 1005  
     public static void setApplicationRoot(String val)
 1006  
     {
 1007  43
         applicationRoot = val;
 1008  43
     }
 1009  
 
 1010  
     /**
 1011  
      * Get the application root for this Turbine webapp. This
 1012  
      * concept was started in 3.0 and will allow an app to be
 1013  
      * developed from a standard CVS layout. With a simple
 1014  
      * switch the app will work fully within the servlet
 1015  
      * container for deployment.
 1016  
      *
 1017  
      * @return String applicationRoot
 1018  
      */
 1019  
     public static String getApplicationRoot()
 1020  
     {
 1021  309
         return applicationRoot;
 1022  
     }
 1023  
 
 1024  
     /**
 1025  
      * Used to get the real path of configuration and resource
 1026  
      * information. This can be used by an app being
 1027  
      * developed in a standard CVS layout.
 1028  
      *
 1029  
      * @param path path translated to the application root
 1030  
      * @return the real path
 1031  
      */
 1032  
     public static String getRealPath(String path)
 1033  
     {
 1034  263
         if (path.startsWith("/"))
 1035  
         {
 1036  180
             return new File(getApplicationRoot(), path.substring(1)).getAbsolutePath();
 1037  
         }
 1038  
 
 1039  83
         return new File(getApplicationRoot(), path).getAbsolutePath();
 1040  
     }
 1041  
 
 1042  
     /**
 1043  
      * Return an instance of the currently configured Service Manager
 1044  
      *
 1045  
      * @return A service Manager instance
 1046  
      */
 1047  
     private ServiceManager getServiceManager()
 1048  
     {
 1049  256
         return TurbineServices.getInstance();
 1050  
     }
 1051  
 
 1052  
     /**
 1053  
      * Returns the default input encoding for the servlet.
 1054  
      *
 1055  
      * @return the default input encoding.
 1056  
      */
 1057  
     public String getDefaultInputEncoding()
 1058  
     {
 1059  4
         return inputEncoding;
 1060  
     }
 1061  
 
 1062  
     /**
 1063  
      * Static Helper method for looking up the RunDataService
 1064  
      * @return A RunDataService
 1065  
      */
 1066  
     private RunDataService getRunDataService()
 1067  
     {
 1068  41
         return (RunDataService) getServiceManager().getService(RunDataService.SERVICE_NAME);
 1069  
     }
 1070  
 }