/*
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999 Bull S.A.
 * Contact: jonas-team@objectweb.org
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * Initial developer(s): Helene Joanin
 * Contributor(s): 
 *   01/05/30 Christophe Ney - Lutris Technologies
 *          Fix to handle arguments containing white spaces.
 *   01/11/23 Guillaume Riviere
 *          Fix the addClassesInJar() method
 *          (on David, the Stub/Skel classes names are different).
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */


package org.objectweb.jonas_ejb.genic;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URLClassLoader;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;

import org.objectweb.common.Env;
import org.objectweb.common.Cmd;
import org.objectweb.jonas_ejb.deployment.api.BeanDesc;
import org.objectweb.jonas_ejb.deployment.api.DeploymentDesc;
import org.objectweb.jonas_ejb.deployment.api.DeploymentDescException;
import org.objectweb.jonas_ejb.deployment.api.MessageDrivenDesc;
import org.objectweb.jonas_ejb.lib.BeanNaming;
import org.objectweb.jonas_ejb.lib.Version;


/**
 *  This class allows to generate:
 * <ul>
 * <li>
 *  the classes that implements the Enterprise bean's remote interface,
 * <li>
 *  the classes that implements the Enterprise bean's home interface,
 * <li>
 *  the classes that implements the Handles of the Entity beans,
 * <li>
 *  the classes that implements the persistence of the Entity beans with CMP,
 * </ul>
 * of all the Enterprise Java Beans defined in the given Deployment Descriptor.
 */

public class GenIC {

    private static final int EXIT_SUCCESS = 0;
    private static final int EXIT_FAILURE = 2;

    // Is the command is verbose ?
    private boolean verbose = false;
    
    // Generate with or without the  propagation of the security  context
    private boolean withSecPropag;

    // Name of the directory where to place the generated file
    private String outputdir = null;

    // A BeanSources for each bean
    private Vector beanList = null;

    // Path names of the generated files to delete
    private Vector filesToDelete = null;

    // Path names of the java generated sources (remote/not remote)
    private Vector remoteJavas = null;
    private Vector noRemoteJavas = null;

    // Names of the remote classes (package name included)
    private Vector remoteClasses = null;


    /**
     * GenIC allows to generate the container classes for JOnAS for the given Enterprise Java Beans.
     * <p>
     *
     * Usage: java org.objectweb.jonas_ejb.genic.GenIC -help
     * <br>
     *         to print this help message
     * <p>
     *
     * or    java org.objectweb.jonas_ejb.genic.GenIC <Options> <Input_File>
     * <br>
     *         to generate the container classes for the given EJBs.
     * <p>
     *           Options include:
     * <ul>
     * <li>
     *             -d <output_dir>  specify where to place the generated files
     * <li>
     *             -keepgenerated   do not delete intermediate generated source files
     * <li>
     *             -nocompil        do not compile the generated source files via
     *                              the java and rmi compilers    
     * <li>
     *             -noaddinjar      do not add the generated classes in the given
     *                              ejb-jar file
     * <li>
     *             -novalidation    parse the XML deployment descriptors without validation
     * <li>
     *             -javac     <opt> specify the name of the java compiler to use
     * <li>
     *             -javacopts <opt> specify the options to pass to the java compiler
     * <li>
     *             -rmicopts <opt>  specify the options to pass to the rmi compiler
     * <li>
     *             -verbose
     * <li>
     *             -secpropag       modify the RMI Skel. and Stub. to implement
     *                              the implicit propagation of the transactionnal
     *                              context and security context
     * </ul>
     * <p>
     *           Input_File         file name of the standard deployment descriptor (.xml ended),
     *                              or file name of the EJB-jar (.jar ended).
     */

