KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > optional > ejb > GenericDeploymentTool


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18 package org.apache.tools.ant.taskdefs.optional.ejb;
19
20 import java.io.File JavaDoc;
21 import java.io.FileInputStream JavaDoc;
22 import java.io.FileOutputStream JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.InputStream JavaDoc;
25 import java.util.Enumeration JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.Hashtable JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.Set JavaDoc;
30 import java.util.jar.JarOutputStream JavaDoc;
31 import java.util.jar.Manifest JavaDoc;
32 import java.util.zip.ZipEntry JavaDoc;
33
34 import javax.xml.parsers.SAXParser JavaDoc;
35
36 import org.apache.tools.ant.BuildException;
37 import org.apache.tools.ant.DirectoryScanner;
38 import org.apache.tools.ant.Location;
39 import org.apache.tools.ant.Project;
40 import org.apache.tools.ant.Task;
41 import org.apache.tools.ant.types.FileSet;
42 import org.apache.tools.ant.types.Path;
43 import org.apache.tools.ant.util.depend.DependencyAnalyzer;
44 import org.xml.sax.InputSource JavaDoc;
45 import org.xml.sax.SAXException JavaDoc;
46
47
48 /**
49  * A deployment tool which creates generic EJB jars. Generic jars contains
50  * only those classes and META-INF entries specified in the EJB 1.1 standard
51  *
52  * This class is also used as a framework for the creation of vendor specific
53  * deployment tools. A number of template methods are provided through which the
54  * vendor specific tool can hook into the EJB creation process.
55  *
56  */

