~ubuntu-branches/ubuntu/jaunty/ant/jaunty-proposed

« back to all changes in this revision

Viewing changes to src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java

  • Committer: Bazaar Package Importer
  • Author(s): Stefan Gybas
  • Date: 2002-02-14 14:28:48 UTC
  • Revision ID: james.westby@ubuntu.com-20020214142848-2ww7ynmqkj31vlmn
Tags: upstream-1.4.1
ImportĀ upstreamĀ versionĀ 1.4.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * The Apache Software License, Version 1.1
 
3
 *
 
4
 * Copyright (c) 2000 The Apache Software Foundation.  All rights
 
5
 * reserved.
 
6
 *
 
7
 * Redistribution and use in source and binary forms, with or without
 
8
 * modification, are permitted provided that the following conditions
 
9
 * are met:
 
10
 *
 
11
 * 1. Redistributions of source code must retain the above copyright
 
12
 *    notice, this list of conditions and the following disclaimer.
 
13
 *
 
14
 * 2. Redistributions in binary form must reproduce the above copyright
 
15
 *    notice, this list of conditions and the following disclaimer in
 
16
 *    the documentation and/or other materials provided with the
 
17
 *    distribution.
 
18
 *
 
19
 * 3. The end-user documentation included with the redistribution, if
 
20
 *    any, must include the following acknowlegement:
 
21
 *       "This product includes software developed by the
 
