aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java')
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java953
1 files changed, 953 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java
new file mode 100644
index 00000000..069bdfcf
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java
@@ -0,0 +1,953 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+import javax.xml.parsers.SAXParser;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.depend.DependencyAnalyzer;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+
+/**
+ * A deployment tool which creates generic EJB jars. Generic jars contains
+ * only those classes and META-INF entries specified in the EJB 1.1 standard
+ *
+ * This class is also used as a framework for the creation of vendor specific
+ * deployment tools. A number of template methods are provided through which the
+ * vendor specific tool can hook into the EJB creation process.
+ *
+ */
+public class GenericDeploymentTool implements EJBDeploymentTool {
+ /** The default buffer byte size to use for IO */
+ public static final int DEFAULT_BUFFER_SIZE = 1024;
+ /** The level to use for compression */
+ public static final int JAR_COMPRESS_LEVEL = 9;
+
+ /** The standard META-INF directory in jar files */
+ protected static final String META_DIR = "META-INF/";
+
+ /** The standard MANIFEST file */
+ protected static final String MANIFEST = META_DIR + "MANIFEST.MF";
+
+ /** Name for EJB Deployment descriptor within EJB jars */
+ protected static final String EJB_DD = "ejb-jar.xml";
+
+ /** A dependency analyzer name to find ancestor classes */
+ public static final String ANALYZER_SUPER = "super";
+ /** A dependency analyzer name to find all related classes */
+ public static final String ANALYZER_FULL = "full";
+ /** A dependency analyzer name for no analyzer */
+ public static final String ANALYZER_NONE = "none";
+
+ /** The default analyzer */
+ public static final String DEFAULT_ANALYZER = ANALYZER_SUPER;
+
+ /** The analyzer class for the super analyzer */
+ public static final String ANALYZER_CLASS_SUPER
+ = "org.apache.tools.ant.util.depend.bcel.AncestorAnalyzer";
+ /** The analyzer class for the super analyzer */
+ public static final String ANALYZER_CLASS_FULL
+ = "org.apache.tools.ant.util.depend.bcel.FullAnalyzer";
+
+ /**
+ * The configuration from the containing task. This config combined
+ * with the settings of the individual attributes here constitues the
+ * complete config for this deployment tool.
+ */
+ private EjbJar.Config config;
+
+ /** Stores a handle to the directory to put the Jar files in */
+ private File destDir;
+
+ /** The classpath to use with this deployment tool. This is appended to
+ any paths from the ejbjar task itself.*/
+ private Path classpath;
+
+ /** Instance variable that stores the suffix for the generated jarfile. */
+ private String genericJarSuffix = "-generic.jar";
+
+ /**
+ * The task to which this tool belongs. This is used to access services
+ * provided by the ant core, such as logging.
+ */
+ private Task task;
+
+ /**
+ * The classloader generated from the given classpath to load
+ * the super classes and super interfaces.
+ */
+ private ClassLoader classpathLoader = null;
+
+ /**
+ * Set of files have been loaded into the EJB jar
+ */
+ private Set addedfiles;
+
+ /**
+ * Handler used to parse the EJB XML descriptor
+ */
+ private DescriptorHandler handler;
+
+ /**
+ * Dependency analyzer used to collect class dependencies
+ */
+ private DependencyAnalyzer dependencyAnalyzer;
+
+ /** No arg constructor */
+ public GenericDeploymentTool() {
+ }
+
+
+ /**
+ * Set the destination directory; required.
+ * @param inDir the destination directory.
+ */
+ public void setDestdir(File inDir) {
+ this.destDir = inDir;
+ }
+
+ /**
+ * Get the destination directory.
+ *
+ * @return the destination directory into which EJB jars are to be written
+ */
+ protected File getDestDir() {
+ return destDir;
+ }
+
+
+ /**
+ * Set the task which owns this tool
+ *
+ * @param task the Task to which this deployment tool is associated.
+ */
+ public void setTask(Task task) {
+ this.task = task;
+ }
+
+ /**
+ * Get the task for this tool.
+ *
+ * @return the Task instance this tool is associated with.
+ */
+ protected Task getTask() {
+ return task;
+ }
+
+ /**
+ * Get the basename terminator.
+ *
+ * @return an ejbjar task configuration
+ */
+ protected EjbJar.Config getConfig() {
+ return config;
+ }
+
+ /**
+ * Indicate if this build is using the base jar name.
+ *
+ * @return true if the name of the generated jar is coming from the
+ * basejarname attribute
+ */
+ protected boolean usingBaseJarName() {
+ return config.baseJarName != null;
+ }
+
+ /**
+ * Set the suffix for the generated jar file.
+ * @param inString the string to use as the suffix.
+ */
+ public void setGenericJarSuffix(String inString) {
+ this.genericJarSuffix = inString;
+ }
+
+ /**
+ * Add the classpath for the user classes
+ *
+ * @return a Path instance to be configured by Ant.
+ */
+ public Path createClasspath() {
+ if (classpath == null) {
+ classpath = new Path(task.getProject());
+ }
+ return classpath.createPath();
+ }
+
+ /**
+ * Set the classpath to be used for this compilation.
+ *
+ * @param classpath the classpath to be used for this build.
+ */
+ public void setClasspath(Path classpath) {
+ this.classpath = classpath;
+ }
+
+ /**
+ * Get the classpath by combining the one from the surrounding task, if any
+ * and the one from this tool.
+ *
+ * @return the combined classpath
+ */
+ protected Path getCombinedClasspath() {
+ Path combinedPath = classpath;
+ if (config.classpath != null) {
+ if (combinedPath == null) {
+ combinedPath = config.classpath;
+ } else {
+ combinedPath.append(config.classpath);
+ }
+ }
+
+ return combinedPath;
+ }
+
+ /**
+ * Log a message to the Ant output.
+ *
+ * @param message the message to be logged.
+ * @param level the severity of this message.
+ */
+ protected void log(String message, int level) {
+ getTask().log(message, level);
+ }
+
+ /**
+ * Get the build file location associated with this element's task.
+ *
+ * @return the task's location instance.
+ */
+ protected Location getLocation() {
+ return getTask().getLocation();
+ }
+
+ private void createAnalyzer() {
+ String analyzer = config.analyzer;
+ if (analyzer == null) {
+ analyzer = DEFAULT_ANALYZER;
+ }
+
+ if (analyzer.equals(ANALYZER_NONE)) {
+ return;
+ }
+
+ String analyzerClassName = null;
+ if (analyzer.equals(ANALYZER_SUPER)) {
+ analyzerClassName = ANALYZER_CLASS_SUPER;
+ } else if (analyzer.equals(ANALYZER_FULL)) {
+ analyzerClassName = ANALYZER_CLASS_FULL;
+ } else {
+ analyzerClassName = analyzer;
+ }
+
+ try {
+ Class analyzerClass = Class.forName(analyzerClassName);
+ dependencyAnalyzer
+ = (DependencyAnalyzer) analyzerClass.newInstance();
+ dependencyAnalyzer.addClassPath(new Path(task.getProject(),
+ config.srcDir.getPath()));
+ dependencyAnalyzer.addClassPath(config.classpath);
+ } catch (NoClassDefFoundError e) {
+ dependencyAnalyzer = null;
+ task.log("Unable to load dependency analyzer: " + analyzerClassName
+ + " - dependent class not found: " + e.getMessage(),
+ Project.MSG_WARN);
+ } catch (Exception e) {
+ dependencyAnalyzer = null;
+ task.log("Unable to load dependency analyzer: " + analyzerClassName
+ + " - exception: " + e.getMessage(),
+ Project.MSG_WARN);
+ }
+ }
+
+
+ /**
+ * Configure this tool for use in the ejbjar task.
+ *
+ * @param config the configuration from the surrounding ejbjar task.
+ */
+ public void configure(EjbJar.Config config) {
+ this.config = config;
+
+ createAnalyzer();
+ classpathLoader = null;
+ }
+
+ /**
+ * Utility method that encapsulates the logic of adding a file entry to
+ * a .jar file. Used by execute() to add entries to the jar file as it is
+ * constructed.
+ * @param jStream A JarOutputStream into which to write the
+ * jar entry.
+ * @param inputFile A File from which to read the
+ * contents the file being added.
+ * @param logicalFilename A String representing the name, including
+ * all relevant path information, that should be stored for the entry
+ * being added.
+ * @throws BuildException if there is a problem.
+ */
+ protected void addFileToJar(JarOutputStream jStream,
+ File inputFile,
+ String logicalFilename)
+ throws BuildException {
+ FileInputStream iStream = null;
+ try {
+ if (!addedfiles.contains(logicalFilename)) {
+ iStream = new FileInputStream(inputFile);
+ // Create the zip entry and add it to the jar file
+ ZipEntry zipEntry = new ZipEntry(logicalFilename.replace('\\', '/'));
+ jStream.putNextEntry(zipEntry);
+
+ // Create the file input stream, and buffer everything over
+ // to the jar output stream
+ byte[] byteBuffer = new byte[2 * DEFAULT_BUFFER_SIZE];
+ int count = 0;
+ do {
+ jStream.write(byteBuffer, 0, count);
+ count = iStream.read(byteBuffer, 0, byteBuffer.length);
+ } while (count != -1);
+
+ //add it to list of files in jar
+ addedfiles.add(logicalFilename);
+ }
+ } catch (IOException ioe) {
+ log("WARNING: IOException while adding entry "
+ + logicalFilename + " to jarfile from "
+ + inputFile.getPath() + " " + ioe.getClass().getName()
+ + "-" + ioe.getMessage(), Project.MSG_WARN);
+ } finally {
+ // Close up the file input stream for the class file
+ if (iStream != null) {
+ try {
+ iStream.close();
+ } catch (IOException closeException) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ /**
+ * Get a descriptionHandler.
+ * @param srcDir the source directory.
+ * @return a handler.
+ */
+ protected DescriptorHandler getDescriptorHandler(File srcDir) {
+ DescriptorHandler h = new DescriptorHandler(getTask(), srcDir);
+
+ registerKnownDTDs(h);
+
+ // register any DTDs supplied by the user
+ for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) {
+ EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next();
+ h.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation());
+ }
+ return h;
+ }
+
+ /**
+ * Register the locations of all known DTDs.
+ *
+ * vendor-specific subclasses should override this method to define
+ * the vendor-specific locations of the EJB DTDs
+ * @param handler no used in this class.
+ */
+ protected void registerKnownDTDs(DescriptorHandler handler) {
+ // none to register for generic
+ }
+
+ /** {@inheritDoc}. */
+ public void processDescriptor(String descriptorFileName, SAXParser saxParser) {
+
+ checkConfiguration(descriptorFileName, saxParser);
+
+ try {
+ handler = getDescriptorHandler(config.srcDir);
+
+ // Retrive the files to be added to JAR from EJB descriptor
+ Hashtable ejbFiles = parseEjbFiles(descriptorFileName, saxParser);
+
+ // Add any support classes specified in the build file
+ addSupportClasses(ejbFiles);
+
+ // Determine the JAR filename (without filename extension)
+ String baseName = getJarBaseName(descriptorFileName);
+
+ String ddPrefix = getVendorDDPrefix(baseName, descriptorFileName);
+
+ File manifestFile = getManifestFile(ddPrefix);
+ if (manifestFile != null) {
+ ejbFiles.put(MANIFEST, manifestFile);
+ }
+
+
+
+ // First the regular deployment descriptor
+ ejbFiles.put(META_DIR + EJB_DD,
+ new File(config.descriptorDir, descriptorFileName));
+
+ // now the vendor specific files, if any
+ addVendorFiles(ejbFiles, ddPrefix);
+
+ // add any dependent files
+ checkAndAddDependants(ejbFiles);
+
+ // Lastly create File object for the Jar files. If we are using
+ // a flat destination dir, then we need to redefine baseName!
+ if (config.flatDestDir && baseName.length() != 0) {
+ int startName = baseName.lastIndexOf(File.separator);
+ if (startName == -1) {
+ startName = 0;
+ }
+
+ int endName = baseName.length();
+ baseName = baseName.substring(startName, endName);
+ }
+
+ File jarFile = getVendorOutputJarFile(baseName);
+
+
+ // Check to see if we need a build and start doing the work!
+ if (needToRebuild(ejbFiles, jarFile)) {
+ // Log that we are going to build...
+ log("building "
+ + jarFile.getName()
+ + " with "
+ + String.valueOf(ejbFiles.size())
+ + " files",
+ Project.MSG_INFO);
+
+ // Use helper method to write the jarfile
+ String publicId = getPublicId();
+ writeJar(baseName, jarFile, ejbFiles, publicId);
+
+ } else {
+ // Log that the file is up to date...
+ log(jarFile.toString() + " is up to date.",
+ Project.MSG_VERBOSE);
+ }
+
+ } catch (SAXException se) {
+ String msg = "SAXException while parsing '"
+ + descriptorFileName
+ + "'. This probably indicates badly-formed XML."
+ + " Details: "
+ + se.getMessage();
+ throw new BuildException(msg, se);
+ } catch (IOException ioe) {
+ String msg = "IOException while parsing'"
+ + descriptorFileName
+ + "'. This probably indicates that the descriptor"
+ + " doesn't exist. Details: "
+ + ioe.getMessage();
+ throw new BuildException(msg, ioe);
+ }
+ }
+
+ /**
+ * This method is called as the first step in the processDescriptor method
+ * to allow vendor-specific subclasses to validate the task configuration
+ * prior to processing the descriptor. If the configuration is invalid,
+ * a BuildException should be thrown.
+ *
+ * @param descriptorFileName String representing the file name of an EJB
+ * descriptor to be processed
+ * @param saxParser SAXParser which may be used to parse the XML
+ * descriptor
+ * @throws BuildException if there is a problem.
+ */
+ protected void checkConfiguration(String descriptorFileName,
+ SAXParser saxParser) throws BuildException {
+
+ /*
+ * For the GenericDeploymentTool, do nothing. Vendor specific
+ * subclasses should throw a BuildException if the configuration is
+ * invalid for their server.
+ */
+ }
+
+ /**
+ * This method returns a list of EJB files found when the specified EJB
+ * descriptor is parsed and processed.
+ *
+ * @param descriptorFileName String representing the file name of an EJB
+ * descriptor to be processed
+ * @param saxParser SAXParser which may be used to parse the XML
+ * descriptor
+ * @return Hashtable of EJB class (and other) files to be
+ * added to the completed JAR file
+ * @throws SAXException Any SAX exception, possibly wrapping another
+ * exception
+ * @throws IOException An IOException from the parser, possibly from a
+ * the byte stream or character stream
+ */
+ protected Hashtable parseEjbFiles(String descriptorFileName, SAXParser saxParser)
+ throws IOException, SAXException {
+ FileInputStream descriptorStream = null;
+ Hashtable ejbFiles = null;
+
+ try {
+
+ /* Parse the ejb deployment descriptor. While it may not
+ * look like much, we use a SAXParser and an inner class to
+ * get hold of all the classfile names for the descriptor.
+ */
+ descriptorStream
+ = new FileInputStream(new File(config.descriptorDir, descriptorFileName));
+ saxParser.parse(new InputSource(descriptorStream), handler);
+
+ ejbFiles = handler.getFiles();
+
+ } finally {
+ if (descriptorStream != null) {
+ try {
+ descriptorStream.close();
+ } catch (IOException closeException) {
+ // ignore
+ }
+ }
+ }
+
+ return ejbFiles;
+ }
+
+ /**
+ * Adds any classes the user specifies using <i>support</i> nested elements
+ * to the <code>ejbFiles</code> Hashtable.
+ *
+ * @param ejbFiles Hashtable of EJB classes (and other) files that will be
+ * added to the completed JAR file
+ */
+ protected void addSupportClasses(Hashtable ejbFiles) {
+ // add in support classes if any
+ Project project = task.getProject();
+ for (Iterator i = config.supportFileSets.iterator(); i.hasNext();) {
+ FileSet supportFileSet = (FileSet) i.next();
+ File supportBaseDir = supportFileSet.getDir(project);
+ DirectoryScanner supportScanner = supportFileSet.getDirectoryScanner(project);
+ supportScanner.scan();
+ String[] supportFiles = supportScanner.getIncludedFiles();
+ for (int j = 0; j < supportFiles.length; ++j) {
+ ejbFiles.put(supportFiles[j], new File(supportBaseDir, supportFiles[j]));
+ }
+ }
+ }
+
+
+ /**
+ * Using the EJB descriptor file name passed from the <code>ejbjar</code>
+ * task, this method returns the "basename" which will be used to name the
+ * completed JAR file.
+ *
+ * @param descriptorFileName String representing the file name of an EJB
+ * descriptor to be processed
+ * @return The "basename" which will be used to name the
+ * completed JAR file
+ */
+ protected String getJarBaseName(String descriptorFileName) {
+
+ String baseName = "";
+
+ // Work out what the base name is
+ if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.BASEJARNAME)) {
+ String canonicalDescriptor = descriptorFileName.replace('\\', '/');
+ int index = canonicalDescriptor.lastIndexOf('/');
+ if (index != -1) {
+ baseName = descriptorFileName.substring(0, index + 1);
+ }
+ baseName += config.baseJarName;
+ } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
+ int lastSeparatorIndex = descriptorFileName.lastIndexOf(File.separator);
+ int endBaseName = -1;
+ if (lastSeparatorIndex != -1) {
+ endBaseName = descriptorFileName.indexOf(config.baseNameTerminator,
+ lastSeparatorIndex);
+ } else {
+ endBaseName = descriptorFileName.indexOf(config.baseNameTerminator);
+ }
+
+ if (endBaseName != -1) {
+ baseName = descriptorFileName.substring(0, endBaseName);
+ } else {
+ throw new BuildException("Unable to determine jar name "
+ + "from descriptor \"" + descriptorFileName + "\"");
+ }
+ } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DIRECTORY)) {
+ File descriptorFile = new File(config.descriptorDir, descriptorFileName);
+ String path = descriptorFile.getAbsolutePath();
+ int lastSeparatorIndex
+ = path.lastIndexOf(File.separator);
+ if (lastSeparatorIndex == -1) {
+ throw new BuildException("Unable to determine directory name holding descriptor");
+ }
+ String dirName = path.substring(0, lastSeparatorIndex);
+ int dirSeparatorIndex = dirName.lastIndexOf(File.separator);
+ if (dirSeparatorIndex != -1) {
+ dirName = dirName.substring(dirSeparatorIndex + 1);
+ }
+
+ baseName = dirName;
+ } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.EJB_NAME)) {
+ baseName = handler.getEjbName();
+ }
+ return baseName;
+ }
+
+ /**
+ * Get the prefix for vendor deployment descriptors.
+ *
+ * This will contain the path and the start of the descriptor name,
+ * depending on the naming scheme
+ * @param baseName the base name to use.
+ * @param descriptorFileName the file name to use.
+ * @return the prefix.
+ */
+ public String getVendorDDPrefix(String baseName, String descriptorFileName) {
+ String ddPrefix = null;
+
+ if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
+ ddPrefix = baseName + config.baseNameTerminator;
+ } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.BASEJARNAME)
+ || config.namingScheme.getValue().equals(EjbJar.NamingScheme.EJB_NAME)
+ || config.namingScheme.getValue().equals(EjbJar.NamingScheme.DIRECTORY)) {
+ String canonicalDescriptor = descriptorFileName.replace('\\', '/');
+ int index = canonicalDescriptor.lastIndexOf('/');
+ if (index == -1) {
+ ddPrefix = "";
+ } else {
+ ddPrefix = descriptorFileName.substring(0, index + 1);
+ }
+ }
+ return ddPrefix;
+ }
+
+ /**
+ * Add any vendor specific files which should be included in the
+ * EJB Jar.
+ * @param ejbFiles a hashtable entryname -> file.
+ * @param ddPrefix a prefix to use.
+ */
+ protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
+ // nothing to add for generic tool.
+ }
+
+
+ /**
+ * Get the vendor specific name of the Jar that will be output. The modification date
+ * of this jar will be checked against the dependent bean classes.
+ * @param baseName the basename to use.
+ */
+ File getVendorOutputJarFile(String baseName) {
+ return new File(destDir, baseName + genericJarSuffix);
+ }
+
+ /**
+ * This method checks the timestamp on each file listed in the <code>
+ * ejbFiles</code> and compares them to the timestamp on the <code>jarFile
+ * </code>. If the <code>jarFile</code>'s timestamp is more recent than
+ * each EJB file, <code>true</code> is returned. Otherwise, <code>false
+ * </code> is returned.
+ * TODO: find a way to check the manifest-file, that is found by naming convention
+ *
+ * @param ejbFiles Hashtable of EJB classes (and other) files that will be
+ * added to the completed JAR file
+ * @param jarFile JAR file which will contain all of the EJB classes (and
+ * other) files
+ * @return boolean indicating whether or not the <code>jarFile</code>
+ * is up to date
+ */
+ protected boolean needToRebuild(Hashtable ejbFiles, File jarFile) {
+ if (jarFile.exists()) {
+ long lastBuild = jarFile.lastModified();
+
+ Iterator fileIter = ejbFiles.values().iterator();
+
+ // Loop through the files seeing if any has been touched
+ // more recently than the destination jar.
+ while (fileIter.hasNext()) {
+ File currentFile = (File) fileIter.next();
+ if (lastBuild < currentFile.lastModified()) {
+ log("Build needed because " + currentFile.getPath() + " is out of date",
+ Project.MSG_VERBOSE);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the Public ID of the DTD specified in the EJB descriptor. Not
+ * every vendor-specific <code>DeploymentTool</code> will need to reference
+ * this value or may want to determine this value in a vendor-specific way.
+ *
+ * @return Public ID of the DTD specified in the EJB descriptor.
+ */
+ protected String getPublicId() {
+ return handler.getPublicId();
+ }
+
+ /**
+ * Get the manifest file to use for building the generic jar.
+ *
+ * If the file does not exist the global manifest from the config is used
+ * otherwise the default Ant manifest will be used.
+ *
+ * @param prefix the prefix where to llook for the manifest file based on
+ * the naming convention.
+ *
+ * @return the manifest file or null if the manifest file does not exist
+ */
+ protected File getManifestFile(String prefix) {
+ File manifestFile
+ = new File(getConfig().descriptorDir, prefix + "manifest.mf");
+ if (manifestFile.exists()) {
+ return manifestFile;
+ }
+
+ if (config.manifest != null) {
+ return config.manifest;
+ }
+ return null;
+ }
+
+ /**
+ * Method used to encapsulate the writing of the JAR file. Iterates over the
+ * filenames/java.io.Files in the Hashtable stored on the instance variable
+ * ejbFiles.
+ * @param baseName the base name to use.
+ * @param jarfile the jar file to write to.
+ * @param files the files to write to the jar.
+ * @param publicId the id to use.
+ * @throws BuildException if there is a problem.
+ */
+ protected void writeJar(String baseName, File jarfile, Hashtable files,
+ String publicId) throws BuildException {
+
+ JarOutputStream jarStream = null;
+ try {
+ // clean the addedfiles set
+ if (addedfiles == null) {
+ addedfiles = new HashSet();
+ } else {
+ addedfiles.clear();
+ }
+
+ /* If the jarfile already exists then whack it and recreate it.
+ * Should probably think of a more elegant way to handle this
+ * so that in case of errors we don't leave people worse off
+ * than when we started =)
+ */
+ if (jarfile.exists()) {
+ jarfile.delete();
+ }
+ jarfile.getParentFile().mkdirs();
+ jarfile.createNewFile();
+
+ InputStream in = null;
+ Manifest manifest = null;
+ try {
+ File manifestFile = (File) files.get(MANIFEST);
+ if (manifestFile != null && manifestFile.exists()) {
+ in = new FileInputStream(manifestFile);
+ } else {
+ String defaultManifest = "/org/apache/tools/ant/defaultManifest.mf";
+ in = this.getClass().getResourceAsStream(defaultManifest);
+ if (in == null) {
+ throw new BuildException("Could not find "
+ + "default manifest: " + defaultManifest);
+ }
+ }
+
+ manifest = new Manifest(in);
+ } catch (IOException e) {
+ throw new BuildException ("Unable to read manifest", e, getLocation());
+ } finally {
+ if (in != null) {
+ in.close();
+ }
+ }
+
+ // Create the streams necessary to write the jarfile
+
+ jarStream = new JarOutputStream(new FileOutputStream(jarfile), manifest);
+ jarStream.setMethod(JarOutputStream.DEFLATED);
+
+ // Loop through all the class files found and add them to the jar
+ for (Iterator entryIterator = files.keySet().iterator(); entryIterator.hasNext();) {
+ String entryName = (String) entryIterator.next();
+ if (entryName.equals(MANIFEST)) {
+ continue;
+ }
+
+ File entryFile = (File) files.get(entryName);
+
+ log("adding file '" + entryName + "'",
+ Project.MSG_VERBOSE);
+
+ addFileToJar(jarStream, entryFile, entryName);
+
+ // See if there are any inner classes for this class and add them in if there are
+ InnerClassFilenameFilter flt = new InnerClassFilenameFilter(entryFile.getName());
+ File entryDir = entryFile.getParentFile();
+ String[] innerfiles = entryDir.list(flt);
+ if (innerfiles != null) {
+ for (int i = 0, n = innerfiles.length; i < n; i++) {
+
+ //get and clean up innerclass name
+ int entryIndex = entryName.lastIndexOf(entryFile.getName()) - 1;
+ if (entryIndex < 0) {
+ entryName = innerfiles[i];
+ } else {
+ entryName = entryName.substring(0, entryIndex)
+ + File.separatorChar + innerfiles[i];
+ }
+ // link the file
+ entryFile = new File(config.srcDir, entryName);
+
+ log("adding innerclass file '" + entryName + "'",
+ Project.MSG_VERBOSE);
+
+ addFileToJar(jarStream, entryFile, entryName);
+
+ }
+ }
+ }
+ } catch (IOException ioe) {
+ String msg = "IOException while processing ejb-jar file '"
+ + jarfile.toString()
+ + "'. Details: "
+ + ioe.getMessage();
+ throw new BuildException(msg, ioe);
+ } finally {
+ if (jarStream != null) {
+ try {
+ jarStream.close();
+ } catch (IOException closeException) {
+ // ignore
+ }
+ }
+ }
+ } // end of writeJar
+
+
+ /**
+ * Add all available classes, that depend on Remote, Home, Bean, PK
+ * @param checkEntries files, that are extracted from the deployment descriptor
+ * @throws BuildException if there is a problem.
+ */
+ protected void checkAndAddDependants(Hashtable checkEntries)
+ throws BuildException {
+
+ if (dependencyAnalyzer == null) {
+ return;
+ }
+
+ dependencyAnalyzer.reset();
+
+ Iterator i = checkEntries.keySet().iterator();
+ while (i.hasNext()) {
+ String entryName = (String) i.next();
+ if (entryName.endsWith(".class")) {
+ String className = entryName.substring(0,
+ entryName.length() - ".class".length());
+ className = className.replace(File.separatorChar, '/');
+ className = className.replace('/', '.');
+
+ dependencyAnalyzer.addRootClass(className);
+ }
+ }
+
+ Enumeration e = dependencyAnalyzer.getClassDependencies();
+
+ while (e.hasMoreElements()) {
+ String classname = (String) e.nextElement();
+ String location
+ = classname.replace('.', File.separatorChar) + ".class";
+ File classFile = new File(config.srcDir, location);
+ if (classFile.exists()) {
+ checkEntries.put(location, classFile);
+ log("dependent class: " + classname + " - " + classFile,
+ Project.MSG_VERBOSE);
+ }
+ }
+ }
+
+
+ /**
+ * Returns a Classloader object which parses the passed in generic EjbJar classpath.
+ * The loader is used to dynamically load classes from javax.ejb.* and the classes
+ * being added to the jar.
+ * @return a classloader.
+ */
+ protected ClassLoader getClassLoaderForBuild() {
+ if (classpathLoader != null) {
+ return classpathLoader;
+ }
+
+ Path combinedClasspath = getCombinedClasspath();
+
+ // only generate a new ClassLoader if we have a classpath
+ if (combinedClasspath == null) {
+ classpathLoader = getClass().getClassLoader();
+ } else {
+ // Memory leak in line below
+ classpathLoader
+ = getTask().getProject().createClassLoader(combinedClasspath);
+ }
+
+ return classpathLoader;
+ }
+
+ /**
+ * Called to validate that the tool parameters have been configured.
+ *
+ * @throws BuildException If the Deployment Tool's configuration isn't
+ * valid
+ */
+ public void validateConfigured() throws BuildException {
+ if ((destDir == null) || (!destDir.isDirectory())) {
+ String msg = "A valid destination directory must be specified "
+ + "using the \"destdir\" attribute.";
+ throw new BuildException(msg, getLocation());
+ }
+ }
+}