/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2010 Bull S.A.
 * Contact: jonas-team@ow2.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): ____________________________________.
 * Contributor(s): Eric Hardesty
 *
 *
 * --------------------------------------------------------------------------
 * $Id: RAConfig.java 20307 2010-09-10 20:23:04Z pelletib $
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.generators.raconfig;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import javax.naming.Context;

import org.ow2.jonas.deployment.common.DeploymentDescException;
import org.ow2.jonas.deployment.rar.ConfigPropertyDesc;
import org.ow2.jonas.deployment.rar.RarDeploymentDesc;
import org.ow2.jonas.deployment.rar.RarDeploymentDescException;
import org.ow2.jonas.deployment.rar.lib.RarDeploymentDescManager;
import org.ow2.jonas.deployment.rar.xml.Activationspec;
import org.ow2.jonas.deployment.rar.xml.Adminobject;
import org.ow2.jonas.deployment.rar.xml.ConfigProperty;
import org.ow2.jonas.deployment.rar.xml.ConnectionDefinition;
import org.ow2.jonas.deployment.rar.xml.Connector;
import org.ow2.jonas.deployment.rar.xml.InboundResourceadapter;
import org.ow2.jonas.deployment.rar.xml.JdbcConnParams;
import org.ow2.jonas.deployment.rar.xml.JonasActivationspec;
import org.ow2.jonas.deployment.rar.xml.JonasAdminobject;
import org.ow2.jonas.deployment.rar.xml.JonasConfigProperty;
import org.ow2.jonas.deployment.rar.xml.JonasConnectionDefinition;
import org.ow2.jonas.deployment.rar.xml.JonasConnector;
import org.ow2.jonas.deployment.rar.xml.JonasSecurityMapping;
import org.ow2.jonas.deployment.rar.xml.Messageadapter;
import org.ow2.jonas.deployment.rar.xml.Messagelistener;
import org.ow2.jonas.deployment.rar.xml.OutboundResourceadapter;
import org.ow2.jonas.deployment.rar.xml.PoolParams;
import org.ow2.jonas.deployment.rar.xml.RequiredConfigProperty;
import org.ow2.jonas.deployment.rar.xml.Resourceadapter;
import org.ow2.jonas.deployment.rar.xml.SecurityEntry;
import org.ow2.jonas.lib.naming.ComponentContext;
import org.ow2.jonas.lib.util.Base64;

/**
 * This class may be used to parse a resource adapter deployment descriptor and
 * generate the corresponding properties files for the jca service java RAConfig
 * -j jndiname -r rarlink -verbose <input_rar>java
 * org.ow2.jonas.generators.raconfig.RAConfig -u jonas-ra.xml <input_rar>java
 * org.ow2.jonas.generators.raconfig.RAConfig -dm -p db.properties
 * <input_rar><output_rar>
 */
public class RAConfig {

    /**
     * Buffer Size
     */
    private static final int BUFFER_SIZE = 500;

    /** JCA 1.5 */
    private static final double JCA_1_5 = 1.5;

    /** JCA 1.0 */
    private static final double JCA_1_0 = 1.0;

    RarDeploymentDesc rarDD = null;

    /**
     * Validate the XML
     */
    private static boolean validation = true;

    /**
     * jonas-ra.xml XML filename
     */
    private static final String JONAS_RA_FILENAME = "META-INF/jonas-ra.xml";

    /**
     * Constructor
     */
    public RAConfig(final String fileName) throws RAConfigException {
        ClassLoader curLoader = Thread.currentThread().getContextClassLoader();

        try {
            RarDeploymentDescManager.setParsingWithValidation(validation);
            rarDD = getRarDD(fileName, curLoader);
        } catch (Exception e) {
            throw new RAConfigException(e);
        }

    }

