/*
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2004 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): ____________________________________.
 * Contributor(s): ______________________________________.
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */


package org.objectweb.jonas.tools;


import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.StringTokenizer;

import org.objectweb.jonas.common.JProp;

/**
 *  This class allows to check if the environment is correct for JOnAS.
 */

public class CheckEnv {

    /**
     * CheckEnv allows to check if the JOnAS environment is correct or not.
     * <p>
     * Usage: java org.objectweb.jonas.tools.CheckEnv -help
     * <br>
     *         to print this help message
     * <p>
     * or    java org.objectweb.jonas.tools.CheckEnv
     * <br>
     *         to check if the JOnAS environment is correct or not,
     *         and to display the content of the configuration files.
     */

    /**
     * Constant result of JOnAS's conf check [OK]
     */
    public static final int ENV_OK      = 0;

    /**
     * Constant result of JOnAS's conf check [Warning]
     */
    public static final int ENV_WARNING = 1;

    /**
     * Constant result of JOnAS's conf check [Error]
     */
    public static final int ENV_ERROR   = 2;

    /**
     * Constant esult of JOnAS's conf check [Unknown]
     */
    private static final int ENV_UNKNOWN = -1;

    /**
     * Current status of the checking
     */
    private static int envStatus = ENV_UNKNOWN;

    /**
     * Constant JONAS_ROOT
     */
    private static final String INSTALL_ROOT = "install.root";

    /**
     * Constant JONAS_BASE
     */
    private static final String JONAS_BASE   = "jonas.base";

    /**
     * Constant dbm service name
     */
    private static final String DBM_SERVICE = "dbm";

    /**
     * Constant jms service name
     */
    private static final String JMS_SERVICE = "jms";

    /**
     * Constant resource service name
     */
    private static final String RESOURCE_SERVICE = "resource";

    /**
     * Constant joram's rar name
     */
    private static final String JORAM_RAR = "joram_for_jonas_ra";

    /**
     * JORAM's RAR admin configuration file
     */
    private static final String JORAM_RAR_ADMIN_CFG_FILE = "joramAdmin.xml";

    /**
     * JORAM's RAR former admin configuration file
     */
    private static final String JORAM_RAR_ADMIN_CFG_FILE_DEPRECATED = "joram-admin.cfg";

    /**
     * Constant dbms's datasource list in jonas.properties
     */
    private static final String DATASOURCES = "jonas.service.dbm.datasources";

    /**
     * Constant datasource class name
     */
    private static final String DATASOURCE_CLASS_NAME = "datasource.classname";

    /**
     * Variable classes to check
     */
    private static Hashtable classesToCheck = new Hashtable();

    /**
     * Init classes to check
     */
    static {
        classesToCheck.put("javax.ejb.EnterpriseBean", "ejb-2_1-api.jar");
        classesToCheck.put("javax.sql.DataSource", "jdbc2_0-stdext.jar");
        classesToCheck.put("javax.transaction.UserTransaction", "jta-spec1_0_1.jar");
        classesToCheck.put("org.apache.xerces.parsers.SAXParser", "xerces.jar");
        classesToCheck.put("javax.jms.Message", "jms.jar");
        classesToCheck.put("org.apache.regexp.REUtil", "jakarta-regexp-1.2.jar");
        classesToCheck.put("org.objectweb.joram.mom.dest.Topic", "joram-mom.jar");
        classesToCheck.put("org.objectweb.transaction.jta.TMService", "jotm.jar");
    }

    /**
     * Constructor
     */
    private CheckEnv() {
    }

