diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EJBDeploymentTool.java b/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EJBDeploymentTool.java index deabe5c72..6ff969b9e 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EJBDeploymentTool.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EJBDeploymentTool.java @@ -60,6 +60,7 @@ import javax.xml.parsers.SAXParser; 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 @@ public interface EJBDeploymentTool { * 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); } \ No newline at end of file diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java b/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java index e6c0d7f40..425ac6722 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java @@ -69,6 +69,7 @@ import org.apache.tools.ant.BuildException; 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.*; /** *
Provides automated ejb jar file creation for ant. Extends the MatchingTask
@@ -113,6 +114,11 @@ public class EjbJar extends MatchingTask {
/** 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 @@ public class EjbJar extends MatchingTask {
*/
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 @@ public class EjbJar extends MatchingTask {
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);
@@ -146,7 +164,25 @@ public class EjbJar extends MatchingTask {
}
/**
- * Setter used to store the value of srcDir prior to execute() being called.
+ * 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();
+ }
+
+ /**
+ * 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 @@ public class EjbJar extends MatchingTask {
}
/**
- * 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,16 +204,26 @@ public class EjbJar extends MatchingTask {
}
/**
- * 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) {
@@ -179,15 +231,37 @@ public class EjbJar extends MatchingTask {
}
/**
- * Setter used to store the value of flatDestDir.
- * @param inValue a string, either 'true' or 'false'.
+ * 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;
+ }
+
+ /**
+ * 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,31 +269,30 @@ public class EjbJar extends MatchingTask {
}
/**
- * 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) {
this.baseNameTerminator = inValue;
}
- /**
- * 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
+ * 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;
+ }
}
-compiler
) to use
*/
- public void setClasspath(Path classpath) {
- this.classpath = classpath;
+ public void setCompiler(String compiler) {
+ this.compiler = compiler;
}
-
+
/**
- * The compiler (switch -compiler
) 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 @@ public class WeblogicDeploymentTool extends GenericDeploymentTool {
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 @@ public class WeblogicDeploymentTool extends GenericDeploymentTool {
}
- getTask().log("Calling weblogic.ejbc for " + sourceJar.toString(),
+ log("Calling weblogic.ejbc for " + sourceJar.toString(),
Project.MSG_VERBOSE);
javaTask.execute();
@@ -257,9 +260,12 @@ public class WeblogicDeploymentTool extends GenericDeploymentTool {
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();
}
@@ -272,4 +278,222 @@ public class WeblogicDeploymentTool extends GenericDeploymentTool {
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.
+ *