    /**
     * Get an instance of an RAR deployment descriptor by parsing the ra.xml and
     * jonas-ra.xml deployment descriptors.
     * @param rarFileName the fileName of the rar file for the deployment
     *        descriptors.
     * @param classLoader the classloader for the classes.
     * @return an RAR deployment descriptor by parsing the ra.xml & jonas-ra.xml
     *         deployment descriptors.
     * @throws DeploymentDescException if the deployment descriptors are
     *         corrupted.
     */
    private RarDeploymentDesc getRarDD(final String rarFileName, final ClassLoader classLoader) throws DeploymentDescException {

        // The context to give for the creation of an instance
        // of RarDeploymentDesc
        Context contctx = null;
        try {
            contctx = new ComponentContext(rarFileName);
            contctx.rebind("rarFileName", rarFileName);
            contctx.rebind("classloader", classLoader);
        } catch (Exception e) {
            // An exception occurred trying to bind context
            // logger.log(BasicLevel.ERROR, "Error when binding context for
            // RarDeploymentDesc for '" + rarFileName + "'");
            // logger.log(BasicLevel.ERROR, e.getMessage());

            throw new RarDeploymentDescException("Error during the deployment", e);
        }
        // want both ra.xml & jonas-ra.xml
        return RarDeploymentDescManager.getInstance(contctx);
    }

    public RarDeploymentDesc getRarDeploymentDesc() {
        return rarDD;
    }