    /**
     * check the JOnAS's configuration
     * @return status of the configuration
     */
    public static int getStatus() {

        envStatus = ENV_OK;
        int res;

        // Check classes
        Enumeration cList = classesToCheck.keys();
        while (cList.hasMoreElements()) {
            String cname = (String) cList.nextElement();
            if (!classIsAccessible(cname)) {
                System.err.println("JOnAS environment ERROR: Classes of '"
                                   + classesToCheck.get(cname) + "' not accessible");
                System.err.println("(The JOnAS package is bad)");
                envStatus = ENV_ERROR;
            }
        }

        // Check the 'install.root' system property
        // (JONAS_ROOT variable environment)
        try {
            String sRoot = System.getProperty(INSTALL_ROOT);
            if (sRoot == null) {
                System.err.println("JOnAS environment ERROR: '"
                                   + INSTALL_ROOT + "' system property not defined");
                envStatus = ENV_ERROR;
            } else {
                File fRoot = new File(sRoot);
                if (!fRoot.isDirectory()) {
                    System.err.println("JOnAS environment ERROR: Invalid 'JONAS_ROOT' value");
                    System.err.println("                         '" + sRoot + "' directory not exist");
                    envStatus = ENV_ERROR;
                } else {
                    System.out.println("");
                    System.out.println("- JONAS_ROOT value: ");
                    System.out.println("   " + sRoot);
                }
            }
        } catch (SecurityException e) {
            System.err.println("CheckEnv ERROR: Cannot get the '"
                               + INSTALL_ROOT + "' system property (" + e + ")");
            envStatus = ENV_ERROR;
        }

        // Check the 'jonas.base' system property
        // (JONAS_BASE variable environment)
        try {
            String sBase = System.getProperty(JONAS_BASE);
            if (sBase == null) {
                System.err.println("JOnAS environment ERROR: '"
                                   + JONAS_BASE + "' environment property not defined");
                envStatus = ENV_ERROR;
            } else {
                File fBase = new File(sBase);
                if (!fBase.isDirectory()) {
                    System.err.println("JOnAS environment ERROR: Invalid 'JOAS_BASE' value");
                    System.err.println("                         '" + sBase + "' directory not exist");
                    envStatus = ENV_ERROR;
                } else {
                    System.out.println("");
                    System.out.println("- JONAS_BASE value: ");
                    System.out.println("   " + sBase);
                }
            }
        } catch (SecurityException e) {
            System.err.println("CheckEnv ERROR: Cannot get the '"
                               + JONAS_BASE + "' system property (" + e + ")");
            envStatus = ENV_ERROR;
        }

        // Check jonas.properties and <datasource>.properties
        if ((res = checkJonasProperties()) != ENV_OK) {
            envStatus = res;
        }

        // Check trace.properties
        if (getProperties("trace.properties") == null) {
            System.err.println("JOnAS environment ERROR: 'trace.properties' not accessible");
            envStatus = ENV_ERROR;
        }

        // Check carol.properties
        if (getProperties("carol.properties") == null) {
            System.err.println("JOnAS environment ERROR: 'carol.properties' not accessible");
            envStatus = ENV_ERROR;
        }

        // Check jonas-realm.xml file
        if ((res = checkRealm()) != ENV_OK) {
            envStatus = res;
        }
        // Check the JORAM's RA configuration
        if ((res = checkJoramRar()) != ENV_OK) {
            envStatus = res;
        }

        return envStatus;
    }

    /**
     * entry point of the program
     * @param args arguments of the program
     */
    public static void main(String[] args) {
        boolean isHelp    = false;
        // Get args
        for (int argn = 0; argn < args.length; argn++) {
            String arg = args[argn];
            if (arg.equals("-help") || arg.equals("-?")) {
                isHelp = true;
                continue;
            }
        }
        // Usage ?
        if (isHelp) {
            usage();
            System.exit(0);
        }

        // Status
        int status = getStatus();
        System.out.println("");
        if (status == ENV_OK) {
            System.err.println("The JOnAS environment seems correct.");
            System.exit(0);
        } else if (status == ENV_WARNING) {
            System.err.println("WARNING: The JOnAS environment may be incomplete.");
            System.exit(1);
        } else if (status == ENV_ERROR) {
            System.err.println("ERROR: The JOnAS environment is NOT correct.");
            System.exit(2);
        }
    }

    /**
     * check if the class is accessible
     * @param name class name
     * @return true if class is accessible, false else
     */
    static boolean classIsAccessible(String name) {
        try {
            Class.forName(name);
            return true;
        } catch (Exception e) {
            return false;
        } catch (Error e) {
            System.err.println(e);
            return false;
        }
    }

    // Return null if properties are not accessible
    /**
     * get properties from a file
     * @param name file containing properties list
     * @return properties list
     */
    static Properties getProperties(String name) {
        Properties props = null;
        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(name);
        if (is != null) {
            props = new Properties();
            try {
                props.load(is);
                System.out.println("");
                System.out.println("- Contents of '" + name + "':");
                if (props.isEmpty()) {
                    System.out.println("JOnAS environment ERROR :'" + name + "' is empty");
                    envStatus = ENV_ERROR;
                } else {
                    System.out.println("  (" + Thread.currentThread().getContextClassLoader().getResource(name).toString() + ")");
                    Enumeration pList = props.propertyNames();
                    while (pList.hasMoreElements()) {
                        String pname = (String) pList.nextElement();
                        System.out.println("   " + pname + " = " + props.getProperty(pname));
                    }
                }
            } catch (IOException e) {
                props = null;
            }
        } else {
            props = null;
        }
        return props;
    }