    public static void main(String args[]) {

	boolean  error = false;

	boolean  isHelp          = false;
	boolean  isVerbose       = false;
	boolean  isKeepGenerated = false;
	boolean  isSecPropag     = false;
	boolean  isCompil        = true;
	boolean  isAddInJar      = true;
	boolean  parseWithValidation = true;

	String   fileInputName = null;
	String   dirOutputName = null;
	String   nameJavaC     = new String("javac");
	Vector   optionsJavaC  = new Vector();
	Vector   optionsRmiC   = new Vector();

	// Get args
	for (int argn=0; argn<args.length; argn++) {
	    String arg = args[argn];
	    if (arg.equals("-help") || arg.equals("-?")) {
		isHelp = true;
		continue;
	    }
	    if (arg.equals("-verbose")) {
		isVerbose = true;
		continue;
	    }
	    if (arg.equals("-keepgenerated")) {
		isKeepGenerated = true;
		optionsRmiC.addElement(args[argn]);
		continue;
	    }
	    if (arg.equals("-secpropag")) {
		isSecPropag = true; 
		continue;
	    }
	    if (arg.equals("-nocompil")) {
		isCompil = false;
		isKeepGenerated = true;
		continue;
	    }
	    if (arg.equals("-noaddinjar")) {
		isAddInJar = false;
		continue;
	    }
	    if (arg.equals("-novalidation")) {
		parseWithValidation = false;
		continue;
	    }
	    if (arg.equals("-javac")) {
		argn++;
		if (argn<args.length) {
		    nameJavaC = args[argn];
		    if (Env.isJeremie() || Env.isDavid()) {
			optionsRmiC.addElement("-c");
			optionsRmiC.addElement(args[argn]);
		    }
		} else {
		    error = true;
		}
		continue;
	    }
	    if (arg.equals("-javacopts")) {
		argn++;
		if (argn<args.length) {
		    StringTokenizer st = new StringTokenizer(args[argn]);
		    while (st.hasMoreTokens()) {
			optionsJavaC.addElement(st.nextToken());
		    }
		} else {
		    error = true;
		}
		continue;
	    }
	    if (arg.equals("-rmicopts")) {
		argn++;
		if (argn<args.length) {
		    StringTokenizer st = new StringTokenizer(args[argn]);
		    while (st.hasMoreTokens()) {
			optionsRmiC.addElement(st.nextToken());
		    }
		} else {
		    error = true;
		}
		continue;
	    }
	    if (arg.equals("-d")) {
		argn++;
		if (argn<args.length) {
		    dirOutputName = args[argn];
		} else {
		    error = true;
		}
		continue;
	    }
	    fileInputName = args[argn];
	}

	// Usage ?
	if (isHelp) {
	    usage();
	    System.exit(EXIT_SUCCESS);
	}

	// Check args
	if (error || (fileInputName == null)) {
	    usage();
	    System.exit(EXIT_FAILURE);
	}
	if (dirOutputName == null) {
	    dirOutputName = new String("");
	}

	// In case of ejb-jar file, the dirOutputName must be initialized
	// because the file names of the generated classes must contain the package's name.
	if (fileInputName.endsWith(".jar")
	    && isAddInJar
	    && "".equals(dirOutputName)) {
	    dirOutputName = new String(".");
	}

	// Init the classpath used for javac, rmic, JRMICompiler
	String classpath;
	if (fileInputName.endsWith(".jar")) {
	    classpath = fileInputName +
		System.getProperty("path.separator", "") +
		System.getProperty("java.class.path", "");
	} else {
	    classpath = System.getProperty("java.class.path", "");
	}

	// Set the parsing mode (with or without validation)
	if (!parseWithValidation) {
	    DeploymentDesc.setParsingWithValidation(false);
	}

	// Generates the classes for the set of the beans
	try {
	    DeploymentDesc ejbJarDD = null;
	    if (fileInputName.endsWith(".jar")) {
		// ejb-jar file
		URL url[] = new URL[1];
		url[0] = (new File(fileInputName)).toURL();
		URLClassLoader loaderForCls = new URLClassLoader(url);
		URLClassLoader loaderForDD = new URLClassLoader(url, null);
		// The loader used to get the DDs has no parent, to avoid to get wrong DDs
		// (ie META-INF/ejb-jar.xml resource accessible via the classpath)
		ejbJarDD = DeploymentDesc.getInstance(loaderForDD, loaderForCls);
	    } else {
		// xml file
		ejbJarDD = DeploymentDesc.getInstance(fileInputName,
						      BeanNaming.getJonasXmlName(fileInputName));
	    }

	    GenIC gwc = new GenIC(ejbJarDD, dirOutputName, isSecPropag, isVerbose);
	    gwc.genSources();
	    if (isCompil) {
		gwc.compilClasses(nameJavaC, optionsJavaC, optionsRmiC, classpath);
		if (fileInputName.endsWith(".jar") && isAddInJar) {
		    gwc.addClassesInJar(fileInputName);
		}
		if (!isKeepGenerated) {
		    gwc.clean();
		}
	    }

	} catch (MalformedURLException e) {
	    GenIC.fatalError("Invalid ejb-jar file name ("+ e.getMessage() + ")");
	} catch (GenICException e) {
	    GenIC.fatalError(e.getMessage());
	} catch (DeploymentDescException e) {
	    GenIC.fatalError("Cannot read the Deployment Descriptors from "
			     +fileInputName+": "+e.getMessage());
	}
	// End
    }