    /**
     * Process the ra.xml file in the input rar file and build the corresponding
     * jonas-ra.xml. If the -p argument is specified, then retrieve the
     * appropriate configuration values to pre-configure the corrsponding JDBC
     * RA config properties.
     * @param fileInputName String file name
     * @param jndiname String jndi name
     * @param rarlink String rar link
     * @param dbProps Properties
     * @param encrypt boolean
     * @param securityfile String security file
     * @param path String path
     * @param isVerbose boolean
     * @param createNew boolean
     * @throws RAConfigException if an Exception occurs
     */
    private static void buildXML(final String fileInputName, String jndiname, final String rarlink, final Properties dbProps, final boolean encrypt,
            final String securityfile, final String path, final boolean isVerbose, final boolean createNew) throws RAConfigException {

        // Check if jonas-ra.xml already exists and return it
        if (!createNew) {
            boolean ext = extractJonasRAXml(fileInputName, path);
            if (ext) {
                return;
            }
        }

        RAConfig raxml = new RAConfig(fileInputName);
        Connector conn = raxml.getRarDeploymentDesc().getConnector();
        double specVersion = 0.0;
        ConfigPropertyDesc[] raProps = raxml.getRarDeploymentDesc().getRaConfigPropTags();
        if (raProps == null) {
            throw new RAConfigException("RAR file doesn't contain ra.xml");
        }
        if (conn.getSpecVersion().equals("1.0")) {
            specVersion = JCA_1_0;
        } else if (conn.getSpecVersion().equals("1.5")) {
            specVersion = JCA_1_5;
        } else {
            throw new RAConfigException("ra.xml doesn't correct spec version");
        }

        JonasConnector jConn = new JonasConnector();
        PoolParams jPool = new PoolParams();
        JdbcConnParams jConnParams = new JdbcConnParams();
        JonasConfigProperty jCfg = null;

        String val = "";
        String str = "";

        val = "0";
        if (dbProps.getProperty("jdbc.initconpool") != null && dbProps.getProperty("jdbc.initconpool").length() > 0) {
            val = dbProps.getProperty("jdbc.initconpool").trim();
        }
        jPool.setPoolInit(val);

        val = "0";
        if (dbProps.getProperty("jdbc.minconpool") != null && dbProps.getProperty("jdbc.minconpool").length() > 0) {
            val = dbProps.getProperty("jdbc.minconpool").trim();
        }
        jPool.setPoolMin(val);

        val = "-1";
        if (dbProps.getProperty("jdbc.maxconpool") != null && dbProps.getProperty("jdbc.maxconpool").length() > 0) {
            val = dbProps.getProperty("jdbc.maxconpool").trim();
        }
        jPool.setPoolMax(val);

        val = "0";
        if (dbProps.getProperty("jdbc.connmaxage") != null && dbProps.getProperty("jdbc.connmaxage").length() > 0) {
            val = dbProps.getProperty("jdbc.connmaxage").trim();
        }
        jPool.setPoolMaxAgeMinutes(val);

        val = "0";
        if (dbProps.getProperty("jdbc.maxopentime") != null && dbProps.getProperty("jdbc.maxopentime").length() > 0) {
            val = dbProps.getProperty("jdbc.maxopentime").trim();
        }
        jPool.setPoolMaxOpentime(val);

        val = "0";
        if (dbProps.getProperty("jdbc.maxwaiters") != null && dbProps.getProperty("jdbc.maxwaiters").length() > 0) {
            val = dbProps.getProperty("jdbc.maxwaiters").trim();
        }
        jPool.setPoolMaxWaiters(val);

        val = "0";
        if (dbProps.getProperty("jdbc.maxwaittime") != null && dbProps.getProperty("jdbc.maxwaittime").length() > 0) {
            val = dbProps.getProperty("jdbc.maxwaittime").trim();
        }
        jPool.setPoolMaxWaittime(val);

        val = "30";
        if (dbProps.getProperty("jdbc.samplingperiod") != null && dbProps.getProperty("jdbc.samplingperiod").length() > 0) {
            val = dbProps.getProperty("jdbc.samplingperiod").trim();
        }
        jPool.setPoolSamplingPeriod(val);

        val = "10";
        if (dbProps.getProperty("jdbc.pstmtmax") != null && dbProps.getProperty("jdbc.pstmtmax").length() > 0) {
            val = dbProps.getProperty("jdbc.pstmtmax").trim();
        }
        jPool.setPstmtMax(val);

        jConn.setLogEnabled("false");
        jConn.setLogTopic("");
        jConn.setNativeLib("");
        if (jndiname.length() == 0) {
            jndiname = dbProps.getProperty("datasource.name", "").trim();
        }

        val = "0";
        if (dbProps.getProperty("jdbc.connchecklevel") != null && dbProps.getProperty("jdbc.connchecklevel").length() > 0) {
            val = dbProps.getProperty("jdbc.connchecklevel").trim();
        }
        jConnParams.setJdbcCheckLevel(val);

        val = "";
        if (dbProps.getProperty("jdbc.connteststmt") != null && dbProps.getProperty("jdbc.connteststmt").length() > 0) {
            val = dbProps.getProperty("jdbc.connteststmt").trim();
            jConnParams.setJdbcTestStatement(val);
        }

        if (specVersion == JCA_1_0) {
            jConn.setJndiName(jndiname);
        }
        jConn.setRarlink(rarlink);
        jConn.setPoolParams(jPool);
        jConn.setJdbcConnParams(jConnParams);

        if (specVersion == JCA_1_0) {
            val = "";
            str = "";
            for (int i = 0; i < raProps.length; i++) {
                jCfg = new JonasConfigProperty();
                str = raProps[i].getConfigPropertyName();
                jCfg.setJonasConfigPropertyName(str);
                if (dbProps.size() > 0) {
                    val = getProp(dbProps, str);
                } else {
                    val = "";
                }
                jCfg.setJonasConfigPropertyValue(val);
                jConn.addJonasConfigProperty(jCfg);
            }
        } else if (specVersion == JCA_1_5) {
            Resourceadapter rAdapter = conn.getResourceadapter();
            ConfigProperty cProperty = null;
            // TODO rProperty never used !!!
            RequiredConfigProperty rProperty = null;
            // Process config properties if they exist
            for (Iterator i = rAdapter.getConfigPropertyList().iterator(); i.hasNext();) {
                cProperty = (ConfigProperty) i.next();
                jCfg = new JonasConfigProperty();
                str = cProperty.getConfigPropertyName();
                jCfg.setJonasConfigPropertyName(str);
                if (dbProps.size() > 0) {
                    val = getProp(dbProps, str);
                } else {
                    val = "";
                }
                jCfg.setJonasConfigPropertyValue(val);
                jConn.addJonasConfigProperty(jCfg);
            }

            // is it the first jonas-connection-definition
            boolean first = true;

            // Now process the outbound-resourceadapter
            OutboundResourceadapter oRes = rAdapter.getOutboundResourceadapter();
            if (oRes != null) {
                ConnectionDefinition cDef = null;
                JonasConnectionDefinition jcDef = null;
                for (Iterator it = oRes.getConnectionDefinitionList().iterator(); it.hasNext();) {
                    cDef = (ConnectionDefinition) it.next();
                    jcDef = new JonasConnectionDefinition();
                    jcDef.setId(cDef.getId());
                    jcDef.addDescription(cDef.getManagedconnectionfactoryClass());

                    // This allows the jndi name to be set for the first
                    // jonas-connection-definition
                    // with a 1.5 version of the connector.
                    if (first && (jndiname.length() != 0)) {
                        jcDef.setJndiName(jndiname);
                        first = false;
                    } else {
                        jcDef.setJndiName("");
                    }

                    for (Iterator i = cDef.getConfigPropertyList().iterator(); i.hasNext();) {
                        cProperty = (ConfigProperty) i.next();
                        jCfg = new JonasConfigProperty();
                        str = cProperty.getConfigPropertyName();
                        jCfg.setJonasConfigPropertyName(str);
                        jCfg.setJonasConfigPropertyValue("");
                        jcDef.addJonasConfigProperty(jCfg);
                    }
                    jConn.addJonasConnectionDefinition(jcDef);
                }
            }
            // Now process the inbound-resourceadapter
            InboundResourceadapter iRes = rAdapter.getInboundResourceadapter();
            if (iRes != null) {
                Messageadapter mAdapter = iRes.getMessageadapter();
                if (mAdapter != null) {
                    Messagelistener mList = null;
                    JonasActivationspec jaSpec = null;
                    Activationspec aSpec = null;
                    for (Iterator it = mAdapter.getMessagelistenerList().iterator(); it.hasNext();) {
                        mList = (Messagelistener) it.next();
                        aSpec = mList.getActivationspec();
                        if (aSpec != null) {
                            jaSpec = new JonasActivationspec();
                            jaSpec.setId(mList.getId());
                            jaSpec.addDescription(mList.getMessagelistenerType());
                            jaSpec.setJndiName("");
                            jConn.addJonasActivationspec(jaSpec);
                        }
                    }
                }
            }
            // Now process the adminobject
            for (Iterator it = rAdapter.getAdminobjectList().iterator(); it.hasNext();) {
                Adminobject aObj = (Adminobject) it.next();
                JonasAdminobject jaObj = null;
                if (aObj != null) {
                    jaObj = new JonasAdminobject();
                    jaObj.setId(aObj.getId());
                    jaObj.addDescription(aObj.getAdminobjectClass());
                    jaObj.setJndiName("");
                    // Process config properties if they exist
                    for (Iterator i = aObj.getConfigPropertyList().iterator(); i.hasNext();) {
                        cProperty = (ConfigProperty) i.next();
                        jCfg = new JonasConfigProperty();
                        str = cProperty.getConfigPropertyName();
                        jCfg.setJonasConfigPropertyName(str);
                        jCfg.setJonasConfigPropertyValue("");
                        jaObj.addJonasConfigProperty(jCfg);
                    }
                    jConn.addJonasAdminobject(jaObj);
                }
            }
        }

        if (securityfile != null) {
            Properties secProps = new Properties();
            File f = null;
            try {
                f = new File(securityfile);
                FileInputStream is = new FileInputStream(f);
                secProps.load(is);
            } catch (FileNotFoundException e) {
                System.err.println("Cannot find security properties file: " + securityfile);
                System.err.println("Continuing with creation of jonas-ra.xml");
            } catch (IOException e) {
                System.err.println(e);
            }

            JonasSecurityMapping jsMap = new JonasSecurityMapping();
            String pName = null;
            String user = null;
            String pass = null;
            String tmp = null;
            int offset = 0;
            SecurityEntry sEnt = null;
            for (Enumeration e = secProps.propertyNames(); e.hasMoreElements();) {
                pName = (String) e.nextElement();
                tmp = secProps.getProperty(pName);
                offset = tmp.indexOf("::");
                if (offset < 0) {
                    System.err.println("Error in format of file:  principalName = user::password");
                    continue;
                }
                user = tmp.substring(0, offset);
                pass = tmp.substring(offset + 2);

                sEnt = new SecurityEntry();
                sEnt.setPrincipalName(pName);
                sEnt.setUser(user);
                if (encrypt) {
                    try {
                        pass = new String(Base64.encode(pass.getBytes()));
                        sEnt.setEncrypted("" + encrypt);
                    } catch (Exception ex) {
                        System.err.println("Unable to encrypt the password.");
                    }
                }
                sEnt.setPassword(pass);
                jsMap.addSecurityEntry(sEnt);
            }
            jConn.setJonasSecurityMapping(jsMap);
        }

        try {
            String tmpDir = path;
            if (path.length() == 0) {
                tmpDir = System.getProperty("java.io.tmpdir");
            }
            String fileName = tmpDir;
            int fileLen = fileName.length();
            if (fileName.charAt(fileLen - 1) != File.separatorChar) {
                fileName += File.separatorChar;
            }
            fileName += "jonas-ra.xml";
            FileWriter fw = new FileWriter(fileName);
            fw.write(jConn.toXML());
            fw.close();
            if (isVerbose) {
                System.out.println("Build Jonas Specific DD File '" + fileName + "' OK");
            }

        } catch (Exception e) {
            throw new RAConfigException("Error writing output", e);
        }
    }

