GenericDeploymentTool.java

00001 
00017 package org.objectweb.jonas.ant;
00018 
00019 import java.io.File;
00020 import java.io.FileInputStream;
00021 import java.io.FileOutputStream;
00022 import java.io.IOException;
00023 import java.io.InputStream;
00024 import java.util.ArrayList;
00025 import java.util.Enumeration;
00026 import java.util.Hashtable;
00027 import java.util.Iterator;
00028 import java.util.List;
00029 import java.util.jar.JarOutputStream;
00030 import java.util.jar.Manifest;
00031 import java.util.zip.ZipEntry;
00032 import javax.xml.parsers.SAXParser;
00033 import org.apache.tools.ant.BuildException;
00034 import org.apache.tools.ant.DirectoryScanner;
00035 import org.apache.tools.ant.Location;
00036 import org.apache.tools.ant.Project;
00037 import org.apache.tools.ant.Task;
00038 import org.apache.tools.ant.types.FileSet;
00039 import org.apache.tools.ant.types.Path;
00040 import org.apache.tools.ant.util.depend.DependencyAnalyzer;
00041 import org.xml.sax.InputSource;
00042 import org.xml.sax.SAXException;
00043 
00044 
00063 public class GenericDeploymentTool implements EJBDeploymentTool {
00065     protected static final String META_DIR  = "META-INF/";
00066 
00068     protected static final String MANIFEST  = META_DIR + "MANIFEST.MF";
00069 
00071     protected static final String EJB_DD    = "ejb-jar.xml";
00072 
00074     public static final String ANALYZER_SUPER = "super";
00076     public static final String ANALYZER_FULL = "full";
00078     public static final String ANALYZER_NONE = "none";
00079 
00081     public static final String DEFAULT_ANALYZER = ANALYZER_SUPER;
00082 
00084     public static final String ANALYZER_CLASS_SUPER
00085         = "org.apache.tools.ant.util.depend.bcel.AncestorAnalyzer";
00087     public static final String ANALYZER_CLASS_FULL
00088         = "org.apache.tools.ant.util.depend.bcel.FullAnalyzer";
00089 
00095     private EjbJar.Config config;
00096 
00098     private File destDir;
00099 
00102     private Path classpath;
00103 
00105     private String genericJarSuffix = "-generic.jar";
00106 
00111     private Task task;
00112 
00117     private ClassLoader classpathLoader = null;
00118 
00122     private List addedfiles;
00123 
00127     private DescriptorHandler handler;
00128 
00132     private DependencyAnalyzer dependencyAnalyzer;
00133 
00134     public GenericDeploymentTool() {
00135     }
00136 
00137 
00142     public void setDestdir(File inDir) {
00143         this.destDir = inDir;
00144     }
00145 
00151     protected File getDestDir() {
00152         return destDir;
00153     }
00154 
00155 
00161     public void setTask(Task task) {
00162         this.task = task;
00163     }
00164 
00170     protected Task getTask() {
00171         return task;
00172     }
00173 
00179     protected EjbJar.Config getConfig() {
00180         return config;
00181     }
00182 
00189     protected boolean usingBaseJarName() {
00190         return config.baseJarName != null;
00191     }
00192 
00197     public void setGenericJarSuffix(String inString) {
00198         this.genericJarSuffix = inString;
00199     }
00200 
00206     public Path createClasspath() {
00207         if (classpath == null) {
00208             classpath = new Path(task.getProject());
00209         }
00210         return classpath.createPath();
00211     }
00212 
00218     public void setClasspath(Path classpath) {
00219         this.classpath = classpath;
00220     }
00221 
00228     protected Path getCombinedClasspath() {
00229         Path combinedPath = classpath;
00230         if (config.classpath != null) {
00231             if (combinedPath == null) {
00232                 combinedPath = config.classpath;
00233             } else {
00234                 combinedPath.append(config.classpath);
00235             }
00236         }
00237 
00238         return combinedPath;
00239     }
00240 
00247     protected void log(String message, int level) {
00248         getTask().log(message, level);
00249     }
00250 
00256     protected Location getLocation() {
00257         return getTask().getLocation();
00258     }
00259 
00260     private void createAnalyzer() {
00261         String analyzer = config.analyzer;
00262         if (analyzer == null) {
00263             analyzer = DEFAULT_ANALYZER;
00264         }
00265 
00266         if (analyzer.equals(ANALYZER_NONE)) {
00267             return;
00268         }
00269 
00270         String analyzerClassName = null;
00271         if (analyzer.equals(ANALYZER_SUPER)) {
00272             analyzerClassName = ANALYZER_CLASS_SUPER;
00273         } else if (analyzer.equals(ANALYZER_FULL)) {
00274             analyzerClassName = ANALYZER_CLASS_FULL;
00275         } else {
00276             analyzerClassName = analyzer;
00277         }
00278 
00279         try {
00280             Class analyzerClass = Class.forName(analyzerClassName);
00281             dependencyAnalyzer
00282                 = (DependencyAnalyzer) analyzerClass.newInstance();
00283             dependencyAnalyzer.addClassPath(new Path(task.getProject(),
00284                 config.srcDir.getPath()));
00285             dependencyAnalyzer.addClassPath(config.classpath);
00286         } catch (NoClassDefFoundError e) {
00287             dependencyAnalyzer = null;
00288             task.log("Unable to load dependency analyzer: " + analyzerClassName
00289                 + " - dependent class not found: " + e.getMessage(),
00290                 Project.MSG_WARN);
00291         } catch (Exception e) {
00292             dependencyAnalyzer = null;
00293             task.log("Unable to load dependency analyzer: " + analyzerClassName
00294                      + " - exception: " + e.getMessage(),
00295                 Project.MSG_WARN);
00296         }
00297     }
00298 
00299 
00305     public void configure(EjbJar.Config config) {
00306         this.config = config;
00307 
00308         createAnalyzer();
00309         classpathLoader = null;
00310     }
00311 
00324     protected void addFileToJar(JarOutputStream jStream,
00325                                 File inputFile,
00326                                 String logicalFilename)
00327         throws BuildException {
00328         FileInputStream iStream = null;
00329         try {
00330             if (!addedfiles.contains(logicalFilename)) {
00331                 iStream = new FileInputStream(inputFile);
00332                 // Create the zip entry and add it to the jar file
00333                 ZipEntry zipEntry = new ZipEntry(logicalFilename.replace('\\', '/'));
00334                 jStream.putNextEntry(zipEntry);
00335 
00336                 // Create the file input stream, and buffer everything over
00337                 // to the jar output stream
00338                 byte[] byteBuffer = new byte[2 * 1024];
00339                 int count = 0;
00340                 do {
00341                     jStream.write(byteBuffer, 0, count);
00342                     count = iStream.read(byteBuffer, 0, byteBuffer.length);
00343                 } while (count != -1);
00344 
00345                 //add it to list of files in jar
00346                 addedfiles.add(logicalFilename);
00347            }
00348         } catch (IOException ioe) {
00349             log("WARNING: IOException while adding entry "
00350                 + logicalFilename + " to jarfile from "
00351                 + inputFile.getPath() + " "  + ioe.getClass().getName()
00352                 + "-" + ioe.getMessage(), Project.MSG_WARN);
00353         } finally {
00354             // Close up the file input stream for the class file
00355             if (iStream != null) {
00356                 try {
00357                     iStream.close();
00358                 } catch (IOException closeException) {
00359                     // ignore
00360                 }
00361             }
00362         }
00363     }
00364 
00365     protected DescriptorHandler getDescriptorHandler(File srcDir) {
00366         DescriptorHandler handler = new DescriptorHandler(getTask(), srcDir);
00367 
00368         registerKnownDTDs(handler);
00369 
00370         // register any DTDs supplied by the user
00371         for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) {
00372             EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next();
00373             handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation());
00374         }
00375         return handler;
00376     }
00377 
00384     protected void registerKnownDTDs(DescriptorHandler handler) {
00385         // none to register for generic
00386     }
00387 
00388     public void processDescriptor(String descriptorFileName, SAXParser saxParser) {
00389 
00390         checkConfiguration(descriptorFileName, saxParser);
00391 
00392         try {
00393             handler = getDescriptorHandler(config.srcDir);
00394 
00395             // Retrive the files to be added to JAR from EJB descriptor
00396             Hashtable ejbFiles = parseEjbFiles(descriptorFileName, saxParser);
00397 
00398             // Add any support classes specified in the build file
00399             addSupportClasses(ejbFiles);
00400 
00401             // Determine the JAR filename (without filename extension)
00402             String baseName = getJarBaseName(descriptorFileName);
00403 
00404             String ddPrefix = getVendorDDPrefix(baseName, descriptorFileName);
00405 
00406             File manifestFile = getManifestFile(ddPrefix);
00407             if (manifestFile != null) {
00408                 ejbFiles.put(MANIFEST, manifestFile);
00409             }
00410 
00411 
00412 
00413             // First the regular deployment descriptor
00414             ejbFiles.put(META_DIR + EJB_DD,
00415                          new File(config.descriptorDir, descriptorFileName));
00416 
00417             // now the vendor specific files, if any
00418             addVendorFiles(ejbFiles, ddPrefix);
00419 
00420             // add any dependent files
00421             checkAndAddDependants(ejbFiles);
00422 
00423             // Lastly create File object for the Jar files. If we are using
00424             // a flat destination dir, then we need to redefine baseName!
00425             if (config.flatDestDir && baseName.length() != 0) {
00426                 int startName = baseName.lastIndexOf(File.separator);
00427                 if (startName == -1) {
00428                     startName = 0;
00429                 }
00430 
00431                 int endName   = baseName.length();
00432                 baseName = baseName.substring(startName, endName);
00433             }
00434 
00435             File jarFile = getVendorOutputJarFile(baseName);
00436 
00437 
00438             // Check to see if we need a build and start doing the work!
00439             if (needToRebuild(ejbFiles, jarFile)) {
00440                 // Log that we are going to build...
00441                 log("building "
00442                               + jarFile.getName()
00443                               + " with "
00444                               + String.valueOf(ejbFiles.size())
00445                               + " files",
00446                               Project.MSG_INFO);
00447 
00448                 // Use helper method to write the jarfile
00449                 String publicId = getPublicId();
00450                 writeJar(baseName, jarFile, ejbFiles, publicId, true);
00451 
00452             } else {
00453                 // Log that the file is up to date...
00454                 log(jarFile.toString() + " is up to date.",
00455                               Project.MSG_VERBOSE);
00456             }
00457 
00458         } catch (SAXException se) {
00459             String msg = "SAXException while parsing '"
00460                 + descriptorFileName.toString()
00461                 + "'. This probably indicates badly-formed XML."
00462                 + "  Details: "
00463                 + se.getMessage();
00464             throw new BuildException(msg, se);
00465         } catch (IOException ioe) {
00466             String msg = "IOException while parsing'"
00467                 + descriptorFileName.toString()
00468                 + "'.  This probably indicates that the descriptor"
00469                 + " doesn't exist. Details: "
00470                 + ioe.getMessage();
00471             throw new BuildException(msg, ioe);
00472         }
00473     }
00474 
00487     protected void checkConfiguration(String descriptorFileName,
00488                                     SAXParser saxParser) throws BuildException {
00489 
00490         /*
00491          * For the GenericDeploymentTool, do nothing.  Vendor specific
00492          * subclasses should throw a BuildException if the configuration is
00493          * invalid for their server.
00494          */
00495     }
00496 
00512     protected Hashtable parseEjbFiles(String descriptorFileName, SAXParser saxParser)
00513                             throws IOException, SAXException {
00514         FileInputStream descriptorStream = null;
00515         Hashtable ejbFiles = null;
00516 
00517         try {
00518 
00519             /* Parse the ejb deployment descriptor.  While it may not
00520              * look like much, we use a SAXParser and an inner class to
00521              * get hold of all the classfile names for the descriptor.
00522              */
00523             descriptorStream
00524                 = new FileInputStream(new File(config.descriptorDir, descriptorFileName));
00525             saxParser.parse(new InputSource(descriptorStream), handler);
00526 
00527             ejbFiles = handler.getFiles();
00528 
00529         } finally {
00530             if (descriptorStream != null) {
00531                 try {
00532                     descriptorStream.close();
00533                 } catch (IOException closeException) {
00534                     // ignore
00535                 }
00536             }
00537         }
00538 
00539         return ejbFiles;
00540     }
00541 
00549     protected void addSupportClasses(Hashtable ejbFiles) {
00550         // add in support classes if any
00551         Project project = task.getProject();
00552         for (Iterator i = config.supportFileSets.iterator(); i.hasNext();) {
00553             FileSet supportFileSet = (FileSet) i.next();
00554             File supportBaseDir = supportFileSet.getDir(project);
00555             DirectoryScanner supportScanner = supportFileSet.getDirectoryScanner(project);
00556             supportScanner.scan();
00557             String[] supportFiles = supportScanner.getIncludedFiles();
00558             for (int j = 0; j < supportFiles.length; ++j) {
00559                 ejbFiles.put(supportFiles[j], new File(supportBaseDir, supportFiles[j]));
00560             }
00561         }
00562     }
00563 
00564 
00575     protected String getJarBaseName(String descriptorFileName) {
00576 
00577         String baseName = "";
00578 
00579         // Work out what the base name is
00580         if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.BASEJARNAME)) {
00581             String canonicalDescriptor = descriptorFileName.replace('\\', '/');
00582             int index = canonicalDescriptor.lastIndexOf('/');
00583             if (index != -1) {
00584                 baseName = descriptorFileName.substring(0, index + 1);
00585             }
00586             baseName += config.baseJarName;
00587         } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
00588             int lastSeparatorIndex = descriptorFileName.lastIndexOf(File.separator);
00589             int endBaseName = -1;
00590             if (lastSeparatorIndex != -1) {
00591                 endBaseName = descriptorFileName.indexOf(config.baseNameTerminator,
00592                                                             lastSeparatorIndex);
00593             } else {
00594                 endBaseName = descriptorFileName.indexOf(config.baseNameTerminator);
00595             }
00596 
00597             if (endBaseName != -1) {
00598                 baseName = descriptorFileName.substring(0, endBaseName);
00599             } else {
00600                 throw new BuildException("Unable to determine jar name "
00601                     + "from descriptor \"" + descriptorFileName + "\"");
00602             }
00603         } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DIRECTORY)) {
00604             File descriptorFile = new File(config.descriptorDir, descriptorFileName);
00605             String path = descriptorFile.getAbsolutePath();
00606             int lastSeparatorIndex
00607                 = path.lastIndexOf(File.separator);
00608             if (lastSeparatorIndex == -1) {
00609                 throw new BuildException("Unable to determine directory name holding descriptor");
00610             }
00611             String dirName = path.substring(0, lastSeparatorIndex);
00612             int dirSeparatorIndex = dirName.lastIndexOf(File.separator);
00613             if (dirSeparatorIndex != -1) {
00614                 dirName = dirName.substring(dirSeparatorIndex + 1);
00615             }
00616 
00617             baseName = dirName;
00618         } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.EJB_NAME)) {
00619             baseName = handler.getEjbName();
00620         }
00621         return baseName;
00622     }
00623 
00630     public String getVendorDDPrefix(String baseName, String descriptorFileName) {
00631         String ddPrefix = null;
00632 
00633         if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
00634             ddPrefix = baseName + config.baseNameTerminator;
00635         } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.BASEJARNAME)
00636             || config.namingScheme.getValue().equals(EjbJar.NamingScheme.EJB_NAME)
00637             || config.namingScheme.getValue().equals(EjbJar.NamingScheme.DIRECTORY)) {
00638             String canonicalDescriptor = descriptorFileName.replace('\\', '/');
00639             int index = canonicalDescriptor.lastIndexOf('/');
00640             if (index == -1) {
00641                 ddPrefix = "";
00642             } else {
00643                 ddPrefix = descriptorFileName.substring(0, index + 1);
00644             }
00645         }
00646         return ddPrefix;
00647     }
00648 
00653     protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
00654         // nothing to add for generic tool.
00655     }
00656 
00657 
00662     File getVendorOutputJarFile(String baseName) {
00663         return new File(destDir, baseName + genericJarSuffix);
00664     }
00665 
00681     protected boolean needToRebuild(Hashtable ejbFiles, File jarFile) {
00682         if (jarFile.exists()) {
00683             long lastBuild = jarFile.lastModified();
00684 
00685             Iterator fileIter = ejbFiles.values().iterator();
00686 
00687             // Loop through the files seeing if any has been touched
00688             // more recently than the destination jar.
00689             while (fileIter.hasNext()) {
00690                 File currentFile = (File) fileIter.next();
00691                 if (lastBuild < currentFile.lastModified()) {
00692                     log("Build needed because " + currentFile.getPath() + " is out of date",
00693                         Project.MSG_VERBOSE);
00694                     return true;
00695                 }
00696             }
00697             return false;
00698         }
00699 
00700         return true;
00701     }
00702 
00710     protected String getPublicId() {
00711         return handler.getPublicId();
00712     }
00713 
00725     protected File getManifestFile(String prefix) {
00726         File manifestFile
00727             = new File(getConfig().descriptorDir, prefix + "manifest.mf");
00728         if (manifestFile.exists()) {
00729             return manifestFile;
00730         }
00731 
00732         if (config.manifest != null) {
00733             return config.manifest;
00734         }
00735         return null;
00736     }
00737 
00744     protected void writeJar(String baseName, File jarfile, Hashtable files,
00745                             String publicId, boolean includeInnerClasses) throws BuildException {
00746 
00747         JarOutputStream jarStream = null;
00748         try {
00749             // clean the addedfiles Vector
00750             addedfiles = new ArrayList();
00751 
00752             /* If the jarfile already exists then whack it and recreate it.
00753              * Should probably think of a more elegant way to handle this
00754              * so that in case of errors we don't leave people worse off
00755              * than when we started =)
00756              */
00757             if (jarfile.exists()) {
00758                 jarfile.delete();
00759             }
00760             jarfile.getParentFile().mkdirs();
00761             jarfile.createNewFile();
00762 
00763             InputStream in = null;
00764             Manifest manifest = null;
00765             try {
00766                 File manifestFile = (File) files.get(MANIFEST);
00767                 if (manifestFile != null && manifestFile.exists()) {
00768                     in = new FileInputStream(manifestFile);
00769                 } else {
00770                     String defaultManifest = "/org/apache/tools/ant/defaultManifest.mf";
00771                     in = this.getClass().getResourceAsStream(defaultManifest);
00772                     if (in == null) {
00773                         throw new BuildException("Could not find "
00774                             + "default manifest: " + defaultManifest);
00775                     }
00776                 }
00777 
00778                 manifest = new Manifest(in);
00779             } catch (IOException e) {
00780                 throw new BuildException ("Unable to read manifest", e, getLocation());
00781             } finally {
00782                 if (in != null) {
00783                     in.close();
00784                 }
00785             }
00786 
00787             // Create the streams necessary to write the jarfile
00788 
00789             jarStream = new JarOutputStream(new FileOutputStream(jarfile), manifest);
00790             jarStream.setMethod(JarOutputStream.DEFLATED);
00791 
00792             // Loop through all the class files found and add them to the jar
00793             for (Iterator entryIterator = files.keySet().iterator(); entryIterator.hasNext();) {
00794                 String entryName = (String) entryIterator.next();
00795                 if (entryName.equals(MANIFEST)) {
00796                     continue;
00797                 }
00798 
00799                 File entryFile = (File) files.get(entryName);
00800 
00801                 log("adding file '" + entryName + "'",
00802                               Project.MSG_VERBOSE);
00803 
00804                 addFileToJar(jarStream, entryFile, entryName);
00805 
00806                 // See if there are any inner classes for this class and add them in if there are
00807                 InnerClassFilenameFilter flt = new InnerClassFilenameFilter(entryFile.getName());
00808                 File entryDir = entryFile.getParentFile();
00809                 String[] innerfiles = entryDir.list(flt);
00810                 if (innerfiles != null && includeInnerClasses) {
00811                     for (int i = 0, n = innerfiles.length; i < n; i++) {
00812 
00813                         //get and clean up innerclass name
00814                         int entryIndex = entryName.lastIndexOf(entryFile.getName()) - 1;
00815                         if (entryIndex < 0) {
00816                             entryName = innerfiles[i];
00817                         } else {
00818                             entryName = entryName.substring(0, entryIndex)
00819                                 + File.separatorChar + innerfiles[i];
00820                         }
00821                         // link the file
00822                         entryFile = new File(config.srcDir, entryName);
00823 
00824                         log("adding innerclass file '" + entryName + "'",
00825                                 Project.MSG_VERBOSE);
00826 
00827                         addFileToJar(jarStream, entryFile, entryName);
00828 
00829                     }
00830                 }
00831             }
00832         } catch (IOException ioe) {
00833             String msg = "IOException while processing ejb-jar file '"
00834                 + jarfile.toString()
00835                 + "'. Details: "
00836                 + ioe.getMessage();
00837             throw new BuildException(msg, ioe);
00838         } finally {
00839             if (jarStream != null) {
00840                 try {
00841                     jarStream.close();
00842                 } catch (IOException closeException) {
00843                     // ignore
00844                 }
00845             }
00846         }
00847     } // end of writeJar
00848 
00849 
00854     protected void checkAndAddDependants(Hashtable checkEntries)
00855         throws BuildException {
00856 
00857         if (dependencyAnalyzer == null) {
00858             return;
00859         }
00860 
00861         dependencyAnalyzer.reset();
00862 
00863         Iterator i = checkEntries.keySet().iterator();
00864         while (i.hasNext()) {
00865             String entryName = (String) i.next();
00866             if (entryName.endsWith(".class")) {
00867                 String className = entryName.substring(0,
00868                     entryName.length() - ".class".length());
00869                 className = className.replace(File.separatorChar, '/');
00870                 className = className.replace('/', '.');
00871 
00872                 dependencyAnalyzer.addRootClass(className);
00873             }
00874         }
00875 
00876         Enumeration e = dependencyAnalyzer.getClassDependencies();
00877 
00878         while (e.hasMoreElements()) {
00879             String classname = (String) e.nextElement();
00880             String location
00881                 = classname.replace('.', File.separatorChar) + ".class";
00882             File classFile = new File(config.srcDir, location);
00883             if (classFile.exists()) {
00884                 checkEntries.put(location, classFile);
00885                 log("dependent class: " + classname + " - " + classFile,
00886                     Project.MSG_VERBOSE);
00887             }
00888         }
00889     }
00890 
00891 
00898     protected ClassLoader getClassLoaderForBuild() {
00899         if (classpathLoader != null) {
00900             return classpathLoader;
00901         }
00902 
00903         Path combinedClasspath = getCombinedClasspath();
00904 
00905         // only generate a new ClassLoader if we have a classpath
00906         if (combinedClasspath == null) {
00907             classpathLoader = getClass().getClassLoader();
00908         } else {
00909             classpathLoader
00910                 = getTask().getProject().createClassLoader(combinedClasspath);
00911         }
00912 
00913         return classpathLoader;
00914     }
00915 
00922     public void validateConfigured() throws BuildException {
00923         if ((destDir == null) || (!destDir.isDirectory())) {
00924             String msg = "A valid destination directory must be specified "
00925                             + "using the \"destdir\" attribute.";
00926             throw new BuildException(msg, getLocation());
00927         }
00928     }
00929 }

Generated on Tue Feb 15 15:05:13 2005 for JOnAS by  doxygen 1.3.9.1