All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.catalina.loader.WebappLoader Maven / Gradle / Ivy

There is a newer version: 4.1.2.181
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 *
 *
 * This file incorporates work covered by the following copyright and
 * permission notice:
 *
 * Copyright 2004 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// Portions Copyright [2016] [Payara Foundation and/or its affiliates]

package org.apache.catalina.loader;


import com.sun.enterprise.deployment.Application;
import org.apache.catalina.*;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardServer;
import org.apache.catalina.util.LifecycleSupport;
import org.apache.catalina.util.StringManager;
import org.apache.naming.resources.DirContextURLStreamHandler;
import org.apache.naming.resources.DirContextURLStreamHandlerFactory;
import org.apache.naming.resources.Resource;
import org.glassfish.logging.annotation.LogMessageInfo;
import org.glassfish.web.loader.WebappClassLoader;

import javax.management.ObjectName;
import javax.naming.Binding;
import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.servlet.ServletContext;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandlerFactory;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Classloader implementation which is specialized for handling web
 * applications in the most efficient way, while being Catalina aware (all
 * accesses to resources are made through the DirContext interface).
 * This class loader supports detection of modified
 * Java classes, which can be used to implement auto-reload support.
 * 

* This class loader is configured by adding the pathnames of directories, * JAR files, and ZIP files with the addRepository() method, * prior to calling start(). When a new class is required, * these repositories will be consulted first to locate the class. If it * is not present, the system class loader will be used instead. * * @author Craig R. McClanahan * @author Remy Maucherat * @version $Revision: 1.10 $ $Date: 2007/05/05 05:32:09 $ */ public class WebappLoader implements Lifecycle, Loader, PropertyChangeListener { /** * First load of the class. */ private static boolean first = true; private static final Logger log = StandardServer.log; private static final ResourceBundle rb = log.getResourceBundle(); @LogMessageInfo( message = "Error registering loader", level = "SEVERE", cause = "Could not register loader", action = "Verify Object name" ) public static final String REGISTERING_LOADER_EXCEPTION = "AS-WEB-CORE-00288"; @LogMessageInfo( message = "Error registering jndi stream handler", level = "SEVERE", cause = "Could not register jndi stream handler", action = "Verify if the application has already set a factory, " + "if a security manager exists and its" + "checkSetFactory method doesn't allow" + "the operation" ) public static final String REGISTERING_JNDI_STREAM_HANDLER_EXCEPTION = "AS-WEB-CORE-00289"; @LogMessageInfo( message = "Loader has already been started", level = "WARNING" ) public static final String LOADER_ALREADY_STARTED_EXCEPTION = "AS-WEB-CORE-00290"; @LogMessageInfo( message = "No resources for {0}", level = "INFO" ) public static final String NO_RESOURCE_INFO = "AS-WEB-CORE-00291"; @LogMessageInfo( message = "LifecycleException", level = "SEVERE", cause = "Could not construct a class loader", action = "Verify if there is any lifecycle exception" ) public static final String LIFECYCLE_EXCEPTION = "AS-WEB-CORE-00292"; @LogMessageInfo( message = "Loader has not yet been started", level = "WARNING" ) public static final String LOADER_NOT_STARTED_EXCEPTION = "AS-WEB-CORE-00293"; @LogMessageInfo( message = "Cannot set reloadable property to {0}", level = "SEVERE", cause = "Could not set reloadable property", action = "Verify the value for the property" ) public static final String SET_RELOADABLE_PROPERTY_EXCEPTION = "AS-WEB-CORE-00294"; @LogMessageInfo( message = "WebappLoader[{0}]: {1}", level = "WARNING" ) public static final String WEB_APP_LOADER_EXCEPTION = "AS-WEB-CORE-00295"; @LogMessageInfo( message = "No work dir for {0}", level = "INFO" ) public static final String NO_WORK_DIR_INFO = "AS-WEB-CORE-00296"; @LogMessageInfo( message = "Failed to create destination directory to copy resources", level = "WARNING" ) public static final String FAILED_CREATE_DEST_DIR = "AS-WEB-CORE-00297"; @LogMessageInfo( message = "Failed to copy resources", level = "WARNING" ) public static final String FAILED_COPY_RESOURCE = "AS-WEB-CORE-00298"; @LogMessageInfo( message = "Failed to create work directory to {0}", level = "SEVERE", cause = "Coud not create work directory", action = "Verify the PATH " ) public static final String FAILED_CREATE_WORK_DIR_EXCEPTION = "AS-WEB-CORE-00299"; // --------------------------------------------------------- Constructors /** * Construct a new WebappLoader with no defined parent class loader * (so that the actual parent will be the system class loader). */ public WebappLoader() { this(null); } /** * Construct a new WebappLoader with the specified class loader * to be defined as the parent of the ClassLoader we ultimately create. * * @param parent The parent class loader */ public WebappLoader(ClassLoader parent) { super(); this.parentClassLoader = parent; } // --------------------------------------------------- Instance Variables private ObjectName oname; private ObjectName controller; /** * The class loader being managed by this Loader component. */ private WebappClassLoader classLoader = null; /** * The Container with which this Loader has been associated. */ private Container container = null; /** * The debugging detail level for this component. */ private int debug = 0; /** * The "follow standard delegation model" flag that will be used to * configure our ClassLoader. */ private boolean delegate = false; /** * The descriptive information about this Loader implementation. */ private static final String info = "org.apache.catalina.loader.WebappLoader/1.0"; /** * The lifecycle event support for this component. */ protected LifecycleSupport lifecycle = new LifecycleSupport(this); /** * The Java class name of the ClassLoader implementation to be used. * This class should extend WebappClassLoader, otherwise, a different * loader implementation must be used. */ private String loaderClass = "org.glassfish.web.loader.WebappClassLoader"; /** * The parent class loader of the class loader we will create. */ private ClassLoader parentClassLoader = null; /** * The reloadable flag for this Loader. */ private boolean reloadable = false; /** * The set of repositories associated with this class loader. */ private String repositories[] = new String[0]; /** * The string manager for this package. */ protected static final StringManager sm = StringManager.getManager(WebappLoader.class.getPackage().getName()); /** * Has this component been started? */ private boolean started = false; /** * The property change support for this component. */ protected PropertyChangeSupport support = new PropertyChangeSupport(this); /** * Classpath set in the loader. */ private String classpath = null; // START PE 4985680` /** * List of packages that may always be overridden, regardless of whether * they belong to a protected namespace (i.e., a namespace that may never * be overridden by a webapp) */ private ArrayList overridablePackages; // END PE 4985680 // START PWC 1.1 6314481 private boolean ignoreHiddenJarFiles; // END PWC 1.1 6314481 private boolean useMyFaces; // ------------------------------------------------------------- Properties /** * Return the Java class loader to be used by this Container. */ public ClassLoader getClassLoader() { return classLoader; } /** * Return the Container with which this Logger has been associated. */ public Container getContainer() { return (container); } /** * Set the Container with which this Logger has been associated. * * @param container The associated Container */ public void setContainer(Container container) { // Deregister from the old Container (if any) if ((this.container != null) && (this.container instanceof Context)) ((Context) this.container).removePropertyChangeListener(this); // Process this property change Container oldContainer = this.container; this.container = container; support.firePropertyChange("container", oldContainer, this.container); // Register with the new Container (if any) if (this.container instanceof Context) { setReloadable( ((Context) this.container).getReloadable() ); ((Context) this.container).addPropertyChangeListener(this); } } /** * Return the debugging detail level for this component. */ public int getDebug() { return (this.debug); } /** * Set the debugging detail level for this component. * * @param debug The new debugging detail level */ public void setDebug(int debug) { int oldDebug = this.debug; this.debug = debug; support.firePropertyChange("debug", Integer.valueOf(oldDebug), Integer.valueOf(this.debug)); } /** * Return the "follow standard delegation model" flag used to configure * our ClassLoader. */ public boolean getDelegate() { return (this.delegate); } /** * Set the "follow standard delegation model" flag used to configure * our ClassLoader. * * @param delegate The new flag */ public void setDelegate(boolean delegate) { boolean oldDelegate = this.delegate; this.delegate = delegate; support.firePropertyChange("delegate", Boolean.valueOf(oldDelegate), Boolean.valueOf(this.delegate)); } /** * Return descriptive information about this Loader implementation and * the corresponding version number, in the format * <description>/<version>. */ public String getInfo() { return (info); } /** * Return the ClassLoader class name. */ public String getLoaderClass() { return (this.loaderClass); } /** * Set the ClassLoader class name. * * @param loaderClass The new ClassLoader class name */ public void setLoaderClass(String loaderClass) { this.loaderClass = loaderClass; } /** * Return the reloadable flag for this Loader. */ public boolean getReloadable() { return (this.reloadable); } /** * Set the reloadable flag for this Loader. * * @param reloadable The new reloadable flag */ public void setReloadable(boolean reloadable) { // Process this property change boolean oldReloadable = this.reloadable; this.reloadable = reloadable; support.firePropertyChange("reloadable", Boolean.valueOf(oldReloadable), Boolean.valueOf(this.reloadable)); } public void setUseMyFaces(boolean useMyFaces) { this.useMyFaces = useMyFaces; } // --------------------------------------------------------- Public Methods /** * Add a property change listener to this component. * * @param listener The listener to add */ public void addPropertyChangeListener(PropertyChangeListener listener) { support.addPropertyChangeListener(listener); } /** * Add a new repository to the set of repositories for this class loader. * * @param repository Repository to be added */ public void addRepository(String repository) { if (log.isLoggable(Level.FINEST)) log.log(Level.FINEST, "Adding repository " + repository); for (int i = 0; i < repositories.length; i++) { if (repository.equals(repositories[i])) return; } String results[] = new String[repositories.length + 1]; for (int i = 0; i < repositories.length; i++) results[i] = repositories[i]; results[repositories.length] = repository; repositories = results; if (started && (classLoader != null)) { classLoader.addRepository(repository); setClassPath(); } } /** * Return the set of repositories defined for this class loader. * If none are defined, a zero-length array is returned. * For security reason, returns a clone of the Array (since * String are immutable). */ public String[] findRepositories() { return repositories.clone(); } public String[] getRepositories() { return repositories.clone(); } /** * Classpath, as set in org.apache.catalina.jsp_classpath context * property * * @return The classpath */ public String getClasspath() { return classpath; } /** * Has the internal repository associated with this Loader been modified, * such that the loaded classes should be reloaded? */ public boolean modified() { return (classLoader.modified()); } /** * Used to periodically signal to the classloader to release JAR resources. */ public void closeJARs(boolean force) { if (classLoader !=null){ classLoader.closeJARs(force); } } /** * Remove a property change listener from this component. * * @param listener The listener to remove */ public void removePropertyChangeListener(PropertyChangeListener listener) { support.removePropertyChangeListener(listener); } /** * Return a String representation of this component. */ public String toString() { StringBuilder sb = new StringBuilder("WebappLoader["); if (container != null) sb.append(container.getName()); sb.append("]"); return (sb.toString()); } // ------------------------------------------------------ Lifecycle Methods /** * Add a lifecycle event listener to this component. * * @param listener The listener to add */ public void addLifecycleListener(LifecycleListener listener) { lifecycle.addLifecycleListener(listener); } /** * Gets the (possibly empty) list of lifecycle listeners associated * with this WebappLoader. */ public List findLifecycleListeners() { return lifecycle.findLifecycleListeners(); } /** * Remove a lifecycle event listener from this component. * * @param listener The listener to remove */ public void removeLifecycleListener(LifecycleListener listener) { lifecycle.removeLifecycleListener(listener); } private boolean initialized=false; public void init() { initialized=true; if( oname==null ) { // not registered yet - standalone or API if( container instanceof StandardContext) { // Register ourself. The container must be a webapp try { StandardContext ctx=(StandardContext)container; String path = ctx.getEncodedPath(); if (path.equals("")) { path = "/"; } oname = new ObjectName(ctx.getEngineName() + ":type=Loader,path=" + path + ",host=" + ctx.getParent().getName()); controller = oname; } catch (Exception e) { log.log(Level.SEVERE, REGISTERING_LOADER_EXCEPTION, e); } } } /* if( container == null ) { // JMX created the loader // TODO } */ } public void destroy() { if( controller==oname ) { oname = null; } initialized = false; } private static synchronized void initStreamHandlerFactory() { // Register a stream handler factory for the JNDI protocol URLStreamHandlerFactory streamHandlerFactory = new DirContextURLStreamHandlerFactory(); synchronized (WebappLoader.class) { if (first) { first = false; try { URL.setURLStreamHandlerFactory(streamHandlerFactory); } catch (Exception e) { // Log and continue anyway, this is not critical log.log(Level.SEVERE, REGISTERING_JNDI_STREAM_HANDLER_EXCEPTION, e); } catch (Throwable t) { // This is likely a dual registration if (log.isLoggable(Level.FINE)) { log.log(Level.FINE, "Dual registration of jndi stream handler: " + t.getMessage()); } } } } } /** * Start this component, initializing our associated class loader. * * @exception LifecycleException if a lifecycle error occurs */ public void start() throws LifecycleException { // Validate and update our current component state if( ! initialized ) init(); if (started) throw new LifecycleException (rb.getString(LOADER_ALREADY_STARTED_EXCEPTION)); if (log.isLoggable(Level.FINEST)) log.log(Level.FINEST, "Starting this Loader"); lifecycle.fireLifecycleEvent(START_EVENT, null); started = true; if (container.getResources() == null) { if (log.isLoggable(Level.INFO)) { log.log(Level.INFO, NO_RESOURCE_INFO, container); } return; } // Register a stream handler factory for the JNDI protocol initStreamHandlerFactory(); // Construct a class loader based on our current repositories list try { final ClassLoader cl = createClassLoader(); if (cl instanceof WebappClassLoader) { classLoader = (WebappClassLoader) cl; } else { classLoader = AccessController.doPrivileged(new PrivilegedAction() { @Override public WebappClassLoader run() { return new WebappClassLoader(cl, Application.createApplication()); } }); } classLoader.setResources(container.getResources()); classLoader.setDebug(this.debug); classLoader.setDelegate(this.delegate); for (int i = 0; i < repositories.length; i++) { classLoader.addRepository(repositories[i]); } // START OF PE 4985680 if (overridablePackages != null){ for (int i = 0; i < overridablePackages.size(); i++) { classLoader.addOverridablePackage( overridablePackages.get(i)); } overridablePackages = null; } // END OF PE 4985680 // Configure our repositories setRepositories(); setClassPath(); setPermissions(); // Binding the Webapp class loader to the directory context DirContextURLStreamHandler.bind(classLoader, this.container.getResources()); } catch (Throwable t) { log.log(Level.SEVERE, LIFECYCLE_EXCEPTION, t); throw new LifecycleException("start: ", t); } } /** * Stop this component, finalizing our associated class loader. * * @exception LifecycleException if a lifecycle error occurs */ public void stop() throws LifecycleException { // Validate and update our current component state if (!started) throw new LifecycleException (rb.getString(LOADER_NOT_STARTED_EXCEPTION)); if (log.isLoggable(Level.FINEST)) log.log(Level.FINEST, "Stopping this Loader"); lifecycle.fireLifecycleEvent(STOP_EVENT, null); started = false; // Remove context attributes as appropriate if (container instanceof Context) { ServletContext servletContext = ((Context) container).getServletContext(); servletContext.removeAttribute(Globals.CLASS_PATH_ATTR); } // Throw away our current class loader stopNestedClassLoader(); DirContextURLStreamHandler.unbind(classLoader); classLoader = null; destroy(); } /** * Stops the nested classloader */ public void stopNestedClassLoader() throws LifecycleException { try { classLoader.stop(); } catch (Exception e) { throw new LifecycleException(e); } } // ----------------------------------------- PropertyChangeListener Methods /** * Process property change events from our associated Context. * * @param event The property change event that has occurred */ public void propertyChange(PropertyChangeEvent event) { // Validate the source of this event if (!(event.getSource() instanceof Context)) return; // Process a relevant property change String propName = event.getPropertyName(); if ("reloadable".equals(propName)) { try { setReloadable ( ((Boolean) event.getNewValue()).booleanValue() ); } catch (NumberFormatException e) { log.log(Level.SEVERE, SET_RELOADABLE_PROPERTY_EXCEPTION, event.getNewValue().toString()); } } else if ("antiJARLocking".equals(propName)) { ClassLoader cloader = Thread.currentThread().getContextClassLoader(); if (cloader instanceof WebappClassLoader) { ((WebappClassLoader)cloader).setAntiJARLocking( ((Boolean)event.getNewValue()).booleanValue()); } } } // ------------------------------------------------------- Private Methods /** * Create associated classLoader. */ protected ClassLoader createClassLoader() throws Exception { Class clazz = Class.forName(loaderClass); WebappClassLoader classLoader = null; if (parentClassLoader == null) { parentClassLoader = Thread.currentThread().getContextClassLoader(); } Class[] argTypes = { ClassLoader.class }; Object[] args = { parentClassLoader }; Constructor constr = clazz.getConstructor(argTypes); classLoader = (WebappClassLoader) constr.newInstance(args); classLoader.setUseMyFaces(useMyFaces); /* * Start the WebappClassLoader here as opposed to in the course of * WebappLoader#start, in order to prevent it from being started * twice (during normal deployment, the WebappClassLoader is created * by the deployment backend without calling * WebappLoader#createClassLoader, and will have been started * by the time WebappLoader#start is called) */ try { classLoader.start(); } catch (Exception e) { throw new LifecycleException(e); } return classLoader; } /** * Log a message on the Logger associated with our Container (if any) * * @param message Message to be logged * private void log(String message) { org.apache.catalina.Logger logger = null; String containerName = null; if (container != null) { logger = container.getLogger(); containerName = container.getName(); } if (logger != null) { logger.log("WebappLoader[" + containerName + "]: " + message); } else { if (log.isLoggable(Level.INFO)) { log.info("WebappLoader[" + containerName + "]: " + message); } } } */ /** * Log a message on the Logger associated with our Container (if any) * * @param message Message to be logged * @param t Associated exception */ private void log(String message, Throwable t) { org.apache.catalina.Logger logger = null; String containerName = null; if (container != null) { logger = container.getLogger(); containerName = container.getName(); } if (logger != null) { String msg = MessageFormat.format(rb.getString(WEB_APP_LOADER_EXCEPTION), new Object[] {containerName, message}); logger.log(msg, t); } else { String msg = MessageFormat.format(rb.getString(WEB_APP_LOADER_EXCEPTION), new Object[] {containerName, message}); log.log(Level.WARNING, msg, t); } } /** * Configure associated class loader permissions. */ private void setPermissions() { if (!Globals.IS_SECURITY_ENABLED) return; if (!(container instanceof Context)) return; try { AccessController.doPrivileged( new PrivilegedExceptionAction() { public Object run() throws SecurityException { setPermissions_priv(); return null; } }); } catch (PrivilegedActionException e) { throw (SecurityException ) e.getException(); } } private void setPermissions_priv() { // Tell the class loader the root of the context ServletContext servletContext = ((Context) container).getServletContext(); // Assigning permissions for the work directory File workDir = (File) servletContext.getAttribute(ServletContext.TEMPDIR); if (workDir != null) { try { String workDirPath = workDir.getCanonicalPath(); classLoader.addPermission (new FilePermission(workDirPath, "read,write")); classLoader.addPermission (new FilePermission(workDirPath + File.separator + "-", "read,write,delete")); } catch (IOException e) { // Ignore } } try { URL rootURL = servletContext.getResource("/"); classLoader.addPermission(rootURL); String contextRoot = servletContext.getRealPath("/"); if (contextRoot != null) { try { contextRoot = (new File(contextRoot)).getCanonicalPath(); classLoader.addPermission(contextRoot); } catch (IOException e) { // Ignore } } URL classesURL = servletContext.getResource("/WEB-INF/classes/"); classLoader.addPermission(classesURL); URL libURL = servletContext.getResource("/WEB-INF/lib/"); classLoader.addPermission(libURL); if (contextRoot != null) { if (libURL != null) { File rootDir = new File(contextRoot); File libDir = new File(rootDir, "WEB-INF/lib/"); try { String path = libDir.getCanonicalPath(); classLoader.addPermission(path); } catch (IOException e) { } } } else { if (workDir != null) { if (libURL != null) { File libDir = new File(workDir, "WEB-INF/lib/"); try { String path = libDir.getCanonicalPath(); classLoader.addPermission(path); } catch (IOException e) { } } if (classesURL != null) { File classesDir = new File(workDir, "WEB-INF/classes/"); try { String path = classesDir.getCanonicalPath(); classLoader.addPermission(path); } catch (IOException e) { } } } } } catch (MalformedURLException e) { } } /** * Configure the repositories for our class loader, based on the * associated Context. */ private void setRepositories() throws IOException { if (!(container instanceof Context)) return; ServletContext servletContext = ((Context) container).getServletContext(); if (servletContext == null) return; // Loading the work directory File workDir = (File) servletContext.getAttribute(ServletContext.TEMPDIR); if (workDir == null) { if (log.isLoggable(Level.INFO)) { log.log(Level.INFO, NO_WORK_DIR_INFO, servletContext); } } if (log.isLoggable(Level.FINEST) && workDir != null) log.log(Level.FINEST, "Deploying class repositories to work directory" + workDir.getAbsolutePath()); DirContext resources = container.getResources(); // Setting up the class repository (/WEB-INF/classes), if it exists String classesPath = "/WEB-INF/classes"; DirContext classes = null; try { Object object = resources.lookup(classesPath); if (object instanceof DirContext) { classes = (DirContext) object; } } catch(NamingException e) { // Silent catch: it's valid that no /WEB-INF/classes collection // exists } if (classes != null) { File classRepository = null; String absoluteClassesPath = servletContext.getRealPath(classesPath); if (absoluteClassesPath != null) { classRepository = new File(absoluteClassesPath); } else { classRepository = new File(workDir, classesPath); if (!classRepository.mkdirs() && !classRepository.isDirectory()) { throw new IOException(rb.getString(FAILED_CREATE_DEST_DIR)); } if (!copyDir(classes, classRepository)) { throw new IOException(rb.getString(FAILED_COPY_RESOURCE)); } } if (log.isLoggable(Level.FINEST)) log.log(Level.FINEST, "Deploy class files " +classesPath+" to " + classRepository.getAbsolutePath()); } // Setting up the JAR repository (/WEB-INF/lib), if it exists String libPath = "/WEB-INF/lib"; classLoader.setJarPath(libPath); DirContext libDir = null; // Looking up directory /WEB-INF/lib in the context try { Object object = resources.lookup(libPath); if (object instanceof DirContext) libDir = (DirContext) object; } catch(NamingException e) { // Silent catch: it's valid that no /WEB-INF/lib collection // exists } if (libDir != null) { boolean copyJars = false; String absoluteLibPath = servletContext.getRealPath(libPath); File destDir = null; if (absoluteLibPath != null) { destDir = new File(absoluteLibPath); } else { copyJars = true; destDir = new File(workDir, libPath); if (!destDir.mkdirs() && !destDir.isDirectory()) { log.log(Level.SEVERE, FAILED_CREATE_WORK_DIR_EXCEPTION, destDir.getAbsolutePath()); } } if (!copyJars) { return; } // Looking up directory /WEB-INF/lib in the context try { NamingEnumeration enumeration = resources.listBindings(libPath); while (enumeration.hasMoreElements()) { Binding binding = enumeration.nextElement(); String filename = libPath + "/" + binding.getName(); // START OF IASRI 4657979 if (!filename.endsWith(".jar") && !filename.endsWith(".zip")) // END OF IASRI 4657979 continue; // START PWC 1.1 6314481 if (binding.getName() != null && binding.getName().startsWith(".") && ignoreHiddenJarFiles) { continue; } // END PWC 1.1 6314481 File destFile = new File(destDir, binding.getName()); if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, "Deploy JAR "+filename+" to " + destFile.getAbsolutePath()); } Object obj = binding.getObject(); if (!(obj instanceof Resource)) continue; Resource jarResource = (Resource) obj; if (!copy(jarResource.streamContent(), new FileOutputStream(destFile))) { continue; } } } catch (NamingException e) { // Silent catch: it's valid that no /WEB-INF/lib directory // exists } catch (IOException e) { log("Unable to configure repositories", e); } } } /** * Set the appropriate context attribute for our class path. This * is required only because Jasper depends on it. */ private void setClassPath() { // Validate our current state information if (!(container instanceof Context)) return; ServletContext servletContext = ((Context) container).getServletContext(); if (servletContext == null) return; if (container instanceof StandardContext) { String baseClasspath = ((StandardContext) container).getCompilerClasspath(); if (baseClasspath != null) { servletContext.setAttribute(Globals.CLASS_PATH_ATTR, baseClasspath); return; } } StringBuilder classpath = new StringBuilder(); // Assemble the class path information from our class loader chain ClassLoader loader = getClassLoader(); boolean first = true; while (loader != null) { if (!(loader instanceof URLClassLoader)) { String cp = getClasspath(loader); if (cp != null) { if (!first) { classpath.append(File.pathSeparator); } else { first = false; } classpath.append(cp); } } else { URL[] repositories = ((URLClassLoader) loader).getURLs(); for (int i = 0; i < repositories.length; i++) { if (repositories[i] == null) { continue; } String repository = repositories[i].toString(); if (repository.startsWith("file://")) { repository = repository.substring(7); } else if (repository.startsWith("file:")) { repository = repository.substring(5); } else if (repository.startsWith("jndi:")) { repository = servletContext.getRealPath( repository.substring(5)); } else { continue; } if (!repository.isEmpty()) { if (!first) { classpath.append(File.pathSeparator); } else { first = false; } classpath.append(repository); } } } loader = loader.getParent(); } this.classpath = classpath.toString(); // Store the assembled class path as a servlet context attribute servletContext.setAttribute(Globals.CLASS_PATH_ATTR, classpath.toString()); } // try to extract the classpath from a loader that is not URLClassLoader private String getClasspath( ClassLoader loader ) { try { Method m=loader.getClass().getMethod("getClasspath", new Class[] {}); if (log.isLoggable(Level.FINEST)) log.log(Level.FINEST, "getClasspath " + m); Object o=m.invoke( loader, new Object[] {} ); if (log.isLoggable(Level.FINEST)) log.log(Level.FINEST, "gotClasspath " + o); if (o instanceof String ) return (String)o; return null; } catch (Exception ex) { if (log.isLoggable(Level.FINEST)) log.log(Level.FINEST, "getClasspath ", ex); } return null; } /** * Copy directory. */ private boolean copyDir(DirContext srcDir, File destDir) { try { NamingEnumeration enumeration = srcDir.list(""); while (enumeration.hasMoreElements()) { NameClassPair ncPair = enumeration.nextElement(); String name = ncPair.getName(); Object object = srcDir.lookup(name); File currentFile = new File(destDir, name); if (object instanceof Resource) { InputStream is = ((Resource) object).streamContent(); OutputStream os = new FileOutputStream(currentFile); if (!copy(is, os)) return false; } else if (object instanceof InputStream) { OutputStream os = new FileOutputStream(currentFile); if (!copy((InputStream) object, os)) return false; } else if (object instanceof DirContext) { if (!currentFile.isDirectory() && !currentFile.mkdir()) return false; if (!copyDir((DirContext) object, currentFile)) return false; } } } catch (NamingException e) { return false; } catch (IOException e) { return false; } return true; } /** * Copy a file to the specified temp directory. This is required only * because Jasper depends on it. */ private boolean copy(InputStream is, OutputStream os) { try { byte[] buf = new byte[4096]; while (true) { int len = is.read(buf); if (len < 0) break; os.write(buf, 0, len); } } catch (IOException e) { return false; } finally { try { is.close(); } catch (Exception e) { // do nothing } try { os.close(); } catch (Exception e) { // do nothing } } return true; } public ObjectName getController() { return controller; } public void setController(ObjectName controller) { this.controller = controller; } // START OF PE 4985680 /** * Adds the given package name to the list of packages that may always be * overriden, regardless of whether they belong to a protected namespace */ public void addOverridablePackage(String packageName){ if ( overridablePackages == null){ overridablePackages = new ArrayList(); } overridablePackages.add( packageName ); } // END OF PE 4985680 // START PWC 1.1 6314481 public void setIgnoreHiddenJarFiles(boolean ignoreHiddenJarFiles) { this.ignoreHiddenJarFiles = ignoreHiddenJarFiles; } // END PWC 1.1 6314481 }