    /**
     * Mapping routine for the JDBC RA config values and the existing
     * <DB>.properties values.
     * @param prop Properties
     * @param str String
     * @return String
     */
    private static String getProp(final Properties prop, final String str) {
        // ra-keys | properties-keys
        String[][] mapping = { {"connCheckLevel", "jdbc.connchecklevel"}, {"connMaxAge", "jdbc.connmaxage"},
                {"connTestStmt", "jdbc.connteststmt"}, {"URL", "datasource.url"}, {"dsClass", "datasource.classname"},
                {"user", "datasource.username"}, {"password", "datasource.password"},
                {"isolationLevel", "datasource.isolationlevel"}, {"mapperName", "datasource.mapper"},};

        String ret = "";
        for (int i = 0; i < mapping.length; i++) {
            if (mapping[i][0].equalsIgnoreCase(str)) {
                String value = prop.getProperty(mapping[i][1]);
                if (value == null) {
                    ret = "";
                } else {
                    ret = value.trim();
                }
            }
        }

        return ret;
    }

    /**
     * Update the RAR file
     * @param infile String input RAR file
     * @param outfile String output RAR file
     * @param updatename String update file
     * @param rarlink String RAR link
     * @throws RAConfigException to throw if an Exception occurs
     */
    private static void updateRAR(final String infile, final String outfile, final String updatename, final String rarlink) throws RAConfigException {

        ZipOutputStream zipOutput = null;
        ZipFile zipFile = null;
        Enumeration zippedFiles = null;
        ZipEntry currEntry = null;
        ZipEntry entry = null;
        byte[] buffer = new byte[BUFFER_SIZE];
        int num = 0;

        String filename = infile;
        String outfilename = infile;
        boolean outNew = false;
        if (outfile != null && outfile.length() != 0) {
            outfilename = outfile;
            outNew = true;
        }

        try {
            File file = new File(filename);
            if (file.exists()) {
                zipFile = new ZipFile(file.getAbsolutePath());
                // get an enumeration of all existing entries
                zippedFiles = zipFile.entries();
                // create your output zip file
                if (!outNew) {
                    zipOutput = new ZipOutputStream(new FileOutputStream(new File(file.getAbsolutePath() + "NEW")));
                } else {
                    File ofile = new File(outfilename);
                    if (ofile.exists()) {
                        ofile.delete();
                    }

                    zipOutput = new ZipOutputStream(new FileOutputStream(new File(outfilename)));
                }
                // Get all the data out of the previously zipped files and
                // write
                // it to a new ZipEntry to go into a new file archive
                if ((outfile == null || outfile.length() == 0) && rarlink.length() == 0) {
                    while (zippedFiles.hasMoreElements()) {
                        // Retrieve entry of existing files
                        currEntry = (ZipEntry) zippedFiles.nextElement();
                        if (currEntry.getName().equalsIgnoreCase(JONAS_RA_FILENAME)) {
                            continue;
                        }
                        // Read data from existing file
                        zipOutput.putNextEntry(new ZipEntry(currEntry.getName()));
                        InputStream reader = zipFile.getInputStream(currEntry);
                        while ((num = reader.read(buffer)) != -1) {
                            zipOutput.write(buffer, 0, num);
                        }
                        // Commit the data
                        zipOutput.flush();
                        zipOutput.closeEntry();
                    }
                }
                // Close the old zip file
                zipFile.close();

                // Write the 'new' file to the archive, commit, and close
                entry = new ZipEntry(JONAS_RA_FILENAME);
                zipOutput.putNextEntry(entry);

                // Read data from existing file
                try {
                    BufferedReader reader = new BufferedReader(new FileReader(updatename));
                    int ch = 0;
                    while ((ch = reader.read()) != -1) {
                        zipOutput.write(ch);
                    }
                } catch (Exception ex) {
                    System.out.println("Error reading input file: " + updatename + " " + ex);
                    // Commit the data
                    zipOutput.flush();
                    zipOutput.closeEntry();
                    zipOutput.finish();
                    zipOutput.close();
                }
                // Commit the data
                zipOutput.flush();
                zipOutput.closeEntry();
                zipOutput.finish();
                zipOutput.close();

                if (!outNew) {
                    // delete the old file and rename the new one
                    File toBeDeleted = new File(file.getAbsolutePath());
                    toBeDeleted.delete();
                    File toBeRenamed = new File(file.getAbsolutePath() + "NEW");
                    toBeRenamed.renameTo(file);
                }
            } else {
                // create your output zip file
                zipOutput = new ZipOutputStream(new FileOutputStream(new File(outfilename)));

                // Write the file to the archive, commit, and close
                entry = new ZipEntry(JONAS_RA_FILENAME);
                zipOutput.putNextEntry(entry);
                // Read data from existing file
                BufferedReader reader = new BufferedReader(new FileReader(updatename));
                int ch = 0;
                while ((ch = reader.read()) != -1) {
                    zipOutput.write(ch);
                }

                // Commit the data
                zipOutput.flush();
                zipOutput.closeEntry();
                zipOutput.finish();
                zipOutput.close();
            }
            // finally, we delete generated jonas-ra.xml
            new File(updatename).delete();
        } catch (Exception ex) {
            throw new RAConfigException(ex);
        }

    }

