Browse Source

Upgraded Classloader to allow more control over which classes are loaded

by the system classloader
Upgrade Ejbc task and its helper to use the new facilities of the classloader
Runs the weblogic.ejbc task in the ant VM. Examines the build.compiler setting and
uses Jikes if it is selected.


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@267929 13f79535-47bb-0310-9956-ffa450edef68
master
Conor MacNeill 25 years ago
parent
commit
655df7f9e1
3 changed files with 336 additions and 136 deletions
  1. +138
    -36
      src/main/org/apache/tools/ant/AntClassLoader.java
  2. +51
    -36
      src/main/org/apache/tools/ant/taskdefs/optional/ejb/Ejbc.java
  3. +147
    -64
      src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbcHelper.java

+ 138
- 36
src/main/org/apache/tools/ant/AntClassLoader.java View File

@@ -57,6 +57,7 @@ package org.apache.tools.ant;
import java.util.*;
import java.util.zip.*;
import java.io.*;
import org.apache.tools.ant.types.Path;

/**
* Used to load classes within ant with a different claspath from that used to start ant.
@@ -75,7 +76,7 @@ public class AntClassLoader extends ClassLoader {
/**
* The classpath that is to be used when loading classes using this class loader.
*/
private org.apache.tools.ant.types.Path classpath;
private Path classpath;
/**
* The project to which this class loader belongs.
@@ -83,9 +84,22 @@ public class AntClassLoader extends ClassLoader {
private Project project;

/**
* The File components of the path. Typically these will be directories or jar files.
* Indicates whether the system class loader should be
* consulted before trying to load with this class loader.
*/
private Vector components = null;
private boolean systemFirst = true;

