0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package org.apache.cocoon.servlet;
0018:
0019: import org.apache.avalon.excalibur.logger.Log4JLoggerManager;
0020: import org.apache.avalon.excalibur.logger.LogKitLoggerManager;
0021: import org.apache.avalon.excalibur.logger.LoggerManager;
0022: import org.apache.avalon.framework.activity.Disposable;
0023: import org.apache.avalon.framework.activity.Initializable;
0024: import org.apache.avalon.framework.component.ComponentManager;
0025: import org.apache.avalon.framework.configuration.Configurable;
0026: import org.apache.avalon.framework.configuration.Configuration;
0027: import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
0028: import org.apache.avalon.framework.container.ContainerUtil;
0029: import org.apache.avalon.framework.context.Contextualizable;
0030: import org.apache.avalon.framework.context.DefaultContext;
0031: import org.apache.avalon.framework.logger.LogEnabled;
0032: import org.apache.avalon.framework.logger.LogKitLogger;
0033: import org.apache.avalon.framework.logger.Logger;
0034:
0035: import org.apache.cocoon.Cocoon;
0036: import org.apache.cocoon.ConnectionResetException;
0037: import org.apache.cocoon.Constants;
0038: import org.apache.cocoon.ResourceNotFoundException;
0039: import org.apache.cocoon.components.notification.DefaultNotifyingBuilder;
0040: import org.apache.cocoon.components.notification.Notifier;
0041: import org.apache.cocoon.components.notification.Notifying;
0042: import org.apache.cocoon.environment.Environment;
0043: import org.apache.cocoon.environment.http.HttpContext;
0044: import org.apache.cocoon.environment.http.HttpEnvironment;
0045: import org.apache.cocoon.servlet.multipart.MultipartHttpServletRequest;
0046: import org.apache.cocoon.servlet.multipart.RequestFactory;
0047: import org.apache.cocoon.util.ClassUtils;
0048: import org.apache.cocoon.util.Deprecation;
0049: import org.apache.cocoon.util.IOUtils;
0050: import org.apache.cocoon.util.StringUtils;
0051: import org.apache.cocoon.util.log.CocoonLogFormatter;
0052: import org.apache.cocoon.util.log.Log4JConfigurator;
0053:
0054: import org.apache.commons.lang.BooleanUtils;
0055: import org.apache.commons.lang.SystemUtils;
0056: import org.apache.commons.lang.time.StopWatch;
0057: import org.apache.excalibur.instrument.InstrumentManager;
0058: import org.apache.excalibur.instrument.manager.impl.DefaultInstrumentManagerImpl;
0059: import org.apache.log.ContextMap;
0060: import org.apache.log.ErrorHandler;
0061: import org.apache.log.Hierarchy;
0062: import org.apache.log.Priority;
0063: import org.apache.log.output.ServletOutputLogTarget;
0064: import org.apache.log.util.DefaultErrorHandler;
0065: import org.apache.log4j.LogManager;
0066:
0067: import javax.servlet.ServletConfig;
0068: import javax.servlet.ServletContext;
0069: import javax.servlet.ServletException;
0070: import javax.servlet.ServletOutputStream;
0071: import javax.servlet.http.HttpServlet;
0072: import javax.servlet.http.HttpServletRequest;
0073: import javax.servlet.http.HttpServletResponse;
0074: import java.io.File;
0075: import java.io.FileInputStream;
0076: import java.io.FileOutputStream;
0077: import java.io.IOException;
0078: import java.io.InputStream;
0079: import java.io.OutputStream;
0080: import java.lang.reflect.Constructor;
0081: import java.net.MalformedURLException;
0082: import java.net.URL;
0083: import java.util.ArrayList;
0084: import java.util.Arrays;
0085: import java.util.HashMap;
0086: import java.util.Iterator;
0087: import java.util.List;
0088: import java.util.StringTokenizer;
0089: import java.util.jar.Attributes;
0090: import java.util.jar.Manifest;
0091:
0092: /**
0093: * This is the entry point for Cocoon execution as an HTTP Servlet.
0094: *
0095: * @author <a href="mailto:pier@apache.org">Pierpaolo Fumagalli</a>
0096: * (Apache Software Foundation)
0097: * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
0098: * @author <a href="mailto:nicolaken@apache.org">Nicola Ken Barozzi</a>
0099: * @author <a href="mailto:bloritsch@apache.org">Berin Loritsch</a>
0100: * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
0101: * @author <a href="mailto:leo.sutic@inspireinfrastructure.com">Leo Sutic</a>
0102: * @version $Id: CocoonServlet.java 433543 2006-08-22 06:22:54Z crossley $
0103: */
0104: public class CocoonServlet extends HttpServlet {
0105:
0106: /**
0107: * Application <code>Context</code> Key for the servlet configuration
0108: * @since 2.1.3
0109: */
0110: public static final String CONTEXT_SERVLET_CONFIG = "servlet-config";
0111:
0112: // Processing time message
0113: protected static final String PROCESSED_BY = "Processed by "
0114: + Constants.COMPLETE_NAME + " in ";
0115:
0116: // Used by "show-time"
0117: static final float SECOND = 1000;
0118: static final float MINUTE = 60 * SECOND;
0119: static final float HOUR = 60 * MINUTE;
0120:
0121: private Logger log;
0122: private LoggerManager loggerManager;
0123:
0124: /**
0125: * The time the cocoon instance was created
0126: */
0127: protected long creationTime;
0128:
0129: /**
0130: * The <code>Cocoon</code> instance
0131: */
0132: protected Cocoon cocoon;
0133:
0134: /**
0135: * Holds exception happened during initialization (if any)
0136: */
0137: protected Exception exception;
0138:
0139: /**
0140: * Avalon application context
0141: */
0142: protected DefaultContext appContext = new DefaultContext();
0143:
0144: /**
0145: * Default value for {@link #allowReload} parameter (false)
0146: */
0147: protected static final boolean ALLOW_RELOAD = false;
0148:
0149: /**
0150: * Allow reloading of cocoon by specifying the
0151: * <code>cocoon-reload=true</code> parameter with a request
0152: */
0153: protected boolean allowReload;
0154:
0155: /**
0156: * Allow adding processing time to the response
0157: */
0158: protected boolean showTime;
0159:
0160: /**
0161: * If true, processing time will be added as an HTML comment
0162: */
0163: protected boolean hiddenShowTime;
0164:
0165: /** Flag to enable/disable X-Cocoon-Version header */
0166: private boolean showCocoonVersion;
0167:
0168: /**
0169: * Default value for {@link #enableUploads} parameter (false)
0170: */
0171: private static final boolean ENABLE_UPLOADS = false;
0172: private static final boolean SAVE_UPLOADS_TO_DISK = true;
0173: private static final int MAX_UPLOAD_SIZE = 10000000; // 10Mb
0174:
0175: /**
0176: * Allow processing of upload requests (mime/multipart)
0177: */
0178: private boolean enableUploads;
0179: private boolean autoSaveUploads;
0180: private boolean allowOverwrite;
0181: private boolean silentlyRename;
0182: private int maxUploadSize;
0183:
0184: private File uploadDir;
0185: private File workDir;
0186: private File cacheDir;
0187: private String containerEncoding;
0188: private String defaultFormEncoding;
0189:
0190: protected ServletContext servletContext;
0191:
0192: /** The classloader that will be set as the context classloader if init-classloader is true */
0193: protected ClassLoader classLoader = this .getClass()
0194: .getClassLoader();
0195: protected boolean initClassLoader = false;
0196:
0197: private String parentComponentManagerClass;
0198: private String parentComponentManagerInitParam;
0199:
0200: /** The parent ComponentManager, if any. Stored here in order to be able to dispose it in destroy(). */
0201: private ComponentManager parentComponentManager;
0202:
0203: protected String forceLoadParameter;
0204: protected String forceSystemProperty;
0205:
0206: /**
0207: * If true or not set, this class will try to catch and handle all Cocoon exceptions.
0208: * If false, it will rethrow them to the servlet container.
0209: */
0210: private boolean manageExceptions;
0211:
0212: /**
0213: * Flag to enable avalon excalibur instrumentation of Cocoon.
0214: */
0215: private boolean enableInstrumentation;
0216:
0217: /**
0218: * The <code>InstrumentManager</code> instance
0219: */
0220: private InstrumentManager instrumentManager;
0221:
0222: /**
0223: * This is the path to the servlet context (or the result
0224: * of calling getRealPath('/') on the ServletContext.
0225: * Note, that this can be null.
0226: */
0227: protected String servletContextPath;
0228:
0229: /**
0230: * This is the url to the servlet context directory
0231: */
0232: protected String servletContextURL;
0233:
0234: /**
0235: * The RequestFactory is responsible for wrapping multipart-encoded
0236: * forms and for handing the file payload of incoming requests
0237: */
0238: protected RequestFactory requestFactory;
0239:
0240: /**
0241: * Initialize this <code>CocoonServlet</code> instance. You will
0242: * notice that I have broken the init into sub methods to make it
0243: * easier to maintain (BL). The context is passed to a couple of
0244: * the subroutines. This is also because it is better to explicitly
0245: * pass variables than implicitely. It is both more maintainable,
0246: * and more elegant.
0247: *
0248: * @param conf The ServletConfig object from the servlet engine.
0249: *
0250: * @throws ServletException
0251: */
0252: public void init(ServletConfig conf) throws ServletException {
0253:
0254: super .init(conf);
0255:
0256: // Check the init-classloader parameter only if it's not already true.
0257: // This is useful for subclasses of this servlet that override the value
0258: // initially set by this class (i.e. false).
0259: if (!this .initClassLoader) {
0260: this .initClassLoader = getInitParameterAsBoolean(
0261: "init-classloader", false);
0262: }
0263:
0264: if (this .initClassLoader) {
0265: // Force context classloader so that JAXP can work correctly
0266: // (see javax.xml.parsers.FactoryFinder.findClassLoader())
0267: try {
0268: Thread.currentThread().setContextClassLoader(
0269: this .classLoader);
0270: } catch (Exception e) {
0271: // ignore this
0272: }
0273: }
0274:
0275: try {
0276: // FIXME (VG): We shouldn't have to specify these. Need to override
0277: // jaxp implementation of weblogic before initializing logger.
0278: // This piece of code is also required in the Cocoon class.
0279: String value = System
0280: .getProperty("javax.xml.parsers.SAXParserFactory");
0281: if (value != null && value.startsWith("weblogic")) {
0282: System.setProperty(
0283: "javax.xml.parsers.SAXParserFactory",
0284: "org.apache.xerces.jaxp.SAXParserFactoryImpl");
0285: System
0286: .setProperty(
0287: "javax.xml.parsers.DocumentBuilderFactory",
0288: "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
0289: }
0290: } catch (Exception e) {
0291: // Ignore security exception
0292: System.out
0293: .println("CocoonServlet: Could not check system properties, got: "
0294: + e);
0295: }
0296:
0297: this .servletContext = conf.getServletContext();
0298: this .appContext.put(Constants.CONTEXT_ENVIRONMENT_CONTEXT,
0299: new HttpContext(this .servletContext));
0300: this .servletContextPath = this .servletContext.getRealPath("/");
0301:
0302: // first init the work-directory for the logger.
0303: // this is required if we are running inside a war file!
0304: final String workDirParam = getInitParameter("work-directory");
0305: if (workDirParam != null) {
0306: if (this .servletContextPath == null) {
0307: // No context path : consider work-directory as absolute
0308: this .workDir = new File(workDirParam);
0309: } else {
0310: // Context path exists : is work-directory absolute ?
0311: File workDirParamFile = new File(workDirParam);
0312: if (workDirParamFile.isAbsolute()) {
0313: // Yes : keep it as is
0314: this .workDir = workDirParamFile;
0315: } else {
0316: // No : consider it relative to context path
0317: this .workDir = new File(servletContextPath,
0318: workDirParam);
0319: }
0320: }
0321: } else {
0322: this .workDir = (File) this .servletContext
0323: .getAttribute("javax.servlet.context.tempdir");
0324: this .workDir = new File(workDir, "cocoon-files");
0325: }
0326: this .workDir.mkdirs();
0327: this .appContext.put(Constants.CONTEXT_WORK_DIR, workDir);
0328:
0329: String path = this .servletContextPath;
0330: // these two variables are just for debugging. We can't log at this point
0331: // as the logger isn't initialized yet.
0332: String debugPathOne = null, debugPathTwo = null;
0333: if (path == null) {
0334: // Try to figure out the path of the root from that of WEB-INF
0335: try {
0336: path = this .servletContext.getResource("/WEB-INF")
0337: .toString();
0338: } catch (MalformedURLException me) {
0339: throw new ServletException(
0340: "Unable to get resource 'WEB-INF'.", me);
0341: }
0342: debugPathOne = path;
0343: path = path
0344: .substring(0, path.length() - "WEB-INF".length());
0345: debugPathTwo = path;
0346: }
0347: try {
0348: if (path.indexOf(':') > 1) {
0349: this .servletContextURL = path;
0350: } else {
0351: this .servletContextURL = new File(path).toURL()
0352: .toExternalForm();
0353: }
0354: } catch (MalformedURLException me) {
0355: // VG: Novell has absolute file names starting with the
0356: // volume name which is easily more then one letter.
0357: // Examples: sys:/apache/cocoon or sys:\apache\cocoon
0358: try {
0359: this .servletContextURL = new File(path).toURL()
0360: .toExternalForm();
0361: } catch (MalformedURLException ignored) {
0362: throw new ServletException(
0363: "Unable to determine servlet context URL.", me);
0364: }
0365: }
0366: try {
0367: this .appContext.put("context-root", new URL(
0368: this .servletContextURL));
0369: } catch (MalformedURLException ignore) {
0370: // we simply ignore this
0371: }
0372:
0373: // Init logger
0374: initLogger();
0375:
0376: if (getLogger().isDebugEnabled()) {
0377: getLogger().debug(
0378: "getRealPath for /: " + this .servletContextPath);
0379: if (this .servletContextPath == null) {
0380: getLogger().debug(
0381: "getResource for /WEB-INF: " + debugPathOne);
0382: getLogger().debug("Path for Root: " + debugPathTwo);
0383: }
0384: }
0385:
0386: this .forceLoadParameter = getInitParameter("load-class", null);
0387: this .forceSystemProperty = getInitParameter("force-property",
0388: null);
0389:
0390: // Output some debug info
0391: if (getLogger().isDebugEnabled()) {
0392: getLogger().debug(
0393: "Servlet Context URL: " + this .servletContextURL);
0394: if (workDirParam != null) {
0395: getLogger().debug(
0396: "Using work-directory " + this .workDir);
0397: } else {
0398: getLogger().debug(
0399: "Using default work-directory " + this .workDir);
0400: }
0401: }
0402:
0403: final String uploadDirParam = conf
0404: .getInitParameter("upload-directory");
0405: if (uploadDirParam != null) {
0406: if (this .servletContextPath == null) {
0407: this .uploadDir = new File(uploadDirParam);
0408: } else {
0409: // Context path exists : is upload-directory absolute ?
0410: File uploadDirParamFile = new File(uploadDirParam);
0411: if (uploadDirParamFile.isAbsolute()) {
0412: // Yes : keep it as is
0413: this .uploadDir = uploadDirParamFile;
0414: } else {
0415: // No : consider it relative to context path
0416: this .uploadDir = new File(servletContextPath,
0417: uploadDirParam);
0418: }
0419: }
0420: if (getLogger().isDebugEnabled()) {
0421: getLogger().debug(
0422: "Using upload-directory " + this .uploadDir);
0423: }
0424: } else {
0425: this .uploadDir = new File(workDir, "upload-dir"
0426: + File.separator);
0427: if (getLogger().isDebugEnabled()) {
0428: getLogger().debug(
0429: "Using default upload-directory "
0430: + this .uploadDir);
0431: }
0432: }
0433: this .uploadDir.mkdirs();
0434: this .appContext.put(Constants.CONTEXT_UPLOAD_DIR,
0435: this .uploadDir);
0436:
0437: this .enableUploads = getInitParameterAsBoolean(
0438: "enable-uploads", ENABLE_UPLOADS);
0439:
0440: this .autoSaveUploads = getInitParameterAsBoolean(
0441: "autosave-uploads", SAVE_UPLOADS_TO_DISK);
0442:
0443: String overwriteParam = getInitParameter("overwrite-uploads",
0444: "rename");
0445: // accepted values are deny|allow|rename - rename is default.
0446: if ("deny".equalsIgnoreCase(overwriteParam)) {
0447: this .allowOverwrite = false;
0448: this .silentlyRename = false;
0449: } else if ("allow".equalsIgnoreCase(overwriteParam)) {
0450: this .allowOverwrite = true;
0451: this .silentlyRename = false; // ignored in this case
0452: } else {
0453: // either rename is specified or unsupported value - default to rename.
0454: this .allowOverwrite = false;
0455: this .silentlyRename = true;
0456: }
0457:
0458: this .maxUploadSize = getInitParameterAsInteger(
0459: "upload-max-size", MAX_UPLOAD_SIZE);
0460:
0461: String cacheDirParam = conf.getInitParameter("cache-directory");
0462: if (cacheDirParam != null) {
0463: if (this .servletContextPath == null) {
0464: this .cacheDir = new File(cacheDirParam);
0465: } else {
0466: // Context path exists : is cache-directory absolute ?
0467: File cacheDirParamFile = new File(cacheDirParam);
0468: if (cacheDirParamFile.isAbsolute()) {
0469: // Yes : keep it as is
0470: this .cacheDir = cacheDirParamFile;
0471: } else {
0472: // No : consider it relative to context path
0473: this .cacheDir = new File(servletContextPath,
0474: cacheDirParam);
0475: }
0476: }
0477: if (getLogger().isDebugEnabled()) {
0478: getLogger().debug(
0479: "Using cache-directory " + this .cacheDir);
0480: }
0481: } else {
0482: this .cacheDir = IOUtils.createFile(workDir, "cache-dir"
0483: + File.separator);
0484: if (getLogger().isDebugEnabled()) {
0485: getLogger().debug(
0486: "cache-directory was not set - defaulting to "
0487: + this .cacheDir);
0488: }
0489: }
0490: this .cacheDir.mkdirs();
0491: this .appContext.put(Constants.CONTEXT_CACHE_DIR, this .cacheDir);
0492:
0493: this .appContext.put(Constants.CONTEXT_CONFIG_URL,
0494: getConfigFile(conf.getInitParameter("configurations")));
0495: if (conf.getInitParameter("configurations") == null) {
0496: if (getLogger().isDebugEnabled()) {
0497: getLogger()
0498: .debug(
0499: "configurations was not set - defaulting to... ?");
0500: }
0501: }
0502:
0503: // get allow reload parameter, default is true
0504: this .allowReload = getInitParameterAsBoolean("allow-reload",
0505: ALLOW_RELOAD);
0506:
0507: String value = conf.getInitParameter("show-time");
0508: this .showTime = BooleanUtils.toBoolean(value)
0509: || (this .hiddenShowTime = "hide".equals(value));
0510: if (value == null) {
0511: if (getLogger().isDebugEnabled()) {
0512: getLogger().debug(
0513: "show-time was not set - defaulting to false");
0514: }
0515: }
0516:
0517: this .showCocoonVersion = getInitParameterAsBoolean(
0518: "show-cocoon-version", true);
0519:
0520: parentComponentManagerClass = getInitParameter(
0521: "parent-component-manager", null);
0522: if (parentComponentManagerClass != null) {
0523: int dividerPos = parentComponentManagerClass.indexOf('/');
0524: if (dividerPos != -1) {
0525: parentComponentManagerInitParam = parentComponentManagerClass
0526: .substring(dividerPos + 1);
0527: parentComponentManagerClass = parentComponentManagerClass
0528: .substring(0, dividerPos);
0529: }
0530: }
0531:
0532: this .containerEncoding = getInitParameter("container-encoding",
0533: "ISO-8859-1");
0534: this .defaultFormEncoding = getInitParameter("form-encoding",
0535: "ISO-8859-1");
0536: this .appContext.put(Constants.CONTEXT_DEFAULT_ENCODING,
0537: this .defaultFormEncoding);
0538:
0539: this .manageExceptions = getInitParameterAsBoolean(
0540: "manage-exceptions", true);
0541:
0542: this .enableInstrumentation = getInitParameterAsBoolean(
0543: "enable-instrumentation", false);
0544:
0545: this .requestFactory = new RequestFactory(this .autoSaveUploads,
0546: this .uploadDir, this .allowOverwrite,
0547: this .silentlyRename, this .maxUploadSize,
0548: this .containerEncoding);
0549: // Add the servlet configuration
0550: this .appContext.put(CONTEXT_SERVLET_CONFIG, conf);
0551: this .createCocoon();
0552: }
0553:
0554: /**
0555: * Dispose Cocoon when servlet is destroyed
0556: */
0557: public void destroy() {
0558: if (this .initClassLoader) {
0559: try {
0560: Thread.currentThread().setContextClassLoader(
0561: this .classLoader);
0562: } catch (Exception e) {
0563: // Ignored
0564: }
0565: }
0566:
0567: if (this .cocoon != null) {
0568: if (getLogger().isDebugEnabled()) {
0569: getLogger().debug(
0570: "Servlet destroyed - disposing Cocoon");
0571: }
0572: disposeCocoon();
0573: }
0574:
0575: if (this .instrumentManager instanceof Disposable) {
0576: ((Disposable) this .instrumentManager).dispose();
0577: }
0578:
0579: if (this .parentComponentManager != null
0580: && this .parentComponentManager instanceof Disposable) {
0581: ((Disposable) this .parentComponentManager).dispose();
0582: }
0583:
0584: this .appContext = null;
0585: this .classLoader = null;
0586: this .log = null;
0587: this .loggerManager = null;
0588: }
0589:
0590: /**
0591: * Adds an URL to the classloader. Does nothing here, but is
0592: * overriden in {@link ParanoidCocoonServlet}.
0593: */
0594: protected void addClassLoaderURL(URL URL) {
0595: // Nothing
0596: }
0597:
0598: /**
0599: * Adds a directory to the classloader. Does nothing here, but is
0600: * overriden in {@link ParanoidCocoonServlet}.
0601: */
0602: protected void addClassLoaderDirectory(String dir) {
0603: // Nothing
0604: }
0605:
0606: /**
0607: * This builds the important ClassPath used by this Servlet. It
0608: * does so in a Servlet Engine neutral way. It uses the
0609: * <code>ServletContext</code>'s <code>getRealPath</code> method
0610: * to get the Servlet 2.2 identified classes and lib directories.
0611: * It iterates in alphabetical order through every file in the
0612: * lib directory and adds it to the classpath.
0613: *
0614: * Also, we add the files to the ClassLoader for the Cocoon system.
0615: * In order to protect ourselves from skitzofrantic classloaders,
0616: * we need to work with a known one.
0617: *
0618: * We need to get this to work properly when Cocoon is in a war.
0619: *
0620: * @throws ServletException
0621: */
0622: protected String getClassPath() throws ServletException {
0623: StringBuffer buildClassPath = new StringBuffer();
0624:
0625: File root = null;
0626: if (servletContextPath != null) {
0627: // Old method. There *MUST* be a better method than this...
0628:
0629: String classDir = this .servletContext
0630: .getRealPath("/WEB-INF/classes");
0631: String libDir = this .servletContext
0632: .getRealPath("/WEB-INF/lib");
0633:
0634: if (libDir != null) {
0635: root = new File(libDir);
0636: }
0637:
0638: if (classDir != null) {
0639: buildClassPath.append(classDir);
0640:
0641: addClassLoaderDirectory(classDir);
0642: }
0643: } else {
0644: // New(ish) method for war'd deployments
0645: URL classDirURL = null;
0646: URL libDirURL = null;
0647:
0648: try {
0649: classDirURL = this .servletContext
0650: .getResource("/WEB-INF/classes");
0651: } catch (MalformedURLException me) {
0652: if (getLogger().isWarnEnabled()) {
0653: this
0654: .getLogger()
0655: .warn(
0656: "Unable to add WEB-INF/classes to the classpath",
0657: me);
0658: }
0659: }
0660:
0661: try {
0662: libDirURL = this .servletContext
0663: .getResource("/WEB-INF/lib");
0664: } catch (MalformedURLException me) {
0665: if (getLogger().isWarnEnabled()) {
0666: this
0667: .getLogger()
0668: .warn(
0669: "Unable to add WEB-INF/lib to the classpath",
0670: me);
0671: }
0672: }
0673:
0674: if (libDirURL != null
0675: && libDirURL.toExternalForm().startsWith("file:")) {
0676: root = new File(libDirURL.toExternalForm().substring(
0677: "file:".length()));
0678: }
0679:
0680: if (classDirURL != null) {
0681: buildClassPath.append(classDirURL.toExternalForm());
0682:
0683: addClassLoaderURL(classDirURL);
0684: }
0685: }
0686:
0687: // Unable to find lib directory. Going the hard way.
0688: if (root == null) {
0689: root = extractLibraries();
0690: }
0691:
0692: if (root != null && root.isDirectory()) {
0693: File[] libraries = root.listFiles();
0694: Arrays.sort(libraries);
0695: for (int i = 0; i < libraries.length; i++) {
0696: String fullName = IOUtils.getFullFilename(libraries[i]);
0697: buildClassPath.append(File.pathSeparatorChar).append(
0698: fullName);
0699:
0700: addClassLoaderDirectory(fullName);
0701: }
0702: }
0703:
0704: buildClassPath.append(File.pathSeparatorChar).append(
0705: SystemUtils.JAVA_CLASS_PATH);
0706:
0707: buildClassPath.append(File.pathSeparatorChar).append(
0708: getExtraClassPath());
0709: return buildClassPath.toString();
0710: }
0711:
0712: private File extractLibraries() {
0713: try {
0714: URL manifestURL = this .servletContext
0715: .getResource("/META-INF/MANIFEST.MF");
0716: if (manifestURL == null) {
0717: this .getLogger().fatalError("Unable to get Manifest");
0718: return null;
0719: }
0720:
0721: Manifest mf = new Manifest(manifestURL.openStream());
0722: Attributes attr = mf.getMainAttributes();
0723: String libValue = attr.getValue("Cocoon-Libs");
0724: if (libValue == null) {
0725: this
0726: .getLogger()
0727: .fatalError(
0728: "Unable to get 'Cocoon-Libs' attribute from the Manifest");
0729: return null;
0730: }
0731:
0732: List libList = new ArrayList();
0733: for (StringTokenizer st = new StringTokenizer(libValue, " "); st
0734: .hasMoreTokens();) {
0735: libList.add(st.nextToken());
0736: }
0737:
0738: File root = new File(this .workDir, "lib");
0739: root.mkdirs();
0740:
0741: File[] oldLibs = root.listFiles();
0742: for (int i = 0; i < oldLibs.length; i++) {
0743: String oldLib = oldLibs[i].getName();
0744: if (!libList.contains(oldLib)) {
0745: this .getLogger().debug(
0746: "Removing old library " + oldLibs[i]);
0747: oldLibs[i].delete();
0748: }
0749: }
0750:
0751: this .getLogger().warn("Extracting libraries into " + root);
0752: byte[] buffer = new byte[65536];
0753: for (Iterator i = libList.iterator(); i.hasNext();) {
0754: String libName = (String) i.next();
0755:
0756: long lastModified = -1;
0757: try {
0758: lastModified = Long.parseLong(attr
0759: .getValue("Cocoon-Lib-"
0760: + libName.replace('.', '_')));
0761: } catch (Exception e) {
0762: this .getLogger().debug(
0763: "Failed to parse lastModified: "
0764: + attr
0765: .getValue("Cocoon-Lib-"
0766: + libName.replace(
0767: '.', '_')));
0768: }
0769:
0770: File lib = new File(root, libName);
0771: if (lib.exists() && lib.lastModified() != lastModified) {
0772: this .getLogger().debug(
0773: "Removing modified library " + lib);
0774: lib.delete();
0775: }
0776:
0777: InputStream is = null;
0778: OutputStream os = null;
0779: try {
0780: is = this .servletContext
0781: .getResourceAsStream("/WEB-INF/lib/"
0782: + libName);
0783: if (is != null) {
0784: this .getLogger().debug("Extracting " + libName);
0785: os = new FileOutputStream(lib);
0786: int count;
0787: while ((count = is.read(buffer)) > 0) {
0788: os.write(buffer, 0, count);
0789: }
0790: } else {
0791: this .getLogger().warn("Skipping " + libName);
0792: }
0793: } finally {
0794: if (os != null)
0795: os.close();
0796: if (is != null)
0797: is.close();
0798: }
0799:
0800: if (lastModified != -1) {
0801: lib.setLastModified(lastModified);
0802: }
0803: }
0804:
0805: return root;
0806: } catch (IOException e) {
0807: this .getLogger().fatalError(
0808: "Exception while processing Manifest file", e);
0809: return null;
0810: }
0811: }
0812:
0813: /**
0814: * Retreives the "extra-classpath" attribute, that needs to be
0815: * added to the class path.
0816: *
0817: * @throws ServletException
0818: */
0819: protected String getExtraClassPath() throws ServletException {
0820: String extraClassPath = this
0821: .getInitParameter("extra-classpath");
0822: if (extraClassPath != null) {
0823: StringBuffer sb = new StringBuffer();
0824: StringTokenizer st = new StringTokenizer(extraClassPath,
0825: SystemUtils.PATH_SEPARATOR, false);
0826: int i = 0;
0827: while (st.hasMoreTokens()) {
0828: String s = st.nextToken();
0829: if (i++ > 0) {
0830: sb.append(File.pathSeparatorChar);
0831: }
0832: if ((s.charAt(0) == File.separatorChar)
0833: || (s.charAt(1) == ':')) {
0834: if (getLogger().isDebugEnabled()) {
0835: getLogger().debug(
0836: "extraClassPath is absolute: " + s);
0837: }
0838: sb.append(s);
0839:
0840: addClassLoaderDirectory(s);
0841: } else {
0842: if (s.indexOf("${") != -1) {
0843: String path = StringUtils.replaceToken(s);
0844: sb.append(path);
0845: if (getLogger().isDebugEnabled()) {
0846: getLogger().debug(
0847: "extraClassPath is not absolute replacing using token: ["
0848: + s + "] : " + path);
0849: }
0850: addClassLoaderDirectory(path);
0851: } else {
0852: String path = null;
0853: if (this .servletContextPath != null) {
0854: path = this .servletContextPath + s;
0855: if (getLogger().isDebugEnabled()) {
0856: getLogger().debug(
0857: "extraClassPath is not absolute pre-pending context path: "
0858: + path);
0859: }
0860: } else {
0861: path = this .workDir.toString() + s;
0862: if (getLogger().isDebugEnabled()) {
0863: getLogger().debug(
0864: "extraClassPath is not absolute pre-pending work-directory: "
0865: + path);
0866: }
0867: }
0868: sb.append(path);
0869: addClassLoaderDirectory(path);
0870: }
0871: }
0872: }
0873: return sb.toString();
0874: }
0875: return "";
0876: }
0877:
0878: /**
0879: * Set up the log level and path. The default log level is
0880: * Priority.ERROR, although it can be overwritten by the parameter
0881: * "log-level". The log system goes to both a file and the Servlet
0882: * container's log system. Only messages that are Priority.ERROR
0883: * and above go to the servlet context. The log messages can
0884: * be as restrictive (Priority.FATAL_ERROR and above) or as liberal
0885: * (Priority.DEBUG and above) as you want that get routed to the
0886: * file.
0887: */
0888: protected void initLogger() {
0889: final String logLevel = getInitParameter("log-level", "INFO");
0890:
0891: final String accesslogger = getInitParameter("servlet-logger",
0892: "cocoon");
0893:
0894: final Priority logPriority = Priority
0895: .getPriorityForName(logLevel);
0896:
0897: final CocoonLogFormatter formatter = new CocoonLogFormatter();
0898: formatter
0899: .setFormat("%7.7{priority} %{time} [%8.8{category}] "
0900: + "(%{uri}) %{thread}/%{class:short}: %{message}\\n%{throwable}");
0901: final ServletOutputLogTarget servTarget = new ServletOutputLogTarget(
0902: this .servletContext, formatter);
0903:
0904: final Hierarchy defaultHierarchy = Hierarchy
0905: .getDefaultHierarchy();
0906: final ErrorHandler errorHandler = new DefaultErrorHandler();
0907: defaultHierarchy.setErrorHandler(errorHandler);
0908: defaultHierarchy.setDefaultLogTarget(servTarget);
0909: defaultHierarchy.setDefaultPriority(logPriority);
0910: final Logger logger = new LogKitLogger(Hierarchy
0911: .getDefaultHierarchy().getLoggerFor(""));
0912: final String loggerManagerClass = this .getInitParameter(
0913: "logger-class", LogKitLoggerManager.class.getName());
0914:
0915: // the log4j support requires currently that the log4j system is already configured elsewhere
0916:
0917: final LoggerManager loggerManager = newLoggerManager(
0918: loggerManagerClass, defaultHierarchy);
0919: ContainerUtil.enableLogging(loggerManager, logger);
0920:
0921: final DefaultContext subcontext = new DefaultContext(
0922: this .appContext);
0923: subcontext.put("servlet-context", this .servletContext);
0924: subcontext.put("context-work", this .workDir);
0925: if (this .servletContextPath == null) {
0926: File logSCDir = new File(this .workDir, "log");
0927: logSCDir.mkdirs();
0928: if (logger.isWarnEnabled()) {
0929: logger.warn("Setting context-root for LogKit to "
0930: + logSCDir);
0931: }
0932: subcontext.put("context-root", logSCDir.toString());
0933: } else {
0934: subcontext.put("context-root", this .servletContextPath);
0935: }
0936:
0937: try {
0938: ContainerUtil.contextualize(loggerManager, subcontext);
0939: this .loggerManager = loggerManager;
0940:
0941: if (loggerManager instanceof Configurable) {
0942: //Configure the logkit management
0943: String logkitConfig = getInitParameter("logkit-config",
0944: "/WEB-INF/logkit.xconf");
0945:
0946: // test if this is a qualified url
0947: InputStream is = null;
0948: if (logkitConfig.indexOf(':') == -1) {
0949: is = this .servletContext
0950: .getResourceAsStream(logkitConfig);
0951: if (is == null)
0952: is = new FileInputStream(logkitConfig);
0953: } else {
0954: URL logkitURL = new URL(logkitConfig);
0955: is = logkitURL.openStream();
0956: }
0957: final DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
0958: final Configuration conf = builder.build(is);
0959: ContainerUtil.configure(loggerManager, conf);
0960: }
0961:
0962: // let's configure log4j
0963: final String log4jConfig = getInitParameter("log4j-config",
0964: null);
0965: if (log4jConfig != null) {
0966: final Log4JConfigurator configurator = new Log4JConfigurator(
0967: subcontext);
0968:
0969: // test if this is a qualified url
0970: InputStream is = null;
0971: if (log4jConfig.indexOf(':') == -1) {
0972: is = this .servletContext
0973: .getResourceAsStream(log4jConfig);
0974: if (is == null)
0975: is = new FileInputStream(log4jConfig);
0976: } else {
0977: final URL log4jURL = new URL(log4jConfig);
0978: is = log4jURL.openStream();
0979: }
0980: configurator.doConfigure(is, LogManager
0981: .getLoggerRepository());
0982: }
0983:
0984: ContainerUtil.initialize(loggerManager);
0985: } catch (Exception e) {
0986: errorHandler
0987: .error(
0988: "Could not set up Cocoon Logger, will use screen instead",
0989: e, null);
0990: }
0991:
0992: this .log = this .loggerManager
0993: .getLoggerForCategory(accesslogger);
0994:
0995: final String deprecationLevel = getInitParameter(
0996: "forbidden-deprecation-level", "ERROR");
0997: Deprecation.setForbiddenLevel(Deprecation.LogLevel
0998: .getLevel(deprecationLevel));
0999: }
1000:
1001: private LoggerManager newLoggerManager(String loggerManagerClass,
1002: Hierarchy hierarchy) {
1003: if (loggerManagerClass.equals(LogKitLoggerManager.class
1004: .getName())) {
1005: return new LogKitLoggerManager(hierarchy);
1006: } else if (loggerManagerClass.equals(Log4JLoggerManager.class
1007: .getName())
1008: || loggerManagerClass.equalsIgnoreCase("LOG4J")) {
1009: return new Log4JLoggerManager();
1010: } else {
1011: try {
1012: Class clazz = Class.forName(loggerManagerClass);
1013: return (LoggerManager) clazz.newInstance();
1014: } catch (Exception e) {
1015: return new LogKitLoggerManager(hierarchy);
1016: }
1017: }
1018: }
1019:
1020: /**
1021: * Set the ConfigFile for the Cocoon object.
1022: *
1023: * @param configFileName The file location for the cocoon.xconf
1024: *
1025: * @throws ServletException
1026: */
1027: private URL getConfigFile(final String configFileName)
1028: throws ServletException {
1029: final String usedFileName;
1030:
1031: if (configFileName == null) {
1032: if (getLogger().isWarnEnabled()) {
1033: getLogger()
1034: .warn(
1035: "Servlet initialization argument 'configurations' not specified, attempting to use '/WEB-INF/cocoon.xconf'");
1036: }
1037: usedFileName = "/WEB-INF/cocoon.xconf";
1038: } else {
1039: usedFileName = configFileName;
1040: }
1041:
1042: if (getLogger().isDebugEnabled()) {
1043: getLogger().debug(
1044: "Using configuration file: " + usedFileName);
1045: }
1046:
1047: URL result;
1048: try {
1049: // test if this is a qualified url
1050: if (usedFileName.indexOf(':') == -1) {
1051: result = this .servletContext.getResource(usedFileName);
1052: } else {
1053: result = new URL(usedFileName);
1054: }
1055: } catch (Exception mue) {
1056: String msg = "Init parameter 'configurations' is invalid : "
1057: + usedFileName;
1058: getLogger().error(msg, mue);
1059: throw new ServletException(msg, mue);
1060: }
1061:
1062: if (result == null) {
1063: File resultFile = new File(usedFileName);
1064: if (resultFile.isFile()) {
1065: try {
1066: result = resultFile.getCanonicalFile().toURL();
1067: } catch (Exception e) {
1068: String msg = "Init parameter 'configurations' is invalid : "
1069: + usedFileName;
1070: getLogger().error(msg, e);
1071: throw new ServletException(msg, e);
1072: }
1073: }
1074: }
1075:
1076: if (result == null) {
1077: String msg = "Init parameter 'configuration' doesn't name an existing resource : "
1078: + usedFileName;
1079: getLogger().error(msg);
1080: throw new ServletException(msg);
1081: }
1082: return result;
1083: }
1084:
1085: /**
1086: * Handle the <code>load-class</code> parameter. This overcomes
1087: * limits in many classpath issues. One of the more notorious
1088: * ones is a bug in WebSphere that does not load the URL handler
1089: * for the <code>classloader://</code> protocol. In order to
1090: * overcome that bug, set <code>load-class</code> parameter to
1091: * the <code>com.ibm.servlet.classloader.Handler</code> value.
1092: *
1093: * <p>If you need to load more than one class, then separate each
1094: * entry with whitespace, a comma, or a semi-colon. Cocoon will
1095: * strip any whitespace from the entry.</p>
1096: */
1097: private void forceLoad() {
1098: if (this .forceLoadParameter != null) {
1099: StringTokenizer fqcnTokenizer = new StringTokenizer(
1100: forceLoadParameter, " \t\r\n\f;,", false);
1101:
1102: while (fqcnTokenizer.hasMoreTokens()) {
1103: final String fqcn = fqcnTokenizer.nextToken().trim();
1104:
1105: try {
1106: if (getLogger().isDebugEnabled()) {
1107: getLogger().debug("Loading: " + fqcn);
1108: }
1109: ClassUtils.loadClass(fqcn).newInstance();
1110: } catch (Exception e) {
1111: if (getLogger().isWarnEnabled()) {
1112: getLogger().warn(
1113: "Could not load class: " + fqcn, e);
1114: }
1115: // Do not throw an exception, because it is not a fatal error.
1116: }
1117: }
1118: }
1119: }
1120:
1121: /**
1122: * Handle the "force-property" parameter.
1123: *
1124: * If you need to force more than one property to load, then
1125: * separate each entry with whitespace, a comma, or a semi-colon.
1126: * Cocoon will strip any whitespace from the entry.
1127: */
1128: private void forceProperty() {
1129: if (this .forceSystemProperty != null) {
1130: StringTokenizer tokenizer = new StringTokenizer(
1131: forceSystemProperty, " \t\r\n\f;,", false);
1132:
1133: while (tokenizer.hasMoreTokens()) {
1134: final String property = tokenizer.nextToken().trim();
1135: if (property.indexOf('=') == -1) {
1136: continue;
1137: }
1138: try {
1139: String key = property.substring(0, property
1140: .indexOf('='));
1141: String value = property.substring(property
1142: .indexOf('=') + 1);
1143: if (value.indexOf("${") != -1) {
1144: value = StringUtils.replaceToken(value);
1145: }
1146: if (getLogger().isDebugEnabled()) {
1147: getLogger().debug(
1148: "Setting " + key + "=" + value);
1149: }
1150: System.setProperty(key, value);
1151: } catch (Exception e) {
1152: if (getLogger().isWarnEnabled()) {
1153: getLogger().warn(
1154: "Could not set property: " + property,
1155: e);
1156: }
1157: // Do not throw an exception, because it is not a fatal error.
1158: }
1159: }
1160: }
1161: }
1162:
1163: /**
1164: * Process the specified <code>HttpServletRequest</code> producing output
1165: * on the specified <code>HttpServletResponse</code>.
1166: */
1167: public void service(HttpServletRequest req, HttpServletResponse res)
1168: throws ServletException, IOException {
1169:
1170: /* HACK for reducing class loader problems. */
1171: /* example: xalan extensions fail if someone adds xalan jars in tomcat3.2.1/lib */
1172: if (this .initClassLoader) {
1173: try {
1174: Thread.currentThread().setContextClassLoader(
1175: this .classLoader);
1176: } catch (Exception e) {
1177: }
1178: }
1179:
1180: // used for timing the processing
1181: StopWatch stopWatch = new StopWatch();
1182: stopWatch.start();
1183:
1184: // add the cocoon header timestamp
1185: if (this .showCocoonVersion) {
1186: res.addHeader("X-Cocoon-Version", Constants.VERSION);
1187: }
1188:
1189: // get the request (wrapped if contains multipart-form data)
1190: HttpServletRequest request;
1191: try {
1192: if (this .enableUploads) {
1193: request = requestFactory.getServletRequest(req);
1194: } else {
1195: request = req;
1196: }
1197: } catch (Exception e) {
1198: if (getLogger().isErrorEnabled()) {
1199: getLogger().error("Problem with Cocoon servlet", e);
1200: }
1201:
1202: manageException(req, res, null, null,
1203: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1204: "Problem in creating the Request", null, null, e);
1205: return;
1206: }
1207:
1208: // Get the cocoon engine instance
1209:
1210: if (reloadCocoon(request.getPathInfo(), request
1211: .getParameter(Constants.RELOAD_PARAM))) {
1212: disposeCocoon();
1213: initLogger();
1214: createCocoon();
1215: }
1216:
1217: // Check if cocoon was initialized
1218: if (this .cocoon == null) {
1219: manageException(
1220: request,
1221: res,
1222: null,
1223: null,
1224: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1225: "Initialization Problem",
1226: null /* "Cocoon was not initialized" */,
1227: null /* "Cocoon was not initialized, cannot process request" */,
1228: this .exception);
1229: return;
1230: }
1231:
1232: // We got it... Process the request
1233: String uri = request.getServletPath();
1234: if (uri == null) {
1235: uri = "";
1236: }
1237: String pathInfo = request.getPathInfo();
1238: if (pathInfo != null) {
1239: // VG: WebLogic fix: Both uri and pathInfo starts with '/'
1240: // This problem exists only in WL6.1sp2, not in WL6.0sp2 or WL7.0b.
1241: if (uri.length() > 0 && uri.charAt(0) == '/') {
1242: uri = uri.substring(1);
1243: }
1244: uri += pathInfo;
1245: }
1246:
1247: if (uri.length() == 0) {
1248: /* empty relative URI
1249: -> HTTP-redirect from /cocoon to /cocoon/ to avoid
1250: StringIndexOutOfBoundsException when calling
1251: "".charAt(0)
1252: else process URI normally
1253: */
1254: String prefix = request.getRequestURI();
1255: if (prefix == null) {
1256: prefix = "";
1257: }
1258:
1259: res.sendRedirect(res.encodeRedirectURL(prefix + "/"));
1260: return;
1261: }
1262:
1263: String contentType = null;
1264: ContextMap ctxMap = null;
1265:
1266: Environment env;
1267: try {
1268: if (uri.charAt(0) == '/') {
1269: uri = uri.substring(1);
1270: }
1271: // Pass uri into environment without URLDecoding, as it is already decoded.
1272: env = getEnvironment(uri, request, res);
1273: } catch (Exception e) {
1274: if (getLogger().isErrorEnabled()) {
1275: getLogger().error("Problem with Cocoon servlet", e);
1276: }
1277:
1278: manageException(request, res, null, uri,
1279: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1280: "Problem in creating the Environment", null, null,
1281: e);
1282: return;
1283: }
1284:
1285: try {
1286: try {
1287: // Initialize a fresh log context containing the object model: it
1288: // will be used by the CocoonLogFormatter
1289: ctxMap = ContextMap.getCurrentContext();
1290: // Add thread name (default content for empty context)
1291: String threadName = Thread.currentThread().getName();
1292: ctxMap.set("threadName", threadName);
1293: // Add the object model
1294: ctxMap.set("objectModel", env.getObjectModel());
1295: // Add a unique request id (threadName + currentTime
1296: ctxMap.set("request-id", threadName
1297: + System.currentTimeMillis());
1298:
1299: if (this .cocoon.process(env)) {
1300: contentType = env.getContentType();
1301: } else {
1302: // We reach this when there is nothing in the processing change that matches
1303: // the request. For example, no matcher matches.
1304: getLogger()
1305: .fatalError(
1306: "The Cocoon engine failed to process the request.");
1307: manageException(
1308: request,
1309: res,
1310: env,
1311: uri,
1312: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1313: "Request Processing Failed",
1314: "Cocoon engine failed in process the request",
1315: "The processing engine failed to process the request. This could be due to lack of matching or bugs in the pipeline engine.",
1316: null);
1317: return;
1318: }
1319: } catch (ResourceNotFoundException e) {
1320: if (getLogger().isDebugEnabled()) {
1321: getLogger().warn(e.getMessage(), e);
1322: } else if (getLogger().isWarnEnabled()) {
1323: getLogger().warn(e.getMessage());
1324: }
1325:
1326: manageException(request, res, env, uri,
1327: HttpServletResponse.SC_NOT_FOUND,
1328: "Resource Not Found", "Resource Not Found",
1329: "The requested resource \""
1330: + request.getRequestURI()
1331: + "\" could not be found", e);
1332: return;
1333:
1334: } catch (ConnectionResetException e) {
1335: if (getLogger().isDebugEnabled()) {
1336: getLogger().debug(e.toString(), e);
1337: } else if (getLogger().isWarnEnabled()) {
1338: getLogger().warn(e.toString());
1339: }
1340:
1341: } catch (IOException e) {
1342: // Tomcat5 wraps SocketException into ClientAbortException which extends IOException.
1343: if (getLogger().isDebugEnabled()) {
1344: getLogger().debug(e.toString(), e);
1345: } else if (getLogger().isWarnEnabled()) {
1346: getLogger().warn(e.toString());
1347: }
1348:
1349: } catch (Exception e) {
1350: if (getLogger().isErrorEnabled()) {
1351: getLogger().error("Internal Cocoon Problem", e);
1352: }
1353:
1354: manageException(request, res, env, uri,
1355: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
1356: "Internal Server Error", null, null, e);
1357: return;
1358: }
1359:
1360: stopWatch.stop();
1361: String timeString = null;
1362: if (getLogger().isInfoEnabled()) {
1363: timeString = processTime(stopWatch.getTime());
1364: getLogger().info("'" + uri + "' " + timeString);
1365: }
1366:
1367: if (contentType != null && contentType.equals("text/html")) {
1368: String showTime = request
1369: .getParameter(Constants.SHOWTIME_PARAM);
1370: boolean show = this .showTime;
1371: if (showTime != null) {
1372: show = !showTime.equalsIgnoreCase("no");
1373: }
1374: if (show) {
1375: if (timeString == null) {
1376: timeString = processTime(stopWatch.getTime());
1377: }
1378: boolean hide = this .hiddenShowTime;
1379: if (showTime != null) {
1380: hide = showTime.equalsIgnoreCase("hide");
1381: }
1382: ServletOutputStream out = res.getOutputStream();
1383: out.print((hide) ? "<!-- " : "<p>");
1384: out.print(timeString);
1385: out.println((hide) ? " -->" : "</p>");
1386: }
1387: }
1388: } finally {
1389: if (ctxMap != null) {
1390: ctxMap.clear();
1391: }
1392:
1393: try {
1394: if (request instanceof MultipartHttpServletRequest) {
1395: if (getLogger().isDebugEnabled()) {
1396: getLogger().debug("Deleting uploaded file(s).");
1397: }
1398: ((MultipartHttpServletRequest) request).cleanup();
1399: }
1400: } catch (IOException e) {
1401: getLogger()
1402: .error(
1403: "Cocoon got an Exception while trying to cleanup the uploaded files.",
1404: e);
1405: }
1406:
1407: /*
1408: * Servlet Specification 2.2, 6.5 Closure of Response Object:
1409: *
1410: * A number of events can indicate that the servlet has provided all of the
1411: * content to satisfy the request and that the response object can be
1412: * considered to be closed. The events are:
1413: * o The termination of the service method of the servlet.
1414: * o When the amount of content specified in the setContentLength method
1415: * of the response has been written to the response.
1416: * o The sendError method is called.
1417: * o The sendRedirect method is called.
1418: * When a response is closed, all content in the response buffer, if any remains,
1419: * must be immediately flushed to the client.
1420: *
1421: * Due to the above, out.flush() and out.close() are not necessary, and sometimes
1422: * (if sendError or sendRedirect were used) request may be already closed.
1423: */
1424: }
1425: }
1426:
1427: protected void manageException(HttpServletRequest req,
1428: HttpServletResponse res, Environment env, String uri,
1429: int errorStatus, String title, String message,
1430: String description, Exception e) throws IOException {
1431: if (this .manageExceptions) {
1432: if (env != null) {
1433: env.tryResetResponse();
1434: } else {
1435: res.reset();
1436: }
1437:
1438: String type = Notifying.FATAL_NOTIFICATION;
1439: HashMap extraDescriptions = null;
1440:
1441: if (errorStatus == HttpServletResponse.SC_NOT_FOUND) {
1442: type = "resource-not-found";
1443: // Do not show the exception stacktrace for such common errors.
1444: e = null;
1445: } else {
1446: extraDescriptions = new HashMap(2);
1447: extraDescriptions.put(Notifying.EXTRA_REQUESTURI, req
1448: .getRequestURI());
1449: if (uri != null) {
1450: extraDescriptions.put("Request URI", uri);
1451: }
1452:
1453: // Do not show exception stack trace when log level is WARN or above. Show only message.
1454: if (!getLogger().isInfoEnabled()) {
1455: Throwable t = DefaultNotifyingBuilder
1456: .getRootCause(e);
1457: if (t != null)
1458: extraDescriptions.put(Notifying.EXTRA_CAUSE, t
1459: .getMessage());
1460: e = null;
1461: }
1462: }
1463:
1464: Notifying n = new DefaultNotifyingBuilder().build(this , e,
1465: type, title, "Cocoon Servlet", message,
1466: description, extraDescriptions);
1467:
1468: res.setContentType("text/html");
1469: res.setStatus(errorStatus);
1470: Notifier.notify(n, res.getOutputStream(), "text/html");
1471: } else {
1472: res.sendError(errorStatus, title);
1473: res.flushBuffer();
1474: }
1475: }
1476:
1477: /**
1478: * Create the environment for the request
1479: */
1480: protected Environment getEnvironment(String uri,
1481: HttpServletRequest req, HttpServletResponse res)
1482: throws Exception {
1483: HttpEnvironment env;
1484:
1485: String formEncoding = req.getParameter("cocoon-form-encoding");
1486: if (formEncoding == null) {
1487: formEncoding = this .defaultFormEncoding;
1488: }
1489: env = new HttpEnvironment(uri, this .servletContextURL, req,
1490: res, this .servletContext, (HttpContext) this .appContext
1491: .get(Constants.CONTEXT_ENVIRONMENT_CONTEXT),
1492: this .containerEncoding, formEncoding);
1493: env.enableLogging(getLogger());
1494: return env;
1495: }
1496:
1497: /**
1498: * Instatiates the parent component manager, as specified in the
1499: * parent-component-manager init parameter.
1500: *
1501: * If none is specified, the method returns <code>null</code>.
1502: *
1503: * @return the parent component manager, or <code>null</code>.
1504: */
1505: protected synchronized ComponentManager getParentComponentManager() {
1506: if (parentComponentManager != null
1507: && parentComponentManager instanceof Disposable) {
1508: ((Disposable) parentComponentManager).dispose();
1509: }
1510:
1511: parentComponentManager = null;
1512: if (parentComponentManagerClass != null) {
1513: try {
1514: Class pcm = ClassUtils
1515: .loadClass(parentComponentManagerClass);
1516: Constructor pcmc = pcm
1517: .getConstructor(new Class[] { String.class });
1518: parentComponentManager = (ComponentManager) pcmc
1519: .newInstance(new Object[] { parentComponentManagerInitParam });
1520:
1521: if (parentComponentManager instanceof LogEnabled) {
1522: ((LogEnabled) parentComponentManager)
1523: .enableLogging(getLogger());
1524: }
1525: if (parentComponentManager instanceof Contextualizable) {
1526: ((Contextualizable) parentComponentManager)
1527: .contextualize(this .appContext);
1528: }
1529: if (parentComponentManager instanceof Initializable) {
1530: ((Initializable) parentComponentManager)
1531: .initialize();
1532: }
1533: } catch (Exception e) {
1534: if (getLogger().isErrorEnabled()) {
1535: getLogger()
1536: .error(
1537: "Could not initialize parent component manager.",
1538: e);
1539: }
1540: }
1541: }
1542: return parentComponentManager;
1543: }
1544:
1545: /**
1546: * Creates the Cocoon object and handles exception handling.
1547: */
1548: protected synchronized void createCocoon() throws ServletException {
1549:
1550: // Recheck that we need to create the cocoon object. It can have been created by
1551: // a concurrent invocation to this method.
1552: if (this .cocoon != null) {
1553: return;
1554: }
1555:
1556: /* HACK for reducing class loader problems. */
1557: /* example: xalan extensions fail if someone adds xalan jars in tomcat3.2.1/lib */
1558: if (this .initClassLoader) {
1559: try {
1560: Thread.currentThread().setContextClassLoader(
1561: this .classLoader);
1562: } catch (Exception e) {
1563: }
1564: }
1565:
1566: updateEnvironment();
1567: forceLoad();
1568: forceProperty();
1569:
1570: try {
1571: this .exception = null;
1572: URL configFile = (URL) this .appContext
1573: .get(Constants.CONTEXT_CONFIG_URL);
1574: if (getLogger().isInfoEnabled()) {
1575: getLogger().info(
1576: "Reloading from: "
1577: + configFile.toExternalForm());
1578: }
1579: Cocoon c = (Cocoon) ClassUtils
1580: .newInstance("org.apache.cocoon.Cocoon");
1581: ContainerUtil.enableLogging(c, getCocoonLogger());
1582: c.setLoggerManager(getLoggerManager());
1583: ContainerUtil.contextualize(c, this .appContext);
1584: final ComponentManager parent = this
1585: .getParentComponentManager();
1586: if (parent != null) {
1587: ContainerUtil.compose(c, parent);
1588: }
1589: if (this .enableInstrumentation) {
1590: c.setInstrumentManager(getInstrumentManager());
1591: }
1592: ContainerUtil.initialize(c);
1593: this .creationTime = System.currentTimeMillis();
1594:
1595: this .cocoon = c;
1596: } catch (Exception e) {
1597: if (getLogger().isErrorEnabled()) {
1598: getLogger().error("Exception reloading", e);
1599: }
1600: this .exception = e;
1601: disposeCocoon();
1602: }
1603: }
1604:
1605: private Logger getCocoonLogger() {
1606: final String rootlogger = getInitParameter("cocoon-logger");
1607: if (rootlogger != null) {
1608: return this .getLoggerManager().getLoggerForCategory(
1609: rootlogger);
1610: } else {
1611: return getLogger();
1612: }
1613: }
1614:
1615: /**
1616: * Method to update the environment before Cocoon instances are created.
1617: *
1618: * This is also useful if you wish to customize any of the 'protected'
1619: * variables from this class before a Cocoon instance is built in a derivative
1620: * of this class (eg. Cocoon Context).
1621: */
1622: protected void updateEnvironment() throws ServletException {
1623: this .appContext
1624: .put(Constants.CONTEXT_CLASS_LOADER, classLoader);
1625: this .appContext
1626: .put(Constants.CONTEXT_CLASSPATH, getClassPath());
1627: }
1628:
1629: /**
1630: * Helper method to obtain an <code>InstrumentManager</code> instance
1631: *
1632: * @return an <code>InstrumentManager</code> instance
1633: */
1634: private InstrumentManager getInstrumentManager() throws Exception {
1635: String imConfig = getInitParameter("instrumentation-config");
1636: if (imConfig == null) {
1637: throw new ServletException(
1638: "Please define the init-param 'instrumentation-config' in your web.xml");
1639: }
1640:
1641: final InputStream is = this .servletContext
1642: .getResourceAsStream(imConfig);
1643: final DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
1644: final Configuration conf = builder.build(is);
1645:
1646: // Get the logger for the instrument manager
1647: final String imLoggerCategory = conf.getAttribute("logger",
1648: "core.instrument");
1649: Logger imLogger = this .loggerManager
1650: .getLoggerForCategory(imLoggerCategory);
1651:
1652: // Set up the Instrument Manager
1653: DefaultInstrumentManagerImpl instrumentManager = new DefaultInstrumentManagerImpl();
1654: instrumentManager.enableLogging(imLogger);
1655: instrumentManager.configure(conf);
1656: instrumentManager.initialize();
1657:
1658: if (getLogger().isDebugEnabled()) {
1659: getLogger().debug(
1660: "Instrument manager created " + instrumentManager);
1661: }
1662:
1663: this .instrumentManager = instrumentManager;
1664: return instrumentManager;
1665: }
1666:
1667: private String processTime(long time) {
1668: StringBuffer out = new StringBuffer(PROCESSED_BY);
1669: if (time <= SECOND) {
1670: out.append(time);
1671: out.append(" milliseconds.");
1672: } else if (time <= MINUTE) {
1673: out.append(time / SECOND);
1674: out.append(" seconds.");
1675: } else if (time <= HOUR) {
1676: out.append(time / MINUTE);
1677: out.append(" minutes.");
1678: } else {
1679: out.append(time / HOUR);
1680: out.append(" hours.");
1681: }
1682: return out.toString();
1683: }
1684:
1685: /**
1686: * Gets the current cocoon object. Reload cocoon if configuration
1687: * changed or we are reloading.
1688: */
1689: private boolean reloadCocoon(final String pathInfo,
1690: final String reloadParam) throws ServletException {
1691: if (this .allowReload) {
1692: boolean reload = false;
1693:
1694: if (this .cocoon != null) {
1695: if (this .cocoon.modifiedSince(this .creationTime)) {
1696: if (getLogger().isInfoEnabled()) {
1697: getLogger().info(
1698: "Configuration changed reload attempt");
1699: }
1700: reload = true;
1701: } else if (pathInfo == null && reloadParam != null) {
1702: if (getLogger().isInfoEnabled()) {
1703: getLogger().info("Forced reload attempt");
1704: }
1705: reload = true;
1706: }
1707: } else if (pathInfo == null && reloadParam != null) {
1708: if (getLogger().isInfoEnabled()) {
1709: getLogger().info("Invalid configurations reload");
1710: }
1711: reload = true;
1712: }
1713:
1714: return reload;
1715: } else {
1716: return false;
1717: }
1718: }
1719:
1720: /**
1721: * Destroy Cocoon
1722: */
1723: protected final void disposeCocoon() {
1724: if (this .cocoon != null) {
1725: ContainerUtil.dispose(this .cocoon);
1726: this .cocoon = null;
1727: }
1728: }
1729:
1730: /**
1731: * Get an initialisation parameter. The value is trimmed, and null is returned if the trimmed value
1732: * is empty.
1733: */
1734: public String getInitParameter(String name) {
1735: String result = super .getInitParameter(name);
1736: if (result != null) {
1737: result = result.trim();
1738: if (result.length() == 0) {
1739: result = null;
1740: }
1741: }
1742:
1743: return result;
1744: }
1745:
1746: /** Convenience method to access servlet parameters */
1747: protected String getInitParameter(String name, String defaultValue) {
1748: String result = getInitParameter(name);
1749: if (result == null) {
1750: if (getLogger() != null && getLogger().isDebugEnabled()) {
1751: getLogger().debug(
1752: name + " was not set - defaulting to '"
1753: + defaultValue + "'");
1754: }
1755: return defaultValue;
1756: } else {
1757: return result;
1758: }
1759: }
1760:
1761: /** Convenience method to access boolean servlet parameters */
1762: protected boolean getInitParameterAsBoolean(String name,
1763: boolean defaultValue) {
1764: String value = getInitParameter(name);
1765: if (value == null) {
1766: if (getLogger() != null && getLogger().isDebugEnabled()) {
1767: getLogger().debug(
1768: name + " was not set - defaulting to '"
1769: + defaultValue + "'");
1770: }
1771: return defaultValue;
1772: }
1773:
1774: return BooleanUtils.toBoolean(value);
1775: }
1776:
1777: protected int getInitParameterAsInteger(String name,
1778: int defaultValue) {
1779: String value = getInitParameter(name);
1780: if (value == null) {
1781: if (getLogger() != null && getLogger().isDebugEnabled()) {
1782: getLogger().debug(
1783: name + " was not set - defaulting to '"
1784: + defaultValue + "'");
1785: }
1786: return defaultValue;
1787: } else {
1788: return Integer.parseInt(value);
1789: }
1790: }
1791:
1792: protected Logger getLogger() {
1793: return this .log;
1794: }
1795:
1796: protected LoggerManager getLoggerManager() {
1797: return this.loggerManager;
1798: }
1799: }
|