    /**
     * Extract the jonas-ra.xml file if it exists in the rar
     * @param infile String rar file name
     * @param path String path
     */
    private static boolean extractJonasRAXml(final String infile, final String path) {
        ZipFile zipFile = null;
        ZipEntry entry = null;
        byte[] buffer = new byte[BUFFER_SIZE];

        String filename = infile;
        File file = new File(filename);
        if (file.exists()) {
            try {
                zipFile = new ZipFile(file.getAbsolutePath());
                entry = zipFile.getEntry(JONAS_RA_FILENAME);
                if (entry != null) {
                    InputStream input = zipFile.getInputStream(entry);
                    String tmpDir = path;
                    if (path.length() == 0) {
                        tmpDir = System.getProperty("java.io.tmpdir");
                    }
                    String fileName = tmpDir;
                    int fileLen = fileName.length();
                    if (fileName.charAt(fileLen - 1) != File.separatorChar) {
                        fileName += File.separatorChar;
                    }
                    fileName += "jonas-ra.xml";

                    FileOutputStream output = new FileOutputStream(fileName);
                    for (int j = 0;;) {
                        int length = input.read(buffer);
                        if (length <= 0) {
                            break;
                        }
                        output.write(buffer, 0, length);
                    }
                    output.close();
                    return true;
                }
            } catch (Exception ex) {
                return false;
            }
        }
        return false;
    }

