/*
 * 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
 * <http://www.apache.org/>.
 */

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.types.*;
import org.apache.tools.ant.taskdefs.ExecTask;
/**
 * Websphere deployment tool that augments the ejbjar task.
 * @author <mailto:msahu@interkeel.com>Maneesh Sahu</mailto>
 */
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;
	
	/**
	 * 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 <code>-compiler</code>) 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();
	}
	
	/**
	 * 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(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(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 + getConfig().baseNameTerminator);
		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 {
			Path classpath = wasClasspath;
			if (classpath == null) {
				classpath = getCombinedClasspath();
			}
			
//			String scratchDir = getTask().getProject().getUserProperty("websphere.scratchdir");
//			String ejbDeployTool = getTask().getProject().getUserProperty("websphere.ejbdeploy");
//
//			log("Scratch Dir: " + scratchDir, Project.MSG_VERBOSE);
//			log("Deploy Tool: " + ejbDeployTool, Project.MSG_VERBOSE);
			
			String args = "";
			args += " " + sourceJar.getPath() +
				" " + "_ejb_temp" +
				" " + destJar.getPath() +
//				"  -cp \"" + classpath + "\"" +
				" " + getOptions();
			
			// Why do my ""'s get stripped away???
			log("EJB Deploy Options: " + args, Project.MSG_VERBOSE);
			
			ExecTask ejbdeployTask = (ExecTask) getTask().getProject().createTask("exec");
			// Invoking the Java class causes some wierd exception to be thrown...
			// org.eclipse...ejbdeploy.class not found. It should have been com.ibm.eclipse...
			// Until its resolved, this is the only solution. Also EJBDeploy works only on NT/2000!
			// See commented code for Java usage which didnt work for me
			ejbdeployTask.setExecutable("ejbdeploy.bat");
			
			Commandline.Argument arguments = ejbdeployTask.createArg();
			arguments.setLine(args);
			
			log("Calling websphere.ejbdeploy for " + sourceJar.toString(),
					Project.MSG_VERBOSE);
			
			ejbdeployTask.execute();
			
//			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().getUserProperty("websphere.home") +
//										 "\\lib\"");
//			javaTask.addSysproperty(var);
//
//			// 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) throws BuildException {
		// need to create a generic jar first.
		File genericJarFile = super.getVendorOutputJarFile(baseName);
		super.writeJar(baseName, genericJarFile, files);
		
		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.
	 * <p>
	 * 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);
	}
}