57 public class GenericDeploymentTool implements EJBDeploymentTool {
58     /** The default buffer byte size to use for IO */
59     public static final int DEFAULT_BUFFER_SIZE = 1024;
60     /** The level to use for compression */
61     public static final int JAR_COMPRESS_LEVEL = 9;
62
63     /** The standard META-INF directory in jar files */
64     protected static final String JavaDoc META_DIR = "META-INF/";
65
66     /** The standard MANIFEST file */
67     protected static final String JavaDoc MANIFEST = META_DIR + "MANIFEST.MF";
68
69     /** Name for EJB Deployment descriptor within EJB jars */
70     protected static final String JavaDoc EJB_DD = "ejb-jar.xml";
71
72     /** A dependency analyzer name to find ancestor classes */
73     public static final String JavaDoc ANALYZER_SUPER = "super";
74     /** A dependency analyzer name to find all related classes */
75     public static final String JavaDoc ANALYZER_FULL = "full";
76     /** A dependency analyzer name for no analyzer */
77     public static final String JavaDoc ANALYZER_NONE = "none";
78
79     /** The default analyzer */
80     public static final String JavaDoc DEFAULT_ANALYZER = ANALYZER_SUPER;
81
82     /** The analyzer class for the super analyzer */
83     public static final String JavaDoc ANALYZER_CLASS_SUPER
84         = "org.apache.tools.ant.util.depend.bcel.AncestorAnalyzer";
85     /** The analyzer class for the super analyzer */
86     public static final String JavaDoc ANALYZER_CLASS_FULL
87         = "org.apache.tools.ant.util.depend.bcel.FullAnalyzer";
88
89     /**
90      * The configuration from the containing task. This config combined
91      * with the settings of the individual attributes here constitues the
92      * complete config for this deployment tool.
93      */

94     private EjbJar.Config config;
95
96     /** Stores a handle to the directory to put the Jar files in */
97     private File JavaDoc destDir;
98
99     /** The classpath to use with this deployment tool. This is appended to
100         any paths from the ejbjar task itself.*/

101     private Path classpath;
102
103     /** Instance variable that stores the suffix for the generated jarfile. */
104     private String JavaDoc genericJarSuffix = "-generic.jar";
105
106     /**
107      * The task to which this tool belongs. This is used to access services
108      * provided by the ant core, such as logging.
109      */

110     private Task task;
111
112     /**
113      * The classloader generated from the given classpath to load
114      * the super classes and super interfaces.
115      */

116     private ClassLoader JavaDoc classpathLoader = null;
117
118      /**
119      * Set of files have been loaded into the EJB jar
120      */

121     private Set JavaDoc addedfiles;
122
123     /**
124      * Handler used to parse the EJB XML descriptor
125      */

126     private DescriptorHandler handler;
127
128     /**
129      * Dependency analyzer used to collect class dependencies
130      */

131     private DependencyAnalyzer dependencyAnalyzer;
132
133     /** No arg constructor */
134     public GenericDeploymentTool() {
135     }
136
137
138     /**
139      * Set the destination directory; required.
140      * @param inDir the destination directory.
141      */

142     public void setDestdir(File JavaDoc inDir) {
143         this.destDir = inDir;
144     }
145
146     /**
147      * Get the destination directory.
148      *
149      * @return the destination directory into which EJB jars are to be written
150      */

151     protected File JavaDoc getDestDir() {
152         return destDir;
153     }
154
155
156     /**
157      * Set the task which owns this tool
158      *
159      * @param task the Task to which this deployment tool is associated.
160      */

161     public void setTask(Task task) {
162         this.task = task;
163     }
164
165     /**
166      * Get the task for this tool.
167      *
168      * @return the Task instance this tool is associated with.
169      */

170     protected Task getTask() {
171         return task;
172     }
173
174     /**
175      * Get the basename terminator.
176      *
177      * @return an ejbjar task configuration
178      */

179     protected EjbJar.Config getConfig() {
180         return config;
181     }
182
183     /**
184      * Indicate if this build is using the base jar name.
185      *
186      * @return true if the name of the generated jar is coming from the
187      * basejarname attribute
188      */

189     protected boolean usingBaseJarName() {
190         return config.baseJarName != null;
191     }
192
193     /**
194      * Set the suffix for the generated jar file.
195      * @param inString the string to use as the suffix.
196      */

197     public void setGenericJarSuffix(String JavaDoc inString) {
198         this.genericJarSuffix = inString;
199     }
200
201     /**
202      * Add the classpath for the user classes
203      *
204      * @return a Path instance to be configured by Ant.
205      */

206     public Path createClasspath() {
207         if (classpath == null) {
208             classpath = new Path(task.getProject());
209         }
210         return classpath.createPath();
211     }
212
213     /**
214      * Set the classpath to be used for this compilation.
215      *
216      * @param classpath the classpath to be used for this build.
217      */

218     public void setClasspath(Path classpath) {
219         this.classpath = classpath;
220     }
221
222     /**
223      * Get the classpath by combining the one from the surrounding task, if any
224      * and the one from this tool.
225      *
226      * @return the combined classpath
227      */

228     protected Path getCombinedClasspath() {
229         Path combinedPath = classpath;
230         if (config.classpath != null) {
231             if (combinedPath == null) {
232                 combinedPath = config.classpath;
233             } else {
234                 combinedPath.append(config.classpath);
235             }
236         }
237
238         return combinedPath;
239     }
240
241     /**
242      * Log a message to the Ant output.
243      *
244      * @param message the message to be logged.
245      * @param level the severity of this message.
246      */

247     protected void log(String JavaDoc message, int level) {
248         getTask().log(message, level);
249     }
250
251     /**
252      * Get the build file location associated with this element's task.
253      *
254      * @return the task's location instance.
255      */

256     protected Location getLocation() {
257         return getTask().getLocation();
258     }
259
260     private void createAnalyzer() {
261         String JavaDoc analyzer = config.analyzer;
262         if (analyzer == null) {
263             analyzer = DEFAULT_ANALYZER;
264         }
265
266         if (analyzer.equals(ANALYZER_NONE)) {
267             return;
268         }
269
270         String JavaDoc analyzerClassName = null;
271         if (analyzer.equals(ANALYZER_SUPER)) {
272             analyzerClassName = ANALYZER_CLASS_SUPER;
273         } else if (analyzer.equals(ANALYZER_FULL)) {
274             analyzerClassName = ANALYZER_CLASS_FULL;
275         } else {
276             analyzerClassName = analyzer;
277         }
278
279         try {
280             Class JavaDoc analyzerClass = Class.forName(analyzerClassName);
281             dependencyAnalyzer
282                 = (DependencyAnalyzer) analyzerClass.newInstance();
283             dependencyAnalyzer.addClassPath(new Path(task.getProject(),
284                 config.srcDir.getPath()));
285             dependencyAnalyzer.addClassPath(config.classpath);
286         } catch (NoClassDefFoundError JavaDoc e) {
287             dependencyAnalyzer = null;
288             task.log("Unable to load dependency analyzer: " + analyzerClassName
289                 + " - dependent class not found: " + e.getMessage(),
290                 Project.MSG_WARN);
291         } catch (Exception JavaDoc e) {
292             dependencyAnalyzer = null;
293             task.log("Unable to load dependency analyzer: " + analyzerClassName
294                      + " - exception: " + e.getMessage(),
295                 Project.MSG_WARN);
296         }
297     }
298
299
300     /**
301      * Configure this tool for use in the ejbjar task.
302      *
303      * @param config the configuration from the surrounding ejbjar task.
304      */

305     public void configure(EjbJar.Config config) {
306         this.config = config;
307
308         createAnalyzer();
309         classpathLoader = null;
310     }
311
312     /**
313      * Utility method that encapsulates the logic of adding a file entry to
314      * a .jar file. Used by execute() to add entries to the jar file as it is
315      * constructed.
316      * @param jStream A JarOutputStream into which to write the
317      * jar entry.
318      * @param inputFile A File from which to read the
319      * contents the file being added.
320      * @param logicalFilename A String representing the name, including
321      * all relevant path information, that should be stored for the entry
322      * being added.
323      * @throws BuildException if there is a problem.
324      */

325     protected void addFileToJar(JarOutputStream JavaDoc jStream,
326                                 File JavaDoc inputFile,
327                                 String JavaDoc logicalFilename)
328         throws BuildException {
329         FileInputStream JavaDoc iStream = null;
330         try {
331             if (!addedfiles.contains(logicalFilename)) {
332                 iStream = new FileInputStream JavaDoc(inputFile);
333                 // Create the zip entry and add it to the jar file
334
ZipEntry JavaDoc zipEntry = new ZipEntry JavaDoc(logicalFilename.replace('\\', '/'));
335                 jStream.putNextEntry(zipEntry);
336
337                 // Create the file input stream, and buffer everything over
338
// to the jar output stream
339
byte[] byteBuffer = new byte[2 * DEFAULT_BUFFER_SIZE];
340                 int count = 0;
341                 do {
342                     jStream.write(byteBuffer, 0, count);
343                     count = iStream.read(byteBuffer, 0, byteBuffer.length);
344                 } while (count != -1);
345
346                 //add it to list of files in jar
347
addedfiles.add(logicalFilename);
348            }
349         } catch (IOException JavaDoc ioe) {
350             log("WARNING: IOException while adding entry "
351                 + logicalFilename + " to jarfile from "
352                 + inputFile.getPath() + " " + ioe.getClass().getName()
353                 + "-" + ioe.getMessage(), Project.MSG_WARN);
354         } finally {
355             // Close up the file input stream for the class file
356
if (iStream != null) {
357                 try {
358                     iStream.close();
359                 } catch (IOException JavaDoc closeException) {
360                     // ignore
361
}
362             }
363         }
364     }
365
366     /**
367      * Get a descriptionHandler.
368      * @param srcDir the source directory.
369      * @return a handler.
370      */

371     protected DescriptorHandler getDescriptorHandler(File JavaDoc srcDir) {
372         DescriptorHandler h = new DescriptorHandler(getTask(), srcDir);
373
374         registerKnownDTDs(h);
375
376         // register any DTDs supplied by the user
377
for (Iterator JavaDoc i = getConfig().dtdLocations.iterator(); i.hasNext();) {
378             EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next();
379             h.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation());
380         }
381         return h;
382     }
383
384     /**
385      * Register the locations of all known DTDs.
386      *
387      * vendor-specific subclasses should override this method to define
388      * the vendor-specific locations of the EJB DTDs
389      * @param handler no used in this class.
390      */

