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