KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > servlet > CocoonServlet


1 /*
2  * Copyright 1999-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.servlet;
17
18 import org.apache.avalon.excalibur.logger.Log4JLoggerManager;
19 import org.apache.avalon.excalibur.logger.LogKitLoggerManager;
20 import org.apache.avalon.excalibur.logger.LoggerManager;
21 import org.apache.avalon.framework.activity.Disposable;
22 import org.apache.avalon.framework.activity.Initializable;
23 import org.apache.avalon.framework.component.ComponentManager;
24 import org.apache.avalon.framework.configuration.Configurable;
25 import org.apache.avalon.framework.configuration.Configuration;
26 import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
27 import org.apache.avalon.framework.container.ContainerUtil;
28 import org.apache.avalon.framework.context.Contextualizable;
29 import org.apache.avalon.framework.context.DefaultContext;
30 import org.apache.avalon.framework.logger.LogEnabled;
31 import org.apache.avalon.framework.logger.LogKitLogger;
32 import org.apache.avalon.framework.logger.Logger;
33
34 import org.apache.cocoon.Cocoon;
35 import org.apache.cocoon.ConnectionResetException;
36 import org.apache.cocoon.Constants;
37 import org.apache.cocoon.ResourceNotFoundException;
38 import org.apache.cocoon.components.notification.DefaultNotifyingBuilder;
39 import org.apache.cocoon.components.notification.Notifier;
40 import org.apache.cocoon.components.notification.Notifying;
41 import org.apache.cocoon.environment.Environment;
42 import org.apache.cocoon.environment.http.HttpContext;
43 import org.apache.cocoon.environment.http.HttpEnvironment;
44 import org.apache.cocoon.servlet.multipart.MultipartHttpServletRequest;
45 import org.apache.cocoon.servlet.multipart.RequestFactory;
46 import org.apache.cocoon.util.ClassUtils;
47 import org.apache.cocoon.util.Deprecation;
48 import org.apache.cocoon.util.IOUtils;
49 import org.apache.cocoon.util.StringUtils;
50 import org.apache.cocoon.util.log.CocoonLogFormatter;
51 import org.apache.cocoon.util.log.Log4JConfigurator;
52
53 import org.apache.commons.lang.BooleanUtils;
54 import org.apache.commons.lang.SystemUtils;
55 import org.apache.commons.lang.time.StopWatch;
56 import org.apache.excalibur.instrument.InstrumentManager;
57 import org.apache.excalibur.instrument.manager.impl.DefaultInstrumentManagerImpl;
58 import org.apache.log.ContextMap;
59 import org.apache.log.ErrorHandler;
60 import org.apache.log.Hierarchy;
61 import org.apache.log.Priority;
62 import org.apache.log.output.ServletOutputLogTarget;
63 import org.apache.log.util.DefaultErrorHandler;
64 import org.apache.log4j.LogManager;
65
66 import javax.servlet.ServletConfig JavaDoc;
67 import javax.servlet.ServletContext JavaDoc;
68 import javax.servlet.ServletException JavaDoc;
69 import javax.servlet.ServletOutputStream JavaDoc;
70 import javax.servlet.http.HttpServlet JavaDoc;
71 import javax.servlet.http.HttpServletRequest JavaDoc;
72 import javax.servlet.http.HttpServletResponse JavaDoc;
73 import java.io.File JavaDoc;
74 import java.io.FileInputStream JavaDoc;
75 import java.io.FileOutputStream JavaDoc;
76 import java.io.IOException JavaDoc;
77 import java.io.InputStream JavaDoc;
78 import java.io.OutputStream JavaDoc;
79 import java.lang.reflect.Constructor JavaDoc;
80 import java.net.MalformedURLException JavaDoc;
81 import java.net.SocketException JavaDoc;
82 import java.net.URL JavaDoc;
83 import java.util.ArrayList JavaDoc;
84 import java.util.Arrays JavaDoc;
85 import java.util.HashMap JavaDoc;
86 import java.util.Iterator JavaDoc;
87 import java.util.List JavaDoc;
88 import java.util.StringTokenizer JavaDoc;
89 import java.util.jar.Attributes JavaDoc;
90 import java.util.jar.Manifest JavaDoc;
91
92 /**
93  * This is the entry point for Cocoon execution as an HTTP Servlet.
94  *
95  * @author <a HREF="mailto:pier@apache.org">Pierpaolo Fumagalli</a>
96  * (Apache Software Foundation)
97  * @author <a HREF="mailto:stefano@apache.org">Stefano Mazzocchi</a>
98  * @author <a HREF="mailto:nicolaken@apache.org">Nicola Ken Barozzi</a>
99  * @author <a HREF="mailto:bloritsch@apache.org">Berin Loritsch</a>
100  * @author <a HREF="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
101  * @author <a HREF="mailto:leo.sutic@inspireinfrastructure.com">Leo Sutic</a>
102  * @version $Id: CocoonServlet.java 325950 2005-10-17 18:26:07Z vgritsenko $
103  */

