FileDocCategorySizeDatePackage
GenericDeploymentTool.javaAPI DocApache Ant 1.7035151Wed Dec 13 06:16:20 GMT 2006org.apache.tools.ant.taskdefs.optional.ejb

GenericDeploymentTool

public class GenericDeploymentTool extends Object implements EJBDeploymentTool
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.

Fields Summary
public static final int
DEFAULT_BUFFER_SIZE
The default buffer byte size to use for IO
public static final int
JAR_COMPRESS_LEVEL
The level to use for compression
protected static final String
META_DIR
The standard META-INF directory in jar files
protected static final String
MANIFEST
The standard MANIFEST file
protected static final String
EJB_DD
Name for EJB Deployment descriptor within EJB jars
public static final String
ANALYZER_SUPER
A dependency analyzer name to find ancestor classes
public static final String
ANALYZER_FULL
A dependency analyzer name to find all related classes
public static final String
ANALYZER_NONE
A dependency analyzer name for no analyzer
public static final String
DEFAULT_ANALYZER
The default analyzer
public static final String
ANALYZER_CLASS_SUPER
The analyzer class for the super analyzer
public static final String
ANALYZER_CLASS_FULL
The analyzer class for the super analyzer
private EjbJar.Config
config
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 File
destDir
Stores a handle to the directory to put the Jar files in
private org.apache.tools.ant.types.Path
classpath
The classpath to use with this deployment tool. This is appended to any paths from the ejbjar task itself.
private String
genericJarSuffix
Instance variable that stores the suffix for the generated jarfile.
private org.apache.tools.ant.Task
task
The task to which this tool belongs. This is used to access services provided by the ant core, such as logging.
private ClassLoader
classpathLoader
The classloader generated from the given classpath to load the super classes and super interfaces.
private Set
addedfiles
Set of files have been loaded into the EJB jar
private DescriptorHandler
handler
Handler used to parse the EJB XML descriptor
private org.apache.tools.ant.util.depend.DependencyAnalyzer
dependencyAnalyzer
Dependency analyzer used to collect class dependencies
Constructors Summary
public GenericDeploymentTool()
No arg constructor


        
      
    
Methods Summary
protected voidaddFileToJar(java.util.jar.JarOutputStream jStream, java.io.File inputFile, java.lang.String logicalFilename)
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.

        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
                }
            }
        }
    
protected voidaddSupportClasses(java.util.Hashtable ejbFiles)
Adds any classes the user specifies using support nested elements to the ejbFiles Hashtable.

param
ejbFiles Hashtable of EJB classes (and other) files that will be added to the completed JAR file

        // 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]));
            }
        }
    
protected voidaddVendorFiles(java.util.Hashtable ejbFiles, java.lang.String 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.

        // nothing to add for generic tool.
    
protected voidcheckAndAddDependants(java.util.Hashtable checkEntries)
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.


        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);
            }
        }
    
protected voidcheckConfiguration(java.lang.String descriptorFileName, javax.xml.parsers.SAXParser saxParser)
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.


        /*
         * For the GenericDeploymentTool, do nothing.  Vendor specific
         * subclasses should throw a BuildException if the configuration is
         * invalid for their server.
         */
    
public voidconfigure(EjbJar.Config config)
Configure this tool for use in the ejbjar task.

param
config the configuration from the surrounding ejbjar task.

        this.config = config;

        createAnalyzer();
        classpathLoader = null;
    
private voidcreateAnalyzer()

        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);
        }
    
public org.apache.tools.ant.types.PathcreateClasspath()
Add the classpath for the user classes

return
a Path instance to be configured by Ant.

        if (classpath == null) {
            classpath = new Path(task.getProject());
        }
        return classpath.createPath();
    
protected java.lang.ClassLoadergetClassLoaderForBuild()
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.

        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 {
            classpathLoader
                = getTask().getProject().createClassLoader(combinedClasspath);
        }

        return classpathLoader;
    
protected org.apache.tools.ant.types.PathgetCombinedClasspath()
Get the classpath by combining the one from the surrounding task, if any and the one from this tool.

return
the combined classpath

        Path combinedPath = classpath;
        if (config.classpath != null) {
            if (combinedPath == null) {
                combinedPath = config.classpath;
            } else {
                combinedPath.append(config.classpath);
            }
        }

        return combinedPath;
    