    /**
     * print program usage
     */
    private static void usage() {
        System.out.println("");
        System.out.println("Usage: CheckEnv [ -help | -? ]");
        System.out.println("    to print this help message");
        System.out.println("");
        System.out.println("or     CheckEnv");
        System.out.println("    to check if the JOnAS environment is correct or not,");
        System.out.println("    and to display the content of the configuration files.");
        System.out.println("");
    }
    /**
     * Check jonas's Realm
     * @return : result
     */
    private static int checkJonasProperties() {
        // Check jonas.properties and <datasource>.properties
        try {
            // Check jonas.properties
            boolean isServiceDbm = false;
            JProp jonasProps = JProp.getInstance();

            System.out.println("");
            System.out.println("- JOnAS Services:");
            System.out.println("   " + jonasProps.getValue("jonas.services", "none"));

            String[] services = jonasProps.getValueAsArray("jonas.services");

            if (services != null) {
                for (int i = 0; i < services.length; i++) {
                    if (DBM_SERVICE.equals(services[i])) {
                        isServiceDbm = true;
                    }
                    String serviceClass = jonasProps.getValue("jonas.service." + services[i] + ".class");
                    if (serviceClass == null) {
                        System.err.println("JOnAS environment ERROR: 'jonas.service." + services[i] + ".class' property not defined");
                        return ENV_ERROR;
                    } else {
                        if (!classIsAccessible(serviceClass)) {
                            System.err.println("JOnAS environment ERROR: '" + serviceClass + "' class not accessible ('" + services[i] + "' service class)");
                            return ENV_ERROR;
                        }
                    }
                }
            } else {
                System.err.println("JOnAS environment ERROR: jonas.services not defined in jonas.properties");
                return ENV_ERROR;
            }

            System.out.println("");
            System.out.println("- Contents of 'jonas.properties':");
            String jonasContent = jonasProps.toString();
            if (jonasContent.length() > 0) {
                System.out.println(jonasProps.toString());
            } else {
                System.err.println("JOnAS environment ERROR : jonas.properties empty");
                return ENV_ERROR;
            }
            if (isServiceDbm) {
                // Check <datasource>.properties
                String dsList = jonasProps.getValue(DATASOURCES, "");
                StringTokenizer st = new StringTokenizer(dsList, ",");
                String dsName = null;
                while (st.hasMoreTokens()) {
                    try {
                        dsName = st.nextToken().trim();
                        JProp dsProps = JProp.getInstance(dsName);
                        System.out.println("");
                        System.out.println("- Contents of '" + dsName + ".properties':");
                        System.out.println(dsProps.toString());
                        String driverName = dsProps.getValue(DATASOURCE_CLASS_NAME, "");
                        if (!classIsAccessible(driverName)) {
                            System.err.println("JOnAS environment ERROR: '" + driverName + "' class not accessible ('" + dsName + "' datasource's driver)");
                            return ENV_ERROR;
                        }
                    } catch (Exception e) {
                        System.err.println("JOnAS environment ERROR: '" + dsName + ".properties' "
                                + "not available in JONAS_BASE/conf");
                        return ENV_ERROR;
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("JOnAS environment ERROR: 'jonas.properties' not accessible (" + e + ")");
            return ENV_ERROR;
        }
        return ENV_OK;
    }

    /**
     * Check jonas's Realm
     * @return : result
     */
    private static int checkRealm() {
        System.out.println("\n- Check 'jonas-realm.xml':");

        try {
            String jonasBase = JProp.getJonasBase();
            File f = new File(jonasBase + File.separator + "conf" + File.separator + "jonas-realm.xml");
            if (!f.exists()) {
                System.err.println("The file jonas-realm.xml was not found in your JONAS_BASE/conf directory");
                return ENV_ERROR;
            } else {
                System.out.println("  File is present.");
                return ENV_OK;
            }
        } catch (Exception fe) {
            System.err.println("Error while checking the jonas-realm.xml file : " + fe.getMessage());
            return ENV_ERROR;
        }
    }


    /**
     * Check not presence both of jms service and joram's rar
     * @return : result
     */
    private static int checkJoramRar() {
        boolean isServiceJms = false;
        boolean isServiceResource = false;
        boolean isJoramRarAutoloadedFromJprop = false;
        boolean isJoramRarAutoloadedFromAutoloadDir = false;
        JProp jonasProps = null;
        String jonasBase = null;

        System.out.println("\n- Check 'JORAM configuration':");

        try {
            jonasProps = JProp.getInstance();
        } catch (Exception e) {
            System.err.println("JOnAS environment ERROR: 'jonas.properties' not accessible (" + e + ")");
            return ENV_ERROR;
        }

        try {
            String[] services = jonasProps.getValueAsArray("jonas.services");

            if (services != null) {
                for (int i = 0; i < services.length; i++) {
                    if (JMS_SERVICE.equals(services[i])) {
                        isServiceJms = true;
                    }
                    if (RESOURCE_SERVICE.equals(services[i])) {
                        isServiceResource = true;
                    }
                }
            } else {
                System.err.println("JOnAS environment ERROR: jonas.services not defined in " + "jonas.properties");
                return ENV_ERROR;
            }
        } catch (Exception e) {
            System.err.println("JOnAS environment ERROR: 'jonas.properties' not accessible (" + e + ")");
            return ENV_ERROR;
        }
        try {
            String[] resources = jonasProps.getValueAsArray("jonas.service.resource.resources");
            if (resources != null) {
                for (int i = 0; i < resources.length; i++) {
                    if (JORAM_RAR.equals(resources[i])) {
                        isJoramRarAutoloadedFromJprop = true;
                    }
                    if ((JORAM_RAR + ".rar").equals(resources[i])) {
                        isJoramRarAutoloadedFromJprop = true;
                    }
                }
            } else {
                System.err.println("JOnAS environment ERROR: jonas.service.resource.resources not defined in "
                        + "jonas.properties");
                return ENV_ERROR;
            }

        } catch (Exception e) {
            System.err
                    .println("JOnAS environment ERROR: 'jonas.service.resource.resources' not accessible (" + e + ")");
            return ENV_ERROR;
        }
        String[] resourcesAutoloadDir = null;
        try {
            resourcesAutoloadDir = jonasProps.getValueAsArray("jonas.service.resource.autoloaddir");
        } catch (Exception e) {
            System.err.println("JOnAS environment ERROR: 'jonas.service.resource.autoloaddir' not accessible (" + e
                    + ")");
            return ENV_ERROR;
        }
        jonasBase = JProp.getJonasBase();

        if (resourcesAutoloadDir != null) {
            for (int i = 0; i < resourcesAutoloadDir.length; i++) {
                File dir = new File(jonasBase + File.separator + "rars" + File.separator + resourcesAutoloadDir[i]);
                if (!dir.exists() || !dir.isDirectory()) {
                    System.err.println("JOnAS environment ERROR: 'resource service configuration error' - "
                            + dir.getName() + " access error");
                    return ENV_ERROR;
                }
                File[] files = dir.listFiles();
                for (int j = 0; j < files.length; j++) {
                    if ((JORAM_RAR + ".rar").equals(files[j].getName())) {
                        isJoramRarAutoloadedFromAutoloadDir = true;
                    }
                }
            }
        } else {
            System.err.println("JOnAS environment ERROR: jonas.service.resource.autoloaddir not defined in "
                    + "jonas.properties");
            return ENV_ERROR;
        }

        if (isJoramRarAutoloadedFromJprop && isJoramRarAutoloadedFromAutoloadDir) {
            System.err
                    .println("JOnAS environment ERROR: joram rar loaded twice in conf : jonas.properties and resource.autoloaddir");
            return ENV_ERROR;
        }

        if (isServiceJms && isServiceResource && (isJoramRarAutoloadedFromJprop || isJoramRarAutoloadedFromAutoloadDir)) {
            System.err.println("JOnAS environment ERROR: joram rar and jms service must be exclusive'");
            return ENV_ERROR;
        }

        // Check presence of joramAdmin.xml and not presence of joram-admin.cfg
        if (isServiceResource) {
            try {
                File NewJoramAdminCfgFile = new File(jonasBase + File.separator + "conf" + File.separator + JORAM_RAR_ADMIN_CFG_FILE);
                File DepJoramAdminCfgFile = new File(jonasBase + File.separator + "conf" + File.separator + JORAM_RAR_ADMIN_CFG_FILE_DEPRECATED);

                if (!NewJoramAdminCfgFile.exists()) {
                    System.err.println("The file " + JORAM_RAR_ADMIN_CFG_FILE + " was not found in your JONAS_BASE/conf directory");
                    return ENV_ERROR;
                }
                if (DepJoramAdminCfgFile.exists()) {
                    System.err.println("The file " + JORAM_RAR_ADMIN_CFG_FILE_DEPRECATED + " was found in your JONAS_BASE/conf directory and may cause confusing with the " + JORAM_RAR_ADMIN_CFG_FILE + "file.");
                    return ENV_ERROR;
                }
            } catch (Exception fe) {
                System.err.println("Error while checking the JORAM RA's configuration files : " + fe.getMessage());
                return ENV_ERROR;
            }
        }

        System.out.println("Ok");

        return ENV_OK;
    }
}