104 public class CocoonServlet extends HttpServlet JavaDoc {
105
106     /**
107      * Application <code>Context</code> Key for the servlet configuration
108      * @since 2.1.3
109      */

110     public static final String JavaDoc CONTEXT_SERVLET_CONFIG = "servlet-config";
111
112     // Processing time message
113
protected static final String JavaDoc PROCESSED_BY = "Processed by "
114             + Constants.COMPLETE_NAME + " in ";
115
116     // Used by "show-time"
117
static final float SECOND = 1000;
118     static final float MINUTE = 60 * SECOND;
119     static final float HOUR = 60 * MINUTE;
120
121     private Logger log;
122     private LoggerManager loggerManager;
123
124     /**
125      * The time the cocoon instance was created
126      */

127     protected long creationTime;
128
129     /**
130      * The <code>Cocoon</code> instance
131      */

132     protected Cocoon cocoon;
133
134     /**
135      * Holds exception happened during initialization (if any)
136      */

137     protected Exception JavaDoc exception;
138
139     /**
140      * Avalon application context
141      */

142     protected DefaultContext appContext = new DefaultContext();
143
144
145     /**
146      * Default value for {@link #allowReload} parameter (false)
147      */

148     protected static final boolean ALLOW_RELOAD = false;
149
150     /**
151      * Allow reloading of cocoon by specifying the
152      * <code>cocoon-reload=true</code> parameter with a request
153      */

154     protected boolean allowReload;
155
156
157     /**
158      * Allow adding processing time to the response
159      */

160     protected boolean showTime;
161
162     /**
163      * If true, processing time will be added as an HTML comment
164      */

165     protected boolean hiddenShowTime;
166
167     /** Flag to enable/disable X-Cocoon-Version header */
168     private boolean showCocoonVersion;
169
170     /**
171      * Default value for {@link #enableUploads} parameter (false)
172      */

173     private static final boolean ENABLE_UPLOADS = false;
174     private static final boolean SAVE_UPLOADS_TO_DISK = true;
175     private static final int MAX_UPLOAD_SIZE = 10000000; // 10Mb
176

177     /**
178      * Allow processing of upload requests (mime/multipart)
179      */

180     private boolean enableUploads;
181     private boolean autoSaveUploads;
182     private boolean allowOverwrite;
183     private boolean silentlyRename;
184     private int maxUploadSize;
185
186     private File JavaDoc uploadDir;
187     private File JavaDoc workDir;
188     private File JavaDoc cacheDir;
189     private String JavaDoc containerEncoding;
190     private String JavaDoc defaultFormEncoding;
191
192     protected ServletContext JavaDoc servletContext;
193
194     /** The classloader that will be set as the context classloader if init-classloader is true */
195     protected ClassLoader JavaDoc classLoader = this.getClass().getClassLoader();
196     protected boolean initClassLoader = false;
197
198     private String JavaDoc parentComponentManagerClass;
199     private String JavaDoc parentComponentManagerInitParam;
200
201     /** The parent ComponentManager, if any. Stored here in order to be able to dispose it in destroy(). */
202     private ComponentManager parentComponentManager;
203
204     protected String JavaDoc forceLoadParameter;
205     protected String JavaDoc forceSystemProperty;
206
207     /**
208      * If true or not set, this class will try to catch and handle all Cocoon exceptions.
209      * If false, it will rethrow them to the servlet container.
210      */

211     private boolean manageExceptions;
212
213     /**
214      * Flag to enable avalon excalibur instrumentation of Cocoon.
215      */

216     private boolean enableInstrumentation;
217
218     /**
219      * The <code>InstrumentManager</code> instance
220      */

221     private InstrumentManager instrumentManager;
222
223     /**
224      * This is the path to the servlet context (or the result
225      * of calling getRealPath('/') on the ServletContext.
226      * Note, that this can be null.
227      */

228     protected String JavaDoc servletContextPath;
229
230     /**
231      * This is the url to the servlet context directory
232      */

233     protected String JavaDoc servletContextURL;
234
235     /**
236      * The RequestFactory is responsible for wrapping multipart-encoded
237      * forms and for handing the file payload of incoming requests
238      */

239     protected RequestFactory requestFactory;
240
241     /**
242      * Initialize this <code>CocoonServlet</code> instance. You will
243      * notice that I have broken the init into sub methods to make it
244      * easier to maintain (BL). The context is passed to a couple of
245      * the subroutines. This is also because it is better to explicitly
246      * pass variables than implicitely. It is both more maintainable,
247      * and more elegant.
248      *
249      * @param conf The ServletConfig object from the servlet engine.
250      *
251      * @throws ServletException
252      */

253     public void init(ServletConfig JavaDoc conf)
254     throws ServletException JavaDoc {
255
256         super.init(conf);
257
258         // Check the init-classloader parameter only if it's not already true.
259
// This is useful for subclasses of this servlet that override the value
260
// initially set by this class (i.e. false).
261
if (!this.initClassLoader) {
262             this.initClassLoader = getInitParameterAsBoolean("init-classloader", false);
263         }
264
265         if (this.initClassLoader) {
266             // Force context classloader so that JAXP can work correctly
267
// (see javax.xml.parsers.FactoryFinder.findClassLoader())
268
try {
269                 Thread.currentThread().setContextClassLoader(this.classLoader);
270             } catch (Exception JavaDoc e) {
271                 // ignore this
272
}
273         }
274
275         try {
276             // FIXME (VG): We shouldn't have to specify these. Need to override
277
// jaxp implementation of weblogic before initializing logger.
278
// This piece of code is also required in the Cocoon class.
279
String JavaDoc value = System.getProperty("javax.xml.parsers.SAXParserFactory");
280             if (value != null && value.startsWith("weblogic")) {
281                 System.setProperty("javax.xml.parsers.SAXParserFactory", "org.apache.xerces.jaxp.SAXParserFactoryImpl");
282                 System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
283             }
284         } catch (Exception JavaDoc e) {
285             // Ignore security exception
286
System.out.println("CocoonServlet: Could not check system properties, got: " + e);
287         }
288
289         this.servletContext = conf.getServletContext();
290         this.appContext.put(Constants.CONTEXT_ENVIRONMENT_CONTEXT, new HttpContext(this.servletContext));
291         this.servletContextPath = this.servletContext.getRealPath("/");
292
293         // first init the work-directory for the logger.
294
// this is required if we are running inside a war file!
295
final String JavaDoc workDirParam = getInitParameter("work-directory");
296         if (workDirParam != null) {
297             if (this.servletContextPath == null) {
298                 // No context path : consider work-directory as absolute
299
this.workDir = new File JavaDoc(workDirParam);
300             } else {
301                 // Context path exists : is work-directory absolute ?
302
File JavaDoc workDirParamFile = new File JavaDoc(workDirParam);
303                 if (workDirParamFile.isAbsolute()) {
304                     // Yes : keep it as is
305
this.workDir = workDirParamFile;
306                 } else {
307                     // No : consider it relative to context path
308
this.workDir = new File JavaDoc(servletContextPath, workDirParam);
309                 }
310             }
311         } else {
312             this.workDir = (File JavaDoc) this.servletContext.getAttribute("javax.servlet.context.tempdir");
313             this.workDir = new File JavaDoc(workDir, "cocoon-files");
314         }
315         this.workDir.mkdirs();
316         this.appContext.put(Constants.CONTEXT_WORK_DIR, workDir);
317
318         String JavaDoc path = this.servletContextPath;
319         // these two variables are just for debugging. We can't log at this point
320
// as the logger isn't initialized yet.
321
String JavaDoc debugPathOne = null, debugPathTwo = null;
322         if (path == null) {
323             // Try to figure out the path of the root from that of WEB-INF
324
try {
325                 path = this.servletContext.getResource("/WEB-INF").toString();
326             } catch (MalformedURLException JavaDoc me) {
327                 throw new ServletException JavaDoc("Unable to get resource 'WEB-INF'.", me);
328             }
329             debugPathOne = path;
330             path = path.substring(0, path.length() - "WEB-INF".length());
331             debugPathTwo = path;
332         }
333         try {
334             if (path.indexOf(':') > 1) {
335                 this.servletContextURL = path;
336             } else {
337                 this.servletContextURL = new File JavaDoc(path).toURL().toExternalForm();
338             }
339         } catch (MalformedURLException JavaDoc me) {
340             // VG: Novell has absolute file names starting with the
341
// volume name which is easily more then one letter.
342
// Examples: sys:/apache/cocoon or sys:\apache\cocoon
343
try {
344                 this.servletContextURL = new File JavaDoc(path).toURL().toExternalForm();
345             } catch (MalformedURLException JavaDoc ignored) {
346                 throw new ServletException JavaDoc("Unable to determine servlet context URL.", me);
347             }
348         }
349         try {
350             this.appContext.put("context-root", new URL JavaDoc(this.servletContextURL));
351         } catch (MalformedURLException JavaDoc ignore) {
352             // we simply ignore this
353
}
354
355         // Init logger
356
initLogger();
357
358         if (getLogger().isDebugEnabled()) {
359             getLogger().debug("getRealPath for /: " + this.servletContextPath);
360             if (this.servletContextPath == null) {
361                 getLogger().debug("getResource for /WEB-INF: " + debugPathOne);
362                 getLogger().debug("Path for Root: " + debugPathTwo);
363             }
364         }
365
366         this.forceLoadParameter = getInitParameter("load-class", null);
367         this.forceSystemProperty = getInitParameter("force-property", null);
368
369         // Output some debug info
370
if (getLogger().isDebugEnabled()) {
371             getLogger().debug("Servlet Context URL: " + this.servletContextURL);
372             if (workDirParam != null) {
373                 getLogger().debug("Using work-directory " + this.workDir);
374             } else {
375                 getLogger().debug("Using default work-directory " + this.workDir);
376             }
377         }
378
379         final String JavaDoc uploadDirParam = conf.getInitParameter("upload-directory");
380         if (uploadDirParam != null) {
381             if (this.servletContextPath == null) {
382                 this.uploadDir = new File JavaDoc(uploadDirParam);
383             } else {
384                 // Context path exists : is upload-directory absolute ?
385
File JavaDoc uploadDirParamFile = new File JavaDoc(uploadDirParam);
386                 if (uploadDirParamFile.isAbsolute()) {
387                     // Yes : keep it as is
388
this.uploadDir = uploadDirParamFile;
389                 } else {
390                     // No : consider it relative to context path
391
this.uploadDir = new File JavaDoc(servletContextPath, uploadDirParam);
392                 }
393             }
394             if (getLogger().isDebugEnabled()) {
395                 getLogger().debug("Using upload-directory " + this.uploadDir);
396             }
397         } else {
398             this.uploadDir = new File JavaDoc(workDir, "upload-dir" + File.separator);
399             if (getLogger().isDebugEnabled()) {
400                 getLogger().debug("Using default upload-directory " + this.uploadDir);
401             }
402         }
403         this.uploadDir.mkdirs();
404         this.appContext.put(Constants.CONTEXT_UPLOAD_DIR, this.uploadDir);
405
406         this.enableUploads = getInitParameterAsBoolean("enable-uploads", ENABLE_UPLOADS);
407
408         this.autoSaveUploads = getInitParameterAsBoolean("autosave-uploads", SAVE_UPLOADS_TO_DISK);
409
410         String JavaDoc overwriteParam = getInitParameter("overwrite-uploads", "rename");
411         // accepted values are deny|allow|rename - rename is default.
412
if ("deny".equalsIgnoreCase(overwriteParam)) {
413             this.allowOverwrite = false;
414             this.silentlyRename = false;
415         } else if ("allow".equalsIgnoreCase(overwriteParam)) {
416             this.allowOverwrite = true;
417             this.silentlyRename = false; // ignored in this case
418
} else {
419             // either rename is specified or unsupported value - default to rename.
420
this.allowOverwrite = false;
421             this.silentlyRename = true;
422         }
423
424         this.maxUploadSize = getInitParameterAsInteger("upload-max-size", MAX_UPLOAD_SIZE);
425
426         String JavaDoc cacheDirParam = conf.getInitParameter("cache-directory");
427         if (cacheDirParam != null) {
428             if (this.servletContextPath == null) {
429                 this.cacheDir = new File JavaDoc(cacheDirParam);
430             } else {
431                 // Context path exists : is cache-directory absolute ?
432
File JavaDoc cacheDirParamFile = new File JavaDoc(cacheDirParam);
433                 if (cacheDirParamFile.isAbsolute()) {
434                     // Yes : keep it as is
435
this.cacheDir = cacheDirParamFile;
436                 } else {
437                     // No : consider it relative to context path
438
this.cacheDir = new File JavaDoc(servletContextPath, cacheDirParam);
439                 }
440             }
441             if (getLogger().isDebugEnabled()) {
442                 getLogger().debug("Using cache-directory " + this.cacheDir);
443             }
444         } else {
445             this.cacheDir = IOUtils.createFile(workDir, "cache-dir" + File.separator);
446             if (getLogger().isDebugEnabled()) {
447                 getLogger().debug("cache-directory was not set - defaulting to " + this.cacheDir);
448             }
449         }
450         this.cacheDir.mkdirs();
451         this.appContext.put(Constants.CONTEXT_CACHE_DIR, this.cacheDir);
452
453         this.appContext.put(Constants.CONTEXT_CONFIG_URL,
454                             getConfigFile(conf.getInitParameter("configurations")));
455         if (conf.getInitParameter("configurations") == null) {
456             if (getLogger().isDebugEnabled()) {
457                 getLogger().debug("configurations was not set - defaulting to... ?");
458             }
459         }
460
461         // get allow reload parameter, default is true
462
this.allowReload = getInitParameterAsBoolean("allow-reload", ALLOW_RELOAD);
463
464         String JavaDoc value = conf.getInitParameter("show-time");
465         this.showTime = BooleanUtils.toBoolean(value) || (this.hiddenShowTime = "hide".equals(value));
466         if (value == null) {
467             if (getLogger().isDebugEnabled()) {
468                 getLogger().debug("show-time was not set - defaulting to false");
469             }
470         }
471
472         this.showCocoonVersion = getInitParameterAsBoolean("show-cocoon-version", true);
473
474         parentComponentManagerClass = getInitParameter("parent-component-manager", null);
475         if (parentComponentManagerClass != null) {
476             int dividerPos = parentComponentManagerClass.indexOf('/');
477             if (dividerPos != -1) {
478                 parentComponentManagerInitParam = parentComponentManagerClass.substring(dividerPos + 1);
479                 parentComponentManagerClass = parentComponentManagerClass.substring(0, dividerPos);
480             }
481         }
482
483         this.containerEncoding = getInitParameter("container-encoding", "ISO-8859-1");
484         this.defaultFormEncoding = getInitParameter("form-encoding", "ISO-8859-1");
485         this.appContext.put(Constants.CONTEXT_DEFAULT_ENCODING, this.defaultFormEncoding);
486
487         this.manageExceptions = getInitParameterAsBoolean("manage-exceptions", true);
488
489         this.enableInstrumentation = getInitParameterAsBoolean("enable-instrumentation", false);
490
491         this.requestFactory = new RequestFactory(this.autoSaveUploads,
492                                                  this.uploadDir,
493                                                  this.allowOverwrite,
494                                                  this.silentlyRename,
495                                                  this.maxUploadSize,
496                                                  this.containerEncoding);
497         // Add the servlet configuration
498
this.appContext.put(CONTEXT_SERVLET_CONFIG, conf);
499         this.createCocoon();
500     }
501
502     /**
503      * Dispose Cocoon when servlet is destroyed
504      */

505     public void destroy() {
506         if (this.initClassLoader) {
507             try {
508                 Thread.currentThread().setContextClassLoader(this.classLoader);
509             } catch (Exception JavaDoc e) {
510                 // Ignored
511
}
512         }
513
514         if (this.cocoon != null) {
515             if (getLogger().isDebugEnabled()) {
516                 getLogger().debug("Servlet destroyed - disposing Cocoon");
517             }
518             disposeCocoon();
519         }
520
521         if (this.instrumentManager instanceof Disposable) {
522             ((Disposable) this.instrumentManager).dispose();
523         }
524
525         if (this.parentComponentManager != null && this.parentComponentManager instanceof Disposable) {
526             ((Disposable) this.parentComponentManager).dispose();
527         }
528
529         this.appContext = null;
530         this.classLoader = null;
531         this.log = null;
532         this.loggerManager = null;
533     }
534
535     /**
536      * Adds an URL to the classloader. Does nothing here, but is
537      * overriden in {@link ParanoidCocoonServlet}.
538      */

539     protected void addClassLoaderURL(URL JavaDoc URL) {
540         // Nothing
541
}
542
543     /**
544      * Adds a directory to the classloader. Does nothing here, but is
545      * overriden in {@link ParanoidCocoonServlet}.
546      */

547     protected void addClassLoaderDirectory(String JavaDoc dir) {
548         // Nothing
549
}
550
551     /**
552      * This builds the important ClassPath used by this Servlet. It
553      * does so in a Servlet Engine neutral way. It uses the
554      * <code>ServletContext</code>'s <code>getRealPath</code> method
555      * to get the Servlet 2.2 identified classes and lib directories.
556      * It iterates in alphabetical order through every file in the
557      * lib directory and adds it to the classpath.
558      *
559      * Also, we add the files to the ClassLoader for the Cocoon system.
560      * In order to protect ourselves from skitzofrantic classloaders,
561      * we need to work with a known one.
562      *
563      * We need to get this to work properly when Cocoon is in a war.
564      *
565      * @throws ServletException
566      */

567     protected String JavaDoc getClassPath() throws ServletException JavaDoc {
568         StringBuffer JavaDoc buildClassPath = new StringBuffer JavaDoc();
569
570         File JavaDoc root = null;
571         if (servletContextPath != null) {
572             // Old method. There *MUST* be a better method than this...
573

574             String JavaDoc classDir = this.servletContext.getRealPath("/WEB-INF/classes");
575             String JavaDoc libDir = this.servletContext.getRealPath("/WEB-INF/lib");
576
577             if (libDir != null) {
578                 root = new File JavaDoc(libDir);
579             }
580
581             if (classDir != null) {
582                 buildClassPath.append(classDir);
583
584                 addClassLoaderDirectory(classDir);
585             }
586         } else {
587             // New(ish) method for war'd deployments
588
URL JavaDoc classDirURL = null;
589             URL JavaDoc libDirURL = null;
590
591             try {
592                 classDirURL = this.servletContext.getResource("/WEB-INF/classes");
593             } catch (MalformedURLException JavaDoc me) {
594                 if (getLogger().isWarnEnabled()) {
595                     this.getLogger().warn("Unable to add WEB-INF/classes to the classpath", me);
596                 }
597             }
598
599             try {
600                 libDirURL = this.servletContext.getResource("/WEB-INF/lib");
601             } catch (MalformedURLException JavaDoc me) {
602                 if (getLogger().isWarnEnabled()) {
603                     this.getLogger().warn("Unable to add WEB-INF/lib to the classpath", me);
604                 }
605             }
606
607             if (libDirURL != null && libDirURL.toExternalForm().startsWith("file:")) {
608                 root = new File JavaDoc(libDirURL.toExternalForm().substring("file:".length()));
609             }
610
611             if (classDirURL != null) {
612                 buildClassPath.append(classDirURL.toExternalForm());
613
614                 addClassLoaderURL(classDirURL);
615             }
616         }
617
618         // Unable to find lib directory. Going the hard way.
619
if (root == null) {
620             root = extractLibraries();
621         }
622
623         if (root != null && root.isDirectory()) {
624             File JavaDoc[] libraries = root.listFiles();
625             Arrays.sort(libraries);
626             for (int i = 0; i < libraries.length; i++) {
627                 String JavaDoc fullName = IOUtils.getFullFilename(libraries[i]);
628                 buildClassPath.append(File.pathSeparatorChar).append(fullName);
629
630                 addClassLoaderDirectory(fullName);
631             }
632         }
633
634         buildClassPath.append(File.pathSeparatorChar)
635                       .append(SystemUtils.JAVA_CLASS_PATH);
636
637         buildClassPath.append(File.pathSeparatorChar)
638                       .append(getExtraClassPath());
639         return buildClassPath.toString();
640     }
641
642     private File JavaDoc extractLibraries() {
643         try {
644             URL JavaDoc manifestURL = this.servletContext.getResource("/META-INF/MANIFEST.MF");
645             if (manifestURL == null) {
646                 this.getLogger().fatalError("Unable to get Manifest");
647                 return null;
648             }
649
650             Manifest JavaDoc mf = new Manifest JavaDoc(manifestURL.openStream());
651             Attributes JavaDoc attr = mf.getMainAttributes();
652             String JavaDoc libValue = attr.getValue("Cocoon-Libs");
653             if (libValue == null) {
654                 this.getLogger().fatalError("Unable to get 'Cocoon-Libs' attribute from the Manifest");
655                 return null;
656             }
657
658             List JavaDoc libList = new ArrayList JavaDoc();
659             for (StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(libValue, " "); st.hasMoreTokens();) {
660                 libList.add(st.nextToken());
661             }
662
663             File JavaDoc root = new File JavaDoc(this.workDir, "lib");
664             root.mkdirs();
665
666             File JavaDoc[] oldLibs = root.listFiles();
667             for (int i = 0; i < oldLibs.length; i++) {
668                 String JavaDoc oldLib = oldLibs[i].getName();
669                 if (!libList.contains(oldLib)) {
670                     this.getLogger().debug("Removing old library " + oldLibs[i]);
671                     oldLibs[i].delete();
672                 }
673             }
674
675             this.getLogger().warn("Extracting libraries into " + root);
676             byte[] buffer = new byte[65536];
677             for (Iterator JavaDoc i = libList.iterator(); i.hasNext();) {
678                 String JavaDoc libName = (String JavaDoc) i.next();
679
680                 long lastModified = -1;
681                 try {
682                     lastModified = Long.parseLong(attr.getValue("Cocoon-Lib-" + libName.replace('.', '_')));
683                 } catch (Exception JavaDoc e) {
684                     this.getLogger().debug("Failed to parse lastModified: " + attr.getValue("Cocoon-Lib-" + libName.replace('.', '_')));
685                 }
686
687                 File JavaDoc lib = new File JavaDoc(root, libName);
688                 if (lib.exists() && lib.lastModified() != lastModified) {
689                     this.getLogger().debug("Removing modified library " + lib);
690                     lib.delete();
691                 }
692
693                 InputStream JavaDoc is = this.servletContext.getResourceAsStream("/WEB-INF/lib/" + libName);
694                 if (is == null) {
695                     this.getLogger().warn("Skipping " + libName);
696                 } else {
697                     this.getLogger().debug("Extracting " + libName);
698                     OutputStream JavaDoc os = null;
699                     try {
700                         os = new FileOutputStream JavaDoc(lib);
701                         int count;
702                         while ((count = is.read(buffer)) > 0) {
703                             os.write(buffer, 0, count);
704                         }
705                     } finally {
706                         if (is != null) is.close();
707                         if (os != null) os.close();
708                     }
709                 }
710
711                 if (lastModified != -1) {
712                     lib.setLastModified(lastModified);
713                 }
714             }
715
716             return root;
717         } catch (IOException JavaDoc e) {
718             this.getLogger().fatalError("Exception while processing Manifest file", e);
719             return null;
720         }
721     }
722
723
724     /**
725      * Retreives the "extra-classpath" attribute, that needs to be
726      * added to the class path.
727      *
728      * @throws ServletException
729      */

730     protected String JavaDoc getExtraClassPath() throws ServletException JavaDoc {
731         String JavaDoc extraClassPath = this.getInitParameter("extra-classpath");
732         if (extraClassPath != null) {
733             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
734             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(extraClassPath, SystemUtils.PATH_SEPARATOR, false);
735             int i = 0;
736             while (st.hasMoreTokens()) {
737                 String JavaDoc s = st.nextToken();
738                 if (i++ > 0) {
739                     sb.append(File.pathSeparatorChar);
740                 }
741                 if ((s.charAt(0) == File.separatorChar) ||
742                         (s.charAt(1) == ':')) {
743                     if (getLogger().isDebugEnabled()) {
744                         getLogger().debug("extraClassPath is absolute: " + s);
745                     }
746                     sb.append(s);
747
748                     addClassLoaderDirectory(s);
749                 } else {
750                     if (s.indexOf("${") != -1) {
751                         String JavaDoc path = StringUtils.replaceToken(s);
752                         sb.append(path);
753                         if (getLogger().isDebugEnabled()) {
754                             getLogger().debug("extraClassPath is not absolute replacing using token: [" + s + "] : " + path);
755                         }
756                         addClassLoaderDirectory(path);
757                     } else {
758                         String JavaDoc path = null;
759                         if (this.servletContextPath != null) {
760                             path = this.servletContextPath + s;
761                             if (getLogger().isDebugEnabled()) {
762                                 getLogger().debug("extraClassPath is not absolute pre-pending context path: " + path);
763                             }
764                         } else {
765                             path = this.workDir.toString() + s;
766                             if (getLogger().isDebugEnabled()) {
767                                 getLogger().debug("extraClassPath is not absolute pre-pending work-directory: " + path);
768                             }
769                         }
770                         sb.append(path);
771                         addClassLoaderDirectory(path);
772                     }
773                 }
774             }
775             return sb.toString();
776         }
777         return "";
778     }
779
780     /**
781      * Set up the log level and path. The default log level is
782      * Priority.ERROR, although it can be overwritten by the parameter
783      * "log-level". The log system goes to both a file and the Servlet
784      * container's log system. Only messages that are Priority.ERROR
785      * and above go to the servlet context. The log messages can
786      * be as restrictive (Priority.FATAL_ERROR and above) or as liberal
787      * (Priority.DEBUG and above) as you want that get routed to the
788      * file.
789      */

790     protected void initLogger() {
791         final String JavaDoc logLevel = getInitParameter("log-level", "INFO");
792
793         final String JavaDoc accesslogger = getInitParameter("servlet-logger", "cocoon");
794
795         final Priority logPriority = Priority.getPriorityForName(logLevel);
796
797         final CocoonLogFormatter formatter = new CocoonLogFormatter();
798         formatter.setFormat("%7.7{priority} %{time} [%8.8{category}] " +
799                             "(%{uri}) %{thread}/%{class:short}: %{message}\\n%{throwable}");
800         final ServletOutputLogTarget servTarget = new ServletOutputLogTarget(this.servletContext, formatter);
801
802         final Hierarchy defaultHierarchy = Hierarchy.getDefaultHierarchy();
803         final ErrorHandler errorHandler = new DefaultErrorHandler();
804         defaultHierarchy.setErrorHandler(errorHandler);
805         defaultHierarchy.setDefaultLogTarget(servTarget);
806         defaultHierarchy.setDefaultPriority(logPriority);
807         final Logger logger = new LogKitLogger(Hierarchy.getDefaultHierarchy().getLoggerFor(""));
808         final String JavaDoc loggerManagerClass =
809             this.getInitParameter("logger-class", LogKitLoggerManager.class.getName());
810
811         // the log4j support requires currently that the log4j system is already configured elsewhere
812

813         final LoggerManager loggerManager =
814                 newLoggerManager(loggerManagerClass, defaultHierarchy);
815         ContainerUtil.enableLogging(loggerManager, logger);
816
817         final DefaultContext subcontext = new DefaultContext(this.appContext);
818         subcontext.put("servlet-context", this.servletContext);
819         subcontext.put("context-work", this.workDir);
820         if (this.servletContextPath == null) {
821             File JavaDoc logSCDir = new File JavaDoc(this.workDir, "log");
822             logSCDir.mkdirs();
823             if (logger.isWarnEnabled()) {
824                 logger.warn("Setting context-root for LogKit to " + logSCDir);
825             }
826             subcontext.put("context-root", logSCDir.toString());
827         } else {
828             subcontext.put("context-root", this.servletContextPath);
829         }
830
831         try {
832             ContainerUtil.contextualize(loggerManager, subcontext);
833             this.loggerManager = loggerManager;
834
835             if (loggerManager instanceof Configurable) {
836                 //Configure the logkit management
837
String JavaDoc logkitConfig = getInitParameter("logkit-config", "/WEB-INF/logkit.xconf");
838
839                 // test if this is a qualified url
840
InputStream JavaDoc is = null;
841                 if (logkitConfig.indexOf(':') == -1) {
842                     is = this.servletContext.getResourceAsStream(logkitConfig);
843                     if (is == null) is = new FileInputStream JavaDoc(logkitConfig);
844                 } else {
845                     URL JavaDoc logkitURL = new URL JavaDoc(logkitConfig);
846                     is = logkitURL.openStream();
847                 }
848                 final DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
849                 final Configuration conf = builder.build(is);
850                 ContainerUtil.configure(loggerManager, conf);
851             }
852
853             // let's configure log4j
854
final String JavaDoc log4jConfig = getInitParameter("log4j-config", null);
855             if ( log4jConfig != null ) {
856                 final Log4JConfigurator configurator = new Log4JConfigurator(subcontext);
857
858                 // test if this is a qualified url
859
InputStream JavaDoc is = null;
860                 if ( log4jConfig.indexOf(':') == -1) {
861                     is = this.servletContext.getResourceAsStream(log4jConfig);
862                     if (is == null) is = new FileInputStream JavaDoc(log4jConfig);
863                 } else {
864                     final URL JavaDoc log4jURL = new URL JavaDoc(log4jConfig);
865                     is = log4jURL.openStream();
866                 }
867                 configurator.doConfigure(is, LogManager.getLoggerRepository());
868             }
869
870             ContainerUtil.initialize(loggerManager);
871         } catch (Exception JavaDoc e) {
872             errorHandler.error("Could not set up Cocoon Logger, will use screen instead", e, null);
873         }
874
875         this.log = this.loggerManager.getLoggerForCategory(accesslogger);
876
877         final String JavaDoc deprecationLevel = getInitParameter("forbidden-deprecation-level", "ERROR");
878         Deprecation.setForbiddenLevel(Deprecation.LogLevel.getLevel(deprecationLevel));
879     }
880
881     private LoggerManager newLoggerManager(String JavaDoc loggerManagerClass, Hierarchy hierarchy) {
882         if (loggerManagerClass.equals(LogKitLoggerManager.class.getName())) {
883             return new LogKitLoggerManager(hierarchy);
884         } else if (loggerManagerClass.equals(Log4JLoggerManager.class.getName()) ||
885                    loggerManagerClass.equalsIgnoreCase("LOG4J")) {
886             return new Log4JLoggerManager();
887         } else {
888             try {
889                 Class JavaDoc clazz = Class.forName(loggerManagerClass);
890                 return (LoggerManager)clazz.newInstance();
891             } catch (Exception JavaDoc e) {
892                 return new LogKitLoggerManager(hierarchy);
893             }
894         }
895     }
896
897     /**
898      * Set the ConfigFile for the Cocoon object.
899      *
900      * @param configFileName The file location for the cocoon.xconf
901      *
902      * @throws ServletException
903      */

904     private URL JavaDoc getConfigFile(final String JavaDoc configFileName)
905     throws ServletException JavaDoc {
906         final String JavaDoc usedFileName;
907
908         if (configFileName == null) {
909             if (getLogger().isWarnEnabled()) {
910                 getLogger().warn("Servlet initialization argument 'configurations' not specified, attempting to use '/WEB-INF/cocoon.xconf'");
911             }
912             usedFileName = "/WEB-INF/cocoon.xconf";
913         } else {
914             usedFileName = configFileName;
915         }
916
917         if (getLogger().isDebugEnabled()) {
918             getLogger().debug("Using configuration file: " + usedFileName);
919         }
920
921         URL JavaDoc result;
922         try {
923             // test if this is a qualified url
924
if (usedFileName.indexOf(':') == -1) {
925                 result = this.servletContext.getResource(usedFileName);
926             } else {
927                 result = new URL JavaDoc(usedFileName);
928             }
929         } catch (Exception JavaDoc mue) {
930             String JavaDoc msg = "Init parameter 'configurations' is invalid : " + usedFileName;
931             getLogger().error(msg, mue);
932             throw new ServletException JavaDoc(msg, mue);
933         }
934
935         if (result == null) {
936             File JavaDoc resultFile = new File JavaDoc(usedFileName);
937             if (resultFile.isFile()) {
938                 try {
939                     result = resultFile.getCanonicalFile().toURL();
940                 } catch (Exception JavaDoc e) {
941                     String JavaDoc msg = "Init parameter 'configurations' is invalid : " + usedFileName;
942                     getLogger().error(msg, e);
943                     throw new ServletException JavaDoc(msg, e);
944                 }
945             }
946         }
947
948         if (result == null) {
949             String JavaDoc msg = "Init parameter 'configuration' doesn't name an existing resource : " + usedFileName;
950             getLogger().error(msg);
951             throw new ServletException JavaDoc(msg);
952         }
953         return result;
954     }
955
956     /**
957      * Handle the <code>load-class</code> parameter. This overcomes
958      * limits in many classpath issues. One of the more notorious
959      * ones is a bug in WebSphere that does not load the URL handler
960      * for the <code>classloader://</code> protocol. In order to
961      * overcome that bug, set <code>load-class</code> parameter to
962      * the <code>com.ibm.servlet.classloader.Handler</code> value.
963      *
964      * <p>If you need to load more than one class, then separate each
965      * entry with whitespace, a comma, or a semi-colon. Cocoon will
966      * strip any whitespace from the entry.</p>
967      */

968     private void forceLoad() {
969         if (this.forceLoadParameter != null) {
970             StringTokenizer JavaDoc fqcnTokenizer = new StringTokenizer JavaDoc(forceLoadParameter, " \t\r\n\f;,", false);
971
972             while (fqcnTokenizer.hasMoreTokens()) {
973                 final String JavaDoc fqcn = fqcnTokenizer.nextToken().trim();
974
975                 try {
976                     if (getLogger().isDebugEnabled()) {
977                         getLogger().debug("Loading: " + fqcn);
978                     }
979                     ClassUtils.loadClass(fqcn).newInstance();
980                 } catch (Exception JavaDoc e) {
981                     if (getLogger().isWarnEnabled()) {
982                         getLogger().warn("Could not load class: " + fqcn, e);
983                     }
984                     // Do not throw an exception, because it is not a fatal error.
985
}
986             }
987         }
988     }
989
990     /**
991      * Handle the "force-property" parameter.
992      *
993      * If you need to force more than one property to load, then
994      * separate each entry with whitespace, a comma, or a semi-colon.
995      * Cocoon will strip any whitespace from the entry.
996      */

997     private void forceProperty() {
998         if (this.forceSystemProperty != null) {
999             StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(forceSystemProperty, " \t\r\n\f;,", false);
1000
1001            while (tokenizer.hasMoreTokens()) {
1002                final String JavaDoc property = tokenizer.nextToken().trim();
1003                if (property.indexOf('=') == -1) {
1004                    continue;
1005                }
1006                try {
1007                    String JavaDoc key = property.substring(0, property.indexOf('='));
1008                    String JavaDoc value = property.substring(property.indexOf('=') + 1);
1009                    if (value.indexOf("${") != -1) {
1010                        value = StringUtils.replaceToken(value);
1011                    }
1012                    if (getLogger().isDebugEnabled()) {
1013                        getLogger().debug("Setting " + key + "=" + value);
1014                    }
1015                    System.setProperty(key, value);
1016                } catch (Exception JavaDoc e) {
1017                    if (getLogger().isWarnEnabled()) {
1018                        getLogger().warn("Could not set property: " + property, e);
1019                    }
1020                    // Do not throw an exception, because it is not a fatal error.
1021
}
1022            }
1023        }
1024    }
1025
1026    /**
1027     * Process the specified <code>HttpServletRequest</code> producing output
1028     * on the specified <code>HttpServletResponse</code>.
1029     */

1030    public void service(HttpServletRequest req, HttpServletResponse JavaDoc res)
1031    throws ServletException JavaDoc, IOException JavaDoc {
1032
1033        /* HACK for reducing class loader problems. */
1034        /* example: xalan extensions fail if someone adds xalan jars in tomcat3.2.1/lib */
1035        if (this.initClassLoader) {
1036            try {
1037                Thread.currentThread().setContextClassLoader(this.classLoader);
1038            } catch (Exception JavaDoc e) {
1039            }
1040        }
1041
1042        // used for timing the processing
1043
StopWatch stopWatch = new StopWatch();
1044        stopWatch.start();
1045
1046        // add the cocoon header timestamp
1047
if (this.showCocoonVersion) {
1048            res.addHeader("X-Cocoon-Version", Constants.VERSION);
1049        }
1050
1051        // get the request (wrapped if contains multipart-form data)
1052
HttpServletRequest request;
1053        try{
1054            if (this.enableUploads) {
1055                request = requestFactory.getServletRequest(req);
1056            } else {
1057                request = req;
1058            }
1059        } catch (Exception JavaDoc e) {
1060            if (getLogger().isErrorEnabled()) {
1061                getLogger().error("Problem with Cocoon servlet", e);
1062            }
1063
1064            manageException(req, res, null, null,
1065                            HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1066                            "Problem in creating the Request", null, null, e);
1067            return;
1068        }
1069
1070        // Get the cocoon engine instance
1071

1072        if (reloadCocoon(request.getPathInfo(), request.getParameter(Constants.RELOAD_PARAM))) {
1073            disposeCocoon();
1074            initLogger();
1075            createCocoon();
1076        }
1077
1078        // Check if cocoon was initialized
1079
if (this.cocoon == null) {
1080            manageException(request, res, null, null,
1081                            HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1082                            "Initialization Problem",
1083                            null /* "Cocoon was not initialized" */,
1084                            null /* "Cocoon was not initialized, cannot process request" */,
1085                            this.exception);
1086            return;
1087        }
1088
1089        // We got it... Process the request
1090
String JavaDoc uri = request.getServletPath();
1091        if (uri == null) {
1092            uri = "";
1093        }
1094        String JavaDoc pathInfo = request.getPathInfo();
1095        if (pathInfo != null) {
1096            // VG: WebLogic fix: Both uri and pathInfo starts with '/'
1097
// This problem exists only in WL6.1sp2, not in WL6.0sp2 or WL7.0b.
1098
if (uri.length() > 0 && uri.charAt(0) == '/') {
1099                uri = uri.substring(1);
1100            }
1101            uri += pathInfo;
1102        }
1103
1104        if (uri.length() == 0) {
1105            /* empty relative URI
1106                 -> HTTP-redirect from /cocoon to /cocoon/ to avoid
1107                    StringIndexOutOfBoundsException when calling
1108                    "".charAt(0)
1109               else process URI normally
1110            */

1111            String JavaDoc prefix = request.getRequestURI();
1112            if (prefix == null) {
1113                prefix = "";
1114            }
1115
1116            res.sendRedirect(res.encodeRedirectURL(prefix + "/"));
1117            return;
1118        }
1119
1120        String JavaDoc contentType = null;
1121        ContextMap ctxMap = null;
1122
1123        Environment env;
1124        try{
1125            if (uri.charAt(0) == '/') {
1126                uri = uri.substring(1);
1127            }
1128            // Pass uri into environment without URLDecoding, as it is already decoded.
1129
env = getEnvironment(uri, request, res);
1130        } catch (Exception JavaDoc e) {
1131            if (getLogger().isErrorEnabled()) {
1132                getLogger().error("Problem with Cocoon servlet", e);
1133            }
1134
1135            manageException(request, res, null, uri,
1136                            HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1137                            "Problem in creating the Environment", null, null, e);
1138            return;
1139        }
1140
1141        try {
1142            try {
1143                // Initialize a fresh log context containing the object model: it
1144
// will be used by the CocoonLogFormatter
1145
ctxMap = ContextMap.getCurrentContext();
1146                // Add thread name (default content for empty context)
1147
String JavaDoc threadName = Thread.currentThread().getName();
1148                ctxMap.set("threadName", threadName);
1149                // Add the object model
1150
ctxMap.set("objectModel", env.getObjectModel());
1151                // Add a unique request id (threadName + currentTime
1152
ctxMap.set("request-id", threadName + System.currentTimeMillis());
1153
1154                if (this.cocoon.process(env)) {
1155                    contentType = env.getContentType();
1156                } else {
1157                    // We reach this when there is nothing in the processing change that matches
1158
// the request. For example, no matcher matches.
1159
getLogger().fatalError("The Cocoon engine failed to process the request.");
1160                    manageException(request, res, env, uri,
1161                                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1162                                    "Request Processing Failed",
1163                                    "Cocoon engine failed in process the request",
1164                                    "The processing engine failed to process the request. This could be due to lack of matching or bugs in the pipeline engine.",
1165                                    null);
1166                    return;
1167                }
1168            } catch (ResourceNotFoundException e) {
1169                if (getLogger().isDebugEnabled()) {
1170                    getLogger().warn(e.getMessage(), e);
1171                } else if (getLogger().isWarnEnabled()) {
1172                    getLogger().warn(e.getMessage());
1173                }
1174
1175                manageException(request, res, env, uri,
1176                                HttpServletResponse.SC_NOT_FOUND,
1177                                "Resource Not Found",
1178                                "Resource Not Found",
1179                                "The requested resource \"" + request.getRequestURI() + "\" could not be found",
1180                                e);
1181                return;
1182
1183            } catch (ConnectionResetException e) {
1184                if (getLogger().isDebugEnabled()) {
1185                    getLogger().debug(e.toString(), e);
1186                } else if (getLogger().isWarnEnabled()) {
1187                    getLogger().warn(e.toString());
1188                }
1189
1190            } catch (IOException JavaDoc e) {
1191                // Tomcat5 wraps SocketException into ClientAbortException which extends IOException.
1192
if (getLogger().isDebugEnabled()) {
1193                    getLogger().debug(e.toString(), e);
1194                } else if (getLogger().isWarnEnabled()) {
1195                    getLogger().warn(e.toString());
1196                }
1197
1198            } catch (Exception JavaDoc e) {
1199                if (getLogger().isErrorEnabled()) {
1200                    getLogger().error("Internal Cocoon Problem", e);
1201                }
1202
1203                manageException(request, res, env, uri,
1204                                HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1205                                "Internal Server Error", null, null, e);
1206                return;
1207            }
1208
1209            stopWatch.stop();
1210            String JavaDoc timeString = null;
1211            if (getLogger().isInfoEnabled()) {
1212                timeString = processTime(stopWatch.getTime());
1213                getLogger().info("'" + uri + "' " + timeString);
1214            }
1215
1216            if (contentType != null && contentType.equals("text/html")) {
1217                String JavaDoc showTime = request.getParameter(Constants.SHOWTIME_PARAM);
1218                boolean show = this.showTime;
1219                if (showTime != null) {
1220                    show = !showTime.equalsIgnoreCase("no");
1221                }
1222                if (show) {
1223                    if (timeString == null) {
1224                        timeString = processTime(stopWatch.getTime());
1225                    }
1226                    boolean hide = this.hiddenShowTime;
1227                    if (showTime != null) {
1228                        hide = showTime.equalsIgnoreCase("hide");
1229                    }
1230                    ServletOutputStream JavaDoc out = res.getOutputStream();
1231                    out.print((hide) ? "<!-- " : "<p>");
1232                    out.print(timeString);
1233                    out.println((hide) ? " -->" : "</p>");
1234                }
1235            }
1236        } finally {
1237            if (ctxMap != null) {
1238                ctxMap.clear();
1239            }
1240
1241            try {
1242                if (request instanceof MultipartHttpServletRequest) {
1243                    if (getLogger().isDebugEnabled()) {
1244                        getLogger().debug("Deleting uploaded file(s).");
1245                    }
1246                    ((MultipartHttpServletRequest) request).cleanup();
1247                }
1248            } catch (IOException JavaDoc e) {
1249                getLogger().error("Cocoon got an Exception while trying to cleanup the uploaded files.", e);
1250            }
1251
1252            /*
1253             * Servlet Specification 2.2, 6.5 Closure of Response Object:
1254             *
1255             * A number of events can indicate that the servlet has provided all of the
1256             * content to satisfy the request and that the response object can be
1257             * considered to be closed. The events are:
1258             * o The termination of the service method of the servlet.
1259             * o When the amount of content specified in the setContentLength method
1260             * of the response has been written to the response.
1261             * o The sendError method is called.
1262             * o The sendRedirect method is called.
1263             * When a response is closed, all content in the response buffer, if any remains,
1264             * must be immediately flushed to the client.
1265             *
1266             * Due to the above, out.flush() and out.close() are not necessary, and sometimes
1267             * (if sendError or sendRedirect were used) request may be already closed.
1268             */

1269        }
1270    }
1271
1272    protected void manageException(HttpServletRequest req, HttpServletResponse JavaDoc res, Environment env,
1273                                   String JavaDoc uri, int errorStatus,
1274                                   String JavaDoc title, String JavaDoc message, String JavaDoc description,
1275                                   Exception JavaDoc e)
1276            throws IOException JavaDoc {
1277        if (this.manageExceptions) {
1278            if (env != null) {
1279                env.tryResetResponse();
1280            } else {
1281                res.reset();
1282            }
1283
1284            String JavaDoc type = Notifying.FATAL_NOTIFICATION;
1285            HashMap JavaDoc extraDescriptions = null;
1286
1287            if (errorStatus == HttpServletResponse.SC_NOT_FOUND) {
1288                type = "resource-not-found";
1289                // Do not show the exception stacktrace for such common errors.
1290
e = null;
1291            } else {
1292                extraDescriptions = new HashMap JavaDoc(2);
1293                extraDescriptions.put(Notifying.EXTRA_REQUESTURI, req.getRequestURI());
1294                if (uri != null) {
1295                     extraDescriptions.put("Request URI", uri);
1296                }
1297
1298                // Do not show exception stack trace when log level is WARN or above. Show only message.
1299
if (!getLogger().isInfoEnabled()) {
1300                    Throwable JavaDoc t = DefaultNotifyingBuilder.getRootCause(e);
1301                    if (t != null) extraDescriptions.put(Notifying.EXTRA_CAUSE, t.getMessage());
1302                    e = null;
1303                }
1304            }
1305
1306            Notifying n = new DefaultNotifyingBuilder().build(this,
1307                                                              e,
1308                                                              type,
1309                                                              title,
1310                                                              "Cocoon Servlet",
1311                                                              message,
1312                                                              description,
1313                                                              extraDescriptions);
1314
1315            res.setContentType("text/html");
1316            res.setStatus(errorStatus);
1317            Notifier.notify(n, res.getOutputStream(), "text/html");
1318        } else {
1319            res.sendError(errorStatus, title);
1320            res.flushBuffer();
1321        }
1322    }
1323
1324    /**
1325     * Create the environment for the request
1326     */

1327    protected Environment getEnvironment(String JavaDoc uri,
1328                                         HttpServletRequest req,
1329                                         HttpServletResponse JavaDoc res)
1330    throws Exception JavaDoc {
1331        HttpEnvironment env;
1332
1333        String JavaDoc formEncoding = req.getParameter("cocoon-form-encoding");
1334        if (formEncoding == null) {
1335            formEncoding = this.defaultFormEncoding;
1336        }
1337        env = new HttpEnvironment(uri,
1338                                  this.servletContextURL,
1339                                  req,
1340                                  res,
1341                                  this.servletContext,
1342                                  (HttpContext) this.appContext.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT),
1343                                  this.containerEncoding,
1344                                  formEncoding);
1345        env.enableLogging(getLogger());
1346        return env;
1347    }
1348
1349    /**
1350     * Instatiates the parent component manager, as specified in the
1351     * parent-component-manager init parameter.
1352     *
1353     * If none is specified, the method returns <code>null</code>.
1354     *
1355     * @return the parent component manager, or <code>null</code>.
1356     */

