Browse Source

Task to build EJB 1.1 jars. Currently this task supports the Weblogic

server but it is envisaged that it can be expanded to cover other app
servers.

Note that to build this task you need to define the property ejb.build.
You can do that with

    ant -Dejb.build=1

Submitted by:	Tim Fennell <TFennell@sapient.com>


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@267785 13f79535-47bb-0310-9956-ffa450edef68
master
Conor MacNeill 25 years ago
parent
commit
e02f0ab1e4
3 changed files with 647 additions and 0 deletions
  1. +1
    -0
      build.xml
  2. +1
    -0
      src/main/org/apache/tools/ant/taskdefs/defaults.properties
  3. +645
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java

+ 1
- 0
build.xml View File

@@ -79,6 +79,7 @@
<exclude name="**/DDCreator*.java" unless="ejb.DDCreator.present" /> <exclude name="**/DDCreator*.java" unless="ejb.DDCreator.present" />
<exclude name="**/WLRun.java" unless="ejb.wls.present" /> <exclude name="**/WLRun.java" unless="ejb.wls.present" />
<exclude name="**/WLStop.java" unless="ejb.wls.present" /> <exclude name="**/WLStop.java" unless="ejb.wls.present" />
<exclude name="**/EjbJar.java" unless="ejb.build" />
</javac> </javac>
<copydir src="${src.dir}" dest="${build.classes}"> <copydir src="${src.dir}" dest="${build.classes}">


+ 1
- 0
src/main/org/apache/tools/ant/taskdefs/defaults.properties View File

@@ -46,6 +46,7 @@ ddcreator=org.apache.tools.ant.taskdefs.optional.ejb.DDCreator
wlrun=org.apache.tools.ant.taskdefs.optional.ejb.WLRun wlrun=org.apache.tools.ant.taskdefs.optional.ejb.WLRun
wlstop=org.apache.tools.ant.taskdefs.optional.ejb.WLStop wlstop=org.apache.tools.ant.taskdefs.optional.ejb.WLStop
vssget=org.apache.tools.ant.taskdefs.optional.vss.MSVSSGET vssget=org.apache.tools.ant.taskdefs.optional.vss.MSVSSGET
ejbjar=org.apache.tools.ant.taskdefs.optional.ejb.EjbJar


# deprecated ant tasks (kept for back compatibility) # deprecated ant tasks (kept for back compatibility)
javadoc2=org.apache.tools.ant.taskdefs.Javadoc javadoc2=org.apache.tools.ant.taskdefs.Javadoc


+ 645
- 0
src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java View File

@@ -0,0 +1,645 @@
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 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 acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", 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;

// Standard java imports
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.File;
import java.io.IOException;
import java.util.jar.*;
import java.util.zip.*;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;

// XML imports
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.Parser;
import org.xml.sax.Locator;
import org.xml.sax.InputSource;
import org.xml.sax.AttributeList;
import org.xml.sax.DocumentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.ParserFactory;

// Apache/Ant imports
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.taskdefs.MatchingTask;
import org.apache.tools.ant.taskdefs.Java;