22
 *        Apache Software Foundation (http://www.apache.org/)."
 
23
 *    Alternately, this acknowlegement may appear in the software itself,
 
24
 *    if and wherever such third-party acknowlegements normally appear.
 
25
 *
 
26
 * 4. The names "The Jakarta Project", "Ant", and "Apache Software
 
27
 *    Foundation" must not be used to endorse or promote products derived
 
28
 *    from this software without prior written permission. For written
 
29
 *    permission, please contact apache@apache.org.
 
30
 *
 
31
 * 5. Products derived from this software may not be called "Apache"
 
32
 *    nor may "Apache" appear in their names without prior written
 
33
 *    permission of the Apache Group.
 
34
 *
 
35
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 
36
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
37
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 
38
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 
39
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
40
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
41
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 
42
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 
43
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 
44
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 
45
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 
46
 * SUCH DAMAGE.
 
47
 * ====================================================================
 
48
 *
 
49
 * This software consists of voluntary contributions made by many
 
50
 * individuals on behalf of the Apache Software Foundation.  For more
 
51
 * information on the Apache Software Foundation, please see
 
52
 * <http://www.apache.org/>.
 
53
 */
 
54
package org.apache.tools.ant.taskdefs.optional.ejb;
 
55
 
 
56
import java.io.*;
 
57
import java.util.*;
 
58
import java.util.jar.*;
 
59
import java.util.zip.*;
 
60
import java.net.*;
 
61
 
 
62
import javax.xml.parsers.SAXParser;
 
63
import org.xml.sax.InputSource;
 
64
import org.xml.sax.SAXException;
 
65
 
 
66
import org.apache.tools.ant.*;
 
67
import org.apache.tools.ant.types.*;
 
68
 
 
69
/**
 
70
 * A deployment tool which creates generic EJB jars. Generic jars contains
 
71
 * only those classes and META-INF entries specified in the EJB 1.1 standard
 
72
 *
 
73
 * This class is also used as a framework for the creation of vendor specific
 
74
 * deployment tools. A number of template methods are provided through which the
 
75
 * vendor specific tool can hook into the EJB creation process.
 
76
 */
 
77
public class GenericDeploymentTool implements EJBDeploymentTool {
 
78
    /** Private constants that are used when constructing the standard jarfile */
 
79
    protected static final String META_DIR  = "META-INF/";
 
80
    protected static final String EJB_DD    = "ejb-jar.xml";
 
81
 
 
82
    /**
 
83
     * The configuration from the containing task. This config combined with the 
 
84
     * settings of the individual attributes here constitues the complete config for
 
85
     * this deployment tool.
 
86
     */
 
87
    private EjbJar.Config config;
 
88
 
 
89
    /** Stores a handle to the directory to put the Jar files in */
 
90
    private File destDir;
 
91
    
 
92
    /** The classpath to use with this deployment tool. This is appended to 
 
93
        any paths from the ejbjar task itself.*/
 
94
    private Path classpath;
 
95
 
 
96
    /** Instance variable that stores the suffix for the generated jarfile. */
 
97
    private String genericJarSuffix = "-generic.jar";
 
98
 
 
99
    /**
 
100
     * The task to which this tool belongs. This is used to access services provided
 
101
     * by the ant core, such as logging.
 
102
     */
 
103
    private Task task;
 
104
    
 
105
    /**
 
106
     * The classloader generated from the given classpath to load
 
107
     * the super classes and super interfaces.
 
108
     */
 
109
    private ClassLoader classpathLoader = null;
 
110
    
 
111
     /**
 
112
     * List of files have been loaded into the EJB jar
 
113
     */
 
114
    private List addedfiles;
 
115
 
 
116
    /**
 
117
     * Handler used to parse the EJB XML descriptor
 
118
     */
 
119
    private DescriptorHandler handler;
 
120
 
 
121
    /**
 
122
     * Setter used to store the value of destination directory prior to execute()
 
123
     * being called.
 
124
     * @param inDir the destination directory.
 
125
     */
 
126
    public void setDestdir(File inDir) {
 
127
        this.destDir = inDir;
 
128
    }
 
129
 
 
130
    /**
 
131
     * Get the desitination directory.
 
132
     */
 
133
    protected File getDestDir() {
 
134
        return destDir;
 
135
    }
 
136
    
 
137
 
 
138
    /**
 
139
     * Set the task which owns this tool
 
140
     */
 
141
    public void setTask(Task task) {
 
142
        this.task = task;
 
143
    }
 
144
       
 
145
    /**
 
146
     * Get the task for this tool.
 
147
     */
 
148
    protected Task getTask() {
 
149
        return task;
 
150
    }
 
151
 
 
152
    /**
 
153
     * Get the basename terminator.
 
154
     */
 
155
    protected EjbJar.Config getConfig() {
 
156
        return config;
 
157
    }
 
158
    
 
159
    /**
 
160
     * Returns true, if the meta-inf dir is being explicitly set, false otherwise.
 
161
     */
 
162
    protected boolean usingBaseJarName() {
 
163
        return config.baseJarName != null;
 
164
    }
 
165
    
 
166
    /**
 
167
     * Setter used to store the suffix for the generated jar file.
 
168
     * @param inString the string to use as the suffix.
 
169
     */
 
170
    public void setGenericJarSuffix(String inString) {
 
171
        this.genericJarSuffix = inString;
 
172
    }
 
173
 
 
174
    /**
 
175
     * Add the classpath for the user classes
 
176
     */
 
177
    public Path createClasspath() {
 
178
        if (classpath == null) {
 
179
            classpath = new Path(task.getProject());
 
180
        }
 
181
        return classpath.createPath();
 
182
    }
 
183
 
 
184
    /**
 
185
     * Set the classpath to be used for this compilation.
 
186
     */
 
187
    public void setClasspath(Path classpath) {
 
188
        this.classpath = classpath;
 
189
    }
 
190
 
 
191
    /**
 
192
     * Get the classpath by combining the one from the surrounding task, if any
 
193
     * and the one from tis tool.
 
194
     */
 
195
    protected Path getCombinedClasspath() {
 
196
        Path combinedPath = classpath;
 
197
        if (config.classpath != null) {
 
198
            if (combinedPath == null) {
 
199
                combinedPath = config.classpath;
 
200
            }
 
201
            else {
 
202
                combinedPath.append(config.classpath);
 
203
            }
 
204
        }
 
205
        
 
206
        return combinedPath;
 
207
    }
 
208
    
 
209
    protected void log(String message, int level) {
 
210
        getTask().log(message, level);
 
211
    }
 
212
 
 
213
    protected Location getLocation() {
 
214
        return getTask().getLocation();
 
215
    }
 
216
 
 
217
 
 
218
    /**
 
219
     * Configure this tool for use in the ejbjar task.
 
220
     */
 
221
    public void configure(EjbJar.Config config) {
 
222
        this.config = config;
 
223
        
 
224
        classpathLoader = null;
 
225
    }
 
226
 
 
227
    /**
 
228
     * Utility method that encapsulates the logic of adding a file entry to
 
229
     * a .jar file.  Used by execute() to add entries to the jar file as it is
 
230
     * constructed.
 
231
     * @param jStream A JarOutputStream into which to write the
 
232
     *        jar entry.
 
233
     * @param inputFile A File from which to read the
 
234
     *        contents the file being added.
 
235
     * @param logicalFilename A String representing the name, including
 
236
     *        all relevant path information, that should be stored for the entry
 
237
     *        being added.
 
238
     */
 
239
    protected void addFileToJar(JarOutputStream jStream,
 
240
                                File inputFile,
 
241
                                String logicalFilename)
 
242
        throws BuildException {
 
243
        FileInputStream iStream = null;
 
244
        try {
 
245
            if (!addedfiles.contains(logicalFilename)) {
 
246
                iStream = new FileInputStream(inputFile);
 
247
                // Create the zip entry and add it to the jar file
 
248
                ZipEntry zipEntry = new ZipEntry(logicalFilename.replace('\\','/'));
 
249
                jStream.putNextEntry(zipEntry);
 
250
                   
 
251
                // Create the file input stream, and buffer everything over
 
252
                // to the jar output stream
 
253
                byte[] byteBuffer = new byte[2 * 1024];
 
254
                int count = 0;
 
255
                do {
 
256
                    jStream.write(byteBuffer, 0, count);
 
257
                    count = iStream.read(byteBuffer, 0, byteBuffer.length);
 
258
                } while (count != -1);
 
259
                
 
260
                //add it to list of files in jar
 
261
                addedfiles.add(logicalFilename);
 
262
           }       
 
263
        }
 
264
        catch (IOException ioe) {
 
265
            log("WARNING: IOException while adding entry " + 
 
266
                logicalFilename + " to jarfile from " + inputFile.getPath() + " " + 
 
267
                ioe.getClass().getName() + "-" + ioe.getMessage(), Project.MSG_WARN);
 
268
        }
 
269
        finally {
 
270
            // Close up the file input stream for the class file
 
271
            if (iStream != null) {
 
272
                try {
 
273
                    iStream.close();
 
274
                }
 
275
                catch (IOException closeException) {}
 
276
            }
 
277
        }
 
278
    }
 
279
 
 
280
    protected DescriptorHandler getDescriptorHandler(File srcDir) { 
 
281
        DescriptorHandler handler = new DescriptorHandler(getTask(), srcDir); 
 
282
        
 
283
        registerKnownDTDs(handler);
 
284
        
 
285
        // register any DTDs supplied by the user
 
286
        for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) { 
 
287
            EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation)i.next(); 
 
288
            handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation()); 
 
289
        } 
 
