Clover coverage report - Ant Coverage
Coverage timestamp: Tue Apr 8 2003 20:43:55 EST
file stats: LOC: 1,530   Methods: 69
NCLOC: 776   Classes: 6
 
 Source file Conditionals Statements Methods TOTAL
IPlanetEjbc.java 0% 0% 0% 0%
 1   
 /*
 2   
  * The Apache Software License, Version 1.1
 3   
  *
 4   
  * Copyright (c) 2001-2002 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 "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   
 
 55   
 package org.apache.tools.ant.taskdefs.optional.ejb;
 56   
 
 57   
 import java.io.BufferedReader;
 58   
 import java.io.File;
 59   
 import java.io.FileInputStream;
 60   
 import java.io.IOException;
 61   
 import java.io.InputStream;
 62   
 import java.io.InputStreamReader;
 63   
 import java.util.ArrayList;
 64   
 import java.util.Date;
 65   
 import java.util.HashMap;
 66   
 import java.util.Hashtable;
 67   
 import java.util.Iterator;
 68   
 import java.util.List;
 69   
 import java.util.Map;
 70   
 import java.util.Properties;
 71   
 import java.util.StringTokenizer;
 72   
 import javax.xml.parsers.SAXParser;
 73   
 import javax.xml.parsers.SAXParserFactory;
 74   
 import org.xml.sax.AttributeList;
 75   
 import org.xml.sax.HandlerBase;
 76   
 import org.xml.sax.InputSource;
 77   
 import org.xml.sax.SAXException;
 78   
 
 79   
 /**
 80   
  * Compiles EJB stubs and skeletons for the iPlanet Application
 81   
  * Server (iAS).  The class will read a standard EJB descriptor (as well as an
 82   
  * EJB descriptor specific to iPlanet Application Server) to identify one or
 83   
  * more EJBs to process.  It will search for EJB "source" classes (the remote
 84   
 ; * interface, home interface, and EJB implementation class) and the EJB stubs
 85   
  * and skeletons in the specified destination directory.  Only if the stubs and
 86   
  * skeletons cannot be found or if they're out of date will the iPlanet
 87   
  * Application Server ejbc utility be run.
 88   
  * <p>
 89   
  * Because this class (and it's assorted inner classes) may be bundled into the
 90   
  * iPlanet Application Server distribution at some point (and removed from the
 91   
  * Ant distribution), the class has been written to be independent of all
 92   
  * Ant-specific classes.  It is also for this reason (and to avoid cluttering
 93   
  * the Apache Ant source files) that this utility has been packaged into a
 94   
  * single source file.
 95   
  * <p>
 96   
  * For more information on Ant Tasks for iPlanet Application Server, see the
 97   
  * <code>IPlanetDeploymentTool</code> and <code>IPlanetEjbcTask</code> classes.
 98   
  *
 99   
  * @see    IPlanetDeploymentTool
 100   
  * @see    IPlanetEjbcTask
 101   
  * @author Greg Nelson <a href="mailto:greg@netscape.com">greg@netscape.com</a>
 102   
  * @ant.task ignore="true"
 103   
  */
 104   
 public class IPlanetEjbc {
 105   
 
 106   
     /* Constants used for the "beantype" attribute */
 107   
     private static final String ENTITY_BEAN       = "entity";
 108   
     private static final String STATELESS_SESSION = "stateless";
 109   
     private static final String STATEFUL_SESSION  = "stateful";
 110   
 
 111   
     /* Filenames of the standard EJB descriptor and the iAS-specific descriptor */
 112   
     private File        stdDescriptor;
 113   
     private File        iasDescriptor;
 114   
 
 115   
     /*
 116   
      * Directory where "source" EJB files are stored and where stubs and
 117   
      * skeletons will also be written.
 118   
      */
 119   
     private File        destDirectory;
 120   
 
 121   
     /* Classpath used when the iAS ejbc is called */
 122   
     private String      classpath;
 123   
     private String[]    classpathElements;
 124   
 
 125   
     /* Options passed to the iAS ejbc */
 126   
     private boolean     retainSource = false;
 127   
     private boolean     debugOutput  = false;
 128   
 
 129   
     /* iAS installation directory (used if ejbc isn't on user's PATH) */
 130   
     private File        iasHomeDir;
 131   
 
 132   
     /* Parser and handler used to process both EJB descriptor files */
 133   
     private SAXParser   parser;
 134   
     private EjbcHandler handler = new EjbcHandler();
 135   
 
 136   
     /*
 137   
      * This Hashtable maintains a list of EJB class files processed by the ejbc
 138   
      * utility (both "source" class files as well as stubs and skeletons). The
 139   
      * key for the Hashtable is a String representing the path to the class file
 140   
      * (relative to the destination directory).  The value for the Hashtable is
 141   
      * a File object which reference the actual class file.
 142   
      */
 143   
     private Hashtable   ejbFiles     = new Hashtable();
 144   
 
 145   
     /* Value of the display-name element read from the standard EJB descriptor */
 146   
     private String      displayName;
 147   
 
 148   
     /**
 149   
      * Constructs an instance which may be used to process EJB descriptors and
 150   
      * generate EJB stubs and skeletons, if needed.
 151   
      *
 152   
      * @param stdDescriptor File referencing a standard EJB descriptor.
 153   
      * @param iasDescriptor File referencing an iAS-specific EJB descriptor.
 154   
      * @param destDirectory File referencing the base directory where both
 155   
      *                      EJB "source" files are found and where stubs and
 156   
      *                      skeletons will be written.
 157   
      * @param classpath     String representation of the classpath to be used
 158   
      *                      by the iAS ejbc utility.
 159   
      * @param parser        SAXParser to be used to process both of the EJB
 160   
      *                      descriptors.
 161   
      * @todo classpathElements is not needed here, its never used
 162   
      *       (at least IDEA tells me so! :)
 163   
      */
 164  0
     public IPlanetEjbc(File stdDescriptor,
 165   
                        File iasDescriptor,
 166   
                        File destDirectory,
 167   
                        String classpath,
 168   
                        SAXParser parser) {
 169  0
         this.stdDescriptor = stdDescriptor;
 170  0
         this.iasDescriptor      = iasDescriptor;
 171  0
         this.destDirectory      = destDirectory;
 172  0
         this.classpath          = classpath;
 173  0
         this.parser             = parser;
 174   
 
 175   
         /*
 176   
          * Parse the classpath into it's individual elements and store the
 177   
          * results in the "classpathElements" instance variable.
 178   
          */
 179  0
         List elements = new ArrayList();
 180  0
         if (classpath != null) {
 181  0
             StringTokenizer st = new StringTokenizer(classpath,
 182   
                                                         File.pathSeparator);
 183  0
             while (st.hasMoreTokens()) {
 184  0
                 elements.add(st.nextToken());
 185   
             }
 186  0
             classpathElements
 187   
                     = (String[]) elements.toArray(new String[elements.size()]);
 188   
         }
 189   
     }
 190   
 
 191   
     /**
 192   
      * If true, the Java source files which are generated by the
 193   
      * ejbc process are retained.
 194   
      *
 195   
      * @param retainSource A boolean indicating if the Java source files for
 196   
      *                     the stubs and skeletons should be retained.
 197   
      * @todo This is not documented in the HTML. On purpose?
 198   
      */
 199  0
     public void setRetainSource(boolean retainSource) {
 200  0
         this.retainSource = retainSource;
 201   
     }
 202   
 
 203   
     /**
 204   
      * If true, enables debugging output when ejbc is executed.
 205   
      *
 206   
      * @param debugOutput A boolean indicating if debugging output should be
 207   
      *                    generated
 208   
      */
 209  0
     public void setDebugOutput(boolean debugOutput) {
 210  0
         this.debugOutput = debugOutput;
 211   
     }
 212   
 
 213   
     /**
 214   
      * Registers the location of a local DTD file or resource.  By registering
 215   
      * a local DTD, EJB descriptors can be parsed even when the remote servers
 216   
      * which contain the "public" DTDs cannot be accessed.
 217   
      *
 218   
      * @param publicID The public DTD identifier found in an XML document.
 219   
      * @param location The file or resource name for the appropriate DTD stored
 220   
      *                 on the local machine.
 221   
      */
 222  0
     public void registerDTD(String publicID, String location) {
 223  0
         handler.registerDTD(publicID, location);
 224   
     }
 225   
 
 226   
     /**
 227   
      * May be used to specify the "home" directory for this iAS installation.
 228   
      * The directory specified should typically be
 229   
      * <code>[install-location]/iplanet/ias6/ias</code>.
 230   
      *
 231   
      * @param iasHomeDir The home directory for the user's iAS installation.
 232   
      */
 233  0
     public void setIasHomeDir(File iasHomeDir) {
 234  0
         this.iasHomeDir = iasHomeDir;
 235   
     }
 236   
 
 237   
     /**
 238   
      * Returns a Hashtable which contains a list of EJB class files processed by
 239   
      * the ejbc utility (both "source" class files as well as stubs and
 240   
      * skeletons). The key for the Hashtable is a String representing the path
 241   
      * to the class file (relative to the destination directory).  The value for
 242   
      * the Hashtable is a File object which reference the actual class file.
 243   
      *
 244   
      * @return The list of EJB files processed by the ejbc utility.
 245   
      */
 246  0
     public Hashtable getEjbFiles() {
 247  0
         return ejbFiles;
 248   
     }
 249   
 
 250   
     /**
 251   
      * Returns the display-name element read from the standard EJB descriptor.
 252   
      *
 253   
      * @return The EJB-JAR display name.
 254   
      */
 255  0
     public String getDisplayName() {
 256  0
         return displayName;
 257   
     }
 258   
 
 259   
     /**
 260   
      * Returns the list of CMP descriptors referenced in the EJB descriptors.
 261   
      *
 262   
      * @return An array of CMP descriptors.
 263   
      */
 264  0
     public String[] getCmpDescriptors() {
 265  0
         List returnList = new ArrayList();
 266   
 
 267  0
         EjbInfo[] ejbs = handler.getEjbs();
 268   
 
 269  0
         for (int i = 0; i < ejbs.length ; i++) {
 270  0
             List descriptors = (List) ejbs[i].getCmpDescriptors();
 271  0
             returnList.addAll(descriptors);
 272   
         }
 273   
 
 274  0
         return (String[]) returnList.toArray(new String[returnList.size()]);
 275   
     }
 276   
 
 277   
     /**
 278   
      * Main application method for the iPlanet Application Server ejbc utility.
 279   
      * If the application is run with no commandline arguments, a usage
 280   
      * statement is printed for the user.
 281   
      *
 282   
      * @param args The commandline arguments passed to the application.
 283   
      */
 284  0
     public static void main(String[] args) {
 285  0
         File        stdDescriptor;
 286  0
         File        iasDescriptor;
 287  0
         File        destDirectory = null;
 288  0
         String      classpath     = null;
 289  0
         SAXParser   parser        = null;
 290  0
         boolean     debug         = false;
 291  0
         boolean     retainSource  = false;
 292  0
         IPlanetEjbc ejbc;
 293   
 
 294  0
         if ((args.length < 2) || (args.length > 8)) {
 295  0
             usage();
 296  0
             return;
 297   
         }
 298   
 
 299  0
         stdDescriptor = new File(args[args.length - 2]);
 300  0
         iasDescriptor = new File(args[args.length - 1]);
 301   
 
 302  0
         for (int i = 0; i < args.length - 2; i++) {
 303  0
             if (args[i].equals("-classpath")) {
 304  0
                 classpath = args[++i];
 305  0
             } else if (args[i].equals("-d")) {
 306  0
                 destDirectory = new File(args[++i]);
 307  0
             } else if (args[i].equals("-debug")) {
 308  0
                 debug = true;
 309  0
             } else if (args[i].equals("-keepsource")) {
 310  0
                 retainSource = true;
 311   
             } else {
 312  0
                 usage();
 313  0
                 return;
 314   
             }
 315   
         }
 316   
 
 317   
         /* If the -classpath flag isn't specified, use the system classpath */
 318  0
         if (classpath == null) {
 319  0
             Properties props = System.getProperties();
 320  0
             classpath = props.getProperty("java.class.path");
 321   
         }
 322   
 
 323   
         /*
 324   
          * If the -d flag isn't specified, use the working directory as the
 325   
          * destination directory
 326   
          */
 327  0
         if (destDirectory == null) {
 328  0
             Properties props = System.getProperties();
 329  0
             destDirectory = new File(props.getProperty("user.dir"));
 330   
         }
 331   
 
 332   
         /* Construct a SAXParser used to process the descriptors */
 333  0
         SAXParserFactory parserFactory = SAXParserFactory.newInstance();
 334  0
         parserFactory.setValidating(true);
 335  0
         try {
 336  0
             parser = parserFactory.newSAXParser();
 337   
         } catch (Exception e) {
 338   
             // SAXException or ParserConfigurationException may be thrown
 339  0
             System.out.println("An exception was generated while trying to ");
 340  0
             System.out.println("create a new SAXParser.");
 341  0
             e.printStackTrace();
 342  0
             return;
 343   
         }
 344   
 
 345   
         /* Build and populate an instance of the ejbc utility */
 346  0
         ejbc = new IPlanetEjbc(stdDescriptor, iasDescriptor, destDirectory,
 347   
                                 classpath, parser);
 348  0
         ejbc.setDebugOutput(debug);
 349  0
         ejbc.setRetainSource(retainSource);
 350   
 
 351   
         /* Execute the ejbc utility -- stubs/skeletons are rebuilt, if needed */
 352  0
         try {
 353  0
             ejbc.execute();
 354   
         } catch (IOException e) {
 355  0
             System.out.println("An IOException has occurred while reading the "
 356   
                     + "XML descriptors (" + e.getMessage() + ").");
 357  0
             return;
 358   
         } catch (SAXException e) {
 359  0
             System.out.println("A SAXException has occurred while reading the "
 360   
                     + "XML descriptors (" + e.getMessage() + ").");
 361  0
             return;
 362   
         } catch (IPlanetEjbc.EjbcException e) {
 363  0
             System.out.println("An error has occurred while executing the ejbc "
 364   
                     + "utility (" + e.getMessage() + ").");
 365  0
             return;
 366   
         }
 367   
     }
 368   
 
 369   
     /**
 370   
      * Print a usage statement.
 371   
      */
 372  0
     private static void usage() {
 373  0
         System.out.println("java org.apache.tools.ant.taskdefs.optional.ejb.IPlanetEjbc \\");
 374  0
         System.out.println("  [OPTIONS] [EJB 1.1 descriptor] [iAS EJB descriptor]");
 375  0
         System.out.println("");
 376  0
         System.out.println("Where OPTIONS are:");
 377  0
         System.out.println("  -debug -- for additional debugging output");
 378  0
         System.out.println("  -keepsource -- to retain Java source files generated");
 379  0
         System.out.println("  -classpath [classpath] -- classpath used for compilation");
 380  0
         System.out.println("  -d [destination directory] -- directory for compiled classes");
 381  0
         System.out.println("");
 382  0
         System.out.println("If a classpath is not specified, the system classpath");
 383  0
         System.out.println("will be used.  If a destination directory is not specified,");
 384  0
         System.out.println("the current working directory will be used (classes will");
 385  0
         System.out.println("still be placed in subfolders which correspond to their");
 386  0
         System.out.println("package name).");
 387  0
         System.out.println("");
 388  0
         System.out.println("The EJB home interface, remote interface, and implementation");
 389  0
         System.out.println("class must be found in the destination directory.  In");
 390  0
         System.out.println("addition, the destination will look for the stubs and skeletons");
 391  0
         System.out.println("in the destination directory to ensure they are up to date.");
 392   
     }
 393   
 
 394   
     /**
 395   
      * Compiles the stub and skeletons for the specified EJBs, if they need to
 396   
      * be updated.
 397   
      *
 398   
      * @throws EjbcException If the ejbc utility cannot be correctly configured
 399   
      *                       or if one or more of the EJB "source" classes
 400   
      *                       cannot be found in the destination directory
 401   
      * @throws IOException   If the parser encounters a problem reading the XML
 402   
      *                       file
 403   
      * @throws SAXException  If the parser encounters a problem processing the
 404   
      *                       XML descriptor (it may wrap another exception)
 405   
      */
 406  0
     public void execute() throws EjbcException, IOException, SAXException {
 407   
 
 408  0
         checkConfiguration();   // Throws EjbcException if unsuccessful
 409   
 
 410  0
         EjbInfo[] ejbs = getEjbs(); // Returns list of EJBs for processing
 411   
 
 412  0
         for (int i = 0; i < ejbs.length ; i++) {
 413  0
             log("EJBInfo...");
 414  0
             log(ejbs[i].toString());
 415   
         }
 416   
 
 417  0
         for (int i = 0; i < ejbs.length; i++) {
 418  0
             EjbInfo ejb = ejbs[i];
 419   
 
 420  0
             ejb.checkConfiguration(destDirectory);  // Throws EjbcException
 421   
 
 422  0
             if (ejb.mustBeRecompiled(destDirectory)) {
 423  0
                 log(ejb.getName() + " must be recompiled using ejbc.");
 424   
 
 425  0
                 String[] arguments = buildArgumentList(ejb);
 426  0
                 callEjbc(arguments);
 427   
 
 428   
             } else {
 429  0
                 log(ejb.getName() + " is up to date.");
 430   
             }
 431   
         }
 432   
     }
 433   
 
 434   
     /**
 435   
      * Executes the iPlanet Application Server ejbc command-line utility.
 436   
      *
 437   
      * @param arguments Command line arguments to be passed to the ejbc utility.
 438   
      */
 439  0
     private void callEjbc(String[] arguments) {
 440   
 
 441   
         /* Concatenate all of the command line arguments into a single String */
 442  0
         StringBuffer args = new StringBuffer();
 443  0
         for (int i = 0; i < arguments.length; i++) {
 444  0
             args.append(arguments[i]).append(" ");
 445   
         }
 446   
 
 447   
         /* If an iAS home directory is specified, prepend it to the commmand */
 448  0
         String command;
 449  0
         if (iasHomeDir == null) {
 450  0
             command = "";
 451   
         } else {
 452  0
             command = iasHomeDir.toString() + File.separator + "bin"
 453   
                                                         + File.separator;
 454   
         }
 455  0
         command += "ejbc ";
 456   
 
 457  0
         log(command + args);
 458   
 
 459   
         /*
 460   
          * Use the Runtime object to execute an external command.  Use the
 461   
          * RedirectOutput inner class to direct the standard and error output
 462   
          * from the command to the JRE's standard output
 463   
          */
 464  0
         try {
 465  0
             Process p = Runtime.getRuntime().exec(command + args);
 466  0
             RedirectOutput output = new RedirectOutput(p.getInputStream());
 467  0
             RedirectOutput error  = new RedirectOutput(p.getErrorStream());
 468  0
             output.start();
 469  0
             error.start();
 470  0
             p.waitFor();
 471  0
             p.destroy();
 472   
         } catch (IOException e) {
 473  0
             log("An IOException has occurred while trying to execute ejbc.");
 474  0
             e.printStackTrace();
 475   
         } catch (InterruptedException e) {
 476   
             // Do nothing
 477   
         }
 478   
     }
 479   
 
 480   
     /**
 481   
      * Verifies that the user selections are valid.
 482   
      *
 483   
      * @throws EjbcException If the user selections are invalid.
 484   
      */
 485  0
     protected void checkConfiguration() throws EjbcException {
 486   
 
 487  0
         String msg = "";
 488   
 
 489  0
         if (stdDescriptor == null) {
 490  0
             msg += "A standard XML descriptor file must be specified.  ";
 491   
         }
 492  0
         if (iasDescriptor == null) {
 493  0
             msg += "An iAS-specific XML descriptor file must be specified.  ";
 494   
         }
 495  0
         if (classpath == null) {
 496  0
             msg += "A classpath must be specified.    ";
 497   
         }
 498  0
         if (parser == null) {
 499  0
             msg += "An XML parser must be specified.    ";
 500   
         }
 501   
 
 502  0
         if (destDirectory == null) {
 503  0
             msg += "A destination directory must be specified.  ";
 504  0
         } else if (!destDirectory.exists()) {
 505  0
             msg += "The destination directory specified does not exist.  ";
 506  0
         } else if (!destDirectory.isDirectory()) {
 507  0
             msg += "The destination specified is not a directory.  ";
 508   
         }
 509   
 
 510  0
         if (msg.length() > 0) {
 511  0
             throw new EjbcException(msg);
 512   
         }
 513   
     }
 514   
 
 515   
     /**
 516   
      * Parses the EJB descriptors and returns a list of EJBs which may need to
 517   
      * be compiled.
 518   
      *
 519   
      * @return               An array of objects which describe the EJBs to be
 520   
      *                       processed.
 521   
      * @throws IOException   If the parser encounters a problem reading the XML
 522   
      *                       files
 523   
      * @throws SAXException  If the parser encounters a problem processing the
 524   
      *                       XML descriptor (it may wrap another exception)
 525   
      */
 526  0
     private EjbInfo[] getEjbs() throws IOException, SAXException {
 527  0
         EjbInfo[] ejbs = null;
 528   
 
 529   
         /*
 530   
          * The EJB information is gathered from the standard XML EJB descriptor
 531   
          * and the iAS-specific XML EJB descriptor using a SAX parser.
 532   
          */
 533   
 
 534  0
         parser.parse(stdDescriptor, handler);
 535  0
         parser.parse(iasDescriptor, handler);
 536  0
         ejbs = handler.getEjbs();
 537   
 
 538  0
         return ejbs;
 539   
     }
 540   
 
 541   
     /**
 542   
      * Based on this object's instance variables as well as the EJB to be
 543   
      * processed, the correct flags and parameters are set for the ejbc
 544   
      * command-line utility.
 545   
      * @param ejb The EJB for which stubs and skeletons will be compiled.
 546   
      * @return    An array of Strings which are the command-line parameters for
 547   
      *            for the ejbc utility.
 548   
      */
 549  0
     private String[] buildArgumentList(EjbInfo ejb) {
 550   
 
 551  0
         List arguments = new ArrayList();
 552   
 
 553   
         /* OPTIONAL COMMAND LINE PARAMETERS */
 554   
 
 555  0
         if (debugOutput) {
 556  0
             arguments.add("-debug");
 557   
         }
 558   
 
 559   
         /* No beantype flag is needed for an entity bean */
 560  0
         if (ejb.getBeantype().equals(STATELESS_SESSION)) {
 561  0
             arguments.add("-sl");
 562  0
         } else if (ejb.getBeantype().equals(STATEFUL_SESSION)) {
 563  0
             arguments.add("-sf");
 564   
         }
 565   
 
 566  0
         if (ejb.getIiop()) {
 567  0
             arguments.add("-iiop");
 568   
         }
 569   
 
 570  0
         if (ejb.getCmp()) {
 571  0
             arguments.add("-cmp");
 572   
         }
 573   
 
 574  0
         if (retainSource) {
 575  0
             arguments.add("-gs");
 576   
         }
 577   
 
 578  0
         if (ejb.getHasession()) {
 579  0
             arguments.add("-fo");
 580   
         }
 581   
 
 582   
         /* REQUIRED COMMAND LINE PARAMETERS */
 583   
 
 584  0
         arguments.add("-classpath");
 585  0
         arguments.add(classpath);
 586   
 
 587  0
         arguments.add("-d");
 588  0
         arguments.add(destDirectory.toString());
 589   
 
 590  0
         arguments.add(ejb.getHome().getQualifiedClassName());
 591  0
         arguments.add(ejb.getRemote().getQualifiedClassName());
 592  0
         arguments.add(ejb.getImplementation().getQualifiedClassName());
 593   
 
 594   
         /* Convert the List into an Array and return it */
 595  0
         return (String[]) arguments.toArray(new String[arguments.size()]);
 596   
     }
 597   
 
 598   
     /**
 599   
      * Convenience method used to print messages to the user if debugging
 600   
      * messages are enabled.
 601   
      *
 602   
      * @param msg The String to print to standard output.
 603   
      */
 604  0
     private void log(String msg) {
 605  0
         if (debugOutput) {
 606  0
             System.out.println(msg);
 607   
         }
 608   
     }
 609   
 
 610   
 
 611   
     /* Inner classes follow */
 612   
 
 613   
 
 614   
     /**
 615   
      * This inner class is used to signal any problems during the execution of
 616   
      * the ejbc compiler.
 617   
      *
 618   
      * @author Greg Nelson
 619   
      *         <a href="mailto:greg@netscape.com">greg@netscape.com</a>
 620   
      */
 621   
     public class EjbcException extends Exception {
 622   
 
 623   
         /**
 624   
          * Constructs an exception with the given descriptive message.
 625   
          *
 626   
          * @param msg Description of the exception which has occurred.
 627   
          */
 628  0
         public EjbcException(String msg) {
 629  0
             super(msg);
 630   
         }
 631   
     }  // End of EjbcException inner class
 632   
 
 633   
 
 634   
     /**
 635   
      * This inner class is an XML document handler that can be used to parse EJB
 636   
      * descriptors (both the standard EJB descriptor as well as the iAS-specific
 637   
      * descriptor that stores additional values for iAS).  Once the descriptors
 638   
      * have been processed, the list of EJBs found can be obtained by calling
 639   
      * the <code>getEjbs()</code> method.
 640   
      *
 641   
      * @see    IPlanetEjbc.EjbInfo
 642   
      * @author Greg Nelson
 643   
      *         <a href="mailto:greg@netscape.com">greg@netscape.com</a>
 644   
      */
 645   
     private class EjbcHandler extends HandlerBase {
 646   
 
 647   
         /*
 648   
          * Two Maps are used to track local DTDs that will be used in case the
 649   
          * remote copies of these DTDs cannot be accessed.  The key for the Map
 650   
          * is the DTDs public ID and the value is the local location for the DTD
 651   
          */
 652   
         private Map       resourceDtds = new HashMap();
 653   
         private Map       fileDtds = new HashMap();
 654   
 
 655   
         private Map       ejbs = new HashMap();      // List of EJBs found in XML
 656   
         private EjbInfo   currentEjb;             // One item within the Map
 657   
         private boolean   iasDescriptor = false;  // Is doc iAS or EJB descriptor
 658   
 
 659   
         private String    currentLoc = "";        // Tracks current element
 660   
         private String    currentText;            // Tracks current text data
 661   
         private String    ejbType;                // "session" or "entity"
 662   
 
 663   
         /**
 664   
          * Constructs a new instance of the handler and registers local copies
 665   
          * of the standard EJB 1.1 descriptor DTD as well as iAS's EJB
 666   
          * descriptor DTD.
 667   
          */
 668  0
         public EjbcHandler() {
 669  0
             final String PUBLICID_EJB11 =
 670   
                 "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN";
 671  0
             final String PUBLICID_IPLANET_EJB_60 =
 672   
                 "-//Sun Microsystems, Inc.//DTD iAS Enterprise JavaBeans 1.0//EN";
 673   
 
 674   
 
 675  0
             final String DEFAULT_IAS60_EJB11_DTD_LOCATION =
 676   
                 "ejb-jar_1_1.dtd";
 677  0
             final String DEFAULT_IAS60_DTD_LOCATION =
 678   
                 "IASEjb_jar_1_0.dtd";
 679   
 
 680  0
             registerDTD(PUBLICID_EJB11, DEFAULT_IAS60_EJB11_DTD_LOCATION);
 681  0
             registerDTD(PUBLICID_IPLANET_EJB_60, DEFAULT_IAS60_DTD_LOCATION);
 682   
         }
 683   
 
 684   
         /**
 685   
          * Returns the list of EJB objects found during the processing of the
 686   
          * standard EJB 1.1 descriptor and iAS-specific EJB descriptor.
 687   
          *
 688   
          * @return An array of EJBs which were found during the descriptor
 689   
          *         parsing.
 690   
          */
 691  0
         public EjbInfo[] getEjbs() {
 692  0
             return (EjbInfo[]) ejbs.values().toArray(new EjbInfo[ejbs.size()]);
 693   
         }
 694   
 
 695   
         /**
 696   
          * Returns the value of the display-name element found in the standard
 697   
          * EJB 1.1 descriptor.
 698   
          *
 699   
          * @return String display-name value.
 700   
          */
 701  0
         public String getDisplayName() {
 702  0
             return displayName;
 703   
         }
 704   
 
 705   
         /**
 706   
          * Registers a local DTD that will be used when parsing an EJB
 707   
          * descriptor.  When the DTD's public identifier is found in an XML
 708   
          * document, the parser will reference the local DTD rather than the
 709   
          * remote DTD.  This enables XML documents to be processed even when the
 710   
          * public DTD isn't available.
 711   
          *
 712   
          * @param publicID The DTD's public identifier.
 713   
          * @param location The location of the local DTD copy -- the location
 714   
          *                 may either be a resource found on the classpath or a
 715   
          *                 local file.
 716   
          */
 717  0
         public void registerDTD(String publicID, String location) {
 718  0
             log("Registering: " + location);
 719  0
             if ((publicID == null) || (location == null)) {
 720  0
                 return;
 721   
             }
 722   
 
 723  0
             if (ClassLoader.getSystemResource(location) != null) {
 724  0
                 log("Found resource: " + location);
 725  0
                 resourceDtds.put(publicID, location);
 726   
             } else {
 727  0
                 File dtdFile = new File(location);
 728  0
                 if (dtdFile.exists() && dtdFile.isFile()) {
 729  0
                     log("Found file: " + location);
 730  0
                     fileDtds.put(publicID, location);
 731   
                 }
 732   
             }
 733   
         }
 734   
 
 735   
         /**
 736   
          * Resolves an external entity found during XML processing.  If a public
 737   
          * ID is found that has been registered with the handler, an <code>
 738   
          * InputSource</code> will be returned which refers to the local copy.
 739   
          * If the public ID hasn't been registered or if an error occurs, the
 740   
          * superclass implementation is used.
 741   
          *
 742   
          * @param publicId The DTD's public identifier.
 743   
          * @param systemId The location of the DTD, as found in the XML document.
 744   
          */
 745  0
         public InputSource resolveEntity(String publicId, String systemId)
 746   
                 throws SAXException {
 747  0
             InputStream inputStream = null;
 748   
 
 749   
 
 750  0
             try {
 751   
 
 752   
                 /* Search the resource Map and (if not found) file Map */
 753   
 
 754  0
                 String location = (String) resourceDtds.get(publicId);
 755  0
                 if (location != null) {
 756  0
                     inputStream
 757   
                         = ClassLoader.getSystemResource(location).openStream();
 758   
                 } else {
 759  0
                     location = (String) fileDtds.get(publicId);
 760  0
                     if (location != null) {
 761  0
                         inputStream = new FileInputStream(location);
 762   
                     }
 763   
                 }
 764   
             } catch (IOException e) {
 765  0
                 return super.resolveEntity(publicId, systemId);
 766   
             }
 767   
 
 768  0
             if (inputStream == null) {
 769  0
                 return super.resolveEntity(publicId, systemId);
 770   
             } else {
 771  0
                 return new InputSource(inputStream);
 772   
             }
 773   
         }
 774   
 
 775   
         /**
 776   
          * Receive notification that the start of an XML element has been found.
 777   
          *
 778   
          * @param name String name of the element found.
 779   
          * @param atts AttributeList of the attributes included with the element
 780   
          *             (if any).
 781   
          * @throws SAXException If the parser cannot process the document.
 782   
          */
 783  0
         public void startElement(String name, AttributeList atts)
 784   
                 throws SAXException {
 785   
 
 786   
             /*
 787   
              * I need to "push" the element onto the String (currentLoc) which
 788   
              * always represents the current location in the XML document.
 789   
              */
 790  0
             currentLoc += "\\" + name;
 791   
 
 792   
             /* A new element has started, so reset the text being captured */
 793  0
             currentText = "";
 794   
 
 795  0
             if (currentLoc.equals("\\ejb-jar")) {
 796  0
                 iasDescriptor = false;
 797  0
             } else if (currentLoc.equals("\\ias-ejb-jar")) {
 798  0
                 iasDescriptor = true;
 799   
             }
 800   
 
 801  0
             if ((name.equals("session")) || (name.equals("entity"))) {
 802  0
                 ejbType = name;
 803   
             }
 804   
         }
 805   
 
 806   
         /**
 807   
          * Receive notification that character data has been found in the XML
 808   
          * document
 809   
          *
 810   
          * @param ch Array of characters which have been found in the document.
 811   
          * @param start Starting index of the data found in the document.
 812   
          * @param len The number of characters found in the document.
 813   
          * @throws SAXException If the parser cannot process the document.
 814   
          */
 815  0
         public void characters(char[] ch, int start, int len)
 816   
                 throws SAXException {
 817   
 
 818  0
             currentText += new String(ch).substring(start, start + len);
 819   
         }
 820   
 
 821   
         /**
 822   
          * Receive notification that the end of an XML element has been found.
 823   
          *
 824   
          * @param name String name of the element.
 825   
          * @throws SAXException If the parser cannot process the document.
 826   
          */
 827  0
         public void endElement(String name) throws SAXException {
 828   
 
 829   
             /*
 830   
              * If this is a standard EJB 1.1 descriptor, we are looking for one
 831   
              * set of data, while if this is an iAS-specific descriptor, we're
 832   
              * looking for different set of data.  Hand the processing off to
 833   
              * the appropriate method.
 834   
              */
 835  0
             if (iasDescriptor) {
 836  0
                 iasCharacters(currentText);
 837   
             } else {
 838  0
                 stdCharacters(currentText);
 839   
             }
 840   
 
 841   
             /*
 842   
              * I need to "pop" the element off the String (currentLoc) which
 843   
              * always represents my current location in the XML document.
 844   
              */
 845   
 
 846  0
             int nameLength = name.length() + 1; // Add one for the "\"
 847  0
             int locLength  = currentLoc.length();
 848   
 
 849  0
             currentLoc = currentLoc.substring(0, locLength - nameLength);
 850   
         }
 851   
 
 852   
         /**
 853   
          * Receive notification that character data has been found in a standard
 854   
          * EJB 1.1 descriptor.  We're interested in retrieving the home
 855   
          * interface, remote interface, implementation class, the type of bean,
 856   
          * and if the bean uses CMP.
 857   
          *
 858   
          * @param value String data found in the XML document.
 859   
          */
 860  0
         private void stdCharacters(String value) {
 861   
 
 862  0
             if (currentLoc.equals("\\ejb-jar\\display-name")) {
 863  0
                 displayName = value;
 864  0
                 return;
 865   
             }
 866   
 
 867  0
             String base = "\\ejb-jar\\enterprise-beans\\" + ejbType;
 868   
 
 869  0
             if (currentLoc.equals(base + "\\ejb-name")) {
 870  0
                 currentEjb = (EjbInfo) ejbs.get(value);
 871  0
                 if (currentEjb == null) {
 872  0
                     currentEjb = new EjbInfo(value);
 873  0
                     ejbs.put(value, currentEjb);
 874   
                 }
 875  0
             } else if (currentLoc.equals(base + "\\home")) {
 876  0
                 currentEjb.setHome(value);
 877  0
             } else if (currentLoc.equals(base + "\\remote")) {
 878  0
                 currentEjb.setRemote(value);
 879  0
             } else if (currentLoc.equals(base + "\\ejb-class")) {
 880  0
                 currentEjb.setImplementation(value);
 881  0
             } else if (currentLoc.equals(base + "\\prim-key-class")) {
 882  0
                 currentEjb.setPrimaryKey(value);
 883  0
             } else if (currentLoc.equals(base + "\\session-type")) {
 884  0
                 currentEjb.setBeantype(value);
 885  0
             } else if (currentLoc.equals(base + "\\persistence-type")) {
 886  0
                 currentEjb.setCmp(value);
 887   
             }
 888   
         }
 889   
 
 890   
         /**
 891   
          * Receive notification that character data has been found in an
 892   
          * iAS-specific descriptor.  We're interested in retrieving data
 893   
          * indicating whether the bean must support RMI/IIOP access, whether
 894   
          * the bean must provide highly available stubs and skeletons (in the
 895   
          * case of stateful session beans), and if this bean uses additional
 896   
          * CMP XML descriptors (in the case of entity beans with CMP).
 897   
          *
 898   
          * @param value String data found in the XML document.
 899   
          */
 900  0
         private void iasCharacters(String value) {
 901  0
             String base = "\\ias-ejb-jar\\enterprise-beans\\" + ejbType;
 902   
 
 903  0
             if (currentLoc.equals(base + "\\ejb-name")) {
 904  0
                 currentEjb = (EjbInfo) ejbs.get(value);
 905  0
                 if (currentEjb == null) {
 906  0
                     currentEjb = new EjbInfo(value);
 907  0
                     ejbs.put(value, currentEjb);
 908   
                 }
 909  0
             } else if (currentLoc.equals(base + "\\iiop")) {
 910  0
                 currentEjb.setIiop(value);
 911  0
             } else if (currentLoc.equals(base + "\\failover-required")) {
 912  0
                 currentEjb.setHasession(value);
 913  0
             } else if (currentLoc.equals(base + "\\persistence-manager"
 914   
                                               + "\\properties-file-location")) {
 915  0
                 currentEjb.addCmpDescriptor(value);
 916   
             }
 917   
         }
 918   
     }  // End of EjbcHandler inner class
 919   
 
 920   
 
 921   
     /**
 922   
      * This inner class represents an EJB that will be compiled using ejbc.
 923   
      *
 924   
      * @author Greg Nelson
 925   
      *         <a href="mailto:greg@netscape.com">greg@netscape.com</a>
 926   
      */
 927   
     private class EjbInfo {
 928   
         private String     name;              // EJB's display name
 929   
         private Classname  home;              // EJB's home interface name
 930   
         private Classname  remote;            // EJB's remote interface name
 931   
         private Classname  implementation;      // EJB's implementation class
 932   
         private Classname  primaryKey;        // EJB's primary key class
 933   
         private String  beantype = "entity";  // or "stateful" or "stateless"
 934   
         private boolean cmp       = false;      // Does this EJB support CMP?
 935   
         private boolean iiop      = false;      // Does this EJB support IIOP?
 936   
         private boolean hasession = false;      // Does this EJB require failover?
 937   
         private List cmpDescriptors = new ArrayList();  // CMP descriptor list
 938   
 
 939   
         /**
 940   
          * Construct a new EJBInfo object with the given name.
 941   
          *
 942   
          * @param name The display name for the EJB.
 943   
          */
 944  0
         public EjbInfo(String name) {
 945  0
             this.name = name;
 946   
         }
 947   
 
 948   
         /**
 949   
          * Returns the display name of the EJB.  If a display name has not been
 950   
          * set, it returns the EJB implementation classname (if the
 951   
          * implementation class is not set, it returns "[unnamed]").
 952   
          *
 953   
          * @return The display name for the EJB.
 954   
          */
 955  0
         public String getName() {
 956  0
             if (name == null) {
 957  0
                 if (implementation == null) {
 958  0
                     return "[unnamed]";
 959   
                 } else {
 960  0
                     return implementation.getClassName();
 961   
                 }
 962   
             }
 963  0
             return name;
 964   
         }
 965   
 
 966   
         /*
 967   
          * Below are getter's and setter's for each of the instance variables.
 968   
          * Note that (in addition to supporting setters with the same type as
 969   
          * the instance variable) a setter is provided with takes a String
 970   
          * argument -- this are provided so the XML document handler can set
 971   
          * the EJB values using the Strings it parses.
 972   
          */
 973   
 
 974  0
         public void setHome(String home) {
 975  0
             setHome(new Classname(home));
 976   
         }
 977   
 
 978  0
         public void setHome(Classname home) {
 979  0
             this.home = home;
 980   
         }
 981   
 
 982  0
         public Classname getHome() {
 983  0
             return home;
 984   
         }
 985   
 
 986  0
         public void setRemote(String remote) {
 987  0
             setRemote(new Classname(remote));
 988   
         }
 989   
 
 990  0
         public void setRemote(Classname remote) {
 991  0
             this.remote = remote;
 992   
         }
 993   
 
 994  0
         public Classname getRemote() {
 995  0
             return remote;
 996   
         }
 997   
 
 998  0
         public void setImplementation(String implementation) {
 999  0
             setImplementation(new Classname(implementation));
 1000   
         }
 1001   
 
 1002  0
         public void setImplementation(Classname implementation) {
 1003  0
             this.implementation = implementation;
 1004   
         }
 1005   
 
 1006  0
         public Classname getImplementation() {
 1007  0
             return implementation;
 1008   
         }
 1009   
 
 1010  0
         public void setPrimaryKey(String primaryKey) {
 1011  0
             setPrimaryKey(new Classname(primaryKey));
 1012   
         }
 1013   
 
 1014  0
         public void setPrimaryKey(Classname primaryKey) {
 1015  0
             this.primaryKey = primaryKey;
 1016   
         }
 1017   
 
 1018  0
         public Classname getPrimaryKey() {
 1019  0
             return primaryKey;
 1020   
         }
 1021   
 
 1022  0
         public void setBeantype(String beantype) {
 1023  0
             this.beantype = beantype.toLowerCase();
 1024   
         }
 1025   
 
 1026  0
         public String getBeantype() {
 1027  0
             return beantype;
 1028   
         }
 1029   
 
 1030  0
         public void setCmp(boolean cmp) {
 1031  0
             this.cmp = cmp;
 1032   
         }
 1033   
 
 1034  0
         public void setCmp(String cmp) {
 1035  0
             setCmp(cmp.equals("Container"));
 1036   
         }
 1037   
 
 1038  0
         public boolean getCmp() {
 1039  0
             return cmp;
 1040   
         }
 1041   
 
 1042  0
         public void setIiop(boolean iiop) {
 1043  0
             this.iiop = iiop;
 1044   
         }
 1045   
 
 1046  0
         public void setIiop(String iiop) {
 1047  0
             setIiop(iiop.equals("true"));
 1048   
         }
 1049   
 
 1050  0
         public boolean getIiop() {
 1051  0
             return iiop;
 1052   
         }
 1053   
 
 1054  0
         public void setHasession(boolean hasession) {
 1055  0
             this.hasession = hasession;
 1056   
         }
 1057   
 
 1058  0
         public void setHasession(String hasession) {
 1059  0
             setHasession(hasession.equals("true"));
 1060   
         }
 1061   
 
 1062  0
         public boolean getHasession() {
 1063  0
             return hasession;
 1064   
         }
 1065   
 
 1066  0
         public void addCmpDescriptor(String descriptor) {
 1067  0
             cmpDescriptors.add(descriptor);
 1068   
         }
 1069   
 
 1070  0
         public List getCmpDescriptors() {
 1071  0
             return cmpDescriptors;
 1072   
         }
 1073   
 
 1074   
         /**
 1075   
          * Verifies that the EJB is valid--if it is invalid, an exception is
 1076   
          * thrown
 1077   
          *
 1078   
          *
 1079   
          * @param buildDir The directory where the EJB remote interface, home
 1080   
          *                 interface, and implementation class must be found.
 1081   
          * @throws EjbcException If the EJB is invalid.
 1082   
          */
 1083  0
         private void checkConfiguration(File buildDir) throws EjbcException  {
 1084   
 
 1085   
             /* Check that the specified instance variables are valid */
 1086  0
             if (home == null) {
 1087  0
                 throw new EjbcException("A home interface was not found "
 1088   
                             + "for the " + name + " EJB.");
 1089   
             }
 1090  0
             if (remote == null) {
 1091  0
                 throw new EjbcException("A remote interface was not found "
 1092   
                             + "for the " + name + " EJB.");
 1093   
             }
 1094  0
             if (implementation == null) {
 1095  0
                 throw new EjbcException("An EJB implementation class was not "
 1096   
                             + "found for the " + name + " EJB.");
 1097   
             }
 1098   
 
 1099  0
             if ((!beantype.equals(ENTITY_BEAN))
 1100   
                         && (!beantype.equals(STATELESS_SESSION))
 1101   
                         && (!beantype.equals(STATEFUL_SESSION))) {
 1102  0
                 throw new EjbcException("The beantype found (" + beantype + ") "
 1103   
                             + "isn't valid in the " + name + " EJB.");
 1104   
             }
 1105   
 
 1106  0
             if (cmp && (!beantype.equals(ENTITY_BEAN))) {
 1107  0
                 System.out.println("CMP stubs and skeletons may not be generated"
 1108   
                     + " for a Session Bean -- the \"cmp\" attribute will be"
 1109   
                     + " ignoredfor the " + name + " EJB.");
 1110   
             }
 1111   
 
 1112  0
             if (hasession && (!beantype.equals(STATEFUL_SESSION))) {
 1113  0
                 System.out.println("Highly available stubs and skeletons may "
 1114   
                     + "only be generated for a Stateful Session Bean -- the "
 1115   
                     + "\"hasession\" attribute will be ignored for the "
 1116   
                     + name + " EJB.");
 1117   
             }
 1118   
 
 1119   
             /* Check that the EJB "source" classes all exist */
 1120  0
             if (!remote.getClassFile(buildDir).exists()) {
 1121  0
                 throw new EjbcException("The remote interface "
 1122   
                             + remote.getQualifiedClassName() + " could not be "
 1123   
                             + "found.");
 1124   
             }
 1125  0
             if (!home.getClassFile(buildDir).exists()) {
 1126  0
                 throw new EjbcException("The home interface "
 1127   
                             + home.getQualifiedClassName() + " could not be "
 1128   
                             + "found.");
 1129   
             }
 1130  0
             if (!implementation.getClassFile(buildDir).exists()) {
 1131  0
                 throw new EjbcException("The EJB implementation class "
 1132   
                             + implementation.getQualifiedClassName() + " could "
 1133   
                             + "not be found.");
 1134   
             }
 1135   
         }
 1136   
 
 1137   
         /**
 1138   
          * Determines if the ejbc utility needs to be run or not.  If the stubs
 1139   
          * and skeletons can all be found in the destination directory AND all
 1140   
          * of their timestamps are more recent than the EJB source classes
 1141   
          * (home, remote, and implementation classes), the method returns
 1142   
          * <code>false</code>.  Otherwise, the method returns <code>true</code>.
 1143   
          *
 1144   
          * @param destDir The directory where the EJB source classes, stubs and
 1145   
          *                skeletons are located.
 1146   
          * @return A boolean indicating whether or not the ejbc utility needs to
 1147   
          *         be run to bring the stubs and skeletons up to date.
 1148   
          */
 1149  0
         public boolean mustBeRecompiled(File destDir) {
 1150   
 
 1151  0
             long sourceModified = sourceClassesModified(destDir);
 1152   
 
 1153  0
             long destModified = destClassesModified(destDir);
 1154   
 
 1155  0
             return (destModified < sourceModified);
 1156   
         }
 1157   
 
 1158   
         /**
 1159   
          * Examines each of the EJB source classes (home, remote, and
 1160   
          * implementation) and returns the modification timestamp for the
 1161   
          * "oldest" class.
 1162   
          *
 1163   
          * @param classpath The classpath to be used to find the source EJB
 1164   
          *                  classes.  If <code>null</code>, the system classpath
 1165   
          *                  is used.
 1166   
          * @return The modification timestamp for the "oldest" EJB source class.
 1167   
          * @throws BuildException If one of the EJB source classes cannot be
 1168   
          *                        found on the classpath.
 1169   
          */
 1170  0
         private long sourceClassesModified(File buildDir) {
 1171  0
             long latestModified; // The timestamp of the "newest" class
 1172  0
             long modified;       // Timestamp for a given class
 1173  0
             File remoteFile;     // File for the remote interface class
 1174  0
             File homeFile;       // File for the home interface class
 1175  0
             File implFile;       // File for the EJB implementation class
 1176  0
             File pkFile;         // File for the EJB primary key class
 1177   
 
 1178   
             /* Check the timestamp on the remote interface */
 1179  0
             remoteFile = remote.getClassFile(buildDir);
 1180  0
             modified = remoteFile.lastModified();
 1181  0
             if (modified == -1) {
 1182  0
                 System.out.println("The class "
 1183   
                                 + remote.getQualifiedClassName() + " couldn't "
 1184   
                                 + "be found on the classpath");
 1185  0
                 return -1;
 1186   
             }
 1187  0
             latestModified = modified;
 1188   
 
 1189   
             /* Check the timestamp on the home interface */
 1190  0
             homeFile = home.getClassFile(buildDir);
 1191  0
             modified = homeFile.lastModified();
 1192  0
             if (modified == -1) {
 1193  0
                 System.out.println("The class "
 1194   
                                 + home.getQualifiedClassName() + " couldn't be "
 1195   
                                 + "found on the classpath");
 1196  0
                 return -1;
 1197   
             }
 1198  0
             latestModified = Math.max(latestModified, modified);
 1199   
 
 1200   
             /* Check the timestamp of the primary key class */
 1201  0
             if (primaryKey != null) {
 1202  0
                 pkFile = primaryKey.getClassFile(buildDir);
 1203  0
                 modified = pkFile.lastModified();
 1204  0
                 if (modified == -1) {
 1205  0
                     System.out.println("The class "
 1206   
                                     + primaryKey.getQualifiedClassName() + "couldn't be "
 1207   
                                     + "found on the classpath");
 1208  0
                     return -1;
 1209   
                 }
 1210  0
                 latestModified = Math.max(latestModified, modified);
 1211   
             } else {
 1212  0
                 pkFile = null;
 1213   
             }
 1214   
 
 1215   
             /* Check the timestamp on the EJB implementation class.
 1216   
              *
 1217   
              * Note that if ONLY the implementation class has changed, it's not
 1218   
              * necessary to rebuild the EJB stubs and skeletons.  For this
 1219   
              * reason, we ensure the file exists (using lastModified above), but
 1220   
              * we DON'T compare it's timestamp with the timestamps of the home
 1221   
              * and remote interfaces (because it's irrelevant in determining if
 1222   
              * ejbc must be run)
 1223   
              */
 1224  0
             implFile = implementation.getClassFile(buildDir);
 1225  0
             modified = implFile.lastModified();
 1226  0
             if (modified == -1) {
 1227  0
                 System.out.println("The class "
 1228   
                                 + implementation.getQualifiedClassName()
 1229   
                                 + " couldn't be found on the classpath");
 1230  0
                 return -1;
 1231   
             }
 1232   
 
 1233  0
             String pathToFile = remote.getQualifiedClassName();
 1234  0
             pathToFile = pathToFile.replace('.', File.separatorChar) + ".class";
 1235  0
             ejbFiles.put(pathToFile, remoteFile);
 1236   
 
 1237  0
             pathToFile = home.getQualifiedClassName();
 1238  0
             pathToFile = pathToFile.replace('.', File.separatorChar) + ".class";
 1239  0
             ejbFiles.put(pathToFile, homeFile);
 1240   
 
 1241  0
             pathToFile = implementation.getQualifiedClassName();
 1242  0
             pathToFile = pathToFile.replace('.', File.separatorChar) + ".class";
 1243  0
             ejbFiles.put(pathToFile, implFile);
 1244   
 
 1245  0
             if (pkFile != null) {
 1246  0
                 pathToFile = primaryKey.getQualifiedClassName();
 1247  0
                 pathToFile = pathToFile.replace('.', File.separatorChar) + ".class";
 1248  0
                 ejbFiles.put(pathToFile, pkFile);
 1249   
             }
 1250   
 
 1251  0
             return latestModified;
 1252   
         }
 1253   
 
 1254   
         /**
 1255   
          * Examines each of the EJB stubs and skeletons in the destination
 1256   
          * directory and returns the modification timestamp for the "oldest"
 1257   
          * class. If one of the stubs or skeletons cannot be found, <code>-1
 1258   
          * </code> is returned.
 1259   
          *
 1260   
          * @param dest The directory in which the EJB stubs and skeletons are
 1261   
          *             stored.
 1262   
          * @return The modification timestamp for the "oldest" EJB stub or
 1263   
          *         skeleton.  If one of the classes cannot be found, <code>-1
 1264   
          *         </code> is returned.
 1265   
          * @throws BuildException If the canonical path of the destination
 1266   
          *                        directory cannot be found.
 1267   
          */
 1268  0
         private long destClassesModified(File destDir) {
 1269  0
             String[] classnames = classesToGenerate(); // List of all stubs & skels
 1270  0
             long destClassesModified = new Date().getTime(); // Earliest mod time
 1271  0
             boolean allClassesFound  = true;           // Has each been found?
 1272   
 
 1273   
             /*
 1274   
              * Loop through each stub/skeleton class that must be generated, and
 1275   
              * determine (if all exist) which file has the most recent timestamp
 1276   
              */
 1277  0
             for (int i = 0; i < classnames.length; i++) {
 1278   
 
 1279  0
                 String pathToClass =
 1280   
                         classnames[i].replace('.', File.separatorChar) + ".class";
 1281  0
                 File classFile = new File(destDir, pathToClass);
 1282   
 
 1283   
                 /*
 1284   
                  * Add each stub/skeleton class to the list of EJB files.  Note
 1285   
                  * that each class is added even if it doesn't exist now.
 1286   
                  */
 1287  0
                 ejbFiles.put(pathToClass, classFile);
 1288   
 
 1289  0
                 allClassesFound = allClassesFound && classFile.exists();
 1290   
 
 1291  0
                 if (allClassesFound) {
 1292  0
                     long fileMod = classFile.lastModified();
 1293   
 
 1294   
                     /* Keep track of the oldest modification timestamp */
 1295  0
                     destClassesModified = Math.min(destClassesModified, fileMod);
 1296   
                 }
 1297   
             }
 1298   
 
 1299  0
             return (allClassesFound) ? destClassesModified : -1;
 1300   
         }
 1301   
 
 1302   
         /**
 1303   
          * Builds an array of class names which represent the stubs and
 1304   
          * skeletons which need to be generated for a given EJB.  The class
 1305   
          * names are fully qualified.  Nine classes are generated for all EJBs
 1306   
          * while an additional six classes are generated for beans requiring
 1307   
          * RMI/IIOP access.
 1308   
          *
 1309   
          * @return An array of Strings representing the fully-qualified class
 1310   
          *         names for the stubs and skeletons to be generated.
 1311   
          */
 1312  0
         private String[] classesToGenerate() {
 1313  0
             String[] classnames = (iiop) ? new String[15] : new String[9];
 1314   
 
 1315  0
             final String remotePkg     = remote.getPackageName() + ".";
 1316  0
             final String remoteClass   = remote.getClassName();
 1317  0
             final String homePkg       = home.getPackageName() + ".";
 1318  0
             final String homeClass     = home.getClassName();
 1319  0
             final String implPkg       = implementation.getPackageName() + ".";
 1320  0
             final String implFullClass = implementation.getQualifiedWithUnderscores();
 1321  0
             int index = 0;
 1322   
 
 1323  0
             classnames[index++] = implPkg + "ejb_fac_" + implFullClass;
 1324  0
             classnames[index++] = implPkg + "ejb_home_" + implFullClass;
 1325  0
             classnames[index++] = implPkg + "ejb_skel_" + implFullClass;
 1326  0
             classnames[index++] = remotePkg + "ejb_kcp_skel_" + remoteClass;
 1327  0
             classnames[index++] = homePkg + "ejb_kcp_skel_" + homeClass;
 1328  0
             classnames[index++] = remotePkg + "ejb_kcp_stub_" + remoteClass;
 1329  0
             classnames[index++] = homePkg + "ejb_kcp_stub_" + homeClass;
 1330  0
             classnames[index++] = remotePkg + "ejb_stub_" + remoteClass;
 1331  0
             classnames[index++] = homePkg + "ejb_stub_" + homeClass;
 1332   
 
 1333  0
             if (!iiop) {
 1334  0
                 return classnames;
 1335   
             }
 1336   
 
 1337  0
             classnames[index++] = "org.omg.stub." + remotePkg + "_"
 1338   
                                     + remoteClass + "_Stub";
 1339  0
             classnames[index++] = "org.omg.stub." + homePkg + "_"
 1340   
                                     + homeClass + "_Stub";
 1341  0
             classnames[index++] = "org.omg.stub." + remotePkg
 1342   
                                     + "_ejb_RmiCorbaBridge_"
 1343   
                                     + remoteClass + "_Tie";
 1344  0
             classnames[index++] = "org.omg.stub." + homePkg
 1345   
                                     + "_ejb_RmiCorbaBridge_"
 1346   
                                     + homeClass + "_Tie";
 1347   
 
 1348  0
             classnames[index++] = remotePkg + "ejb_RmiCorbaBridge_"
 1349   
                                                         + remoteClass;
 1350  0
             classnames[index++] = homePkg + "ejb_RmiCorbaBridge_" + homeClass;
 1351   
 
 1352  0
             return classnames;
 1353   
         }
 1354   
 
 1355   
         /**
 1356   
          * Convenience method which creates a String representation of all the
 1357   
          * instance variables of an EjbInfo object.
 1358   
          *
 1359   
          * @return A String representing the EjbInfo instance.
 1360   
          */
 1361  0
         public String toString() {
 1362  0
             String s = "EJB name: " + name
 1363   
                         + "\n\r              home:      " + home
 1364   
                         + "\n\r              remote:    " + remote
 1365   
                         + "\n\r              impl:      " + implementation
 1366   
                         + "\n\r              primaryKey: " + primaryKey
 1367   
                         + "\n\r              beantype:  " + beantype
 1368   
                         + "\n\r              cmp:       " + cmp
 1369   
                         + "\n\r              iiop:      " + iiop
 1370   
                         + "\n\r              hasession: " + hasession;
 1371   
 
 1372  0
             Iterator i = cmpDescriptors.iterator();
 1373  0
             while (i.hasNext()) {
 1374  0
                 s += "\n\r              CMP Descriptor: " + i.next();
 1375   
             }
 1376   
 
 1377  0
             return s;
 1378   
         }
 1379   
 
 1380   
     } // End of EjbInfo inner class
 1381   
 
 1382   
     /**
 1383   
      * Convenience class used to represent the fully qualified name of a Java
 1384   
      * class.  It provides an easy way to retrieve components of the class name
 1385   
      * in a format that is convenient for building iAS stubs and skeletons.
 1386   
      *
 1387   
      * @author Greg Nelson <a href="mailto:greg@netscape.com">greg@netscape.com</a>
 1388   
      */
 1389   
     private class Classname {
 1390   
         private String qualifiedName;  // Fully qualified name of the Java class
 1391   
         private String packageName;    // Name of the package for this class
 1392   
         private String className;      // Name of the class without the package
 1393   
 
 1394   
         /**
 1395   
          * This constructor builds an object which represents the name of a Java
 1396   
          * class.
 1397   
          *
 1398   
          * @param qualifiedName String representing the fully qualified class
 1399   
          *                      name of the Java class.
 1400   
          */
 1401  0
         public Classname(String qualifiedName) {
 1402  0
             if (qualifiedName == null) {
 1403  0
                 return;
 1404   
             }
 1405   
 
 1406  0
             this.qualifiedName = qualifiedName;
 1407   
 
 1408  0
             int index = qualifiedName.lastIndexOf('.');
 1409  0
             if (index == -1) {
 1410  0
                 className = qualifiedName;
 1411  0
                 packageName = "";
 1412   
             } else {
 1413  0
                 packageName = qualifiedName.substring(0, index);
 1414  0
                 className   = qualifiedName.substring(index + 1);
 1415   
             }
 1416   
         }
 1417   
 
 1418   
         /**
 1419   
          * Gets the fully qualified name of the Java class.
 1420   
          *
 1421   
          * @return String representing the fully qualified class name.
 1422   
          */
 1423  0
         public String getQualifiedClassName() {
 1424  0
             return qualifiedName;
 1425   
         }
 1426   
 
 1427   
         /**
 1428   
          * Gets the package name for the Java class.
 1429   
          *
 1430   
          * @return String representing the package name for the class.
 1431   
          */
 1432  0
         public String getPackageName() {
 1433  0
             return packageName;
 1434   
         }
 1435   
 
 1436   
         /**
 1437   
          * Gets the Java class name without the package structure.
 1438   
          *
 1439   
          * @return String representing the name for the class.
 1440   
          */
 1441  0
         public String getClassName() {
 1442  0
             return className;
 1443   
         }
 1444   
 
 1445   
         /**
 1446   
          * Gets the fully qualified name of the Java class with underscores
 1447   
          * separating the components of the class name rather than periods.
 1448   
          * This format is used in naming some of the stub and skeleton classes
 1449   
          * for the iPlanet Application Server.
 1450   
          *
 1451   
          * @return String representing the fully qualified class name using
 1452   
          *         underscores instead of periods.
 1453   
          */
 1454  0
         public String getQualifiedWithUnderscores() {
 1455  0
             return qualifiedName.replace('.', '_');
 1456   
         }
 1457   
 
 1458   
         /**
 1459   
          * Returns a File which references the class relative to the specified
 1460   
          * directory.  Note that the class file may or may not exist.
 1461   
          *
 1462   
          * @param  directory A File referencing the base directory containing
 1463   
          *                   class files.
 1464   
          * @return File referencing this class.
 1465   
          */
 1466  0
         public File getClassFile(File directory) {
 1467  0
             String pathToFile = qualifiedName.replace('.', File.separatorChar)
 1468   
                                             + ".class";
 1469  0
             return new File(directory, pathToFile);
 1470   
         }
 1471   
 
 1472   
         /**
 1473   
          * String representation of this class name.  It returns the fully
 1474   
          * qualified class name.
 1475   
          *
 1476   
          * @return String representing the fully qualified class name.
 1477   
          */
 1478  0
         public String toString() {
 1479  0
             return getQualifiedClassName();
 1480   
         }
 1481   
     }  // End of Classname inner class
 1482   
 
 1483   
 
 1484   
     /**
 1485   
      * Thread class used to redirect output from an <code>InputStream</code> to
 1486   
      * the JRE standard output.  This class may be used to redirect output from
 1487   
      * an external process to the standard output.
 1488   
      *
 1489   
      * @author Greg Nelson <a href="mailto:greg@netscape.com">greg@netscape.com</a>
 1490   
      */
 1491   
     private class RedirectOutput extends Thread {
 1492   
         InputStream stream;  // Stream to read and redirect to standard output
 1493   
 
 1494   
         /**
 1495   
          * Constructs a new instance that will redirect output from the
 1496   
          * specified stream to the standard output.
 1497   
          *
 1498   
          * @param stream InputStream which will be read and redirected to the
 1499   
          *               standard output.
 1500   
          */
 1501  0
         public RedirectOutput(InputStream stream) {
 1502  0
             this.stream = stream;
 1503   
         }
 1504   
 
 1505   
         /**
 1506   
          * Reads text from the input stream and redirects it to standard output
 1507   
          * using a separate thread.
 1508   
          */
 1509  0
         public void run() {
 1510  0
             BufferedReader reader = new BufferedReader(
 1511   
                                             new InputStreamReader(stream));
 1512  0
             String text;
 1513  0
             try {
 1514  0
                 while ((text = reader.readLine()) != null) {
 1515  0
                     System.out.println(text);
 1516   
                 }
 1517   
             } catch (IOException e) {
 1518  0
                 e.printStackTrace();
 1519   
             } finally {
 1520  0
                 try {
 1521  0
                     reader.close();
 1522   
                 } catch (IOException e) {
 1523   
                     // Do nothing
 1524   
                 }
 1525   
             }
 1526   
         }
 1527   
     }  // End of RedirectOutput inner class
 1528   
 
 1529   
 }
 1530