    /**
     * GenIC Constructor
     *
     * @param     ejbJarDescr    deployment descriptors of the beans
     * @param     dirOutputName  path of the directory where to place the generated files
     *                            (empty string "" if the output directory is the current directory)
     * @param     isSecPropag    generation with or without the security propagation
     *                             of the transactionnal context
     * @param     isVerbose      if true, some traces are print
     * @exception GenICException In error case
     */
    public GenIC(DeploymentDesc ejbJarDesc, String dirOutputName,
		 boolean isSecPropag, boolean isVerbose)
	throws GenICException {
	
	verbose = isVerbose;
	withSecPropag = isSecPropag;
	outputdir = dirOutputName;
	filesToDelete = new Vector();
	remoteJavas = new Vector();
	noRemoteJavas = new Vector();
	remoteClasses = new Vector();

	//
	beanList = new Vector();
	BeanDesc[] beansDD = ejbJarDesc.getBeanDesc();
	for (int i = 0; i < beansDD.length; i++) {
	    if (!(beansDD[i] instanceof MessageDrivenDesc)) {
		// Nothing to generate in case of MessageDriven Bean
		beanList.addElement(new BeanSources(beansDD[i], outputdir));
	    }
	}

	// Display the bean's names
	StringBuffer message = new StringBuffer();
	message.append("GenIC for JOnAS "+Version.NUMBER+": ");
	if (beanList.size() != 0) {
	    message.append("Beans ");
	    for (int i = 0; i < beanList.size(); i++) {
		BeanSources ics = (BeanSources)beanList.elementAt(i);
		message.append((i == 0 ? "" : ", ") + "'" + ics.getEjbName() + "'");
	    }
	    message.append(" generation ...");
	} else {
	    message.append("No generation to do (only message driven beans)");
	}
	GenIC.info(message.toString());

	// Init the lists of the remote/non-remote java sources and the remote classes
	for (Enumeration e = beanList.elements() ; e.hasMoreElements() ;) {
	    BeanSources ics = (BeanSources)e.nextElement();
	    if (ics.getWrpHomeFileName() != null) {
		remoteJavas.addElement(ics.getWrpHomeFileName());
		remoteClasses.addElement(ics.getWrpHomeClassName());
	    }
	    if (ics.getWrpLocalHomeFileName() != null) {
		noRemoteJavas.addElement(ics.getWrpLocalHomeFileName());
	    }
	    if (ics.getWrpRemoteFileName() != null) {
		remoteJavas.addElement(ics.getWrpRemoteFileName());
		remoteClasses.addElement(ics.getWrpRemoteClassName());
	    }
	    if (ics.getWrpLocalFileName() != null) {
		noRemoteJavas.addElement(ics.getWrpLocalFileName());
	    }
	    if (ics.getWrpHandleFileName() != null) {
		noRemoteJavas.addElement(ics.getWrpHandleFileName());
	    }
	    if (ics.getWrpBeanFileName() != null) {
		noRemoteJavas.addElement(ics.getWrpBeanFileName());        
	    }
	}
    }