290
        return handler; 
 
291
    } 
 
292
    
 
293
    /**
 
294
     * Register the locations of all known DTDs.
 
295
     *
 
296
     * vendor-specific subclasses should override this method to define
 
297
     * the vendor-specific locations of the EJB DTDs
 
298
     */
 
299
    protected void registerKnownDTDs(DescriptorHandler handler) {
 
300
        // none to register for generic
 
301
    }
 
302
 
 
303
    public void processDescriptor(String descriptorFileName, SAXParser saxParser) {
 
304
 
 
305
        checkConfiguration(descriptorFileName, saxParser);
 
306
                    
 
307
        try {
 
308
            handler = getDescriptorHandler(config.srcDir);
 
309
 
 
310
            // Retrive the files to be added to JAR from EJB descriptor
 
311
            Hashtable ejbFiles = parseEjbFiles(descriptorFileName, saxParser);
 
312
 
 
313
            // Add any support classes specified in the build file
 
314
            addSupportClasses(ejbFiles);
 
315
 
 
316
            // Determine the JAR filename (without filename extension)
 
317
            String baseName = getJarBaseName(descriptorFileName);
 
318
 
 
319
            String ddPrefix = getVendorDDPrefix(baseName, descriptorFileName);
 
320
 
 
321
            // First the regular deployment descriptor
 
322
            ejbFiles.put(META_DIR + EJB_DD,
 
323
                         new File(config.descriptorDir, descriptorFileName));
 
324
            
 
325
            // now the vendor specific files, if any             
 
326
            addVendorFiles(ejbFiles, ddPrefix);
 
327
 
 
328
            // add any inherited files
 
329
            checkAndAddInherited(ejbFiles);
 
330
 
 
331
            // Lastly create File object for the Jar files. If we are using
 
332
            // a flat destination dir, then we need to redefine baseName!
 
333
            if (config.flatDestDir && baseName.length() != 0) {
 
334
                int startName = baseName.lastIndexOf(File.separator);
 
335
                if (startName == -1) {
 
336
                    startName = 0;
 
337
                }
 
338
                
 
339
                int endName   = baseName.length();
 
340
                baseName = baseName.substring(startName, endName);
 
341
            }
 
342
            
 
343
            File jarFile = getVendorOutputJarFile(baseName);
 
344
            
 
345
            
 
346
            // Check to see if we need a build and start doing the work!
 
347
            if (needToRebuild(ejbFiles, jarFile)) {
 
348
                // Log that we are going to build...
 
349
                log( "building "
 
350
                              + jarFile.getName()
 
351
                              + " with "
 
352
                              + String.valueOf(ejbFiles.size())
 
353
                              + " files",
 
354
                              Project.MSG_INFO);
 
355
    
 
356
                // Use helper method to write the jarfile
 
357
                String publicId = getPublicId();
 
358
                writeJar(baseName, jarFile, ejbFiles, publicId);
 
359
 
 
360
            }
 
361
            else {
 
362
                // Log that the file is up to date...
 
363
                log(jarFile.toString() + " is up to date.",
 
364
                              Project.MSG_VERBOSE);
 
365
            }
 
366
 
 
367
        }
 
368
        catch (SAXException se) {
 
369
            String msg = "SAXException while parsing '"
 
370
                + descriptorFileName.toString()
 
371
                + "'. This probably indicates badly-formed XML."
 
372
                + "  Details: "
 
373
                + se.getMessage();
 
374
            throw new BuildException(msg, se);
 
375
        }
 
376
        catch (IOException ioe) {
 
377
            String msg = "IOException while parsing'"
 
378
                + descriptorFileName.toString()
 
379
                + "'.  This probably indicates that the descriptor"
 
380
                + " doesn't exist. Details: "
 
381
                + ioe.getMessage();
 
382
            throw new BuildException(msg, ioe);
 
383
        }
 
384
    }
 