391     protected void registerKnownDTDs(DescriptorHandler handler) {
392         // none to register for generic
393
}
394
395     /** {@inheritDoc}. */
396     public void processDescriptor(String JavaDoc descriptorFileName, SAXParser JavaDoc saxParser) {
397
398         checkConfiguration(descriptorFileName, saxParser);
399
400         try {
401             handler = getDescriptorHandler(config.srcDir);
402
403             // Retrive the files to be added to JAR from EJB descriptor
404
Hashtable JavaDoc ejbFiles = parseEjbFiles(descriptorFileName, saxParser);
405
406             // Add any support classes specified in the build file
407
addSupportClasses(ejbFiles);
408
409             // Determine the JAR filename (without filename extension)
410
String JavaDoc baseName = getJarBaseName(descriptorFileName);
411
412             String JavaDoc ddPrefix = getVendorDDPrefix(baseName, descriptorFileName);
413
414             File JavaDoc manifestFile = getManifestFile(ddPrefix);
415             if (manifestFile != null) {
416                 ejbFiles.put(MANIFEST, manifestFile);
417             }
418
419
420
421             // First the regular deployment descriptor
422
ejbFiles.put(META_DIR + EJB_DD,
423                          new File JavaDoc(config.descriptorDir, descriptorFileName));
424
425             // now the vendor specific files, if any
426
addVendorFiles(ejbFiles, ddPrefix);
427
428             // add any dependent files
429
checkAndAddDependants(ejbFiles);
430
431             // Lastly create File object for the Jar files. If we are using
432
// a flat destination dir, then we need to redefine baseName!
433
if (config.flatDestDir && baseName.length() != 0) {
434                 int startName = baseName.lastIndexOf(File.separator);
435                 if (startName == -1) {
436                     startName = 0;
437                 }
438
439                 int endName = baseName.length();
440                 baseName = baseName.substring(startName, endName);
441             }
442
443             File JavaDoc jarFile = getVendorOutputJarFile(baseName);
444
445
446             // Check to see if we need a build and start doing the work!
447
if (needToRebuild(ejbFiles, jarFile)) {
448                 // Log that we are going to build...
449
log("building "
450                               + jarFile.getName()
451                               + " with "
452                               + String.valueOf(ejbFiles.size())
453                               + " files",
454                               Project.MSG_INFO);
455
456                 // Use helper method to write the jarfile
457
String JavaDoc publicId = getPublicId();
458                 writeJar(baseName, jarFile, ejbFiles, publicId);
459
460             } else {
461                 // Log that the file is up to date...
462
log(jarFile.toString() + " is up to date.",
463                               Project.MSG_VERBOSE);
464             }
465
466         } catch (SAXException JavaDoc se) {
467             String JavaDoc msg = "SAXException while parsing '"
468                 + descriptorFileName
469                 + "'. This probably indicates badly-formed XML."
470                 + " Details: "
471                 + se.getMessage();
472             throw new BuildException(msg, se);
473         } catch (IOException JavaDoc ioe) {
474             String JavaDoc msg = "IOException while parsing'"
475                 + descriptorFileName.toString()
476                 + "'. This probably indicates that the descriptor"
477                 + " doesn't exist. Details: "
478                 + ioe.getMessage();
479             throw new BuildException(msg, ioe);
480         }
481     }
482
483     /**
484      * This method is called as the first step in the processDescriptor method
485      * to allow vendor-specific subclasses to validate the task configuration
486      * prior to processing the descriptor. If the configuration is invalid,
487      * a BuildException should be thrown.
488      *
489      * @param descriptorFileName String representing the file name of an EJB
490      * descriptor to be processed
491      * @param saxParser SAXParser which may be used to parse the XML
492      * descriptor
493      * @throws BuildException if there is a problem.
494      */

