package org.mobicents.servlet.sip.annotations;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FilePermission;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.security.AccessControlException;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Policy;
import java.security.PrivilegedAction;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import javax.naming.NamingException;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.WebResource;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.loader.Constants;
//import org.apache.catalina.loader.Reloader;
import org.apache.catalina.loader.ResourceEntry;
import org.apache.log4j.Logger;
import org.apache.naming.JndiPermission;
import org.apache.tomcat.util.IntrospectionUtils;
//import org.apache.catalina.util.StringManager;
import org.apache.tomcat.util.res.StringManager;
/**
* Specialized web application class loader.
*
* This class loader is a full reimplementation of the
* URLClassLoader
from the JDK. It is desinged to be fully
* compatible with a normal URLClassLoader
, although its internal
* behavior may be completely different.
*
* IMPLEMENTATION NOTE - This class loader faithfully follows
* the delegation model recommended in the specification. The system class
* loader will be queried first, then the local repositories, and only then
* delegation to the parent class loader will occur. This allows the web
* application to override any shared class except the classes from J2SE.
* Special handling is provided from the JAXP XML parser interfaces, the JNDI
* interfaces, and the classes from the servlet API, which are never loaded
* from the webapp repository.
*
* IMPLEMENTATION NOTE - Due to limitations in Jasper
* compilation technology, any repository which contains classes from
* the servlet API will be ignored by the class loader.
*
* IMPLEMENTATION NOTE - The class loader generates source
* URLs which include the full JAR URL when a class is loaded from a JAR file,
* which allows setting security permission at the class level, even when a
* class is contained inside a JAR.
*
* IMPLEMENTATION NOTE - Local repositories are searched in
* the order they are added via the initial constructor and/or any subsequent
* calls to addRepository()
or addJar()
.
*
* IMPLEMENTATION NOTE - No check for sealing violations or
* security is made unless a security manager is present.
*
* @author Remy Maucherat
* @author Craig R. McClanahan
*
* @author Vladimir Ralev:
* This class is forked to allow adding jar directories and method access changes.
*/
/**
* @author jean
*
*/
public class AnnotationsClassLoader
extends URLClassLoader
implements Lifecycle
{
private static transient final Logger log = Logger.getLogger(AnnotationsClassLoader.class);
public static final boolean ENABLE_CLEAR_REFERENCES =
Boolean.valueOf(System.getProperty("org.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES", "true")).booleanValue();
private static final String CLASS_FILE_SUFFIX = ".class";
private static final String SERVICES_PREFIX = "/META-INF/services/";
protected class PrivilegedFindResource
implements PrivilegedAction {
protected File file;
protected String path;
PrivilegedFindResource(File file, String path) {
this.file = file;
this.path = path;
}
public Object run() {
return findResourceInternal(file, path);
}
}
// ------------------------------------------------------- Static Variables
/**
* The set of trigger classes that will cause a proposed repository not
* to be added if this class is visible to the class loader that loaded
* this factory class. Typically, trigger classes will be listed for
* components that have been integrated into the JDK for later versions,
* but where the corresponding JAR files are required to run on
* earlier versions.
*/
static final String[] triggers = {
"javax.servlet.Servlet" // Servlet API
};
/**
* Set of package names which are not allowed to be loaded from a webapp
* class loader without delegating first.
*/
protected static final String[] packageTriggers = {
};
/**
* The string manager for this package.
*/
protected static final StringManager sm =
StringManager.getManager(Constants.Package);
/**
* Use anti JAR locking code, which does URL rerouting when accessing
* resources.
*/
boolean antiJARLocking = false;
// ----------------------------------------------------------- Constructors
/**
* Construct a new ClassLoader with no defined repositories and no
* parent ClassLoader.
*/
public AnnotationsClassLoader() {
super(new URL[0]);
this.parent = getParent();
system = getSystemClassLoader();
securityManager = System.getSecurityManager();
if (securityManager != null) {
refreshPolicy();
}
}
/**
* Construct a new ClassLoader with no defined repositories and no
* parent ClassLoader.
*/
public AnnotationsClassLoader(ClassLoader parent) {
super(new URL[0], parent);
this.parent = getParent();
system = getSystemClassLoader();
securityManager = System.getSecurityManager();
if (securityManager != null) {
refreshPolicy();
}
}
// ----------------------------------------------------- Instance Variables
/**
* Associated directory context giving access to the resources in this
* webapp.
*/
protected WebResourceRoot resources = null;
/**
* The cache of ResourceEntry for classes and resources we have loaded,
* keyed by resource name.
*/
protected Map resourceEntries = new HashMap();
/**
* The list of not found resources.
*/
protected Map notFoundResources = new HashMap();
/**
* Should this class loader delegate to the parent class loader
* before searching its own repositories (i.e. the
* usual Java2 delegation model)? If set to false
,
* this class loader will search its own repositories first, and
* delegate to the parent only if the class or resource is not
* found locally.
*/
protected boolean delegate = false;
private final HashMap jarModificationTimes = new HashMap<>();
/**
* Last time a JAR was accessed.
*/
protected long lastJarAccessed = 0L;
/**
* The list of local repositories, in the order they should be searched
* for locally loaded classes or resources.
*/
protected String[] repositories = new String[0];
/**
* Repositories URLs, used to cache the result of getURLs.
*/
protected URL[] repositoryURLs = null;
/**
* Repositories translated as path in the work directory (for Jasper
* originally), but which is used to generate fake URLs should getURLs be
* called.
*/
protected File[] files = new File[0];
/**
* The list of JARs, in the order they should be searched
* for locally loaded classes or resources.
*/
protected JarFile[] jarFiles = new JarFile[0];
/**
* The list of JARs, in the order they should be searched
* for locally loaded classes or resources.
*/
protected File[] jarRealFiles = new File[0];
/**
* The path which will be monitored for added Jar files.
*/
protected String jarPath = null;
/**
* The list of JARs, in the order they should be searched
* for locally loaded classes or resources.
*/
protected String[] jarNames = new String[0];
/**
* The list of JARs last modified dates, in the order they should be
* searched for locally loaded classes or resources.
*/
protected long[] lastModifiedDates = new long[0];
/**
* The list of resources which should be checked when checking for
* modifications.
*/
protected String[] paths = new String[0];
/**
* A list of read File and Jndi Permission's required if this loader
* is for a web application context.
*/
protected ArrayList permissionList = new ArrayList();
/**
* Path where resources loaded from JARs will be extracted.
*/
protected File loaderDir = null;
/**
* The PermissionCollection for each CodeSource for a web
* application context.
*/
protected Map loaderPC = new HashMap();
/**
* Instance of the SecurityManager installed.
*/
protected SecurityManager securityManager = null;
/**
* The parent class loader.
*/
protected ClassLoader parent = null;
/**
* The system class loader.
*/
protected ClassLoader system = null;
/**
* Has this component been started?
* HACK to simulate running app for parsing annotations.
*/
protected boolean started = true;
/**
* Has external repositories.
*/
protected boolean hasExternalRepositories = false;
/**
* need conversion for properties files
*/
protected boolean needConvert = false;
/**
* All permission.
*/
protected Permission allPermission = new java.security.AllPermission();
// ------------------------------------------------------------- Properties
/**
* Get associated resources.
*/
public WebResourceRoot getResources() {
return this.resources;
}
/**
* Set associated resources.
*/
public void setResources(WebResourceRoot resources) {
this.resources = resources;
}
/**
* Return the "delegate first" flag for this class loader.
*/
public boolean getDelegate() {
return (this.delegate);
}
/**
* Set the "delegate first" flag for this class loader.
*
* @param delegate The new "delegate first" flag
*/
public void setDelegate(boolean delegate) {
this.delegate = delegate;
}
/**
* @return Returns the antiJARLocking.
*/
public boolean getAntiJARLocking() {
return antiJARLocking;
}
/**
* @param antiJARLocking The antiJARLocking to set.
*/
public void setAntiJARLocking(boolean antiJARLocking) {
this.antiJARLocking = antiJARLocking;
}
/**
* If there is a Java SecurityManager create a read FilePermission
* or JndiPermission for the file directory path.
*
* @param path file directory path
*/
public void addPermission(String path) {
if (path == null) {
return;
}
if (securityManager != null) {
Permission permission = null;
if( path.startsWith("jndi:") || path.startsWith("jar:jndi:") ) {
if (!path.endsWith("/")) {
path = path + "/";
}
permission = new JndiPermission(path + "*");
addPermission(permission);
} else {
if (!path.endsWith(File.separator)) {
permission = new FilePermission(path, "read");
addPermission(permission);
path = path + File.separator;
}
permission = new FilePermission(path + "-", "read");
addPermission(permission);
}
}
}
/**
* If there is a Java SecurityManager create a read FilePermission
* or JndiPermission for URL.
*
* @param url URL for a file or directory on local system
*/
public void addPermission(URL url) {
if (url != null) {
addPermission(url.toString());
}
}
/**
* If there is a Java SecurityManager create a Permission.
*
* @param permission The permission
*/
public void addPermission(Permission permission) {
if ((securityManager != null) && (permission != null)) {
permissionList.add(permission);
}
}
/**
* Return the JAR path.
*/
public String getJarPath() {
return this.jarPath;
}
/**
* Change the Jar path.
*/
public void setJarPath(String jarPath) {
this.jarPath = jarPath;
}
/**
* Change the work directory.
*/
public void setWorkDir(File workDir) {
this.loaderDir = new File(workDir, "loader");
}
/**
* Utility method for use in subclasses.
* Must be called before Lifecycle methods to have any effect.
*/
protected void setParentClassLoader(ClassLoader pcl) {
parent = pcl;
}
// ------------------------------------------------------- Reloader Methods
/**
* Add a new repository to the set of places this ClassLoader can look for
* classes to be loaded.
*
* @param repository Name of a source of classes to be loaded, such as a
* directory pathname, a JAR file pathname, or a ZIP file pathname
*
* @exception IllegalArgumentException if the specified repository is
* invalid or does not exist
*/
public void addRepository(String repository) {
// Ignore any of the standard repositories, as they are set up using
// either addJar or addRepository
if (repository.startsWith("/WEB-INF/lib")
|| repository.startsWith("/WEB-INF/classes"))
return;
// Add this repository to our underlying class loader
try {
URL url = new URL(repository);
super.addURL(url);
hasExternalRepositories = true;
repositoryURLs = null;
} catch (MalformedURLException e) {
IllegalArgumentException iae = new IllegalArgumentException
("Invalid repository: " + repository);
iae.initCause(e);
throw iae;
}
}
/**
* Add a new repository to the set of places this ClassLoader can look for
* classes to be loaded.
*
* @param repository Name of a source of classes to be loaded, such as a
* directory pathname, a JAR file pathname, or a ZIP file pathname
*
* @exception IllegalArgumentException if the specified repository is
* invalid or does not exist
*/
synchronized void addRepository(String repository, File file) {
// Note : There should be only one (of course), but I think we should
// keep this a bit generic
if (repository == null)
return;
if (log.isDebugEnabled())
log.debug("addRepository(" + repository + ")");
int i;
// Add this repository to our internal list
String[] result = new String[repositories.length + 1];
for (i = 0; i < repositories.length; i++) {
result[i] = repositories[i];
}
result[repositories.length] = repository;
repositories = result;
// Add the file to the list
File[] result2 = new File[files.length + 1];
for (i = 0; i < files.length; i++) {
result2[i] = files[i];
}
result2[files.length] = file;
files = result2;
}
public void addJarDir(String dirPath) {
File dir = new File(dirPath);
if(!dir.isDirectory()) {
log.info("No libraries loaded from this directory: " + dir.getAbsolutePath());
return;
}
File[] files = dir.listFiles();
for(File file:files) {
if(!file.isDirectory() && file.getName().indexOf(".jar") != -1) {
try {
addJar(file.getName(), new JarFile(file), file );
} catch (IOException e) {
log.error("An exception occured when trying to add the following jar to the AnnotationsClassLoader : " + file.getAbsolutePath(), e);
}
} else {
log.info(file.getAbsolutePath() + " is a directory in " + dirPath + " and as such will be skipped.");
}
}
}
synchronized void addJar(String jar, JarFile jarFile, File file)
throws IOException {
if (jar == null)
return;
if (jarFile == null)
return;
if (file == null)
return;
if (log.isDebugEnabled())
log.debug("addJar(" + jar + ")");
int i;
if ((jarPath != null) && (jar.startsWith(jarPath))) {
String jarName = jar.substring(jarPath.length());
while (jarName.startsWith("/"))
jarName = jarName.substring(1);
String[] result = new String[jarNames.length + 1];
for (i = 0; i < jarNames.length; i++) {
result[i] = jarNames[i];
}
result[jarNames.length] = jarName;
jarNames = result;
}
// Register the JAR for tracking
long lastModified =
resources.getClassLoaderResource(jar).getLastModified();
String[] result = new String[paths.length + 1];
for (i = 0; i < paths.length; i++) {
result[i] = paths[i];
}
result[paths.length] = jar;
paths = result;
long[] result3 = new long[lastModifiedDates.length + 1];
for (i = 0; i < lastModifiedDates.length; i++) {
result3[i] = lastModifiedDates[i];
}
result3[lastModifiedDates.length] = lastModified;
lastModifiedDates = result3;
// If the JAR currently contains invalid classes, don't actually use it
// for classloading
if (!validateJarFile(file))
return;
JarFile[] result2 = new JarFile[jarFiles.length + 1];
for (i = 0; i < jarFiles.length; i++) {
result2[i] = jarFiles[i];
}
result2[jarFiles.length] = jarFile;
jarFiles = result2;
// Add the file to the list
File[] result4 = new File[jarRealFiles.length + 1];
for (i = 0; i < jarRealFiles.length; i++) {
result4[i] = jarRealFiles[i];
}
result4[jarRealFiles.length] = file;
jarRealFiles = result4;
}
/**
* Return a String array of the current repositories for this class
* loader. If there are no repositories, a zero-length array is
* returned.For security reason, returns a clone of the Array (since
* String are immutable).
*/
public String[] findRepositories() {
return ((String[])repositories.clone());
}
/**
* Have one or more classes or resources been modified so that a reload
* is appropriate?
*/
public boolean modified() {
if (log.isDebugEnabled())
log.debug("modified()");
for (Entry entry : resourceEntries.entrySet()) {
long cachedLastModified = entry.getValue().lastModified;
long lastModified = resources.getClassLoaderResource(
entry.getKey()).getLastModified();
if (lastModified != cachedLastModified) {
if( log.isDebugEnabled() )
log.debug(sm.getString("webappClassLoader.resourceModified",
entry.getKey(),
new Date(cachedLastModified),
new Date(lastModified)));
return true;
}
}
// Check if JARs have been added or removed
WebResource[] jars = resources.listResources("/WEB-INF/lib");
// Filter out non-JAR resources
int jarCount = 0;
for (WebResource jar : jars) {
if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) {
jarCount++;
Long recordedLastModified = jarModificationTimes.get(jar.getName());
if (recordedLastModified == null) {
// Jar has been added
log.info(sm.getString("webappClassLoader.jarsAdded",
resources.getContext().getName()));
return true;
}
if (recordedLastModified.longValue() != jar.getLastModified()) {
// Jar has been changed
log.info(sm.getString("webappClassLoader.jarsModified",
resources.getContext().getName()));
return true;
}
}
}
if (jarCount < jarModificationTimes.size()){
log.info(sm.getString("webappClassLoader.jarsRemoved",
resources.getContext().getName()));
return true;
}
// No classes have been modified
return false;
}
/**
* Render a String representation of this object.
*/
public String toString() {
StringBuffer sb = new StringBuffer("WebappClassLoader\r\n");
sb.append(" delegate: ");
sb.append(delegate);
sb.append("\r\n");
sb.append(" repositories:\r\n");
if (repositories != null) {
for (int i = 0; i < repositories.length; i++) {
sb.append(" ");
sb.append(repositories[i]);
sb.append("\r\n");
}
}
if (this.parent != null) {
sb.append("----------> Parent Classloader:\r\n");
sb.append(this.parent.toString());
sb.append("\r\n");
}
return (sb.toString());
}
// ---------------------------------------------------- ClassLoader Methods
/**
* Add the specified URL to the classloader.
*/
protected void addURL(URL url) {
super.addURL(url);
hasExternalRepositories = true;
repositoryURLs = null;
}
/**
* Find the specified class in our local repositories, if possible. If
* not found, throw ClassNotFoundException
.
*
* @param name Name of the class to be loaded
*
* @exception ClassNotFoundException if the class was not found
*/
public Class findClass(String name) throws ClassNotFoundException {
if (log.isDebugEnabled())
log.debug(" findClass(" + name + ")");
// Cannot load anything from local repositories if class loader is stopped
if (!started) {
throw new ClassNotFoundException(name);
}
// (1) Permission to define this class when using a SecurityManager
if (securityManager != null) {
int i = name.lastIndexOf('.');
if (i >= 0) {
try {
if (log.isTraceEnabled())
log.trace(" securityManager.checkPackageDefinition");
securityManager.checkPackageDefinition(name.substring(0,i));
} catch (Exception se) {
if (log.isTraceEnabled())
log.trace(" -->Exception-->ClassNotFoundException", se);
throw new ClassNotFoundException(name, se);
}
}
}
// Ask our superclass to locate this class, if possible
// (throws ClassNotFoundException if it is not found)
Class clazz = null;
try {
if (log.isTraceEnabled())
log.trace(" findClassInternal(" + name + ")");
try {
clazz = findClassInternal(name);
} catch(ClassNotFoundException cnfe) {
if (!hasExternalRepositories) {
throw cnfe;
}
} catch(AccessControlException ace) {
throw new ClassNotFoundException(name, ace);
} catch (RuntimeException e) {
if (log.isTraceEnabled())
log.trace(" -->RuntimeException Rethrown", e);
throw e;
}
if ((clazz == null) && hasExternalRepositories) {
try {
clazz = super.findClass(name);
} catch(AccessControlException ace) {
throw new ClassNotFoundException(name, ace);
} catch (RuntimeException e) {
if (log.isTraceEnabled())
log.trace(" -->RuntimeException Rethrown", e);
throw e;
}
}
if (clazz == null) {
if (log.isDebugEnabled())
log.debug(" --> Returning ClassNotFoundException");
throw new ClassNotFoundException(name);
}
} catch (ClassNotFoundException e) {
if (log.isTraceEnabled())
log.trace(" --> Passing on ClassNotFoundException");
throw e;
}
// Return the class we have located
if (log.isTraceEnabled())
log.debug(" Returning class " + clazz);
if (log.isTraceEnabled())
log.debug(" Loaded by " + clazz.getClassLoader());
return (clazz);
}
/**
* Find the specified resource in our local repository, and return a
* URL
refering to it, or null
if this resource
* cannot be found.
*
* @param name Name of the resource to be found
*/
public URL findResource(final String name) {
if (log.isDebugEnabled())
log.debug(" findResource(" + name + ")");
URL url = null;
ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
if (entry == null) {
entry = findResourceInternal(name, name);
}
if (entry != null) {
url = entry.source;
}
if ((url == null) && hasExternalRepositories)
url = super.findResource(name);
if (log.isDebugEnabled()) {
if (url != null)
log.debug(" --> Returning '" + url.toString() + "'");
else
log.debug(" --> Resource not found, returning null");
}
return (url);
}
/**
* Return an enumeration of URLs
representing all of the
* resources with the given name. If no resources with this name are
* found, return an empty enumeration.
*
* @param name Name of the resources to be found
*
* @exception IOException if an input/output error occurs
*/
@Override
public Enumeration findResources(String name) throws IOException {
if (log.isDebugEnabled())
log.debug(" findResources(" + name + ")");
checkStateForResourceLoading(name);
LinkedHashSet result = new LinkedHashSet<>();
String path = nameToPath(name);
WebResource[] webResources = resources.getClassLoaderResources(path);
for (WebResource webResource : webResources) {
if (webResource.exists()) {
result.add(webResource.getURL());
}
}
// Adding the results of a call to the superclass
if (hasExternalRepositories) {
Enumeration otherResourcePaths = super.findResources(name);
while (otherResourcePaths.hasMoreElements()) {
result.add(otherResourcePaths.nextElement());
}
}
return Collections.enumeration(result);
}
private String binaryNameToPath(String binaryName, boolean withLeadingSlash) {
// 1 for leading '/', 6 for ".class"
StringBuilder path = new StringBuilder(7 + binaryName.length());
if (withLeadingSlash) {
path.append('/');
}
path.append(binaryName.replace('.', '/'));
path.append(CLASS_FILE_SUFFIX);
return path.toString();
}
private String nameToPath(String name) {
if (name.startsWith("/")) {
return name;
}
StringBuilder path = new StringBuilder(
1 + name.length());
path.append('/');
path.append(name);
return path.toString();
}
/**
* Find the resource with the given name. A resource is some data
* (images, audio, text, etc.) that can be accessed by class code in a
* way that is independent of the location of the code. The name of a
* resource is a "/"-separated path name that identifies the resource.
* If the resource cannot be found, return null
.
*
* This method searches according to the following algorithm, returning
* as soon as it finds the appropriate URL. If the resource cannot be
* found, returns null
.
*
* If the delegate
property is set to true
,
* call the getResource()
method of the parent class
* loader, if any.
* Call findResource()
to find this resource in our
* locally defined repositories.
* Call the getResource()
method of the parent class
* loader, if any.
*
*
* @param name Name of the resource to return a URL for
*/
public URL getResource(String name) {
if (log.isDebugEnabled())
log.debug("getResource(" + name + ")");
URL url = null;
// (1) Delegate to parent if requested
if (delegate) {
if (log.isDebugEnabled())
log.debug(" Delegating to parent classloader " + parent);
ClassLoader loader = parent;
if (loader == null)
loader = system;
url = loader.getResource(name);
if (url != null) {
if (log.isDebugEnabled())
log.debug(" --> Returning '" + url.toString() + "'");
return (url);
}
}
// (2) Search local repositories
url = findResource(name);
if (url != null) {
// Locating the repository for special handling in the case
// of a JAR
if (antiJARLocking) {
ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
try {
String repository = entry.codeBase.toString();
if ((repository.endsWith(".jar"))
&& (!(name.endsWith(".class")))) {
// Copy binary content to the work directory if not present
File resourceFile = new File(loaderDir, name);
url = getURI(resourceFile);
}
} catch (Exception e) {
// Ignore
}
}
if (log.isDebugEnabled())
log.debug(" --> Returning '" + url.toString() + "'");
return (url);
}
// (3) Delegate to parent unconditionally if not already attempted
if( !delegate ) {
ClassLoader loader = parent;
if (loader == null)
loader = system;
url = loader.getResource(name);
if (url != null) {
if (log.isDebugEnabled())
log.debug(" --> Returning '" + url.toString() + "'");
return (url);
}
}
// (4) Resource was not found
if (log.isDebugEnabled())
log.debug(" --> Resource not found, returning null");
return (null);
}
/**
* Find the resource with the given name, and return an input stream
* that can be used for reading it. The search order is as described
* for getResource()
, after checking to see if the resource
* data has been previously cached. If the resource cannot be found,
* return null
.
*
* @param name Name of the resource to return an input stream for
*/
public InputStream getResourceAsStream(String name) {
if (log.isDebugEnabled())
log.debug("getResourceAsStream(" + name + ")");
InputStream stream = null;
// (0) Check for a cached copy of this resource
stream = findLoadedResource(name);
if (stream != null) {
if (log.isDebugEnabled())
log.debug(" --> Returning stream from cache");
return (stream);
}
// (1) Delegate to parent if requested
if (delegate) {
if (log.isDebugEnabled())
log.debug(" Delegating to parent classloader " + parent);
ClassLoader loader = parent;
if (loader == null)
loader = system;
stream = loader.getResourceAsStream(name);
if (stream != null) {
// FIXME - cache???
if (log.isDebugEnabled())
log.debug(" --> Returning stream from parent");
return (stream);
}
}
// (2) Search local repositories
if (log.isDebugEnabled())
log.debug(" Searching local repositories");
URL url = findResource(name);
if (url != null) {
// FIXME - cache???
if (log.isDebugEnabled())
log.debug(" --> Returning stream from local");
stream = findLoadedResource(name);
try {
if (hasExternalRepositories && (stream == null))
stream = url.openStream();
} catch (IOException e) {
; // Ignore
}
if (stream != null)
return (stream);
}
// (3) Delegate to parent unconditionally
if (!delegate) {
if (log.isDebugEnabled())
log.debug(" Delegating to parent classloader unconditionally " + parent);
ClassLoader loader = parent;
if (loader == null)
loader = system;
stream = loader.getResourceAsStream(name);
if (stream != null) {
// FIXME - cache???
if (log.isDebugEnabled())
log.debug(" --> Returning stream from parent");
return (stream);
}
}
// (4) Resource was not found
if (log.isDebugEnabled())
log.debug(" --> Resource not found, returning null");
return (null);
}
/**
* Load the class with the specified name. This method searches for
* classes in the same manner as loadClass(String, boolean)
* with false
as the second argument.
*
* @param name Name of the class to be loaded
*
* @exception ClassNotFoundException if the class was not found
*/
public Class loadClass(String name) throws ClassNotFoundException {
return (loadClass(name, false));
}
/**
* Load the class with the specified name, searching using the following
* algorithm until it finds and returns the class. If the class cannot
* be found, returns ClassNotFoundException
.
*
* Call findLoadedClass(String)
to check if the
* class has already been loaded. If it has, the same
* Class
object is returned.
* If the delegate
property is set to true
,
* call the loadClass()
method of the parent class
* loader, if any.
* Call findClass()
to find this class in our locally
* defined repositories.
* Call the loadClass()
method of our parent
* class loader, if any.
*
* If the class was found using the above steps, and the
* resolve
flag is true
, this method will then
* call resolveClass(Class)
on the resulting Class object.
*
* @param name Name of the class to be loaded
* @param resolve If true
then resolve the class
*
* @exception ClassNotFoundException if the class was not found
*/
public Class loadClass(String name, boolean resolve)
throws ClassNotFoundException {
if (log.isDebugEnabled())
log.debug("loadClass(" + name + ", " + resolve + ")");
Class clazz = null;
/* Log access to stopped classloader
if (!started) {
try {
throw new IllegalStateException();
} catch (IllegalStateException e) {
log.info(sm.getString("webappClassLoader.stopped", name), e);
}
}*/
// (0) Check our previously loaded local class cache
clazz = findLoadedClass0(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug(" Returning class from cache");
if (resolve)
resolveClass(clazz);
return (clazz);
}
// (0.1) Check our previously loaded class cache
clazz = findLoadedClass(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug(" Returning class from cache");
if (resolve)
resolveClass(clazz);
return (clazz);
}
// (0.2) Try loading the class with the system class loader, to prevent
// the webapp from overriding J2SE classes
try {
clazz = system.loadClass(name);
if (clazz != null) {
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
// Ignore
}
// (0.5) Permission to access this class when using a SecurityManager
if (securityManager != null) {
int i = name.lastIndexOf('.');
if (i >= 0) {
try {
securityManager.checkPackageAccess(name.substring(0,i));
} catch (SecurityException se) {
String error = "Security Violation, attempt to use " +
"Restricted Class: " + name;
log.info(error, se);
throw new ClassNotFoundException(error, se);
}
}
}
boolean delegateLoad = delegate || filter(name);
// (1) Delegate to our parent if requested
if (delegateLoad) {
if (log.isDebugEnabled())
log.debug(" Delegating to parent classloader1 " + parent);
ClassLoader loader = parent;
if (loader == null)
loader = system;
try {
clazz = loader.loadClass(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug(" Loading class from parent");
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
;
}
}
// (2) Search local repositories
if (log.isDebugEnabled())
log.debug(" Searching local repositories");
try {
clazz = findClass(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug(" Loading class from local repository");
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
;
}
// (3) Delegate to parent unconditionally
if (!delegateLoad) {
if (log.isDebugEnabled())
log.debug(" Delegating to parent classloader at end: " + parent);
ClassLoader loader = parent;
if (loader == null)
loader = system;
try {
clazz = loader.loadClass(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug(" Loading class from parent");
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
;
}
}
throw new ClassNotFoundException(name);
}
/**
* Get the Permissions for a CodeSource. If this instance
* of WebappClassLoader is for a web application context,
* add read FilePermission or JndiPermissions for the base
* directory (if unpacked),
* the context URL, and jar file resources.
*
* @param codeSource where the code was loaded from
* @return PermissionCollection for CodeSource
*/
protected PermissionCollection getPermissions(CodeSource codeSource) {
String codeUrl = codeSource.getLocation().toString();
PermissionCollection pc;
if ((pc = (PermissionCollection)loaderPC.get(codeUrl)) == null) {
pc = super.getPermissions(codeSource);
if (pc != null) {
Iterator perms = permissionList.iterator();
while (perms.hasNext()) {
Permission p = (Permission)perms.next();
pc.add(p);
}
loaderPC.put(codeUrl,pc);
}
}
return (pc);
}
/**
* Returns the search path of URLs for loading classes and resources.
* This includes the original list of URLs specified to the constructor,
* along with any URLs subsequently appended by the addURL() method.
* @return the search path of URLs for loading classes and resources.
*/
public URL[] getURLs() {
if (repositoryURLs != null) {
return repositoryURLs;
}
URL[] external = super.getURLs();
int filesLength = files.length;
int jarFilesLength = jarRealFiles.length;
int length = filesLength + jarFilesLength + external.length;
int i;
try {
URL[] urls = new URL[length];
for (i = 0; i < length; i++) {
if (i < filesLength) {
urls[i] = getURL(files[i], true);
} else if (i < filesLength + jarFilesLength) {
urls[i] = getURL(jarRealFiles[i - filesLength], true);
} else {
urls[i] = external[i - filesLength - jarFilesLength];
}
}
repositoryURLs = urls;
} catch (MalformedURLException e) {
repositoryURLs = new URL[0];
}
return repositoryURLs;
}
// ------------------------------------------------------ Lifecycle Methods
/**
* Add a lifecycle event listener to this component.
*
* @param listener The listener to add
*/
public void addLifecycleListener(LifecycleListener listener) {
}
/**
* Get the lifecycle listeners associated with this lifecycle. If this
* Lifecycle has no listeners registered, a zero-length array is returned.
*/
public LifecycleListener[] findLifecycleListeners() {
return new LifecycleListener[0];
}
/**
* Remove a lifecycle event listener from this component.
*
* @param listener The listener to remove
*/
public void removeLifecycleListener(LifecycleListener listener) {
}
/**
* Start the class loader.
*
* @exception LifecycleException if a lifecycle error occurs
*/
@Override
public void start() throws LifecycleException {
// state = LifecycleState.STARTING_PREP;
//
// WebResource classes = resources.getResource("/WEB-INF/classes");
// if (classes.isDirectory() && classes.canRead()) {
// localRepositories.add(classes.getURL());
// }
// WebResource[] jars = resources.listResources("/WEB-INF/lib");
// for (WebResource jar : jars) {
// if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) {
// localRepositories.add(jar.getURL());
// jarModificationTimes.put(
// jar.getName(), Long.valueOf(jar.getLastModified()));
// }
// }
//
// state = LifecycleState.STARTING;
String encoding = null;
try {
encoding = System.getProperty("file.encoding");
} catch (SecurityException e) {
return;
}
if (encoding.indexOf("EBCDIC")!=-1) {
needConvert = true;
}
// state = LifecycleState.STARTED;
}
/**
* Stop the class loader.
*
* @exception LifecycleException if a lifecycle error occurs
*/
public void stop() throws LifecycleException {
// Clearing references should be done before setting started to
// false, due to possible side effects
clearReferences();
// Annotations dont need a running app.
//started = false;
int length = files.length;
for (int i = 0; i < length; i++) {
files[i] = null;
}
length = jarFiles.length;
for (int i = 0; i < length; i++) {
try {
if (jarFiles[i] != null) {
jarFiles[i].close();
}
} catch (IOException e) {
// Ignore
}
jarFiles[i] = null;
}
notFoundResources.clear();
resourceEntries.clear();
resources = null;
repositories = null;
repositoryURLs = null;
files = null;
jarFiles = null;
jarRealFiles = null;
jarPath = null;
jarNames = null;
lastModifiedDates = null;
paths = null;
hasExternalRepositories = false;
parent = null;
permissionList.clear();
loaderPC.clear();
if (loaderDir != null) {
deleteDir(loaderDir);
}
}
/**
* Used to periodically signal to the classloader to release
* JAR resources.
*/
public void closeJARs(boolean force) {
if (jarFiles.length > 0) {
synchronized (jarFiles) {
if (force || (System.currentTimeMillis()
> (lastJarAccessed + 90000))) {
for (int i = 0; i < jarFiles.length; i++) {
try {
if (jarFiles[i] != null) {
jarFiles[i].close();
jarFiles[i] = null;
}
} catch (IOException e) {
if (log.isDebugEnabled()) {
log.debug("Failed to close JAR", e);
}
}
}
}
}
}
}
// ------------------------------------------------------ Protected Methods
/**
* Clear references.
*/
protected void clearReferences() {
// Unregister any JDBC drivers loaded by this classloader
Enumeration drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = (Driver) drivers.nextElement();
if (driver.getClass().getClassLoader() == this) {
try {
DriverManager.deregisterDriver(driver);
} catch (SQLException e) {
log.warn("SQL driver deregistration failed", e);
}
}
}
// Null out any static or final fields from loaded classes,
// as a workaround for apparent garbage collection bugs
if (ENABLE_CLEAR_REFERENCES) {
Iterator loadedClasses = ((HashMap)((HashMap) resourceEntries).clone()).values().iterator();
while (loadedClasses.hasNext()) {
ResourceEntry entry = (ResourceEntry) loadedClasses.next();
if (entry.loadedClass != null) {
Class clazz = entry.loadedClass;
try {
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
int mods = field.getModifiers();
if (field.getType().isPrimitive()
|| (field.getName().indexOf("$") != -1)) {
continue;
}
if (Modifier.isStatic(mods)) {
try {
field.setAccessible(true);
if (Modifier.isFinal(mods)) {
if (!((field.getType().getName().startsWith("java."))
|| (field.getType().getName().startsWith("javax.")))) {
nullInstance(field.get(null));
}
} else {
field.set(null, null);
if (log.isDebugEnabled()) {
log.debug("Set field " + field.getName()
+ " to null in class " + clazz.getName());
}
}
} catch (Throwable t) {
if (log.isDebugEnabled()) {
log.debug("Could not set field " + field.getName()
+ " to null in class " + clazz.getName(), t);
}
}
}
}
} catch (Throwable t) {
if (log.isDebugEnabled()) {
log.debug("Could not clean fields for class " + clazz.getName(), t);
}
}
}
}
}
// Clear the IntrospectionUtils cache.
IntrospectionUtils.clear();
// Clear the classloader reference in common-logging
org.apache.juli.logging.LogFactory.release(this);
// Clear the classloader reference in the VM's bean introspector
java.beans.Introspector.flushCaches();
}
protected void nullInstance(Object instance) {
if (instance == null) {
return;
}
Field[] fields = instance.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
int mods = field.getModifiers();
if (field.getType().isPrimitive()
|| (field.getName().indexOf("$") != -1)) {
continue;
}
try {
field.setAccessible(true);
if (Modifier.isStatic(mods) && Modifier.isFinal(mods)) {
// Doing something recursively is too risky
continue;
} else {
Object value = field.get(instance);
if (null != value) {
Class valueClass = value.getClass();
if (!loadedByThisOrChild(valueClass)) {
if (log.isDebugEnabled()) {
log.debug("Not setting field " + field.getName() +
" to null in object of class " +
instance.getClass().getName() +
" because the referenced object was of type " +
valueClass.getName() +
" which was not loaded by this WebappClassLoader.");
}
} else {
field.set(instance, null);
if (log.isDebugEnabled()) {
log.debug("Set field " + field.getName()
+ " to null in class " + instance.getClass().getName());
}
}
}
}
} catch (Throwable t) {
if (log.isDebugEnabled()) {
log.debug("Could not set field " + field.getName()
+ " to null in object instance of class "
+ instance.getClass().getName(), t);
}
}
}
}
/**
* Determine whether a class was loaded by this class loader or one of
* its child class loaders.
*/
protected boolean loadedByThisOrChild(Class clazz)
{
boolean result = false;
for (ClassLoader classLoader = clazz.getClassLoader();
null != classLoader; classLoader = classLoader.getParent()) {
if (classLoader.equals(this)) {
result = true;
break;
}
}
return result;
}
/**
* Used to periodically signal to the classloader to release JAR resources.
*/
protected boolean openJARs() {
if (started && (jarFiles.length > 0)) {
lastJarAccessed = System.currentTimeMillis();
if (jarFiles[0] == null) {
for (int i = 0; i < jarFiles.length; i++) {
try {
jarFiles[i] = new JarFile(jarRealFiles[i]);
} catch (IOException e) {
if (log.isDebugEnabled()) {
log.debug("Failed to open JAR", e);
}
return false;
}
}
}
}
return true;
}
/**
* Find specified class in local repositories.
*
* @return the loaded class, or null if the class isn't found
*/
protected Class findClassInternal(String name)
throws ClassNotFoundException {
if (!validate(name))
throw new ClassNotFoundException(name);
String tempPath = name.replace('.', '/');
String classPath = tempPath + ".class";
ResourceEntry entry = null;
entry = findResourceInternal(name, classPath);
if (entry == null)
throw new ClassNotFoundException(name);
Class clazz = entry.loadedClass;
if (clazz != null)
return clazz;
synchronized (this) {
if (entry.binaryContent == null && entry.loadedClass == null)
throw new ClassNotFoundException(name);
// Looking up the package
String packageName = null;
int pos = name.lastIndexOf('.');
if (pos != -1)
packageName = name.substring(0, pos);
Package pkg = null;
if (packageName != null) {
pkg = getPackage(packageName);
// Define the package (if null)
if (pkg == null) {
try {
if (entry.manifest == null) {
definePackage(packageName, null, null, null, null,
null, null, null);
} else {
definePackage(packageName, entry.manifest,
entry.codeBase);
}
} catch (IllegalArgumentException e) {
// Ignore: normal error due to dual definition of package
}
pkg = getPackage(packageName);
}
}
if (securityManager != null) {
// Checking sealing
if (pkg != null) {
boolean sealCheck = true;
if (pkg.isSealed()) {
sealCheck = pkg.isSealed(entry.codeBase);
} else {
sealCheck = (entry.manifest == null)
|| !isPackageSealed(packageName, entry.manifest);
}
if (!sealCheck)
throw new SecurityException
("Sealing violation loading " + name + " : Package "
+ packageName + " is sealed.");
}
}
if (entry.loadedClass == null) {
clazz = defineClass(name, entry.binaryContent, 0,
entry.binaryContent.length,
new CodeSource(entry.codeBase, entry.certificates));
entry.loadedClass = clazz;
entry.binaryContent = null;
entry.source = null;
entry.codeBase = null;
entry.manifest = null;
entry.certificates = null;
} else {
clazz = entry.loadedClass;
}
}
return clazz;
}
/**
* Find specified resource in local repositories. This block
* will execute under an AccessControl.doPrivilege block.
*
* @return the loaded resource, or null if the resource isn't found
*/
protected ResourceEntry findResourceInternal(File file, String path){
ResourceEntry entry = new ResourceEntry();
try {
entry.source = getURI(new File(file, path));
entry.codeBase = getURL(new File(file, path), false);
} catch (MalformedURLException e) {
return null;
}
return entry;
}
protected void checkStateForClassLoading(String className) throws ClassNotFoundException {
// It is not permitted to load new classes once the web application has
// been stopped.
try {
checkStateForResourceLoading(className);
} catch (IllegalStateException ise) {
ClassNotFoundException cnfe = new ClassNotFoundException();
cnfe.initCause(ise);
throw cnfe;
}
}
protected void checkStateForResourceLoading(String resource) throws IllegalStateException {
// It is not permitted to load resources once the web application has
// been stopped.
if (!started) {
String msg = sm.getString("webappClassLoader.stopped", resource);
IllegalStateException ise = new IllegalStateException(msg);
log.info(msg, ise);
throw ise;
}
}
/**
* Find specified resource in local repositories.
*
* @return the loaded resource, or null if the resource isn't found
*/
protected ResourceEntry findResourceInternal(final String name, final String path) {
checkStateForResourceLoading(name);
if (name == null || path == null) {
return null;
}
ResourceEntry entry = resourceEntries.get(path);
if (entry != null) {
return entry;
}
boolean isClassResource = path.endsWith(CLASS_FILE_SUFFIX);
boolean isCacheable = isClassResource;
if (!isCacheable) {
isCacheable = path.startsWith(SERVICES_PREFIX);
}
WebResource resource = null;
boolean fileNeedConvert = false;
resource = resources.getClassLoaderResource(path);
if (!resource.exists()) {
return null;
}
entry = new ResourceEntry();
entry.source = resource.getURL();
entry.codeBase = resource.getCodeBase();
entry.lastModified = resource.getLastModified();
if (needConvert && path.endsWith(".properties")) {
fileNeedConvert = true;
}
/* Only cache the binary content if there is some content
* available one of the following is true:
* a) It is a class file since the binary content is only cached
* until the class has been loaded
* or
* b) The file needs conversion to address encoding issues (see
* below)
* or
* c) The resource is a service provider configuration file located
* under META=INF/services
*
* In all other cases do not cache the content to prevent
* excessive memory usage if large resources are present (see
* https://issues.apache.org/bugzilla/show_bug.cgi?id=53081).
*/
if (isCacheable || fileNeedConvert) {
byte[] binaryContent = resource.getContent();
if (binaryContent != null) {
if (fileNeedConvert) {
// Workaround for certain files on platforms that use
// EBCDIC encoding, when they are read through FileInputStream.
// See commit message of rev.303915 for details
// http://svn.apache.org/viewvc?view=revision&revision=303915
String str = new String(binaryContent);
try {
binaryContent = str.getBytes(StandardCharsets.UTF_8);
} catch (Exception e) {
return null;
}
}
entry.binaryContent = binaryContent;
// The certificates and manifest are made available as a side
// effect of reading the binary content
entry.certificates = resource.getCertificates();
}
}
entry.manifest = resource.getManifest();
// if (isClassResource && entry.binaryContent != null &&
// this.transformers.size() > 0) {
// // If the resource is a class just being loaded, decorate it
// // with any attached transformers
// String className = name.endsWith(CLASS_FILE_SUFFIX) ?
// name.substring(0, name.length() - CLASS_FILE_SUFFIX.length()) : name;
// String internalName = className.replace(".", "/");
//
// for (ClassFileTransformer transformer : this.transformers) {
// try {
// byte[] transformed = transformer.transform(
// this, internalName, null, null, entry.binaryContent
// );
// if (transformed != null) {
// entry.binaryContent = transformed;
// }
// } catch (IllegalClassFormatException e) {
// log.error(sm.getString("webappClassLoader.transformError", name), e);
// return null;
// }
// }
// }
// Add the entry in the local resource repository
synchronized (resourceEntries) {
// Ensures that all the threads which may be in a race to load
// a particular class all end up with the same ResourceEntry
// instance
ResourceEntry entry2 = resourceEntries.get(path);
if (entry2 == null) {
resourceEntries.put(path, entry);
} else {
entry = entry2;
}
}
return entry;
}
/**
* Returns true if the specified package name is sealed according to the
* given manifest.
*/
protected boolean isPackageSealed(String name, Manifest man) {
String path = name.replace('.', '/') + '/';
Attributes attr = man.getAttributes(path);
String sealed = null;
if (attr != null) {
sealed = attr.getValue(Name.SEALED);
}
if (sealed == null) {
if ((attr = man.getMainAttributes()) != null) {
sealed = attr.getValue(Name.SEALED);
}
}
return "true".equalsIgnoreCase(sealed);
}
/**
* Finds the resource with the given name if it has previously been
* loaded and cached by this class loader, and return an input stream
* to the resource data. If this resource has not been cached, return
* null
.
*
* @param name Name of the resource to return
*/
protected InputStream findLoadedResource(String name) {
ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
if (entry != null) {
if (entry.binaryContent != null)
return new ByteArrayInputStream(entry.binaryContent);
}
return (null);
}
/**
* Finds the class with the given name if it has previously been
* loaded and cached by this class loader, and return the Class object.
* If this class has not been cached, return null
.
*
* @param name Name of the resource to return
*/
protected Class findLoadedClass0(String name) {
ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
if (entry != null) {
return entry.loadedClass;
}
return (null); // FIXME - findLoadedResource()
}
/**
* Refresh the system policy file, to pick up eventual changes.
*/
protected void refreshPolicy() {
try {
// The policy file may have been modified to adjust
// permissions, so we're reloading it when loading or
// reloading a Context
Policy policy = Policy.getPolicy();
policy.refresh();
} catch (AccessControlException e) {
// Some policy files may restrict this, even for the core,
// so this exception is ignored
}
}
/**
* Filter classes.
*
* @param name class name
* @return true if the class should be filtered
*/
protected boolean filter(String name) {
if (name == null)
return false;
// Looking up the package
String packageName = null;
int pos = name.lastIndexOf('.');
if (pos != -1)
packageName = name.substring(0, pos);
else
return false;
for (int i = 0; i < packageTriggers.length; i++) {
if (packageName.startsWith(packageTriggers[i]))
return true;
}
return false;
}
/**
* Validate a classname. As per SRV.9.7.2, we must restict loading of
* classes from J2SE (java.*) and classes of the servlet API
* (javax.servlet.*). That should enhance robustness and prevent a number
* of user error (where an older version of servlet.jar would be present
* in /WEB-INF/lib).
*
* @param name class name
* @return true if the name is valid
*/
protected boolean validate(String name) {
if (name == null)
return false;
if (name.startsWith("java."))
return false;
return true;
}
/**
* Check the specified JAR file, and return true
if it does
* not contain any of the trigger classes.
*
* @param jarfile The JAR file to be checked
*
* @exception IOException if an input/output error occurs
*/
protected boolean validateJarFile(File jarfile)
throws IOException {
if (triggers == null)
return (true);
JarFile jarFile = new JarFile(jarfile);
for (int i = 0; i < triggers.length; i++) {
Class clazz = null;
try {
if (parent != null) {
clazz = parent.loadClass(triggers[i]);
} else {
clazz = Class.forName(triggers[i]);
}
} catch (Throwable t) {
clazz = null;
}
if (clazz == null)
continue;
String name = triggers[i].replace('.', '/') + ".class";
if (log.isDebugEnabled())
log.debug(" Checking for " + name);
JarEntry jarEntry = jarFile.getJarEntry(name);
if (jarEntry != null) {
log.info("validateJarFile(" + jarfile +
") - jar not loaded. See Servlet Spec 2.3, "
+ "section 9.7.2. Offending class: " + name);
jarFile.close();
return (false);
}
}
jarFile.close();
return (true);
}
/**
* Get URL.
*/
protected URL getURL(File file, boolean encoded)
throws MalformedURLException {
File realFile = file;
try {
realFile = realFile.getCanonicalFile();
} catch (IOException e) {
// Ignore
}
if(encoded) {
return getURI(realFile);
} else {
return realFile.toURL();
}
}
/**
* Get URL.
*/
protected URL getURI(File file)
throws MalformedURLException {
File realFile = file;
try {
realFile = realFile.getCanonicalFile();
} catch (IOException e) {
// Ignore
}
return realFile.toURI().toURL();
}
/**
* Delete the specified directory, including all of its contents and
* subdirectories recursively.
*
* @param dir File object representing the directory to be deleted
*/
protected static void deleteDir(File dir) {
String files[] = dir.list();
if (files == null) {
files = new String[0];
}
for (int i = 0; i < files.length; i++) {
File file = new File(dir, files[i]);
if (file.isDirectory()) {
deleteDir(file);
} else {
try {
file.delete();
} catch (SecurityException se) {
log.error("The file " + file.getAbsolutePath() + " couldn't be deleted");
}
}
}
try {
dir.delete();
} catch (SecurityException se) {
log.error("The directory " + dir.getAbsolutePath() + " couldn't be deleted");
}
}
@Override
public void init() throws LifecycleException {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public void destroy() throws LifecycleException {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public LifecycleState getState() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public String getStateName() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
}