KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > portlet > CocoonPortlet


1 /*
2  * Copyright 1999-2004 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.portlet;
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.portlet.PortletContext;
43 import org.apache.cocoon.environment.portlet.PortletEnvironment;
44 import org.apache.cocoon.portlet.multipart.MultipartActionRequest;
45 import org.apache.cocoon.portlet.multipart.RequestFactory;
46 import org.apache.cocoon.util.ClassUtils;
47 import org.apache.cocoon.util.IOUtils;
48 import org.apache.cocoon.util.StringUtils;
49 import org.apache.cocoon.util.log.CocoonLogFormatter;
50 import org.apache.cocoon.util.log.Log4JConfigurator;
51
52 import org.apache.commons.lang.BooleanUtils;
53 import org.apache.commons.lang.SystemUtils;
54 import org.apache.excalibur.instrument.InstrumentManager;
55 import org.apache.excalibur.instrument.manager.impl.DefaultInstrumentManagerImpl;
56 import org.apache.log.ContextMap;
57 import org.apache.log.ErrorHandler;
58 import org.apache.log.Hierarchy;
59 import org.apache.log.Priority;
60 import org.apache.log.util.DefaultErrorHandler;
61 import org.apache.log4j.LogManager;
62
63 import javax.portlet.ActionRequest;
64 import javax.portlet.ActionResponse;
65 import javax.portlet.GenericPortlet;
66 import javax.portlet.PortletConfig;
67 import javax.portlet.PortletException;
68 import javax.portlet.PortletSession;
69 import javax.portlet.RenderRequest;
70 import javax.portlet.RenderResponse;
71 import javax.portlet.PortletRequest;
72 import java.io.File JavaDoc;
73 import java.io.FileInputStream JavaDoc;
74 import java.io.FileOutputStream JavaDoc;
75 import java.io.IOException JavaDoc;
76 import java.io.InputStream JavaDoc;
77 import java.io.OutputStream JavaDoc;
78 import java.io.PrintStream 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 JSR-168 Portlet.
94  *
95  * @version CVS $Id: CocoonPortlet.java 325950 2005-10-17 18:26:07Z vgritsenko $
96  */