/**
* <p>Provides automated ejb jar file creation for ant. Extends the MatchingTask
* class provided in the default ant distribution to provide a directory scanning
* EJB jarfile generator.</p>
*
* <p>The task works by taking the deployment descriptors one at a time and
* parsing them to locate the names of the classes which should be placed in
* the jar. The classnames are translated to java.io.Files by replacing periods
* with File.separatorChar and resolving the generated filename as a relative
* path under the srcdir attribute. All necessary files are then assembled into
* a jarfile. One jarfile is constructed for each deployment descriptor found.
* </p>
*
* <p>Functionality is currently provided for standard EJB1.1 jars and Weblogic
* 5.1 jars. The weblogic deployment descriptors, used in constructing the
* Weblogic jar, are located based on a simple naming convention. The name of the
* standard deployment descriptor is taken upto the first instance of a String,
* specified by the attribute basenameterminator, and then the regular Weblogic
* descriptor name is appended. For example if basenameterminator is set to '-',
* its default value, and a standard descriptor is called Foo-ejb-jar.xml then
* the files Foo-weblogic-ejb-jar.xml and Foo-weblogic-cmp-rdbms-jar.xml will be
* looked for, and if found, included in the jarfile.</p>
*
* <p>Attributes and setter methods are provided to support optional generation
* of Weblogic5.1 jars, optional deletion of generic jar files, setting alternate
* values for basenameterminator, and setting the strings to append to the names
* of the generated jarfiles.</p>
*
* @author <a href="mailto:tfennell@sapient.com">Tim Fennell</a>
*/
public class EjbJar extends MatchingTask {

/**
* Inner class used by EjbJar to facilitate the parsing of deployment
* descriptors and the capture of appropriate information. Extends
* HandlerBase so it only implements the methods needed. During parsing
* creates a hashtable consisting of entries mapping the name it should be
* inserted into an EJB jar as to a File representing the file on disk. This
* list can then be accessed through the getFiles() method.
*/
protected class DescriptorHandler extends org.xml.sax.HandlerBase {
/**
* Bunch of constants used for storing entries in a hashtable, and for
* constructing the filenames of various parts of the ejb jar.
*/
private static final String HOME_INTERFACE = "home";
private static final String REMOTE_INTERFACE = "remote";
private static final String BEAN_CLASS = "ejb-class";
private static final String PK_CLASS = "prim-key-class";

/**
* Instance variable used to store the name of the current attribute being
* processed by the SAX parser. Accessed by the SAX parser call-back methods
* startElement() and endElement().
*/
private String currentAttribute = null;

/**
* Instance variable that stores the names of the files as they will be
* put into the jar file, mapped to File objects Accessed by the SAX
* parser call-back method characters().
*/
private Hashtable ejbFiles = null;

/** Instance variable to store the source directory of the task */

/**
* Getter method that returns the set of files to include in the EJB jar.
*/
public Hashtable getFiles() {
return (ejbFiles == null) ? new Hashtable() : ejbFiles;
}


/**
* SAX parser call-back method that is used to initialize the values of some
* instance variables to ensure safe operation.
*/
public void startDocument() throws SAXException {
this.ejbFiles = new Hashtable(10, 1);
this.currentAttribute = null;
}


/**
* SAX parser call-back method that is invoked when a new element is entered
* into. Used to store the context (attribute name) in the currentAttribute
* instance variable.
* @param name The name of the element being entered.
* @param attrs Attributes associated to the element.
*/
public void startElement(String name, AttributeList attrs)
throws SAXException {
this.currentAttribute = name;
}


/**
* SAX parser call-back method that is invoked when an element is exited.
* Used to blank out (set to the empty string, not nullify) the name of
* the currentAttribute. A better method would be to use a stack as an
* instance variable, however since we are only interested in leaf-node
* data this is a simpler and workable solution.
* @param name The name of the attribute being exited. Ignored
* in this implementation.
*/
public void endElement(String name) throws SAXException {
this.currentAttribute = "";
}

/**
* SAX parser call-back method invoked whenever characters are located within
* an element. currentAttribute (modified by startElement and endElement)
* tells us whether we are in an interesting element (one of the up to four
* classes of an EJB). If so then converts the classname from the format
* org.apache.tools.ant.Parser to the convention for storing such a class,
* org/apache/tools/ant/Parser.class. This is then resolved into a file
* object under the srcdir which is stored in a Hashtable.
* @param ch A character array containing all the characters in
* the element, and maybe others that should be ignored.
* @param start An integer marking the position in the char
* array to start reading from.
* @param length An integer representing an offset into the
* char array where the current data terminates.
*/
public void characters(char[] ch, int start, int length)
throws SAXException {
if (currentAttribute.equals(DescriptorHandler.HOME_INTERFACE) ||
currentAttribute.equals(DescriptorHandler.REMOTE_INTERFACE) ||
currentAttribute.equals(DescriptorHandler.BEAN_CLASS) ||
currentAttribute.equals(DescriptorHandler.PK_CLASS)) {
// Get the filename into a String object
File classFile = null;
String className = new String(ch, start, length);

// If it's a primitive wrapper then we shouldn't try and put
// it into the jar, so ignore it.
if (!className.startsWith("java.lang")) {
// Translate periods into path separators, add .class to the
// name, create the File object and add it to the Hashtable.
className = className.replace('.', File.separatorChar);
className += ".class";
classFile = new File(srcdir, className);
ejbFiles.put(className, classFile);
}
}
}
} // End of DescriptorHandler

/** Private constants that are used when constructing the standard jarfile */
private static final String META_DIR = "META-INF/";
private static final String EJB_DD = "ejb-jar.xml";
private static final String WL_DD = "weblogic-ejb-jar.xml";
private static final String WL_CMP_DD = "weblogic-cmp-rdbms-jar.xml";

/** Stores a handle to the directory under which to search for files */
private File srcdir = null;

/** Stores a handle to the directory to put the Jar files in */
private File destdir = null;

/** Instance variable that determines whether to generate weblogic jars. */
private boolean generateweblogic = false;

/** Instance variable that determines whether generic ejb jars are kept. */
private boolean keepgeneric = true;
/** Instance variable that marks the end of the 'basename' */
private String basenameterminator = "-";

/** Instance variable that stores the suffix for the generated jarfile. */
private String genericjarsuffix = "-generic.jar";

/** Instance variable that stores the suffix for the weblogic jarfile. */
private String weblogicjarsuffix = "-wl.jar";

/**
* Setter used to store the value of srcdir prior to execute() being called.
* @param inDir The string indicating the source directory.
*/
public void setSrcdir(String inDir) {
this.srcdir = this.project.resolveFile(inDir);
}

/**
* Setter used to store the value of destination directory prior to execute()
* being called.
* @param inFile The string indicating the source directory.
*/
public void setDestdir(String inDir) {
this.destdir = this.project.resolveFile(inDir);
}

/**
* Setter used to store the suffix for the generated jar file.
* @param inString the string to use as the suffix.
*/
public void setGenericjarsuffix(String inString) {
this.genericjarsuffix = inString;
}

/**
* Setter used to store the suffix for the generated weblogic jar file.
* @param inString the string to use as the suffix.
*/
public void setWeblogicjarsuffix(String inString) {
this.weblogicjarsuffix = inString;
}

/**
* Setter used to store the value of generateweblogic.
* @param inValue a string, either 'true' or 'false'.
*/
public void setGenerateweblogic(String inValue) {
this.generateweblogic = Boolean.valueOf(inValue).booleanValue();
}

/**
* Setter used to store the value of keepgeneric
* @param inValue a string, either 'true' or 'false'.
*/
public void setKeepgeneric(String inValue) {
this.keepgeneric = Boolean.valueOf(inValue).booleanValue();
}
/**
* Setter used to store the value of basenameterminator
* @param inValue a string which marks the end of the basename.
*/
public void setBasenameterminator(String inValue) {
if (inValue != null) this.basenameterminator = inValue;
}

/**
* 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 iStream A FileInputStream from which to read the
* contents the file being added.
* @param filename A String representing the name, including
* all relevant path information, that should be stored for the entry
* being added.
*/
protected void addFileToJar(JarOutputStream jStream,
FileInputStream iStream,
String filename)
throws BuildException {
try {
// Create the zip entry and add it to the jar file
ZipEntry zipEntry = new ZipEntry(filename);
jStream.putNextEntry(zipEntry);
// Create the file input stream, and buffer everything over
// to the jar output stream
byte[] byteBuffer = new byte[2 * 1024];
int count = 0;
do {
jStream.write(byteBuffer, 0, count);
count = iStream.read(byteBuffer, 0, byteBuffer.length);
} while (count != -1);
// Close up the file input stream for the class file
iStream.close();
}
catch (IOException ioe) {
String msg = "IOException while adding entry "
+ filename + "to jarfile."
+ ioe.getMessage();
throw new BuildException(msg, ioe);
}
}

/**
* 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.
*/
public void writeJar(File jarfile, Hashtable files) throws BuildException{
JarOutputStream jarStream = null;
Iterator entryIterator = null;
String entryName = null;
File entryFile = null;

try {
/* 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();
// Create the streams necessary to write the jarfile
jarStream = new JarOutputStream(new FileOutputStream(jarfile));
jarStream.setMethod(JarOutputStream.DEFLATED);
// Loop through all the class files found and add them to the jar
entryIterator = files.keySet().iterator();
while (entryIterator.hasNext()) {
entryName = (String) entryIterator.next();
entryFile = (File) files.get(entryName);
this.log("adding file '" + entryName + "'",
Project.MSG_VERBOSE);

addFileToJar(jarStream,
new FileInputStream(entryFile),
entryName);
}
// All done. Close the jar stream.
jarStream.close();
}
catch(IOException ioe) {
String msg = "IOException while processing ejb-jar file '"
+ jarfile.toString()
+ "'. Details: "
+ ioe.getMessage();
throw new BuildException(msg, ioe);
}
} // end of writeJar

/**
*
*/
public void buildWeblogicJar(File sourceJar, File destJar) {
org.apache.tools.ant.taskdefs.Java javaTask = null;
try {
// Unfortunately, because weblogic.ejbc calls system.exit(), we
// cannot do it 'in-process'. If they ever fix this, we should
// change this code - it would be much quicker!
String args = "-noexit " + sourceJar + " " + destJar;
javaTask = (Java) this.project.createTask("java");
javaTask.setClassname("weblogic.ejbc");
javaTask.setArgs(args);
javaTask.setFork("false");

this.log("Calling weblogic.ejbc for " + sourceJar.toString(),
Project.MSG_INFO);

javaTask.execute();
}
catch (Exception e) {
// Have to catch this because of the semantics of calling main()
String msg = "Exception while calling ejbc. Details: " + e.toString();
throw new BuildException(msg, e);
}
}


/**
* Invoked by Ant after the task is prepared, when it is ready to execute
* this task. Parses the XML deployment descriptor to acquire the list of
* files, then constructs the destination jar file (first deleting it if it
* already exists) from the list of classfiles encountered and the descriptor
* itself. File will be of the expected format with classes under full
* package hierarchies and the descriptor in META-INF/ejb-jar.xml
* @exception BuildException thrown whenever a problem is
* encountered that cannot be recovered from, to signal to ant
* that a major problem occurred within this task.
*/
public void execute() throws BuildException {
boolean needBuild = true;
DirectoryScanner ds = null;
String[] files = null;
int index = 0;
File weblogicDD = null;
File jarfile = null;
File wlJarfile = null;
File jarToCheck = null;
DescriptorHandler handler = null;
Hashtable ejbFiles = null;
String baseName = null;

// Lets do a little asserting to make sure we have all the
// required attributes from the task processor
StringBuffer sb = new StringBuffer();
boolean die = false;
sb.append("Processing ejbjar - the following attributes ");
sb.append("must be specified: ");
if (this.srcdir == null) { sb.append("srcdir "); die = true; }
if (this.destdir == null) { sb.append("destdir"); die = true; }
if ( die ) throw new BuildException(sb.toString());

try {
// Create the parser using whatever parser the system dictates
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
saxParserFactory.setValidating(false);
SAXParser saxParser = saxParserFactory.newSAXParser();

ds = this.getDirectoryScanner(this.srcdir);
ds.scan();
files = ds.getIncludedFiles();

this.log(files.length + " deployment descriptors located.",
Project.MSG_VERBOSE);

// Loop through the files. Each file represents one deployment
// descriptor, and hence one bean in our model.
for (index=0; index < files.length; ++index) {

// By default we assume we need to build.
needBuild = true;

// Work out what the base name is
int endBaseName =
files[index].indexOf(basenameterminator,
files[index].lastIndexOf(File.separator));
baseName = files[index].substring(0, endBaseName);

/* Parse the ejb deployment descriptor. While it may not
* look like much, passing 'this' in the above method allows
* the parser to call us back when it finds interesting things.
*/
handler = new DescriptorHandler();
saxParser.parse(new InputSource
(new FileInputStream
(new File(this.srcdir, files[index]))),
handler);

ejbFiles = handler.getFiles();
/* Now that we've parsed the deployment descriptor we have the
* bean name, so we can figure out all the .xml filenames and
* add them to the set of files for the jar.
*/

// First the regular deployment descriptor
ejbFiles.put(EjbJar.META_DIR + EjbJar.EJB_DD,
new File(this.srcdir, files[index]));

// Then the weblogic deployment descriptor
weblogicDD = new File(this.srcdir,
baseName
+ this.basenameterminator
+ EjbJar.WL_DD);

if (weblogicDD.exists()) {
ejbFiles.put(EjbJar.META_DIR + EjbJar.WL_DD,
weblogicDD);
}

// The the weblogic cmp deployment descriptor
weblogicDD = new File(this.srcdir,
baseName
+ this.basenameterminator
+ EjbJar.WL_CMP_DD);

if (weblogicDD.exists()) {
ejbFiles.put(EjbJar.META_DIR + EjbJar.WL_CMP_DD,
weblogicDD);
}

// Lastly for the jarfiles
jarfile = new File(this.destdir,
baseName
+ this.genericjarsuffix);
wlJarfile = new File(this.destdir,
baseName
+ this.weblogicjarsuffix);
/* Check to see if the jar file is already up to date.
* Unfortunately we have to parse the descriptor just to do
* that, but it's still a saving over re-constructing the jar
* file each time. Tertiary is used to determine which jarfile
* we should check times against...think about it.
*/
jarToCheck = this.generateweblogic ? wlJarfile : jarfile;
if (jarToCheck.exists()) {
long lastBuild = jarToCheck.lastModified();
Iterator fileIter = ejbFiles.values().iterator();
File currentFile = null;
// Set the need build to false until we find out otherwise.
needBuild = false;

// Loop through the files seeing if any has been touched
// more recently than the destination jar.
while( (needBuild == false) && (fileIter.hasNext()) ) {
currentFile = (File) fileIter.next();
needBuild = ( lastBuild < currentFile.lastModified() );
}
}
// Check to see if we need a build and start
// doing the work!
if (needBuild) {
// Log that we are going to build...
this.log( "building "
+ jarfile.getName()
+ " with "
+ String.valueOf(ejbFiles.size())
+ " total files",
Project.MSG_INFO);

// Use helper method to write the jarfile
this.writeJar(jarfile, ejbFiles);

// Generate weblogic jar if requested
if (this.generateweblogic) {
this.buildWeblogicJar(jarfile, wlJarfile);
}

// Delete the original jar if we weren't asked to keep it.
if (!this.keepgeneric) {
this.log("deleting jar " + jarfile.toString(),
Project.MSG_INFO);
jarfile.delete();
}
}
else {
// Log that the file is up to date...
this.log(jarfile.toString() + " is up to date.",
Project.MSG_INFO);
}
}
}
catch (SAXException se) {
String msg = "SAXException while parsing '"
+ files[index].toString()
+ "'. This probably indicates badly-formed XML."
+ " Details: "
+ se.getMessage();
throw new BuildException(msg, se);
}
catch (ParserConfigurationException pce) {
String msg = "ParserConfigurationException while creating parser. "
+ "Details: " + pce.getMessage();
throw new BuildException(msg, pce);
}
catch (IOException ioe) {
String msg = "IOException while parsing'"
+ files[index].toString()
+ "'. This probably indicates that the descriptor"
+ " doesn't exist. Details:"
+ ioe.getMessage();
throw new BuildException(msg, ioe);
}
} // end of execute()
}








Loading…
Cancel
Save