495     protected void checkConfiguration(String JavaDoc descriptorFileName,
496                                     SAXParser JavaDoc saxParser) throws BuildException {
497
498         /*
499          * For the GenericDeploymentTool, do nothing. Vendor specific
500          * subclasses should throw a BuildException if the configuration is
501          * invalid for their server.
502          */

503     }
504
505     /**
506      * This method returns a list of EJB files found when the specified EJB
507      * descriptor is parsed and processed.
508      *
509      * @param descriptorFileName String representing the file name of an EJB
510      * descriptor to be processed
511      * @param saxParser SAXParser which may be used to parse the XML
512      * descriptor
513      * @return Hashtable of EJB class (and other) files to be
514      * added to the completed JAR file
515      * @throws SAXException Any SAX exception, possibly wrapping another
516      * exception
517      * @throws IOException An IOException from the parser, possibly from a
518      * the byte stream or character stream
519      */

520     protected Hashtable JavaDoc parseEjbFiles(String JavaDoc descriptorFileName, SAXParser JavaDoc saxParser)
521                             throws IOException JavaDoc, SAXException JavaDoc {
522         FileInputStream JavaDoc descriptorStream = null;
523         Hashtable JavaDoc ejbFiles = null;
524
525         try {
526
527             /* Parse the ejb deployment descriptor. While it may not
528              * look like much, we use a SAXParser and an inner class to
529              * get hold of all the classfile names for the descriptor.
530              */

531             descriptorStream
532                 = new FileInputStream JavaDoc(new File JavaDoc(config.descriptorDir, descriptorFileName));
533             saxParser.parse(new InputSource JavaDoc(descriptorStream), handler);
534
535             ejbFiles = handler.getFiles();
536
537         } finally {
538             if (descriptorStream != null) {
539                 try {
540                     descriptorStream.close();
541                 } catch (IOException JavaDoc closeException) {
542                     // ignore
543
}
544             }
545         }
546
547         return ejbFiles;
548     }
549
550     /**
551      * Adds any classes the user specifies using <i>support</i> nested elements
552      * to the <code>ejbFiles</code> Hashtable.
553      *
554      * @param ejbFiles Hashtable of EJB classes (and other) files that will be
555      * added to the completed JAR file
556      */