    /**
     * Usage of RAConfig
     */
    public static void usage() {
        System.out.println("");
        System.out.println("Usage: java org.ow2.jonas.generators.raconfig.RAConfig <Options> <Input_File> [Output_File]");
        System.out.println("     to generate the jonas-ra.xml file for a Resource Adapter.");
        System.out.println("   The output file will be written to the default location of the java ");
        System.out.println("     system property 'java.io.tmpdir' unless the -path parameter is specified");
        System.out.println("");
        System.out.println("   Note: for a parameter either the whole name must be used or only the");
        System.out.println("         the capital letters, e.g.  -jndiname or -j are equivalent");
        System.out.println("");
        System.out.println("       With Options:");
        System.out.println("         -? or -help                print this help message");
        System.out.println("         -DM,-DS,-CP,-XA            DriverManager, Datasource, ConnectionPoolDatasource,");
        System.out.println("                                    XAConnection only used with -p.  Additional");
        System.out.println("                                    user configuration of the jonas-ra.xml file");
        System.out.println("                                    will be necessary for any option but -DM.");
        System.out.println("         -Jndiname <jndiname>       specifies the jndiname RA");
        System.out.println("         -Property <property file>  specifies the database property file");
        System.out.println("                                     to process");
        System.out.println("         -Rarlink <rarlink>         specifies the rar file to link to");
        System.out.println("         -Update <input name>       update the input rar with the specified file");
        System.out.println("                                    unless an output_file named is specified");
        System.out.println("         -ENcrypt                   used with -sf to encrypt the passwords ");
        System.out.println("         -SecurityFile              security xml file to add to jonas-ra.xml ");
        System.out.println("         -Verbose                   output the contents of the  deployment descriptor ");
        System.out.println("         -NoValidation              turn off xml dtd/schema validation ");
        System.out.println("         -PATH <path>               path where jonas-ra.xml should be written to ");
        System.out.println("                                     default is System.property('java.io.tmpdir')");
        System.out.println("         -NEW                       don't extract jonas-ra.xml create a new one ");
        System.out.println("");
        System.out.println("       Input_File     Resource Adapter RAR file");
        System.out.println("       Output_File    Resource Adapter RAR file only used with ");
        System.out.println("                          -p(required) or -u(optional)");
        System.out.println("");
    }