385
    
 
386
    /**
 
387
     * This method is called as the first step in the processDescriptor method
 
388
     * to allow vendor-specific subclasses to validate the task configuration
 
389
     * prior to processing the descriptor.  If the configuration is invalid,
 
390
     * a BuildException should be thrown.
 
391
     *
 
392
     * @param descriptorFileName String representing the file name of an EJB
 
393
     *                           descriptor to be processed
 
394
     * @param saxParser          SAXParser which may be used to parse the XML
 
395
     *                           descriptor
 
396
     * @thows BuildException     Thrown if the configuration is invalid
 
397
     */
 
398
    protected void checkConfiguration(String descriptorFileName, 
 
399
                                    SAXParser saxParser) throws BuildException {
 
400
 
 
401
        /* 
 
402
         * For the GenericDeploymentTool, do nothing.  Vendor specific 
 
403
         * subclasses should throw a BuildException if the configuration is 
 
404
         * invalid for their server.
 
405
         */
 
406
    }
 
407
 
 
408
    /**
 
409
     * This method returns a list of EJB files found when the specified EJB
 
410
     * descriptor is parsed and processed.
 
411
     *
 
412
     * @param descriptorFileName String representing the file name of an EJB
 
413
     *                           descriptor to be processed
 
414
     * @param saxParser          SAXParser which may be used to parse the XML
 
415
     *                           descriptor
 
416
     * @return                   Hashtable of EJB class (and other) files to be
 
417
     *                           added to the completed JAR file
 
418
     * @throws SAXException      Any SAX exception, possibly wrapping another 
 
419
     *                           exception
 
420
     * @throws IOException       An IOException from the parser, possibly from a
 
421
     *                           the byte stream or character stream 
 
422
     */
 
423
    protected Hashtable parseEjbFiles(String descriptorFileName, SAXParser saxParser)
 
424
                            throws IOException, SAXException {
 
425
        FileInputStream descriptorStream = null;
 
426
        Hashtable ejbFiles = null;
 
427
 
 
428
        try {
 
429
 
 
430
            /* Parse the ejb deployment descriptor.  While it may not
 
431
             * look like much, we use a SAXParser and an inner class to
 
432
             * get hold of all the classfile names for the descriptor.
 
433
             */
 
434
            descriptorStream = new FileInputStream(new File(config.descriptorDir, descriptorFileName));
 
435
            saxParser.parse(new InputSource(descriptorStream), handler);
 
436
                            
 
437
            ejbFiles = handler.getFiles();
 
438
 
 
439
        } finally {
 
440
            if (descriptorStream != null) {
 
441
                try {
 
442
                    descriptorStream.close();
 
443
                }
 
444
                catch (IOException closeException) {}
 
445
            }
 
446
        }
 
447
 
 
448
        return ejbFiles;
 
449
    }
 
450
 
 
451
    /**
 
452
     * Adds any classes the user specifies using <i>support</i> nested elements
 
453
     * to the <code>ejbFiles</code> Hashtable.
 
454
     *
 
455
     * @param ejbFiles Hashtable of EJB classes (and other) files that will be
 
456
     *                 added to the completed JAR file
 
457
     */
 
