Save This Page
Home » cocoon-2.1.11-src » org.apache » cocoon » servlet » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   package org.apache.cocoon.servlet;
   18   
   19   import org.apache.avalon.excalibur.logger.Log4JLoggerManager;
   20   import org.apache.avalon.excalibur.logger.LogKitLoggerManager;
   21   import org.apache.avalon.excalibur.logger.LoggerManager;
   22   import org.apache.avalon.framework.activity.Disposable;
   23   import org.apache.avalon.framework.activity.Initializable;
   24   import org.apache.avalon.framework.component.ComponentManager;
   25   import org.apache.avalon.framework.configuration.Configurable;
   26   import org.apache.avalon.framework.configuration.Configuration;
   27   import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
   28   import org.apache.avalon.framework.container.ContainerUtil;
   29   import org.apache.avalon.framework.context.Contextualizable;
   30   import org.apache.avalon.framework.context.DefaultContext;
   31   import org.apache.avalon.framework.logger.LogEnabled;
   32   import org.apache.avalon.framework.logger.LogKitLogger;
   33   import org.apache.avalon.framework.logger.Logger;
   34   
   35   import org.apache.cocoon.Cocoon;
   36   import org.apache.cocoon.ConnectionResetException;
   37   import org.apache.cocoon.Constants;
   38   import org.apache.cocoon.ResourceNotFoundException;
   39   import org.apache.cocoon.components.notification.DefaultNotifyingBuilder;
   40   import org.apache.cocoon.components.notification.Notifier;
   41   import org.apache.cocoon.components.notification.Notifying;
   42   import org.apache.cocoon.environment.Environment;
   43   import org.apache.cocoon.environment.http.HttpContext;
   44   import org.apache.cocoon.environment.http.HttpEnvironment;
   45   import org.apache.cocoon.servlet.multipart.MultipartHttpServletRequest;
   46   import org.apache.cocoon.servlet.multipart.RequestFactory;
   47   import org.apache.cocoon.util.ClassUtils;
   48   import org.apache.cocoon.util.Deprecation;
   49   import org.apache.cocoon.util.IOUtils;
   50   import org.apache.cocoon.util.StringUtils;
   51   import org.apache.cocoon.util.log.CocoonLogFormatter;
   52   import org.apache.cocoon.util.log.Log4JConfigurator;
   53   
   54   import org.apache.commons.lang.BooleanUtils;
   55   import org.apache.commons.lang.SystemUtils;
   56   import org.apache.commons.lang.time.StopWatch;
   57   import org.apache.excalibur.instrument.InstrumentManager;
   58   import org.apache.excalibur.instrument.manager.impl.DefaultInstrumentManagerImpl;
   59   import org.apache.log.ContextMap;
   60   import org.apache.log.ErrorHandler;
   61   import org.apache.log.Hierarchy;
   62   import org.apache.log.Priority;
   63   import org.apache.log.output.ServletOutputLogTarget;
   64   import org.apache.log.util.DefaultErrorHandler;
   65   import org.apache.log4j.LogManager;
   66   
   67   import javax.servlet.ServletConfig;
   68   import javax.servlet.ServletContext;
   69   import javax.servlet.ServletException;
   70   import javax.servlet.ServletOutputStream;
   71   import javax.servlet.http.HttpServlet;
   72   import javax.servlet.http.HttpServletRequest;
   73   import javax.servlet.http.HttpServletResponse;
   74   import java.io.File;
   75   import java.io.FileInputStream;
   76   import java.io.FileOutputStream;
   77   import java.io.IOException;
   78   import java.io.InputStream;
   79   import java.io.OutputStream;
   80   import java.lang.reflect.Constructor;
   81   import java.net.MalformedURLException;
   82   import java.net.URL;
   83   import java.util.ArrayList;
   84   import java.util.Arrays;
   85   import java.util.HashMap;
   86   import java.util.Iterator;
   87   import java.util.List;
   88   import java.util.StringTokenizer;
   89   import java.util.jar.Attributes;
   90   import java.util.jar.Manifest;
   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 606745 2007-12-24 22:26:08Z solprovider $
  103    */
  104   public class CocoonServlet extends HttpServlet {
  105   
  106       /**
  107        * Application <code>Context</code> Key for the servlet configuration
  108        * @since 2.1.3
  109        */
  110       public static final String CONTEXT_SERVLET_CONFIG = "servlet-config";
  111   
  112       // Processing time message
  113       protected static final String 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 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 uploadDir;
  187       private File workDir;
  188       private File cacheDir;
  189       private String containerEncoding;
  190       private String defaultFormEncoding;
  191   
  192       protected ServletContext servletContext;
  193   
  194       /** The classloader that will be set as the context classloader if init-classloader is true */
  195       protected ClassLoader classLoader = this.getClass().getClassLoader();
  196       protected boolean initClassLoader = false;
  197   
  198       private String parentComponentManagerClass;
  199       private String 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 forceLoadParameter;
  205       protected String 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 servletContextPath;
  229   
  230       /**
  231        * This is the url to the servlet context directory
  232        */
  233       protected String 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 conf)
  254       throws ServletException {
  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 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 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 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 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(workDirParam);
  300               } else {
  301                   // Context path exists : is work-directory absolute ?
  302                   File workDirParamFile = new File(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(servletContextPath, workDirParam);
  309                   }
  310               }
  311           } else {
  312               this.workDir = (File) this.servletContext.getAttribute("javax.servlet.context.tempdir");
  313               this.workDir = new File(workDir, "cocoon-files");
  314           }
  315           this.workDir.mkdirs();
  316           this.appContext.put(Constants.CONTEXT_WORK_DIR, workDir);
  317   
  318           String 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 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 me) {
  327                   throw new ServletException("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(path).toURL().toExternalForm();
  338               }
  339           } catch (MalformedURLException 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(path).toURL().toExternalForm();
  345               } catch (MalformedURLException ignored) {
  346                   throw new ServletException("Unable to determine servlet context URL.", me);
  347               }
  348           }
  349           try {
  350               this.appContext.put("context-root", new URL(this.servletContextURL));
  351           } catch (MalformedURLException 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 uploadDirParam = conf.getInitParameter("upload-directory");
  380           if (uploadDirParam != null) {
  381               if (this.servletContextPath == null) {
  382                   this.uploadDir = new File(uploadDirParam);
  383               } else {
  384                   // Context path exists : is upload-directory absolute ?
  385                   File uploadDirParamFile = new File(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(servletContextPath, uploadDirParam);
  392                   }
  393               }
  394               if (getLogger().isDebugEnabled()) {
  395                   getLogger().debug("Using upload-directory " + this.uploadDir);
  396               }
  397           } else {
  398               this.uploadDir = new File(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 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 cacheDirParam = conf.getInitParameter("cache-directory");
  427           if (cacheDirParam != null) {
  428               if (this.servletContextPath == null) {
  429                   this.cacheDir = new File(cacheDirParam);
  430               } else {
  431                   // Context path exists : is cache-directory absolute ?
  432                   File cacheDirParamFile = new File(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(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 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 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 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 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 getClassPath() throws ServletException {
  568           StringBuffer buildClassPath = new StringBuffer();
  569   
  570           File root = null;
  571           if (servletContextPath != null) {
  572               // Old method.  There *MUST* be a better method than this...
  573   
  574               String classDir = this.servletContext.getRealPath("/WEB-INF/classes");
  575               String libDir = this.servletContext.getRealPath("/WEB-INF/lib");
  576   
  577               if (libDir != null) {
  578                   root = new File(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 classDirURL = null;
  589               URL libDirURL = null;
  590   
  591               try {
  592                   classDirURL = this.servletContext.getResource("/WEB-INF/classes");
  593               } catch (MalformedURLException 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 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(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[] libraries = root.listFiles();
  625               Arrays.sort(libraries);
  626               for (int i = 0; i < libraries.length; i++) {
  627                   String 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 extractLibraries() {
  643           try {
  644               URL 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 mf = new Manifest(manifestURL.openStream());
  651               Attributes attr = mf.getMainAttributes();
  652               String 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 libList = new ArrayList();
  659               for (StringTokenizer st = new StringTokenizer(libValue, " "); st.hasMoreTokens();) {
  660                   libList.add(st.nextToken());
  661               }
  662   
  663               File root = new File(this.workDir, "lib");
  664               root.mkdirs();
  665   
  666               File[] oldLibs = root.listFiles();
  667               for (int i = 0; i < oldLibs.length; i++) {
  668                   String 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 i = libList.iterator(); i.hasNext();) {
  678                   String libName = (String) i.next();
  679   
  680                   long lastModified = -1;
  681                   try {
  682                       lastModified = Long.parseLong(attr.getValue("Cocoon-Lib-" + libName.replace('.', '_')));
  683                   } catch (Exception e) {
  684                       this.getLogger().debug("Failed to parse lastModified: " + attr.getValue("Cocoon-Lib-" + libName.replace('.', '_')));
  685                   }
  686   
  687                   File lib = new File(root, libName);
  688                   if (lib.exists() && lib.lastModified() != lastModified) {
  689                       this.getLogger().debug("Removing modified library " + lib);
  690                       lib.delete();
  691                   }
  692   
  693                   InputStream is = null;
  694                   OutputStream os = null;
  695                   try {
  696                       is = this.servletContext.getResourceAsStream("/WEB-INF/lib/" + libName);
  697                       if (is != null) {
  698                           this.getLogger().debug("Extracting " + libName);
  699                           os = new FileOutputStream(lib);
  700                           int count;
  701                           while ((count = is.read(buffer)) > 0) {
  702                               os.write(buffer, 0, count);
  703                           }
  704                       } else {
  705                           this.getLogger().warn("Skipping " + libName);
  706                       }
  707                   } finally {
  708                       if (os != null) os.close();
  709                       if (is != null) is.close();
  710                   }
  711   
  712                   if (lastModified != -1) {
  713                       lib.setLastModified(lastModified);
  714                   }
  715               }
  716   
  717               return root;
  718           } catch (IOException e) {
  719               this.getLogger().fatalError("Exception while processing Manifest file", e);
  720               return null;
  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 getExtraClassPath() throws ServletException {
  731           String extraClassPath = this.getInitParameter("extra-classpath");
  732           if (extraClassPath != null) {
  733               StringBuffer sb = new StringBuffer();
  734               StringTokenizer st = new StringTokenizer(extraClassPath, SystemUtils.PATH_SEPARATOR, false);
  735               int i = 0;
  736               while (st.hasMoreTokens()) {
  737                   String 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 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 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 logLevel = getInitParameter("log-level", "INFO");
  792   
  793           final String 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 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 logSCDir = new File(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 logkitConfig = getInitParameter("logkit-config", "/WEB-INF/logkit.xconf");
  838   
  839                   // test if this is a qualified url
  840                   InputStream is = null;
  841                   if (logkitConfig.indexOf(':') == -1) {
  842                       is = this.servletContext.getResourceAsStream(logkitConfig);
  843                       if (is == null) is = new FileInputStream(logkitConfig);
  844                   } else {
  845                       URL logkitURL = new URL(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 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 is = null;
  860                   if ( log4jConfig.indexOf(':') == -1) {
  861                       is = this.servletContext.getResourceAsStream(log4jConfig);
  862                       if (is == null) is = new FileInputStream(log4jConfig);
  863                   } else {
  864                       final URL log4jURL = new URL(log4jConfig);
  865                       is = log4jURL.openStream();
  866                   }
  867                   configurator.doConfigure(is, LogManager.getLoggerRepository());
  868               }
  869   
  870               ContainerUtil.initialize(loggerManager);
  871           } catch (Exception 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 deprecationLevel = getInitParameter("forbidden-deprecation-level", "ERROR");
  878           Deprecation.setForbiddenLevel(Deprecation.LogLevel.getLevel(deprecationLevel));
  879       }
  880   
  881       private LoggerManager newLoggerManager(String 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 clazz = Class.forName(loggerManagerClass);
  890                   return (LoggerManager)clazz.newInstance();
  891               } catch (Exception 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 getConfigFile(final String configFileName)
  905       throws ServletException {
  906           final String 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 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(usedFileName);
  928               }
  929           } catch (Exception mue) {
  930               String msg = "Init parameter 'configurations' is invalid : " + usedFileName;
  931               getLogger().error(msg, mue);
  932               throw new ServletException(msg, mue);
  933           }
  934   
  935           if (result == null) {
  936               File resultFile = new File(usedFileName);
  937               if (resultFile.isFile()) {
  938                   try {
  939                       result = resultFile.getCanonicalFile().toURL();
  940                   } catch (Exception e) {
  941                       String msg = "Init parameter 'configurations' is invalid : " + usedFileName;
  942                       getLogger().error(msg, e);
  943                       throw new ServletException(msg, e);
  944                   }
  945               }
  946           }
  947   
  948           if (result == null) {
  949               String msg = "Init parameter 'configurations' doesn't name an existing resource : " + usedFileName;
  950               getLogger().error(msg);
  951               throw new ServletException(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 fqcnTokenizer = new StringTokenizer(forceLoadParameter, " \t\r\n\f;,", false);
  971   
  972               while (fqcnTokenizer.hasMoreTokens()) {
  973                   final String 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 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 tokenizer = new StringTokenizer(forceSystemProperty, " \t\r\n\f;,", false);
 1000   
 1001               while (tokenizer.hasMoreTokens()) {
 1002                   final String property = tokenizer.nextToken().trim();
 1003                   if (property.indexOf('=') == -1) {
 1004                       continue;
 1005                   }
 1006                   try {
 1007                       String key = property.substring(0, property.indexOf('='));
 1008                       String 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 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 res)
 1031       throws ServletException, IOException {
 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 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 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 uri = request.getServletPath();
 1091           if (uri == null) {
 1092               uri = "";
 1093           }
 1094           String 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 prefix = request.getRequestURI();
 1112               if (prefix == null) {
 1113                   prefix = "";
 1114               }
 1115   
 1116               res.sendRedirect(res.encodeRedirectURL(prefix + "/"));
 1117               return;
 1118           }
 1119   
 1120           String 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 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 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 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 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 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 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 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 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 res, Environment env,
 1273                                      String uri, int errorStatus,
 1274                                      String title, String message, String description,
 1275                                      Exception e)
 1276               throws IOException {
 1277           if (this.manageExceptions) {
 1278               if (env != null) {
 1279                   env.tryResetResponse();
 1280               } else {
 1281                   res.reset();
 1282               }
 1283   
 1284               String type = Notifying.FATAL_NOTIFICATION;
 1285               HashMap 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(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 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 uri,
 1328                                            HttpServletRequest req,
 1329                                            HttpServletResponse res)
 1330       throws Exception {
 1331           HttpEnvironment env;
 1332   
 1333           String 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 pcm = ClassUtils.loadClass(parentComponentManagerClass);
 1366                   Constructor pcmc = pcm.getConstructor(new Class[]{String.class});
 1367                   parentComponentManager = (ComponentManager) pcmc.newInstance(new Object[]{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 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 {
 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 e) {
 1405               }
 1406           }
 1407   
 1408           updateEnvironment();
 1409           forceLoad();
 1410           forceProperty();
 1411   
 1412           try {
 1413               this.exception = null;
 1414               URL configFile = (URL) 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 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 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 {
 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 {
 1470           String imConfig = getInitParameter("instrumentation-config");
 1471           if (imConfig == null) {
 1472               throw new ServletException("Please define the init-param 'instrumentation-config' in your web.xml");
 1473           }
 1474   
 1475           final InputStream 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 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 processTime(long time) {
 1498           StringBuffer out = new StringBuffer(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 pathInfo, final String reloadParam)
 1520       throws ServletException {
 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 getInitParameter(String name) {
 1564           String 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 getInitParameter(String name, String defaultValue) {
 1577           String 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 name, boolean defaultValue) {
 1590           String 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 name, int defaultValue) {
 1602           String 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   }

Save This Page
Home » cocoon-2.1.11-src » org.apache » cocoon » servlet » [javadoc | source]