0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: *
0017: */
0018:
0019: package org.apache.tools.ant.taskdefs.optional.ejb;
0020:
0021: import java.io.BufferedReader;
0022: import java.io.File;
0023: import java.io.FileInputStream;
0024: import java.io.IOException;
0025: import java.io.InputStream;
0026: import java.io.InputStreamReader;
0027: import java.util.ArrayList;
0028: import java.util.Date;
0029: import java.util.HashMap;
0030: import java.util.Hashtable;
0031: import java.util.Iterator;
0032: import java.util.List;
0033: import java.util.Map;
0034: import java.util.Properties;
0035: import java.util.StringTokenizer;
0036: import javax.xml.parsers.SAXParser;
0037: import javax.xml.parsers.SAXParserFactory;
0038: import org.xml.sax.AttributeList;
0039: import org.xml.sax.HandlerBase;
0040: import org.xml.sax.InputSource;
0041: import org.xml.sax.SAXException;
0042:
0043: /**
0044: * Compiles EJB stubs and skeletons for the iPlanet Application
0045: * Server (iAS). The class will read a standard EJB descriptor (as well as an
0046: * EJB descriptor specific to iPlanet Application Server) to identify one or
0047: * more EJBs to process. It will search for EJB "source" classes (the remote
0048: ; * interface, home interface, and EJB implementation class) and the EJB stubs
0049: * and skeletons in the specified destination directory. Only if the stubs and
0050: * skeletons cannot be found or if they're out of date will the iPlanet
0051: * Application Server ejbc utility be run.
0052: * <p>
0053: * Because this class (and it's assorted inner classes) may be bundled into the
0054: * iPlanet Application Server distribution at some point (and removed from the
0055: * Ant distribution), the class has been written to be independent of all
0056: * Ant-specific classes. It is also for this reason (and to avoid cluttering
0057: * the Apache Ant source files) that this utility has been packaged into a
0058: * single source file.
0059: * <p>
0060: * For more information on Ant Tasks for iPlanet Application Server, see the
0061: * <code>IPlanetDeploymentTool</code> and <code>IPlanetEjbcTask</code> classes.
0062: *
0063: * @see IPlanetDeploymentTool
0064: * @see IPlanetEjbcTask
0065: * @ant.task ignore="true"
0066: */
0067: public class IPlanetEjbc {
0068:
0069: private static final int MIN_NUM_ARGS = 2;
0070: private static final int MAX_NUM_ARGS = 8;
0071: private static final int NUM_CLASSES_WITH_IIOP = 15;
0072: private static final int NUM_CLASSES_WITHOUT_IIOP = 9;
0073:
0074: /* Constants used for the "beantype" attribute */
0075: private static final String ENTITY_BEAN = "entity";
0076: private static final String STATELESS_SESSION = "stateless";
0077: private static final String STATEFUL_SESSION = "stateful";
0078:
0079: /* Filenames of the standard EJB descriptor and the iAS-specific descriptor */
0080: private File stdDescriptor;
0081: private File iasDescriptor;
0082:
0083: /*
0084: * Directory where "source" EJB files are stored and where stubs and
0085: * skeletons will also be written.
0086: */
0087: private File destDirectory;
0088:
0089: /* Classpath used when the iAS ejbc is called */
0090: private String classpath;
0091: private String[] classpathElements;
0092:
0093: /* Options passed to the iAS ejbc */
0094: private boolean retainSource = false;
0095: private boolean debugOutput = false;
0096:
0097: /* iAS installation directory (used if ejbc isn't on user's PATH) */
0098: private File iasHomeDir;
0099:
0100: /* Parser and handler used to process both EJB descriptor files */
0101: private SAXParser parser;
0102: private EjbcHandler handler = new EjbcHandler();
0103:
0104: /*
0105: * This Hashtable maintains a list of EJB class files processed by the ejbc
0106: * utility (both "source" class files as well as stubs and skeletons). The
0107: * key for the Hashtable is a String representing the path to the class file
0108: * (relative to the destination directory). The value for the Hashtable is
0109: * a File object which reference the actual class file.
0110: */
0111: private Hashtable ejbFiles = new Hashtable();
0112:
0113: /* Value of the display-name element read from the standard EJB descriptor */
0114: private String displayName;
0115:
0116: /**
0117: * Constructs an instance which may be used to process EJB descriptors and
0118: * generate EJB stubs and skeletons, if needed.
0119: *
0120: * @param stdDescriptor File referencing a standard EJB descriptor.
0121: * @param iasDescriptor File referencing an iAS-specific EJB descriptor.
0122: * @param destDirectory File referencing the base directory where both
0123: * EJB "source" files are found and where stubs and
0124: * skeletons will be written.
0125: * @param classpath String representation of the classpath to be used
0126: * by the iAS ejbc utility.
0127: * @param parser SAXParser to be used to process both of the EJB
0128: * descriptors.
0129: * @todo classpathElements is not needed here, its never used
0130: * (at least IDEA tells me so! :)
0131: */
0132: public IPlanetEjbc(File stdDescriptor, File iasDescriptor,
0133: File destDirectory, String classpath, SAXParser parser) {
0134: this .stdDescriptor = stdDescriptor;
0135: this .iasDescriptor = iasDescriptor;
0136: this .destDirectory = destDirectory;
0137: this .classpath = classpath;
0138: this .parser = parser;
0139:
0140: /*
0141: * Parse the classpath into it's individual elements and store the
0142: * results in the "classpathElements" instance variable.
0143: */
0144: List elements = new ArrayList();
0145: if (classpath != null) {
0146: StringTokenizer st = new StringTokenizer(classpath,
0147: File.pathSeparator);
0148: while (st.hasMoreTokens()) {
0149: elements.add(st.nextToken());
0150: }
0151: classpathElements = (String[]) elements
0152: .toArray(new String[elements.size()]);
0153: }
0154: }
0155:
0156: /**
0157: * If true, the Java source files which are generated by the
0158: * ejbc process are retained.
0159: *
0160: * @param retainSource A boolean indicating if the Java source files for
0161: * the stubs and skeletons should be retained.
0162: * @todo This is not documented in the HTML. On purpose?
0163: */
0164: public void setRetainSource(boolean retainSource) {
0165: this .retainSource = retainSource;
0166: }
0167:
0168: /**
0169: * If true, enables debugging output when ejbc is executed.
0170: *
0171: * @param debugOutput A boolean indicating if debugging output should be
0172: * generated
0173: */
0174: public void setDebugOutput(boolean debugOutput) {
0175: this .debugOutput = debugOutput;
0176: }
0177:
0178: /**
0179: * Registers the location of a local DTD file or resource. By registering
0180: * a local DTD, EJB descriptors can be parsed even when the remote servers
0181: * which contain the "public" DTDs cannot be accessed.
0182: *
0183: * @param publicID The public DTD identifier found in an XML document.
0184: * @param location The file or resource name for the appropriate DTD stored
0185: * on the local machine.
0186: */
0187: public void registerDTD(String publicID, String location) {
0188: handler.registerDTD(publicID, location);
0189: }
0190:
0191: /**
0192: * May be used to specify the "home" directory for this iAS installation.
0193: * The directory specified should typically be
0194: * <code>[install-location]/iplanet/ias6/ias</code>.
0195: *
0196: * @param iasHomeDir The home directory for the user's iAS installation.
0197: */
0198: public void setIasHomeDir(File iasHomeDir) {
0199: this .iasHomeDir = iasHomeDir;
0200: }
0201:
0202: /**
0203: * Returns a Hashtable which contains a list of EJB class files processed by
0204: * the ejbc utility (both "source" class files as well as stubs and
0205: * skeletons). The key for the Hashtable is a String representing the path
0206: * to the class file (relative to the destination directory). The value for
0207: * the Hashtable is a File object which reference the actual class file.
0208: *
0209: * @return The list of EJB files processed by the ejbc utility.
0210: */
0211: public Hashtable getEjbFiles() {
0212: return ejbFiles;
0213: }
0214:
0215: /**
0216: * Returns the display-name element read from the standard EJB descriptor.
0217: *
0218: * @return The EJB-JAR display name.
0219: */
0220: public String getDisplayName() {
0221: return displayName;
0222: }
0223:
0224: /**
0225: * Returns the list of CMP descriptors referenced in the EJB descriptors.
0226: *
0227: * @return An array of CMP descriptors.
0228: */
0229: public String[] getCmpDescriptors() {
0230: List returnList = new ArrayList();
0231:
0232: EjbInfo[] ejbs = handler.getEjbs();
0233:
0234: for (int i = 0; i < ejbs.length; i++) {
0235: List descriptors = (List) ejbs[i].getCmpDescriptors();
0236: returnList.addAll(descriptors);
0237: }
0238:
0239: return (String[]) returnList.toArray(new String[returnList
0240: .size()]);
0241: }
0242:
0243: /**
0244: * Main application method for the iPlanet Application Server ejbc utility.
0245: * If the application is run with no commandline arguments, a usage
0246: * statement is printed for the user.
0247: *
0248: * @param args The commandline arguments passed to the application.
0249: */
0250: public static void main(String[] args) {
0251: File stdDescriptor;
0252: File iasDescriptor;
0253: File destDirectory = null;
0254: String classpath = null;
0255: SAXParser parser = null;
0256: boolean debug = false;
0257: boolean retainSource = false;
0258: IPlanetEjbc ejbc;
0259:
0260: if ((args.length < MIN_NUM_ARGS)
0261: || (args.length > MAX_NUM_ARGS)) {
0262: usage();
0263: return;
0264: }
0265:
0266: stdDescriptor = new File(args[args.length - 2]);
0267: iasDescriptor = new File(args[args.length - 1]);
0268:
0269: for (int i = 0; i < args.length - 2; i++) {
0270: if (args[i].equals("-classpath")) {
0271: classpath = args[++i];
0272: } else if (args[i].equals("-d")) {
0273: destDirectory = new File(args[++i]);
0274: } else if (args[i].equals("-debug")) {
0275: debug = true;
0276: } else if (args[i].equals("-keepsource")) {
0277: retainSource = true;
0278: } else {
0279: usage();
0280: return;
0281: }
0282: }
0283:
0284: /* If the -classpath flag isn't specified, use the system classpath */
0285: if (classpath == null) {
0286: Properties props = System.getProperties();
0287: classpath = props.getProperty("java.class.path");
0288: }
0289:
0290: /*
0291: * If the -d flag isn't specified, use the working directory as the
0292: * destination directory
0293: */
0294: if (destDirectory == null) {
0295: Properties props = System.getProperties();
0296: destDirectory = new File(props.getProperty("user.dir"));
0297: }
0298:
0299: /* Construct a SAXParser used to process the descriptors */
0300: SAXParserFactory parserFactory = SAXParserFactory.newInstance();
0301: parserFactory.setValidating(true);
0302: try {
0303: parser = parserFactory.newSAXParser();
0304: } catch (Exception e) {
0305: // SAXException or ParserConfigurationException may be thrown
0306: System.out
0307: .println("An exception was generated while trying to ");
0308: System.out.println("create a new SAXParser.");
0309: e.printStackTrace();
0310: return;
0311: }
0312:
0313: /* Build and populate an instance of the ejbc utility */
0314: ejbc = new IPlanetEjbc(stdDescriptor, iasDescriptor,
0315: destDirectory, classpath, parser);
0316: ejbc.setDebugOutput(debug);
0317: ejbc.setRetainSource(retainSource);
0318:
0319: /* Execute the ejbc utility -- stubs/skeletons are rebuilt, if needed */
0320: try {
0321: ejbc.execute();
0322: } catch (IOException e) {
0323: System.out
0324: .println("An IOException has occurred while reading the "
0325: + "XML descriptors ("
0326: + e.getMessage()
0327: + ").");
0328: return;
0329: } catch (SAXException e) {
0330: System.out
0331: .println("A SAXException has occurred while reading the "
0332: + "XML descriptors ("
0333: + e.getMessage()
0334: + ").");
0335: return;
0336: } catch (IPlanetEjbc.EjbcException e) {
0337: System.out
0338: .println("An error has occurred while executing the ejbc "
0339: + "utility (" + e.getMessage() + ").");
0340: return;
0341: }
0342: }
0343:
0344: /**
0345: * Print a usage statement.
0346: */
0347: private static void usage() {
0348: System.out
0349: .println("java org.apache.tools.ant.taskdefs.optional.ejb.IPlanetEjbc \\");
0350: System.out
0351: .println(" [OPTIONS] [EJB 1.1 descriptor] [iAS EJB descriptor]");
0352: System.out.println("");
0353: System.out.println("Where OPTIONS are:");
0354: System.out
0355: .println(" -debug -- for additional debugging output");
0356: System.out
0357: .println(" -keepsource -- to retain Java source files generated");
0358: System.out
0359: .println(" -classpath [classpath] -- classpath used for compilation");
0360: System.out
0361: .println(" -d [destination directory] -- directory for compiled classes");
0362: System.out.println("");
0363: System.out
0364: .println("If a classpath is not specified, the system classpath");
0365: System.out
0366: .println("will be used. If a destination directory is not specified,");
0367: System.out
0368: .println("the current working directory will be used (classes will");
0369: System.out
0370: .println("still be placed in subfolders which correspond to their");
0371: System.out.println("package name).");
0372: System.out.println("");
0373: System.out
0374: .println("The EJB home interface, remote interface, and implementation");
0375: System.out
0376: .println("class must be found in the destination directory. In");
0377: System.out
0378: .println("addition, the destination will look for the stubs and skeletons");
0379: System.out
0380: .println("in the destination directory to ensure they are up to date.");
0381: }
0382:
0383: /**
0384: * Compiles the stub and skeletons for the specified EJBs, if they need to
0385: * be updated.
0386: *
0387: * @throws EjbcException If the ejbc utility cannot be correctly configured
0388: * or if one or more of the EJB "source" classes
0389: * cannot be found in the destination directory
0390: * @throws IOException If the parser encounters a problem reading the XML
0391: * file
0392: * @throws SAXException If the parser encounters a problem processing the
0393: * XML descriptor (it may wrap another exception)
0394: */
0395: public void execute() throws EjbcException, IOException,
0396: SAXException {
0397:
0398: checkConfiguration(); // Throws EjbcException if unsuccessful
0399:
0400: EjbInfo[] ejbs = getEjbs(); // Returns list of EJBs for processing
0401:
0402: for (int i = 0; i < ejbs.length; i++) {
0403: log("EJBInfo...");
0404: log(ejbs[i].toString());
0405: }
0406:
0407: for (int i = 0; i < ejbs.length; i++) {
0408: EjbInfo ejb = ejbs[i];
0409:
0410: ejb.checkConfiguration(destDirectory); // Throws EjbcException
0411:
0412: if (ejb.mustBeRecompiled(destDirectory)) {
0413: log(ejb.getName() + " must be recompiled using ejbc.");
0414:
0415: String[] arguments = buildArgumentList(ejb);
0416: callEjbc(arguments);
0417:
0418: } else {
0419: log(ejb.getName() + " is up to date.");
0420: }
0421: }
0422: }
0423:
0424: /**
0425: * Executes the iPlanet Application Server ejbc command-line utility.
0426: *
0427: * @param arguments Command line arguments to be passed to the ejbc utility.
0428: */
0429: private void callEjbc(String[] arguments) {
0430:
0431: /* Concatenate all of the command line arguments into a single String */
0432: StringBuffer args = new StringBuffer();
0433: for (int i = 0; i < arguments.length; i++) {
0434: args.append(arguments[i]).append(" ");
0435: }
0436:
0437: /* If an iAS home directory is specified, prepend it to the commmand */
0438: String command;
0439: if (iasHomeDir == null) {
0440: command = "";
0441: } else {
0442: command = iasHomeDir.toString() + File.separator + "bin"
0443: + File.separator;
0444: }
0445: command += "ejbc ";
0446:
0447: log(command + args);
0448:
0449: /*
0450: * Use the Runtime object to execute an external command. Use the
0451: * RedirectOutput inner class to direct the standard and error output
0452: * from the command to the JRE's standard output
0453: */
0454: try {
0455: Process p = Runtime.getRuntime().exec(command + args);
0456: RedirectOutput output = new RedirectOutput(p
0457: .getInputStream());
0458: RedirectOutput error = new RedirectOutput(p
0459: .getErrorStream());
0460: output.start();
0461: error.start();
0462: p.waitFor();
0463: p.destroy();
0464: } catch (IOException e) {
0465: log("An IOException has occurred while trying to execute ejbc.");
0466: e.printStackTrace();
0467: } catch (InterruptedException e) {
0468: // Do nothing
0469: }
0470: }
0471:
0472: /**
0473: * Verifies that the user selections are valid.
0474: *
0475: * @throws EjbcException If the user selections are invalid.
0476: */
0477: protected void checkConfiguration() throws EjbcException {
0478:
0479: String msg = "";
0480:
0481: if (stdDescriptor == null) {
0482: msg += "A standard XML descriptor file must be specified. ";
0483: }
0484: if (iasDescriptor == null) {
0485: msg += "An iAS-specific XML descriptor file must be specified. ";
0486: }
0487: if (classpath == null) {
0488: msg += "A classpath must be specified. ";
0489: }
0490: if (parser == null) {
0491: msg += "An XML parser must be specified. ";
0492: }
0493:
0494: if (destDirectory == null) {
0495: msg += "A destination directory must be specified. ";
0496: } else if (!destDirectory.exists()) {
0497: msg += "The destination directory specified does not exist. ";
0498: } else if (!destDirectory.isDirectory()) {
0499: msg += "The destination specified is not a directory. ";
0500: }
0501:
0502: if (msg.length() > 0) {
0503: throw new EjbcException(msg);
0504: }
0505: }
0506:
0507: /**
0508: * Parses the EJB descriptors and returns a list of EJBs which may need to
0509: * be compiled.
0510: *
0511: * @return An array of objects which describe the EJBs to be
0512: * processed.
0513: * @throws IOException If the parser encounters a problem reading the XML
0514: * files
0515: * @throws SAXException If the parser encounters a problem processing the
0516: * XML descriptor (it may wrap another exception)
0517: */
0518: private EjbInfo[] getEjbs() throws IOException, SAXException {
0519: EjbInfo[] ejbs = null;
0520:
0521: /*
0522: * The EJB information is gathered from the standard XML EJB descriptor
0523: * and the iAS-specific XML EJB descriptor using a SAX parser.
0524: */
0525:
0526: parser.parse(stdDescriptor, handler);
0527: parser.parse(iasDescriptor, handler);
0528: ejbs = handler.getEjbs();
0529:
0530: return ejbs;
0531: }
0532:
0533: /**
0534: * Based on this object's instance variables as well as the EJB to be
0535: * processed, the correct flags and parameters are set for the ejbc
0536: * command-line utility.
0537: * @param ejb The EJB for which stubs and skeletons will be compiled.
0538: * @return An array of Strings which are the command-line parameters for
0539: * for the ejbc utility.
0540: */
0541: private String[] buildArgumentList(EjbInfo ejb) {
0542:
0543: List arguments = new ArrayList();
0544:
0545: /* OPTIONAL COMMAND LINE PARAMETERS */
0546:
0547: if (debugOutput) {
0548: arguments.add("-debug");
0549: }
0550:
0551: /* No beantype flag is needed for an entity bean */
0552: if (ejb.getBeantype().equals(STATELESS_SESSION)) {
0553: arguments.add("-sl");
0554: } else if (ejb.getBeantype().equals(STATEFUL_SESSION)) {
0555: arguments.add("-sf");
0556: }
0557:
0558: if (ejb.getIiop()) {
0559: arguments.add("-iiop");
0560: }
0561:
0562: if (ejb.getCmp()) {
0563: arguments.add("-cmp");
0564: }
0565:
0566: if (retainSource) {
0567: arguments.add("-gs");
0568: }
0569:
0570: if (ejb.getHasession()) {
0571: arguments.add("-fo");
0572: }
0573:
0574: /* REQUIRED COMMAND LINE PARAMETERS */
0575:
0576: arguments.add("-classpath");
0577: arguments.add(classpath);
0578:
0579: arguments.add("-d");
0580: arguments.add(destDirectory.toString());
0581:
0582: arguments.add(ejb.getHome().getQualifiedClassName());
0583: arguments.add(ejb.getRemote().getQualifiedClassName());
0584: arguments.add(ejb.getImplementation().getQualifiedClassName());
0585:
0586: /* Convert the List into an Array and return it */
0587: return (String[]) arguments
0588: .toArray(new String[arguments.size()]);
0589: }
0590:
0591: /**
0592: * Convenience method used to print messages to the user if debugging
0593: * messages are enabled.
0594: *
0595: * @param msg The String to print to standard output.
0596: */
0597: private void log(String msg) {
0598: if (debugOutput) {
0599: System.out.println(msg);
0600: }
0601: }
0602:
0603: /* Inner classes follow */
0604:
0605: /**
0606: * This inner class is used to signal any problems during the execution of
0607: * the ejbc compiler.
0608: *
0609: */
0610: public class EjbcException extends Exception {
0611:
0612: /**
0613: * Constructs an exception with the given descriptive message.
0614: *
0615: * @param msg Description of the exception which has occurred.
0616: */
0617: public EjbcException(String msg) {
0618: super (msg);
0619: }
0620: } // End of EjbcException inner class
0621:
0622: /**
0623: * This inner class is an XML document handler that can be used to parse EJB
0624: * descriptors (both the standard EJB descriptor as well as the iAS-specific
0625: * descriptor that stores additional values for iAS). Once the descriptors
0626: * have been processed, the list of EJBs found can be obtained by calling
0627: * the <code>getEjbs()</code> method.
0628: *
0629: * @see IPlanetEjbc.EjbInfo
0630: */
0631: private class EjbcHandler extends HandlerBase {
0632: /** EJB 1.1 ID */
0633: private static final String PUBLICID_EJB11 = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN";
0634: /** IPlanet ID */
0635: private static final String PUBLICID_IPLANET_EJB_60 = "-//Sun Microsystems, Inc.//DTD iAS Enterprise JavaBeans 1.0//EN";
0636: /** EJB 1.1 location */
0637: private static final String DEFAULT_IAS60_EJB11_DTD_LOCATION = "ejb-jar_1_1.dtd";
0638: /** IAS60 location */
0639: private static final String DEFAULT_IAS60_DTD_LOCATION = "IASEjb_jar_1_0.dtd";
0640:
0641: /*
0642: * Two Maps are used to track local DTDs that will be used in case the
0643: * remote copies of these DTDs cannot be accessed. The key for the Map
0644: * is the DTDs public ID and the value is the local location for the DTD
0645: */
0646: private Map resourceDtds = new HashMap();
0647: private Map fileDtds = new HashMap();
0648:
0649: private Map ejbs = new HashMap(); // List of EJBs found in XML
0650: private EjbInfo currentEjb; // One item within the Map
0651: private boolean iasDescriptor = false; // Is doc iAS or EJB descriptor
0652:
0653: private String currentLoc = ""; // Tracks current element
0654: private String currentText; // Tracks current text data
0655: private String ejbType; // "session" or "entity"
0656:
0657: /**
0658: * Constructs a new instance of the handler and registers local copies
0659: * of the standard EJB 1.1 descriptor DTD as well as iAS's EJB
0660: * descriptor DTD.
0661: */
0662: public EjbcHandler() {
0663: registerDTD(PUBLICID_EJB11,
0664: DEFAULT_IAS60_EJB11_DTD_LOCATION);
0665: registerDTD(PUBLICID_IPLANET_EJB_60,
0666: DEFAULT_IAS60_DTD_LOCATION);
0667: }
0668:
0669: /**
0670: * Returns the list of EJB objects found during the processing of the
0671: * standard EJB 1.1 descriptor and iAS-specific EJB descriptor.
0672: *
0673: * @return An array of EJBs which were found during the descriptor
0674: * parsing.
0675: */
0676: public EjbInfo[] getEjbs() {
0677: return (EjbInfo[]) ejbs.values().toArray(
0678: new EjbInfo[ejbs.size()]);
0679: }
0680:
0681: /**
0682: * Returns the value of the display-name element found in the standard
0683: * EJB 1.1 descriptor.
0684: *
0685: * @return String display-name value.
0686: */
0687: public String getDisplayName() {
0688: return displayName;
0689: }
0690:
0691: /**
0692: * Registers a local DTD that will be used when parsing an EJB
0693: * descriptor. When the DTD's public identifier is found in an XML
0694: * document, the parser will reference the local DTD rather than the
0695: * remote DTD. This enables XML documents to be processed even when the
0696: * public DTD isn't available.
0697: *
0698: * @param publicID The DTD's public identifier.
0699: * @param location The location of the local DTD copy -- the location
0700: * may either be a resource found on the classpath or a
0701: * local file.
0702: */
0703: public void registerDTD(String publicID, String location) {
0704: log("Registering: " + location);
0705: if ((publicID == null) || (location == null)) {
0706: return;
0707: }
0708:
0709: if (ClassLoader.getSystemResource(location) != null) {
0710: log("Found resource: " + location);
0711: resourceDtds.put(publicID, location);
0712: } else {
0713: File dtdFile = new File(location);
0714: if (dtdFile.exists() && dtdFile.isFile()) {
0715: log("Found file: " + location);
0716: fileDtds.put(publicID, location);
0717: }
0718: }
0719: }
0720:
0721: /**
0722: * Resolves an external entity found during XML processing. If a public
0723: * ID is found that has been registered with the handler, an <code>
0724: * InputSource</code> will be returned which refers to the local copy.
0725: * If the public ID hasn't been registered or if an error occurs, the
0726: * superclass implementation is used.
0727: *
0728: * @param publicId The DTD's public identifier.
0729: * @param systemId The location of the DTD, as found in the XML document.
0730: */
0731: public InputSource resolveEntity(String publicId,
0732: String systemId) throws SAXException {
0733: InputStream inputStream = null;
0734:
0735: try {
0736:
0737: /* Search the resource Map and (if not found) file Map */
0738:
0739: String location = (String) resourceDtds.get(publicId);
0740: if (location != null) {
0741: inputStream = ClassLoader.getSystemResource(
0742: location).openStream();
0743: } else {
0744: location = (String) fileDtds.get(publicId);
0745: if (location != null) {
0746: inputStream = new FileInputStream(location);
0747: }
0748: }
0749: } catch (IOException e) {
0750: return super .resolveEntity(publicId, systemId);
0751: }
0752:
0753: if (inputStream == null) {
0754: return super .resolveEntity(publicId, systemId);
0755: } else {
0756: return new InputSource(inputStream);
0757: }
0758: }
0759:
0760: /**
0761: * Receive notification that the start of an XML element has been found.
0762: *
0763: * @param name String name of the element found.
0764: * @param atts AttributeList of the attributes included with the element
0765: * (if any).
0766: * @throws SAXException If the parser cannot process the document.
0767: */
0768: public void startElement(String name, AttributeList atts)
0769: throws SAXException {
0770:
0771: /*
0772: * I need to "push" the element onto the String (currentLoc) which
0773: * always represents the current location in the XML document.
0774: */
0775: currentLoc += "\\" + name;
0776:
0777: /* A new element has started, so reset the text being captured */
0778: currentText = "";
0779:
0780: if (currentLoc.equals("\\ejb-jar")) {
0781: iasDescriptor = false;
0782: } else if (currentLoc.equals("\\ias-ejb-jar")) {
0783: iasDescriptor = true;
0784: }
0785:
0786: if ((name.equals("session")) || (name.equals("entity"))) {
0787: ejbType = name;
0788: }
0789: }
0790:
0791: /**
0792: * Receive notification that character data has been found in the XML
0793: * document
0794: *
0795: * @param ch Array of characters which have been found in the document.
0796: * @param start Starting index of the data found in the document.
0797: * @param len The number of characters found in the document.
0798: * @throws SAXException If the parser cannot process the document.
0799: */
0800: public void characters(char[] ch, int start, int len)
0801: throws SAXException {
0802:
0803: currentText += new String(ch).substring(start, start + len);
0804: }
0805:
0806: /**
0807: * Receive notification that the end of an XML element has been found.
0808: *
0809: * @param name String name of the element.
0810: * @throws SAXException If the parser cannot process the document.
0811: */
0812: public void endElement(String name) throws SAXException {
0813:
0814: /*
0815: * If this is a standard EJB 1.1 descriptor, we are looking for one
0816: * set of data, while if this is an iAS-specific descriptor, we're
0817: * looking for different set of data. Hand the processing off to
0818: * the appropriate method.
0819: */
0820: if (iasDescriptor) {
0821: iasCharacters(currentText);
0822: } else {
0823: stdCharacters(currentText);
0824: }
0825:
0826: /*
0827: * I need to "pop" the element off the String (currentLoc) which
0828: * always represents my current location in the XML document.
0829: */
0830:
0831: int nameLength = name.length() + 1; // Add one for the "\"
0832: int locLength = currentLoc.length();
0833:
0834: currentLoc = currentLoc
0835: .substring(0, locLength - nameLength);
0836: }
0837:
0838: /**
0839: * Receive notification that character data has been found in a standard
0840: * EJB 1.1 descriptor. We're interested in retrieving the home
0841: * interface, remote interface, implementation class, the type of bean,
0842: * and if the bean uses CMP.
0843: *
0844: * @param value String data found in the XML document.
0845: */
0846: private void stdCharacters(String value) {
0847:
0848: if (currentLoc.equals("\\ejb-jar\\display-name")) {
0849: displayName = value;
0850: return;
0851: }
0852:
0853: String base = "\\ejb-jar\\enterprise-beans\\" + ejbType;
0854:
0855: if (currentLoc.equals(base + "\\ejb-name")) {
0856: currentEjb = (EjbInfo) ejbs.get(value);
0857: if (currentEjb == null) {
0858: currentEjb = new EjbInfo(value);
0859: ejbs.put(value, currentEjb);
0860: }
0861: } else if (currentLoc.equals(base + "\\home")) {
0862: currentEjb.setHome(value);
0863: } else if (currentLoc.equals(base + "\\remote")) {
0864: currentEjb.setRemote(value);
0865: } else if (currentLoc.equals(base + "\\ejb-class")) {
0866: currentEjb.setImplementation(value);
0867: } else if (currentLoc.equals(base + "\\prim-key-class")) {
0868: currentEjb.setPrimaryKey(value);
0869: } else if (currentLoc.equals(base + "\\session-type")) {
0870: currentEjb.setBeantype(value);
0871: } else if (currentLoc.equals(base + "\\persistence-type")) {
0872: currentEjb.setCmp(value);
0873: }
0874: }
0875:
0876: /**
0877: * Receive notification that character data has been found in an
0878: * iAS-specific descriptor. We're interested in retrieving data
0879: * indicating whether the bean must support RMI/IIOP access, whether
0880: * the bean must provide highly available stubs and skeletons (in the
0881: * case of stateful session beans), and if this bean uses additional
0882: * CMP XML descriptors (in the case of entity beans with CMP).
0883: *
0884: * @param value String data found in the XML document.
0885: */
0886: private void iasCharacters(String value) {
0887: String base = "\\ias-ejb-jar\\enterprise-beans\\" + ejbType;
0888:
0889: if (currentLoc.equals(base + "\\ejb-name")) {
0890: currentEjb = (EjbInfo) ejbs.get(value);
0891: if (currentEjb == null) {
0892: currentEjb = new EjbInfo(value);
0893: ejbs.put(value, currentEjb);
0894: }
0895: } else if (currentLoc.equals(base + "\\iiop")) {
0896: currentEjb.setIiop(value);
0897: } else if (currentLoc.equals(base + "\\failover-required")) {
0898: currentEjb.setHasession(value);
0899: } else if (currentLoc.equals(base + "\\persistence-manager"
0900: + "\\properties-file-location")) {
0901: currentEjb.addCmpDescriptor(value);
0902: }
0903: }
0904: } // End of EjbcHandler inner class
0905:
0906: /**
0907: * This inner class represents an EJB that will be compiled using ejbc.
0908: *
0909: */
0910: private class EjbInfo {
0911: private String name; // EJB's display name
0912: private Classname home; // EJB's home interface name
0913: private Classname remote; // EJB's remote interface name
0914: private Classname implementation; // EJB's implementation class
0915: private Classname primaryKey; // EJB's primary key class
0916: private String beantype = "entity"; // or "stateful" or "stateless"
0917: private boolean cmp = false; // Does this EJB support CMP?
0918: private boolean iiop = false; // Does this EJB support IIOP?
0919: private boolean hasession = false; // Does this EJB require failover?
0920: private List cmpDescriptors = new ArrayList(); // CMP descriptor list
0921:
0922: /**
0923: * Construct a new EJBInfo object with the given name.
0924: *
0925: * @param name The display name for the EJB.
0926: */
0927: public EjbInfo(String name) {
0928: this .name = name;
0929: }
0930:
0931: /**
0932: * Returns the display name of the EJB. If a display name has not been
0933: * set, it returns the EJB implementation classname (if the
0934: * implementation class is not set, it returns "[unnamed]").
0935: *
0936: * @return The display name for the EJB.
0937: */
0938: public String getName() {
0939: if (name == null) {
0940: if (implementation == null) {
0941: return "[unnamed]";
0942: } else {
0943: return implementation.getClassName();
0944: }
0945: }
0946: return name;
0947: }
0948:
0949: /*
0950: * Below are getter's and setter's for each of the instance variables.
0951: * Note that (in addition to supporting setters with the same type as
0952: * the instance variable) a setter is provided with takes a String
0953: * argument -- this are provided so the XML document handler can set
0954: * the EJB values using the Strings it parses.
0955: */
0956:
0957: public void setHome(String home) {
0958: setHome(new Classname(home));
0959: }
0960:
0961: public void setHome(Classname home) {
0962: this .home = home;
0963: }
0964:
0965: public Classname getHome() {
0966: return home;
0967: }
0968:
0969: public void setRemote(String remote) {
0970: setRemote(new Classname(remote));
0971: }
0972:
0973: public void setRemote(Classname remote) {
0974: this .remote = remote;
0975: }
0976:
0977: public Classname getRemote() {
0978: return remote;
0979: }
0980:
0981: public void setImplementation(String implementation) {
0982: setImplementation(new Classname(implementation));
0983: }
0984:
0985: public void setImplementation(Classname implementation) {
0986: this .implementation = implementation;
0987: }
0988:
0989: public Classname getImplementation() {
0990: return implementation;
0991: }
0992:
0993: public void setPrimaryKey(String primaryKey) {
0994: setPrimaryKey(new Classname(primaryKey));
0995: }
0996:
0997: public void setPrimaryKey(Classname primaryKey) {
0998: this .primaryKey = primaryKey;
0999: }
1000:
1001: public Classname getPrimaryKey() {
1002: return primaryKey;
1003: }
1004:
1005: public void setBeantype(String beantype) {
1006: this .beantype = beantype.toLowerCase();
1007: }
1008:
1009: public String getBeantype() {
1010: return beantype;
1011: }
1012:
1013: public void setCmp(boolean cmp) {
1014: this .cmp = cmp;
1015: }
1016:
1017: public void setCmp(String cmp) {
1018: setCmp(cmp.equals("Container"));
1019: }
1020:
1021: public boolean getCmp() {
1022: return cmp;
1023: }
1024:
1025: public void setIiop(boolean iiop) {
1026: this .iiop = iiop;
1027: }
1028:
1029: public void setIiop(String iiop) {
1030: setIiop(iiop.equals("true"));
1031: }
1032:
1033: public boolean getIiop() {
1034: return iiop;
1035: }
1036:
1037: public void setHasession(boolean hasession) {
1038: this .hasession = hasession;
1039: }
1040:
1041: public void setHasession(String hasession) {
1042: setHasession(hasession.equals("true"));
1043: }
1044:
1045: public boolean getHasession() {
1046: return hasession;
1047: }
1048:
1049: public void addCmpDescriptor(String descriptor) {
1050: cmpDescriptors.add(descriptor);
1051: }
1052:
1053: public List getCmpDescriptors() {
1054: return cmpDescriptors;
1055: }
1056:
1057: /**
1058: * Verifies that the EJB is valid--if it is invalid, an exception is
1059: * thrown
1060: *
1061: *
1062: * @param buildDir The directory where the EJB remote interface, home
1063: * interface, and implementation class must be found.
1064: * @throws EjbcException If the EJB is invalid.
1065: */
1066: private void checkConfiguration(File buildDir)
1067: throws EjbcException {
1068:
1069: /* Check that the specified instance variables are valid */
1070: if (home == null) {
1071: throw new EjbcException(
1072: "A home interface was not found " + "for the "
1073: + name + " EJB.");
1074: }
1075: if (remote == null) {
1076: throw new EjbcException(
1077: "A remote interface was not found "
1078: + "for the " + name + " EJB.");
1079: }
1080: if (implementation == null) {
1081: throw new EjbcException(
1082: "An EJB implementation class was not "
1083: + "found for the " + name + " EJB.");
1084: }
1085:
1086: if ((!beantype.equals(ENTITY_BEAN))
1087: && (!beantype.equals(STATELESS_SESSION))
1088: && (!beantype.equals(STATEFUL_SESSION))) {
1089: throw new EjbcException("The beantype found ("
1090: + beantype + ") " + "isn't valid in the "
1091: + name + " EJB.");
1092: }
1093:
1094: if (cmp && (!beantype.equals(ENTITY_BEAN))) {
1095: System.out
1096: .println("CMP stubs and skeletons may not be generated"
1097: + " for a Session Bean -- the \"cmp\" attribute will be"
1098: + " ignoredfor the " + name + " EJB.");
1099: }
1100:
1101: if (hasession && (!beantype.equals(STATEFUL_SESSION))) {
1102: System.out
1103: .println("Highly available stubs and skeletons may "
1104: + "only be generated for a Stateful Session Bean -- the "
1105: + "\"hasession\" attribute will be ignored for the "
1106: + name + " EJB.");
1107: }
1108:
1109: /* Check that the EJB "source" classes all exist */
1110: if (!remote.getClassFile(buildDir).exists()) {
1111: throw new EjbcException("The remote interface "
1112: + remote.getQualifiedClassName()
1113: + " could not be " + "found.");
1114: }
1115: if (!home.getClassFile(buildDir).exists()) {
1116: throw new EjbcException("The home interface "
1117: + home.getQualifiedClassName()
1118: + " could not be " + "found.");
1119: }
1120: if (!implementation.getClassFile(buildDir).exists()) {
1121: throw new EjbcException("The EJB implementation class "
1122: + implementation.getQualifiedClassName()
1123: + " could " + "not be found.");
1124: }
1125: }
1126:
1127: /**
1128: * Determines if the ejbc utility needs to be run or not. If the stubs
1129: * and skeletons can all be found in the destination directory AND all
1130: * of their timestamps are more recent than the EJB source classes
1131: * (home, remote, and implementation classes), the method returns
1132: * <code>false</code>. Otherwise, the method returns <code>true</code>.
1133: *
1134: * @param destDir The directory where the EJB source classes, stubs and
1135: * skeletons are located.
1136: * @return A boolean indicating whether or not the ejbc utility needs to
1137: * be run to bring the stubs and skeletons up to date.
1138: */
1139: public boolean mustBeRecompiled(File destDir) {
1140:
1141: long sourceModified = sourceClassesModified(destDir);
1142:
1143: long destModified = destClassesModified(destDir);
1144:
1145: return (destModified < sourceModified);
1146: }
1147:
1148: /**
1149: * Examines each of the EJB source classes (home, remote, and
1150: * implementation) and returns the modification timestamp for the
1151: * "oldest" class.
1152: *
1153: * @param classpath The classpath to be used to find the source EJB
1154: * classes. If <code>null</code>, the system classpath
1155: * is used.
1156: * @return The modification timestamp for the "oldest" EJB source class.
1157: * @throws BuildException If one of the EJB source classes cannot be
1158: * found on the classpath.
1159: */
1160: private long sourceClassesModified(File buildDir) {
1161: long latestModified; // The timestamp of the "newest" class
1162: long modified; // Timestamp for a given class
1163: File remoteFile; // File for the remote interface class
1164: File homeFile; // File for the home interface class
1165: File implFile; // File for the EJB implementation class
1166: File pkFile; // File for the EJB primary key class
1167:
1168: /* Check the timestamp on the remote interface */
1169: remoteFile = remote.getClassFile(buildDir);
1170: modified = remoteFile.lastModified();
1171: if (modified == -1) {
1172: System.out.println("The class "
1173: + remote.getQualifiedClassName() + " couldn't "
1174: + "be found on the classpath");
1175: return -1;
1176: }
1177: latestModified = modified;
1178:
1179: /* Check the timestamp on the home interface */
1180: homeFile = home.getClassFile(buildDir);
1181: modified = homeFile.lastModified();
1182: if (modified == -1) {
1183: System.out.println("The class "
1184: + home.getQualifiedClassName()
1185: + " couldn't be " + "found on the classpath");
1186: return -1;
1187: }
1188: latestModified = Math.max(latestModified, modified);
1189:
1190: /* Check the timestamp of the primary key class */
1191: if (primaryKey != null) {
1192: pkFile = primaryKey.getClassFile(buildDir);
1193: modified = pkFile.lastModified();
1194: if (modified == -1) {
1195: System.out
1196: .println("The class "
1197: + primaryKey
1198: .getQualifiedClassName()
1199: + "couldn't be "
1200: + "found on the classpath");
1201: return -1;
1202: }
1203: latestModified = Math.max(latestModified, modified);
1204: } else {
1205: pkFile = null;
1206: }
1207:
1208: /* Check the timestamp on the EJB implementation class.
1209: *
1210: * Note that if ONLY the implementation class has changed, it's not
1211: * necessary to rebuild the EJB stubs and skeletons. For this
1212: * reason, we ensure the file exists (using lastModified above), but
1213: * we DON'T compare it's timestamp with the timestamps of the home
1214: * and remote interfaces (because it's irrelevant in determining if
1215: * ejbc must be run)
1216: */
1217: implFile = implementation.getClassFile(buildDir);
1218: modified = implFile.lastModified();
1219: if (modified == -1) {
1220: System.out.println("The class "
1221: + implementation.getQualifiedClassName()
1222: + " couldn't be found on the classpath");
1223: return -1;
1224: }
1225:
1226: String pathToFile = remote.getQualifiedClassName();
1227: pathToFile = pathToFile.replace('.', File.separatorChar)
1228: + ".class";
1229: ejbFiles.put(pathToFile, remoteFile);
1230:
1231: pathToFile = home.getQualifiedClassName();
1232: pathToFile = pathToFile.replace('.', File.separatorChar)
1233: + ".class";
1234: ejbFiles.put(pathToFile, homeFile);
1235:
1236: pathToFile = implementation.getQualifiedClassName();
1237: pathToFile = pathToFile.replace('.', File.separatorChar)
1238: + ".class";
1239: ejbFiles.put(pathToFile, implFile);
1240:
1241: if (pkFile != null) {
1242: pathToFile = primaryKey.getQualifiedClassName();
1243: pathToFile = pathToFile
1244: .replace('.', File.separatorChar)
1245: + ".class";
1246: ejbFiles.put(pathToFile, pkFile);
1247: }
1248:
1249: return latestModified;
1250: }
1251:
1252: /**
1253: * Examines each of the EJB stubs and skeletons in the destination
1254: * directory and returns the modification timestamp for the "oldest"
1255: * class. If one of the stubs or skeletons cannot be found, <code>-1
1256: * </code> is returned.
1257: *
1258: * @param dest The directory in which the EJB stubs and skeletons are
1259: * stored.
1260: * @return The modification timestamp for the "oldest" EJB stub or
1261: * skeleton. If one of the classes cannot be found, <code>-1
1262: * </code> is returned.
1263: * @throws BuildException If the canonical path of the destination
1264: * directory cannot be found.
1265: */
1266: private long destClassesModified(File destDir) {
1267: String[] classnames = classesToGenerate(); // List of all stubs & skels
1268: long destClassesModified = new Date().getTime(); // Earliest mod time
1269: boolean allClassesFound = true; // Has each been found?
1270:
1271: /*
1272: * Loop through each stub/skeleton class that must be generated, and
1273: * determine (if all exist) which file has the most recent timestamp
1274: */
1275: for (int i = 0; i < classnames.length; i++) {
1276:
1277: String pathToClass = classnames[i].replace('.',
1278: File.separatorChar)
1279: + ".class";
1280: File classFile = new File(destDir, pathToClass);
1281:
1282: /*
1283: * Add each stub/skeleton class to the list of EJB files. Note
1284: * that each class is added even if it doesn't exist now.
1285: */
1286: ejbFiles.put(pathToClass, classFile);
1287:
1288: allClassesFound = allClassesFound && classFile.exists();
1289:
1290: if (allClassesFound) {
1291: long fileMod = classFile.lastModified();
1292:
1293: /* Keep track of the oldest modification timestamp */
1294: destClassesModified = Math.min(destClassesModified,
1295: fileMod);
1296: }
1297: }
1298:
1299: 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: private String[] classesToGenerate() {
1313: String[] classnames = (iiop) ? new String[NUM_CLASSES_WITH_IIOP]
1314: : new String[NUM_CLASSES_WITHOUT_IIOP];
1315:
1316: final String remotePkg = remote.getPackageName() + ".";
1317: final String remoteClass = remote.getClassName();
1318: final String homePkg = home.getPackageName() + ".";
1319: final String homeClass = home.getClassName();
1320: final String implPkg = implementation.getPackageName()
1321: + ".";
1322: final String implFullClass = implementation
1323: .getQualifiedWithUnderscores();
1324: int index = 0;
1325:
1326: classnames[index++] = implPkg + "ejb_fac_" + implFullClass;
1327: classnames[index++] = implPkg + "ejb_home_" + implFullClass;
1328: classnames[index++] = implPkg + "ejb_skel_" + implFullClass;
1329: classnames[index++] = remotePkg + "ejb_kcp_skel_"
1330: + remoteClass;
1331: classnames[index++] = homePkg + "ejb_kcp_skel_" + homeClass;
1332: classnames[index++] = remotePkg + "ejb_kcp_stub_"
1333: + remoteClass;
1334: classnames[index++] = homePkg + "ejb_kcp_stub_" + homeClass;
1335: classnames[index++] = remotePkg + "ejb_stub_" + remoteClass;
1336: classnames[index++] = homePkg + "ejb_stub_" + homeClass;
1337:
1338: if (!iiop) {
1339: return classnames;
1340: }
1341:
1342: classnames[index++] = "org.omg.stub." + remotePkg + "_"
1343: + remoteClass + "_Stub";
1344: classnames[index++] = "org.omg.stub." + homePkg + "_"
1345: + homeClass + "_Stub";
1346: classnames[index++] = "org.omg.stub." + remotePkg
1347: + "_ejb_RmiCorbaBridge_" + remoteClass + "_Tie";
1348: classnames[index++] = "org.omg.stub." + homePkg
1349: + "_ejb_RmiCorbaBridge_" + homeClass + "_Tie";
1350:
1351: classnames[index++] = remotePkg + "ejb_RmiCorbaBridge_"
1352: + remoteClass;
1353: classnames[index++] = homePkg + "ejb_RmiCorbaBridge_"
1354: + homeClass;
1355:
1356: return classnames;
1357: }
1358:
1359: /**
1360: * Convenience method which creates a String representation of all the
1361: * instance variables of an EjbInfo object.
1362: *
1363: * @return A String representing the EjbInfo instance.
1364: */
1365: public String toString() {
1366: String s = "EJB name: " + name
1367: + "\n\r home: " + home
1368: + "\n\r remote: " + remote
1369: + "\n\r impl: " + implementation
1370: + "\n\r primaryKey: " + primaryKey
1371: + "\n\r beantype: " + beantype
1372: + "\n\r cmp: " + cmp
1373: + "\n\r iiop: " + iiop
1374: + "\n\r hasession: " + hasession;
1375:
1376: Iterator i = cmpDescriptors.iterator();
1377: while (i.hasNext()) {
1378: s += "\n\r CMP Descriptor: " + i.next();
1379: }
1380:
1381: return s;
1382: }
1383:
1384: } // End of EjbInfo inner class
1385:
1386: /**
1387: * Convenience class used to represent the fully qualified name of a Java
1388: * class. It provides an easy way to retrieve components of the class name
1389: * in a format that is convenient for building iAS stubs and skeletons.
1390: *
1391: */
1392: private static class Classname {
1393: private String qualifiedName; // Fully qualified name of the Java class
1394: private String packageName; // Name of the package for this class
1395: private String className; // Name of the class without the package
1396:
1397: /**
1398: * This constructor builds an object which represents the name of a Java
1399: * class.
1400: *
1401: * @param qualifiedName String representing the fully qualified class
1402: * name of the Java class.
1403: */
1404: public Classname(String qualifiedName) {
1405: if (qualifiedName == null) {
1406: return;
1407: }
1408:
1409: this .qualifiedName = qualifiedName;
1410:
1411: int index = qualifiedName.lastIndexOf('.');
1412: if (index == -1) {
1413: className = qualifiedName;
1414: packageName = "";
1415: } else {
1416: packageName = qualifiedName.substring(0, index);
1417: className = qualifiedName.substring(index + 1);
1418: }
1419: }
1420:
1421: /**
1422: * Gets the fully qualified name of the Java class.
1423: *
1424: * @return String representing the fully qualified class name.
1425: */
1426: public String getQualifiedClassName() {
1427: return qualifiedName;
1428: }
1429:
1430: /**
1431: * Gets the package name for the Java class.
1432: *
1433: * @return String representing the package name for the class.
1434: */
1435: public String getPackageName() {
1436: return packageName;
1437: }
1438:
1439: /**
1440: * Gets the Java class name without the package structure.
1441: *
1442: * @return String representing the name for the class.
1443: */
1444: public String getClassName() {
1445: return className;
1446: }
1447:
1448: /**
1449: * Gets the fully qualified name of the Java class with underscores
1450: * separating the components of the class name rather than periods.
1451: * This format is used in naming some of the stub and skeleton classes
1452: * for the iPlanet Application Server.
1453: *
1454: * @return String representing the fully qualified class name using
1455: * underscores instead of periods.
1456: */
1457: public String getQualifiedWithUnderscores() {
1458: return qualifiedName.replace('.', '_');
1459: }
1460:
1461: /**
1462: * Returns a File which references the class relative to the specified
1463: * directory. Note that the class file may or may not exist.
1464: *
1465: * @param directory A File referencing the base directory containing
1466: * class files.
1467: * @return File referencing this class.
1468: */
1469: public File getClassFile(File directory) {
1470: String pathToFile = qualifiedName.replace('.',
1471: File.separatorChar)
1472: + ".class";
1473: return new File(directory, pathToFile);
1474: }
1475:
1476: /**
1477: * String representation of this class name. It returns the fully
1478: * qualified class name.
1479: *
1480: * @return String representing the fully qualified class name.
1481: */
1482: public String toString() {
1483: return getQualifiedClassName();
1484: }
1485: } // End of Classname inner class
1486:
1487: /**
1488: * Thread class used to redirect output from an <code>InputStream</code> to
1489: * the JRE standard output. This class may be used to redirect output from
1490: * an external process to the standard output.
1491: *
1492: */
1493: private static class RedirectOutput extends Thread {
1494:
1495: private InputStream stream; // Stream to read and redirect to standard output
1496:
1497: /**
1498: * Constructs a new instance that will redirect output from the
1499: * specified stream to the standard output.
1500: *
1501: * @param stream InputStream which will be read and redirected to the
1502: * standard output.
1503: */
1504: public RedirectOutput(InputStream stream) {
1505: this .stream = stream;
1506: }
1507:
1508: /**
1509: * Reads text from the input stream and redirects it to standard output
1510: * using a separate thread.
1511: */
1512: public void run() {
1513: BufferedReader reader = new BufferedReader(
1514: new InputStreamReader(stream));
1515: String text;
1516: try {
1517: while ((text = reader.readLine()) != null) {
1518: System.out.println(text);
1519: }
1520: } catch (IOException e) {
1521: e.printStackTrace();
1522: } finally {
1523: try {
1524: reader.close();
1525: } catch (IOException e) {
1526: // Do nothing
1527: }
1528: }
1529: }
1530: } // End of RedirectOutput inner class
1531:
1532: }
|