You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by sy...@apache.org on 2003/06/05 18:33:11 UTC

cvs commit: cocoon-2.0/src/java/org/apache/cocoon/servlet BootstrapServlet.java ParanoidClassLoader.java ParanoidCocoonServlet.java

sylvain     2003/06/05 09:33:11

  Modified:    src/java/org/apache/cocoon/servlet BootstrapServlet.java
                        ParanoidClassLoader.java ParanoidCocoonServlet.java
  Log:
  Backported the ParanoidCocoon servlet, which is now really paranoid
  
  Revision  Changes    Path
  1.2       +52 -138   cocoon-2.0/src/java/org/apache/cocoon/servlet/BootstrapServlet.java
  
  Index: BootstrapServlet.java
  ===================================================================
  RCS file: /home/cvs/cocoon-2.0/src/java/org/apache/cocoon/servlet/BootstrapServlet.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- BootstrapServlet.java	9 Mar 2003 00:03:16 -0000	1.1
  +++ BootstrapServlet.java	5 Jun 2003 16:33:10 -0000	1.2
  @@ -51,17 +51,17 @@
   package org.apache.cocoon.servlet;
   
   import java.io.File;
  -import java.io.IOException;
   import java.io.InputStream;
   import java.net.MalformedURLException;
   import java.net.URL;
  -import java.util.ArrayList;
   import java.util.Enumeration;
  -import java.util.List;
   import java.util.Set;
   
  -import javax.servlet.*;
  -import javax.servlet.http.HttpServlet;
  +import javax.servlet.RequestDispatcher;
  +import javax.servlet.Servlet;
  +import javax.servlet.ServletConfig;
  +import javax.servlet.ServletContext;
  +import javax.servlet.ServletException;
   
   /**
    * A bootstrap servlet to allow Cocoon to run in servlet engines that aren't fully
  @@ -81,141 +81,55 @@
    * @version CVS $Id$
    */
   
  -public class BootstrapServlet extends HttpServlet {
  +public class BootstrapServlet extends ParanoidCocoonServlet {
       
  -    /**
  -     * The name of the actual servlet class.
  -     */
  -    public static final String SERVLET_CLASS = "org.apache.cocoon.servlet.CocoonServlet";
  -    
  -    protected Servlet servlet;
  -    
  -    protected ClassLoader classloader;
  -    
  -    protected ServletContext context;
  +    private File contextDir;
       
  -    public void init(ServletConfig config) throws ServletException {
  -        this.context = config.getServletContext();
  -        
  -        this.context.log("getRealPath(\"/\") = " + context.getRealPath("/"));
  +	protected File getContextDir() throws ServletException {
  +		
  +		ServletContext context = getServletContext();
  +		ServletConfig config = getServletConfig();
  +		
  +		log("getRealPath(\"/\") = " + context.getRealPath("/"));
  +
  +		String contextDirParam = config.getInitParameter("context-directory");
  +		
  +		if (contextDirParam == null) {
  +				throw new ServletException("The 'context-directory' parameter must be set to the root of the servlet context");
  +		}
  +        
  +		// Ensure context dir doesn't end with a "/" (servlet spec says that paths for
  +		// getResource() should start by a "/")
  +		if (contextDirParam.endsWith("/")) {
  +			contextDirParam = contextDirParam.substring(0, contextDirParam.length() - 1);
  +		}
  +        
  +		// Ensure context dir exists and is a directory
  +		this.contextDir = new File(contextDirParam);
  +		if (!this.contextDir.exists()) {
  +			String msg = "Context dir '" + this.contextDir + "' doesn't exist";
  +			log(msg);
  +			throw new ServletException(msg);
  +		}
  +
  +		if (!this.contextDir.isDirectory()) {
  +			String msg = "Context dir '" + this.contextDir + "' should be a directory";
  +			log(msg);
  +			throw new ServletException(msg);
  +		}
  +        
  +		context.log("Context dir set to " + this.contextDir);
  +		
  +		return this.contextDir;
  +	}
   
  -        String contextDirParam = config.getInitParameter("context-directory");
  -        if (contextDirParam == null) {
  -            // Check old form, not consistent with other parameter names
  -            contextDirParam = config.getInitParameter("context-dir");
  -            if (contextDirParam == null) {
  -                String msg = "The 'context-directory' parameter must be set to the root of the servlet context";
  -                this.context.log(msg);
  -                throw new ServletException(msg);
  -            } else {
  -                this.context.log("Parameter 'context-dir' is deprecated - use 'context-directory'");
  -            }
  -        }
  -        
  -        // Ensure context dir doesn't end with a "/" (servlet spec says that paths for
  -        // getResource() should start by a "/")
  -        if (contextDirParam.endsWith("/")) {
  -            contextDirParam = contextDirParam.substring(0, contextDirParam.length() - 1);
  -        }
  -        
  -        // Ensure context dir exists and is a directory
  -        File contextDir = new File(contextDirParam);
  -        if (!contextDir.exists()) {
  -            String msg = "Context dir '" + contextDir + "' doesn't exist";
  -            this.context.log(msg);
  -            throw new ServletException(msg);
  -        }
   
  -        if (!contextDir.isDirectory()) {
  -            String msg = "Context dir '" + contextDir + "' should be a directory";
  -            this.context.log(msg);
  -            throw new ServletException(msg);
  -        }
  -        
  -        context.log("Context dir set to " + contextDir);
  -
  -        this.classloader = getClassLoader(contextDirParam);
  -        
  -        try {
  -            Class servletClass = this.classloader.loadClass(SERVLET_CLASS);
  -            
  -            this.servlet = (Servlet)servletClass.newInstance();
  -        } catch(Exception e) {
  -            context.log("Cannot load servlet", e);
  -            throw new ServletException(e);
  -        }
  -        
  -        // Always set the context classloader. JAXP uses it to find a ParserFactory,
  -        // and thus fails if it's not set to the webapp classloader.
  -        Thread.currentThread().setContextClassLoader(this.classloader);
  -        
  -        ServletContext newContext = new ContextWrapper(context, contextDirParam);
  -        ServletConfig newConfig = new ConfigWrapper(config, newContext);
  -        
  -        super.init(newConfig);
  +    protected void initServlet(Servlet servlet) throws ServletException {
           
  -        // Inlitialize the actual servlet
  -        this.servlet.init(newConfig);
  +        ServletContext newContext = new ContextWrapper(getServletContext(), this.contextDir);
  +        ServletConfig newConfig = new ConfigWrapper(getServletConfig(), newContext);
           
  -    }
  -    
  -    /**
  -     * Get the classloader that will be used to create the actual servlet.
  -     */
  -    protected ClassLoader getClassLoader(String contextDirParam) throws ServletException {
  -        List urlList = new ArrayList();
  -        
  -        try {
  -            File classDir = new File(contextDirParam + "/WEB-INF/classes");
  -            if (classDir.exists()) {
  -                if (!classDir.isDirectory()) {
  -                    String msg = classDir + " exists but is not a directory";
  -                    this.context.log(msg);
  -                    throw new ServletException(msg);
  -                }
  -            
  -                URL classURL = classDir.toURL();
  -                context.log("Adding class directory " + classURL);
  -                urlList.add(classURL);
  -                
  -            }
  -            
  -            File libDir = new File(contextDirParam + "/WEB-INF/lib");
  -            File[] libraries = libDir.listFiles();
  -
  -            for (int i = 0; i < libraries.length; i++) {
  -                URL lib = libraries[i].toURL();
  -                context.log("Adding class library " + lib);
  -                urlList.add(lib);
  -            }
  -        } catch (MalformedURLException mue) {
  -            context.log("Malformed url", mue);
  -            throw new ServletException(mue);
  -        }
  -        
  -        URL[] urls = (URL[])urlList.toArray(new URL[urlList.size()]);
  -        
  -        return ParanoidClassLoader.newInstance(urls, this.getClass().getClassLoader());
  -    }
  -    
  -    /**
  -     * Service the request by delegating the call to the real servlet
  -     */
  -    public void service(ServletRequest request, ServletResponse response)
  -      throws ServletException, IOException {
  -
  -        Thread.currentThread().setContextClassLoader(this.classloader);
  -        this.servlet.service(request, response);
  -    }
  -    
  -    /**
  -     * Destroy the actual servlet
  -     */
  -    public void destroy() {
  -
  -        super.destroy();
  -        Thread.currentThread().setContextClassLoader(this.classloader);
  -        this.servlet.destroy();
  +        servlet.init(newConfig);        
       }
   
       //-------------------------------------------------------------------------
  @@ -260,13 +174,13 @@
        */
       public static class ContextWrapper implements ServletContext {
           ServletContext context;
  -        String contextRoot;
  +        File contextRoot;
           
           /**
            * Builds a wrapper around an existing context, and handle all
            * resource resolution relatively to <code>contextRoot</code>
            */
  -        public ContextWrapper(ServletContext context, String contextRoot) {
  +        public ContextWrapper(ServletContext context, File contextRoot) {
               this.context = context;
               this.contextRoot = contextRoot;
           }
  @@ -293,7 +207,7 @@
            * returned.
            */
           public URL getResource(String path) throws MalformedURLException {
  -            File file = new File(this.contextRoot + path);
  +            File file = new File(this.contextRoot, path);
               if (file.exists()) {
                   URL result = file.toURL();
                   //this.context.log("getResource(" + path + ") = " + result);
  
  
  
  1.2       +15 -4     cocoon-2.0/src/java/org/apache/cocoon/servlet/ParanoidClassLoader.java
  
  Index: ParanoidClassLoader.java
  ===================================================================
  RCS file: /home/cvs/cocoon-2.0/src/java/org/apache/cocoon/servlet/ParanoidClassLoader.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ParanoidClassLoader.java	9 Mar 2003 00:03:16 -0000	1.1
  +++ ParanoidClassLoader.java	5 Jun 2003 16:33:10 -0000	1.2
  @@ -50,6 +50,8 @@
   */
   package org.apache.cocoon.servlet;
   
  +import org.apache.cocoon.CascadingIOException;
  +
   import java.io.File;
   import java.io.IOException;
   import java.net.MalformedURLException;
  @@ -62,7 +64,7 @@
    * classes.  It checks this classloader before it checks its parent.
    *
    * @author <a href="mailto:bloritsch@apache.org">Berin Loritsch</a>
  - * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
  + * @author <a href="http://www.apache.org/~sylvain/">Sylvain Wallez</a>
    * @version CVS $Id$
    */
   
  @@ -83,7 +85,7 @@
        * Alternate constructor to define a parent.
        */
       public ParanoidClassLoader(final ClassLoader parent) {
  -        this(null, parent, null);
  +        this(new URL[0], parent, null);
       }
   
       /**
  @@ -149,6 +151,7 @@
               
               try {
                   clazz = findClass(name);
  +                //System.err.println("Paranoid load : " + name);
               } catch (ClassNotFoundException cnfe) {
                   if (this.parent != null) {
                        // Ask to parent ClassLoader (can also throw a CNFE).
  @@ -197,7 +200,15 @@
           try {
               this.addURL(file.getCanonicalFile().toURL());
           } catch (MalformedURLException mue) {
  -            throw new IOException("Could not add repository");
  +            throw new CascadingIOException("Could not add repository", mue);
           }
  +    }
  +    
  +    /**
  +     * Adds a new URL
  +     */
  +    
  +    public void addURL(URL url) {
  +    	super.addURL(url);
       }
   }
  
  
  
  1.2       +148 -103  cocoon-2.0/src/java/org/apache/cocoon/servlet/ParanoidCocoonServlet.java
  
  Index: ParanoidCocoonServlet.java
  ===================================================================
  RCS file: /home/cvs/cocoon-2.0/src/java/org/apache/cocoon/servlet/ParanoidCocoonServlet.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ParanoidCocoonServlet.java	9 Mar 2003 00:03:16 -0000	1.1
  +++ ParanoidCocoonServlet.java	5 Jun 2003 16:33:10 -0000	1.2
  @@ -50,121 +50,166 @@
   */
   package org.apache.cocoon.servlet;
   
  -import org.apache.cocoon.components.classloader.RepositoryClassLoader;
  -import org.apache.cocoon.util.IOUtils;
  -
  -import javax.servlet.ServletException;
   import java.io.File;
  +import java.io.FilenameFilter;
  +import java.io.IOException;
  +import java.net.MalformedURLException;
   import java.net.URL;
  +import java.util.ArrayList;
  +import java.util.List;
  +
  +import javax.servlet.Servlet;
  +import javax.servlet.ServletConfig;
  +import javax.servlet.ServletException;
  +import javax.servlet.ServletRequest;
  +import javax.servlet.ServletResponse;
  +import javax.servlet.http.HttpServlet;
   
   /**
  - * This is the entry point for Cocoon execution as an HTTP Servlet.
  - * It also creates a buffer by loading the whole servlet inside a ClassLoader.
  - * It has been changed to extend <code>CocoonServlet</code> so that it is
  - * easier to add and change functionality between the two servlets.
  - * The only real differences are the ClassLoader and instantiating Cocoon inside
  - * of it.
  + * This servlet builds a classloading sandbox and runs another servlet inside that
  + * sandbox. The purpose is to shield the libraries and classes shipped with the web
  + * application from any other classes with the same name that may exist in the system,
  + * such as Xerces and Xalan versions included in JDK 1.4.
  + * <p>
  + * This servlet propagates all initialisation parameters to the sandboxed servlet, and
  + * accept only one additional parameter, <code>servlet-class</code>, which defined the
  + * sandboxed servlet class. The default is {@link CocoonServlet}.
    *
    * @author <a href="mailto:bloritsch@apache.org">Berin Loritsch</a>
  + * @author <a href="http://www.apache.org/~sylvain/">Sylvain Wallez</a>
    * @version CVS $Id$
    */
   
  -public class ParanoidCocoonServlet extends CocoonServlet {
  +public class ParanoidCocoonServlet extends HttpServlet {
   
  -    protected RepositoryClassLoader repositoryLoader;
  +	/**
  +	 * The name of the actual servlet class.
  +	 */
  +	public static final String DEFAULT_SERVLET_CLASS = "org.apache.cocoon.servlet.CocoonServlet";
       
  -    public ParanoidCocoonServlet() {
  -        super();
  -        // Override the parent class classloader
  -        this.repositoryLoader = new RepositoryClassLoader(new URL[] {}, this.getClass().getClassLoader());
  -        super.classLoader = this.repositoryLoader;
  -    }
  -
  -    /**
  -     * This builds the important ClassPath used by this Servlet.  It
  -     * does so in a Servlet Engine neutral way.  It uses the
  -     * <code>ServletContext</code>'s <code>getRealPath</code> method
  -     * to get the Servlet 2.2 identified classes and lib directories.
  -     * It iterates through every file in the lib directory and adds
  -     * it to the classpath.
  -     *
  -     * Also, we add the files to the ClassLoader for the Cocoon system.
  -     * In order to protect ourselves from skitzofrantic classloaders,
  -     * we need to work with a known one.
  -     *
  -     * @param context  The ServletContext to perform the lookup.
  -     *
  -     * @throws ServletException
  -     */
  -     protected String getClassPath()
  -     throws ServletException {
  -
  -        StringBuffer buildClassPath = new StringBuffer();
  -        String classDirPath = getInitParameter("class-dir");
  -        String libDirPath = getInitParameter("lib-dir");
  -        String classDir;
  -        File root;
  -
  -        if ((classDirPath != null) && !classDirPath.trim().equals("")) {
  -            classDir = classDirPath;
  -        } else {
  -            classDir = this.servletContext.getRealPath("/WEB-INF/classes");
  -        }
  -
  -        if ((libDirPath != null) && !libDirPath.trim().equals("")) {
  -            root = new File(libDirPath);
  -        } else {
  -            root = new File(this.servletContext.getRealPath("/WEB-INF/lib"));
  -        }
  -
  -        addClassLoaderDirectory(classDir);
  -
  -        buildClassPath.append(classDir);
  -
  -        if (root.isDirectory()) {
  -            File[] libraries = root.listFiles();
  -
  -            for (int i = 0; i < libraries.length; i++) {
  -            	String fullName = IOUtils.getFullFilename(libraries[i]);
  -                buildClassPath.append(File.pathSeparatorChar).append(fullName);
  -
  -                addClassLoaderDirectory(fullName);
  -            }
  +	private Servlet servlet;
  +    
  +	private ClassLoader classloader;
  +    
  +	public void init(ServletConfig config) throws ServletException {
  +		
  +		super.init(config);
  +
  +		// Create the classloader in which we will load the servlet
  +		this.classloader = getClassLoader(this.getContextDir());
  +        
  +        String servletName = config.getInitParameter("servlet-class");
  +        if (servletName == null) {
  +            servletName = DEFAULT_SERVLET_CLASS;
           }
  +        
  +        // Create the servlet
  +		try {
  +			Class servletClass = this.classloader.loadClass(servletName);
  +            
  +			this.servlet = (Servlet)servletClass.newInstance();
  +
  +		} catch(Exception e) {
  +			throw new ServletException("Cannot load servlet " + servletName, e);
  +		}
  +        
  +		// Always set the context classloader. JAXP uses it to find a ParserFactory,
  +		// and thus fails if it's not set to the webapp classloader.
  +		Thread.currentThread().setContextClassLoader(this.classloader);
  +        
  +		// Inlitialize the actual servlet
  +		initServlet(servlet);
  +        
  +	}
  +	
  +	/**
  +	 * Initialize the wrapped servlet. Subclasses (see {@link BootstrapServlet} change the
  +	 * <code>ServletConfig</code> given to the servlet.
  +	 * 
  +	 * @param servlet the servlet to initialize
  +	 * @throws ServletException
  +	 */
  +	protected void initServlet(Servlet servlet) throws ServletException {
  +		this.servlet.init(getServletConfig());
  +	}
  +	
  +	/**
  +	 * Get the web application context directory.
  +	 * 
  +	 * @return the context dir
  +	 * @throws ServletException
  +	 */
  +	protected File getContextDir() throws ServletException {
  +		String result = getServletContext().getRealPath("/");
  +		if (result == null) {
  +			throw new ServletException(this.getClass().getName() + " cannot run in an undeployed WAR file");
  +		}
  +		return new File(result);
  +	}
  +    
  +	/**
  +	 * Get the classloader that will be used to create the actual servlet. Its classpath is defined
  +	 * by the WEB-INF/classes and WEB-INF/lib directories in the context dir.
  +	 */
  +	private ClassLoader getClassLoader(File contextDir) throws ServletException {
  +		List urlList = new ArrayList();
  +        
  +		try {
  +			File classDir = new File(contextDir + "/WEB-INF/classes");
  +			if (classDir.exists()) {
  +				if (!classDir.isDirectory()) {
  +					throw new ServletException(classDir + " exists but is not a directory");
  +				}
  +            
  +				URL classURL = classDir.toURL();
  +				log("Adding class directory " + classURL);
  +				urlList.add(classURL);
  +                
  +			}
  +            
  +            // List all .jar and .zip
  +			File libDir = new File(contextDir + "/WEB-INF/lib");
  +			File[] libraries = libDir.listFiles(
  +				new FilenameFilter() {
  +	                public boolean accept(File dir, String name) {
  +	                    return name.endsWith(".zip") || name.endsWith(".jar");
  +	                }
  +				}
  +			);
  +
  +			for (int i = 0; i < libraries.length; i++) {
  +				URL lib = libraries[i].toURL();
  +				log("Adding class library " + lib);
  +				urlList.add(lib);
  +			}
  +		} catch (MalformedURLException mue) {
  +			throw new ServletException(mue);
  +		}
  +        
  +		URL[] urls = (URL[])urlList.toArray(new URL[urlList.size()]);
  +        
  +		return ParanoidClassLoader.newInstance(urls, this.getClass().getClassLoader());
  +	}
  +    
  +	/**
  +	 * Service the request by delegating the call to the real servlet
  +	 */
  +	public void service(ServletRequest request, ServletResponse response)
  +	  throws ServletException, IOException {
  +
  +		Thread.currentThread().setContextClassLoader(this.classloader);
  +		this.servlet.service(request, response);
  +	}
  +    
  +	/**
  +	 * Destroy the actual servlet
  +	 */
  +	public void destroy() {
   
  -        buildClassPath.append(File.pathSeparatorChar)
  -                      .append(System.getProperty("java.class.path"));
  -
  -        buildClassPath.append(File.pathSeparatorChar)
  -                      .append(getExtraClassPath());
  -
  -        return buildClassPath.toString();
  -     }
  -
  -    /**
  -     * Adds an URL to the classloader.
  -     */
  -    protected void addClassLoaderURL(URL url) {
  -        try {
  -            this.repositoryLoader.addURL(url);
  -        } catch (Exception e) {
  -            if (log.isDebugEnabled()) {
  -                log.debug("Could not add URL" + url, e);
  -            }
  -        }
  -    }
  +		Thread.currentThread().setContextClassLoader(this.classloader);
  +		this.servlet.destroy();
   
  -    /**
  -     * Adds a directory to the classloader.
  -     */
  -    protected void addClassLoaderDirectory(String dir) {
  -        try {
  -            this.repositoryLoader.addDirectory(new File(dir));
  -        } catch (Exception e) {
  -            if (log.isDebugEnabled()) {
  -                log.debug("Could not add directory" + dir, e);
  -            }
  -        }
  -    }
  +		super.destroy();
  +	}
   }