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