conor 00/11/11 07:11:42
Modified: src/main/org/apache/tools/ant/taskdefs/optional/ejb
EJBDeploymentTool.java EjbJar.java
GenericDeploymentTool.java
WeblogicDeploymentTool.java
Log:
EJBJar changes
1. Change EJBJar so that super classes and super interfaces are included in
bean jar file. For this to work, the classpath can be specified at the
ejbjar level. This will be used for the weblogic nested element if it
is specified. If a classpath is also provided on the weblogic
nested element, it is concatenated with the task level classpath. Since
the processing is done in the nested element in any case, current usage
where the classpath is specified in the nested element is sufficient
for these checks to work.
2. Allow <classpath> nested elements to be used to specify the classpath.
This can
be used for both the task level and nested element level classpaths.
3. Change the weblogic jar generation to run ejbc only if the interfaces have
changed. Changes to the bean classes will not cause ejbc to run. This
behaviour
must be enabled by setting the attribute rebuild="false" in the weblogic
element.
Submitted by: Brian Towles <[EMAIL PROTECTED]>
Revision Changes Path
1.4 +2 -1
jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EJBDeploymentTool.java
Index: EJBDeploymentTool.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EJBDeploymentTool.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- EJBDeploymentTool.java 2000/09/09 11:48:11 1.3
+++ EJBDeploymentTool.java 2000/11/11 15:11:41 1.4
@@ -60,6 +60,7 @@
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.*;
public interface EJBDeploymentTool {
/**
@@ -87,5 +88,5 @@
* Configure this tool for use in the ejbjar task.
*/
public void configure(File srcDir, File descriptorDir, String
basenameTerminator,
- String baseJarName, boolean flatDestDir);
+ String baseJarName, boolean flatDestDir, Path
classpath);
}
1.9 +100 -28
jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java
Index: EjbJar.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- EjbJar.java 2000/10/16 09:38:57 1.8
+++ EjbJar.java 2000/11/11 15:11:41 1.9
@@ -69,6 +69,7 @@
import org.apache.tools.ant.Project;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.*;
/**
* <p>Provides automated ejb jar file creation for ant. Extends the
MatchingTask
@@ -113,6 +114,11 @@
/** Stores a handle to the destination EJB Jar file */
private String baseJarName;
+
+ /**
+ * The classpath to use when loading classes
+ */
+ private Path classpath;
/**
* Instance variable that determines whether to use a package structure
@@ -131,6 +137,12 @@
*/
private ArrayList deploymentTools = new ArrayList();
+ /**
+ * Create a weblogic nested element used to configure a
+ * deployment tool for Weblogic server.
+ *
+ * @return the deployment tool instance to be configured.
+ */
public WeblogicDeploymentTool createWeblogic() {
WeblogicDeploymentTool tool = new WeblogicDeploymentTool();
tool.setTask(this);
@@ -138,6 +150,12 @@
return tool;
}
+ /**
+ * Create a nested element for weblogic when using the Toplink
+ * Object- Relational mapping.
+ *
+ * @return the deployment tool instance to be configured.
+ */
public WeblogicTOPLinkDeploymentTool createWeblogictoplink() {
WeblogicTOPLinkDeploymentTool tool = new
WeblogicTOPLinkDeploymentTool();
tool.setTask(this);
@@ -145,8 +163,26 @@
return tool;
}
+ /**
+ * creates a nested classpath element.
+ *
+ * This classpath is used to locate the super classes and interfaces
+ * of the classes that will make up the EJB jar.
+ *
+ * @return the path to be configured.
+ */
+ public Path createClasspath() {
+ if (classpath == null) {
+ classpath = new Path(project);
+ }
+ return classpath.createPath();
+ }
+
/**
- * Setter used to store the value of srcDir prior to execute() being
called.
+ * Set the srcdir attribute. The source directory is the directory that
contains
+ * the classes that will be added to the EJB jar. Typically this will
include the
+ * home and remote interfaces and the bean class.
+ *
* @param inDir the source directory.
*/
public void setSrcdir(File inDir) {
@@ -154,7 +190,13 @@
}
/**
- * Setter used to store the value of descriptorDir prior to execute()
being called.
+ * Set the descriptor directory.
+ *
+ * The descriptor directory contains the EJB deployment descriptors.
These are XML
+ * files that declare the properties of a bean in a particular
deployment scenario. Such
+ * properties include, for example, the transactional nature of the bean
and the security
+ * access control to the bean's methods.
+ *
* @param inDir the directory containing the deployment descriptors.
*/
public void setDescriptordir(File inDir) {
@@ -162,32 +204,64 @@
}
/**
- * Setter used to store the value of descriptorDir prior to execute()
being called.
- * @param inDir the directory containing the deployment descriptors.
+ * Set the base name of the EJB jar that is to be created if it is not
to be
+ * determined from the name of the deployment descriptor files.
+ *
+ * @param inValue the basename that will be used when writing the jar
file containing
+ * the EJB
*/
public void setBasejarname(String inValue) {
this.baseJarName = inValue;
}
/**
- * Setter used to store the value of destination directory prior to
execute()
- * being called.
+ * Set the destination directory.
+ *
+ * The EJB jar files will be written into this directory. The jar files
that exist in
+ * this directory are also used when determining if the contents of the
jar file
+ * have changed.
+ *
+ * Note that this parameter is only used if no deployment tools are
specified. Typically
+ * each deployment tool will specify its own destination directory.
+ *
* @param inFile the destination directory.
*/
public void setDestdir(File inDir) {
this.destDir = inDir;
}
+ /**
+ * Set the classpath to use when resolving classes for inclusion in the
jar.
+ *
+ * @param classpath the classpath to use.
+ */
+ public void setClasspath(Path classpath) {
+ this.classpath = classpath;
+ }
+
/**
- * Setter used to store the value of flatDestDir.
- * @param inValue a string, either 'true' or 'false'.
+ * Set the flat dest dir flag.
+ *
+ * This flag controls whether the destination jars are written out in
the
+ * destination directory with the same hierarchal structure from which
+ * the deployment descriptors have been read. If this is set to true the
+ * generated EJB jars are written into the root of the destination
directory,
+ * otherwise they are written out in the same relative position as the
deployment
+ * descriptors in the descriptor directory.
+ *
+ * @param inValue the new value of the flatdestdir flag.
*/
public void setFlatdestdir(boolean inValue) {
this.flatDestDir = inValue;
}
/**
- * Setter used to store the suffix for the generated jar file.
+ * Set the suffix for the generated jar file.
+ * When generic jars are generated, they have a suffix which is appended
to the
+ * the bean name to create the name of the jar file. Note that this
suffix includes
+ * the extension fo te jar file and should therefore end with an
appropriate
+ * extension such as .jar or .ear
+ *
* @param inString the string to use as the suffix.
*/
public void setGenericjarsuffix(String inString) {
@@ -195,7 +269,13 @@
}
/**
- * Setter used to store the value of baseNameTerminator
+ * Set the baseNameTerminator.
+ *
+ * The basename terminator is the string which terminates the bean name.
The convention
+ * used by this task is that bean descriptors are named as the BeanName
with some suffix.
+ * The baseNameTerminator string separates the bean name and the suffix
and is used to
+ * determine the bean name.
+ *
* @param inValue a string which marks the end of the basename.
*/
public void setBasenameterminator(String inValue) {
@@ -203,23 +283,16 @@
}
/**
- * Setter used to store the value of generateweblogic.
- * @param inValue a string, either 'true' or 'false'.
- */
- public void setGenerateweblogic(String inValue) {
- log("The syntax for using ejbjar with Weblogic has changed.",
Project.MSG_ERR);
- log("Please refer to the ejbjar documentation" +
- " for information on the using the <weblogic> nested element",
Project.MSG_ERR);
- throw new BuildException("generateweblogic not supported - use
nested <weblogic> element");
- }
-
- /**
* Invoked by Ant after the task is prepared, when it is ready to execute
- * this task. Parses the XML deployment descriptor to acquire the list
of
- * files, then constructs the destination jar file (first deleting it if
it
- * already exists) from the list of classfiles encountered and the
descriptor
- * itself. File will be of the expected format with classes under full
- * package hierarchies and the descriptor in META-INF/ejb-jar.xml
+ * this task.
+ *
+ * This will configure all of the nested deployment tools to allow them
to
+ * process the jar. If no deployment tools have been configured a
generic
+ * tool is created to handle the jar.
+ *
+ * A parser is configured and then each descriptor found is passed to all
+ * the deployment tool elements for processing.
+ *
* @exception BuildException thrown whenever a problem is
* encountered that cannot be recovered from, to signal to ant
* that a major problem occurred within this task.
@@ -234,7 +307,6 @@
genericTool.setDestdir(destDir);
genericTool.setTask(this);
genericTool.setGenericJarSuffix(genericJarSuffix);
-
deploymentTools.add(genericTool);
}
@@ -245,7 +317,7 @@
for (Iterator i = deploymentTools.iterator(); i.hasNext(); ) {
EJBDeploymentTool tool = (EJBDeploymentTool)i.next();
- tool.configure(srcDir, scanDir, baseNameTerminator, baseJarName,
flatDestDir);
+ tool.configure(srcDir, scanDir, baseNameTerminator, baseJarName,
flatDestDir, classpath);
tool.validateConfigured();
}
1.6 +352 -69
jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java
Index: GenericDeploymentTool.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- GenericDeploymentTool.java 2000/10/16 13:22:17 1.5
+++ GenericDeploymentTool.java 2000/11/11 15:11:41 1.6
@@ -58,15 +58,23 @@
import java.util.*;
import java.util.jar.*;
import java.util.zip.*;
+import java.net.*;
import javax.xml.parsers.SAXParser;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
-import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.Project;
-import org.apache.tools.ant.Task;
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.types.*;
+/**
+ * A deployment tool which creates generic EJB jars. Generic jars contains
+ * only those classes and META-INF entries specified in the EJB 1.1 standard
+ *
+ * This class is also used as a framework for the creation of vendor specific
+ * deployment tools. A number of template methods are provided through which
the
+ * vendor specific tool can hook into the EJB creation process.
+ */
public class GenericDeploymentTool implements EJBDeploymentTool {
/** Private constants that are used when constructing the standard
jarfile */
protected static final String META_DIR = "META-INF/";
@@ -84,6 +92,9 @@
/** Instance variable that stores the jar file name when not using the
naming standard */
private String baseJarName;
+ /** The classpath to use with this deployment tool. */
+ private Path classpath;
+
/**
* Instance variable that determines whether to use a package structure
* of a flat directory as the destination for the jar files.
@@ -97,11 +108,23 @@
private String genericJarSuffix = "-generic.jar";
/**
- * The task to which this tool belongs.
+ * The task to which this tool belongs. This is used to access services
provided
+ * by the ant core, such as logging.
*/
private Task task;
/**
+ * The classloader generated from the given classpath to load
+ * the super classes and super interfaces.
+ */
+ private ClassLoader classpathLoader = null;
+
+ /**
+ * List of files have been loaded into the EJB jar
+ */
+ private List addedfiles;
+
+ /**
* Setter used to store the value of destination directory prior to
execute()
* being called.
* @param inDir the destination directory.
@@ -177,15 +200,48 @@
}
/**
+ * creates a nested classpath element.
+ */
+ public Path createClasspath() {
+ if (classpath == null) {
+ classpath = new Path(task.getProject());
+ }
+ return classpath.createPath();
+ }
+
+ /**
+ * Set the classpath to be used for this compilation.
+ */
+ public void setClasspath(Path classpath) {
+ this.classpath = classpath;
+ }
+
+ protected Path getClasspath() {
+ return classpath;
+ }
+
+ protected void log(String message, int level) {
+ getTask().log(message, level);
+ }
+
+
+ /**
* Configure this tool for use in the ejbjar task.
*/
public void configure(File srcDir, File descriptorDir, String
baseNameTerminator,
- String baseJarName, boolean flatDestDir) {
+ String baseJarName, boolean flatDestDir, Path
classpath) {
this.srcDir = srcDir;
this.descriptorDir = descriptorDir;
this.baseJarName = baseJarName;
this.baseNameTerminator = baseNameTerminator;
this.flatDestDir = flatDestDir;
+ if (this.classpath != null) {
+ this.classpath.append(classpath);
+ }
+ else {
+ this.classpath = classpath;
+ }
+ classpathLoader = null;
}
/**
@@ -194,39 +250,52 @@
* constructed.
* @param jStream A JarOutputStream into which to write the
* jar entry.
- * @param iStream A FileInputStream from which to read the
+ * @param inputFile A File from which to read the
* contents the file being added.
- * @param filename A String representing the name, including
+ * @param logicalFilename A String representing the name, including
* all relevant path information, that should be stored for the
entry
* being added.
*/
protected void addFileToJar(JarOutputStream jStream,
- FileInputStream iStream,
- String filename)
+ File inputFile,
+ String logicalFilename)
throws BuildException {
+ FileInputStream iStream = null;
try {
- // Create the zip entry and add it to the jar file
- ZipEntry zipEntry = new ZipEntry(filename);
- jStream.putNextEntry(zipEntry);
-
- // Create the file input stream, and buffer everything over
- // to the jar output stream
- byte[] byteBuffer = new byte[2 * 1024];
- int count = 0;
- do {
- jStream.write(byteBuffer, 0, count);
- count = iStream.read(byteBuffer, 0, byteBuffer.length);
- } while (count != -1);
-
- // Close up the file input stream for the class file
- iStream.close();
+ if (!addedfiles.contains(logicalFilename)) {
+ iStream = new FileInputStream(inputFile);
+ // Create the zip entry and add it to the jar file
+ ZipEntry zipEntry = new ZipEntry(logicalFilename);
+ jStream.putNextEntry(zipEntry);
+
+ // Create the file input stream, and buffer everything over
+ // to the jar output stream
+ byte[] byteBuffer = new byte[2 * 1024];
+ int count = 0;
+ do {
+ jStream.write(byteBuffer, 0, count);
+ count = iStream.read(byteBuffer, 0, byteBuffer.length);
+ } while (count != -1);
+
+ //add it to list of files in jar
+ addedfiles.add(logicalFilename);
+ }
}
catch (IOException ioe) {
String msg = "IOException while adding entry "
- + filename + "to jarfile."
+ + logicalFilename + " to jarfile from " +
inputFile.getPath() + "."
+ ioe.getMessage();
throw new BuildException(msg, ioe);
}
+ finally {
+ // Close up the file input stream for the class file
+ if (iStream != null) {
+ try {
+ iStream.close();
+ }
+ catch (IOException closeException) {}
+ }
+ }
}
protected DescriptorHandler getDescriptorHandler(File srcDir) {
@@ -234,6 +303,8 @@
}
public void processDescriptor(String descriptorFileName, SAXParser
saxParser) {
+ FileInputStream descriptorStream = null;
+
try {
DescriptorHandler handler = getDescriptorHandler(srcDir);
@@ -241,10 +312,8 @@
* look like much, we use a SAXParser and an inner class to
* get hold of all the classfile names for the descriptor.
*/
- saxParser.parse(new InputSource
- (new FileInputStream
- (new File(getDescriptorDir(),
descriptorFileName))),
- handler);
+ descriptorStream = new FileInputStream(new
File(getDescriptorDir(), descriptorFileName));
+ saxParser.parse(new InputSource(descriptorStream), handler);
Hashtable ejbFiles = handler.getFiles();
@@ -273,8 +342,12 @@
// First the regular deployment descriptor
ejbFiles.put(META_DIR + EJB_DD,
new File(getDescriptorDir(), descriptorFileName));
-
+
+ // now the vendor specific files, if any
addVendorFiles(ejbFiles, baseName);
+
+ // add any inherited files
+ checkAndAddInherited(ejbFiles);
// Lastly create File object for the Jar files. If we are using
// a flat destination dir, then we need to redefine baseName!
@@ -304,6 +377,10 @@
while( (needBuild == false) && (fileIter.hasNext()) ) {
File currentFile = (File) fileIter.next();
needBuild = ( lastBuild < currentFile.lastModified() );
+ if (needBuild) {
+ log("Build needed because " + currentFile.getPath()
+ " is out of date",
+ Project.MSG_VERBOSE);
+ }
}
}
@@ -311,7 +388,7 @@
// doing the work!
if (needBuild) {
// Log that we are going to build...
- getTask().log( "building "
+ log( "building "
+ jarFile.getName()
+ " with "
+ String.valueOf(ejbFiles.size())
@@ -324,7 +401,7 @@
}
else {
// Log that the file is up to date...
- getTask().log(jarFile.toString() + " is up to date.",
+ log(jarFile.toString() + " is up to date.",
Project.MSG_INFO);
}
@@ -345,6 +422,14 @@
+ ioe.getMessage();
throw new BuildException(msg, ioe);
}
+ finally {
+ if (descriptorStream != null) {
+ try {
+ descriptorStream.close();
+ }
+ catch (IOException closeException) {}
+ }
+ }
}
/**
@@ -352,6 +437,7 @@
* EJB Jar.
*/
protected void addVendorFiles(Hashtable ejbFiles, String baseName) {
+ // nothing to add for generic tool.
}
@@ -369,14 +455,12 @@
* ejbFiles.
*/
protected void writeJar(String baseName, File jarfile, Hashtable files)
throws BuildException{
- JarOutputStream jarStream = null;
- Iterator entryIterator = null;
- String entryName = null;
- File entryFile = null;
- File entryDir = null;
- String innerfiles[] = null;
+ JarOutputStream jarStream = null;
try {
+ // clean the addedfiles Vector
+ addedfiles = new ArrayList();
+
/* If the jarfile already exists then whack it and recreate it.
* Should probably think of a more elegant way to handle this
* so that in case of errors we don't leave people worse off
@@ -389,45 +473,39 @@
jarfile.createNewFile();
// Create the streams necessary to write the jarfile
+
jarStream = new JarOutputStream(new FileOutputStream(jarfile));
jarStream.setMethod(JarOutputStream.DEFLATED);
// Loop through all the class files found and add them to the jar
- entryIterator = files.keySet().iterator();
- while (entryIterator.hasNext()) {
- entryName = (String) entryIterator.next();
- entryFile = (File) files.get(entryName);
+ for (Iterator entryIterator = files.keySet().iterator();
entryIterator.hasNext(); ) {
+ String entryName = (String) entryIterator.next();
+ File entryFile = (File) files.get(entryName);
- getTask().log("adding file '" + entryName + "'",
+ log("adding file '" + entryName + "'",
Project.MSG_VERBOSE);
- addFileToJar(jarStream,
- new FileInputStream(entryFile),
- entryName);
-
- // See if there are any inner classes for this class and add
them in if there are
- InnerClassFilenameFilter flt = new
InnerClassFilenameFilter(entryFile.getName());
- entryDir = entryFile.getParentFile();
- innerfiles = entryDir.list(flt);
- for (int i=0, n=innerfiles.length; i < n; i++) {
-
- //get and clean up innerclass name
- entryName = entryName.substring(0,
entryName.lastIndexOf(entryFile.getName())-1) + File.separatorChar +
innerfiles[i];
-
- // link the file
- entryFile = new File(srcDir, entryName);
-
- getTask().log("adding innerclass file '" + entryName +
"'",
- Project.MSG_VERBOSE);
-
- addFileToJar(jarStream,
- new FileInputStream(entryFile),
- entryName);
+ addFileToJar(jarStream, entryFile, entryName);
- }
+ // See if there are any inner classes for this class and add
them in if there are
+ InnerClassFilenameFilter flt = new
InnerClassFilenameFilter(entryFile.getName());
+ File entryDir = entryFile.getParentFile();
+ String[] innerfiles = entryDir.list(flt);
+ for (int i=0, n=innerfiles.length; i < n; i++) {
+
+ //get and clean up innerclass name
+ entryName = entryName.substring(0,
entryName.lastIndexOf(entryFile.getName())-1) + File.separatorChar +
innerfiles[i];
+
+ // link the file
+ entryFile = new File(srcDir, entryName);
+
+ log("adding innerclass file '" + entryName + "'",
+ Project.MSG_VERBOSE);
+
+ addFileToJar(jarStream, entryFile, entryName);
+
+ }
}
- // All done. Close the jar stream.
- jarStream.close();
}
catch(IOException ioe) {
String msg = "IOException while processing ejb-jar file '"
@@ -436,12 +514,217 @@
+ ioe.getMessage();
throw new BuildException(msg, ioe);
}
+ finally {
+ if (jarStream != null) {
+ try {
+ jarStream.close();
+ }
+ catch (IOException closeException) {}
+ }
+ }
} // end of writeJar
+ /**
+ * Check if a EJB Class Inherits from a Superclass, and if a Remote
Interface
+ * extends an interface other then javax.ejb.EJBObject directly. Then
add those
+ * classes to the generic-jar so they dont have to added elsewhere.
+ *
+ */
+ protected void checkAndAddInherited(Hashtable checkEntries) throws
BuildException
+ {
+ //Copy hashtable so were not changing the one we iterate through
+ Hashtable copiedHash = (Hashtable)checkEntries.clone();
+
+ // Walk base level EJBs and see if they have superclasses or extend
extra interfaces which extend EJBObject
+ for (Iterator entryIterator = copiedHash.keySet().iterator();
entryIterator.hasNext(); )
+ {
+ String entryName = (String)entryIterator.next();
+ File entryFile = (File)copiedHash.get(entryName);
+
+ // only want class files, xml doesnt reflect very well =)
+ if (entryName.endsWith(".class"))
+ {
+ String classname =
entryName.substring(0,entryName.lastIndexOf(".class")).replace(File.separatorChar,'.');
+ ClassLoader loader = getClassLoaderForBuild();
+ try {
+ Class c = loader.loadClass(classname);
+
+ // No primatives!! sanity check, probably not nessesary
+ if (!c.isPrimitive())
+ {
+ if (c.isInterface()) //get as an interface
+ {
+ log("looking at interface " + c.getName(),
Project.MSG_VERBOSE);
+ Class[] interfaces = c.getInterfaces();
+ for (int i = 0; i < interfaces.length; i++){
+
+ log(" implements " +
interfaces[i].getName(), Project.MSG_VERBOSE);
+ if
(!interfaces[i].getName().equals("javax.ejb.EJBObject")) // do not add home
interfaces
+ {
+ File superClassFile = new
File(srcDir.getAbsolutePath()
+ +
File.separatorChar
+ +
interfaces[i].getName().replace('.',File.separatorChar)
+ +
".class"
+ );
+ if (superClassFile.exists() &&
superClassFile.isFile())
+ {
+ if
(checkInterfaceClasses(interfaces[i].getName().replace('.',File.separatorChar)+".class",
+ superClassFile, checkEntries))
+ {
+
checkEntries.put(interfaces[i].getName().replace('.',File.separatorChar)+".class",
+ superClassFile);
+ }
+ }
+ }
+ }
+ }
+ else // get as a class
+ {
+ log("looking at class " + c.getName(),
Project.MSG_VERBOSE);
+ Class s = c.getSuperclass();
+ if (!s.getName().equals("java.lang.Object"))
+ {
+ File superClassFile = new
File(srcDir.getAbsolutePath()
+ + File.separatorChar
+ + s.getName().replace('.',File.separatorChar)
+ + ".class"
+ );
+ if (superClassFile.exists() &&
superClassFile.isFile())
+ {
+
checkSuperClasses(s.getName().replace('.',File.separatorChar) + ".class",
+ superClassFile, checkEntries);
+
checkEntries.put(s.getName().replace('.',File.separatorChar) + ".class",
+ superClassFile);
+ }
+ }
+ }
+ } //if primative
+ }
+ catch (ClassNotFoundException cnfe) {
+ log("Could not load class " + classname + " for super
class check",
+ Project.MSG_WARN);
+ }
+ } //if
+ } // while
+ }
+
+ /**
+ * Returns a Classloader object which parses the passed in generic
EjbJar classpath.
+ * The loader is used to dynamically load classes from javax.ejb.* and
the classes
+ * being added to the jar.
+ *
+ */
+ protected ClassLoader getClassLoaderForBuild()
+ {
+ if (classpathLoader != null) {
+ return classpathLoader;
+ }
+
+ // only generate a URLClassLoader if we have a classpath
+ if (classpath == null) {
+ classpathLoader = getClass().getClassLoader();
+ }
+ else {
+ classpathLoader = new AntClassLoader(getTask().getProject(),
classpath);
+ }
+
+ return classpathLoader;
+ }
+ /**
+ * Checks to see if a Superclass of an Object needs to be included in
the EJB Jar.
+ * This is done my checking the class and if it inherits from a
superclass and that
+ * superclass is available then it includes that in the Hashtable of
entries to be added
+ * to the Jar.
+ *
+ */
+ protected void checkSuperClasses(String entryName, File entryFile,
Hashtable checkEntries)
+ {
+ try
+ {
+ if (entryName.endsWith(".class")) //sanity check
+ {
+ // Load class to check superclass and interfaces
+ ClassLoader loader = getClassLoaderForBuild();
+ String classname =
entryName.substring(0,entryName.lastIndexOf(".class")).replace(File.separatorChar,'.');
+ Class c = loader.loadClass(classname);
+
+ Class s = c.getSuperclass();
+ if (!s.getName().equals("java.lang.Object"))
+ {
+ File superClassFile = new File(srcDir.getAbsolutePath()
+ + File.separatorChar
+ +
s.getName().replace('.',File.separatorChar)
+ + ".class"
+ );
+ if (superClassFile.exists() && superClassFile.isFile()){
+
checkSuperClasses(s.getName().replace('.',File.separatorChar) + ".class",
superClassFile, checkEntries);
+
checkEntries.put(s.getName().replace('.',File.separatorChar) + ".class",
superClassFile);
+ }
+ }
+ }
+ }
+ catch(ClassNotFoundException cnfe){
+ String cnfmsg = "ClassNotFoundException while processing ejb-jar
file"
+ + ". Details: "
+ + cnfe.getMessage();
+ throw new BuildException(cnfmsg, cnfe);
+ }
+ }
+ /**
+ * Checks to see if an interface extends another interface and if the
final interface on the
+ * chain implements javax.ejb.EJBObject the it includes all interfaces
in that chain in the Jar.
+ *
+ */
+ protected boolean checkInterfaceClasses(String entryName, File
entryFile, Hashtable checkEntries)
+ {
+ boolean addit = false;
+ try
+ {
+ if (entryName.endsWith(".class")) //sanity check
+ {
+ // Load class to check superclass and interfaces
+ ClassLoader loader = getClassLoaderForBuild();
+ String classname =
entryName.substring(0,entryName.lastIndexOf(".class")).replace(File.separatorChar,'.');
+ Class c = loader.loadClass(classname);
+
+ Class[] interfaces = c.getInterfaces();
+ for (int i = 0; i < interfaces.length; i++){
+ if
(!interfaces[i].getName().equals("javax.ejb.EJBObject")){ // do not add home
interfaces
+ File superClassFile = new
File(srcDir.getAbsolutePath()
+ + File.separatorChar
+ +
interfaces[i].getName().replace('.',File.separatorChar)
+ + ".class"
+ );
+ if (superClassFile.exists() &&
superClassFile.isFile()){
+ log("looking at interface " +
interfaces[i].getName(), Project.MSG_VERBOSE);
+
+ addit =
checkInterfaceClasses(interfaces[i].getName().replace('.',File.separatorChar)+".class",
+ superClassFile, checkEntries);
+ if (addit)
+ {
+ log("adding at interface " +
interfaces[i].getName(), Project.MSG_VERBOSE);
+
checkEntries.put(interfaces[i].getName().replace('.',File.separatorChar)+".class",
+ superClassFile);
+ }
+ }
+ }
+ else {
+ addit = true;
+ }
+ }
+ }
+ }
+ catch(ClassNotFoundException cnfe){
+ String cnfmsg = "ClassNotFoundException while processing ejb-jar
file"
+ + ". Details: "
+ + cnfe.getMessage();
+ throw new BuildException(cnfmsg, cnfe);
+ }
+ return addit;
+ }
-
/**
* Called to validate that the tool parameters have been configured.
*
1.6 +237 -13
jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicDeploymentTool.java
Index: WeblogicDeploymentTool.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicDeploymentTool.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- WeblogicDeploymentTool.java 2000/10/16 09:41:22 1.5
+++ WeblogicDeploymentTool.java 2000/11/11 15:11:41 1.6
@@ -55,7 +55,9 @@
package org.apache.tools.ant.taskdefs.optional.ejb;
import java.io.*;
+import java.util.jar.*;
import java.util.*;
+import java.net.*;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
@@ -76,8 +78,6 @@
/** Instance variable that stores the location of the weblogic DTD file.
*/
private String weblogicDTD;
- private Path classpath;
-
/** Instance variable that determines whether generic ejb jars are kept.
*/
private boolean keepgenerated = false;
@@ -87,20 +87,22 @@
private boolean keepGeneric = false;
private String compiler = null;
+
+ private boolean alwaysRebuild = true;
/**
- * Set the classpath to be used for this compilation.
+ * The compiler (switch <code>-compiler</code>) to use
*/
- public void setClasspath(Path classpath) {
- this.classpath = classpath;
+ public void setCompiler(String compiler) {
+ this.compiler = compiler;
}
-
+
/**
- * The compiler (switch <code>-compiler</code>) to use
+ * Set the rebuild flag to false to only update changes in the
+ * jar rather than rerunning ejbc
*/
- public void setCompiler(String compiler)
- {
- this.compiler = compiler;
+ public void setRebuild(boolean rebuild) {
+ this.alwaysRebuild = rebuild;
}
@@ -226,6 +228,7 @@
javaTask.setClassname("weblogic.ejbc");
Commandline.Argument arguments = javaTask.createArg();
arguments.setLine(args);
+ Path classpath = getClasspath();
if (classpath != null) {
javaTask.setClasspath(classpath);
javaTask.setFork(true);
@@ -235,7 +238,7 @@
}
- getTask().log("Calling weblogic.ejbc for " +
sourceJar.toString(),
+ log("Calling weblogic.ejbc for " + sourceJar.toString(),
Project.MSG_VERBOSE);
javaTask.execute();
@@ -257,9 +260,12 @@
File genericJarFile = super.getVendorOutputJarFile(baseName);
super.writeJar(baseName, genericJarFile, files);
- buildWeblogicJar(genericJarFile, jarFile);
+ if (alwaysRebuild || isRebuildRequired(genericJarFile, jarFile))
+ {
+ buildWeblogicJar(genericJarFile, jarFile);
+ }
if (!keepGeneric) {
- getTask().log("deleting generic jar " +
genericJarFile.toString(),
+ log("deleting generic jar " + genericJarFile.toString(),
Project.MSG_VERBOSE);
genericJarFile.delete();
}
@@ -271,5 +277,223 @@
*/
public void validateConfigured() throws BuildException {
super.validateConfigured();
+ }
+
+
+ /**
+ * Helper method to check to see if a weblogic EBJ1.1 jar needs to be
rebuilt using
+ * ejbc. Called from writeJar it sees if the "Bean" classes are the
only thing that needs
+ * to be updated and either updates the Jar with the Bean classfile or
returns true,
+ * saying that the whole weblogic jar needs to be regened with ejbc.
This allows faster
+ * build times for working developers.
+ * <p>
+ * The way weblogic ejbc works is it creates wrappers for the publicly
defined methods as
+ * they are exposed in the remote interface. If the actual bean changes
without changing the
+ * the method signatures then only the bean classfile needs to be
updated and the rest of the
+ * weblogic jar file can remain the same. If the Interfaces, ie. the
method signatures change
+ * or if the xml deployment dicriptors changed, the whole jar needs to
be rebuilt with ejbc.
+ * This is not strictly true for the xml files. If the JNDI name
changes then the jar doesnt
+ * have to be rebuild, but if the resources references change then it
does. At this point the
+ * weblogic jar gets rebuilt if the xml files change at all.
+ *
+ * @param genericJarFile java.io.File The generic jar file.
+ * @param weblogicJarFile java.io.File The weblogic jar file to check to
see if it needs to be rebuilt.
+ */
+ protected boolean isRebuildRequired(File genericJarFile, File
weblogicJarFile)
+ {
+ boolean rebuild = false;
+
+ JarFile genericJar = null;
+ JarFile wlJar = null;
+ File newWLJarFile = null;
+ JarOutputStream newJarStream = null;
+
+ try
+ {
+ log("Checking if weblogic Jar needs to be rebuilt for jar " +
weblogicJarFile.getName(),
+ Project.MSG_VERBOSE);
+ // Only go forward if the generic and the weblogic file both
exist
+ if (genericJarFile.exists() && genericJarFile.isFile()
+ && weblogicJarFile.exists() && weblogicJarFile.isFile())
+ {
+ //open jar files
+ genericJar = new JarFile(genericJarFile);
+ wlJar = new JarFile(weblogicJarFile);
+
+ Hashtable genericEntries = new Hashtable();
+ Hashtable wlEntries = new Hashtable();
+ Hashtable replaceEntries = new Hashtable();
+
+ //get the list of generic jar entries
+ for (Enumeration e = genericJar.entries();
e.hasMoreElements();)
+ {
+ JarEntry je = (JarEntry)e.nextElement();
+ genericEntries.put(je.getName().replace('\\', '/'), je);
+ }
+ //get the list of weblogic jar entries
+ for (Enumeration e = wlJar.entries() ; e.hasMoreElements();)
+ {
+ JarEntry je = (JarEntry)e.nextElement();
+ wlEntries.put(je.getName(), je);
+ }
+
+ //Cycle Through generic and make sure its in weblogic
+ ClassLoader genericLoader =
getClassLoaderFromJar(genericJarFile);
+ for (Enumeration e = genericEntries.keys();
e.hasMoreElements();)
+ {
+ String filepath = (String)e.nextElement();
+ if (wlEntries.containsKey(filepath)) // File
name/path match
+ {
+ // Check files see if same
+ JarEntry genericEntry =
(JarEntry)genericEntries.get(filepath);
+ JarEntry wlEntry = (JarEntry)wlEntries.get(filepath);
+ if ((genericEntry.getCrc() != wlEntry.getCrc()) ||
// Crc's Match
+ (genericEntry.getSize() != wlEntry.getSize()) )
// Size Match
+ {
+ if (genericEntry.getName().endsWith(".class"))
+ {
+ //File are different see if its an object or
an interface
+ String classname =
genericEntry.getName().replace(File.separatorChar,'.');
+ classname =
classname.substring(0,classname.lastIndexOf(".class"));
+ Class genclass =
genericLoader.loadClass(classname);
+ if (genclass.isInterface())
+ {
+ //Interface changed rebuild jar.
+ log("Interface " + genclass.getName() +
" has changed",Project.MSG_VERBOSE);
+ rebuild = true;
+ break;
+ }
+ else
+ {
+ //Object class Changed update it.
+ replaceEntries.put(filepath,
genericEntry);
+ }
+ }
+ else
+ {
+ //File other then class changed rebuild
+ log("Non class file " +
genericEntry.getName() + " has changed",Project.MSG_VERBOSE);
+ rebuild = true;
+ break;
+ }
+ }
+ }
+ else // a file doesnt exist rebuild
+ {
+ log("File " + filepath + " not present in weblogic
jar",Project.MSG_VERBOSE);
+ rebuild = true;
+ break;
+ }
+ }
+
+ if (!rebuild)
+ {
+ log("No rebuild needed - updating
jar",Project.MSG_VERBOSE);
+ newWLJarFile = new
File(weblogicJarFile.getAbsolutePath() + ".temp");
+ if (newWLJarFile.exists()) {
+ newWLJarFile.delete();
+ }
+
+ newJarStream = new JarOutputStream(new
FileOutputStream(newWLJarFile));
+
+ //Copy files from old weblogic jar
+ for (Enumeration e = wlEntries.elements() ;
e.hasMoreElements();)
+ {
+ byte[] buffer = new byte[1024];
+ int bytesRead;
+ InputStream is;
+ JarEntry je = (JarEntry)e.nextElement();
+
+ // Update with changed Bean class
+ if (replaceEntries.containsKey(je.getName()))
+ {
+ log("Updating Bean class from generic Jar " +
je.getName(),Project.MSG_VERBOSE);
+ // Use the entry from the generic jar
+ je = (JarEntry)replaceEntries.get(je.getName());
+ is = genericJar.getInputStream(je);
+ }
+ else //use fle from original weblogic jar
+ {
+ is = wlJar.getInputStream(je);
+ }
+ newJarStream.putNextEntry(new
JarEntry(je.getName()));
+
+ while ((bytesRead = is.read(buffer)) != -1)
+ {
+ newJarStream.write(buffer,0,bytesRead);
+ }
+ is.close();
+ }
+ }
+ else
+ {
+ log("Weblogic Jar rebuild needed due to changed
interface or XML",Project.MSG_VERBOSE);
+ }
+ }
+ else
+ {
+ rebuild = true;
+ }
+ }
+ catch(ClassNotFoundException cnfe)
+ {
+ String cnfmsg = "ClassNotFoundException while processing ejb-jar
file"
+ + ". Details: "
+ + cnfe.getMessage();
+ throw new BuildException(cnfmsg, cnfe);
+ }
+ catch(IOException ioe) {
+ String msg = "IOException while processing ejb-jar file "
+ + ". Details: "
+ + ioe.getMessage();
+ throw new BuildException(msg, ioe);
+ }
+ finally {
+ // need to close files and perhaps rename output
+ if (genericJar != null) {
+ try {
+ genericJar.close();
+ }
+ catch (IOException closeException) {}
+ }
+
+ if (wlJar != null) {
+ try {
+ wlJar.close();
+ }
+ catch (IOException closeException) {}
+ }
+
+ if (newJarStream != null) {
+ try {
+ newJarStream.close();
+ }
+ catch (IOException closeException) {}
+
+ weblogicJarFile.delete();
+ newWLJarFile.renameTo(weblogicJarFile);
+ if (!weblogicJarFile.exists()) {
+ rebuild = true;
+ }
+ }
+ }
+
+ return rebuild;
+ }
+
+ /**
+ * Helper method invoked by isRebuildRequired to get a ClassLoader for
+ * a Jar File passed to it.
+ *
+ * @param classjar java.io.File representing jar file to get classes from.
+ */
+ protected ClassLoader getClassLoaderFromJar(File classjar) throws
IOException
+ {
+ URLClassLoader loader;
+ URL[] aURL = new URL[1];
+
+ aURL[0] = new URL("file","",0,classjar.getAbsolutePath());
+ loader = new URLClassLoader(aURL);
+ return loader;
}
}