1357    protected synchronized ComponentManager getParentComponentManager() {
1358        if (parentComponentManager != null && parentComponentManager instanceof Disposable) {
1359            ((Disposable) parentComponentManager).dispose();
1360        }
1361
1362        parentComponentManager = null;
1363        if (parentComponentManagerClass != null) {
1364            try {
1365                Class JavaDoc pcm = ClassUtils.loadClass(parentComponentManagerClass);
1366                Constructor JavaDoc pcmc = pcm.getConstructor(new Class JavaDoc[]{String JavaDoc.class});
1367                parentComponentManager = (ComponentManager) pcmc.newInstance(new Object JavaDoc[]{parentComponentManagerInitParam});
1368
1369                if (parentComponentManager instanceof LogEnabled) {
1370                    ((LogEnabled) parentComponentManager).enableLogging(getLogger());
1371                }
1372                if (parentComponentManager instanceof Contextualizable) {
1373                    ((Contextualizable) parentComponentManager).contextualize(this.appContext);
1374                }
1375                if (parentComponentManager instanceof Initializable) {
1376                    ((Initializable) parentComponentManager).initialize();
1377                }
1378            } catch (Exception JavaDoc e) {
1379                if (getLogger().isErrorEnabled()) {
1380                    getLogger().error("Could not initialize parent component manager.", e);
1381                }
1382            }
1383        }
1384        return parentComponentManager;
1385    }
1386
1387    /**
1388     * Creates the Cocoon object and handles exception handling.
1389     */