458
    protected void addSupportClasses(Hashtable ejbFiles) {
 
459
        // add in support classes if any
 
460
        Project project = task.getProject();
 
461
        for (Iterator i = config.supportFileSets.iterator(); i.hasNext();) {
 
462
            FileSet supportFileSet = (FileSet)i.next();
 
463
            File supportBaseDir = supportFileSet.getDir(project);
 
464
            DirectoryScanner supportScanner = supportFileSet.getDirectoryScanner(project);
 
465
            supportScanner.scan();
 
466
            String[] supportFiles = supportScanner.getIncludedFiles();
 
467
            for (int j = 0; j < supportFiles.length; ++j) {
 
468
                ejbFiles.put(supportFiles[j], new File(supportBaseDir, supportFiles[j]));
 
469
            }
 
470
        }            
 
471
    }
 
472
 
 
473
 
 
474
    /**
 
475
     * Using the EJB descriptor file name passed from the <code>ejbjar</code>
 
476
     * task, this method returns the "basename" which will be used to name the
 
477
     * completed JAR file.
 
478
     *
 
479
     * @param descriptorFileName String representing the file name of an EJB
 
480
     *                           descriptor to be processed
 
481
     * @return                   The "basename" which will be used to name the
 
482
     *                           completed JAR file
 
483
     */
 
484
    protected String getJarBaseName(String descriptorFileName) {
 
485
 
 
486
        String baseName = "";
 
487
 
 
488
        // Work out what the base name is
 
489
        if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.BASEJARNAME)) {
 
490
            String canonicalDescriptor = descriptorFileName.replace('\\', '/');
 
491
            int index = canonicalDescriptor.lastIndexOf('/');
 
492
            if (index != -1) {
 
493
                baseName = descriptorFileName.substring(0, index + 1);
 
494
            }
 
495
            baseName += config.baseJarName;
 
496
        } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
 
497
            int lastSeparatorIndex = descriptorFileName.lastIndexOf(File.separator);
 
498
            int endBaseName = -1;
 
499
            if (lastSeparatorIndex != -1) {
 
500
                endBaseName = descriptorFileName.indexOf(config.baseNameTerminator, 
 
501
                                                            lastSeparatorIndex);
 
502
            } else {
 
503
                endBaseName = descriptorFileName.indexOf(config.baseNameTerminator);
 
504
            }
 
505
 
 
506
            if (endBaseName != -1) {
 
507
                baseName = descriptorFileName.substring(0, endBaseName);
 
508
            }
 
509
            baseName = descriptorFileName.substring(0, endBaseName);
 
510
        } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DIRECTORY)) {
 
511
            int lastSeparatorIndex = descriptorFileName.lastIndexOf(File.separator);
 
512
            String dirName = descriptorFileName.substring(0, lastSeparatorIndex);
 
513
            int dirSeparatorIndex = dirName.lastIndexOf(File.separator);
 
514
            if (dirSeparatorIndex != -1) {
 
515
                dirName = dirName.substring(dirSeparatorIndex + 1);
 
516
            }
 
517
            
 
518
            baseName = dirName;
 
519
        } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.EJB_NAME)) {
 
520
            baseName = handler.getEjbName();
 
521
        }
 
522
        return baseName;
 
523
    }
 
524
 
 
525
    /**
 
526
     * Get the prefix for vendor deployment descriptors.
 
527
     *
 
528
     * This will contain the path and the start of the descriptor name, 
 
529
     * depending on the naming scheme
 
530
     */
 
531
    public String getVendorDDPrefix(String baseName, String descriptorFileName) {
 
532
        String ddPrefix = null;
 
533
 
 
534
        if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
 
535
            ddPrefix = baseName + config.baseNameTerminator;
 
536
        } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.BASEJARNAME) ||
 
537
                   config.namingScheme.getValue().equals(EjbJar.NamingScheme.EJB_NAME) ||
 
538
                   config.namingScheme.getValue().equals(EjbJar.NamingScheme.DIRECTORY)) {
 
539
            String canonicalDescriptor = descriptorFileName.replace('\\', '/');
 
540
            int index = canonicalDescriptor.lastIndexOf('/');
 
541
            if (index == -1) {
 
542
                ddPrefix = "";
 
543
            }
 
544
            else {
 
545
                ddPrefix = descriptorFileName.substring(0, index + 1);
 
546
            }
 
547
        }
 
548
        return ddPrefix;
 
549
    }
 
550
 
 
551
    /**
 
552
     * Add any vendor specific files which should be included in the 
 
553
     * EJB Jar.
 
554
     */
 
555
    protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
 
556
        // nothing to add for generic tool.
 
557
    }
 
558
 
 
559
 
 
560
    /**
 
561
     * Get the vendor specific name of the Jar that will be output. The modification date
 
562
     * of this jar will be checked against the dependent bean classes.
 
563
     */
 
