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