Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 950   Methods: 33
NCLOC: 485   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
GenericDeploymentTool.java 0% 0% 0% 0%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
 5   
  * reserved.
 6   
  *
 7   
  * Redistribution and use in source and binary forms, with or without
 8   
  * modification, are permitted provided that the following conditions
 9   
  * are met:
 10   
  *
 11   
  * 1. Redistributions of source code must retain the above copyright
 12   
  *    notice, this list of conditions and the following disclaimer.
 13   
  *
 14   
  * 2. Redistributions in binary form must reproduce the above copyright
 15   
  *    notice, this list of conditions and the following disclaimer in
 16   
  *    the documentation and/or other materials provided with the
 17   
  *    distribution.
 18   
  *
 19   
  * 3. The end-user documentation included with the redistribution, if
 20   
  *    any, must include the following acknowlegement:
 21   
  *       "This product includes software developed by the
 22   
  *        Apache Software Foundation (http://www.apache.org/)."
 23   
  *    Alternately, this acknowlegement may appear in the software itself,
 24   
  *    if and wherever such third-party acknowlegements normally appear.
 25   
  *
 26   
  * 4. The names "Ant" and "Apache Software
 27   
  *    Foundation" must not be used to endorse or promote products derived
 28   
  *    from this software without prior written permission. For written
 29   
  *    permission, please contact apache@apache.org.
 30   
  *
 31   
  * 5. Products derived from this software may not be called "Apache"
 32   
  *    nor may "Apache" appear in their names without prior written
 33   
  *    permission of the Apache Group.
 34   
  *
 35   
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 36   
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 37   
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 38   
  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 39   
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 40   
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 41   
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 42   
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 43   
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 44   
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 45   
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 46   
  * SUCH DAMAGE.
 47   
  * ====================================================================
 48   
  *
 49   
  * This software consists of voluntary contributions made by many
 50   
  * individuals on behalf of the Apache Software Foundation.  For more
 51   
  * information on the Apache Software Foundation, please see
 52   
  * <http://www.apache.org/>.
 53   
  */
 54   
 package org.apache.tools.ant.taskdefs.optional.ejb;
 55   
 
 56   
 import java.io.File;
 57   
 import java.io.FileInputStream;
 58   
 import java.io.FileOutputStream;
 59   
 import java.io.IOException;
 60   
 import java.io.InputStream;
 61   
 import java.util.ArrayList;
 62   
 import java.util.Enumeration;
 63   
 import java.util.Hashtable;
 64   
 import java.util.Iterator;
 65   
 import java.util.List;
 66   
 import java.util.jar.JarOutputStream;
 67   
 import java.util.jar.Manifest;
 68   
 import java.util.zip.ZipEntry;
 69   
 import javax.xml.parsers.SAXParser;
 70   
 import org.apache.tools.ant.AntClassLoader;
 71   
 import org.apache.tools.ant.BuildException;
 72   
 import org.apache.tools.ant.DirectoryScanner;
 73   
 import org.apache.tools.ant.Location;
 74   
 import org.apache.tools.ant.Project;
 75   
 import org.apache.tools.ant.Task;
 76   
 import org.apache.tools.ant.types.FileSet;
 77   
 import org.apache.tools.ant.types.Path;
 78   
 import org.apache.tools.ant.util.depend.DependencyAnalyzer;
 79   
 import org.xml.sax.InputSource;
 80   
 import org.xml.sax.SAXException;
 81   
 
 82   
 
 83   
 /**
 84   
  * A deployment tool which creates generic EJB jars. Generic jars contains
 85   
  * only those classes and META-INF entries specified in the EJB 1.1 standard
 86   
  *
 87   
  * This class is also used as a framework for the creation of vendor specific
 88   
  * deployment tools. A number of template methods are provided through which the
 89   
  * vendor specific tool can hook into the EJB creation process.
 90   
  *
 91   
  * @author Conor MacNeill
 92   
  */
 93   
 public class GenericDeploymentTool implements EJBDeploymentTool {
 94   
     /** The standard META-INF directory in jar files */
 95   
     protected static final String META_DIR  = "META-INF/";
 96   
 
 97   
     /** The standard MANIFEST file */
 98   
     protected static final String MANIFEST  = META_DIR + "MANIFEST.MF";
 99   
 
 100   
     /** Name for EJB Deployment descriptor within EJB jars */
 101   
     protected static final String EJB_DD    = "ejb-jar.xml";
 102   
 
 103   
     /** A dependency analyzer name to find ancestor classes */
 104   
     public static final String ANALYZER_SUPER = "super";
 105   
     /** A dependency analyzer name to find all related classes */
 106   
     public static final String ANALYZER_FULL = "full";
 107   
     /** A dependency analyzer name for no analyzer */
 108   
     public static final String ANALYZER_NONE = "none";
 109   
 
 110   
     /** The default analyzer */
 111   
     public static final String DEFAULT_ANALYZER = ANALYZER_SUPER;
 112   
 
 113   
     /** The analyzer class for the super analyzer */
 114   
     public static final String ANALYZER_CLASS_SUPER
 115   
         = "org.apache.tools.ant.util.depend.bcel.AncestorAnalyzer";
 116   
     /** The analyzer class for the super analyzer */
 117   
     public static final String ANALYZER_CLASS_FULL
 118   
         = "org.apache.tools.ant.util.depend.bcel.FullAnalyzer";
 119   
 
 120   
     /**
 121   
      * The configuration from the containing task. This config combined
 122   
      * with the settings of the individual attributes here constitues the
 123   
      * complete config for this deployment tool.
 124   
      */
 125   
     private EjbJar.Config config;
 126   
 
 127   
     /** Stores a handle to the directory to put the Jar files in */
 128   
     private File destDir;
 129   
 
 130   
     /** The classpath to use with this deployment tool. This is appended to
 131   
         any paths from the ejbjar task itself.*/
 132   
     private Path classpath;
 133   
 
 134   
     /** Instance variable that stores the suffix for the generated jarfile. */
 135   
     private String genericJarSuffix = "-generic.jar";
 136   
 
 137   
     /**
 138   
      * The task to which this tool belongs. This is used to access services
 139   
      * provided by the ant core, such as logging.
 140   
      */
 141   
     private Task task;
 142   
 
 143   
     /**
 144   
      * The classloader generated from the given classpath to load
 145   
      * the super classes and super interfaces.
 146   
      */
 147   
     private ClassLoader classpathLoader = null;
 148   
 
 149   
      /**
 150   
      * List of files have been loaded into the EJB jar
 151   
      */
 152   
     private List addedfiles;
 153   
 
 154   
     /**
 155   
      * Handler used to parse the EJB XML descriptor
 156   
      */
 157   
     private DescriptorHandler handler;
 158   
 
 159   
     /**
 160   
      * Dependency analyzer used to collect class dependencies
 161   
      */
 162   
     private DependencyAnalyzer dependencyAnalyzer;
 163   
 
 164  0
     public GenericDeploymentTool() {
 165   
     }
 166   
 
 167   
 
 168   
     /**
 169   
      * Set the destination directory; required.
 170   
      * @param inDir the destination directory.
 171   
      */
 172  0
     public void setDestdir(File inDir) {
 173  0
         this.destDir = inDir;
 174   
     }
 175   
 
 176   
     /**
 177   
      * Get the destination directory.
 178   
      *
 179   
      * @return the destination directory into which EJB jars are to be written
 180   
      */
 181  0
     protected File getDestDir() {
 182  0
         return destDir;
 183   
     }
 184   
 
 185   
 
 186   
     /**
 187   
      * Set the task which owns this tool
 188   
      *
 189   
      * @param task the Task to which this deployment tool is associated.
 190   
      */
 191  0
     public void setTask(Task task) {
 192  0
         this.task = task;
 193   
     }
 194   
 
 195   
     /**
 196   
      * Get the task for this tool.
 197   
      *
 198   
      * @return the Task instance this tool is associated with.
 199   
      */
 200  0
     protected Task getTask() {
 201  0
         return task;
 202   
     }
 203   
 
 204   
     /**
 205   
      * Get the basename terminator.
 206   
      *
 207   
      * @return an ejbjar task configuration
 208   
      */
 209  0
     protected EjbJar.Config getConfig() {
 210  0
         return config;
 211   
     }
 212   
 
 213   
     /**
 214   
      * Indicate if this build is using the base jar name.
 215   
      *
 216   
      * @return true if the name of the generated jar is coming from the
 217   
      *              basejarname attribute
 218   
      */
 219  0
     protected boolean usingBaseJarName() {
 220  0
         return config.baseJarName != null;
 221   
     }
 222   
 
 223   
     /**
 224   
      * Set the suffix for the generated jar file.
 225   
      * @param inString the string to use as the suffix.
 226   
      */
 227  0
     public void setGenericJarSuffix(String inString) {
 228  0
         this.genericJarSuffix = inString;
 229   
     }
 230   
 
 231   
     /**
 232   
      * Add the classpath for the user classes
 233   
      *
 234   
      * @return a Path instance to be configured by Ant.
 235   
      */
 236  0
     public Path createClasspath() {
 237  0
         if (classpath == null) {
 238  0
             classpath = new Path(task.getProject());
 239   
         }
 240  0
         return classpath.createPath();
 241   
     }
 242   
 
 243   
     /**
 244   
      * Set the classpath to be used for this compilation.
 245   
      *
 246   
      * @param classpath the classpath to be used for this build.
 247   
      */
 248  0
     public void setClasspath(Path classpath) {
 249  0
         this.classpath = classpath;
 250   
     }
 251   
 
 252   
     /**
 253   
      * Get the classpath by combining the one from the surrounding task, if any
 254   
      * and the one from this tool.
 255   
      *
 256   
      * @return the combined classpath
 257   
      */
 258  0
     protected Path getCombinedClasspath() {
 259  0
         Path combinedPath = classpath;
 260  0
         if (config.classpath != null) {
 261  0
             if (combinedPath == null) {
 262  0
                 combinedPath = config.classpath;
 263   
             } else {
 264  0
                 combinedPath.append(config.classpath);
 265   
             }
 266   
         }
 267   
 
 268  0
         return combinedPath;
 269   
     }
 270   
 
 271   
     /**
 272   
      * Log a message to the Ant output.
 273   
      *
 274   
      * @param message the message to be logged.
 275   
      * @param level the severity of this message.
 276   
      */
 277  0
     protected void log(String message, int level) {
 278  0
         getTask().log(message, level);
 279   
     }
 280   
 
 281   
     /**
 282   
      * Get the build file location associated with this element's task.
 283   
      *
 284   
      * @return the task's location instance.
 285   
      */
 286  0
     protected Location getLocation() {
 287  0
         return getTask().getLocation();
 288   
     }
 289   
 
 290  0
     private void createAnalyzer() {
 291  0
         String analyzer = config.analyzer;
 292  0
         if (analyzer == null) {
 293  0
             analyzer = DEFAULT_ANALYZER;
 294   
         }
 295   
 
 296  0
         if (analyzer.equals(ANALYZER_NONE)) {
 297  0
             return;
 298   
         }
 299   
 
 300  0
         String analyzerClassName = null;
 301  0
         if (analyzer.equals(ANALYZER_SUPER)) {
 302  0
             analyzerClassName = ANALYZER_CLASS_SUPER;
 303  0
         } else if (analyzer.equals(ANALYZER_FULL)) {
 304  0
             analyzerClassName = ANALYZER_CLASS_FULL;
 305   
         } else {
 306  0
             analyzerClassName = analyzer;
 307   
         }
 308   
 
 309  0
         try {
 310  0
             Class analyzerClass = Class.forName(analyzerClassName);
 311  0
             dependencyAnalyzer
 312   
                 = (DependencyAnalyzer) analyzerClass.newInstance();
 313  0
             dependencyAnalyzer.addClassPath(new Path(task.getProject(),
 314   
                 config.srcDir.getPath()));
 315  0
             dependencyAnalyzer.addClassPath(config.classpath);
 316   
         } catch (NoClassDefFoundError e) {
 317  0
             dependencyAnalyzer = null;
 318  0
             task.log("Unable to load dependency analyzer: " + analyzerClassName
 319   
                 + " - dependent class not found: " + e.getMessage(),
 320   
                 Project.MSG_WARN);
 321   
         } catch (Exception e) {
 322  0
             dependencyAnalyzer = null;
 323  0
             task.log("Unable to load dependency analyzer: " + analyzerClassName
 324   
                      + " - exception: " + e.getMessage(),
 325   
                 Project.MSG_WARN);
 326   
         }
 327   
     }
 328   
 
 329   
 
 330   
     /**
 331   
      * Configure this tool for use in the ejbjar task.
 332   
      *
 333   
      * @param config the configuration from the surrounding ejbjar task.
 334   
      */
 335  0
     public void configure(EjbJar.Config config) {
 336  0
         this.config = config;
 337   
 
 338  0
         createAnalyzer();
 339  0
         classpathLoader = null;
 340   
     }
 341   
 
 342   
     /**
 343   
      * Utility method that encapsulates the logic of adding a file entry to
 344   
      * a .jar file.  Used by execute() to add entries to the jar file as it is
 345   
      * constructed.
 346   
      * @param jStream A JarOutputStream into which to write the
 347   
      *        jar entry.
 348   
      * @param inputFile A File from which to read the
 349   
      *        contents the file being added.
 350   
      * @param logicalFilename A String representing the name, including
 351   
      *        all relevant path information, that should be stored for the entry
 352   
      *        being added.
 353   
      */
 354  0
     protected void addFileToJar(JarOutputStream jStream,
 355   
                                 File inputFile,
 356   
                                 String logicalFilename)
 357   
         throws BuildException {
 358  0
         FileInputStream iStream = null;
 359  0
         try {
 360  0
             if (!addedfiles.contains(logicalFilename)) {
 361  0
                 iStream = new FileInputStream(inputFile);
 362   
                 // Create the zip entry and add it to the jar file
 363  0
                 ZipEntry zipEntry = new ZipEntry(logicalFilename.replace('\\', '/'));
 364  0
                 jStream.putNextEntry(zipEntry);
 365   
 
 366   
                 // Create the file input stream, and buffer everything over
 367   
                 // to the jar output stream
 368  0
                 byte[] byteBuffer = new byte[2 * 1024];
 369  0
                 int count = 0;
 370  0
                 do {
 371  0
                     jStream.write(byteBuffer, 0, count);
 372  0
                     count = iStream.read(byteBuffer, 0, byteBuffer.length);
 373  0
                 } while (count != -1);
 374   
 
 375   
                 //add it to list of files in jar
 376  0
                 addedfiles.add(logicalFilename);
 377   
            }
 378   
         } catch (IOException ioe) {
 379  0
             log("WARNING: IOException while adding entry " +
 380   
                 logicalFilename + " to jarfile from " + inputFile.getPath() + " " +
 381   
                 ioe.getClass().getName() + "-" + ioe.getMessage(), Project.MSG_WARN);
 382   
         } finally {
 383   
             // Close up the file input stream for the class file
 384  0
             if (iStream != null) {
 385  0
                 try {
 386  0
                     iStream.close();
 387   
                 } catch (IOException closeException) {}
 388   
             }
 389   
         }
 390   
     }
 391   
 
 392  0
     protected DescriptorHandler getDescriptorHandler(File srcDir) {
 393  0
         DescriptorHandler handler = new DescriptorHandler(getTask(), srcDir);
 394   
 
 395  0
         registerKnownDTDs(handler);
 396   
 
 397   
         // register any DTDs supplied by the user
 398  0
         for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) {
 399  0
             EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next();
 400  0
             handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation());
 401   
         }
 402  0
         return handler;
 403   
     }
 404   
 
 405   
     /**
 406   
      * Register the locations of all known DTDs.
 407   
      *
 408   
      * vendor-specific subclasses should override this method to define
 409   
      * the vendor-specific locations of the EJB DTDs
 410   
      */
 411  0
     protected void registerKnownDTDs(DescriptorHandler handler) {
 412   
         // none to register for generic
 413   
     }
 414   
 
 415  0
     public void processDescriptor(String descriptorFileName, SAXParser saxParser) {
 416   
 
 417  0
         checkConfiguration(descriptorFileName, saxParser);
 418   
 
 419  0
         try {
 420  0
             handler = getDescriptorHandler(config.srcDir);
 421   
 
 422   
             // Retrive the files to be added to JAR from EJB descriptor
 423  0
             Hashtable ejbFiles = parseEjbFiles(descriptorFileName, saxParser);
 424   
 
 425   
             // Add any support classes specified in the build file
 426  0
             addSupportClasses(ejbFiles);
 427   
 
 428   
             // Determine the JAR filename (without filename extension)
 429  0
             String baseName = getJarBaseName(descriptorFileName);
 430   
 
 431  0
             String ddPrefix = getVendorDDPrefix(baseName, descriptorFileName);
 432   
 
 433  0
             File manifestFile = getManifestFile(ddPrefix);
 434  0
             if (manifestFile != null) {
 435  0
                 ejbFiles.put(MANIFEST, manifestFile);
 436   
             }
 437   
 
 438   
 
 439   
 
 440   
             // First the regular deployment descriptor
 441  0
             ejbFiles.put(META_DIR + EJB_DD,
 442   
                          new File(config.descriptorDir, descriptorFileName));
 443   
 
 444   
             // now the vendor specific files, if any
 445  0
             addVendorFiles(ejbFiles, ddPrefix);
 446   
 
 447   
             // add any dependent files
 448  0
             checkAndAddDependants(ejbFiles);
 449   
 
 450   
             // Lastly create File object for the Jar files. If we are using
 451   
             // a flat destination dir, then we need to redefine baseName!
 452  0
             if (config.flatDestDir && baseName.length() != 0) {
 453  0
                 int startName = baseName.lastIndexOf(File.separator);
 454  0
                 if (startName == -1) {
 455  0
                     startName = 0;
 456   
                 }
 457   
 
 458  0
                 int endName   = baseName.length();
 459  0
                 baseName = baseName.substring(startName, endName);
 460   
             }
 461   
 
 462  0
             File jarFile = getVendorOutputJarFile(baseName);
 463   
 
 464   
 
 465   
             // Check to see if we need a build and start doing the work!
 466  0
             if (needToRebuild(ejbFiles, jarFile)) {
 467   
                 // Log that we are going to build...
 468  0
                 log("building "
 469   
                               + jarFile.getName()
 470   
                               + " with "
 471   
                               + String.valueOf(ejbFiles.size())
 472   
                               + " files",
 473   
                               Project.MSG_INFO);
 474   
 
 475   
                 // Use helper method to write the jarfile
 476  0
                 String publicId = getPublicId();
 477  0
                 writeJar(baseName, jarFile, ejbFiles, publicId);
 478   
 
 479   
             } else {
 480   
                 // Log that the file is up to date...
 481  0
                 log(jarFile.toString() + " is up to date.",
 482   
                               Project.MSG_VERBOSE);
 483   
             }
 484   
 
 485   
         } catch (SAXException se) {
 486  0
             String msg = "SAXException while parsing '"
 487   
                 + descriptorFileName.toString()
 488   
                 + "'. This probably indicates badly-formed XML."
 489   
                 + "  Details: "
 490   
                 + se.getMessage();
 491  0
             throw new BuildException(msg, se);
 492   
         } catch (IOException ioe) {
 493  0
             String msg = "IOException while parsing'"
 494   
                 + descriptorFileName.toString()
 495   
                 + "'.  This probably indicates that the descriptor"
 496   
                 + " doesn't exist. Details: "
 497   
                 + ioe.getMessage();
 498  0
             throw new BuildException(msg, ioe);
 499   
         }
 500   
     }
 501   
 
 502   
     /**
 503   
      * This method is called as the first step in the processDescriptor method
 504   
      * to allow vendor-specific subclasses to validate the task configuration
 505   
      * prior to processing the descriptor.  If the configuration is invalid,
 506   
      * a BuildException should be thrown.
 507   
      *
 508   
      * @param descriptorFileName String representing the file name of an EJB
 509   
      *                           descriptor to be processed
 510   
      * @param saxParser          SAXParser which may be used to parse the XML
 511   
      *                           descriptor
 512   
      * @exception BuildException     Thrown if the configuration is invalid
 513   
      */
 514  0
     protected void checkConfiguration(String descriptorFileName,
 515   
                                     SAXParser saxParser) throws BuildException {
 516   
 
 517   
         /*
 518   
          * For the GenericDeploymentTool, do nothing.  Vendor specific
 519   
          * subclasses should throw a BuildException if the configuration is
 520   
          * invalid for their server.
 521   
          */
 522   
     }
 523   
 
 524   
     /**
 525   
      * This method returns a list of EJB files found when the specified EJB
 526   
      * descriptor is parsed and processed.
 527   
      *
 528   
      * @param descriptorFileName String representing the file name of an EJB
 529   
      *                           descriptor to be processed
 530   
      * @param saxParser          SAXParser which may be used to parse the XML
 531   
      *                           descriptor
 532   
      * @return                   Hashtable of EJB class (and other) files to be
 533   
      *                           added to the completed JAR file
 534   
      * @throws SAXException      Any SAX exception, possibly wrapping another
 535   
      *                           exception
 536   
      * @throws IOException       An IOException from the parser, possibly from a
 537   
      *                           the byte stream or character stream
 538   
      */
 539  0
     protected Hashtable parseEjbFiles(String descriptorFileName, SAXParser saxParser)
 540   
                             throws IOException, SAXException {
 541  0
         FileInputStream descriptorStream = null;
 542  0
         Hashtable ejbFiles = null;
 543   
 
 544  0
         try {
 545   
 
 546   
             /* Parse the ejb deployment descriptor.  While it may not
 547   
              * look like much, we use a SAXParser and an inner class to
 548   
              * get hold of all the classfile names for the descriptor.
 549   
              */
 550  0
             descriptorStream = new FileInputStream(new File(config.descriptorDir, descriptorFileName));
 551  0
             saxParser.parse(new InputSource(descriptorStream), handler);
 552   
 
 553  0
             ejbFiles = handler.getFiles();
 554   
 
 555   
         } finally {
 556  0
             if (descriptorStream != null) {
 557  0
                 try {
 558  0
                     descriptorStream.close();
 559   
                 } catch (IOException closeException) {}
 560   
             }
 561   
         }
 562   
 
 563  0
         return ejbFiles;
 564   
     }
 565   
 
 566   
     /**
 567   
      * Adds any classes the user specifies using <i>support</i> nested elements
 568   
      * to the <code>ejbFiles</code> Hashtable.
 569   
      *
 570   
      * @param ejbFiles Hashtable of EJB classes (and other) files that will be
 571   
      *                 added to the completed JAR file
 572   
      */
 573  0
     protected void addSupportClasses(Hashtable ejbFiles) {
 574   
         // add in support classes if any
 575  0
         Project project = task.getProject();
 576  0
         for (Iterator i = config.supportFileSets.iterator(); i.hasNext();) {
 577  0
             FileSet supportFileSet = (FileSet) i.next();
 578  0
             File supportBaseDir = supportFileSet.getDir(project);
 579  0
             DirectoryScanner supportScanner = supportFileSet.getDirectoryScanner(project);
 580  0
             supportScanner.scan();
 581  0
             String[] supportFiles = supportScanner.getIncludedFiles();
 582  0
             for (int j = 0; j < supportFiles.length; ++j) {
 583  0
                 ejbFiles.put(supportFiles[j], new File(supportBaseDir, supportFiles[j]));
 584   
             }
 585   
         }
 586   
     }
 587   
 
 588   
 
 589   
     /**
 590   
      * Using the EJB descriptor file name passed from the <code>ejbjar</code>
 591   
      * task, this method returns the "basename" which will be used to name the
 592   
      * completed JAR file.
 593   
      *
 594   
      * @param descriptorFileName String representing the file name of an EJB
 595   
      *                           descriptor to be processed
 596   
      * @return                   The "basename" which will be used to name the
 597   
      *                           completed JAR file
 598   
      */
 599  0
     protected String getJarBaseName(String descriptorFileName) {
 600   
 
 601  0
         String baseName = "";
 602   
 
 603   
         // Work out what the base name is
 604  0
         if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.BASEJARNAME)) {
 605  0
             String canonicalDescriptor = descriptorFileName.replace('\\', '/');
 606  0
             int index = canonicalDescriptor.lastIndexOf('/');
 607  0
             if (index != -1) {
 608  0
                 baseName = descriptorFileName.substring(0, index + 1);
 609   
             }
 610  0
             baseName += config.baseJarName;
 611  0
         } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
 612  0
             int lastSeparatorIndex = descriptorFileName.lastIndexOf(File.separator);
 613  0
             int endBaseName = -1;
 614  0
             if (lastSeparatorIndex != -1) {
 615  0
                 endBaseName = descriptorFileName.indexOf(config.baseNameTerminator,
 616   
                                                             lastSeparatorIndex);
 617   
             } else {
 618  0
                 endBaseName = descriptorFileName.indexOf(config.baseNameTerminator);
 619   
             }
 620   
 
 621  0
             if (endBaseName != -1) {
 622  0
                 baseName = descriptorFileName.substring(0, endBaseName);
 623   
             } else {
 624  0
                 throw new BuildException("Unable to determine jar name "
 625   
                     + "from descriptor \"" + descriptorFileName + "\"");
 626   
             }
 627  0
         } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DIRECTORY)) {
 628  0
             File descriptorFile = new File(config.descriptorDir, descriptorFileName);
 629  0
             String path = descriptorFile.getAbsolutePath();
 630  0
             int lastSeparatorIndex
 631   
                 = path.lastIndexOf(File.separator);
 632  0
             if (lastSeparatorIndex == -1) {
 633  0
                 throw new BuildException("Unable to determine directory name holding descriptor");
 634   
             }
 635  0
             String dirName = path.substring(0, lastSeparatorIndex);
 636  0
             int dirSeparatorIndex = dirName.lastIndexOf(File.separator);
 637  0
             if (dirSeparatorIndex != -1) {
 638  0
                 dirName = dirName.substring(dirSeparatorIndex + 1);
 639   
             }
 640   
 
 641  0
             baseName = dirName;
 642  0
         } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.EJB_NAME)) {
 643  0
             baseName = handler.getEjbName();
 644   
         }
 645  0
         return baseName;
 646   
     }
 647   
 
 648   
     /**
 649   
      * Get the prefix for vendor deployment descriptors.
 650   
      *
 651   
      * This will contain the path and the start of the descriptor name,
 652   
      * depending on the naming scheme
 653   
      */
 654  0
     public String getVendorDDPrefix(String baseName, String descriptorFileName) {
 655  0
         String ddPrefix = null;
 656   
 
 657  0
         if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
 658  0
             ddPrefix = baseName + config.baseNameTerminator;
 659  0
         } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.BASEJARNAME) ||
 660   
                    config.namingScheme.getValue().equals(EjbJar.NamingScheme.EJB_NAME) ||
 661   
                    config.namingScheme.getValue().equals(EjbJar.NamingScheme.DIRECTORY)) {
 662  0
             String canonicalDescriptor = descriptorFileName.replace('\\', '/');
 663  0
             int index = canonicalDescriptor.lastIndexOf('/');
 664  0
             if (index == -1) {
 665  0
                 ddPrefix = "";
 666   
             } else {
 667  0
                 ddPrefix = descriptorFileName.substring(0, index + 1);
 668   
             }
 669   
         }
 670  0
         return ddPrefix;
 671   
     }
 672   
 
 673   
     /**
 674   
      * Add any vendor specific files which should be included in the
 675   
      * EJB Jar.
 676   
      */
 677  0
     protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
 678   
         // nothing to add for generic tool.
 679   
     }
 680   
 
 681   
 
 682   
     /**
 683   
      * Get the vendor specific name of the Jar that will be output. The modification date
 684   
      * of this jar will be checked against the dependent bean classes.
 685   
      */
 686  0
     File getVendorOutputJarFile(String baseName) {
 687  0
         return new File(destDir, baseName + genericJarSuffix);
 688   
     }
 689   
 
 690   
     /**
 691   
      * This method checks the timestamp on each file listed in the <code>
 692   
      * ejbFiles</code> and compares them to the timestamp on the <code>jarFile
 693   
      * </code>.  If the <code>jarFile</code>'s timestamp is more recent than
 694   
      * each EJB file, <code>true</code> is returned.  Otherwise, <code>false
 695   
      * </code> is returned.
 696   
      * TODO: find a way to check the manifest-file, that is found by naming convention
 697   
      *
 698   
      * @param ejbFiles Hashtable of EJB classes (and other) files that will be
 699   
      *                 added to the completed JAR file
 700   
      * @param jarFile  JAR file which will contain all of the EJB classes (and
 701   
      *                 other) files
 702   
      * @return         boolean indicating whether or not the <code>jarFile</code>
 703   
      *                 is up to date
 704   
      */
 705  0
     protected boolean needToRebuild(Hashtable ejbFiles, File jarFile) {
 706  0
         if (jarFile.exists()) {
 707  0
             long lastBuild = jarFile.lastModified();
 708   
 
 709  0
             Iterator fileIter = ejbFiles.values().iterator();
 710   
 
 711   
             // Loop through the files seeing if any has been touched
 712   
             // more recently than the destination jar.
 713  0
             while (fileIter.hasNext()) {
 714  0
                 File currentFile = (File) fileIter.next();
 715  0
                 if (lastBuild < currentFile.lastModified()) {
 716  0
                     log("Build needed because " + currentFile.getPath() + " is out of date",
 717   
                         Project.MSG_VERBOSE);
 718  0
                     return true;
 719   
                 }
 720   
             }
 721  0
             return false;
 722   
         }
 723   
 
 724  0
         return true;
 725   
     }
 726   
 
 727   
     /**
 728   
      * Returns the Public ID of the DTD specified in the EJB descriptor.  Not
 729   
      * every vendor-specific <code>DeploymentTool</code> will need to reference
 730   
      * this value or may want to determine this value in a vendor-specific way.
 731   
      *
 732   
      * @return Public ID of the DTD specified in the EJB descriptor.
 733   
      */
 734  0
     protected String getPublicId() {
 735  0
         return handler.getPublicId();
 736   
     }
 737   
 
 738   
     /**
 739   
      * Get the manifets file to use for building the generic jar.
 740   
      *
 741   
      * If the file does not exist the global manifest from the config is used
 742   
      * otherwise the default Ant manifest will be used.
 743   
      *
 744   
      * @param prefix the prefix where to llook for the manifest file based on
 745   
      *        the naming convention.
 746   
      *
 747   
      * @return the manifest file or null if the manifest file does not exist
 748   
      */
 749  0
     protected File getManifestFile(String prefix) {
 750  0
         File manifestFile
 751   
             = new File(getConfig().descriptorDir, prefix + "manifest.mf");
 752  0
         if (manifestFile.exists()) {
 753  0
             return manifestFile;
 754   
         }
 755   
 
 756  0
         if (config.manifest != null) {
 757  0
             return config.manifest;
 758   
         }
 759  0
         return null;
 760   
     }
 761   
 
 762   
     /**
 763   
      * Method used to encapsulate the writing of the JAR file. Iterates over the
 764   
      * filenames/java.io.Files in the Hashtable stored on the instance variable
 765   
      * ejbFiles.
 766   
      */
 767  0
     protected void writeJar(String baseName, File jarfile, Hashtable files,
 768   
                             String publicId) throws BuildException{
 769   
 
 770  0
         JarOutputStream jarStream = null;
 771  0
         try {
 772   
             // clean the addedfiles Vector
 773  0
             addedfiles = new ArrayList();
 774   
 
 775   
             /* If the jarfile already exists then whack it and recreate it.
 776   
              * Should probably think of a more elegant way to handle this
 777   
              * so that in case of errors we don't leave people worse off
 778   
              * than when we started =)
 779   
              */
 780  0
             if (jarfile.exists()) {
 781  0
                 jarfile.delete();
 782   
             }
 783  0
             jarfile.getParentFile().mkdirs();
 784  0
             jarfile.createNewFile();
 785   
 
 786  0
             InputStream in = null;
 787  0
             Manifest manifest = null;
 788  0
             try {
 789  0
                 File manifestFile = (File) files.get(MANIFEST);
 790  0
                 if (manifestFile != null && manifestFile.exists()) {
 791  0
                     in = new FileInputStream(manifestFile);
 792   
                 } else {
 793  0
                     String defaultManifest = "/org/apache/tools/ant/defaultManifest.mf";
 794  0
                     in = this.getClass().getResourceAsStream(defaultManifest);
 795  0
                     if (in == null) {
 796  0
                         throw new BuildException("Could not find default manifest: " + defaultManifest,
 797   
                                                   getLocation());
 798   
                     }
 799   
                 }
 800   
 
 801  0
                 manifest = new Manifest(in);
 802   
             } catch (IOException e) {
 803  0
                 throw new BuildException ("Unable to read manifest", e, getLocation());
 804   
             } finally {
 805  0
                 if (in != null) {
 806  0
                     in.close();
 807   
                 }
 808   
             }
 809   
 
 810   
             // Create the streams necessary to write the jarfile
 811   
 
 812  0
             jarStream = new JarOutputStream(new FileOutputStream(jarfile), manifest);
 813  0
             jarStream.setMethod(JarOutputStream.DEFLATED);
 814   
 
 815   
             // Loop through all the class files found and add them to the jar
 816  0
             for (Iterator entryIterator = files.keySet().iterator(); entryIterator.hasNext();) {
 817  0
                 String entryName = (String) entryIterator.next();
 818  0
                 if (entryName.equals(MANIFEST)) {
 819  0
                     continue;
 820   
                 }
 821   
 
 822  0
                 File entryFile = (File) files.get(entryName);
 823   
 
 824  0
                 log("adding file '" + entryName + "'",
 825   
                               Project.MSG_VERBOSE);
 826   
 
 827  0
                 addFileToJar(jarStream, entryFile, entryName);
 828   
 
 829   
                 // See if there are any inner classes for this class and add them in if there are
 830  0
                 InnerClassFilenameFilter flt = new InnerClassFilenameFilter(entryFile.getName());
 831  0
                 File entryDir = entryFile.getParentFile();
 832  0
                 String[] innerfiles = entryDir.list(flt);
 833  0
                 if (innerfiles != null) {
 834  0
                     for (int i = 0, n = innerfiles.length; i < n; i++) {
 835   
 
 836   
                         //get and clean up innerclass name
 837  0
                         int entryIndex = entryName.lastIndexOf(entryFile.getName()) - 1;
 838  0
                         if (entryIndex < 0) {
 839  0
                             entryName = innerfiles[i];
 840   
                         } else {
 841  0
                             entryName = entryName.substring(0, entryIndex) + File.separatorChar + innerfiles[i];
 842   
                         }
 843   
                         // link the file
 844  0
                         entryFile = new File(config.srcDir, entryName);
 845   
 
 846  0
                         log("adding innerclass file '" + entryName + "'",
 847   
                                 Project.MSG_VERBOSE);
 848   
 
 849  0
                         addFileToJar(jarStream, entryFile, entryName);
 850   
 
 851   
                     }
 852   
                 }
 853   
             }
 854   
         } catch (IOException ioe) {
 855  0
             String msg = "IOException while processing ejb-jar file '"
 856   
                 + jarfile.toString()
 857   
                 + "'. Details: "
 858   
                 + ioe.getMessage();
 859  0
             throw new BuildException(msg, ioe);
 860   
         } finally {
 861  0
             if (jarStream != null) {
 862  0
                 try {
 863  0
                     jarStream.close();
 864   
                 } catch (IOException closeException) {}
 865   
             }
 866   
         }
 867   
     } // end of writeJar
 868   
 
 869   
 
 870   
     /**
 871   
      * Add all available classes, that depend on Remote, Home, Bean, PK
 872   
      * @param checkEntries files, that are extracted from the deployment descriptor
 873   
      */
 874  0
     protected void checkAndAddDependants(Hashtable checkEntries)
 875   
         throws BuildException {
 876   
 
 877  0
         if (dependencyAnalyzer == null) {
 878  0
             return;
 879   
         }
 880   
 
 881  0
         dependencyAnalyzer.reset();
 882   
 
 883  0
         Iterator i = checkEntries.keySet().iterator();
 884  0
         while (i.hasNext()) {
 885  0
             String entryName = (String) i.next();
 886  0
             if (entryName.endsWith(".class")) {
 887  0
                 String className = entryName.substring(0,
 888   
                     entryName.length() - ".class".length());
 889  0
                 className = className.replace(File.separatorChar, '/');
 890  0
                 className = className.replace('/', '.');
 891   
 
 892  0
                 dependencyAnalyzer.addRootClass(className);
 893   
             }
 894   
         }
 895   
 
 896  0
         Enumeration e = dependencyAnalyzer.getClassDependencies();
 897   
 
 898  0
         while (e.hasMoreElements()) {
 899  0
             String classname = (String) e.nextElement();
 900  0
             String location
 901   
                 = classname.replace('.', File.separatorChar) + ".class";
 902  0
             File classFile = new File(config.srcDir, location);
 903  0
             if (classFile.exists()) {
 904  0
                 checkEntries.put(location, classFile);
 905  0
                 log("dependent class: " + classname + " - " + classFile,
 906   
                     Project.MSG_VERBOSE);
 907   
             }
 908   
         }
 909   
     }
 910   
 
 911   
 
 912   
     /**
 913   
      * Returns a Classloader object which parses the passed in generic EjbJar classpath.
 914   
      * The loader is used to dynamically load classes from javax.ejb.* and the classes
 915   
      * being added to the jar.
 916   
      *
 917   
      */
 918  0
     protected ClassLoader getClassLoaderForBuild() {
 919  0
         if (classpathLoader != null) {
 920  0
             return classpathLoader;
 921   
         }
 922   
 
 923  0
         Path combinedClasspath = getCombinedClasspath();
 924   
 
 925   
         // only generate a new ClassLoader if we have a classpath
 926  0
         if (combinedClasspath == null) {
 927  0
             classpathLoader = getClass().getClassLoader();
 928   
         } else {
 929  0
             classpathLoader
 930   
                 = getTask().getProject().createClassLoader(combinedClasspath);
 931   
         }
 932   
 
 933  0
         return classpathLoader;
 934   
     }
 935   
 
 936   
     /**
 937   
      * Called to validate that the tool parameters have been configured.
 938   
      *
 939   
      * @throws BuildException If the Deployment Tool's configuration isn't
 940   
      *                        valid
 941   
      */
 942  0
     public void validateConfigured() throws BuildException {
 943  0
         if ((destDir == null) || (!destDir.isDirectory())) {
 944  0
             String msg = "A valid destination directory must be specified "
 945   
                             + "using the \"destdir\" attribute.";
 946  0
             throw new BuildException(msg, getLocation());
 947   
         }
 948   
     }
 949   
 }
 950