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

Classes in this File | Line Coverage | Branch Coverage | Complexity
Turbine |
80% | 173/216
59% | 31/52
| 2.4827586206896552;2,483

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