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

Classes in this File | Line Coverage | Branch Coverage | Complexity
Turbine |
80% | 195/243
59% | 42/71
| 2.896551724137931;2,897
Turbine$1 |
100% | 1/1
N/A | N/A
| 2.896551724137931;2,897
Turbine$ConfigurationStyle |
100% | 4/4
N/A | N/A
| 2.896551724137931;2,897

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