blob: 354b16e1986cd1f7784fc49a1684780ff5995cab [file] [log] [blame]
/**
* 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.
*/
package org.apache.axis2.classloader;
import java.io.IOException;
import java.io.File;
import java.net.URL;
import java.net.URI;
import java.net.URLClassLoader;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import java.security.cert.Certificate;
import java.util.Collection;
import java.util.Enumeration;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
/**
* The JarFileClassLoader that loads classes and resources from a list of JarFiles. This method is simmilar to URLClassLoader
* except it properly closes JarFiles when the classloader is destroyed so that the file read lock will be released, and
* the jar file can be modified and deleted.
* <p>
*
* @version $Rev$ $Date$
*/
public class JarFileClassLoader extends MultiParentClassLoader {
private static final URL[] EMPTY_URLS = new URL[0];
private final UrlResourceFinder resourceFinder = new UrlResourceFinder();
private final AccessControlContext acc;
/**
* Creates a JarFileClassLoader that is a child of the system class loader.
* @param urls a list of URLs from which classes and resources should be loaded
*/
public JarFileClassLoader(URL[] urls) {
super(EMPTY_URLS);
this.acc = AccessController.getContext();
addURLs(urls);
}
/**
* Creates a JarFileClassLoader that is a child of the specified class loader.
* @param urls a list of URLs from which classes and resources should be loaded
* @param parent the parent of this class loader
*/
public JarFileClassLoader(URL[] urls, ClassLoader parent) {
super(EMPTY_URLS, parent);
this.acc = AccessController.getContext();
addURLs(urls);
}
public JarFileClassLoader(URL[] urls, ClassLoader parent, boolean inverseClassLoading, String[] hiddenClasses, String[] nonOverridableClasses) {
super(EMPTY_URLS, parent, inverseClassLoading, hiddenClasses, nonOverridableClasses);
this.acc = AccessController.getContext();
addURLs(urls);
}
/**
* Creates a named class loader as a child of the specified parents.
* @param urls the urls from which this class loader will classes and resources
* @param parents the parents of this class loader
*/
public JarFileClassLoader(URL[] urls, ClassLoader[] parents) {
super(EMPTY_URLS, parents);
this.acc = AccessController.getContext();
addURLs(urls);
}
public JarFileClassLoader(URL[] urls, ClassLoader[] parents, boolean inverseClassLoading, Collection hiddenClasses, Collection nonOverridableClasses) {
super(EMPTY_URLS, parents, inverseClassLoading, hiddenClasses, nonOverridableClasses);
this.acc = AccessController.getContext();
addURLs(urls);
}
public JarFileClassLoader(URL[] urls, ClassLoader[] parents, boolean inverseClassLoading, String[] hiddenClasses, String[] nonOverridableClasses) {
super(EMPTY_URLS, parents, inverseClassLoading, hiddenClasses, nonOverridableClasses);
this.acc = AccessController.getContext();
addURLs(urls);
}
public JarFileClassLoader(JarFileClassLoader source) {
super(source);
this.acc = AccessController.getContext();
addURLs(source.getURLs());
}
public static ClassLoader copy(ClassLoader source) {
if (source instanceof JarFileClassLoader) {
return new JarFileClassLoader((JarFileClassLoader) source);
} else if (source instanceof MultiParentClassLoader) {
return new MultiParentClassLoader((MultiParentClassLoader) source);
} else if (source instanceof URLClassLoader) {
return new URLClassLoader(((URLClassLoader)source).getURLs(), source.getParent());
} else {
return new URLClassLoader(new URL[0], source);
}
}
ClassLoader copy() {
return JarFileClassLoader.copy(this);
}
/**
* {@inheritDoc}
*/
public URL[] getURLs() {
return resourceFinder.getUrls();
}
/**
* {@inheritDoc}
*/
public void addURL(final URL url) {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
resourceFinder.addUrl(url);
return null;
}
}, acc);
}
/**
* Adds an array of urls to the end of this class loader.
* @param urls the URLs to add
*/
protected void addURLs(final URL[] urls) {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
resourceFinder.addUrls(urls);
return null;
}
}, acc);
}
/**
* {@inheritDoc}
*/
public void destroy() {
resourceFinder.destroy();
super.destroy();
}
/**
* {@inheritDoc}
*/
public URL findResource(final String resourceName) {
return (URL) AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return resourceFinder.findResource(resourceName);
}
}, acc);
}
/**
* {@inheritDoc}
*/
protected Enumeration<URL> internalfindResources(final String name) throws IOException {
return AccessController.doPrivileged(new PrivilegedAction<Enumeration<URL>>() {
public Enumeration<URL> run() {
return resourceFinder.findResources(name);
}
}, acc);
}
/**
* {@inheritDoc}
*/
protected String findLibrary(String libraryName) {
// if the libraryName is actually a directory it is invalid
int pathEnd = libraryName.lastIndexOf('/');
if (pathEnd == libraryName.length() - 1) {
throw new IllegalArgumentException("libraryName ends with a '/' character: " + libraryName);
}
// get the name if the library file
final String resourceName;
if (pathEnd < 0) {
resourceName = System.mapLibraryName(libraryName);
} else {
String path = libraryName.substring(0, pathEnd + 1);
String file = libraryName.substring(pathEnd + 1);
resourceName = path + System.mapLibraryName(file);
}
// get a resource handle to the library
ResourceHandle resourceHandle = (ResourceHandle) AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return resourceFinder.getResource(resourceName);
}
}, acc);
if (resourceHandle == null) {
return null;
}
// the library must be accessable on the file system
URL url = resourceHandle.getUrl();
if (!"file".equals(url.getProtocol())) {
return null;
}
return new File(URI.create(url.toString())).getPath();
}
/**
* {@inheritDoc}
*/
protected Class findClass(final String className) throws ClassNotFoundException {
try {
return (Class) AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws ClassNotFoundException {
// first think check if we are allowed to define the package
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
String packageName;
int packageEnd = className.lastIndexOf('.');
if (packageEnd >= 0) {
packageName = className.substring(0, packageEnd);
securityManager.checkPackageDefinition(packageName);
}
}
// convert the class name to a file name
String resourceName = className.replace('.', '/') + ".class";
// find the class file resource
ResourceHandle resourceHandle = resourceFinder.getResource(resourceName);
if (resourceHandle == null) {
throw new ClassNotFoundException(className);
}
byte[] bytes;
Manifest manifest;
try {
// get the bytes from the class file
bytes = resourceHandle.getBytes();
// get the manifest for defining the packages
manifest = resourceHandle.getManifest();
} catch (IOException e) {
throw new ClassNotFoundException(className, e);
}
// get the certificates for the code source
Certificate[] certificates = resourceHandle.getCertificates();
// the code source url is used to define the package and as the security context for the class
URL codeSourceUrl = resourceHandle.getCodeSourceUrl();
// define the package (required for security)
definePackage(className, codeSourceUrl, manifest);
// this is the security context of the class
CodeSource codeSource = new CodeSource(codeSourceUrl, certificates);
// load the class into the vm
return defineClass(className, bytes, 0, bytes.length, codeSource);
}
}, acc);
} catch (PrivilegedActionException e) {
throw (ClassNotFoundException) e.getException();
}
}
private void definePackage(String className, URL jarUrl, Manifest manifest) {
int packageEnd = className.lastIndexOf('.');
if (packageEnd < 0) {
return;
}
String packageName = className.substring(0, packageEnd);
String packagePath = packageName.replace('.', '/') + "/";
Attributes packageAttributes = null;
Attributes mainAttributes = null;
if (manifest != null) {
packageAttributes = manifest.getAttributes(packagePath);
mainAttributes = manifest.getMainAttributes();
}
Package pkg = getPackage(packageName);
if (pkg != null) {
if (pkg.isSealed()) {
if (!pkg.isSealed(jarUrl)) {
throw new SecurityException("Package was already sealed with another URL: package=" + packageName + ", url=" + jarUrl);
}
} else {
if (isSealed(packageAttributes, mainAttributes)) {
throw new SecurityException("Package was already been loaded and not sealed: package=" + packageName + ", url=" + jarUrl);
}
}
} else {
String specTitle = getAttribute(Attributes.Name.SPECIFICATION_TITLE, packageAttributes, mainAttributes);
String specVendor = getAttribute(Attributes.Name.SPECIFICATION_VENDOR, packageAttributes, mainAttributes);
String specVersion = getAttribute(Attributes.Name.SPECIFICATION_VERSION, packageAttributes, mainAttributes);
String implTitle = getAttribute(Attributes.Name.IMPLEMENTATION_TITLE, packageAttributes, mainAttributes);
String implVendor = getAttribute(Attributes.Name.IMPLEMENTATION_VENDOR, packageAttributes, mainAttributes);
String implVersion = getAttribute(Attributes.Name.IMPLEMENTATION_VERSION, packageAttributes, mainAttributes);
URL sealBase = null;
if (isSealed(packageAttributes, mainAttributes)) {
sealBase = jarUrl;
}
definePackage(packageName, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase);
}
}
private String getAttribute(Attributes.Name name, Attributes packageAttributes, Attributes mainAttributes) {
if (packageAttributes != null) {
String value = packageAttributes.getValue(name);
if (value != null) {
return value;
}
}
if (mainAttributes != null) {
return mainAttributes.getValue(name);
}
return null;
}
private boolean isSealed(Attributes packageAttributes, Attributes mainAttributes) {
String sealed = getAttribute(Attributes.Name.SEALED, packageAttributes, mainAttributes);
return sealed != null && "true".equalsIgnoreCase(sealed);
}
}