557     protected void addSupportClasses(Hashtable JavaDoc ejbFiles) {
558         // add in support classes if any
559
Project project = task.getProject();
560         for (Iterator JavaDoc i = config.supportFileSets.iterator(); i.hasNext();) {
561             FileSet supportFileSet = (FileSet) i.next();
562             File JavaDoc supportBaseDir = supportFileSet.getDir(project);
563             DirectoryScanner supportScanner = supportFileSet.getDirectoryScanner(project);
564             supportScanner.scan();
565             String JavaDoc[] supportFiles = supportScanner.getIncludedFiles();
566             for (int j = 0; j < supportFiles.length; ++j) {
567                 ejbFiles.put(supportFiles[j], new File JavaDoc(supportBaseDir, supportFiles[j]));
568             }
569         }
570     }
571
572
573     /**
574      * Using the EJB descriptor file name passed from the <code>ejbjar</code>
575      * task, this method returns the "basename" which will be used to name the
576      * completed JAR file.
577      *
578      * @param descriptorFileName String representing the file name of an EJB
579      * descriptor to be processed
580      * @return The "basename" which will be used to name the
581      * completed JAR file
582      */

583     protected String JavaDoc getJarBaseName(String JavaDoc descriptorFileName) {
584
585         String JavaDoc baseName = "";
586
587         // Work out what the base name is
588
if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.BASEJARNAME)) {
589             String JavaDoc canonicalDescriptor = descriptorFileName.replace('\\', '/');
590             int index = canonicalDescriptor.lastIndexOf('/');
591             if (index != -1) {
592                 baseName = descriptorFileName.substring(0, index + 1);
593             }
594             baseName += config.baseJarName;
595         } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
596             int lastSeparatorIndex = descriptorFileName.lastIndexOf(File.separator);
597             int endBaseName = -1;
598             if (lastSeparatorIndex != -1) {
599                 endBaseName = descriptorFileName.indexOf(config.baseNameTerminator,
600                                                             lastSeparatorIndex);
601             } else {
602                 endBaseName = descriptorFileName.indexOf(config.baseNameTerminator);
603             }
604
605             if (endBaseName != -1) {
606                 baseName = descriptorFileName.substring(0, endBaseName);
607             } else {
608                 throw new BuildException("Unable to determine jar name "
609                     + "from descriptor \"" + descriptorFileName + "\"");
610             }
611         } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DIRECTORY)) {
612             File JavaDoc descriptorFile = new File JavaDoc(config.descriptorDir, descriptorFileName);
613             String JavaDoc path = descriptorFile.getAbsolutePath();
614             int lastSeparatorIndex
615                 = path.lastIndexOf(File.separator);
616             if (lastSeparatorIndex == -1) {
617                 throw new BuildException("Unable to determine directory name holding descriptor");
618             }
619             String JavaDoc dirName = path.substring(0, lastSeparatorIndex);
620             int dirSeparatorIndex = dirName.lastIndexOf(File.separator);
621             if (dirSeparatorIndex != -1) {
622                 dirName = dirName.substring(dirSeparatorIndex + 1);
623             }
624
625             baseName = dirName;
626         } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.EJB_NAME)) {
627             baseName = handler.getEjbName();
628         }
629         return baseName;
630     }
631
632     /**
633      * Get the prefix for vendor deployment descriptors.
634      *
635      * This will contain the path and the start of the descriptor name,
636      * depending on the naming scheme
637      * @param baseName the base name to use.
638      * @param descriptorFileName the file name to use.
639      * @return the prefix.
640      */