97 public class CocoonPortlet extends GenericPortlet {
98
99     /**
100      * Application <code>Context</code> Key for the portlet configuration
101      * @since 2.1.3
102      */

103     public static final String JavaDoc CONTEXT_PORTLET_CONFIG = "portlet-config";
104
105     // Processing time message
106
protected static final String JavaDoc PROCESSED_BY = "Processed by "
107             + Constants.COMPLETE_NAME + " in ";
108
109     // Used by "show-time"
110
static final float SECOND = 1000;
111     static final float MINUTE = 60 * SECOND;
112     static final float HOUR = 60 * MINUTE;
113
114     private Logger log;
115     private LoggerManager loggerManager;
116
117     /**
118      * The time the cocoon instance was created
119      */

120     protected long creationTime = 0;
121
122     /**
123      * The <code>Cocoon</code> instance
124      */

125     protected Cocoon cocoon;
126
127     /**
128      * Holds exception happened during initialization (if any)
129      */

130     protected Exception JavaDoc exception;
131
132     /**
133      * Avalon application context
134      */

135     protected DefaultContext appContext = new DefaultContext();
136
137
138     /**
139      * Default value for {@link #allowReload} parameter (false)
140      */

141     protected static final boolean ALLOW_RELOAD = false;
142
143     /**
144      * Allow reloading of cocoon by specifying the <code>cocoon-reload=true</code> parameter with a request
145      */

146     protected boolean allowReload;
147
148
149     /**
150      * Allow adding processing time to the response
151      */

152     protected boolean showTime;
153
154     /**
155      * If true, processing time will be added as an HTML comment
156      */

157     protected boolean hiddenShowTime;
158
159
160     /**
161      * Default value for {@link #enableUploads} parameter (false)
162      */

163     private static final boolean ENABLE_UPLOADS = false;
164     private static final boolean SAVE_UPLOADS_TO_DISK = true;
165     private static final int MAX_UPLOAD_SIZE = 10000000; // 10Mb
166

167     /**
168      * Allow processing of upload requests (mime/multipart)
169      */

170     private boolean enableUploads;
171     private boolean autoSaveUploads;
172     private boolean allowOverwrite;
173     private boolean silentlyRename;
174     private int maxUploadSize;
175
176     private File JavaDoc uploadDir;
177     private File JavaDoc workDir;
178     private File JavaDoc cacheDir;
179     private String JavaDoc containerEncoding;
180     private String JavaDoc defaultFormEncoding;
181
182     protected javax.portlet.PortletContext portletContext;
183
184     /** The classloader that will be set as the context classloader if init-classloader is true */
185     protected ClassLoader JavaDoc classLoader = this.getClass().getClassLoader();
186     protected boolean initClassLoader = false;
187
188     private String JavaDoc parentComponentManagerClass;
189     private String JavaDoc parentComponentManagerInitParam;
190
191     /** The parent ComponentManager, if any. Stored here in order to be able to dispose it in destroy(). */
192     private ComponentManager parentComponentManager;
193
194     protected String JavaDoc forceLoadParameter;
195     protected String JavaDoc forceSystemProperty;
196
197     /**
198      * If true or not set, this class will try to catch and handle all Cocoon exceptions.
199      * If false, it will rethrow them to the portlet container.
200      */

201     private boolean manageExceptions;
202
203     /**
204      * Flag to enable avalon excalibur instrumentation of Cocoon.
205      */

206     private boolean enableInstrumentation;
207
208     /**
209      * The <code>InstrumentManager</code> instance
210      */

211     private InstrumentManager instrumentManager;
212
213     /**
214      * This is the path to the portlet context (or the result
215      * of calling getRealPath('/') on the PortletContext.
216      * Note, that this can be null.
217      */

218     protected String JavaDoc portletContextPath;
219
220     /**
221      * This is the url to the portlet context directory
222      */

223     protected String JavaDoc portletContextURL;
224
225     /**
226      * The RequestFactory is responsible for wrapping multipart-encoded
227      * forms and for handing the file payload of incoming requests
228      */

229     protected RequestFactory requestFactory;
230
231     /**
232      * Value to be used as servletPath in the request.
233      * Provided via configuration parameter, if missing, defaults to the
234      * '/portlets/' + portletName.
235      */

236     protected String JavaDoc servletPath;
237
238     /**
239      * Default scope for the session attributes, either
240      * {@link javax.portlet.PortletSession#PORTLET_SCOPE} or
241      * {@link javax.portlet.PortletSession#APPLICATION_SCOPE}.
242      * This corresponds to <code>default-session-scope</code>
243      * parameter, with default value <code>portlet</code>.
244      *
245      * @see org.apache.cocoon.environment.portlet.PortletSession
246      */

247     protected int defaultSessionScope;
248
249     /**
250      * Store pathInfo in session
251      */

252     protected boolean storeSessionPath;
253
254     /**
255      * Initialize this <code>CocoonPortlet</code> instance.
256      *
257      * <p>Uses the following parameters:
258      * portlet-logger
259      * enable-uploads
260      * autosave-uploads
261      * overwrite-uploads
262      * upload-max-size
263      * show-time
264      * container-encoding
265      * form-encoding
266      * manage-exceptions
267      * servlet-path
268      *
269      * @param conf The PortletConfig object from the portlet container.
270      * @throws PortletException
271      */

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

543     public void destroy() {
544         if (this.initClassLoader) {
545             try {
546                 Thread.currentThread().setContextClassLoader(this.classLoader);
547             } catch (Exception JavaDoc e) {
548             }
549         }
550
551         if (this.cocoon != null) {
552             if (getLogger().isDebugEnabled()) {
553                 getLogger().debug("Portlet destroyed - disposing Cocoon");
554             }
555             this.disposeCocoon();
556         }
557
558         if (this.instrumentManager instanceof Disposable) {
559             ((Disposable) this.instrumentManager).dispose();
560         }
561
562         if (this.parentComponentManager != null && this.parentComponentManager instanceof Disposable) {
563             ((Disposable) this.parentComponentManager).dispose();
564         }
565     }
566
567     /**
568      * Adds an URL to the classloader. Does nothing here, but can
569      * be overriden.
570      */

571     protected void addClassLoaderURL(URL JavaDoc URL) {
572         // Nothing
573
}
574
575     /**
576      * Adds a directory to the classloader. Does nothing here, but can
577      * be overriden.
578      */

579     protected void addClassLoaderDirectory(String JavaDoc dir) {
580         // Nothing
581
}
582
583     /**
584      * This builds the important ClassPath used by this Portlet. It
585      * does so in a Portlet Engine neutral way. It uses the
586      * <code>PortletContext</code>'s <code>getRealPath</code> method
587      * to get the Portlet identified classes and lib directories.
588      * It iterates in alphabetical order through every file in the
589      * lib directory and adds it to the classpath.
590      *
591      * Also, we add the files to the ClassLoader for the Cocoon system.
592      * In order to protect ourselves from skitzofrantic classloaders,
593      * we need to work with a known one.
594      *
595      * We need to get this to work properly when Cocoon is in a war.
596      *
597      * @throws PortletException
598      */

599     protected String JavaDoc getClassPath() throws PortletException {
600         StringBuffer JavaDoc buildClassPath = new StringBuffer JavaDoc();
601
602         File JavaDoc root = null;
603         if (portletContextPath != null) {
604             // Old method. There *MUST* be a better method than this...
605

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

762     protected String JavaDoc getExtraClassPath() throws PortletException {
763         String JavaDoc extraClassPath = this.getInitParameter("extra-classpath");
764         if (extraClassPath != null) {
765             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
766             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(extraClassPath, SystemUtils.PATH_SEPARATOR, false);
767             int i = 0;
768             while (st.hasMoreTokens()) {
769                 String JavaDoc s = st.nextToken();
770                 if (i++ > 0) {
771                     sb.append(File.pathSeparatorChar);
772                 }
773                 if ((s.charAt(0) == File.separatorChar) ||
774                         (s.charAt(1) == ':')) {
775                     if (getLogger().isDebugEnabled()) {
776                         getLogger().debug("extraClassPath is absolute: " + s);
777                     }
778                     sb.append(s);
779
780                     addClassLoaderDirectory(s);
781                 } else {
782                     if (s.indexOf("${") != -1) {
783                         String JavaDoc path = StringUtils.replaceToken(s);
784                         sb.append(path);
785                         if (getLogger().isDebugEnabled()) {
786                             getLogger().debug("extraClassPath is not absolute replacing using token: [" + s + "] : " + path);
787                         }
788                         addClassLoaderDirectory(path);
789                     } else {
790                         String JavaDoc path = null;
791                         if (this.portletContextPath != null) {
792                             path = this.portletContextPath + s;
793                             if (getLogger().isDebugEnabled()) {
794                                 getLogger().debug("extraClassPath is not absolute pre-pending context path: " + path);
795                             }
796                         } else {
797                             path = this.workDir.toString() + s;
798                             if (getLogger().isDebugEnabled()) {
799                                 getLogger().debug("extraClassPath is not absolute pre-pending work-directory: " + path);
800                             }
801                         }
802                         sb.append(path);
803                         addClassLoaderDirectory(path);
804                     }
805                 }
806             }
807             return sb.toString();
808         }
809         return "";
810     }
811
812     /**
813      * Set up the log level and path. The default log level is
814      * Priority.ERROR, although it can be overwritten by the parameter
815      * "log-level". The log system goes to both a file and the Portlet
816      * container's log system. Only messages that are Priority.ERROR
817      * and above go to the portlet context. The log messages can
818      * be as restrictive (Priority.FATAL_ERROR and above) or as liberal
819      * (Priority.DEBUG and above) as you want that get routed to the
820      * file.
821      */

822     protected void initLogger() {
823         final String JavaDoc logLevel = getInitParameter("log-level", "INFO");
824
825         final String JavaDoc accesslogger = getInitParameter("portlet-logger", "cocoon");
826
827         final Priority logPriority = Priority.getPriorityForName(logLevel);
828
829         final CocoonLogFormatter formatter = new CocoonLogFormatter();
830         formatter.setFormat("%7.7{priority} %{time} [%8.8{category}] " +
831                             "(%{uri}) %{thread}/%{class:short}: %{message}\\n%{throwable}");
832         final PortletOutputLogTarget servTarget = new PortletOutputLogTarget(this.portletContext, formatter);
833
834         final Hierarchy defaultHierarchy = Hierarchy.getDefaultHierarchy();
835         final ErrorHandler errorHandler = new DefaultErrorHandler();
836         defaultHierarchy.setErrorHandler(errorHandler);
837         defaultHierarchy.setDefaultLogTarget(servTarget);
838         defaultHierarchy.setDefaultPriority(logPriority);
839         final Logger logger = new LogKitLogger(Hierarchy.getDefaultHierarchy().getLoggerFor(""));
840         final String JavaDoc loggerManagerClass =
841             this.getInitParameter("logger-class", LogKitLoggerManager.class.getName());
842
843         // the log4j support requires currently that the log4j system is already configured elsewhere
844

845         final LoggerManager loggerManager =
846                 newLoggerManager(loggerManagerClass, defaultHierarchy);
847         ContainerUtil.enableLogging(loggerManager, logger);
848
849         final DefaultContext subcontext = new DefaultContext(this.appContext);
850         subcontext.put("portlet-context", this.portletContext);
851         if (this.portletContextPath == null) {
852             File JavaDoc logSCDir = new File JavaDoc(this.workDir, "log");
853             logSCDir.mkdirs();
854             if (getLogger().isWarnEnabled()) {
855                 getLogger().warn("Setting context-root for LogKit to " + logSCDir);
856             }
857             subcontext.put("context-root", logSCDir.toString());
858         } else {
859             subcontext.put("context-root", this.portletContextPath);
860         }
861
862         try {
863             ContainerUtil.contextualize(loggerManager, subcontext);
864             this.loggerManager = loggerManager;
865
866             if (loggerManager instanceof Configurable) {
867             //Configure the logkit management
868
String JavaDoc logkitConfig = getInitParameter("logkit-config", "/WEB-INF/logkit.xconf");
869
870             // test if this is a qualified url
871
InputStream JavaDoc is = null;
872             if (logkitConfig.indexOf(':') == -1) {
873                 is = this.portletContext.getResourceAsStream(logkitConfig);
874                 if (is == null) is = new FileInputStream JavaDoc(logkitConfig);
875             } else {
876                 URL JavaDoc logkitURL = new URL JavaDoc(logkitConfig);
877                 is = logkitURL.openStream();
878             }
879             final DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
880             final Configuration conf = builder.build(is);
881                 ContainerUtil.configure(loggerManager, conf);
882             }
883
884             // let's configure log4j
885
final String JavaDoc log4jConfig = getInitParameter("log4j-config", null);
886             if ( log4jConfig != null ) {
887                 final Log4JConfigurator configurator = new Log4JConfigurator(subcontext);
888
889                 // test if this is a qualified url
890
InputStream JavaDoc is = null;
891                 if ( log4jConfig.indexOf(':') == -1) {
892                     is = this.portletContext.getResourceAsStream(log4jConfig);
893                     if (is == null) is = new FileInputStream JavaDoc(log4jConfig);
894                 } else {
895                     final URL JavaDoc log4jURL = new URL JavaDoc(log4jConfig);
896                     is = log4jURL.openStream();
897                 }
898                 configurator.doConfigure(is, LogManager.getLoggerRepository());
899             }
900
901             ContainerUtil.initialize(loggerManager);
902         } catch (Exception JavaDoc e) {
903             errorHandler.error("Could not set up Cocoon Logger, will use screen instead", e, null);
904         }
905
906         this.log = this.loggerManager.getLoggerForCategory(accesslogger);
907     }
908
909     private LoggerManager newLoggerManager(String JavaDoc loggerManagerClass, Hierarchy hierarchy) {
910         if (loggerManagerClass.equals(LogKitLoggerManager.class.getName())) {
911             return new LogKitLoggerManager(hierarchy);
912         } else if (loggerManagerClass.equals(Log4JLoggerManager.class.getName()) ||
913                    loggerManagerClass.equalsIgnoreCase("LOG4J")) {
914             return new Log4JLoggerManager();
915         } else {
916             try {
917                 Class JavaDoc clazz = Class.forName(loggerManagerClass);
918                 return (LoggerManager)clazz.newInstance();
919             } catch (Exception JavaDoc e) {
920                 return new LogKitLoggerManager(hierarchy);
921             }
922         }
923     }
924
925     /**
926      * Set the ConfigFile for the Cocoon object.
927      *
928      * @param configFileName The file location for the cocoon.xconf
929      *
930      * @throws PortletException
931      */

932     private URL JavaDoc getConfigFile(final String JavaDoc configFileName)
933     throws PortletException {
934         final String JavaDoc usedFileName;
935
936         if (configFileName == null) {
937             if (getLogger().isWarnEnabled()) {
938                 getLogger().warn("Portlet initialization argument 'configurations' not specified, attempting to use '/WEB-INF/cocoon.xconf'");
939             }
940             usedFileName = "/WEB-INF/cocoon.xconf";
941         } else {
942             usedFileName = configFileName;
943         }
944
945         if (getLogger().isDebugEnabled()) {
946             getLogger().debug("Using configuration file: " + usedFileName);
947         }
948
949         URL JavaDoc result;
950         try {
951             // test if this is a qualified url
952
if (usedFileName.indexOf(':') == -1) {
953                 result = this.portletContext.getResource(usedFileName);
954             } else {
955                 result = new URL JavaDoc(usedFileName);
956             }
957         } catch (Exception JavaDoc mue) {
958             String JavaDoc msg = "Init parameter 'configurations' is invalid : " + usedFileName;
959             getLogger().error(msg, mue);
960             throw new PortletException(msg, mue);
961         }
962
963         if (result == null) {
964             File JavaDoc resultFile = new File JavaDoc(usedFileName);
965             if (resultFile.isFile()) {
966                 try {
967                     result = resultFile.getCanonicalFile().toURL();
968                 } catch (Exception JavaDoc e) {
969                     String JavaDoc msg = "Init parameter 'configurations' is invalid : " + usedFileName;
970                     getLogger().error(msg, e);
971                     throw new PortletException(msg, e);
972                 }
973             }
974         }
975
976         if (result == null) {
977             String JavaDoc msg = "Init parameter 'configuration' doesn't name an existing resource : " + usedFileName;
978             getLogger().error(msg);
979             throw new PortletException(msg);
980         }
981         return result;
982     }
983
984     /**
985      * Handle the <code>load-class</code> parameter. This overcomes
986      * limits in many classpath issues. One of the more notorious
987      * ones is a bug in WebSphere that does not load the URL handler
988      * for the <code>classloader://</code> protocol. In order to
989      * overcome that bug, set <code>load-class</code> parameter to
990      * the <code>com.ibm.servlet.classloader.Handler</code> value.
991      *
992      * <p>If you need to load more than one class, then separate each
993      * entry with whitespace, a comma, or a semi-colon. Cocoon will
994      * strip any whitespace from the entry.</p>
995      */

996     private void forceLoad() {
997         if (this.forceLoadParameter != null) {
998             StringTokenizer JavaDoc fqcnTokenizer = new StringTokenizer JavaDoc(forceLoadParameter, " \t\r\n\f;,", false);
999
1000            while (fqcnTokenizer.hasMoreTokens()) {
1001                final String JavaDoc fqcn = fqcnTokenizer.nextToken().trim();
1002
1003                try {
1004                    if (getLogger().isDebugEnabled()) {
1005                        getLogger().debug("Trying to load class: " + fqcn);
1006                    }
1007                    ClassUtils.loadClass(fqcn).newInstance();
1008                } catch (Exception JavaDoc e) {
1009                    if (getLogger().isWarnEnabled()) {
1010                        getLogger().warn("Could not force-load class: " + fqcn, e);
1011                    }
1012                    // Do not throw an exception, because it is not a fatal error.
1013
}
1014            }
1015        }
1016    }
1017
1018    /**
1019     * Handle the "force-property" parameter.
1020     *
1021     * If you need to force more than one property to load, then
1022     * separate each entry with whitespace, a comma, or a semi-colon.
1023     * Cocoon will strip any whitespace from the entry.
1024     */

1025    private void forceProperty() {
1026        if (this.forceSystemProperty != null) {
1027            StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(forceSystemProperty, " \t\r\n\f;,", false);
1028
1029            while (tokenizer.hasMoreTokens()) {
1030                final String JavaDoc property = tokenizer.nextToken().trim();
1031                if (property.indexOf('=') == -1) {
1032                    continue;
1033                }
1034                try {
1035                    String JavaDoc key = property.substring(0, property.indexOf('='));
1036                    String JavaDoc value = property.substring(property.indexOf('=') + 1);
1037                    if (value.indexOf("${") != -1) {
1038                        value = StringUtils.replaceToken(value);
1039                    }
1040                    if (getLogger().isDebugEnabled()) {
1041                        getLogger().debug("setting " + key + "=" + value);
1042                    }
1043                    System.setProperty(key, value);
1044                } catch (Exception JavaDoc e) {
1045                    if (getLogger().isWarnEnabled()) {
1046                        getLogger().warn("Could not set property: " + property, e);
1047                    }
1048                    // Do not throw an exception, because it is not a fatal error.
1049
}
1050            }
1051        }
1052    }
1053
1054    /**
1055     * Process the specified <code>ActionRequest</code> producing output
1056     * on the specified <code>ActionResponse</code>.
1057     */

1058    public void processAction(ActionRequest req, ActionResponse res)
1059    throws PortletException, IOException JavaDoc {
1060
1061        /* HACK for reducing class loader problems. */
1062        /* example: xalan extensions fail if someone adds xalan jars in tomcat3.2.1/lib */
1063        if (this.initClassLoader) {
1064            try {
1065                Thread.currentThread().setContextClassLoader(this.classLoader);
1066            } catch (Exception JavaDoc e) {
1067            }
1068        }
1069
1070        // remember when we started (used for timing the processing)
1071
long start = System.currentTimeMillis();
1072
1073        // add the cocoon header timestamp
1074
res.setProperty("X-Cocoon-Version", Constants.VERSION);
1075
1076        // get the request (wrapped if contains multipart-form data)
1077
ActionRequest request;
1078        try {
1079            if (this.enableUploads) {
1080                request = requestFactory.getServletRequest(req);
1081            } else {
1082                request = req;
1083            }
1084        } catch (Exception JavaDoc e) {
1085            if (getLogger().isErrorEnabled()) {
1086                getLogger().error("Problem with Cocoon portlet", e);
1087            }
1088
1089            manageException(req, res, null, null,
1090                            "Problem in creating the Request", null, null, e);
1091            return;
1092        }
1093
1094        // Get the cocoon engine instance
1095
getCocoon(request.getParameter(Constants.RELOAD_PARAM));
1096
1097        // Check if cocoon was initialized
1098
if (this.cocoon == null) {
1099            manageException(request, res, null, null,
1100                            "Initialization Problem",
1101                            null /* "Cocoon was not initialized" */,
1102                            null /* "Cocoon was not initialized, cannot process request" */,
1103                            this.exception);
1104            return;
1105        }
1106
1107        // We got it... Process the request
1108
String JavaDoc servletPath = this.servletPath;
1109        if (servletPath == null) {
1110            servletPath = "portlets/" + getPortletConfig().getPortletName();
1111        }
1112        String JavaDoc pathInfo = getPathInfo(request);
1113
1114        String JavaDoc uri = servletPath;
1115        if (pathInfo != null) {
1116            uri += pathInfo;
1117        }
1118
1119        ContextMap ctxMap = null;
1120
1121        Environment env;
1122        try {
1123            if (uri.charAt(0) == '/') {
1124                uri = uri.substring(1);
1125            }
1126            env = getEnvironment(servletPath, pathInfo, uri, request, res);
1127        } catch (Exception JavaDoc e) {
1128            if (getLogger().isErrorEnabled()) {
1129                getLogger().error("Problem with Cocoon portlet", e);
1130            }
1131
1132            manageException(request, res, null, uri,
1133                            "Problem in creating the Environment", null, null, e);
1134            return;
1135        }
1136
1137        try {
1138            try {
1139                // Initialize a fresh log context containing the object model: it
1140
// will be used by the CocoonLogFormatter
1141
ctxMap = ContextMap.getCurrentContext();
1142                // Add thread name (default content for empty context)
1143
String JavaDoc threadName = Thread.currentThread().getName();
1144                ctxMap.set("threadName", threadName);
1145                // Add the object model
1146
ctxMap.set("objectModel", env.getObjectModel());
1147                // Add a unique request id (threadName + currentTime
1148
ctxMap.set("request-id", threadName + System.currentTimeMillis());
1149
1150                if (this.cocoon.process(env)) {
1151                } else {
1152                    // We reach this when there is nothing in the processing change that matches
1153
// the request. For example, no matcher matches.
1154
getLogger().fatalError("The Cocoon engine failed to process the request.");
1155                    manageException(request, res, env, uri,
1156                                    "Request Processing Failed",
1157                                    "Cocoon engine failed in process the request",
1158                                    "The processing engine failed to process the request. This could be due to lack of matching or bugs in the pipeline engine.",
1159                                    null);
1160                    return;
1161                }
1162            } catch (ResourceNotFoundException e) {
1163                if (getLogger().isDebugEnabled()) {
1164                    getLogger().warn(e.getMessage(), e);
1165                } else if (getLogger().isWarnEnabled()) {
1166                    getLogger().warn(e.getMessage());
1167                }
1168
1169                manageException(request, res, env, uri,
1170                                "Resource Not Found",
1171                                "Resource Not Found",
1172                                "The requested portlet could not be found",
1173                                e);
1174                return;
1175
1176            } catch (ConnectionResetException e) {
1177                if (getLogger().isDebugEnabled()) {
1178                    getLogger().debug(e.getMessage(), e);
1179                } else if (getLogger().isWarnEnabled()) {
1180                    getLogger().warn(e.getMessage());
1181                }
1182
1183            } catch (IOException JavaDoc e) {
1184                // Tomcat5 wraps SocketException into ClientAbortException which extends IOException.
1185
if (getLogger().isDebugEnabled()) {
1186                    getLogger().debug(e.getMessage(), e);
1187                } else if (getLogger().isWarnEnabled()) {
1188                    getLogger().warn(e.getMessage());
1189                }
1190
1191            } catch (Exception JavaDoc e) {
1192                if (getLogger().isErrorEnabled()) {
1193                    getLogger().error("Internal Cocoon Problem", e);
1194                }
1195
1196                manageException(request, res, env, uri,
1197                                "Internal Server Error", null, null, e);
1198                return;
1199            }
1200
1201            long end = System.currentTimeMillis();
1202            String JavaDoc timeString = processTime(end - start);
1203            if (getLogger().isInfoEnabled()) {
1204                getLogger().info("'" + uri + "' " + timeString);
1205            }
1206            res.setProperty("X-Cocoon-Time", timeString);
1207        } finally {
1208            if (ctxMap != null) {
1209                ctxMap.clear();
1210            }
1211
1212            try {
1213                if (request instanceof MultipartActionRequest) {
1214                    if (getLogger().isDebugEnabled()) {
1215                        getLogger().debug("Deleting uploaded file(s).");
1216                    }
1217                    ((MultipartActionRequest) request).cleanup();
1218                }
1219            } catch (IOException JavaDoc e) {
1220                getLogger().error("Cocoon got an Exception while trying to cleanup the uploaded files.", e);
1221            }
1222        }
1223    }
1224
1225    /**
1226     * Process the specified <code>RenderRequest</code> producing output
1227     * on the specified <code>RenderResponse</code>.
1228     */

1229    public void render(RenderRequest req, RenderResponse res)
1230    throws PortletException, IOException JavaDoc {
1231
1232        /* HACK for reducing class loader problems. */
1233        /* example: xalan extensions fail if someone adds xalan jars in tomcat3.2.1/lib */
1234        if (this.initClassLoader) {
1235            try {
1236                Thread.currentThread().setContextClassLoader(this.classLoader);
1237            } catch (Exception JavaDoc e) {
1238            }
1239        }
1240
1241        // remember when we started (used for timing the processing)
1242
long start = System.currentTimeMillis();
1243
1244        // add the cocoon header timestamp
1245
res.setProperty("X-Cocoon-Version", Constants.VERSION);
1246
1247        // get the request (wrapped if contains multipart-form data)
1248
RenderRequest request = req;
1249
1250        // Get the cocoon engine instance
1251
getCocoon(request.getParameter(Constants.RELOAD_PARAM));
1252
1253        // Check if cocoon was initialized
1254
if (this.cocoon == null) {
1255            manageException(request, res, null, null,
1256                            "Initialization Problem",
1257                            null /* "Cocoon was not initialized" */,
1258                            null /* "Cocoon was not initialized, cannot process request" */,
1259                            this.exception);
1260            return;
1261        }
1262
1263        // We got it... Process the request
1264
String JavaDoc servletPath = this.servletPath;
1265        if (servletPath == null) {
1266            servletPath = "portlets/" + getPortletConfig().getPortletName();
1267        }
1268        String JavaDoc pathInfo = getPathInfo(request);
1269
1270        String JavaDoc uri = servletPath;
1271        if (pathInfo != null) {
1272            uri += pathInfo;
1273        }
1274
1275        String JavaDoc contentType = null;
1276        ContextMap ctxMap = null;
1277
1278        Environment env;
1279        try {
1280            if (uri.charAt(0) == '/') {
1281                uri = uri.substring(1);
1282            }
1283            env = getEnvironment(servletPath, pathInfo, uri, request, res);
1284        } catch (Exception JavaDoc e) {
1285            if (getLogger().isErrorEnabled()) {
1286                getLogger().error("Problem with Cocoon portlet", e);
1287            }
1288
1289            manageException(request, res, null, uri,
1290                            "Problem in creating the Environment", null, null, e);
1291            return;
1292        }
1293
1294        try {
1295            try {
1296                // Initialize a fresh log context containing the object model: it
1297
// will be used by the CocoonLogFormatter
1298
ctxMap = ContextMap.getCurrentContext();
1299                // Add thread name (default content for empty context)
1300
String JavaDoc threadName = Thread.currentThread().getName();
1301                ctxMap.set("threadName", threadName);
1302                // Add the object model
1303
ctxMap.set("objectModel", env.getObjectModel());
1304                // Add a unique request id (threadName + currentTime
1305
ctxMap.set("request-id", threadName + System.currentTimeMillis());
1306
1307                if (this.cocoon.process(env)) {
1308                    contentType = env.getContentType();
1309                } else {
1310                    // We reach this when there is nothing in the processing change that matches
1311
// the request. For example, no matcher matches.
1312
getLogger().fatalError("The Cocoon engine failed to process the request.");
1313                    manageException(request, res, env, uri,
1314                                    "Request Processing Failed",
1315                                    "Cocoon engine failed in process the request",
1316                                    "The processing engine failed to process the request. This could be due to lack of matching or bugs in the pipeline engine.",
1317                                    null);
1318                    return;
1319                }
1320            } catch (ResourceNotFoundException rse) {
1321                if (getLogger().isWarnEnabled()) {
1322                    getLogger().warn("The resource was not found", rse);
1323                }
1324
1325                manageException(request, res, env, uri,
1326                                "Resource Not Found",
1327                                "Resource Not Found",
1328                                "The requested portlet could not be found",
1329                                rse);
1330                return;
1331
1332            } catch (ConnectionResetException e) {
1333                if (getLogger().isDebugEnabled()) {
1334                    getLogger().debug(e.getMessage(), e);
1335                } else if (getLogger().isWarnEnabled()) {
1336                    getLogger().warn(e.getMessage());
1337                }
1338
1339            } catch (IOException JavaDoc e) {
1340                // Tomcat5 wraps SocketException into ClientAbortException which extends IOException.
1341
if (getLogger().isDebugEnabled()) {
1342                    getLogger().debug(e.getMessage(), e);
1343                } else if (getLogger().isWarnEnabled()) {
1344                    getLogger().warn(e.getMessage());
1345                }
1346
1347            } catch (Exception JavaDoc e) {
1348                if (getLogger().isErrorEnabled()) {
1349                    getLogger().error("Internal Cocoon Problem", e);
1350                }
1351
1352                manageException(request, res, env, uri,
1353                                "Internal Server Error", null, null, e);
1354                return;
1355            }
1356
1357            long end = System.currentTimeMillis();
1358            String JavaDoc timeString = processTime(end - start);
1359            if (getLogger().isInfoEnabled()) {
1360                getLogger().info("'" + uri + "' " + timeString);
1361            }
1362            res.setProperty("X-Cocoon-Time", timeString);
1363
1364            if (contentType != null && contentType.equals("text/html")) {
1365                String JavaDoc showTime = request.getParameter(Constants.SHOWTIME_PARAM);
1366                boolean show = this.showTime;
1367                if (showTime != null) {
1368                    show = !showTime.equalsIgnoreCase("no");
1369                }
1370                if (show) {
1371                    boolean hide = this.hiddenShowTime;
1372                    if (showTime != null) {
1373                        hide = showTime.equalsIgnoreCase("hide");
1374                    }
1375                    PrintStream JavaDoc out = new PrintStream JavaDoc(res.getPortletOutputStream());
1376                    out.print((hide) ? "<!-- " : "<p>");
1377                    out.print(timeString);
1378                    out.println((hide) ? " -->" : "</p>\n");
1379                }
1380            }
1381        } finally {
1382            if (ctxMap != null) {
1383                ctxMap.clear();
1384            }
1385
1386            try {
1387                if (request instanceof MultipartActionRequest) {
1388                    if (getLogger().isDebugEnabled()) {
1389                        getLogger().debug("Deleting uploaded file(s).");
1390                    }
1391                    ((MultipartActionRequest) request).cleanup();
1392                }
1393            } catch (IOException JavaDoc e) {
1394                getLogger().error("Cocoon got an Exception while trying to cleanup the uploaded files.", e);
1395            }
1396
1397            /*
1398             * Portlet Specification 1.0, PLT.12.3.2 Output Stream and Writer Objects:
1399             * The termination of the render method of the portlet indicates
1400             * that the portlet has satisfied the request and that the output
1401             * object is to be closed.
1402             *
1403             * Portlet container will close the stream, no need to close it here.
1404             */

1405        }
1406    }
1407
1408    private String JavaDoc getPathInfo(PortletRequest request) {
1409        PortletSession session = null;
1410
1411        String JavaDoc pathInfo = request.getParameter(PortletEnvironment.PARAMETER_PATH_INFO);
1412        if (storeSessionPath) {
1413            session = request.getPortletSession(true);
1414            if (pathInfo == null) {
1415                pathInfo = (String JavaDoc)session.getAttribute(PortletEnvironment.PARAMETER_PATH_INFO);
1416            }
1417        }
1418
1419        // Make sure it starts with or equals to '/'
1420
if (pathInfo == null) {
1421            pathInfo = "/";
1422        } else if (!pathInfo.startsWith("/")) {
1423            pathInfo = '/' + pathInfo;
1424        }
1425
1426        if (storeSessionPath) {
1427            session.setAttribute(PortletEnvironment.PARAMETER_PATH_INFO, pathInfo);
1428        }
1429        return pathInfo;
1430    }
1431
1432    protected void manageException(ActionRequest req, ActionResponse res, Environment env,
1433                                   String JavaDoc uri, String JavaDoc title, String JavaDoc message, String JavaDoc description,
1434                                   Exception JavaDoc e)
1435    throws PortletException {
1436        throw new PortletException("Exception in CocoonPortlet", e);
1437    }
1438
1439    protected void manageException(RenderRequest req, RenderResponse res, Environment env,
1440                                   String JavaDoc uri, String JavaDoc title, String JavaDoc message, String JavaDoc description,
1441                                   Exception JavaDoc e)
1442    throws IOException JavaDoc, PortletException {
1443        if (this.manageExceptions) {
1444            if (env != null) {
1445                env.tryResetResponse();
1446            } else {
1447                res.reset();
1448            }
1449
1450            String JavaDoc type = Notifying.FATAL_NOTIFICATION;
1451            HashMap JavaDoc extraDescriptions = null;
1452
1453            extraDescriptions = new HashMap JavaDoc(2);
1454            extraDescriptions.put(Notifying.EXTRA_REQUESTURI, getPortletConfig().getPortletName());
1455            if (uri != null) {
1456                extraDescriptions.put("Request URI", uri);
1457            }
1458
1459            // Do not show exception stack trace when log level is WARN or above. Show only message.
1460
if (!getLogger().isInfoEnabled()) {
1461                Throwable JavaDoc t = DefaultNotifyingBuilder.getRootCause(e);
1462                if (t != null) extraDescriptions.put(Notifying.EXTRA_CAUSE, t.getMessage());
1463                e = null;
1464            }
1465
1466            Notifying n = new DefaultNotifyingBuilder().build(this,
1467                                                              e,
1468                                                              type,
1469                                                              title,
1470                                                              "Cocoon Portlet",
1471                                                              message,
1472                                                              description,
1473                                                              extraDescriptions);
1474
1475            res.setContentType("text/html");
1476            Notifier.notify(n, res.getPortletOutputStream(), "text/html");
1477        } else {
1478            res.flushBuffer();
1479            throw new PortletException("Exception in CocoonPortlet", e);
1480        }
1481    }
1482
1483    /**
1484     * Create the environment for the request
1485     */

1486    protected Environment getEnvironment(String JavaDoc servletPath,
1487                                         String JavaDoc pathInfo,
1488                                         String JavaDoc uri,
1489                                         ActionRequest req,
1490                                         ActionResponse res)
1491    throws Exception JavaDoc {
1492        PortletEnvironment env;
1493
1494        String JavaDoc formEncoding = req.getParameter("cocoon-form-encoding");
1495        if (formEncoding == null) {
1496            formEncoding = this.defaultFormEncoding;
1497        }
1498        env = new PortletEnvironment(servletPath,
1499                                     pathInfo,
1500                                     uri,
1501                                     this.portletContextURL,
1502                                     req,
1503                                     res,
1504                                     this.portletContext,
1505                                     (PortletContext) this.appContext.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT),
1506                                     this.containerEncoding,
1507                                     formEncoding,
1508                                     this.defaultSessionScope);
1509        env.enableLogging(getLogger());
1510        return env;
1511    }
1512
1513    /**
1514     * Create the environment for the request
1515     */

1516    protected Environment getEnvironment(String JavaDoc servletPath,
1517                                         String JavaDoc pathInfo,
1518                                         String JavaDoc uri,
1519                                         RenderRequest req,
1520                                         RenderResponse res)
1521    throws Exception JavaDoc {
1522        PortletEnvironment env;
1523
1524        String JavaDoc formEncoding = req.getParameter("cocoon-form-encoding");
1525        if (formEncoding == null) {
1526            formEncoding = this.defaultFormEncoding;
1527        }
1528        env = new PortletEnvironment(servletPath,
1529                                     pathInfo,
1530                                     uri,
1531                                     this.portletContextURL,
1532                                     req,
1533                                     res,
1534                                     this.portletContext,
1535                                     (PortletContext) this.appContext.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT),
1536                                     this.containerEncoding,
1537                                     formEncoding,
1538                                     this.defaultSessionScope);
1539        env.enableLogging(getLogger());
1540        return env;
1541    }
1542
1543    /**
1544     * Instatiates the parent component manager, as specified in the
1545     * parent-component-manager init parameter.
1546     *
1547     * If none is specified, the method returns <code>null</code>.
1548     *
1549     * @return the parent component manager, or <code>null</code>.
1550     */

1551    protected synchronized ComponentManager getParentComponentManager() {
1552        if (parentComponentManager != null && parentComponentManager instanceof Disposable) {
1553            ((Disposable) parentComponentManager).dispose();
1554        }
1555
1556        parentComponentManager = null;
1557        if (parentComponentManagerClass != null) {
1558            try {
1559                Class JavaDoc pcm = ClassUtils.loadClass(parentComponentManagerClass);
1560                Constructor JavaDoc pcmc = pcm.getConstructor(new Class JavaDoc[]{String JavaDoc.class});
1561                parentComponentManager = (ComponentManager) pcmc.newInstance(new Object JavaDoc[]{parentComponentManagerInitParam});
1562
1563                if (parentComponentManager instanceof LogEnabled) {
1564                    ((LogEnabled) parentComponentManager).enableLogging(getLogger());
1565                }
1566                if (parentComponentManager instanceof Contextualizable) {
1567                    ((Contextualizable) parentComponentManager).contextualize(this.appContext);
1568                }
1569                if (parentComponentManager instanceof Initializable) {
1570                    ((Initializable) parentComponentManager).initialize();
1571                }
1572            } catch (Exception JavaDoc e) {
1573                if (getLogger().isErrorEnabled()) {
1574                    getLogger().error("Could not initialize parent component manager.", e);
1575                }
1576            }
1577        }
1578        return parentComponentManager;
1579    }
1580
1581    /**
1582     * Creates the Cocoon object and handles exception handling.
1583     */

1584    private synchronized void createCocoon()
1585    throws PortletException {
1586
1587        /* HACK for reducing class loader problems. */
1588        /* example: xalan extensions fail if someone adds xalan jars in tomcat3.2.1/lib */
1589        if (this.initClassLoader) {
1590            try {
1591                Thread.currentThread().setContextClassLoader(this.classLoader);
1592            } catch (Exception JavaDoc e) {
1593            }
1594        }
1595
1596        updateEnvironment();
1597        forceLoad();
1598        forceProperty();
1599
1600        try {
1601            URL JavaDoc configFile = (URL JavaDoc) this.appContext.get(Constants.CONTEXT_CONFIG_URL);
1602            if (getLogger().isInfoEnabled()) {
1603                getLogger().info("Reloading from: " + configFile.toExternalForm());
1604            }
1605            Cocoon c = (Cocoon) ClassUtils.newInstance("org.apache.cocoon.Cocoon");
1606            ContainerUtil.enableLogging(c, getCocoonLogger());
1607            c.setLoggerManager(getLoggerManager());
1608            ContainerUtil.contextualize(c, this.appContext);
1609            final ComponentManager parent = this.getParentComponentManager();
1610            if (parent != null) {
1611                ContainerUtil.compose(c, parent);
1612            }
1613            if (this.enableInstrumentation) {
1614                c.setInstrumentManager(getInstrumentManager());
1615            }
1616            ContainerUtil.initialize(c);
1617            this.creationTime = System.currentTimeMillis();
1618
1619            disposeCocoon();
1620            this.cocoon = c;
1621        } catch (Exception JavaDoc e) {
1622            if (getLogger().isErrorEnabled()) {
1623                getLogger().error("Exception reloading", e);
1624            }
1625            this.exception = e;
1626            disposeCocoon();
1627        }
1628    }
1629
1630    private Logger getCocoonLogger() {
1631        final String JavaDoc rootlogger = getInitParameter("cocoon-logger");
1632        if (rootlogger != null) {
1633            return this.getLoggerManager().getLoggerForCategory(rootlogger);
1634        } else {
1635            return getLogger();
1636        }
1637    }
1638
1639    /**
1640     * Method to update the environment before Cocoon instances are created.
1641     *
1642     * This is also useful if you wish to customize any of the 'protected'
1643     * variables from this class before a Cocoon instance is built in a derivative
1644     * of this class (eg. Cocoon Context).
1645     */

1646    protected void updateEnvironment() throws PortletException {
1647        this.appContext.put(Constants.CONTEXT_CLASS_LOADER, classLoader);
1648        this.appContext.put(Constants.CONTEXT_CLASSPATH, getClassPath());
1649    }
1650
1651    /**
1652     * Helper method to obtain an <code>InstrumentManager</code> instance
1653     *
1654     * @return an <code>InstrumentManager</code> instance
1655     */

1656    private InstrumentManager getInstrumentManager()
1657    throws Exception JavaDoc {
1658        String JavaDoc imConfig = getInitParameter("instrumentation-config");
1659        if (imConfig == null) {
1660            throw new PortletException("Please define the init-param 'instrumentation-config' in your web.xml");
1661        }
1662
1663        final InputStream JavaDoc is = this.portletContext.getResourceAsStream(imConfig);
1664        final DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
1665        final Configuration conf = builder.build(is);
1666
1667        // Get the logger for the instrument manager
1668
final String JavaDoc imLoggerCategory = conf.getAttribute("logger", "core.instrument");
1669        Logger imLogger = this.loggerManager.getLoggerForCategory(imLoggerCategory);
1670
1671        // Set up the Instrument Manager
1672
DefaultInstrumentManagerImpl instrumentManager = new DefaultInstrumentManagerImpl();
1673        instrumentManager.enableLogging(imLogger);
1674        instrumentManager.configure(conf);
1675        instrumentManager.initialize();
1676
1677        if (getLogger().isDebugEnabled()) {
1678            getLogger().debug("Instrument manager created " + instrumentManager);
1679        }
1680
1681        this.instrumentManager = instrumentManager;
1682        return instrumentManager;
1683    }
1684
1685    private String JavaDoc processTime(long time) {
1686        StringBuffer JavaDoc out = new StringBuffer JavaDoc(PROCESSED_BY);
1687        if (time <= SECOND) {
1688            out.append(time);
1689            out.append(" milliseconds.");
1690        } else if (time <= MINUTE) {
1691            out.append(time / SECOND);
1692            out.append(" seconds.");
1693        } else if (time <= HOUR) {
1694            out.append(time / MINUTE);
1695            out.append(" minutes.");
1696        } else {
1697            out.append(time / HOUR);
1698            out.append(" hours.");
1699        }
1700        return out.toString();
1701    }
1702
1703    /**
1704     * Gets the current cocoon object. Reload cocoon if configuration
1705     * changed or we are reloading.
1706     */

1707    private void getCocoon(final String JavaDoc reloadParam)
1708    throws PortletException {
1709        if (this.allowReload) {
1710            boolean reload = false;
1711
1712            if (this.cocoon != null) {
1713                if (this.cocoon.modifiedSince(this.creationTime)) {
1714                    if (getLogger().isInfoEnabled()) {
1715                        getLogger().info("Configuration changed reload attempt");
1716                    }
1717                    reload = true;
1718                } else if (reloadParam != null) {
1719                    if (getLogger().isInfoEnabled()) {
1720                        getLogger().info("Forced reload attempt");
1721                    }
1722                    reload = true;
1723                }
1724            } else if (reloadParam != null) {
1725                if (getLogger().isInfoEnabled()) {
1726                    getLogger().info("Invalid configurations reload");
1727                }
1728                reload = true;
1729            }
1730
1731            if (reload) {
1732                initLogger();
1733                createCocoon();
1734            }
1735        }
1736    }
1737
1738    /**
1739     * Destroy Cocoon
1740     */

1741    private final void disposeCocoon() {
1742        if (this.cocoon != null) {
1743            ContainerUtil.dispose(this.cocoon);
1744            this.cocoon = null;
1745        }
1746    }
1747
1748    /**
1749     * Get an initialisation parameter. The value is trimmed, and null is returned if the trimmed value
1750     * is empty.
1751     */

1752    public String JavaDoc getInitParameter(String JavaDoc name) {
1753        String JavaDoc result = super.getInitParameter(name);
1754        if (result != null) {
1755            result = result.trim();
1756            if (result.length() == 0) {
1757                result = null;
1758            }
1759        }
1760
1761        return result;
1762    }
1763
1764    /** Convenience method to access portlet parameters */
1765    protected String JavaDoc getInitParameter(String JavaDoc name, String JavaDoc defaultValue) {
1766        String JavaDoc result = getInitParameter(name);
1767        if (result == null) {
1768            if (getLogger() != null && getLogger().isDebugEnabled()) {
1769                getLogger().debug(name + " was not set - defaulting to '" + defaultValue + "'");
1770            }
1771            return defaultValue;
1772        } else {
1773            return result;
1774        }
1775    }
1776
1777    /** Convenience method to access boolean portlet parameters */
1778    protected boolean getInitParameterAsBoolean(String JavaDoc name, boolean defaultValue) {
1779        String JavaDoc value = getInitParameter(name);
1780        if (value == null) {
1781            if (getLogger() != null && getLogger().isDebugEnabled()) {
1782                getLogger().debug(name + " was not set - defaulting to '" + defaultValue + "'");
1783            }
1784            return defaultValue;
1785        }
1786
1787        return BooleanUtils.toBoolean(value);
1788    }
1789
1790    protected int getInitParameterAsInteger(String JavaDoc name, int defaultValue) {
1791        String JavaDoc value = getInitParameter(name);
1792        if (value == null) {
1793            if (getLogger() != null && getLogger().isDebugEnabled()) {
1794                getLogger().debug(name + " was not set - defaulting to '" + defaultValue + "'");
1795            }
1796            return defaultValue;
1797        } else {
1798            return Integer.parseInt(value);
1799        }
1800    }
1801
1802    protected Logger getLogger() {
1803        return this.log;
1804    }
1805
1806    protected LoggerManager getLoggerManager() {
1807        return this.loggerManager;
1808    }
1809}
1810
Popular Tags