Coverage report

  %line %branch
org.apache.turbine.Turbine
40% 
85% 

 1  
 package org.apache.turbine;
 2  
 
 3  
 /*
 4  
  * Copyright 2001-2005 The Apache Software Foundation.
 5  
  *
 6  
  * Licensed under the Apache License, Version 2.0 (the "License")
 7  
  * you may not use this file except in compliance with the License.
 8  
  * You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing, software
 13  
  * distributed under the License is distributed on an "AS IS" BASIS,
 14  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  
  * See the License for the specific language governing permissions and
 16  
  * limitations under the License.
 17  
  */
 18  
 
 19  
 import java.io.File;
 20  
 import java.io.FileInputStream;
 21  
 import java.io.FileNotFoundException;
 22  
 import java.io.IOException;
 23  
 import java.io.UnsupportedEncodingException;
 24  
 
 25  
 import java.util.Properties;
 26  
 
 27  
 import javax.servlet.ServletConfig;
 28  
 import javax.servlet.ServletContext;
 29  
 import javax.servlet.ServletException;
 30  
 import javax.servlet.http.HttpServlet;
 31  
 import javax.servlet.http.HttpServletRequest;
 32  
 import javax.servlet.http.HttpServletResponse;
 33  
 
 34  
 import org.apache.commons.configuration.Configuration;
 35  
 import org.apache.commons.configuration.ConfigurationFactory;
 36  
 import org.apache.commons.configuration.PropertiesConfiguration;
 37  
 
 38  
 import org.apache.commons.lang.StringUtils;
 39  
 
 40  
 import org.apache.commons.lang.exception.ExceptionUtils;
 41  
 
 42  
 import org.apache.commons.logging.Log;
 43  
 import org.apache.commons.logging.LogFactory;
 44  
 
 45  
 import org.apache.log4j.PropertyConfigurator;
 46  
 
 47  
 import org.apache.turbine.modules.ActionLoader;
 48  
 import org.apache.turbine.modules.PageLoader;
 49  
 
 50  
 import org.apache.turbine.services.ServiceManager;
 51  
 import org.apache.turbine.services.TurbineServices;
 52  
 import org.apache.turbine.services.avaloncomponent.AvalonComponentService;
 53  
 import org.apache.turbine.services.component.ComponentService;
 54  
 import org.apache.turbine.services.template.TemplateService;
 55  
 import org.apache.turbine.services.template.TurbineTemplate;
 56  
 import org.apache.turbine.services.rundata.RunDataService;
 57  
 import org.apache.turbine.services.rundata.TurbineRunDataFacade;
 58  
 import org.apache.turbine.services.velocity.VelocityService;
 59  
 
 60  
 import org.apache.turbine.util.RunData;
 61  
 import org.apache.turbine.util.ServerData;
 62  
 import org.apache.turbine.util.TurbineConfig;
 63  
 import org.apache.turbine.util.TurbineException;
 64  
 import org.apache.turbine.util.security.AccessControlList;
 65  
 import org.apache.turbine.util.template.TemplateInfo;
 66  
 import org.apache.turbine.util.uri.URIConstants;
 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  
  * @version $Id: Turbine.java 264152 2005-08-29 14:50:22Z henning $
 102  
  */
 103  92
 public class Turbine
 104  
         extends HttpServlet
 105  
         implements TurbineConstants
 106  
 {
 107  
     /** SerialVersionUID for serialization */
 108  
     private static final long serialVersionUID = -6895381097045304308L;
 109  
 
 110  
     /**
 111  
      * Name of path info parameter used to indicate the redirected stage of
 112  
      * a given user's initial Turbine request
 113  
      */
 114  
     public static final String REDIRECTED_PATHINFO_NAME = "redirected";
 115  
 
 116  
     /** The base directory key */
 117  
     public static final String BASEDIR_KEY = "basedir";
 118  
 
 119  
     /**
 120  
      * In certain situations the init() method is called more than once,
 121  
      * somtimes even concurrently. This causes bad things to happen,
 122  
      * so we use this flag to prevent it.
 123  
      */
 124  69
     private static boolean firstInit = true;
 125  
 
 126  
     /** Whether init succeeded or not. */
 127  46
     private static Throwable initFailure = null;
 128  
 
 129  
     /**
 130  
      * Should initialization activities be performed during doGet() execution?
 131  
      */
 132  46
     private static boolean firstDoGet = true;
 133  
 
 134  
     /**
 135  
      * Keep all the properties of the web server in a convenient data
 136  
      * structure
 137  
      */
 138  46
     private static ServerData serverData = null;
 139  
 
 140  
     /** The base from which the Turbine application will operate. */
 141  
     private static String applicationRoot;
 142  
 
 143  
     /** Servlet config for this Turbine webapp. */
 144  
     private static ServletConfig servletConfig;
 145  
 
 146  
     /** Servlet context for this Turbine webapp. */
 147  
     private static ServletContext servletContext;
 148  
 
 149  
     /**
 150  
      * The webapp root where the Turbine application
 151  
      * is running in the servlet container.
 152  
      * This might differ from the application root.
 153  
      */
 154  
     private static String webappRoot;
 155  
 
 156  
     /** Our internal configuration object */
 157  46
     private static Configuration configuration = null;
 158  
 
 159  
     /** A reference to the Template Service */
 160  46
     private TemplateService templateService = null;
 161  
 
 162  
     /** A reference to the RunData Service */
 163  46
     private RunDataService rundataService = null;
 164  
 
 165  
     /** Default Input encoding if the servlet container does not report an encoding */
 166  46
     private String inputEncoding = null;
 167  
 
 168  
     /** Logging class from commons.logging */
 169  69
     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  44
         synchronized (this.getClass())
 182  
         {
 183  44
             super.init();
 184  44
             ServletConfig config = getServletConfig();
 185  
 
 186  44
             if (!firstInit)
 187  
             {
 188  0
                 log.info("Double initialization of Turbine was attempted!");
 189  0
                 return;
 190  
             }
 191  
             // executing init will trigger some static initializers, so we have
 192  
             // only one chance.
 193  44
             firstInit = false;
 194  
 
 195  
             try
 196  
             {
 197  44
                 ServletContext context = config.getServletContext();
 198  
 
 199  44
                 configure(config, context);
 200  
 
 201  44
                 templateService = TurbineTemplate.getService();
 202  44
                 rundataService = TurbineRunDataFacade.getService();
 203  
 
 204  44
                 if (rundataService == null)
 205  
                 {
 206  0
                     throw new TurbineException(
 207  
                             "No RunData Service configured!");
 208  
                 }
 209  
 
 210  22
             }
 211  0
             catch (Exception e)
 212  
             {
 213  
                 // save the exception to complain loudly later :-)
 214  0
                 initFailure = e;
 215  0
                 log.fatal("Turbine: init() failed: ", e);
 216  0
                 throw new ServletException("Turbine: init() failed", e);
 217  22
             }
 218  44
             log.info("Turbine: init() Ready to Rumble!");
 219  44
         }
 220  44
     }
 221  
 
 222  
     /**
 223  
      * Read the master configuration file in, configure logging
 224  
      * and start up any early services.
 225  
      *
 226  
      * @param config The Servlet Configuration supplied by the container
 227  
      * @param context The Servlet Context supplied by the container
 228  
      *
 229  
      * @throws Exception A problem occured while reading the configuration or performing early startup
 230  
      */
 231  
 
 232  
     private void configure(ServletConfig config, ServletContext context)
 233  
             throws Exception
 234  
     {
 235  
         // Set the application root. This defaults to the webapp
 236  
         // context if not otherwise set. This is to allow 2.1 apps
 237  
         // to be developed from CVS. This feature will carry over
 238  
         // into 3.0.
 239  44
         applicationRoot = findInitParameter(context, config,
 240  
                 APPLICATION_ROOT_KEY,
 241  
                 APPLICATION_ROOT_DEFAULT);
 242  
 
 243  44
         webappRoot = config.getServletContext().getRealPath("/");
 244  
         // log.info("Web Application root is " + webappRoot);
 245  
         // log.info("Application root is "     + applicationRoot);
 246  
 
 247  44
         if (applicationRoot == null || applicationRoot.equals(WEB_CONTEXT))
 248  
         {
 249  44
             applicationRoot = webappRoot;
 250  
             // log.info("got empty or 'webContext' Application root. Application root now: " + applicationRoot);
 251  
         }
 252  
 
 253  
         // Set the applicationRoot for this webapp.
 254  44
         setApplicationRoot(applicationRoot);
 255  
 
 256  
         // Create any directories that need to be setup for
 257  
         // a running Turbine application.
 258  44
         createRuntimeDirectories(context, config);
 259  
 
 260  
         //
 261  
         // Now we run the Turbine configuration code. There are two ways
 262  
         // to configure Turbine:
 263  
         //
 264  
         // a) By supplying an web.xml init parameter called "configuration"
 265  
         //
 266  
         // <init-param>
 267  
         //   <param-name>configuration</param-name>
 268  
         //   <param-value>/WEB-INF/conf/turbine.xml</param-value>
 269  
         // </init-param>
 270  
         //
 271  
         // This loads an XML based configuration file.
 272  
         //
 273  
         // b) By supplying an web.xml init parameter called "properties"
 274  
         //
 275  
         // <init-param>
 276  
         //   <param-name>properties</param-name>
 277  
         //   <param-value>/WEB-INF/conf/TurbineResources.properties</param-value>
 278  
         // </init-param>
 279  
         //
 280  
         // This loads a Properties based configuration file. Actually, these are
 281  
         // extended properties as provided by commons-configuration
 282  
         //
 283  
         // If neither a) nor b) is supplied, Turbine will fall back to the
 284  
         // known behaviour of loading a properties file called
 285  
         // /WEB-INF/conf/TurbineResources.properties relative to the
 286  
         // web application root.
 287  
 
 288  44
         String confFile= findInitParameter(context, config,
 289  
                 TurbineConfig.CONFIGURATION_PATH_KEY,
 290  
                 null);
 291  
 
 292  
         String confPath;
 293  44
         String confStyle = "unset";
 294  
 
 295  44
         if (StringUtils.isNotEmpty(confFile))
 296  
         {
 297  2
             confPath = getRealPath(confFile);
 298  2
             ConfigurationFactory configurationFactory = new ConfigurationFactory(confPath);
 299  2
             configurationFactory.setBasePath(getApplicationRoot());
 300  2
             configuration = configurationFactory.getConfiguration();
 301  2
             confStyle = "XML";
 302  
         }
 303  
         else
 304  
         {
 305  42
             confFile = findInitParameter(context, config,
 306  
                     TurbineConfig.PROPERTIES_PATH_KEY,
 307  
                     TurbineConfig.PROPERTIES_PATH_DEFAULT);
 308  
 
 309  42
             confPath = getRealPath(confFile);
 310  
 
 311  
             // This should eventually be a Configuration
 312  
             // interface so that service and app configuration
 313  
             // can be stored anywhere.
 314  42
             configuration = (Configuration) new PropertiesConfiguration(confPath);
 315  42
             confStyle = "Properties";
 316  
         }
 317  
 
 318  
 
 319  
         //
 320  
         // Set up logging as soon as possible
 321  
         //
 322  44
         String log4jFile = configuration.getString(LOG4J_CONFIG_FILE,
 323  
                                                    LOG4J_CONFIG_FILE_DEFAULT);
 324  
 
 325  44
         if (StringUtils.isNotEmpty(log4jFile) &&
 326  
                 !log4jFile.equalsIgnoreCase("none"))
 327  
         {
 328  44
             log4jFile = getRealPath(log4jFile);
 329  
 
 330  
             //
 331  
             // Load the config file above into a Properties object and
 332  
             // fix up the Application root
 333  
             //
 334  44
             Properties p = new Properties();
 335  
             try
 336  
             {
 337  44
                 p.load(new FileInputStream(log4jFile));
 338  44
                 p.setProperty(APPLICATION_ROOT_KEY, getApplicationRoot());
 339  44
                 PropertyConfigurator.configure(p);
 340  
 
 341  44
                 log.info("Configured log4j from " + log4jFile);
 342  22
             }
 343  0
             catch (FileNotFoundException fnf)
 344  
             {
 345  0
                 System.err.println("Could not open Log4J configuration file "
 346  
                         + log4jFile + ": ");
 347  0
                 fnf.printStackTrace();
 348  22
             }
 349  
         }
 350  
 
 351  
         // Now report our successful configuration to the world
 352  44
         log.info("Loaded configuration  (" + confStyle + ") from " + confFile + " (" + confPath + ")");
 353  
 
 354  
 
 355  44
         setTurbineServletConfig(config);
 356  44
         setTurbineServletContext(context);
 357  
 
 358  44
         getServiceManager().setApplicationRoot(applicationRoot);
 359  
 
 360  
         // We want to set a few values in the configuration so
 361  
         // that ${variable} interpolation will work for
 362  
         //
 363  
         // ${applicationRoot}
 364  
         // ${webappRoot}
 365  44
         configuration.setProperty(APPLICATION_ROOT_KEY, applicationRoot);
 366  44
         configuration.setProperty(WEBAPP_ROOT_KEY, webappRoot);
 367  
 
 368  
 
 369  
         //
 370  
         // Be sure, that our essential services get run early
 371  
         //
 372  44
         configuration.setProperty(TurbineServices.SERVICE_PREFIX +
 373  
                                   ComponentService.SERVICE_NAME + ".earlyInit",
 374  
                                   Boolean.TRUE);
 375  
 
 376  44
         configuration.setProperty(TurbineServices.SERVICE_PREFIX +
 377  
                                   AvalonComponentService.SERVICE_NAME + ".earlyInit",
 378  
                                   Boolean.TRUE);
 379  
 
 380  44
         getServiceManager().setConfiguration(configuration);
 381  
 
 382  
         // Initialize the service manager. Services
 383  
         // that have its 'earlyInit' property set to
 384  
         // a value of 'true' will be started when
 385  
         // the service manager is initialized.
 386  44
         getServiceManager().init();
 387  
 
 388  
         // Get the default input encoding
 389  44
         inputEncoding = configuration.getString(
 390  
                 TurbineConstants.PARAMETER_ENCODING_KEY,
 391  
                 TurbineConstants.PARAMETER_ENCODING_DEFAULT);
 392  
 
 393  44
         if (log.isDebugEnabled())
 394  
         {
 395  0
             log.debug("Input Encoding has been set to " + inputEncoding);
 396  
         }
 397  44
     }
 398  
 
 399  
     /**
 400  
      * Create any directories that might be needed during
 401  
      * runtime. Right now this includes:
 402  
      *
 403  
      * <ul>
 404  
      *
 405  
      * <li>The directory to write the log files to (relative to the
 406  
      * web application root), or <code>null</code> for the default of
 407  
      * <code>/logs</code>.  The directory is specified via the {@link
 408  
      * TurbineConstants#LOGGING_ROOT} parameter.</li>
 409  
      *
 410  
      * </ul>
 411  
      *
 412  
      * @param context Global initialization parameters.
 413  
      * @param config Initialization parameters specific to the Turbine
 414  
      * servlet.
 415  
      */
 416  
     private static void createRuntimeDirectories(ServletContext context,
 417  
                                                  ServletConfig config)
 418  
     {
 419  44
         String path = findInitParameter(context, config,
 420  
                                         LOGGING_ROOT_KEY,
 421  
                                         LOGGING_ROOT_DEFAULT);
 422  
 
 423  44
         File logDir = new File(getRealPath(path));
 424  44
         if (!logDir.exists())
 425  
         {
 426  
             // Create the logging directory
 427  0
             if (!logDir.mkdirs())
 428  
             {
 429  0
                 System.err.println("Cannot create directory for logs!");
 430  
             }
 431  
         }
 432  44
     }
 433  
 
 434  
     /**
 435  
      * Finds the specified servlet configuration/initialization
 436  
      * parameter, looking first for a servlet-specific parameter, then
 437  
      * for a global parameter, and using the provided default if not
 438  
      * found.
 439  
      */
 440  
     protected static final String findInitParameter(ServletContext context,
 441  
             ServletConfig config, String name, String defaultValue)
 442  
     {
 443  178
         String path = null;
 444  
 
 445  
         // Try the name as provided first.
 446  178
         boolean usingNamespace = name.startsWith(CONFIG_NAMESPACE);
 447  89
         while (true)
 448  
         {
 449  274
             path = config.getInitParameter(name);
 450  274
             if (StringUtils.isEmpty(path))
 451  
             {
 452  192
                 path = context.getInitParameter(name);
 453  192
                 if (StringUtils.isEmpty(path))
 454  
                 {
 455  
                     // The named parameter didn't yield a value.
 456  192
                     if (usingNamespace)
 457  
                     {
 458  96
                         path = defaultValue;
 459  
                     }
 460  
                     else
 461  
                     {
 462  
                         // Try again using Turbine's namespace.
 463  96
                         name = CONFIG_NAMESPACE + '.' + name;
 464  96
                         usingNamespace = true;
 465  96
                         continue;
 466  
                     }
 467  
                 }
 468  
             }
 469  
             break;
 470  
         }
 471  
 
 472  178
         return path;
 473  
     }
 474  
 
 475  
     /**
 476  
      * Initializes the services which need <code>RunData</code> to
 477  
      * initialize themselves (post startup).
 478  
      *
 479  
      * @param data The first <code>GET</code> request.
 480  
      */
 481  
     public final void init(RunData data)
 482  
     {
 483  0
         synchronized (Turbine.class)
 484  
         {
 485  0
             if (firstDoGet)
 486  
             {
 487  
                 // All we want to do here is save some servlet
 488  
                 // information so that services and processes
 489  
                 // that don't have direct access to a RunData
 490  
                 // object can still know something about
 491  
                 // the servlet environment.
 492  0
                 saveServletInfo(data);
 493  
 
 494  
                 // Mark that we're done.
 495  0
                 firstDoGet = false;
 496  0
                 log.info("Turbine: first Request successful");
 497  
             }
 498  0
         }
 499  0
     }
 500  
 
 501  
     /**
 502  
      * Return the current configuration with all keys included
 503  
      *
 504  
      * @return a Configuration Object
 505  
      */
 506  
     public static Configuration getConfiguration()
 507  
     {
 508  632
         return configuration;
 509  
     }
 510  
 
 511  
     /**
 512  
      * Return the server name.
 513  
      *
 514  
      * @return String server name
 515  
      */
 516  
     public static String getServerName()
 517  
     {
 518  0
         return getDefaultServerData().getServerName();
 519  
     }
 520  
 
 521  
     /**
 522  
      * Return the server scheme.
 523  
      *
 524  
      * @return String server scheme
 525  
      */
 526  
     public static String getServerScheme()
 527  
     {
 528  0
         return getDefaultServerData().getServerScheme();
 529  
     }
 530  
 
 531  
     /**
 532  
      * Return the server port.
 533  
      *
 534  
      * @return String server port
 535  
      */
 536  
     public static String getServerPort()
 537  
     {
 538  0
         return Integer.toString(getDefaultServerData().getServerPort());
 539  
     }
 540  
 
 541  
     /**
 542  
      * Get the script name. This is the initial script name.
 543  
      * Actually this is probably not needed any more. I'll
 544  
      * check. jvz.
 545  
      *
 546  
      * @return String initial script name.
 547  
      */
 548  
     public static String getScriptName()
 549  
     {
 550  0
         return getDefaultServerData().getScriptName();
 551  
     }
 552  
 
 553  
     /**
 554  
      * Return the context path.
 555  
      *
 556  
      * @return String context path
 557  
      */
 558  
     public static String getContextPath()
 559  
     {
 560  0
         return getDefaultServerData().getContextPath();
 561  
     }
 562  
 
 563  
     /**
 564  
      * Return all the Turbine Servlet information (Server Name, Port,
 565  
      * Scheme in a ServerData structure. This is generated from the
 566  
      * values set when initializing the Turbine and may not be correct
 567  
      * if you're running in a clustered structure. This might be used
 568  
      * if you need a DataURI and have no RunData object handy-
 569  
      *
 570  
      * @return An initialized ServerData object
 571  
      */
 572  
     public static ServerData getDefaultServerData()
 573  
     {
 574  14
         if(serverData == null)
 575  
         {
 576  2
             log.error("ServerData Information requested from Turbine before first request!");
 577  
             // Will be overwritten once the first request is run;
 578  2
             serverData = new ServerData(null, URIConstants.HTTP_PORT,
 579  
                     URIConstants.HTTP, null, class="keyword">null);
 580  
         }
 581  14
         return serverData;
 582  
     }
 583  
 
 584  
     /**
 585  
      * Set the servlet config for this turbine webapp.
 586  
      *
 587  
      * @param config New servlet config
 588  
      */
 589  
     public static void setTurbineServletConfig(ServletConfig config)
 590  
     {
 591  44
         servletConfig = config;
 592  44
     }
 593  
 
 594  
     /**
 595  
      * Get the servlet config for this turbine webapp.
 596  
      *
 597  
      * @return ServletConfig
 598  
      */
 599  
     public static ServletConfig getTurbineServletConfig()
 600  
     {
 601  28
         return servletConfig;
 602  
     }
 603  
 
 604  
     /**
 605  
      * Set the servlet context for this turbine webapp.
 606  
      *
 607  
      * @param context New servlet context.
 608  
      */
 609  
     public static void setTurbineServletContext(ServletContext context)
 610  
     {
 611  44
         servletContext = context;
 612  44
     }
 613  
 
 614  
     /**
 615  
      * Get the servlet context for this turbine webapp.
 616  
      *
 617  
      * @return ServletContext
 618  
      */
 619  
     public static ServletContext getTurbineServletContext()
 620  
     {
 621  0
         return servletContext;
 622  
     }
 623  
 
 624  
     /**
 625  
      * The <code>Servlet</code> destroy method.  Invokes
 626  
      * <code>ServiceBroker</code> tear down method.
 627  
      */
 628  
     public final void destroy()
 629  
     {
 630  
         // Shut down all Turbine Services.
 631  10
         getServiceManager().shutdownServices();
 632  10
         System.gc();
 633  
 
 634  10
         firstInit = true;
 635  10
         firstDoGet = true;
 636  10
         log.info("Turbine: Done shutting down!");
 637  10
     }
 638  
 
 639  
     /**
 640  
      * The primary method invoked when the Turbine servlet is executed.
 641  
      *
 642  
      * @param req Servlet request.
 643  
      * @param res Servlet response.
 644  
      * @exception IOException a servlet exception.
 645  
      * @exception ServletException a servlet exception.
 646  
      */
 647  
     public final void doGet(HttpServletRequest req, HttpServletResponse res)
 648  
             throws IOException, ServletException
 649  
     {
 650  
         // set to true if the request is to be redirected by the page
 651  0
         boolean requestRedirected = false;
 652  
 
 653  
         // Placeholder for the RunData object.
 654  0
         RunData data = null;
 655  
         try
 656  
         {
 657  
             // Check to make sure that we started up properly.
 658  0
             if (initFailure != null)
 659  
             {
 660  0
                 throw initFailure;
 661  
             }
 662  
 
 663  
             //
 664  
             // If the servlet container gives us no clear indication about the
 665  
             // Encoding of the contents, set it to our default value.
 666  0
             if (req.getCharacterEncoding() == null)
 667  
             {
 668  0
                 if (log.isDebugEnabled())
 669  
                 {
 670  0
                     log.debug("Changing Input Encoding to " + inputEncoding);
 671  
                 }
 672  
 
 673  
                 try
 674  
                 {
 675  0
                     req.setCharacterEncoding(inputEncoding);
 676  
                 }
 677  0
                 catch (UnsupportedEncodingException uee)
 678  
                 {
 679  0
                     log.warn("Could not change request encoding to " + inputEncoding, uee);
 680  0
                 }
 681  
             }
 682  
 
 683  
             // Get general RunData here...
 684  
             // Perform turbine specific initialization below.
 685  0
             data = rundataService.getRunData(req, res, getServletConfig());
 686  
 
 687  
             // If this is the first invocation, perform some
 688  
             // initialization.  Certain services need RunData to initialize
 689  
             // themselves.
 690  0
             if (firstDoGet)
 691  
             {
 692  0
                 init(data);
 693  
             }
 694  
 
 695  
             // set the session timeout if specified in turbine's properties
 696  
             // file if this is a new session
 697  0
             if (data.getSession().isNew())
 698  
             {
 699  0
                 int timeout = configuration.getInt(SESSION_TIMEOUT_KEY,
 700  
                                                    SESSION_TIMEOUT_DEFAULT);
 701  
 
 702  0
                 if (timeout != SESSION_TIMEOUT_DEFAULT)
 703  
                 {
 704  0
                     data.getSession().setMaxInactiveInterval(timeout);
 705  
                 }
 706  
             }
 707  
 
 708  
             // Fill in the screen and action variables.
 709  0
             data.setScreen(data.getParameters().getString(URIConstants.CGI_SCREEN_PARAM));
 710  0
             data.setAction(data.getParameters().getString(URIConstants.CGI_ACTION_PARAM));
 711  
 
 712  
             // Special case for login and logout, this must happen before the
 713  
             // session validator is executed in order either to allow a user to
 714  
             // even login, or to ensure that the session validator gets to
 715  
             // mandate its page selection policy for non-logged in users
 716  
             // after the logout has taken place.
 717  0
             if (data.hasAction())
 718  
             {
 719  0
                 String action = data.getAction();
 720  0
                 log.debug("action = " + action);
 721  
 
 722  0
                 if (action.equalsIgnoreCase(
 723  
                         configuration.getString(ACTION_LOGIN_KEY,
 724  
                                                 ACTION_LOGIN_DEFAULT)))
 725  
                 {
 726  0
                     loginAction(data);
 727  
                 }
 728  0
                 else if (action.equalsIgnoreCase(
 729  
                         configuration.getString(ACTION_LOGOUT_KEY,
 730  
                                                 ACTION_LOGOUT_DEFAULT)))
 731  
                 {
 732  0
                    logoutAction(data);
 733  
                 }
 734  
             }
 735  
 
 736  
             // This is where the validation of the Session information
 737  
             // is performed if the user has not logged in yet, then
 738  
             // the screen is set to be Login. This also handles the
 739  
             // case of not having a screen defined by also setting the
 740  
             // screen to Login. If you want people to go to another
 741  
             // screen other than Login, you need to change that within
 742  
             // TurbineResources.properties...screen.homepage; or, you
 743  
             // can specify your own SessionValidator action.
 744  0
             ActionLoader.getInstance().exec(
 745  
                     data, configuration.getString(ACTION_SESSION_VALIDATOR_KEY,
 746  
                         ACTION_SESSION_VALIDATOR_DEFAULT));
 747  
 
 748  
             // Put the Access Control List into the RunData object, so
 749  
             // it is easily available to modules.  It is also placed
 750  
             // into the session for serialization.  Modules can null
 751  
             // out the ACL to force it to be rebuilt based on more
 752  
             // information.
 753  0
             ActionLoader.getInstance().exec(
 754  
                     data, configuration.getString(ACTION_ACCESS_CONTROLLER_KEY,
 755  
                         ACTION_ACCESS_CONTROLLER_DEFAULT));
 756  
 
 757  
             // Start the execution phase. DefaultPage will execute the
 758  
             // appropriate action as well as get the Layout from the
 759  
             // Screen and then execute that. The Layout is then
 760  
             // responsible for executing the Navigation and Screen
 761  
             // modules.
 762  
             //
 763  
             // Note that by default, this cannot be overridden from
 764  
             // parameters passed in via post/query data. This is for
 765  
             // security purposes.  You should really never need more
 766  
             // than just the default page.  If you do, add logic to
 767  
             // DefaultPage to do what you want.
 768  
 
 769  0
             String defaultPage = (templateService == null)
 770  
                     ? null :templateService.getDefaultPageName(data);
 771  
 
 772  0
             if (defaultPage == null)
 773  
             {
 774  
                 /*
 775  
                  * In this case none of the template services are running.
 776  
                  * The application may be using ECS for views, or a
 777  
                  * decendent of RawScreen is trying to produce output.
 778  
                  * If there is a 'page.default' property in the TR.props
 779  
                  * then use that, otherwise return DefaultPage which will
 780  
                  * handle ECS view scenerios and RawScreen scenerios. The
 781  
                  * app developer can still specify the 'page.default'
 782  
                  * if they wish but the DefaultPage should work in
 783  
                  * most cases.
 784  
                  */
 785  0
                 defaultPage = configuration.getString(PAGE_DEFAULT_KEY,
 786  
                         PAGE_DEFAULT_DEFAULT);
 787  
             }
 788  
 
 789  0
             PageLoader.getInstance().exec(data, defaultPage);
 790  
 
 791  
             // If a module has set data.acl = null, remove acl from
 792  
             // the session.
 793  0
             if (data.getACL() == null)
 794  
             {
 795  
                 try
 796  
                 {
 797  0
                     data.getSession().removeAttribute(
 798  
                             AccessControlList.SESSION_KEY);
 799  
                 }
 800  0
                 catch (IllegalStateException ignored)
 801  
                 {
 802  0
                 }
 803  
             }
 804  
 
 805  
             // handle a redirect request
 806  0
             requestRedirected = ((data.getRedirectURI() != null)
 807  
                                  && (data.getRedirectURI().length() > 0));
 808  0
             if (requestRedirected)
 809  
             {
 810  0
                 if (data.getResponse().isCommitted())
 811  
                 {
 812  0
                     requestRedirected = false;
 813  0
                     log.warn("redirect requested, response already committed: " +
 814  
                              data.getRedirectURI());
 815  
                 }
 816  
                 else
 817  
                 {
 818  0
                     data.getResponse().sendRedirect(data.getRedirectURI());
 819  
                 }
 820  
             }
 821  
 
 822  0
             if (!requestRedirected)
 823  
             {
 824  
                 try
 825  
                 {
 826  0
                     if (data.isPageSet() == false && data.isOutSet() == false)
 827  
                     {
 828  0
                         throw new Exception("Nothing to output");
 829  
                     }
 830  
 
 831  
                     // We are all done! if isPageSet() output that way
 832  
                     // otherwise, data.getOut() has already been written
 833  
                     // to the data.getOut().close() happens below in the
 834  
                     // finally.
 835  0
                     if (data.isPageSet() && data.isOutSet() == false)
 836  
                     {
 837  
                         // Modules can override these.
 838  0
                         data.getResponse().setLocale(data.getLocale());
 839  0
                         data.getResponse().setContentType(
 840  
                                 data.getContentType());
 841  
 
 842  
                         // Set the status code.
 843  0
                         data.getResponse().setStatus(data.getStatusCode());
 844  
                         // Output the Page.
 845  0
                         data.getPage().output(data.getOut());
 846  
                     }
 847  
                 }
 848  0
                 catch (Exception e)
 849  
                 {
 850  
                     // The output stream was probably closed by the client
 851  
                     // end of things ie: the client clicked the Stop
 852  
                     // button on the browser, so ignore any errors that
 853  
                     // result.
 854  0
                     log.debug("Output stream closed? ", e);
 855  0
                 }
 856  
             }
 857  
         }
 858  0
         catch (Exception e)
 859  
         {
 860  0
             handleException(data, res, e);
 861  
         }
 862  0
         catch (Throwable t)
 863  
         {
 864  0
             handleException(data, res, t);
 865  
         }
 866  
         finally
 867  
         {
 868  
             // Return the used RunData to the factory for recycling.
 869  0
             rundataService.putRunData(data);
 870  0
         }
 871  0
     }
 872  
 
 873  
     /**
 874  
      * In this application doGet and doPost are the same thing.
 875  
      *
 876  
      * @param req Servlet request.
 877  
      * @param res Servlet response.
 878  
      * @exception IOException a servlet exception.
 879  
      * @exception ServletException a servlet exception.
 880  
      */
 881  
     public final void doPost(HttpServletRequest req, HttpServletResponse res)
 882  
             throws IOException, ServletException
 883  
     {
 884  0
         doGet(req, res);
 885  0
     }
 886  
 
 887  
     /**
 888  
      * This method is executed if the configured Login action should be
 889  
      * executed by Turbine.
 890  
      * <p>
 891  
      * This Action must be performed before the Session validation or we
 892  
      * get sent in an endless loop back to the Login screen before
 893  
      * the action can be performed
 894  
      *
 895  
      * @param data a RunData object
 896  
      *
 897  
      * @throws Exception A problem while logging in occured.
 898  
      */
 899  
     private void loginAction(RunData data)
 900  
             throws Exception
 901  
     {
 902  0
         ActionLoader.getInstance().exec(data, data.getAction());
 903  0
         cleanupTemplateContext(data);
 904  0
         data.setAction(null);
 905  0
     }
 906  
 
 907  
     /**
 908  
      * This method is executed if the configured Logout action should be
 909  
      * executed by Turbine.
 910  
      * <p>
 911  
      * This Action must be performed before the Session validation for the
 912  
      * session validator to send us back to the Login screen.
 913  
      * <p>
 914  
      * The existing session is invalidated before the logout action is
 915  
      * executed.
 916  
      *
 917  
      * @param data a RunData object
 918  
      *
 919  
      * @throws Exception A problem while logging out occured.
 920  
      */
 921  
     private void logoutAction(RunData data)
 922  
             throws Exception
 923  
     {
 924  0
         ActionLoader.getInstance().exec(data, data.getAction());
 925  0
         cleanupTemplateContext(data);
 926  0
         data.setAction(null);
 927  0
         data.getSession().invalidate();
 928  0
     }
 929  
 
 930  
     /**
 931  
      * cleans the Velocity Context if available.
 932  
      *
 933  
      * @param data A RunData Object
 934  
      *
 935  
      * @throws Exception A problem while cleaning out the Template Context occured.
 936  
      */
 937  
     private void cleanupTemplateContext(RunData data)
 938  
             throws Exception
 939  
     {
 940  
         // This is Velocity specific and shouldn't be done here.
 941  
         // But this is a band aid until we get real listeners
 942  
         // here.
 943  0
         TemplateInfo ti = data.getTemplateInfo();
 944  0
         if (ti != null)
 945  
         {
 946  0
             ti.removeTemp(VelocityService.CONTEXT);
 947  
         }
 948  0
     }
 949  
 
 950  
     /**
 951  
      * Return the servlet info.
 952  
      *
 953  
      * @return a string with the servlet information.
 954  
      */
 955  
     public final String getServletInfo()
 956  
     {
 957  0
         return "Turbine Servlet";
 958  
     }
 959  
 
 960  
     /**
 961  
      * This method is about making sure that we catch and display
 962  
      * errors to the screen in one fashion or another. What happens is
 963  
      * that it will attempt to show the error using your user defined
 964  
      * Error Screen. If that fails, then it will resort to just
 965  
      * displaying the error and logging it all over the place
 966  
      * including the servlet engine log file, the Turbine log file and
 967  
      * on the screen.
 968  
      *
 969  
      * @param data A Turbine RunData object.
 970  
      * @param res Servlet response.
 971  
      * @param t The exception to report.
 972  
      */
 973  
     private void handleException(RunData data, HttpServletResponse res,
 974  
                                        Throwable t)
 975  
     {
 976  
         // make sure that the stack trace makes it the log
 977  0
         log.error("Turbine.handleException: ", t);
 978  
 
 979  0
         String mimeType = "text/plain";
 980  
         try
 981  
         {
 982  
             // This is where we capture all exceptions and show the
 983  
             // Error Screen.
 984  0
             data.setStackTrace(ExceptionUtils.getStackTrace(t), t);
 985  
 
 986  
             // setup the screen
 987  0
             data.setScreen(configuration.getString(SCREEN_ERROR_KEY,
 988  
                     SCREEN_ERROR_DEFAULT));
 989  
 
 990  
             // do more screen setup for template execution if needed
 991  0
             if (data.getTemplateInfo() != null)
 992  
             {
 993  0
                 data.getTemplateInfo()
 994  
                     .setScreenTemplate(configuration.getString(
 995  
                             TEMPLATE_ERROR_KEY, TEMPLATE_ERROR_VM));
 996  
             }
 997  
 
 998  
             // Make sure to not execute an action.
 999  0
             data.setAction("");
 1000  
 
 1001  0
             PageLoader.getInstance().exec(data,
 1002  
                     configuration.getString(PAGE_DEFAULT_KEY,
 1003  
                             PAGE_DEFAULT_DEFAULT));
 1004  
 
 1005  0
             data.getResponse().setContentType(data.getContentType());
 1006  0
             data.getResponse().setStatus(data.getStatusCode());
 1007  0
             if (data.isPageSet())
 1008  
             {
 1009  0
                 data.getOut().print(data.getPage().toString());
 1010  
             }
 1011  
         }
 1012  
         // Catch this one because it occurs if some code hasn't been
 1013  
         // completely re-compiled after a change..
 1014  0
         catch (java.lang.NoSuchFieldError e)
 1015  
         {
 1016  
             try
 1017  
             {
 1018  0
                 data.getResponse().setContentType(mimeType);
 1019  0
                 data.getResponse().setStatus(200);
 1020  
             }
 1021  0
             catch (Exception ignored)
 1022  
             {
 1023  
                 // Ignored
 1024  0
             }
 1025  
 
 1026  
             try
 1027  
             {
 1028  0
                 data.getOut().print("java.lang.NoSuchFieldError: "
 1029  
                         + "Please recompile all of your source code.");
 1030  
             }
 1031  0
             catch (IOException ignored)
 1032  
             {
 1033  0
             }
 1034  
 
 1035  0
             log.error(data.getStackTrace(), e);
 1036  
         }
 1037  
         // Attempt to do *something* at this point...
 1038  0
         catch (Throwable reallyScrewedNow)
 1039  
         {
 1040  0
             StringBuffer msg = new StringBuffer();
 1041  0
             msg.append("Horrible Exception: ");
 1042  0
             if (data != null)
 1043  
             {
 1044  0
                 msg.append(data.getStackTrace());
 1045  
             }
 1046  
             else
 1047  
             {
 1048  0
                 msg.append(t);
 1049  
             }
 1050  
             try
 1051  
             {
 1052  0
                 res.setContentType(mimeType);
 1053  0
                 res.setStatus(200);
 1054  0
                 res.getWriter().print(msg.toString());
 1055  
             }
 1056  0
             catch (Exception ignored)
 1057  
             {
 1058  0
             }
 1059  
 
 1060  0
             log.error(reallyScrewedNow.getMessage(), reallyScrewedNow);
 1061  0
         }
 1062  0
     }
 1063  
 
 1064  
     /**
 1065  
      * Save some information about this servlet so that
 1066  
      * it can be utilized by object instances that do not
 1067  
      * have direct access to RunData.
 1068  
      *
 1069  
      * @param data
 1070  
      */
 1071  
     public static synchronized void saveServletInfo(RunData data)
 1072  
     {
 1073  
         // Store the context path for tools like ContentURI and
 1074  
         // the UIManager that use webapp context path information
 1075  
         // for constructing URLs.
 1076  
 
 1077  
         //
 1078  
         // Bundle all the information above up into a convenient structure
 1079  
         //
 1080  0
         serverData = (ServerData) data.getServerData().clone();
 1081  0
     }
 1082  
 
 1083  
     /**
 1084  
      * Set the application root for the webapp.
 1085  
      *
 1086  
      * @param val New app root.
 1087  
      */
 1088  
     public static void setApplicationRoot(String val)
 1089  
     {
 1090  44
         applicationRoot = val;
 1091  44
     }
 1092  
 
 1093  
     /**
 1094  
      * Get the application root for this Turbine webapp. This
 1095  
      * concept was started in 3.0 and will allow an app to be
 1096  
      * developed from a standard CVS layout. With a simple
 1097  
      * switch the app will work fully within the servlet
 1098  
      * container for deployment.
 1099  
      *
 1100  
      * @return String applicationRoot
 1101  
      */
 1102  
     public static String getApplicationRoot()
 1103  
     {
 1104  456
         return applicationRoot;
 1105  
     }
 1106  
 
 1107  
     /**
 1108  
      * Used to get the real path of configuration and resource
 1109  
      * information. This can be used by an app being
 1110  
      * developed in a standard CVS layout.
 1111  
      *
 1112  
      * @param path path translated to the application root
 1113  
      * @return the real path
 1114  
      */
 1115  
     public static String getRealPath(String path)
 1116  
     {
 1117  410
         if (path.startsWith("/"))
 1118  
         {
 1119  118
             path = path.substring(1);
 1120  
         }
 1121  
 
 1122  410
         return new File(getApplicationRoot(), path).getAbsolutePath();
 1123  
     }
 1124  
 
 1125  
     /**
 1126  
      * Return an instance of the currently configured Service Manager
 1127  
      *
 1128  
      * @return A service Manager instance
 1129  
      */
 1130  
     private ServiceManager getServiceManager()
 1131  
     {
 1132  142
         return TurbineServices.getInstance();
 1133  
     }
 1134  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.