/**
* These are the package roots that are to be loaded by the system class loader
* regardless of whether the system class loader is being searched first or not.
*/
private Vector systemPackages = new Vector();
/**
* These are the package roots that are to be loaded by this class loader
* regardless of whether the system class loader is being searched first or not.
*/
private Vector loaderPackages = new Vector();
/**
* Create a classloader for the given project using the classpath given.
@@ -93,28 +107,47 @@ public class AntClassLoader extends ClassLoader {
* @param project the project to ehich this classloader is to belong.
* @param classpath the classpath to use to load the classes.
*/
public AntClassLoader(Project project,
org.apache.tools.ant.types.Path classpath) {
public AntClassLoader(Project project, Path classpath) {
this.project = project;
this.classpath = classpath;
}

/**
* Set up this classloader for the first time.
* Create a classloader for the given project using the classpath given.
*
* This method will set up the components field with the components from the
* given classpath.
* @param project the project to ehich this classloader is to belong.
* @param classpath the classpath to use to load the classes.
*/
private void setup() {
// We iterate through the class path, resolving each element.
components = new Vector();
String[] pathElements = classpath.list();
for (int i = 0; i < pathElements.length; ++i) {
File element = project.resolveFile(pathElements[i]);
components.addElement(element);
}
public AntClassLoader(Project project, Path classpath, boolean systemFirst) {
this(project, classpath);
this.systemFirst = systemFirst;
}
/**
* Add a package root to the list of packages which must be loaded on the
* system loader.
*
* All subpackages are also included.
*
* @param packageRoot the root of akll packages to be included.
*/
public void addSystemPackageRoot(String packageRoot) {
systemPackages.addElement(packageRoot + ".");
}
/**
* Add a package root to the list of packages which must be loaded using
* this loader.
*
* All subpackages are also included.
*
* @param packageRoot the root of akll packages to be included.
*/
public void addLoaderPackageRoot(String packageRoot) {
loaderPackages.addElement(packageRoot + ".");
}


/**
* Load a class through this class loader even if that class is available on the
@@ -131,6 +164,7 @@ public class AntClassLoader extends ClassLoader {
* this loader's classpath.
*/
public Class forceLoadClass(String classname) throws ClassNotFoundException {
project.log("force loading " + classname, Project.MSG_VERBOSE);
Class theClass = findLoadedClass(classname);

if (theClass == null) {
@@ -140,6 +174,30 @@ public class AntClassLoader extends ClassLoader {
return theClass;
}

/**
* Load a class through this class loader but defer to the system class loader
*
* This ensures that instances of the returned class will be compatible with instances which
* which have already been loaded on the system loader.
*
* @param classname the classname to be loaded.
*
* @return the required Class object
*
* @throws ClassNotFoundException if the requested class does not exist on
* this loader's classpath.
*/
public Class forceLoadSystemClass(String classname) throws ClassNotFoundException {
project.log("force system loading " + classname, Project.MSG_VERBOSE);
Class theClass = findLoadedClass(classname);

if (theClass == null) {
theClass = findSystemClass(classname);
}
return theClass;
}

/**
* Get a stream to read the requested resource name.
*
@@ -149,17 +207,13 @@ public class AntClassLoader extends ClassLoader {
* found on the loader's classpath.
*/
public InputStream getResourceAsStream(String name) {
if (components == null) {
// we haven't set up the list of directories and jars yet.
setup();
}

// we need to search the components of the path to see if we can find the
// class we want.
InputStream stream = null;

for (Enumeration e = components.elements(); e.hasMoreElements() && stream == null; ) {
File pathComponent = (File)e.nextElement();
String[] pathElements = classpath.list();
for (int i = 0; i < pathElements.length && stream == null; ++i) {
File pathComponent = project.resolveFile((String)pathElements[i]);
stream = getResourceStream(pathComponent, name);
}

@@ -168,7 +222,7 @@ public class AntClassLoader extends ClassLoader {

/**
* Get an inputstream to a given resource in the given file which may
* either be a directory or a jar type file.
* either be a directory or a zip file.
*
* @param file the file (directory or jar) in which to search for the resource.
* @param resourceName the name of the resource for which a stream is required.
@@ -184,6 +238,7 @@ public class AntClassLoader extends ClassLoader {
if (file.isDirectory()) {
File resource = new File(file, resourceName);
if (resource.exists()) {
return new FileInputStream(resource);
}
@@ -238,14 +293,49 @@ public class AntClassLoader extends ClassLoader {
* the system classpath or this loader's classpath.
*/
protected Class loadClass(String classname, boolean resolve) throws ClassNotFoundException {

// default to the global setting and then see
// if this class belongs to a package which has been
// designated to use a specific loader first (this one or the system one)
boolean useSystemFirst = systemFirst;

for (Enumeration e = systemPackages.elements(); e.hasMoreElements();) {
String packageName = (String)e.nextElement();
if (classname.startsWith(packageName)) {
useSystemFirst = true;
break;
}
}

for (Enumeration e = loaderPackages.elements(); e.hasMoreElements();) {
String packageName = (String)e.nextElement();
if (classname.startsWith(packageName)) {
useSystemFirst = false;
break;
}
}

Class theClass = findLoadedClass(classname);
if (theClass == null) {
try {
theClass = findSystemClass(classname);
if (useSystemFirst) {
try {
theClass = findSystemClass(classname);
project.log("Class " + classname + " loaded from system loader", Project.MSG_VERBOSE);
}
catch (ClassNotFoundException cnfe) {
theClass = findClass(classname);
project.log("Class " + classname + " loaded from ant loader", Project.MSG_VERBOSE);
}
}
catch (ClassNotFoundException cnfe) {
theClass = findClass(classname);
else {
try {
theClass = findClass(classname);
project.log("Class " + classname + " loaded from ant loader", Project.MSG_VERBOSE);
}
catch (ClassNotFoundException cnfe) {
theClass = findSystemClass(classname);
project.log("Class " + classname + " loaded from system loader", Project.MSG_VERBOSE);
}
}
}
@@ -294,6 +384,7 @@ public class AntClassLoader extends ClassLoader {
return defineClass(classname, classData, 0, classData.length);
}


/**
* Search for and load a class on the classpath of this class loader.
*
@@ -305,18 +396,29 @@ public class AntClassLoader extends ClassLoader {
* this loader's classpath.
*/
public Class findClass(String name) throws ClassNotFoundException {
if (components == null) {
// we haven't set up the list of directories and jars yet.
setup();
project.log("Finding class " + name, Project.MSG_VERBOSE);

try {
return findClass(name, classpath);
}
catch (ClassNotFoundException e) {
throw e;
}
}


/**
* Find a class on the given classpath.
*/
private Class findClass(String name, Path path) throws ClassNotFoundException {
// we need to search the components of the path to see if we can find the
// class we want.
InputStream stream = null;
String classFilename = getClassFilename(name);
try {
for (Enumeration e = components.elements(); e.hasMoreElements() && stream == null; ) {
File pathComponent = (File)e.nextElement();
String[] pathElements = path.list();
for (int i = 0; i < pathElements.length && stream == null; ++i) {
File pathComponent = project.resolveFile((String)pathElements[i]);
stream = getResourceStream(pathComponent, classFilename);
}


+ 51
- 36
src/main/org/apache/tools/ant/taskdefs/optional/ejb/Ejbc.java View File

@@ -54,9 +54,7 @@
package org.apache.tools.ant.taskdefs.optional.ejb;


import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.*;
import org.apache.tools.ant.taskdefs.*;
import org.apache.tools.ant.types.Path;

@@ -70,6 +68,19 @@ import java.io.File;
* @author <a href="mailto:conor@cortexebusiness.com.au">Conor MacNeill</a>, Cortex ebusiness Pty Limited
*/
public class Ejbc extends MatchingTask {
static public interface Helper {
public void initialize(Ejbc ejbcTask);
public void setDescriptorDir(File dir);
public void setDest(File dir);
public void setManifest(File manifestFile);
public void setClasspath(Path classpath);
public void setSrc(File dir);
public void setDescriptors(String[] descriptors);
public void execute();
}
/**
* The root directory of the tree containing the serialised deployment desciptors. The actual
* deployment descriptor files are selected using include and exclude constructs
@@ -91,7 +102,7 @@ public class Ejbc extends MatchingTask {
* The classpath to be used in the weblogic ejbc calls. It must contain the weblogic
* classes <b>and</b> the implementation classes of the home and remote interfaces.
*/
private String classpath;
private Path classpath;
/**
* The source directory for the home and remote interfaces. This is used to determine if
@@ -129,49 +140,53 @@ public class Ejbc extends MatchingTask {
}
String systemClassPath = System.getProperty("java.class.path");
String execClassPath = project.translatePath(systemClassPath + ":" + classpath +
":" + generatedFilesDirectory);
Path execClassPath = new Path(project, classpath + ":" + generatedFilesDirectory + ":" + systemClassPath);
// get all the files in the descriptor directory
DirectoryScanner ds = super.getDirectoryScanner(descriptorDirectory);
String[] files = ds.getIncludedFiles();
String[] descriptorNames = ds.getIncludedFiles();

Java helperTask = (Java)project.createTask("java");
helperTask.setFork(true);
helperTask.setClassname("org.apache.tools.ant.taskdefs.optional.ejb.EjbcHelper");
String args = "";
args += " " + descriptorDirectory;
args += " " + generatedFilesDirectory;
args += " " + sourceDirectory;
args += " " + generatedManifestFile;
for (int i = 0; i < files.length; ++i) {
args += " " + files[i];
// create an class loader
AntClassLoader loader = new AntClassLoader(project, execClassPath, false);
loader.addSystemPackageRoot("org.apache.tools.ant");
loader.addSystemPackageRoot("javax");
try {
Helper helper = (Helper)(loader.forceLoadClass("org.apache.tools.ant.taskdefs.optional.ejb.EjbcHelper").newInstance());
helper.initialize(this);
helper.setDescriptorDir(descriptorDirectory);
helper.setDest(generatedFilesDirectory);
helper.setManifest(generatedManifestFile);
helper.setSrc(sourceDirectory);
helper.setDescriptors(descriptorNames);
helper.setClasspath(execClassPath);
helper.execute();
helper = null;
}
helperTask.setArgs(args);
helperTask.setClasspath(new Path(project, execClassPath));
if (helperTask.executeJava() != 0) {
throw new BuildException("Execution of ejbc helper failed");
catch (Exception e) {
throw new BuildException(e);
}
loader = null;
}

/**
* Set the directory from where the serialised deployment descriptors are
* to be read.
*
* @param dirName the name of the directory containing the serialised deployment descriptors.
* @param dir the directory containing the serialised deployment descriptors.
*/
public void setDescriptors(String dirName) {
descriptorDirectory = new File(dirName);
public void setDescriptors(File dir) {
descriptorDirectory = dir;
}
/**
* Set the directory into which the support classes, RMI stubs, etc are to be written
*
* @param dirName the name of the directory into which code is generated
* @param dir the directory into which code is generated
*/
public void setDest(String dirName) {
generatedFilesDirectory = new File(dirName);
public void setDest(File dir) {
generatedFilesDirectory = dir;
}

/**
@@ -180,27 +195,27 @@ public class Ejbc extends MatchingTask {
* For each EJB that is processed an entry is created in this file. This can then be used
* to create a jar file for dploying the beans.
*
* @param manfestFilename the name of the manifest file to be generated.
* @param manfestFilename the manifest file to be generated.
*/
public void setManifest(String manifestFilename) {
generatedManifestFile = new File(manifestFilename);
public void setManifest(File manifestFile) {
generatedManifestFile = manifestFile;
}
/**
* Set the classpath to be used for this compilation.
*/
public void setClasspath(String s) {
this.classpath = project.translatePath(s);
public void setClasspath(Path classpath) {
this.classpath = classpath;
}

/**
* Set the directory containing the source code for the home interface, remote interface
* and public key class definitions.
*
* @param dirName the directory containg the source tree for the EJB's interface classes.
* @param dir the directory containg the source tree for the EJB's interface classes.
*/
public void setSrc(String dirName) {
sourceDirectory = new File(dirName);
public void setSrc(File dir) {
sourceDirectory = dir;
}
}

+ 147
- 64
src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbcHelper.java View File

@@ -53,16 +53,13 @@
*/
package org.apache.tools.ant.taskdefs.optional.ejb;

import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.FileWriter;
import java.io.*;

import javax.ejb.deployment.EntityDescriptor;
import javax.ejb.deployment.DeploymentDescriptor;

import org.apache.tools.ant.*;
import org.apache.tools.ant.types.Path;

/**
* A helper class which performs the actual work of the ejbc task.
@@ -72,7 +69,7 @@ import javax.ejb.deployment.DeploymentDescriptor;
*
* @author <a href="mailto:conor@cortexebusiness.com.au">Conor MacNeill</a>, Cortex ebusiness Pty Limited
*/
public class EjbcHelper {
public class EjbcHelper implements Ejbc.Helper {
/**
* The root directory of the tree containing the serialised deployment desciptors.
*/
@@ -92,43 +89,127 @@ public class EjbcHelper {
* The classpath to be used in the weblogic ejbc calls. It must contain the weblogic
* classes <b>and</b> the implementation classes of the home and remote interfaces.
*/
private String classpath;
private Path classpath;
/**
* The source directory for the home and remote interfaces. This is used to determine if
* the generated deployment classes are out of date.
*/
private File sourceDirectory;

/**
* The array of descriptor filenames to be processed
*/
private String[] files;

/**
* The Ejbc task that this helper is helping
*/
private Ejbc ejbcTask;
/**
* The names of the serialised deployment descriptors
*/
String[] descriptors;

public EjbcHelper() {
}
public void initialize(Ejbc ejbcTask) {
this.ejbcTask = ejbcTask;
}

/**
* Command line interface for the ejbc helper task.
*/
public static void main(String[] args) throws Exception {
EjbcHelper helper = new EjbcHelper(args);
helper.process();
* Set the directory from where the serialised deployment descriptors are
* to be read.
*
* @param dir the directory containing the serialised deployment descriptors.
*/
public void setDescriptorDir(File dir) {
descriptorDirectory = dir;
}
/**
* Set the directory into which the support classes, RMI stubs, etc are to be written
*
* @param dir the directory into which code is generated
*/
public void setDest(File dir) {
generatedFilesDirectory = dir;
}

/**
* Initialise the EjbcHelper by reading the command arguments.
*/
private EjbcHelper(String[] args) {
int index = 0;
descriptorDirectory = new File(args[index++]);
generatedFilesDirectory = new File(args[index++]);
sourceDirectory = new File(args[index++]);
manifestFile = new File(args[index++]);
descriptors = new String[args.length - index];
for (int i = 0; index < args.length; ++i) {
descriptors[i] = args[index++];
* Set the generated manifest file.
*
* For each EJB that is processed an entry is created in this file. This can then be used
* to create a jar file for dploying the beans.
*
* @param manfestFilename the manifest file to be generated.
*/
public void setManifest(File manifestFile) {
this.manifestFile = manifestFile;
}
/**
* Set the classpath to be used for this compilation.
*/
public void setClasspath(Path classpath) {
this.classpath = classpath;
}

/**
* Set the list of desciptors which ar eto be processed.
*
* @param descriptors an array of serialised deployment descriptor filenames to be processed.
*/
public void setDescriptors(String[] descriptors) {
this.descriptors = descriptors;
}



/**
* Set the directory containing the source code for the home interface, remote interface
* and public key class definitions.
*
* @param dir the directory containg the source tree for the EJB's interface classes.
*/
public void setSrc(File dir) {
sourceDirectory = dir;
}

/**
* Perform the weblogic compiles.
*/
public void execute() throws BuildException {
try {
String manifest = "Manifest-Version: 1.0\n\n";
for (int i = 0; i < descriptors.length; ++i) {
String descriptorName = descriptors[i];
File descriptorFile = new File(descriptorDirectory, descriptorName);
if (isRegenRequired(descriptorFile)) {
ejbcTask.log("Running ejbc for " + descriptorFile.getName(), Project.MSG_INFO);
regenerateSupportClasses(descriptorFile);
}
else {
ejbcTask.log(descriptorFile.getName() + " is up to date", Project.MSG_VERBOSE);
}
manifest += "Name: " + descriptorFile.getName() + "\nEnterprise-Bean: True\n\n";
}
FileWriter fw = new FileWriter(manifestFile);
PrintWriter pw = new PrintWriter(fw);
pw.print(manifest);
fw.flush();
fw.close();
}
catch (IOException e) {
throw new BuildException(e);
}
}


/**
* Determine if the weblogic EJB support classes need to be regenerated
* for a given deployment descriptor.
@@ -210,7 +291,8 @@ public class EjbcHelper {
}
}
catch (Throwable descriptorLoadException) {
System.out.println("Exception occurred reading " + descriptorFile.getName() + " - continuing");
ejbcTask.log("Exception occurred reading " + descriptorFile.getName() + " - continuing",
Project.MSG_WARN);
// any problems - just regenerate
return true;
}
@@ -223,56 +305,57 @@ public class EjbcHelper {
return false;
}

/**
* Process the descriptors in turn generating support classes for each and a manifest
* file for all of the beans.
*/
private void process() throws Exception {
String manifest = "Manifest-Version: 1.0\n\n";
for (int i = 0; i < descriptors.length; ++i) {
String descriptorName = descriptors[i];
File descriptorFile = new File(descriptorDirectory, descriptorName);
if (isRegenRequired(descriptorFile)) {
System.out.println("Running ejbc for " + descriptorFile.getName());
regenerateSupportClasses(descriptorFile);
}
else {
System.out.println(descriptorFile.getName() + " is up to date");
}
manifest += "Name: " + descriptorFile.getName() + "\nEnterprise-Bean: True\n\n";
}
FileWriter fw = new FileWriter(manifestFile);
PrintWriter pw = new PrintWriter(fw);
pw.print(manifest);
fw.flush();
fw.close();
}

/**
* Perform the weblogic.ejbc call to regenerate the support classes.
*
* Note that this method relies on an undocumented -noexit option to the
* ejbc tool to stop the ejbc tool exiting the VM altogether.
*/
private void regenerateSupportClasses(File descriptorFile) throws Exception {
// create a Java task to do the rebuild
private void regenerateSupportClasses(File descriptorFile) {
Project project = ejbcTask.getProject();
String javaHome = System.getProperty("java.home");

String[] args = {"-noexit",
"-keepgenerated",
"-d", generatedFilesDirectory.getPath(),
descriptorFile.getPath()};
String compiler = project.getProperty("build.compiler");
String[] args = null;
if (compiler.equalsIgnoreCase("jikes")) {
Path execClassPath = new Path(project);
if (Project.getJavaVersion() == Project.JAVA_1_1) {
execClassPath.addExisting(new Path(project, System.getProperty("java.home")
+ "/lib/classes.zip"));
} else {
execClassPath.addExisting(new Path(project,
System.getProperty("java.home")
+ "/lib/rt.jar"));
// Just keep the old version as well and let addExisting
// sort it out.
execClassPath.addExisting(new Path(project,
System.getProperty("java.home")
+ "/jre/lib/rt.jar"));
}
execClassPath.append(classpath);
args = new String[] {"-noexit",
"-keepgenerated",
"-compiler", "Jikes",
"-d", generatedFilesDirectory.getPath(),
"-classpath", execClassPath.toString(),
descriptorFile.getPath()};
}
else {
args = new String[]{"-noexit",
"-keepgenerated",
"-d", generatedFilesDirectory.getPath(),
"-classpath", classpath.toString(),
descriptorFile.getPath()};
}
try {
weblogic.ejbc.main(args);
}
catch (Exception e) {
// run with no exit for better reporting
String[] newArgs = {"-keepgenerated",
"-d", generatedFilesDirectory.getPath(),
descriptorFile.getPath()};
weblogic.ejbc.main(newArgs);
e.printStackTrace();
throw new BuildException(e);
}
}
}

Loading…
Cancel
Save