protected EjbJar.ConfiggetConfig()
Get the basename terminator.

return
an ejbjar task configuration

        return config;
    
protected DescriptorHandlergetDescriptorHandler(java.io.File srcDir)
Get a descriptionHandler.

param
srcDir the source directory.
return
a handler.

        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;
    
protected java.io.FilegetDestDir()
Get the destination directory.

return
the destination directory into which EJB jars are to be written

        return destDir;
    
protected java.lang.StringgetJarBaseName(java.lang.String descriptorFileName)
Using the EJB descriptor file name passed from the ejbjar 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


        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;
    
protected org.apache.tools.ant.LocationgetLocation()
Get the build file location associated with this element's task.

return
the task's location instance.

        return getTask().getLocation();
    
protected java.io.FilegetManifestFile(java.lang.String prefix)
Get the manifets 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

        File manifestFile
            = new File(getConfig().descriptorDir, prefix + "manifest.mf");
        if (manifestFile.exists()) {
            return manifestFile;
        }

        if (config.manifest != null) {
            return config.manifest;
        }
        return null;
    
protected java.lang.StringgetPublicId()
Returns the Public ID of the DTD specified in the EJB descriptor. Not every vendor-specific DeploymentTool 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.

        return handler.getPublicId();
    
protected org.apache.tools.ant.TaskgetTask()
Get the task for this tool.

return
the Task instance this tool is associated with.

        return task;
    
public java.lang.StringgetVendorDDPrefix(java.lang.String baseName, java.lang.String descriptorFileName)
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.

        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;
    
java.io.FilegetVendorOutputJarFile(java.lang.String baseName)
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.

        return new File(destDir, baseName + genericJarSuffix);
    
protected voidlog(java.lang.String message, int level)
Log a message to the Ant output.

param
message the message to be logged.
param
level the severity of this message.

        getTask().log(message, level);
    
protected booleanneedToRebuild(java.util.Hashtable ejbFiles, java.io.File jarFile)
This method checks the timestamp on each file listed in the ejbFiles and compares them to the timestamp on the jarFile . 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
param
jarFile JAR file which will contain all of the EJB classes (and other) files
return
boolean indicating whether or not the jarFile is up to date

        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;
    
protected java.util.HashtableparseEjbFiles(java.lang.String descriptorFileName, javax.xml.parsers.SAXParser saxParser)
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

        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;
    
public voidprocessDescriptor(java.lang.String descriptorFileName, javax.xml.parsers.SAXParser saxParser)
{@inheritDoc}.


        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.toString()
                + "'.  This probably indicates that the descriptor"
                + " doesn't exist. Details: "
                + ioe.getMessage();
            throw new BuildException(msg, ioe);
        }
    
protected voidregisterKnownDTDs(DescriptorHandler handler)
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.

        // none to register for generic
    
public voidsetClasspath(org.apache.tools.ant.types.Path classpath)
Set the classpath to be used for this compilation.

param
classpath the classpath to be used for this build.

        this.classpath = classpath;
    
public voidsetDestdir(java.io.File inDir)
Set the destination directory; required.

param
inDir the destination directory.

        this.destDir = inDir;
    
public voidsetGenericJarSuffix(java.lang.String inString)
Set the suffix for the generated jar file.

param
inString the string to use as the suffix.

        this.genericJarSuffix = inString;
    
public voidsetTask(org.apache.tools.ant.Task task)
Set the task which owns this tool

param
task the Task to which this deployment tool is associated.

        this.task = task;
    
protected booleanusingBaseJarName()
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

        return config.baseJarName != null;
    
public voidvalidateConfigured()
Called to validate that the tool parameters have been configured.

throws
BuildException If the Deployment Tool's configuration isn't valid

        if ((destDir == null) || (!destDir.isDirectory())) {
            String msg = "A valid destination directory must be specified "
                            + "using the \"destdir\" attribute.";
            throw new BuildException(msg, getLocation());
        }
    
protected voidwriteJar(java.lang.String baseName, java.io.File jarfile, java.util.Hashtable files, java.lang.String publicId)
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.


        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
                }
            }
        }