641     public String JavaDoc getVendorDDPrefix(String JavaDoc baseName, String JavaDoc descriptorFileName) {
642         String JavaDoc ddPrefix = null;
643
644         if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
645             ddPrefix = baseName + config.baseNameTerminator;
646         } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.BASEJARNAME)
647             || config.namingScheme.getValue().equals(EjbJar.NamingScheme.EJB_NAME)
648             || config.namingScheme.getValue().equals(EjbJar.NamingScheme.DIRECTORY)) {
649             String JavaDoc canonicalDescriptor = descriptorFileName.replace('\\', '/');
650             int index = canonicalDescriptor.lastIndexOf('/');
651             if (index == -1) {
652                 ddPrefix = "";
653             } else {
654                 ddPrefix = descriptorFileName.substring(0, index + 1);
655             }
656         }
657         return ddPrefix;
658     }
659
660     /**
661      * Add any vendor specific files which should be included in the
662      * EJB Jar.
663      * @param ejbFiles a hashtable entryname -> file.
664      * @param ddPrefix a prefix to use.
665      */

666     protected void addVendorFiles(Hashtable JavaDoc ejbFiles, String JavaDoc ddPrefix) {
667         // nothing to add for generic tool.
668
}
669
670
671     /**
672      * Get the vendor specific name of the Jar that will be output. The modification date
673      * of this jar will be checked against the dependent bean classes.
674      * @param baseName the basename to use.
675      */

676     File JavaDoc getVendorOutputJarFile(String JavaDoc baseName) {
677         return new File JavaDoc(destDir, baseName + genericJarSuffix);
678     }
679
680     /**
681      * This method checks the timestamp on each file listed in the <code>
682      * ejbFiles</code> and compares them to the timestamp on the <code>jarFile
683      * </code>. If the <code>jarFile</code>'s timestamp is more recent than
684      * each EJB file, <code>true</code> is returned. Otherwise, <code>false
685      * </code> is returned.
686      * TODO: find a way to check the manifest-file, that is found by naming convention
687      *
688      * @param ejbFiles Hashtable of EJB classes (and other) files that will be
689      * added to the completed JAR file
690      * @param jarFile JAR file which will contain all of the EJB classes (and
691      * other) files
692      * @return boolean indicating whether or not the <code>jarFile</code>
693      * is up to date
694      */

695     protected boolean needToRebuild(Hashtable JavaDoc ejbFiles, File JavaDoc jarFile) {
696         if (jarFile.exists()) {
697             long lastBuild = jarFile.lastModified();
698
699             Iterator JavaDoc fileIter = ejbFiles.values().iterator();
700
701             // Loop through the files seeing if any has been touched
702
// more recently than the destination jar.
703
while (fileIter.hasNext()) {
704                 File JavaDoc currentFile = (File JavaDoc) fileIter.next();
705                 if (lastBuild < currentFile.lastModified()) {
706                     log("Build needed because " + currentFile.getPath() + " is out of date",
707                         Project.MSG_VERBOSE);
708                     return true;
709                 }
710             }
711             return false;
712         }
713
714         return true;
715     }
716
717     /**
718      * Returns the Public ID of the DTD specified in the EJB descriptor. Not
719      * every vendor-specific <code>DeploymentTool</code> will need to reference
720      * this value or may want to determine this value in a vendor-specific way.
721      *
722      * @return Public ID of the DTD specified in the EJB descriptor.
723      */