1390    protected synchronized void createCocoon()
1391    throws ServletException JavaDoc {
1392
1393        // Recheck that we need to create the cocoon object. It can have been created by
1394
// a concurrent invocation to this method.
1395
if (this.cocoon != null) {
1396            return;
1397        }
1398
1399        /* HACK for reducing class loader problems. */
1400        /* example: xalan extensions fail if someone adds xalan jars in tomcat3.2.1/lib */
1401        if (this.initClassLoader) {
1402            try {
1403                Thread.currentThread().setContextClassLoader(this.classLoader);
1404            } catch (Exception JavaDoc e) {
1405            }
1406        }
1407
1408        updateEnvironment();
1409        forceLoad();
1410        forceProperty();
1411
1412        try {
1413            this.exception = null;
1414            URL JavaDoc configFile = (URL JavaDoc) this.appContext.get(Constants.CONTEXT_CONFIG_URL);
1415            if (getLogger().isInfoEnabled()) {
1416                getLogger().info("Reloading from: " + configFile.toExternalForm());
1417            }
1418            Cocoon c = (Cocoon) ClassUtils.newInstance("org.apache.cocoon.Cocoon");
1419            ContainerUtil.enableLogging(c, getCocoonLogger());
1420            c.setLoggerManager(getLoggerManager());
1421            ContainerUtil.contextualize(c, this.appContext);
1422            final ComponentManager parent = this.getParentComponentManager();
1423            if (parent != null) {
1424                ContainerUtil.compose(c, parent);
1425            }
1426            if (this.enableInstrumentation) {
1427                c.setInstrumentManager(getInstrumentManager());
1428            }
1429            ContainerUtil.initialize(c);
1430            this.creationTime = System.currentTimeMillis();
1431
1432            this.cocoon = c;
1433        } catch (Exception JavaDoc e) {
1434            if (getLogger().isErrorEnabled()) {
1435                getLogger().error("Exception reloading", e);
1436            }
1437            this.exception = e;
1438            disposeCocoon();
1439        }
1440    }
1441
1442    private Logger getCocoonLogger() {
1443        final String JavaDoc rootlogger = getInitParameter("cocoon-logger");
1444        if (rootlogger != null) {
1445            return this.getLoggerManager().getLoggerForCategory(rootlogger);
1446        } else {
1447            return getLogger();
1448        }
1449    }
1450
1451    /**
1452     * Method to update the environment before Cocoon instances are created.
1453     *
1454     * This is also useful if you wish to customize any of the 'protected'
1455     * variables from this class before a Cocoon instance is built in a derivative
1456     * of this class (eg. Cocoon Context).
1457     */