564
    File getVendorOutputJarFile(String baseName) {
 
565
        return new File(destDir, baseName + genericJarSuffix);
 
566
    }
 
567
 
 
568
    /**
 
569
     * This method checks the timestamp on each file listed in the <code>
 
570
     * ejbFiles</code> and compares them to the timestamp on the <code>jarFile
 
571
     * </code>.  If the <code>jarFile</code>'s timestamp is more recent than
 
572
     * each EJB file, <code>true</code> is returned.  Otherwise, <code>false
 
573
     * </code> is returned.
 
574
     *
 
575
     * @param ejbFiles Hashtable of EJB classes (and other) files that will be
 
576
     *                 added to the completed JAR file
 
577
     * @param jarFile  JAR file which will contain all of the EJB classes (and
 
578
     *                 other) files
 
579
     * @return         boolean indicating whether or not the <code>jarFile</code>
 
580
     *                 is up to date
 
581
     */
 
582
    protected boolean needToRebuild(Hashtable ejbFiles, File jarFile) {
 
583
        if (jarFile.exists()) {
 
584
            long lastBuild = jarFile.lastModified();
 
585
            if (config.manifest != null && config.manifest.exists() &&
 
586
                config.manifest.lastModified() > lastBuild) {
 
587
                log("Build needed because manifest " + config.manifest + " is out of date",
 
588
                    Project.MSG_VERBOSE);
 
589
                return true;
 
590
            }
 
591
                            
 
592
            
 
593
            Iterator fileIter = ejbFiles.values().iterator();
 
594
 
 
595
            // Loop through the files seeing if any has been touched
 
596
            // more recently than the destination jar.
 
597
            while(fileIter.hasNext()) {
 
598
                File currentFile = (File) fileIter.next();
 
599
                if (lastBuild < currentFile.lastModified()) {
 
600
                    log("Build needed because " + currentFile.getPath() + " is out of date",
 
601
                        Project.MSG_VERBOSE);
 
602
                    return true;                        
 
603
                }
 
604
            }
 
605
            return false;
 
606
        }
 
607
        
 
608
        return true;
 
609
    }
 
610
 
 
611
    /**
 
612
     * Returns the Public ID of the DTD specified in the EJB descriptor.  Not
 
613
     * every vendor-specific <code>DeploymentTool</code> will need to reference
 
614
     * this value or may want to determine this value in a vendor-specific way.
 
615
     *
 
616
     * @return         Public ID of the DTD specified in the EJB descriptor.
 
617
     */
 
618
    protected String getPublicId() {
 
619
        return handler.getPublicId();
 
620
    }
 
621
 
 
622
    /**
 
623
     * Method used to encapsulate the writing of the JAR file. Iterates over the
 
624
     * filenames/java.io.Files in the Hashtable stored on the instance variable
 
625
     * ejbFiles.
 
626
     */
 