724     protected String JavaDoc getPublicId() {
725         return handler.getPublicId();
726     }
727
728     /**
729      * Get the manifets file to use for building the generic jar.
730      *
731      * If the file does not exist the global manifest from the config is used
732      * otherwise the default Ant manifest will be used.
733      *
734      * @param prefix the prefix where to llook for the manifest file based on
735      * the naming convention.
736      *
737      * @return the manifest file or null if the manifest file does not exist
738      */

739     protected File JavaDoc getManifestFile(String JavaDoc prefix) {
740         File JavaDoc manifestFile
741             = new File JavaDoc(getConfig().descriptorDir, prefix + "manifest.mf");
742         if (manifestFile.exists()) {
743             return manifestFile;
744         }
745
746         if (config.manifest != null) {
747             return config.manifest;
748         }
749         return null;
750     }
751
752     /**
753      * Method used to encapsulate the writing of the JAR file. Iterates over the
754      * filenames/java.io.Files in the Hashtable stored on the instance variable
755      * ejbFiles.
756      * @param baseName the base name to use.
757      * @param jarfile the jar file to write to.
758      * @param files the files to write to the jar.
759      * @param publicId the id to use.
760      * @throws BuildException if there is a problem.
761      */

762     protected void writeJar(String JavaDoc baseName, File JavaDoc jarfile, Hashtable JavaDoc files,
763                             String JavaDoc publicId) throws BuildException {
764
765         JarOutputStream JavaDoc jarStream = null;
766         try {
767             // clean the addedfiles set
768
if (addedfiles == null) {
769                 addedfiles = new HashSet JavaDoc();
770             } else {
771                 addedfiles.clear();
772             }
773
774             /* If the jarfile already exists then whack it and recreate it.
775              * Should probably think of a more elegant way to handle this
776              * so that in case of errors we don't leave people worse off
777              * than when we started =)
778              */

779             if (jarfile.exists()) {
780                 jarfile.delete();
781             }
782             jarfile.getParentFile().mkdirs();
783             jarfile.createNewFile();
784
785             InputStream JavaDoc in = null;
786             Manifest JavaDoc manifest = null;
787             try {
788                 File JavaDoc manifestFile = (File JavaDoc) files.get(MANIFEST);
789                 if (manifestFile != null && manifestFile.exists()) {
790                     in = new FileInputStream JavaDoc(manifestFile);
791                 } else {
792                     String JavaDoc defaultManifest = "/org/apache/tools/ant/defaultManifest.mf";
793                     in = this.getClass().getResourceAsStream(defaultManifest);
794                     if (in == null) {
795                         throw new BuildException("Could not find "
796                             + "default manifest: " + defaultManifest);
797                     }
798                 }
799
800                 manifest = new Manifest JavaDoc(in);
801             } catch (IOException JavaDoc e) {
802                 throw new BuildException ("Unable to read manifest", e, getLocation());
803             } finally {
804                 if (in != null) {
805                     in.close();
806                 }
807             }
808
809             // Create the streams necessary to write the jarfile
810

811             jarStream = new JarOutputStream JavaDoc(new FileOutputStream JavaDoc(jarfile), manifest);
812             jarStream.setMethod(JarOutputStream.DEFLATED);
813
814             // Loop through all the class files found and add them to the jar
815
for (Iterator JavaDoc entryIterator = files.keySet().iterator(); entryIterator.hasNext();) {
816                 String JavaDoc entryName = (String JavaDoc) entryIterator.next();
817                 if (entryName.equals(MANIFEST)) {
818                     continue;
819                 }
820
821                 File JavaDoc entryFile = (File JavaDoc) files.get(entryName);
822
823                 log("adding file '" + entryName + "'",
824                               Project.MSG_VERBOSE);
825
826                 addFileToJar(jarStream, entryFile, entryName);
827
828                 // See if there are any inner classes for this class and add them in if there are
829
InnerClassFilenameFilter flt = new InnerClassFilenameFilter(entryFile.getName());
830                 File JavaDoc entryDir = entryFile.getParentFile();
831                 String JavaDoc[] innerfiles = entryDir.list(flt);
832                 if (innerfiles != null) {
833                     for (int i = 0, n = innerfiles.length; i < n; i++) {
834
835                         //get and clean up innerclass name
836
int entryIndex = entryName.lastIndexOf(entryFile.getName()) - 1;
837                         if (entryIndex < 0) {
838                             entryName = innerfiles[i];
839                         } else {
840                             entryName = entryName.substring(0, entryIndex)
841                                 + File.separatorChar + innerfiles[i];
842                         }
843                         // link the file
844
entryFile = new File JavaDoc(config.srcDir, entryName);
845
846                         log("adding innerclass file '" + entryName + "'",
847                                 Project.MSG_VERBOSE);
848
849                         addFileToJar(jarStream, entryFile, entryName);
850
851                     }
852                 }
853             }
854         } catch (IOException JavaDoc ioe) {
855             String JavaDoc msg = "IOException while processing ejb-jar file '"
856                 + jarfile.toString()
857                 + "'. Details: "
858                 + ioe.getMessage();
859             throw new BuildException(msg, ioe);
860         } finally {
861             if (jarStream != null) {
862                 try {
863                     jarStream.close();
864                 } catch (IOException JavaDoc closeException) {
865                     // ignore
866
}
867             }
868         }
869     } // end of writeJar
870