1458    protected void updateEnvironment() throws ServletException JavaDoc {
1459        this.appContext.put(Constants.CONTEXT_CLASS_LOADER, classLoader);
1460        this.appContext.put(Constants.CONTEXT_CLASSPATH, getClassPath());
1461    }
1462
1463    /**
1464     * Helper method to obtain an <code>InstrumentManager</code> instance
1465     *
1466     * @return an <code>InstrumentManager</code> instance
1467     */

1468    private InstrumentManager getInstrumentManager()
1469    throws Exception JavaDoc {
1470        String JavaDoc imConfig = getInitParameter("instrumentation-config");
1471        if (imConfig == null) {
1472            throw new ServletException JavaDoc("Please define the init-param 'instrumentation-config' in your web.xml");
1473        }
1474
1475        final InputStream JavaDoc is = this.servletContext.getResourceAsStream(imConfig);
1476        final DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
1477        final Configuration conf = builder.build(is);
1478
1479        // Get the logger for the instrument manager
1480
final String JavaDoc imLoggerCategory = conf.getAttribute("logger", "core.instrument");
1481        Logger imLogger = this.loggerManager.getLoggerForCategory(imLoggerCategory);
1482
1483        // Set up the Instrument Manager
1484
DefaultInstrumentManagerImpl instrumentManager = new DefaultInstrumentManagerImpl();
1485        instrumentManager.enableLogging(imLogger);
1486        instrumentManager.configure(conf);
1487        instrumentManager.initialize();
1488
1489        if (getLogger().isDebugEnabled()) {
1490            getLogger().debug("Instrument manager created " + instrumentManager);
1491        }
1492
1493        this.instrumentManager = instrumentManager;
1494        return instrumentManager;
1495    }
1496
1497    private String JavaDoc processTime(long time) {
1498        StringBuffer JavaDoc out = new StringBuffer JavaDoc(PROCESSED_BY);
1499        if (time <= SECOND) {
1500            out.append(time);
1501            out.append(" milliseconds.");
1502        } else if (time <= MINUTE) {
1503            out.append(time / SECOND);
1504            out.append(" seconds.");
1505        } else if (time <= HOUR) {
1506            out.append(time / MINUTE);
1507            out.append(" minutes.");
1508        } else {
1509            out.append(time / HOUR);
1510            out.append(" hours.");
1511        }
1512        return out.toString();
1513    }
1514
1515    /**
1516     * Gets the current cocoon object. Reload cocoon if configuration
1517     * changed or we are reloading.
1518     */