627
    protected void writeJar(String baseName, File jarfile, Hashtable files, 
 
628
                            String publicId) throws BuildException{
 
629
 
 
630
        JarOutputStream jarStream = null;
 
631
        try {
 
632
            // clean the addedfiles Vector 
 
633
            addedfiles = new ArrayList();
 
634
 
 
635
            /* If the jarfile already exists then whack it and recreate it.
 
636
             * Should probably think of a more elegant way to handle this
 
637
             * so that in case of errors we don't leave people worse off
 
638
             * than when we started =)
 
639
             */
 
640
            if (jarfile.exists()) {
 
641
                jarfile.delete();
 
642
            }
 
643
            jarfile.getParentFile().mkdirs();
 
644
            jarfile.createNewFile();
 
645
            
 
646
            InputStream in = null;
 
647
            Manifest manifest = null;
 
648
            try {
 
649
                if (config.manifest != null) {
 
650
                    in = new FileInputStream(config.manifest);
 
651
                    if ( in == null ) {
 
652
                        throw new BuildException("Could not find manifest file: " + config.manifest, 
 
653
                                                  getLocation());
 
654
                    }
 
655
                }
 
656
                else {
 
657
                    String defaultManifest = "/org/apache/tools/ant/defaultManifest.mf";
 
658
                    in = this.getClass().getResourceAsStream(defaultManifest);
 
659
                    if ( in == null ) {
 
660
                        throw new BuildException("Could not find default manifest: " + defaultManifest,
 
661
                                                  getLocation());
 
662
                    }
 
663
                }
 
664
                            
 
665
                manifest = new Manifest(in);
 
666
            }
 
667
            catch (IOException e) {
 
668
                throw new BuildException ("Unable to read manifest", e, getLocation());
 
669
            }
 
670
            finally {
 
671
                if (in != null) {
 
672
                    in.close();
 
673
                }
 
674
            }
 
675
            
 
676
            // Create the streams necessary to write the jarfile
 
677
            
 
678
            jarStream = new JarOutputStream(new FileOutputStream(jarfile), manifest);
 
679
            jarStream.setMethod(JarOutputStream.DEFLATED);
 
680
            
 
681
            // Loop through all the class files found and add them to the jar
 
682
            for (Iterator entryIterator = files.keySet().iterator(); entryIterator.hasNext(); ) {
 
683
                String entryName = (String) entryIterator.next();
 
684
                File entryFile = (File) files.get(entryName);
 
685
                
 
686
                log("adding file '" + entryName + "'",
 
687
                              Project.MSG_VERBOSE);
 
688
 
 
689
                addFileToJar(jarStream, entryFile, entryName);
 
690
 
 
691
                // See if there are any inner classes for this class and add them in if there are
 
692
                InnerClassFilenameFilter flt = new InnerClassFilenameFilter(entryFile.getName());
 
693
                File entryDir = entryFile.getParentFile();
 
694
                String[] innerfiles = entryDir.list(flt);
 
695
                for (int i = 0, n = innerfiles.length; i < n; i++) {
 
696
            
 
697
                    //get and clean up innerclass name
 
698
                    int entryIndex = entryName.lastIndexOf(entryFile.getName()) -1;
 
699
                    if ( entryIndex < 0) {
 
700
                        entryName = innerfiles[i];
 
701
                    }
 
702
                    else {
 
703
                        entryName = entryName.substring(0, entryIndex) + File.separatorChar + innerfiles[i];
 
704
                    }
 
705
                    // link the file
 
706
                    entryFile = new File(config.srcDir, entryName);
 
707
        
 
708
                    log("adding innerclass file '" + entryName + "'", 
 
709
                            Project.MSG_VERBOSE);
 
710
        
 
711
                    addFileToJar(jarStream, entryFile, entryName);
 
712
        
 
713
                }
 
714
            }
 
715
        }
 
716
        catch(IOException ioe) {
 
717
            String msg = "IOException while processing ejb-jar file '"
 
718
                + jarfile.toString()
 
719
                + "'. Details: "
 
720
                + ioe.getMessage();
 
721
            throw new BuildException(msg, ioe);
 
722
        }
 
723
        finally {
 
724
            if (jarStream != null) {
 
725
                try {
 
726
                    jarStream.close();
 
727
                }
 
728
                catch (IOException closeException) {}
 
729
            }
 
730
        }
 
731
    } // end of writeJar
 
732
 
 
733
    /**
 
734
     * Check if a EJB Class Inherits from a Superclass, and if a Remote Interface
 
735
     * extends an interface other then javax.ejb.EJBObject directly.  Then add those 
 
736
     * classes to the generic-jar so they dont have to added elsewhere.
 
737
     *
 
738
     */
 
739
    protected void checkAndAddInherited(Hashtable checkEntries) throws BuildException
 
740
    {
 
741
        //Copy hashtable so were not changing the one we iterate through
 
742
        Hashtable copiedHash = (Hashtable)checkEntries.clone();
 
743
 
 
744
        // Walk base level EJBs and see if they have superclasses or extend extra interfaces which extend EJBObject
 
745
        for (Iterator entryIterator = copiedHash.keySet().iterator(); entryIterator.hasNext(); ) 
 
746
        {
 
747
            String entryName = (String)entryIterator.next();
 
748
            File entryFile = (File)copiedHash.get(entryName);
 
749
 
 
750
            // only want class files, xml doesnt reflect very well =)
 
751
            if (entryName.endsWith(".class"))
 
752
            {
 
753
                String classname = entryName.substring(0,entryName.lastIndexOf(".class")).replace(File.separatorChar,'.');
 
754
                ClassLoader loader = getClassLoaderForBuild();
 
755
                try {
 
756
                    Class c = loader.loadClass(classname);
 
757
 
 
758
                    // No primatives!!  sanity check, probably not nessesary
 
759
                    if (!c.isPrimitive())
 
760
                    {
 
761
                        if (c.isInterface()) //get as an interface
 
762
                        {
 
763
                            log("looking at interface " + c.getName(),  Project.MSG_VERBOSE);
 
764
                            Class[] interfaces = c.getInterfaces();
 
765
                            for (int i = 0; i < interfaces.length; i++){
 
766
                                log("     implements " + interfaces[i].getName(),  Project.MSG_VERBOSE);
 
767
                                addInterface(interfaces[i], checkEntries);
 
768
                            }
 
769
                        }
 
770
                        else  // get as a class
 
771
                        {
 
772
                            log("looking at class " + c.getName(),  Project.MSG_VERBOSE);
 
773
                            Class s = c.getSuperclass();
 
774
                            addSuperClass(c.getSuperclass(), checkEntries);
 
775
                        }
 
776
                    } //if primative
 
777
                }
 
778
                catch (ClassNotFoundException cnfe) {
 
779
                    log("Could not load class " + classname + " for super class check", 
 
780
                                  Project.MSG_WARN);
 
781
                }                            
 
782
                catch (NoClassDefFoundError ncdfe) {
 
783
                    log("Could not fully load class " + classname + " for super class check", 
 
784
                                  Project.MSG_WARN);
 
785
                }                            
 
786
            } //if 
 
787
        } // while 
 
788
    }
 
