Used to load classes within ant with a different classpath from
that used to start ant. Note that it is possible to force a class
into this loader even when that class is on the system classpath by
using the forceLoadClass method. Any subsequent classes loaded by that
class will then use this loader rather than the system class loader.
Note that this classloader has a feature to allow loading
in reverse order and for "isolation".
Due to the fact that a number of
methods in java.lang.ClassLoader are final (at least
in java 1.4 getResources) this means that the
class has to fake the given parent.
| Method from org.apache.tools.ant.AntClassLoader Detail: |
public void addJavaLibraries() {
Vector packages = JavaEnvUtils.getJrePackages();
Enumeration e = packages.elements();
while (e.hasMoreElements()) {
String packageName = (String) e.nextElement();
addSystemPackageRoot(packageName);
}
}
add any libraries that come with different java versions
here |
public void addLoaderPackageRoot(String packageRoot) {
loaderPackages.addElement(packageRoot + (packageRoot.endsWith(".") ? "" : "."));
}
Adds a package root to the list of packages which must be loaded using
this loader.
All subpackages are also included. |
public void addPathComponent(File file) {
if (pathComponents.contains(file)) {
return;
}
pathComponents.addElement(file);
}
Add a path component.
This simply adds the file, unlike addPathElement
it does not open jar files and load files from
their CLASSPATH entry in the manifest file. |
public void addPathElement(String pathElement) throws BuildException {
File pathComponent = project != null ? project.resolveFile(pathElement) : new File(
pathElement);
try {
addPathFile(pathComponent);
} catch (IOException e) {
throw new BuildException(e);
}
}
Adds an element to the classpath to be searched. |
protected void addPathFile(File pathComponent) throws IOException {
if (!pathComponents.contains(pathComponent)) {
pathComponents.addElement(pathComponent);
}
if (pathComponent.isDirectory()) {
return;
}
String absPathPlusTimeAndLength = pathComponent.getAbsolutePath()
+ pathComponent.lastModified() + "-" + pathComponent.length();
String classpath = (String) pathMap.get(absPathPlusTimeAndLength);
if (classpath == null) {
JarFile jarFile = null;
try {
jarFile = new JarFile(pathComponent);
Manifest manifest = jarFile.getManifest();
if (manifest == null) {
return;
}
classpath = manifest.getMainAttributes()
.getValue(Attributes.Name.CLASS_PATH);
} finally {
if (jarFile != null) {
jarFile.close();
}
}
if (classpath == null) {
classpath = "";
}
pathMap.put(absPathPlusTimeAndLength, classpath);
}
if (!"".equals(classpath)) {
URL baseURL = FILE_UTILS.getFileURL(pathComponent);
StringTokenizer st = new StringTokenizer(classpath);
while (st.hasMoreTokens()) {
String classpathElement = st.nextToken();
URL libraryURL = new URL(baseURL, classpathElement);
if (!libraryURL.getProtocol().equals("file")) {
log("Skipping jar library " + classpathElement
+ " since only relative URLs are supported by this" + " loader",
Project.MSG_VERBOSE);
continue;
}
String decodedPath = Locator.decodeUri(libraryURL.getFile());
File libraryFile = new File(decodedPath);
if (libraryFile.exists() && !isInPath(libraryFile)) {
addPathFile(libraryFile);
}
}
}
}
Add a file to the path.
Reads the manifest, if available, and adds any additional class path jars
specified in the manifest. |
public void addSystemPackageRoot(String packageRoot) {
systemPackages.addElement(packageRoot + (packageRoot.endsWith(".") ? "" : "."));
}
Adds a package root to the list of packages which must be loaded on the
parent loader.
All subpackages are also included. |
public void buildFinished(BuildEvent event) {
cleanup();
}
Cleans up any resources held by this classloader at the end
of a build. |
public void buildStarted(BuildEvent event) {
// Not significant for the class loader.
}
Empty implementation to satisfy the BuildListener interface. |
public synchronized void cleanup() {
for (Enumeration e = jarFiles.elements(); e.hasMoreElements();) {
JarFile jarFile = (JarFile) e.nextElement();
try {
jarFile.close();
} catch (IOException ioe) {
// ignore
}
}
jarFiles = new Hashtable();
if (project != null) {
project.removeBuildListener(this);
}
project = null;
}
Cleans up any resources held by this classloader. Any open archive
files are closed. |
protected Class defineClassFromData(File container,
byte[] classData,
String classname) throws IOException {
definePackage(container, classname);
ProtectionDomain currentPd = Project.class.getProtectionDomain();
String classResource = getClassFilename(classname);
CodeSource src = new CodeSource(FILE_UTILS.getFileURL(container),
getCertificates(container,
classResource));
ProtectionDomain classesPd =
new ProtectionDomain(src, currentPd.getPermissions(),
this,
currentPd.getPrincipals());
return defineClass(classname, classData, 0, classData.length,
classesPd);
}
Define a class given its bytes |
protected void definePackage(File container,
String className) throws IOException {
int classIndex = className.lastIndexOf('.');
if (classIndex == -1) {
return;
}
String packageName = className.substring(0, classIndex);
if (getPackage(packageName) != null) {
// already defined
return;
}
// define the package now
Manifest manifest = getJarManifest(container);
if (manifest == null) {
definePackage(packageName, null, null, null, null, null, null, null);
} else {
definePackage(container, packageName, manifest);
}
}
Define the package information associated with a class. |
protected void definePackage(File container,
String packageName,
Manifest manifest) {
String sectionName = packageName.replace('.', '/') + "/";
String specificationTitle = null;
String specificationVendor = null;
String specificationVersion = null;
String implementationTitle = null;
String implementationVendor = null;
String implementationVersion = null;
String sealedString = null;
URL sealBase = null;
Attributes sectionAttributes = manifest.getAttributes(sectionName);
if (sectionAttributes != null) {
specificationTitle = sectionAttributes.getValue(Name.SPECIFICATION_TITLE);
specificationVendor = sectionAttributes.getValue(Name.SPECIFICATION_VENDOR);
specificationVersion = sectionAttributes.getValue(Name.SPECIFICATION_VERSION);
implementationTitle = sectionAttributes.getValue(Name.IMPLEMENTATION_TITLE);
implementationVendor = sectionAttributes.getValue(Name.IMPLEMENTATION_VENDOR);
implementationVersion = sectionAttributes.getValue(Name.IMPLEMENTATION_VERSION);
sealedString = sectionAttributes.getValue(Name.SEALED);
}
Attributes mainAttributes = manifest.getMainAttributes();
if (mainAttributes != null) {
if (specificationTitle == null) {
specificationTitle = mainAttributes.getValue(Name.SPECIFICATION_TITLE);
}
if (specificationVendor == null) {
specificationVendor = mainAttributes.getValue(Name.SPECIFICATION_VENDOR);
}
if (specificationVersion == null) {
specificationVersion = mainAttributes.getValue(Name.SPECIFICATION_VERSION);
}
if (implementationTitle == null) {
implementationTitle = mainAttributes.getValue(Name.IMPLEMENTATION_TITLE);
}
if (implementationVendor == null) {
implementationVendor = mainAttributes.getValue(Name.IMPLEMENTATION_VENDOR);
}
if (implementationVersion == null) {
implementationVersion = mainAttributes.getValue(Name.IMPLEMENTATION_VERSION);
}
if (sealedString == null) {
sealedString = mainAttributes.getValue(Name.SEALED);
}
}
if (sealedString != null && sealedString.equalsIgnoreCase("true")) {
try {
sealBase = new URL(FileUtils.getFileUtils().toURI(container.getAbsolutePath()));
} catch (MalformedURLException e) {
// ignore
}
}
definePackage(packageName, specificationTitle, specificationVersion, specificationVendor,
implementationTitle, implementationVersion, implementationVendor, sealBase);
}
Define the package information when the class comes from a
jar with a manifest |
public Class findClass(String name) throws ClassNotFoundException {
log("Finding class " + name, Project.MSG_DEBUG);
return findClassInComponents(name);
}
Searches for and load a class on the classpath of this class loader. |
protected Enumeration findResources(String name) throws IOException {
return findResources(name, true);
}
Returns an enumeration of URLs representing all the resources with the
given name by searching the class loader's classpath. |
protected Enumeration findResources(String name,
boolean parentHasBeenSearched) throws IOException {
Enumeration/*< URL >*/ mine = new ResourceEnumeration(name);
Enumeration/*< URL >*/ base;
if (parent != null && (!parentHasBeenSearched || parent != getParent())) {
// Delegate to the parent:
base = parent.getResources(name);
// Note: could cause overlaps in case
// ClassLoader.this.parent has matches and
// parentHasBeenSearched is true
} else {
// ClassLoader.this.parent is already delegated to for example from
// ClassLoader.getResources, no need:
base = new CollectionUtils.EmptyEnumeration();
}
if (isParentFirst(name)) {
// Normal case.
return CollectionUtils.append(base, mine);
}
if (ignoreBase) {
return getRootLoader() == null ? mine : CollectionUtils.append(mine, getRootLoader()
.getResources(name));
}
// parent last:
return CollectionUtils.append(mine, base);
}
Returns an enumeration of URLs representing all the resources with the
given name by searching the class loader's classpath. |
public Class forceLoadClass(String classname) throws ClassNotFoundException {
log("force loading " + classname, Project.MSG_DEBUG);
Class theClass = findLoadedClass(classname);
if (theClass == null) {
theClass = findClass(classname);
}
return theClass;
}
Loads a class through this class loader even if that class is available
on the parent classpath.
This ensures that any classes which are loaded by the returned class
will use this classloader. |
public Class forceLoadSystemClass(String classname) throws ClassNotFoundException {
log("force system loading " + classname, Project.MSG_DEBUG);
Class theClass = findLoadedClass(classname);
if (theClass == null) {
theClass = findBaseClass(classname);
}
return theClass;
}
Loads a class through this class loader but defer to the parent class
loader.
This ensures that instances of the returned class will be compatible
with instances which have already been loaded on the parent
loader. |
public String getClasspath() {
StringBuffer sb = new StringBuffer();
boolean firstPass = true;
Enumeration componentEnum = pathComponents.elements();
while (componentEnum.hasMoreElements()) {
if (!firstPass) {
sb.append(System.getProperty("path.separator"));
} else {
firstPass = false;
}
sb.append(((File) componentEnum.nextElement()).getAbsolutePath());
}
return sb.toString();
}
Returns the classpath this classloader will consult. |
public ClassLoader getConfiguredParent() {
return parent;
}
Gets the parent as has been specified in the constructor or via
setParent. |
public Enumeration getNamedResources(String name) throws IOException {
return findResources(name, false);
}
|
public URL getResource(String name) {
// we need to search the components of the path to see if
// we can find the class we want.
URL url = null;
if (isParentFirst(name)) {
url = parent == null ? super.getResource(name) : parent.getResource(name);
}
if (url != null) {
log("Resource " + name + " loaded from parent loader", Project.MSG_DEBUG);
} else {
// try and load from this loader if the parent either didn't find
// it or wasn't consulted.
Enumeration e = pathComponents.elements();
while (e.hasMoreElements() && url == null) {
File pathComponent = (File) e.nextElement();
url = getResourceURL(pathComponent, name);
if (url != null) {
log("Resource " + name + " loaded from ant loader", Project.MSG_DEBUG);
}
}
}
if (url == null && !isParentFirst(name)) {
// this loader was first but it didn't find it - try the parent
if (ignoreBase) {
url = getRootLoader() == null ? null : getRootLoader().getResource(name);
} else {
url = parent == null ? super.getResource(name) : parent.getResource(name);
}
if (url != null) {
log("Resource " + name + " loaded from parent loader", Project.MSG_DEBUG);
}
}
if (url == null) {
log("Couldn't load Resource " + name, Project.MSG_DEBUG);
}
return url;
}
Finds 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. |
public InputStream getResourceAsStream(String name) {
InputStream resourceStream = null;
if (isParentFirst(name)) {
resourceStream = loadBaseResource(name);
}
if (resourceStream != null) {
log("ResourceStream for " + name
+ " loaded from parent loader", Project.MSG_DEBUG);
} else {
resourceStream = loadResource(name);
if (resourceStream != null) {
log("ResourceStream for " + name
+ " loaded from ant loader", Project.MSG_DEBUG);
}
}
if (resourceStream == null && !isParentFirst(name)) {
if (ignoreBase) {
resourceStream = getRootLoader() == null ? null : getRootLoader().getResourceAsStream(name);
} else {
resourceStream = loadBaseResource(name);
}
if (resourceStream != null) {
log("ResourceStream for " + name + " loaded from parent loader",
Project.MSG_DEBUG);
}
}
if (resourceStream == null) {
log("Couldn't load ResourceStream for " + name, Project.MSG_DEBUG);
}
return resourceStream;
}
Returns a stream to read the requested resource name. |
protected URL getResourceURL(File file,
String resourceName) {
try {
JarFile jarFile = (JarFile) jarFiles.get(file);
if (jarFile == null && file.isDirectory()) {
File resource = new File(file, resourceName);
if (resource.exists()) {
try {
return FILE_UTILS.getFileURL(resource);
} catch (MalformedURLException ex) {
return null;
}
}
} else {
if (jarFile == null) {
if (file.exists()) {
jarFile = new JarFile(file);
jarFiles.put(file, jarFile);
} else {
return null;
}
// potential race-condition
jarFile = (JarFile) jarFiles.get(file);
}
JarEntry entry = jarFile.getJarEntry(resourceName);
if (entry != null) {
try {
return new URL("jar:" + FILE_UTILS.getFileURL(file) + "!/" + entry);
} catch (MalformedURLException ex) {
return null;
}
}
}
} catch (Exception e) {
String msg = "Unable to obtain resource from " + file + ": ";
log(msg + e, Project.MSG_WARN);
System.err.println(msg);
e.printStackTrace();
}
return null;
}
Returns the URL of a given resource in the given file which may
either be a directory or a zip file. |
public static void initializeClass(Class theClass) {
// ***HACK*** We ask the VM to create an instance
// by voluntarily providing illegal arguments to force
// the VM to run the class' static initializer, while
// at the same time not running a valid constructor.
final Constructor[] cons = theClass.getDeclaredConstructors();
//At least one constructor is guaranteed to be there, but check anyway.
if (cons != null) {
if (cons.length > 0 && cons[0] != null) {
final String[] strs = new String[NUMBER_OF_STRINGS];
try {
cons[0].newInstance((Object[]) strs);
// Expecting an exception to be thrown by this call:
// IllegalArgumentException: wrong number of Arguments
} catch (Exception e) {
// Ignore - we are interested only in the side
// effect - that of getting the static initializers
// invoked. As we do not want to call a valid
// constructor to get this side effect, an
// attempt is made to call a hopefully
// invalid constructor - come on, nobody
// would have a constructor that takes in
// 256 String arguments ;-)
// (In fact, they can't - according to JVM spec
// section 4.10, the number of method parameters is limited
// to 255 by the definition of a method descriptor.
// Constructors count as methods here.)
}
}
}
} Deprecated! since - 1.6.x.
Use Class.forName with initialize=true instead.
Forces initialization of a class in a JDK 1.1 compatible, albeit hacky
way. |
protected boolean isInPath(File component) {
return pathComponents.contains(component);
}
Indicate if the given file is in this loader's path |
protected synchronized Class loadClass(String classname,
boolean resolve) throws ClassNotFoundException {
// 'sync' is needed - otherwise 2 threads can load the same class
// twice, resulting in LinkageError: duplicated class definition.
// findLoadedClass avoids that, but without sync it won't work.
Class theClass = findLoadedClass(classname);
if (theClass != null) {
return theClass;
}
if (isParentFirst(classname)) {
try {
theClass = findBaseClass(classname);
log("Class " + classname + " loaded from parent loader " + "(parentFirst)",
Project.MSG_DEBUG);
} catch (ClassNotFoundException cnfe) {
theClass = findClass(classname);
log("Class " + classname + " loaded from ant loader " + "(parentFirst)",
Project.MSG_DEBUG);
}
} else {
try {
theClass = findClass(classname);
log("Class " + classname + " loaded from ant loader", Project.MSG_DEBUG);
} catch (ClassNotFoundException cnfe) {
if (ignoreBase) {
throw cnfe;
}
theClass = findBaseClass(classname);
log("Class " + classname + " loaded from parent loader", Project.MSG_DEBUG);
}
}
if (resolve) {
resolveClass(theClass);
}
return theClass;
}
Loads a class with this class loader.
This class attempts to load the class in an order determined by whether
or not the class matches the system/loader package lists, with the
loader package list taking priority. If the classloader is in isolated
mode, failure to load the class in this loader will result in a
ClassNotFoundException. |
protected void log(String message,
int priority) {
if (project != null) {
project.log(message, priority);
}
}
Logs a message through the project object if one has been provided. |
public void messageLogged(BuildEvent event) {
// Not significant for the class loader.
}
Empty implementation to satisfy the BuildListener interface. |
public static AntClassLoader newAntClassLoader(ClassLoader parent,
Project project,
Path path,
boolean parentFirst) {
if (subClassToLoad != null) {
return (AntClassLoader)
ReflectUtil.newInstance(subClassToLoad,
CONSTRUCTOR_ARGS,
new Object[] {
parent, project, path,
Boolean.valueOf(parentFirst)
});
}
return new AntClassLoader(parent, project, path, parentFirst);
}
|
public void resetThreadContextLoader() {
if (LoaderUtils.isContextLoaderAvailable() && isContextLoaderSaved) {
LoaderUtils.setContextClassLoader(savedContextLoader);
savedContextLoader = null;
isContextLoaderSaved = false;
}
}
Resets the current thread's context loader to its original value. |
public void setClassPath(Path classpath) {
pathComponents.removeAllElements();
if (classpath != null) {
Path actualClasspath = classpath.concatSystemClasspath("ignore");
String[] pathElements = actualClasspath.list();
for (int i = 0; i < pathElements.length; ++i) {
try {
addPathElement(pathElements[i]);
} catch (BuildException e) {
// ignore path elements which are invalid
// relative to the project
}
}
}
}
Set the classpath to search for classes to load. This should not be
changed once the classloader starts to server classes |
public synchronized void setIsolated(boolean isolated) {
ignoreBase = isolated;
}
Sets whether this classloader should run in isolated mode. In
isolated mode, classes not found on the given classpath will
not be referred to the parent class loader but will cause a
ClassNotFoundException. |
public void setParent(ClassLoader parent) {
this.parent = parent == null ? AntClassLoader.class.getClassLoader() : parent;
}
Set the parent for this class loader. This is the class loader to which
this class loader will delegate to load classes |
public void setParentFirst(boolean parentFirst) {
this.parentFirst = parentFirst;
}
Control whether class lookup is delegated to the parent loader first
or after this loader. Use with extreme caution. Setting this to
false violates the class loader hierarchy and can lead to Linkage errors |
public void setProject(Project project) {
this.project = project;
if (project != null) {
project.addBuildListener(this);
}
}
Set the project associated with this class loader |
public void setThreadContextLoader() {
if (isContextLoaderSaved) {
throw new BuildException("Context loader has not been reset");
}
if (LoaderUtils.isContextLoaderAvailable()) {
savedContextLoader = LoaderUtils.getContextClassLoader();
ClassLoader loader = this;
if (project != null && "only".equals(project.getProperty("build.sysclasspath"))) {
loader = this.getClass().getClassLoader();
}
LoaderUtils.setContextClassLoader(loader);
isContextLoaderSaved = true;
}
}
Sets the current thread's context loader to this classloader, storing
the current loader value for later resetting. |
public void subBuildFinished(BuildEvent event) {
if (event.getProject() == project) {
cleanup();
}
}
Cleans up any resources held by this classloader at the end of
a subbuild if it has been created for the subbuild's project
instance. |
public void subBuildStarted(BuildEvent event) {
// Not significant for the class loader.
}
Empty implementation to satisfy the BuildListener interface. |
public void targetFinished(BuildEvent event) {
// Not significant for the class loader.
}
Empty implementation to satisfy the BuildListener interface. |
public void targetStarted(BuildEvent event) {
// Not significant for the class loader.
}
Empty implementation to satisfy the BuildListener interface. |
public void taskFinished(BuildEvent event) {
// Not significant for the class loader.
}
Empty implementation to satisfy the BuildListener interface. |
public void taskStarted(BuildEvent event) {
// Not significant for the class loader.
}
Empty implementation to satisfy the BuildListener interface. |
public String toString() {
return "AntClassLoader[" + getClasspath() + "]";
}
Returns a String representing this loader. |