1519    private boolean reloadCocoon(final String JavaDoc pathInfo, final String JavaDoc reloadParam)
1520    throws ServletException JavaDoc {
1521        if (this.allowReload) {
1522            boolean reload = false;
1523
1524            if (this.cocoon != null) {
1525                if (this.cocoon.modifiedSince(this.creationTime)) {
1526                    if (getLogger().isInfoEnabled()) {
1527                        getLogger().info("Configuration changed reload attempt");
1528                    }
1529                    reload = true;
1530                } else if (pathInfo == null && reloadParam != null) {
1531                    if (getLogger().isInfoEnabled()) {
1532                        getLogger().info("Forced reload attempt");
1533                    }
1534                    reload = true;
1535                }
1536            } else if (pathInfo == null && reloadParam != null) {
1537                if (getLogger().isInfoEnabled()) {
1538                    getLogger().info("Invalid configurations reload");
1539                }
1540                reload = true;
1541            }
1542
1543            return reload;
1544        } else {
1545            return false;
1546        }
1547    }
1548
1549    /**
1550     * Destroy Cocoon
1551     */

1552    protected final void disposeCocoon() {
1553        if (this.cocoon != null) {
1554            ContainerUtil.dispose(this.cocoon);
1555            this.cocoon = null;
1556        }
1557    }
1558
1559    /**
1560     * Get an initialisation parameter. The value is trimmed, and null is returned if the trimmed value
1561     * is empty.
1562     */

