diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java b/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java index 72077a143..8a684cffe 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java @@ -218,6 +218,19 @@ public class EjbJar extends MatchingTask { return tool; } + /** + * Create a websphere nested element used to configure a + * deployment tool for Websphere 4.0 server. + * + * @return the deployment tool instance to be configured. + */ + public WebsphereDeploymentTool createWebsphere() { + WebsphereDeploymentTool tool = new WebsphereDeploymentTool(); + tool.setTask(this); + deploymentTools.add(tool); + return tool; + } + /** * Create a Borland nested element used to configure a * deployment tool for Borland server. diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java b/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java index 39f354ecd..d6df5b96e 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java @@ -60,6 +60,9 @@ import java.io.InputStream; import java.io.FileOutputStream; import java.util.Hashtable; import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.HashSet; import java.util.Iterator; import java.util.ArrayList; import java.util.jar.JarOutputStream; @@ -71,6 +74,13 @@ import javax.xml.parsers.SAXParser; import org.xml.sax.InputSource; import org.xml.sax.SAXException; +import org.apache.tools.ant.*; +import org.apache.tools.ant.types.*; +import org.apache.tools.ant.util.depend.Dependencies; +import org.apache.tools.ant.util.depend.Filter; +import org.apache.bcel.classfile.*; +import org.apache.bcel.*; + import org.apache.tools.ant.Task; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Location; @@ -80,6 +90,7 @@ import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.FileSet; + /** * 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 @@ -204,7 +215,7 @@ public class GenericDeploymentTool implements EJBDeploymentTool { /** * Get the classpath by combining the one from the surrounding task, if any - * and the one from tis tool. + * and the one from this tool. */ protected Path getCombinedClasspath() { Path combinedPath = classpath; @@ -339,8 +350,8 @@ public class GenericDeploymentTool implements EJBDeploymentTool { // now the vendor specific files, if any addVendorFiles(ejbFiles, ddPrefix); - // add any inherited files - checkAndAddInherited(ejbFiles); + // 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! @@ -585,6 +596,7 @@ public class GenericDeploymentTool implements EJBDeploymentTool { * . If the jarFile's timestamp is more recent than * each EJB file, true is returned. Otherwise, false * 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 @@ -596,6 +608,7 @@ public class GenericDeploymentTool implements EJBDeploymentTool { protected boolean needToRebuild(Hashtable ejbFiles, File jarFile) { if (jarFile.exists()) { long lastBuild = jarFile.lastModified(); + if (config.manifest != null && config.manifest.exists() && config.manifest.lastModified() > lastBuild) { log("Build needed because manifest " + config.manifest + " is out of date", @@ -660,7 +673,11 @@ public class GenericDeploymentTool implements EJBDeploymentTool { InputStream in = null; Manifest manifest = null; try { - if (config.manifest != null) { + File manifestFile = new File(getConfig().descriptorDir, baseName + "-manifest.mf"); + if (manifestFile.exists()) { + in = new FileInputStream(manifestFile); + } + else if (config.manifest != null) { in = new FileInputStream(config.manifest); if ( in == null ) { throw new BuildException("Could not find manifest file: " + config.manifest, @@ -744,106 +761,63 @@ public class GenericDeploymentTool implements EJBDeploymentTool { } } // end of writeJar + /** - * Check if a EJB Class Inherits from a Superclass, and if a Remote Interface - * extends an interface other then javax.ejb.EJBObject directly. Then add those - * classes to the generic-jar so they dont have to added elsewhere. - * + * Add all available classes, that depend on Remote, Home, Bean, PK + * @param checkEntries files, that are extracted from the deployment descriptor */ - protected void checkAndAddInherited(Hashtable checkEntries) throws BuildException + protected void checkAndAddDependants(Hashtable checkEntries) + throws BuildException { - //Copy hashtable so were not changing the one we iterate through - Hashtable copiedHash = (Hashtable)checkEntries.clone(); + Dependencies visitor = new Dependencies(); + Set set = new TreeSet(); + Set newSet = new HashSet(); + final String base = config.srcDir.getAbsolutePath() + File.separator; + + Iterator i = checkEntries.keySet().iterator(); + while (i.hasNext()) { + String entryName = (String)i.next(); + if (entryName.endsWith(".class")) + newSet.add(entryName.substring(0, entryName.length() - ".class".length()).replace(File.separatorChar, '/')); + } + set.addAll(newSet); - // Walk base level EJBs and see if they have superclasses or extend extra interfaces which extend EJBObject - for (Iterator entryIterator = copiedHash.keySet().iterator(); entryIterator.hasNext(); ) - { - String entryName = (String)entryIterator.next(); - File entryFile = (File)copiedHash.get(entryName); + do { + i = newSet.iterator(); + while (i.hasNext()) { + String fileName = base + ((String)i.next()).replace('/', File.separatorChar) + ".class"; - // only want class files, xml doesnt reflect very well =) - if (entryName.endsWith(".class")) - { - String classname = entryName.substring(0,entryName.lastIndexOf(".class")).replace(File.separatorChar,'.'); - ClassLoader loader = getClassLoaderForBuild(); try { - Class c = loader.loadClass(classname); - - // No primatives!! sanity check, probably not nessesary - if (!c.isPrimitive()) - { - if (c.isInterface()) //get as an interface - { - log("looking at interface " + c.getName(), Project.MSG_VERBOSE); - Class[] interfaces = c.getInterfaces(); - for (int i = 0; i < interfaces.length; i++){ - log(" implements " + interfaces[i].getName(), Project.MSG_VERBOSE); - addInterface(interfaces[i], checkEntries); - } - } - else // get as a class - { - log("looking at class " + c.getName(), Project.MSG_VERBOSE); - Class s = c.getSuperclass(); - addSuperClass(c.getSuperclass(), checkEntries); - } - } //if primative + JavaClass javaClass = new ClassParser(fileName).parse(); + javaClass.accept(visitor); } - catch (ClassNotFoundException cnfe) { - log("Could not load class " + classname + " for super class check", - Project.MSG_WARN); - } - catch (NoClassDefFoundError ncdfe) { - log("Could not fully load class " + classname + " for super class check", - Project.MSG_WARN); - } - } //if - } // while - } - - private void addInterface(Class theInterface, Hashtable checkEntries) { - if (!theInterface.getName().startsWith("java")) // do not add system interfaces - { - File interfaceFile = new File(config.srcDir.getAbsolutePath() - + File.separatorChar - + theInterface.getName().replace('.',File.separatorChar) - + ".class" - ); - if (interfaceFile.exists() && interfaceFile.isFile()) - { - checkEntries.put(theInterface.getName().replace('.',File.separatorChar)+".class", - interfaceFile); - Class[] superInterfaces = theInterface.getInterfaces(); - for (int i = 0; i < superInterfaces.length; i++) { - addInterface(superInterfaces[i], checkEntries); + catch (IOException e) { + log("exception: " + e.getMessage(), Project.MSG_INFO); } } + newSet.clear(); + newSet.addAll(visitor.getDependencies()); + visitor.clearDependencies(); + + Dependencies.applyFilter(newSet, new Filter() { + public boolean accept(Object object) { + String fileName = base + ((String)object).replace('/', File.separatorChar) + ".class"; + return new File(fileName).exists(); + } + }); + newSet.removeAll(set); + set.addAll(newSet); } - } - - private void addSuperClass(Class superClass, Hashtable checkEntries) { - - if (!superClass.getName().startsWith("java")) - { - File superClassFile = new File(config.srcDir.getAbsolutePath() - + File.separatorChar - + superClass.getName().replace('.',File.separatorChar) - + ".class"); - if (superClassFile.exists() && superClassFile.isFile()) - { - checkEntries.put(superClass.getName().replace('.',File.separatorChar) + ".class", - superClassFile); - - // now need to get super classes and interfaces for this class - Class[] superInterfaces = superClass.getInterfaces(); - for (int i = 0; i < superInterfaces.length; i++) { - addInterface(superInterfaces[i], checkEntries); - } - - addSuperClass(superClass.getSuperclass(), checkEntries); - } + while (newSet.size() > 0); + + i = set.iterator(); + while (i.hasNext()) { + String next = ((String)i.next()).replace('/', File.separatorChar); + checkEntries.put(next + ".class", new File(base + next + ".class")); + log("dependent class: " + next + ".class" + " - " + base + next + ".class", Project.MSG_VERBOSE); } } + /** * Returns a Classloader object which parses the passed in generic EjbJar classpath. diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WebsphereDeploymentTool.java b/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WebsphereDeploymentTool.java new file mode 100644 index 000000000..25a4d3eb2 --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WebsphereDeploymentTool.java @@ -0,0 +1,1712 @@ +/* + + * The Apache Software License, Version 1.1 + + * + + * Copyright (c) 2000 The Apache Software Foundation. All rights + + * reserved. + + * + + * Redistribution and use in source and binary forms, with or without + + * modification, are permitted provided that the following conditions + + * are met: + + * + + * 1. Redistributions of source code must retain the above copyright + + * notice, this list of conditions and the following disclaimer. + + * + + * 2. Redistributions in binary form must reproduce the above copyright + + * notice, this list of conditions and the following disclaimer in + + * the documentation and/or other materials provided with the + + * distribution. + + * + + * 3. The end-user documentation included with the redistribution, if + + * any, must include the following acknowasegement: + + * "This product includes software developed by the + + * Apache Software Foundation (http://www.apache.org/)." + + * Alternately, this acknowasegement may appear in the software itself, + + * if and wherever such third-party acknowasegements normally appear. + + * + + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + + * Foundation" must not be used to endorse or promote products derived + + * from this software without prior written permission. For written + + * permission, please contact apache@apache.org. + + * + + * 5. Products derived from this software may not be called "Apache" + + * nor may "Apache" appear in their names without prior written + + * permission of the Apache Group. + + * + + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + + * SUCH DAMAGE. + + * ==================================================================== + + * + + * This software consists of voluntary contributions made by many + + * individuals on behalf of the Apache Software Foundation. For more + + * information on the Apache Software Foundation, please see + + * . + + */ + + + +package org.apache.tools.ant.taskdefs.optional.ejb; + + + +import java.io.*; + +import java.util.jar.*; + +import java.util.*; + +import java.net.*; + + + +import javax.xml.parsers.*; + +import org.xml.sax.*; + + + +import org.apache.tools.ant.*; + +import org.apache.tools.ant.taskdefs.*; + +import org.apache.tools.ant.types.*; + +import org.apache.tools.ant.taskdefs.ExecTask; + +/** + + * Websphere deployment tool that augments the ejbjar task. + + * @author Maneesh Sahu + + */ + +public class WebsphereDeploymentTool extends GenericDeploymentTool { + + /** + + * Enumerated attribute with the values for the database vendor types + + */ + + public static class DBVendor extends EnumeratedAttribute { + + public String[] getValues() { + + return new String[] { + + "SQL92", "SQL99", "DB2UDBWIN_V71", "DB2UDBOS390_V6", "DB2UDBAS400_V4R5", + + "ORACLE_V8", "INFORMIX_V92", "SYBASE_V1192", "MSSQLSERVER_V7", "MYSQL_V323" + + }; + + } + + } + + + + public static final String PUBLICID_EJB11 + + = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN"; + + public static final String PUBLICID_EJB20 + + = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"; + + protected static final String SCHEMA_DIR = "Schema/"; + + + + protected static final String WAS_EXT = "ibm-ejb-jar-ext.xmi"; + + protected static final String WAS_BND = "ibm-ejb-jar-bnd.xmi"; + + protected static final String WAS_CMP_MAP = "Map.mapxmi"; + + protected static final String WAS_CMP_SCHEMA = "Schema.dbxmi"; + + + + /** Instance variable that stores the suffix for the websphere jarfile. */ + + private String jarSuffix = ".jar"; + + + + /** Instance variable that stores the location of the ejb 1.1 DTD file. */ + + private String ejb11DTD; + + + + /** Instance variable that determines whether generic ejb jars are kept. */ + + + + private boolean keepgenerated = false; + + + + private String additionalArgs = ""; + + + + private boolean keepGeneric = false; + + + + private String compiler = null; + + + + private boolean alwaysRebuild = true; + + + + private boolean ejbdeploy = true; + + + + /** + + * Indicates if the old CMP location convention is to be used. + + */ + + private boolean newCMP = false; + + + + /** The classpath to the websphere classes. */ + + private Path wasClasspath = null; + + + + /** The DB Vendor name, the EJB is persisted against */ + + private String dbVendor; + + + + /** The name of the database to create. (For top-down mapping only) */ + + private String dbName; + + + + /** The name of the schema to create. (For top-down mappings only) */ + + private String dbSchema; + + + + /** true - Only generate the deployment code, do not run RMIC or Javac */ + + private boolean codegen; + + + + /** true - Only output error messages, suppress informational messages */ + + private boolean quiet = true; + + + + /** true - Disable the validation steps */ + + private boolean novalidate; + + + + /** true - Disable warning and informational messages */ + + private boolean nowarn; + + + + /** true - Disable informational messages */ + + private boolean noinform; + + + + /** true - Enable internal tracing */ + + private boolean trace; + + + + /** Additional options for RMIC */ + + private String rmicOptions; + + + + /** true- Use the WebSphere 3.5 compatible mapping rules */ + + private boolean use35MappingRules; + + + + /** the scratchdir for the ejbdeploy operation */ + + private String tempdir = "_ejbdeploy_temp"; + + + + /** + + * Get the classpath to the websphere classpaths + + */ + + public Path createWASClasspath() { + + if (wasClasspath == null) { + + wasClasspath = new Path(getTask().getProject()); + + } + + return wasClasspath.createPath(); + + } + + + + public void setWASClasspath(Path wasClasspath) { + + this.wasClasspath = wasClasspath; + + } + + + + /** + + * Sets the DB Vendor for the Entity Bean mapping + + */ + + public void setDbvendor(DBVendor dbvendor) { + + this.dbVendor = dbvendor.getValue(); + + } + + + + /** + + * Sets the name of the Database to create + + * @param String + + */ + + public void setDbname(String dbName) { + + this.dbName = dbName; + + } + + + + /** + + * Sets the name of the schema to create + + * @param String + + */ + + public void setDbschema(String dbSchema) { + + this.dbSchema = dbSchema; + + } + + + + /** + + * (true) Only generate the deployment code, do not run RMIC or Javac + + * @param boolean + + */ + + public void setCodegen(boolean codegen) { + + this.codegen = codegen; + + } + + + + /** + + * (true) Only output error messages, suppress informational messages + + * @param boolean + + */ + + public void setQuiet(boolean quiet) { + + this.quiet = quiet; + + } + + + + /** + + * (true) Disable the validation steps + + * @param boolean + + */ + + public void setNovalidate(boolean novalidate) { + + this.novalidate = novalidate; + + } + + + + /** + + * (true) Disable warning and informational messages + + * @param boolean + + */ + + public void setNowarn(boolean nowarn) { + + this.nowarn = nowarn; + + } + + + + /** + + * (true) Disable informational messages + + * @param boolean + + */ + + public void setNoinform(boolean noinfom) { + + this.noinform = noinform; + + } + + + + /** + + * (true) Enable internal tracing + + * @param boolean + + */ + + public void setTrace(boolean trace) { + + this.trace = trace; + + } + + + + /** + + * (true) Use the WebSphere 3.5 compatible mapping rules + + * @param boolean + + */ + + public void setUse35(boolean attr) { + + use35MappingRules = attr; + + } + + + + /** + + * The compiler (switch -compiler) to use + + */ + + public void setCompiler(String compiler) { + + this.compiler = compiler; + + } + + + + /** + + * Set the rebuild flag to false to only update changes in the + + * jar rather than rerunning ejbdeploy + + */ + + public void setRebuild(boolean rebuild) { + + this.alwaysRebuild = rebuild; + + } + + + + + + /** + + * Setter used to store the suffix for the generated websphere jar file. + + * @param inString the string to use as the suffix. + + */ + + public void setSuffix(String inString) { + + this.jarSuffix = inString; + + } + + + + /** + + * Setter used to store the value of keepGeneric + + * @param inValue a string, either 'true' or 'false'. + + */ + + public void setKeepgeneric(boolean inValue) { + + this.keepGeneric = inValue; + + } + + + + /** + + * Sets whether -keepgenerated is passed to ejbdeploy (that is, + + * the .java source files are kept). + + * @param inValue either 'true' or 'false' + + */ + + public void setKeepgenerated(String inValue) { + + this.keepgenerated = Boolean.valueOf(inValue).booleanValue(); + + } + + + + /** + + * Decide, wether ejbdeploy should be called or not + + * @param ejbdeploy + + */ + + public void setEjbdeploy(boolean ejbdeploy) { + + this.ejbdeploy = ejbdeploy; + + } + + + + /** + + * sets some additional args to send to ejbdeploy. + + */ + + public void setArgs(String args) { + + this.additionalArgs = args; + + } + + + + /** + + * Setter used to store the location of the Sun's Generic EJB DTD. + + * This can be a file on the system or a resource on the classpath. + + * @param inString the string to use as the DTD location. + + */ + + public void setEJBdtd(String inString) { + + this.ejb11DTD = inString; + + } + + + + /** + + * Set the value of the oldCMP scheme. This is an antonym for + + * newCMP + + */ + + public void setOldCMP(boolean oldCMP) { + + this.newCMP = !oldCMP; + + } + + + + /** + + * Set the value of the newCMP scheme. The old CMP scheme locates the + + * websphere CMP descriptor based on the naming convention where the + + * websphere CMP file is expected to be named with the bean name as the prefix. + + * + + * Under this scheme the name of the CMP descriptor does not match the name + + * actually used in the main websphere EJB descriptor. Also, descriptors which + + * contain multiple CMP references could not be used. + + * + + */ + + public void setNewCMP(boolean newCMP) { + + this.newCMP = newCMP; + + } + + + + /** + + * Sets the temporary directory for the ejbdeploy task + + */ + + public void setTempdir(String tempdir) { + + this.tempdir = tempdir; + + } + + + + protected DescriptorHandler getDescriptorHandler(File srcDir) { + + DescriptorHandler handler = new DescriptorHandler(getTask(), srcDir); + + // register all the DTDs, both the ones that are known and + + // any supplied by the user + + handler.registerDTD(PUBLICID_EJB11, ejb11DTD); + + + + for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) { + + EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation)i.next(); + + handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation()); + + } + + + + return handler; + + } + + + + protected DescriptorHandler getWebsphereDescriptorHandler(final File srcDir) { + + DescriptorHandler handler = new DescriptorHandler(getTask(), srcDir) { + + protected void processElement() { + + } + + }; + + + + for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) { + + EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation)i.next(); + + handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation()); + + } + + return handler; + + } + + + + /** + + * Add any vendor specific files which should be included in the + + * EJB Jar. + + */ + + protected void addVendorFiles(Hashtable ejbFiles, String baseName) { + + + + String ddPrefix = (usingBaseJarName() ? "" : baseName); + + String dbPrefix = (dbVendor == null)? "": dbVendor + "-"; + + + + // Get the Extensions document + + File websphereEXT = new File(getConfig().descriptorDir, ddPrefix + WAS_EXT); + + if (websphereEXT.exists()) { + + ejbFiles.put(META_DIR + WAS_EXT, + + websphereEXT); + + } else { + + log("Unable to locate websphere extensions. It was expected to be in " + + + websphereEXT.getPath(), Project.MSG_VERBOSE); + + } + + + + File websphereBND = new File(getConfig().descriptorDir, ddPrefix + WAS_BND); + + if (websphereBND.exists()) { + + ejbFiles.put(META_DIR + WAS_BND, + + websphereBND); + + }else { + + log("Unable to locate websphere bindings. It was expected to be in " + + + websphereBND.getPath(), Project.MSG_VERBOSE); + + } + + + + if (!newCMP) { + + log("The old method for locating CMP files has been DEPRECATED.", Project.MSG_VERBOSE); + + log("Please adjust your websphere descriptor and set newCMP=\"true\" " + + + "to use the new CMP descriptor inclusion mechanism. ", Project.MSG_VERBOSE); + + } + + else { + + // We attempt to put in the MAP and Schema files of CMP beans + + try + + { + + // Add the Map file + + File websphereMAP = new File(getConfig().descriptorDir, + + ddPrefix + dbPrefix + WAS_CMP_MAP); + + if (websphereMAP.exists()) { + + ejbFiles.put(META_DIR + WAS_CMP_MAP, + + websphereMAP); + + } else { + + log("Unable to locate the websphere Map: " + + + websphereMAP.getPath(), Project.MSG_VERBOSE); + + } + + File websphereSchema = new File(getConfig().descriptorDir, + + ddPrefix + dbPrefix + WAS_CMP_SCHEMA); + + if (websphereSchema.exists()) { + + ejbFiles.put(META_DIR + SCHEMA_DIR + WAS_CMP_SCHEMA, + + websphereSchema); + + } else { + + log("Unable to locate the websphere Schema: " + + + websphereSchema.getPath(), Project.MSG_VERBOSE); + + } + + // Theres nothing else to see here...keep moving sonny + + } + + catch(Exception e) + + { + + String msg = "Exception while adding Vendor specific files: " + + + e.toString(); + + throw new BuildException(msg, e); + + } + + } + + } + + + + /** + + * 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. + + */ + + File getVendorOutputJarFile(String baseName) { + + return new File(getDestDir(), baseName + jarSuffix); + + } + + + + /** + + * Gets the options for the EJB Deploy operation + + * @return String + + */ + + protected String getOptions() { + + // Set the options + + StringBuffer options = new StringBuffer(); + + if (dbVendor != null) { + + options.append(" -dbvendor ").append(dbVendor); + + } + + if (dbName != null) { + + options.append(" -dbname \"").append(dbName).append("\""); + + } + + + + if (dbSchema != null) { + + options.append(" -dbschema \"").append(dbSchema).append("\""); + + } + + + + if (codegen) { + + options.append(" -codegen"); + + } + + + + if (quiet) { + + options.append(" -quiet"); + + } + + + + if (novalidate) { + + options.append(" -novalidate"); + + } + + + + if (nowarn) { + + options.append(" -nowarn"); + + } + + + + if (noinform) { + + options.append(" -noinform"); + + } + + + + if (trace) { + + options.append(" -trace"); + + } + + + + if (use35MappingRules) { + + options.append(" -35"); + + } + + + + if (rmicOptions != null) { + + options.append(" -rmic \"").append(rmicOptions).append("\""); + + } + + + + return options.toString(); + + } // end getOptions + + + + /** + + * Helper method invoked by execute() for each websphere jar to be built. + + * Encapsulates the logic of constructing a java task for calling + + * websphere.ejbdeploy and executing it. + + * @param sourceJar java.io.File representing the source (EJB1.1) jarfile. + + * @param destJar java.io.File representing the destination, websphere + + * jarfile. + + */ + + private void buildWebsphereJar(File sourceJar, File destJar) { + + try { + + if (ejbdeploy) { + + String args = + + " " + sourceJar.getPath() + + + " " + tempdir + + + " " + destJar.getPath() + + + " " + getOptions(); + + + + if (getCombinedClasspath() != null && getCombinedClasspath().toString().length() > 0) + + args += " -cp " + getCombinedClasspath(); + + + + // Why do my ""'s get stripped away??? + + log("EJB Deploy Options: " + args, Project.MSG_VERBOSE); + + + + Java javaTask = (Java)getTask().getProject().createTask("java"); + + // Set the JvmArgs + + javaTask.createJvmarg().setValue("-Xms64m"); + + javaTask.createJvmarg().setValue("-Xmx128m"); + + + + // Set the Environment variable + + Environment.Variable var = new Environment.Variable(); + + var.setKey("websphere.lib.dir"); + + var.setValue(getTask().getProject().getProperty("websphere.home") + "/lib"); + + javaTask.addSysproperty(var); + + + + // Set the working directory + + javaTask.setDir(new File(getTask().getProject().getProperty("websphere.home"))); + + + + // Set the Java class name + + javaTask.setTaskName("ejbdeploy"); + + javaTask.setClassname("com.ibm.etools.ejbdeploy.EJBDeploy"); + + + + Commandline.Argument arguments = javaTask.createArg(); + + arguments.setLine(args); + + + + Path classpath = wasClasspath; + + if (classpath == null) { + + classpath = getCombinedClasspath(); + + } + + + + if (classpath != null) { + + javaTask.setClasspath(classpath); + + javaTask.setFork(true); + + } + + else { + + javaTask.setFork(true); + + } + + + + log("Calling websphere.ejbdeploy for " + sourceJar.toString(), + + Project.MSG_VERBOSE); + + + + javaTask.execute(); + + } + + } + + catch (Exception e) { + + // Have to catch this because of the semantics of calling main() + + String msg = "Exception while calling ejbdeploy. Details: " + e.toString(); + + throw new BuildException(msg, e); + + } + + } + + + + /** + + * 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. + + */ + + protected void writeJar(String baseName, File jarFile, Hashtable files, String publicId) + + throws BuildException + + { + + if (ejbdeploy) { + + // create the -generic.jar, if required + + File genericJarFile = super.getVendorOutputJarFile(baseName); + + super.writeJar(baseName, genericJarFile, files, publicId); + + + + // create the output .jar, if required + + if (alwaysRebuild || isRebuildRequired(genericJarFile, jarFile)) { + + buildWebsphereJar(genericJarFile, jarFile); + + } + + if (!keepGeneric) { + + log("deleting generic jar " + genericJarFile.toString(), + + Project.MSG_VERBOSE); + + genericJarFile.delete(); + + } + + } + + else { + + // create the "undeployed" output .jar, if required + + super.writeJar(baseName, jarFile, files, publicId); + + } + + /* + + // need to create a generic jar first. + + File genericJarFile = super.getVendorOutputJarFile(baseName); + + super.writeJar(baseName, genericJarFile, files, publicId); + + + + if (alwaysRebuild || isRebuildRequired(genericJarFile, jarFile)) { + + buildWebsphereJar(genericJarFile, jarFile); + + } + + if (!keepGeneric) { + + log("deleting generic jar " + genericJarFile.toString(), + + Project.MSG_VERBOSE); + + genericJarFile.delete(); + + } + + */ + + } + + + + /** + + * Called to validate that the tool parameters have been configured. + + * + + */ + + public void validateConfigured() throws BuildException { + + super.validateConfigured(); + + } + + + + + + /** + + * Helper method to check to see if a websphere EBJ1.1 jar needs to be rebuilt using + + * ejbdeploy. Called from writeJar it sees if the "Bean" classes are the only thing that needs + + * to be updated and either updates the Jar with the Bean classfile or returns true, + + * saying that the whole websphere jar needs to be regened with ejbdeploy. This allows faster + + * build times for working developers. + + *

+ + * The way websphere ejbdeploy works is it creates wrappers for the publicly defined methods as + + * they are exposed in the remote interface. If the actual bean changes without changing the + + * the method signatures then only the bean classfile needs to be updated and the rest of the + + * websphere jar file can remain the same. If the Interfaces, ie. the method signatures change + + * or if the xml deployment dicriptors changed, the whole jar needs to be rebuilt with ejbdeploy. + + * This is not strictly true for the xml files. If the JNDI name changes then the jar doesnt + + * have to be rebuild, but if the resources references change then it does. At this point the + + * websphere jar gets rebuilt if the xml files change at all. + + * + + * @param genericJarFile java.io.File The generic jar file. + + * @param websphereJarFile java.io.File The websphere jar file to check to see if it needs to be rebuilt. + + */ + + protected boolean isRebuildRequired(File genericJarFile, File websphereJarFile) + + { + + boolean rebuild = false; + + + + JarFile genericJar = null; + + JarFile wasJar = null; + + File newwasJarFile = null; + + JarOutputStream newJarStream = null; + + + + try + + { + + log("Checking if websphere Jar needs to be rebuilt for jar " + websphereJarFile.getName(), + + Project.MSG_VERBOSE); + + // Only go forward if the generic and the websphere file both exist + + if (genericJarFile.exists() && genericJarFile.isFile() + + && websphereJarFile.exists() && websphereJarFile.isFile()) + + { + + //open jar files + + genericJar = new JarFile(genericJarFile); + + wasJar = new JarFile(websphereJarFile); + + + + Hashtable genericEntries = new Hashtable(); + + Hashtable wasEntries = new Hashtable(); + + Hashtable replaceEntries = new Hashtable(); + + + + //get the list of generic jar entries + + for (Enumeration e = genericJar.entries(); e.hasMoreElements();) + + { + + JarEntry je = (JarEntry)e.nextElement(); + + genericEntries.put(je.getName().replace('\\', '/'), je); + + } + + //get the list of websphere jar entries + + for (Enumeration e = wasJar.entries() ; e.hasMoreElements();) + + { + + JarEntry je = (JarEntry)e.nextElement(); + + wasEntries.put(je.getName(), je); + + } + + + + //Cycle Through generic and make sure its in websphere + + ClassLoader genericLoader = getClassLoaderFromJar(genericJarFile); + + for (Enumeration e = genericEntries.keys(); e.hasMoreElements();) + + { + + String filepath = (String)e.nextElement(); + + if (wasEntries.containsKey(filepath)) // File name/path match + + { + + // Check files see if same + + JarEntry genericEntry = (JarEntry)genericEntries.get(filepath); + + JarEntry wasEntry = (JarEntry)wasEntries.get(filepath); + + if ((genericEntry.getCrc() != wasEntry.getCrc()) || // Crc's Match + + (genericEntry.getSize() != wasEntry.getSize()) ) // Size Match + + { + + if (genericEntry.getName().endsWith(".class")) + + { + + //File are different see if its an object or an interface + + String classname = genericEntry.getName().replace(File.separatorChar,'.'); + + classname = classname.substring(0,classname.lastIndexOf(".class")); + + Class genclass = genericLoader.loadClass(classname); + + if (genclass.isInterface()) + + { + + //Interface changed rebuild jar. + + log("Interface " + genclass.getName() + " has changed",Project.MSG_VERBOSE); + + rebuild = true; + + break; + + } + + else + + { + + //Object class Changed update it. + + replaceEntries.put(filepath, genericEntry); + + } + + } + + else + + { + + // is it the manifest. If so ignore it + + if (!genericEntry.getName().equals("META-INF/MANIFEST.MF")) { + + //File other then class changed rebuild + + log("Non class file " + genericEntry.getName() + " has changed",Project.MSG_VERBOSE); + + rebuild = true; + + } + + break; + + } + + } + + } + + else // a file doesnt exist rebuild + + { + + log("File " + filepath + " not present in websphere jar",Project.MSG_VERBOSE); + + rebuild = true; + + break; + + } + + } + + + + if (!rebuild) + + { + + log("No rebuild needed - updating jar",Project.MSG_VERBOSE); + + newwasJarFile = new File(websphereJarFile.getAbsolutePath() + ".temp"); + + if (newwasJarFile.exists()) { + + newwasJarFile.delete(); + + } + + + + newJarStream = new JarOutputStream(new FileOutputStream(newwasJarFile)); + + newJarStream.setLevel(0); + + + + //Copy files from old websphere jar + + for (Enumeration e = wasEntries.elements() ; e.hasMoreElements();) + + { + + byte[] buffer = new byte[1024]; + + int bytesRead; + + InputStream is; + + JarEntry je = (JarEntry)e.nextElement(); + + if (je.getCompressedSize() == -1 || + + je.getCompressedSize() == je.getSize()) { + + newJarStream.setLevel(0); + + } + + else { + + newJarStream.setLevel(9); + + } + + + + // Update with changed Bean class + + if (replaceEntries.containsKey(je.getName())) + + { + + log("Updating Bean class from generic Jar " + je.getName(), + + Project.MSG_VERBOSE); + + // Use the entry from the generic jar + + je = (JarEntry)replaceEntries.get(je.getName()); + + is = genericJar.getInputStream(je); + + } + + else //use fle from original websphere jar + + { + + is = wasJar.getInputStream(je); + + } + + newJarStream.putNextEntry(new JarEntry(je.getName())); + + + + while ((bytesRead = is.read(buffer)) != -1) + + { + + newJarStream.write(buffer,0,bytesRead); + + } + + is.close(); + + } + + } + + else + + { + + log("websphere Jar rebuild needed due to changed interface or XML",Project.MSG_VERBOSE); + + } + + } + + else + + { + + rebuild = true; + + } + + } + + catch(ClassNotFoundException cnfe) + + { + + String cnfmsg = "ClassNotFoundException while processing ejb-jar file" + + + ". Details: " + + + cnfe.getMessage(); + + throw new BuildException(cnfmsg, cnfe); + + } + + catch(IOException ioe) { + + String msg = "IOException while processing ejb-jar file " + + + ". Details: " + + + ioe.getMessage(); + + throw new BuildException(msg, ioe); + + } + + finally { + + // need to close files and perhaps rename output + + if (genericJar != null) { + + try { + + genericJar.close(); + + } + + catch (IOException closeException) {} + + } + + + + if (wasJar != null) { + + try { + + wasJar.close(); + + } + + catch (IOException closeException) {} + + } + + + + if (newJarStream != null) { + + try { + + newJarStream.close(); + + } + + catch (IOException closeException) {} + + + + websphereJarFile.delete(); + + newwasJarFile.renameTo(websphereJarFile); + + if (!websphereJarFile.exists()) { + + rebuild = true; + + } + + } + + } + + + + return rebuild; + + } + + + + /** + + * Helper method invoked by isRebuildRequired to get a ClassLoader for + + * a Jar File passed to it. + + * + + * @param classjar java.io.File representing jar file to get classes from. + + */ + + protected ClassLoader getClassLoaderFromJar(File classjar) throws IOException + + { + + Path lookupPath = new Path(getTask().getProject()); + + lookupPath.setLocation(classjar); + + + + Path classpath = getCombinedClasspath(); + + if (classpath != null) { + + lookupPath.append(classpath); + + } + + + + return new AntClassLoader(getTask().getProject(), lookupPath); + + } + +} +