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
00333 ZipEntry zipEntry = new ZipEntry(logicalFilename.replace('\\', '/'));
00334 jStream.putNextEntry(zipEntry);
00335
00336
00337
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
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
00355 if (iStream != null) {
00356 try {
00357 iStream.close();
00358 } catch (IOException closeException) {
00359
00360 }
00361 }
00362 }
00363 }
00364
00365 protected DescriptorHandler getDescriptorHandler(File srcDir) {
00366 DescriptorHandler handler = new DescriptorHandler(getTask(), srcDir);
00367
00368 registerKnownDTDs(handler);
00369
00370
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
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
00396 Hashtable ejbFiles = parseEjbFiles(descriptorFileName, saxParser);
00397
00398
00399 addSupportClasses(ejbFiles);
00400
00401
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
00414 ejbFiles.put(META_DIR + EJB_DD,
00415 new File(config.descriptorDir, descriptorFileName));
00416
00417
00418 addVendorFiles(ejbFiles, ddPrefix);
00419
00420
00421 checkAndAddDependants(ejbFiles);
00422
00423
00424
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
00439 if (needToRebuild(ejbFiles, jarFile)) {
00440
00441 log("building "
00442 + jarFile.getName()
00443 + " with "
00444 + String.valueOf(ejbFiles.size())
00445 + " files",
00446 Project.MSG_INFO);
00447
00448
00449 String publicId = getPublicId();
00450 writeJar(baseName, jarFile, ejbFiles, publicId, true);
00451
00452 } else {
00453
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
00492
00493
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
00520
00521
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
00535 }
00536 }
00537 }
00538
00539 return ejbFiles;
00540 }
00541
00549 protected void addSupportClasses(Hashtable ejbFiles) {
00550
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
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
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
00688
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
00750 addedfiles = new ArrayList();
00751
00752
00753
00754
00755
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
00788
00789 jarStream = new JarOutputStream(new FileOutputStream(jarfile), manifest);
00790 jarStream.setMethod(JarOutputStream.DEFLATED);
00791
00792
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
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
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
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
00844 }
00845 }
00846 }
00847 }
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
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 }