    /**
     * 
     */
    public void genSources() throws GenICException {
	for (Enumeration e = beanList.elements() ; e.hasMoreElements() ;) {
	    BeanSources ics = (BeanSources)e.nextElement();
	    ics.generate();
	    trace("Sources classes successfully generated"+" for '"+ics.getEjbName()+"'");
	}
    }

    /**
     * Compiles the java sources generated by the genSources() method.
     *
     * I.e. :
     * <ul>
     * <li>
     *   compile the classes via javac,
     * <li>
     *   creates the RMI/JEREMIE stubs and skeletons via rmic/jrmic,
     * <li>
     *   in case of RMI, modify the stubs and skeletons and recompile them via javac.
     * </ul>
     *
     * @param     nameJavaC      name the javac tool to use
     * @param     optionsJavaC   options for the javac tool
     * @param     optionsRmiC    options for the rmic tool
     * @param     classpath      classpath value
     * @exception GenICException In error case
     */
    public void compilClasses(String nameJavaC,
			      Vector optionsJavaC,
			      Vector optionsRmiC,
			      String classpath)
	throws GenICException {
	
	String optDirOutput;
	String sRmic;
	Cmd cmd;

	if (beanList.size() == 0) {
	    return;
	}

	/*
	 * Compile the generated sources
	 */
	cmd = new Cmd(nameJavaC);
	cmd.addArgument("-classpath");
	cmd.addArgument(classpath);
	cmd.addArgument("-d");
	cmd.addArgument((outputdir.length() == 0) ? "." : outputdir);
	cmd.addArguments(optionsJavaC);
	for (Enumeration e = remoteJavas.elements() ; e.hasMoreElements() ;) {
	    String srcName = (String)e.nextElement();
	    cmd.addArgument(srcName);
	    filesToDelete.addElement(srcName);
	}
	for (Enumeration e = noRemoteJavas.elements() ; e.hasMoreElements() ;) {
	    String srcName = (String)e.nextElement();
	    cmd.addArgument(srcName);
	    filesToDelete.addElement(srcName);
	}
	
	trace("Running '" + cmd.toString() + "'");
	if (cmd.run()) {
	    trace("Sources classes successfully compiled via java compiler.");
	} else {
	    throw new GenICException("Failed when compiling the generated classes via java compiler");
	}

	if (remoteJavas.size() == 0) {
	    return;
	}

	/*
	 * Generate the stub and skeletons of the home and remote implementations
	 */
	if (Env.isSunRMI()) {
	    sRmic = "rmic";
	    cmd = new Cmd("rmic");
	    cmd.addArgument("-classpath");
	    cmd.addArgument(classpath);
	    cmd.addArgument("-v1.1");
	    cmd.addArgument("-keepgenerated");
	} else if (Env.isJeremie() || Env.isDavid()) {
	    String rmicClassName;
	    if (Env.isJeremie()) {
		rmicClassName = "org.objectweb.jeremie.tools.jrmic.JRMICompiler";
		sRmic = "jrmic";
	    } else {
		rmicClassName = "org.objectweb.david.tools.jrmic.JRMICompiler";
		sRmic = "drmic";
	    }
	    try {
		Class rmicClass = Class.forName(rmicClassName);
	    } catch (ClassNotFoundException e) {
		throw new
		    GenICException("Failed when compiling the generated classes: "+
				   "Jeremie/David stub compiler not found");
	    }
	    cmd = new Cmd("java");
	    cmd.addArgument("-classpath");
	    cmd.addArgument(classpath);
	    cmd.addArgument(rmicClassName);
	    cmd.addArgument("-opt");
	    if (Env.isDavid()) {
		cmd.addArgument("-iiop");
	    }
	} else {
	    throw new GenICException("ORB unknown (neither RMI, nor JEREMIE, nor DAVID)");
	}
	
	if (!"".equals(outputdir)) {
	    cmd.addArgument("-d");
	    cmd.addArgument(outputdir);
	}
	cmd.addArguments(optionsRmiC);
	for (Enumeration e = remoteClasses.elements() ; e.hasMoreElements() ;) {
	    String className = (String)e.nextElement();
	    cmd.addArgument(className);
	}
	trace("Running '" + cmd.toString() + "'");
	if (cmd.run()) {
	    trace("Stubs and Skels successfully generated via "+sRmic+".");
	} else {
	    throw new GenICException("Failed when generating the Stubs and Skels via '"+sRmic+"'");
	}

	/*
	 * In case of Sun RMI, patch the stubs and skels and recompile them.
	 */
	if (Env.isSunRMI()) {

	    Vector namesSkelStub = new Vector();
	    for (Enumeration e = remoteJavas.elements() ; e.hasMoreElements() ;) {
		String srcName = (String)e.nextElement();
		namesSkelStub.addElement(cnvJavaToSkelSrc(srcName));
		namesSkelStub.addElement(cnvJavaToStubSrc(srcName));
	    }
	    
	    // Stubs and skels patch
	    for (Enumeration e = namesSkelStub.elements() ; e.hasMoreElements() ;) {
		String srcName = (String)e.nextElement();
		trace("Modifying the '"+srcName+"' RMI file");
		modifySkelStubRMI(srcName);
		filesToDelete.addElement(srcName);
	    }

	    // Stubs and skels compilation
	    cmd = new Cmd(nameJavaC);  
	    cmd.addArgument("-classpath");
	    cmd.addArgument(classpath);      
	    if (!"".equals(outputdir)) {
		cmd.addArgument("-d");
		cmd.addArgument(outputdir);
	    }
	    cmd.addArguments(optionsJavaC);
	    // Add -nowarn to take off the 'deprecated' messages due to rmic -v1.1
	    cmd.addArgument("-nowarn");
	    for (Enumeration e = namesSkelStub.elements() ; e.hasMoreElements() ;) {
		String srcName = (String)e.nextElement();
		cmd.addArgument(srcName);
	    }
	    trace("Running '" + cmd.toString()+ "'");
	    if (cmd.run()) {
		trace("The 'patched' RMI Skel. and Stub. successfully compiled.");
	    } else {
		throw new GenICException("Failed when compiling the 'patched' RMI Skel. and Stub.");
	    }
	}
    }