871
872     /**
873      * Add all available classes, that depend on Remote, Home, Bean, PK
874      * @param checkEntries files, that are extracted from the deployment descriptor
875      * @throws BuildException if there is a problem.
876      */

877     protected void checkAndAddDependants(Hashtable JavaDoc checkEntries)
878         throws BuildException {
879
880         if (dependencyAnalyzer == null) {
881             return;
882         }
883
884         dependencyAnalyzer.reset();
885
886         Iterator JavaDoc i = checkEntries.keySet().iterator();
887         while (i.hasNext()) {
888             String JavaDoc entryName = (String JavaDoc) i.next();
889             if (entryName.endsWith(".class")) {
890                 String JavaDoc className = entryName.substring(0,
891                     entryName.length() - ".class".length());
892                 className = className.replace(File.separatorChar, '/');
893                 className = className.replace('/', '.');
894
895                 dependencyAnalyzer.addRootClass(className);
896             }
897         }
898
899         Enumeration JavaDoc e = dependencyAnalyzer.getClassDependencies();
900
901         while (e.hasMoreElements()) {
902             String JavaDoc classname = (String JavaDoc) e.nextElement();
903             String JavaDoc location
904                 = classname.replace('.', File.separatorChar) + ".class";
905             File JavaDoc classFile = new File JavaDoc(config.srcDir, location);
906             if (classFile.exists()) {
907                 checkEntries.put(location, classFile);
908                 log("dependent class: " + classname + " - " + classFile,
909                     Project.MSG_VERBOSE);
910             }
911         }
912     }
913
914
915     /**
916      * Returns a Classloader object which parses the passed in generic EjbJar classpath.
917      * The loader is used to dynamically load classes from javax.ejb.* and the classes
918      * being added to the jar.
919      * @return a classloader.
920      */

921     protected ClassLoader JavaDoc getClassLoaderForBuild() {
922         if (classpathLoader != null) {
923             return classpathLoader;
924         }
925
926         Path combinedClasspath = getCombinedClasspath();
927
928         // only generate a new ClassLoader if we have a classpath
929
if (combinedClasspath == null) {
930             classpathLoader = getClass().getClassLoader();
931         } else {
932             classpathLoader
933                 = getTask().getProject().createClassLoader(combinedClasspath);
934         }
935
936         return classpathLoader;
937     }
938
939     /**
940      * Called to validate that the tool parameters have been configured.
941      *
942      * @throws BuildException If the Deployment Tool's configuration isn't
943      * valid
944      */

945     public void validateConfigured() throws BuildException {
946         if ((destDir == null) || (!destDir.isDirectory())) {
947             String JavaDoc msg = "A valid destination directory must be specified "
948                             + "using the \"destdir\" attribute.";
949             throw new BuildException(msg, getLocation());
950         }
951     }
952 }
953
Popular Tags