##### Coverage Report - org.apache.turbine.Turbine

Classes in this File | Line Coverage | Branch Coverage | Complexity
Turbine |
78% | 190/242
58% | 40/68
| 2.7666666666666666;2,767

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