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