    // Main Method
    /**
     * Main routine
     * @param args String [] input arguments
     * @throws RAConfigException if an Exception occurs
     */
    public static void main(final String[] args) throws RAConfigException {
        boolean isHelp = false;
        boolean isVerbose = false;
        boolean doProperty = false;
        boolean doUpdate = false;
        boolean encrypt = false;
        boolean newXml = false;
        String fileInputName = null;
        String fileOutputName = null;
        String securityfile = null;
        String jndiname = "";
        String rarlink = "";
        String updatename = "";
        String jdbcRarLink = "";
        String jonasRarStr = "JOnASJDBC_";
        String propertyFile = "";
        String path = "";
        // Get command args

        Properties dbProps = null;

        if (args.length < 1) {
            isHelp = true;
        }
        for (int argn = 0; argn < args.length; argn++) {
            String arg = args[argn].toLowerCase();
            if (arg.equals("-help") || arg.equals("-?")) {
                isHelp = true;
                continue;
            }
            if (arg.equals("-verbose") || arg.equals("-v")) {
                isVerbose = true;
                continue;
            }
            if (arg.equals("-encrypt") || arg.equals("-en")) {
                encrypt = true;
                continue;
            }
            if (arg.equals("-securityfile") || arg.equals("-sf")) {
                if (argn + 1 == args.length || args[++argn].startsWith("-")) {
                    usage();
                    throw new RAConfigException("Error with -sf input");
                }
                securityfile = args[argn];
                continue;
            }
            if (arg.equals("-novalidation") || arg.equals("-nv")) {
                validation = false;
                continue;
            }
            if (arg.equals("-update") || arg.equals("-u")) {
                doUpdate = true;
                if (argn + 1 == args.length || args[++argn].startsWith("-")) {
                    usage();
                    throw new RAConfigException("Error with -u input");
                }
                updatename = args[argn];
                continue;
            }
            if (arg.equals("-jndiname") || arg.equals("-j")) {
                if (argn + 1 == args.length || args[++argn].startsWith("-")) {
                    usage();
                    throw new RAConfigException("Error with -j input");
                }
                jndiname = args[argn];
                continue;
            }
            if (arg.equals("-rarlink") || arg.equals("-r")) {
                if (argn + 1 == args.length || args[++argn].startsWith("-")) {
                    usage();
                    throw new RAConfigException("Error with -r input");
                }
                rarlink = args[argn];
                continue;
            }
            if (arg.equals("-property") || arg.equals("-p")) {
                doProperty = true;
                if (argn + 1 == args.length || args[++argn].startsWith("-")) {
                    usage();
                    throw new RAConfigException("Error with -p input");
                }
                propertyFile = args[argn];
                continue;
            }
            if (arg.equals("-dm")) {
                jdbcRarLink = jonasRarStr + "DM";
                continue;
            }
            if (arg.equals("-ds")) {
                jdbcRarLink = jonasRarStr + "DS";
                continue;
            }
            if (arg.equals("-xa")) {
                jdbcRarLink = jonasRarStr + "XA";
                continue;
            }
            if (arg.equals("-cp")) {
                jdbcRarLink = jonasRarStr + "CP";
                continue;
            }
            if (arg.equals("-path")) {
                if (argn + 1 == args.length || args[++argn].startsWith("-")) {
                    usage();
                    throw new RAConfigException("Error with -path");
                }
                path = args[argn];
                continue;
            }
            if (arg.equals("-new")) {
                newXml = true;
                continue;
            }
            if (fileInputName == null) {
                fileInputName = args[argn];
                if (!fileInputName.endsWith(".rar")) {
                    fileInputName += ".rar";
                }
            } else if (doProperty || doUpdate) {
                if (fileOutputName == null) {
                    fileOutputName = args[argn];
                    if (!fileOutputName.endsWith(".rar")) {
                        fileOutputName += ".rar";
                    }
                }
            } else {
                usage();
                throw new RAConfigException("Error multiple input files specified without -property or -update");
            }
        }

        // Usage ?
        if (isHelp) {
            usage();
            System.exit(0);
        }
        if (fileInputName == null) {
            usage();
            throw new RAConfigException("Error missing input file");
        }
        if (doProperty && fileOutputName == null) {
            usage();
            throw new RAConfigException("Error missing output file");
        }
        if (fileInputName.equals(fileOutputName)) {
            throw new RAConfigException("Input and Output filenames cannot be the same");
        }

        dbProps = new Properties();
        if (doProperty) {
            if (jdbcRarLink.length() > 0 && rarlink.length() == 0) {
                rarlink = jdbcRarLink;
            }
            try {
                dbProps.load(new FileInputStream(propertyFile));
            } catch (Exception ex) {
                if (propertyFile.endsWith(".properties")) {
                    System.out.println("Error reading " + propertyFile + " " + ex);
                } else {
                    try {
                        dbProps.load(new FileInputStream(propertyFile + ".properties"));
                    } catch (Exception ex1) {
                        System.out.println("Error reading " + propertyFile + " " + ex);
                    }
                }
            }
            buildXML(fileInputName, jndiname, rarlink, dbProps, encrypt, securityfile, path, isVerbose, true);
            if (updatename.length() == 0) {
                updatename = System.getProperty("java.io.tmpdir") + File.separator + "jonas-ra.xml";
            }
            updateRAR(fileInputName, fileOutputName, updatename, rarlink);
        } else if (doUpdate) {
            updateRAR(fileInputName, fileOutputName, updatename, rarlink);
        } else {
            buildXML(fileInputName, jndiname, rarlink, dbProps, encrypt, securityfile, path, isVerbose, newXml);
        }
    }

    /**
     * This method is used to generate rar files from a Properties object
     * @param props the properties object which fields are used to create the
     *        rar file
     * @param rarLink rarlink
     * @param inputfile the jonas rar file used
     * @param outputfile the url of the rar file
     * @throws RAConfigException when a problem occurs while generating the rar
     *         file
     */
    public static void generateRars(final Properties props, final String rarLink, final String inputfile,
            final String outputfile) throws RAConfigException {
        String jndiname = "";
        Boolean encrypt = false;
        String securityfile = null;
        String path = "";
        Boolean isVerbose = false;
        String updatename = "";

        buildXML(inputfile, jndiname, rarLink, props, encrypt, securityfile, path, isVerbose, true);
        if (updatename.length() == 0) {
            updatename = System.getProperty("java.io.tmpdir") + File.separator + "jonas-ra.xml";
        }
        updateRAR(inputfile, outputfile, updatename, rarLink);

    }
}