    /*
     * Modify the Skel/Stub RMI for the implicite propagation of contexts.
     * The modification is different depending on we need to propagate the security context or not.
     */
    private void modifySkelStubRMI(String fileName) throws GenICException {

	String fileSaveName = new String(fileName + ".save");
	filesToDelete.addElement(fileSaveName);
	boolean isSkel = fileName.endsWith("_Skel.java");
	// Rename the original skel or stub file
	try {
	    File f = new File(fileName);
            File saveFile = new File(fileSaveName);
            if (saveFile.exists()) {
                saveFile.delete();
            }
	    if (!(f.renameTo(saveFile))) {
		throw new GenICException("Cannot rename the file '"+fileName+"' to '"+fileSaveName+"'");
	    }
	} catch (Exception e) {
	    throw new GenICException("Failed when renaming a RMI Skel or Stub ("+e+")");
	}
	// Read and Modify the skel or stub file
	BufferedReader  fIn;
	BufferedWriter fOut;
	try {
	    fIn = new BufferedReader(new InputStreamReader(new FileInputStream(fileSaveName)));
	    fOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName)));
	} catch (Exception e) {
	    throw new GenICException("Failed when reading or writing a RMI Skel or Stub ("+e+")");
	}

	String skeletonName = new String("Skeleton");
	String stubName = new String("RemoteStub");
	skeletonName = withSecPropag ? "Security"+skeletonName : skeletonName;
	stubName = withSecPropag ? "Security"+stubName : stubName;
	try {
	    String line;
	    line = fIn.readLine();
	    while (line != null)  {
		if (isSkel) {
		    // Modify a Skel RMI file
		    line = (new JString(line)).replace
			("implements",
			 "extends org.objectweb.jonas.rmifilters."+skeletonName+" implements",
			 false);
		    line = (new JString(line)).replace
			("call.releaseInputStream()",
			 "this.inRequest(call)",
			 false);
		    line = (new JString(line)).replace
			("call.getResultStream(true)",
			 "this.outReply(call)",
			 false);
		} else {
		    // Modify a Stub RMI file
		    line = (new JString(line)).replace
			("java.rmi.server.RemoteStub",
			 "org.objectweb.jonas.rmifilters."+stubName);
		    line = (new JString(line)).replace
			("ref.invoke",
			 "this.invoke");
		}
		fOut.write(line, 0, line.length());
		fOut.newLine();
		line = fIn.readLine();
	    }
	    fIn.close();
	    fOut.close();
	} catch (Exception e) {
	    throw new GenICException("Failed when reading or writing a RMI Skel. or Stub. ("+
				     e+")");
	}
    }

    /**
     * Add the generated classes in the given ejb-jar file.
     */
    public void addClassesInJar(String jarFileName) throws GenICException {

	if (beanList.size() == 0) {
	    return;
	}

	boolean isOsWindows = Env.isOsWindows();
	Cmd cmd = null;
	Vector classesFileName = new Vector();

	// Initialisation of the classes file names list to add
	// Warning: In case of DAVID (RMI/IIOP) the naming conversion of the Stub/Tie is more complex
	//          because the name of the Stub/Tie are not both depend on the remote class name !!!
	if (Env.isDavid()) {
	    for (Enumeration e = beanList.elements() ; e.hasMoreElements() ;) {
		BeanSources ics = (BeanSources)e.nextElement();
		if (ics.getWrpHomeFileName() != null) {
		    classesFileName.addElement(cnvJavaToServerClass(ics.getWrpHomeFileName()));
		    classesFileName.addElement(cnvJavaToClientClass(ics.getEjbHomeFileName()));
		}
		if (ics.getWrpRemoteFileName() != null) {
		    classesFileName.addElement(cnvJavaToServerClass(ics.getWrpRemoteFileName()));
		    classesFileName.addElement(cnvJavaToClientClass(ics.getEjbRemoteFileName()));
		}
	    }
	} else {
	    for (Enumeration e = remoteJavas.elements() ; e.hasMoreElements() ;) {
		String srcName = (String)e.nextElement();
		classesFileName.addElement(cnvJavaToClass(srcName));
		classesFileName.addElement(cnvJavaToSkelClass(srcName));
		classesFileName.addElement(cnvJavaToStubClass(srcName));
	    }
	}
	for (Enumeration e = noRemoteJavas.elements() ; e.hasMoreElements() ;) {
	    String srcName = (String)e.nextElement();
	    classesFileName.addElement(cnvJavaToClass(srcName));
	}

	// Construct and launch the jar command
	if (isOsWindows) {
	    /*
	     * Needed to walk around a Windows problem :-((
	     * (See http://developer.java.sun.com/developer/bugParade/bugs/4386865.html)
	     * If a file in the 'META-INF' directory have been read,
	     * the update of the jar file fails.
	     * The work around is to generated a script called in GenIC.bat
	     * (Warning: This generated script must not have a line too long
	     *           (limitated to 255 characters))
	     */
	    
	    /*
	     * Example of the bat file content:
	     *   -uf ../output/ejbjars/sb.jar -C ../../classes sb/JOnASOpHome.class
	     *                                -C ../../classes sb/JOnASOpHome_Skel.class
	     *                                .................
	     */
	    String batFileName = "GenICTmpJarU";
	    try {
		PrintWriter outBat =
		    new PrintWriter(new BufferedWriter(new FileWriter(batFileName)));
		outBat.println("-uf " + jarFileName);
		for (Enumeration e = classesFileName.elements() ; e.hasMoreElements() ;) {
		    String className = (String)e.nextElement();
		    outBat.println("-C " + outputdir + " "
				   + className.substring(outputdir.length()+1,
							 className.length()));
		}
		outBat.close();
	    } catch (IOException ioe) {
		throw new GenICException("Failed when creating the temporary bat file '"
					 + batFileName+"' needed to update the ejb-jar-file (."
					 + ioe + ").");
	    }
	    trace(batFileName+" file created'");
	} else {
	    /*
	     * Example of a jar command:
	     *   jar -uf ../output/ejbjars/sb.jar -C ../../classes sb/JOnASOpHome.class
	     *                                    -C ../../classes sb/JOnASOpHome_Skel.class
	     *                                    .................
	     */
	    cmd = new Cmd("jar");
	    cmd.addArgument("-uf");
	    cmd.addArgument(jarFileName);
	    for (Enumeration e = classesFileName.elements() ; e.hasMoreElements() ;) {
		String className = (String)e.nextElement();
		cmd.addArgument("-C");
		cmd.addArgument(outputdir);
		cmd.addArgument(className.substring(outputdir.length()+1,
						    className.length()));
		filesToDelete.addElement(className);
	    }
	    trace("Running '" + cmd.toString() + "'");
	    boolean exitCmd = cmd.run();
	    // Analyse the result of the jar command
	    if (exitCmd) {
		trace("The generated classes are successfully added "
		      + "in the given ejb-jar file '"+jarFileName+"'.");
	    } else {
		throw new GenICException("Failed when adding the generated classes "
					 + "in the given ejb-jar file '"+jarFileName+"'.");
	    }
	}
    }

    /**
     * Clean the intermediate generated files.
     */
    public void clean() {
	trace("Deleting " + filesToDelete.toString());
	for (Enumeration e = filesToDelete.elements() ; e.hasMoreElements() ;) {
	    String name = (String)e.nextElement();
	    File f = new File(name);
	    f.delete();
	}
    }

    /*
     * Naming convention: Return the class file name of the given java source file name.
     * Ex: convert a/b/Foo.java to a/b/Foo.class
     */
    static String cnvJavaToClass(String jSrc) {
	return((new JString(jSrc)).replaceEndWith(".java", ".class"));
    }

    /*
     * Naming convention: Return the java source file name of the Skeleton of the given java source file name.
     * Ex: convert a/b/Foo.java to a/b/Foo_Skel.java
     * (ORB: RMI and JEREMIE)
     */
    static String cnvJavaToSkelSrc(String jSrc) {
	return((new JString(jSrc)).replaceEndWith(".java", "_Skel.java"));
    }

    /*
     * Naming convention: Return the java source file name of the Stub of the given java source file name.
     * Ex: convert a/b/Foo.java to a/b/Foo_Stub.java
     * (ORB: RMI and JEREMIE)
     */
    static String cnvJavaToStubSrc(String jSrc) {
	return((new JString(jSrc)).replaceEndWith(".java", "_Stub.java"));
    }

    /*
     * Naming convention: Return the class file name of the Skeleton of the given java source file name.
     * Ex: convert a/b/Foo.java to a/b/Foo_Skel.class.
     * (ORB: RMI and JEREMIE)
     */
    static String cnvJavaToSkelClass(String jSrc) {
	return((new JString(jSrc)).replaceEndWith(".java", "_Skel.class"));
    }

    /*
     * Naming convention: Return the class file name of the Stub of the given java source file name.
     * Ex: convert a/b/Foo.java to a/b/Foo_Stub.class
     * (ORB: RMI and JEREMIE)
     */
    static String cnvJavaToStubClass(String jSrc) {
	return((new JString(jSrc)).replaceEndWith(".java", "_Stub.class"));
    }


    /*
     * Naming convention: Return the server class file name of the given java source file name.
     * Ex: convert a/b/Foo.java to a/b/_Foo_Tie.class
     * (ORB: DAVID)
     */
    static String cnvJavaToServerClass(String jSrc) {
	String jSrcR = (new JString(jSrc)).replaceEndWith(".java", "");
	String result = jSrcR.substring(0,jSrcR.lastIndexOf(File.separatorChar)+1) +
	    "_" + jSrcR.substring(jSrcR.lastIndexOf(File.separatorChar)+1) + "_Tie.class";
	return result;
    }

    /*
     * Naming convention: Return the client class file name of the given java source file name.
     * Ex: convert a/b/Foo.java to a/b/_Foo_Stub.class
     * (ORB: DAVID)
     */
    static String cnvJavaToClientClass(String jSrc) {
	String jSrcR = (new JString(jSrc)).replaceEndWith(".java", "");
	String result = jSrcR.substring(0,jSrcR.lastIndexOf(File.separatorChar)+1) +
	    "_" + jSrcR.substring(jSrcR.lastIndexOf(File.separatorChar)+1) + "_Stub.class";
	return result;
    }

    /**
     * Display the usage
     */
    static void usage() {
	StringBuffer msg = new StringBuffer();
	msg.append("Usage: java org.objectweb.jonas_ejb.genic.GenIC -help \n");
	msg.append("         to print this help message \n");
	msg.append(" or    java org.objectweb.jonas_ejb.genic.GenIC <Options> <Input_File> \n");
	msg.append("         to generate the container-specific classes for given EJB(s). \n");
	msg.append(" \n");
	msg.append("Options include: \n");
        msg.append("       -d <output_dir>  specify where to place the generated files \n");
	msg.append("       -keepgenerated   do not delete intermediate generated source files \n");
	msg.append("       -noaddinjar      do not add the generated classes in the given \n");
	msg.append("                        ejb-jar file \n");
	msg.append("       -nocompil        do not compile the generated source files via \n");
	msg.append("                        the java and rmi compilers \n");
	msg.append("       -novalidation    parse the XML deployment descriptors without \n");
	msg.append("                        validation \n");      
	msg.append("       -javac     <opt> specify the java compiler to use \n");
	msg.append("       -javacopts <opt> specify the options to pass to the java compiler \n");
	msg.append("       -rmicopts  <opt> specify the options to pass to the rmi compiler \n");
	msg.append("       -secpropag       modify the RMI Skel. and Stub. to implement \n");
	msg.append("                        the implicit propagation of the security context \n");
	msg.append("       -verbose \n");
	msg.append(" \n");
	msg.append("       Input_File       standard deployment descriptor file's name or \n");
	msg.append("                        ejb-jar file's name \n");
	GenIC.info(msg.toString());
    }

    /**
     * Display the specified message only if verbose.
     * @param msg the message to display
     */
    void trace(String msg) {
	if (verbose) {
	    info("-- "+msg);
	}
    }

    /**
     * Display the specified message.
     * @param msg the message to display
     */
    static void info(String msg) {
        System.out.println(msg);
    }

    /**
     * Display the specified error message.
     * @param errMsg the error message to display
     */
    static void error(String errMsg) {
        System.err.println("GenIC error: " + errMsg);
    }


    /**
     * Display the specified error message and exits with an
     * EXIT_FAILURE status.
     * @param errMsg the error message to display
     */
    static void fatalError(String errMsg) {
	System.err.println("GenIC fatal error: " + errMsg);
	System.exit(EXIT_FAILURE);
    }

}