789
 
 
790
    private void addInterface(Class theInterface, Hashtable checkEntries) {
 
791
        if (!theInterface.getName().startsWith("java")) // do not add system interfaces
 
792
        { 
 
793
            File interfaceFile = new File(config.srcDir.getAbsolutePath() 
 
794
                                        + File.separatorChar 
 
795
                                        + theInterface.getName().replace('.',File.separatorChar)
 
796
                                        + ".class"
 
797
                                        );
 
798
            if (interfaceFile.exists() && interfaceFile.isFile())
 
799
            {
 
800
                checkEntries.put(theInterface.getName().replace('.',File.separatorChar)+".class",
 
801
                                 interfaceFile);
 
802
                Class[] superInterfaces = theInterface.getInterfaces();
 
803
                for (int i = 0; i < superInterfaces.length; i++) {
 
804
                    addInterface(superInterfaces[i], checkEntries);
 
805
                }
 
806
            }
 
807
        }
 
808
    }
 
809
     
 
810
    private void addSuperClass(Class superClass, Hashtable checkEntries) {
 
811
    
 
812
        if (!superClass.getName().startsWith("java"))
 
813
        {
 
814
            File superClassFile = new File(config.srcDir.getAbsolutePath() 
 
815
                                            + File.separatorChar 
 
816
                                            + superClass.getName().replace('.',File.separatorChar)
 
817
                                            + ".class");
 
818
            if (superClassFile.exists() && superClassFile.isFile())
 
819
            {
 
820
                checkEntries.put(superClass.getName().replace('.',File.separatorChar) + ".class", 
 
821
                                 superClassFile);
 
822
                
 
823
                // now need to get super classes and interfaces for this class
 
824
                Class[] superInterfaces = superClass.getInterfaces();
 
825
                for (int i = 0; i < superInterfaces.length; i++) {
 
826
                    addInterface(superInterfaces[i], checkEntries);
 
827
                }
 
828
                
 
829
                addSuperClass(superClass.getSuperclass(), checkEntries);
 
830
            }               
 
831
        }
 
832
    }
 
833
    
 
834
    /**
 
835
     * Returns a Classloader object which parses the passed in generic EjbJar classpath.
 
836
     * The loader is used to dynamically load classes from javax.ejb.* and the classes 
 
837
     * being added to the jar.
 
838
     *
 
839
     */ 
 
840
    protected ClassLoader getClassLoaderForBuild()
 
841
    {
 
842
        if (classpathLoader != null) {
 
843
            return classpathLoader;
 
844
        }
 
845
        
 
846
        Path combinedClasspath = getCombinedClasspath();
 
847
        
 
848
        // only generate a new ClassLoader if we have a classpath
 
849
        if (combinedClasspath == null) {
 
850
            classpathLoader = getClass().getClassLoader();
 
851
        }
 
852
        else {
 
853
            classpathLoader = new AntClassLoader(getTask().getProject(), combinedClasspath);
 
854
        }
 
855
        
 
856
        return classpathLoader;
 
857
    }
 
858
 
 
859
    /**
 
860
     * Called to validate that the tool parameters have been configured.
 
861
     *
 
862
     * @throws BuildException If the Deployment Tool's configuration isn't
 
863
     *                        valid
 
864
     */
 
865
    public void validateConfigured() throws BuildException {
 
866
        if ((destDir == null) || (!destDir.isDirectory())) {
 
867
            String msg = "A valid destination directory must be specified "
 
868
                            + "using the \"destdir\" attribute.";
 
869
            throw new BuildException(msg, getLocation());
 
870
        }
 
871
    }
 
872
}