You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by co...@locus.apache.org on 2000/11/11 16:11:42 UTC

cvs commit: jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb EJBDeploymentTool.java EjbJar.java GenericDeploymentTool.java WeblogicDeploymentTool.java

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 <br...@towles.com>
  
  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;
       }
   }
  
  
  

RE: cvs commit: jakarta-ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb EJBDeploymentTool.java EjbJar.java GenericDeploymentTool.java WeblogicDeploymentTool.java

Posted by Conor MacNeill <co...@m64.com>.
Just some additional info about this patch.

Brian's original patch used URLClassLoaders. I have changed this to an
AntClassLoader because I was unable to get the URLClassLoader to work
under Windows NT. It may have been to do with absolute files with drive
specs in the classpath. I probably wouldn't change back to a
URLClassLoader anyway as the AntClassLoader handles the Path type more
concisely but if you know how to get URLClassLoader to work, let me know
:-)

Conor