1563    public String JavaDoc getInitParameter(String JavaDoc name) {
1564        String JavaDoc result = super.getInitParameter(name);
1565        if (result != null) {
1566            result = result.trim();
1567            if (result.length() == 0) {
1568                result = null;
1569            }
1570        }
1571
1572        return result;
1573    }
1574
1575    /** Convenience method to access servlet parameters */
1576    protected String JavaDoc getInitParameter(String JavaDoc name, String JavaDoc defaultValue) {
1577        String JavaDoc result = getInitParameter(name);
1578        if (result == null) {
1579            if (getLogger() != null && getLogger().isDebugEnabled()) {
1580                getLogger().debug(name + " was not set - defaulting to '" + defaultValue + "'");
1581            }
1582            return defaultValue;
1583        } else {
1584            return result;
1585        }
1586    }
1587
1588    /** Convenience method to access boolean servlet parameters */
1589    protected boolean getInitParameterAsBoolean(String JavaDoc name, boolean defaultValue) {
1590        String JavaDoc value = getInitParameter(name);
1591        if (value == null) {
1592            if (getLogger() != null && getLogger().isDebugEnabled()) {
1593                getLogger().debug(name + " was not set - defaulting to '" + defaultValue + "'");
1594            }
1595            return defaultValue;
1596        }
1597
1598        return BooleanUtils.toBoolean(value);
1599    }
1600
1601    protected int getInitParameterAsInteger(String JavaDoc name, int defaultValue) {
1602        String JavaDoc value = getInitParameter(name);
1603        if (value == null) {
1604            if (getLogger() != null && getLogger().isDebugEnabled()) {
1605                getLogger().debug(name + " was not set - defaulting to '" + defaultValue + "'");
1606            }
1607            return defaultValue;
1608        } else {
1609            return Integer.parseInt(value);
1610        }
1611    }
1612
1613    protected Logger getLogger() {
1614        return this.log;
1615    }
1616
1617    protected LoggerManager getLoggerManager() {
1618        return this.loggerManager;
1619    }
1620}
1621
Popular Tags