org.opennms.install.Installer.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.install.Installer.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2004-2014 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2014 The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * OpenNMS(R) 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with OpenNMS(R).  If not, see:
 *      http://www.gnu.org/licenses/
 *
 * For more information contact:
 *     OpenNMS(R) Licensing <license@opennms.org>
 *     http://www.opennms.org/
 *     http://www.opennms.com/
 *******************************************************************************/

package org.opennms.install;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.net.InetAddress;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.sql.DataSource;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.io.IOUtils;
import org.opennms.bootstrap.Bootstrap;
import org.opennms.core.db.DataSourceConfigurationFactory;
import org.opennms.core.db.install.InstallerDb;
import org.opennms.core.db.install.SimpleDataSource;
import org.opennms.core.logging.Logging;
import org.opennms.core.schema.ExistingResourceAccessor;
import org.opennms.core.schema.Migration;
import org.opennms.core.schema.Migrator;
import org.opennms.core.utils.ConfigFileConstants;
import org.opennms.core.utils.ProcessExec;
import org.opennms.netmgt.config.opennmsDataSources.JdbcDataSource;
import org.opennms.netmgt.icmp.Pinger;
import org.opennms.netmgt.icmp.PingerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;

/*
 * TODO:
 * - Fix all of the XXX items (some coding, some discussion)
 * - Change the Exceptions to something more reasonable
 * - Do exception handling where it makes sense (give users reasonable error messages for common problems)
 * - Javadoc
 */

/**
 * <p>Installer class.</p>
 */
public class Installer {

    private static final Logger LOG = LoggerFactory.getLogger(Installer.class);

    static final String LIBRARY_PROPERTY_FILE = "libraries.properties";

    String m_opennms_home = null;
    boolean m_update_database = false;
    boolean m_do_inserts = false;
    boolean m_skip_constraints = false;
    boolean m_update_iplike = false;
    boolean m_update_unicode = false;
    boolean m_do_full_vacuum = false;
    boolean m_do_vacuum = false;
    boolean m_install_webapp = false;
    boolean m_fix_constraint = false;
    boolean m_force = false;
    boolean m_ignore_not_null = false;
    boolean m_ignore_database_version = false;
    boolean m_do_not_revert = false;
    boolean m_remove_database = false;
    boolean m_skip_upgrade_tools = false;

    String m_etc_dir = "";
    String m_tomcat_conf = null;
    String m_webappdir = null;
    String m_import_dir = null;
    String m_install_servletdir = null;
    String m_library_search_path = null;
    String m_fix_constraint_name = null;
    boolean m_fix_constraint_remove_rows = false;

    protected Options options = new Options();
    protected CommandLine m_commandLine;
    private Migration m_migration = new Migration();
    private Migrator m_migrator = new Migrator();

    Properties m_properties = null;

    String m_required_options = "At least one of -d, -i, -s, -y, -C, or -T is required.";

    private InstallerDb m_installerDb = new InstallerDb();

    private static final String OPENNMS_DATA_SOURCE_NAME = "opennms";

    private static final String ADMIN_DATA_SOURCE_NAME = "opennms-admin";

    /**
     * <p>Constructor for Installer.</p>
     */
    public Installer() {
    }

    /**
     * <p>install</p>
     *
     * @param argv an array of {@link java.lang.String} objects.
     * @throws java.lang.Exception if any.
     */
    public void install(final String[] argv) throws Exception {
        printHeader();
        loadProperties();
        parseArguments(argv);

        final boolean doDatabase = (m_update_database || m_do_inserts || m_update_iplike || m_update_unicode
                || m_fix_constraint);

        if (!doDatabase && m_tomcat_conf == null && !m_install_webapp && m_library_search_path == null) {
            usage(options, m_commandLine, "Nothing to do.  Use -h for help.", null);
            System.exit(1);
        }

        if (doDatabase) {
            final File cfgFile = ConfigFileConstants
                    .getFile(ConfigFileConstants.OPENNMS_DATASOURCE_CONFIG_FILE_NAME);

            InputStream is = new FileInputStream(cfgFile);
            final JdbcDataSource adminDsConfig = new DataSourceConfigurationFactory(is)
                    .getJdbcDataSource(ADMIN_DATA_SOURCE_NAME);
            final DataSource adminDs = new SimpleDataSource(adminDsConfig);
            is.close();

            is = new FileInputStream(cfgFile);
            final JdbcDataSource dsConfig = new DataSourceConfigurationFactory(is)
                    .getJdbcDataSource(OPENNMS_DATA_SOURCE_NAME);
            final DataSource ds = new SimpleDataSource(dsConfig);
            is.close();

            m_installerDb.setForce(m_force);
            m_installerDb.setIgnoreNotNull(m_ignore_not_null);
            m_installerDb.setNoRevert(m_do_not_revert);
            m_installerDb.setAdminDataSource(adminDs);
            m_installerDb.setPostgresOpennmsUser(dsConfig.getUserName());
            m_installerDb.setDataSource(ds);
            m_installerDb.setDatabaseName(dsConfig.getDatabaseName());

            m_migrator.setDataSource(ds);
            m_migrator.setAdminDataSource(adminDs);
            m_migrator.setValidateDatabaseVersion(!m_ignore_database_version);

            m_migration.setDatabaseName(dsConfig.getDatabaseName());
            m_migration.setSchemaName(dsConfig.getSchemaName());
            m_migration.setAdminUser(adminDsConfig.getUserName());
            m_migration.setAdminPassword(adminDsConfig.getPassword());
            m_migration.setDatabaseUser(dsConfig.getUserName());
            m_migration.setDatabasePassword(dsConfig.getPassword());
            m_migration.setChangeLog("changelog.xml");
        }

        checkIPv6();

        /*
         * Make sure we can execute the rrdtool binary when the
         * JniRrdStrategy is enabled.
         */

        boolean using_jni_rrd_strategy = System.getProperty("org.opennms.rrd.strategyClass", "")
                .contains("JniRrdStrategy");

        if (using_jni_rrd_strategy) {
            File rrd_binary = new File(System.getProperty("rrd.binary"));
            if (!rrd_binary.canExecute()) {
                throw new Exception("Cannot execute the rrdtool binary '" + rrd_binary.getAbsolutePath()
                        + "' required by the current RRD strategy. Update the rrd.binary field in opennms.properties appropriately.");
            }
        }

        /*
         * make sure we can load the ICMP library before we go any farther
         */

        if (!Boolean.getBoolean("skip-native")) {
            String icmp_path = findLibrary("jicmp", m_library_search_path, false);
            String icmp6_path = findLibrary("jicmp6", m_library_search_path, false);
            String jrrd_path = findLibrary("jrrd", m_library_search_path, false);
            String jrrd2_path = findLibrary("jrrd2", m_library_search_path, false);
            writeLibraryConfig(icmp_path, icmp6_path, jrrd_path, jrrd2_path);
        }

        /*
         * Everything needs to use the administrative data source until we
         * verify that the opennms database is created below (and where we
         * create it if it doesn't already exist).
         */

        verifyFilesAndDirectories();

        if (m_install_webapp) {
            checkWebappOldOpennmsDir();
            checkServerXmlOldOpennmsContext();
        }

        if (m_update_database || m_fix_constraint) {
            // OLDINSTALL m_installerDb.readTables();
        }

        m_installerDb.disconnect();
        if (doDatabase) {
            m_migrator.validateDatabaseVersion();

            System.out.println(
                    String.format("* using '%s' as the PostgreSQL user for OpenNMS", m_migration.getAdminUser()));
            System.out.println(String.format("* using '%s' as the PostgreSQL database name for OpenNMS",
                    m_migration.getDatabaseName()));
            if (m_migration.getSchemaName() != null) {
                System.out.println(String.format("* using '%s' as the PostgreSQL schema name for OpenNMS",
                        m_migration.getSchemaName()));
            }
        }

        if (m_update_database) {
            m_migrator.prepareDatabase(m_migration);
        }

        if (doDatabase) {
            m_installerDb.checkUnicode();
        }

        handleConfigurationChanges();

        final GenericApplicationContext context = new GenericApplicationContext();
        context.setClassLoader(Bootstrap.loadClasses(new File(m_opennms_home), true));

        if (m_update_database) {
            m_installerDb.databaseSetUser();
            m_installerDb.disconnect();

            for (final Resource resource : context.getResources("classpath*:/changelog.xml")) {
                System.out.println("- Running migration for changelog: " + resource.getDescription());
                m_migration.setAccessor(new ExistingResourceAccessor(resource));
                m_migrator.migrate(m_migration);
            }
        }

        if (m_update_unicode) {
            System.out.println("WARNING: the -U option is deprecated, it does nothing now");
        }

        if (m_do_vacuum) {
            m_installerDb.vacuumDatabase(m_do_full_vacuum);
        }

        if (m_install_webapp) {
            installWebApp();
        }

        if (m_tomcat_conf != null) {
            updateTomcatConf();
        }

        if (m_update_iplike) {
            m_installerDb.updateIplike();
        }

        if (m_update_database && m_remove_database) {
            m_installerDb.disconnect();
            m_installerDb.databaseRemoveDB();
        }

        if (doDatabase) {
            m_installerDb.disconnect();
        }

        if (m_update_database) {
            createConfiguredFile();
        }

        System.out.println();
        System.out.println("Installer completed successfully!");

        if (!m_skip_upgrade_tools) {
            System.setProperty("opennms.manager.class", "org.opennms.upgrade.support.Upgrade");
            Bootstrap.main(new String[] {});
        }

        context.close();
    }

    private void checkIPv6() {
        final IPv6Validator v6Validator = new IPv6Validator();
        if (!v6Validator.isPlatformIPv6Ready()) {
            System.out.println("Your OS does not support IPv6.");
        }
    }

    private void handleConfigurationChanges() {
        File etcDir = new File(m_opennms_home + File.separator + "etc");
        File importDir = new File(m_import_dir);
        File[] files = etcDir.listFiles(getImportFileFilter());

        if (!importDir.exists()) {
            System.out.print("- Creating imports directory (" + importDir.getAbsolutePath() + "... ");
            if (!importDir.mkdirs()) {
                System.out.println("FAILED");
                System.exit(1);
            }
            System.out.println("OK");
        }

        System.out.print("- Checking for old import files in " + etcDir.getAbsolutePath() + "... ");
        if (files.length > 0) {
            System.out.println("FOUND");
            for (File f : files) {
                String newFileName = f.getName().replace("imports-", "");
                File newFile = new File(importDir, newFileName);
                System.out.print("  - moving " + f.getName() + " to " + importDir.getPath() + "... ");
                if (f.renameTo(newFile)) {
                    System.out.println("OK");
                } else {
                    System.out.println("FAILED");
                }
            }
        } else {
            System.out.println("DONE");
        }
    }

    private FilenameFilter getImportFileFilter() {
        return new FilenameFilter() {

            public boolean accept(File dir, String name) {
                return name.matches("imports-.*\\.xml");
            }

        };
    }

    /**
     * <p>createConfiguredFile</p>
     *
     * @throws java.io.IOException if any.
     */
    public void createConfiguredFile() throws IOException {
        File f = new File(m_opennms_home + File.separator + "etc" + File.separator + "configured");
        if (!f.createNewFile()) {
            LOG.warn("Could not create file: {}", f.getPath());
        }
    }

    /**
     * <p>printHeader</p>
     */
    public void printHeader() {
        System.out.println("==============================================================================");
        System.out.println("OpenNMS Installer");
        System.out.println("==============================================================================");
        System.out.println("");
        System.out.println("Configures PostgreSQL tables, users, and other miscellaneous settings.");
        System.out.println("");
    }

    /**
     * <p>loadProperties</p>
     *
     * @throws java.lang.Exception if any.
     */
    public void loadProperties() throws Exception {
        m_properties = new Properties();
        m_properties.load(Installer.class.getResourceAsStream("/installer.properties"));

        /*
         * Do this if we want to merge our properties with the system
         * properties...
         */
        final Properties sys = System.getProperties();
        m_properties.putAll(sys);

        m_opennms_home = fetchProperty("install.dir");
        m_etc_dir = fetchProperty("install.etc.dir");

        loadEtcPropertiesFile("opennms.properties");
        // Used to retrieve 'org.opennms.rrd.strategyClass'
        loadEtcPropertiesFile("rrd-configuration.properties");

        m_install_servletdir = fetchProperty("install.servlet.dir");
        try {
            m_import_dir = fetchProperty("importer.requisition.dir");
        } catch (Exception e) {
            m_import_dir = m_opennms_home + File.separator + "etc" + File.separator + "imports";
        }

        final String pg_lib_dir = m_properties.getProperty("install.postgresql.dir");

        if (pg_lib_dir != null) {
            m_installerDb.setPostgresPlPgsqlLocation(pg_lib_dir + File.separator + "plpgsql");
            m_installerDb.setPostgresIpLikeLocation(pg_lib_dir + File.separator + "iplike");
        }

        m_installerDb.setStoredProcedureDirectory(m_etc_dir);
        m_installerDb.setCreateSqlLocation(m_etc_dir + File.separator + "create.sql");
    }

    private void loadEtcPropertiesFile(final String propertiesFile) throws IOException {
        try {
            final Properties opennmsProperties = new Properties();
            final InputStream ois = new FileInputStream(m_etc_dir + File.separator + propertiesFile);
            opennmsProperties.load(ois);
            // We only want to put() things that weren't already overridden in installer.properties
            for (final Entry<Object, Object> p : opennmsProperties.entrySet()) {
                if (!m_properties.containsKey(p.getKey())) {
                    m_properties.put(p.getKey(), p.getValue());
                }
            }
        } catch (final FileNotFoundException e) {
            System.out.println("WARNING: unable to load " + m_etc_dir + File.separator + propertiesFile);
        }
    }

    /**
     * <p>fetchProperty</p>
     *
     * @param property a {@link java.lang.String} object.
     * @return a {@link java.lang.String} object.
     * @throws java.lang.Exception if any.
     */
    public String fetchProperty(String property) throws Exception {
        String value;

        if ((value = m_properties.getProperty(property)) == null) {
            throw new Exception(
                    "property \"" + property + "\" not set " + "from bundled installer.properties file");
        }

        return value;
    }

    /**
     * <p>parseArguments</p>
     *
     * @param argv an array of {@link java.lang.String} objects.
     * @throws java.lang.Exception if any.
     */
    public void parseArguments(String[] argv) throws Exception {

        options.addOption("h", "help", false, "this help");

        // database-related options
        options.addOption("d", "do-database", false, "perform database actions");

        options.addOption("Z", "remove-database", false, "remove the OpenNMS database");

        options.addOption("u", "username", true, "username of the database account (default: 'opennms')");
        options.addOption("p", "password", true, "password of the database account (default: 'opennms')");
        options.addOption("a", "admin-username", true,
                "username of the database administrator (default: 'postgres')");
        options.addOption("A", "admin-password", true, "password of the database administrator (default: '')");
        options.addOption("D", "database-url", true,
                "JDBC database URL (default: jdbc:postgresql://localhost:5432/");
        options.addOption("P", "database-name", true, "name of the PostgreSQL database (default: opennms)");

        options.addOption("c", "clean-database", false, "this option does nothing");
        options.addOption("i", "insert-data", false,
                "insert (or upgrade) default data including database and XML configuration");
        options.addOption("s", "stored-procedure", false, "add the IPLIKE stored procedure if it's missing");
        options.addOption("U", "unicode", false, "upgrade the database to Unicode (deprecated, does nothing)");
        options.addOption("v", "vacuum", false, "vacuum (optimize) the database");
        options.addOption("f", "vacuum-full", false, "vacuum full the database (recovers unused disk space)");
        options.addOption("N", "ignore-not-null", false, "ignore NOT NULL constraint when transforming data");
        options.addOption("Q", "ignore-database-version", false, "disable the database version check");

        options.addOption("x", "database-debug", false, "turn on debugging for the database data transformation");
        options.addOption("R", "do-not-revert", false, "do not revert a table to the original if an error occurs");
        options.addOption("n", "skip-constraint", false, "");
        options.addOption("C", "repair-constraint", true,
                "fix rows that violate the specified constraint (sets key column to NULL)");
        options.addOption("X", "drop-constraint", false,
                "drop rows that match the constraint specified in -C, instead of fixing them");
        options.addOption("e", "extended-repairs", false, "enable extended repairs of old schemas");
        // tomcat-related options
        options.addOption("y", "do-webapp", false, "install web application (see '-w')");
        options.addOption("T", "tomcat-conf", true, "location of tomcat.conf");
        options.addOption("w", "tomcat-context", true,
                "location of the tomcat context (eg, conf/Catalina/localhost)");

        // general installation options
        options.addOption("l", "library-path", true,
                "library search path (directories separated by '" + File.pathSeparator + "')");
        options.addOption("r", "rpm-install", false, "RPM install (deprecated)");

        // upgrade tools options
        options.addOption("S", "skip-upgrade-tools", false,
                "Skip the execution of the upgrade tools (post-processing tasks)");

        CommandLineParser parser = new PosixParser();
        m_commandLine = parser.parse(options, argv);

        if (m_commandLine.hasOption("h")) {
            usage(options, m_commandLine);
            System.exit(0);
        }

        options.addOption("u", "username", true, "replaced by opennms-datasources.xml");
        options.addOption("p", "password", true, "replaced by opennms-datasources.xml");
        options.addOption("a", "admin-username", true, "replaced by opennms-datasources.xml");
        options.addOption("A", "admin-password", true, "replaced by opennms-datasources.xml");
        options.addOption("D", "database-url", true, "replaced by opennms-datasources.xml");
        options.addOption("P", "database-name", true, "replaced by opennms-datasources.xml");

        if (m_commandLine.hasOption("c")) {
            usage(options, m_commandLine,
                    "The 'c' option was deprecated in 1.6, and disabled in 1.8.  You should backup and then drop the database before running install to reset your data.",
                    null);
            System.exit(1);
        }

        if (m_commandLine.hasOption("u") || m_commandLine.hasOption("p") || m_commandLine.hasOption("a")
                || m_commandLine.hasOption("A") || m_commandLine.hasOption("D") || m_commandLine.hasOption("P")) {
            usage(options, m_commandLine,
                    "The 'u', 'p', 'a', 'A', 'D', and 'P' options have all been superceded.\nPlease edit $OPENNMS_HOME/etc/opennms-datasources.xml instead.",
                    null);
            System.exit(1);
        }

        //        m_force = m_commandLine.hasOption("c");
        m_fix_constraint = m_commandLine.hasOption("C");
        m_fix_constraint_name = m_commandLine.getOptionValue("C");
        if (m_commandLine.hasOption("e")) {
            System.setProperty("opennms.contexts", "production,repair");
        }
        m_update_database = m_commandLine.hasOption("d");
        m_remove_database = m_commandLine.hasOption("Z");
        m_do_full_vacuum = m_commandLine.hasOption("f");
        m_do_inserts = m_commandLine.hasOption("i");
        m_library_search_path = m_commandLine.getOptionValue("l", m_library_search_path);
        m_skip_constraints = m_commandLine.hasOption("n");
        m_ignore_not_null = m_commandLine.hasOption("N");
        m_ignore_database_version = m_commandLine.hasOption("Q");
        m_do_not_revert = m_commandLine.hasOption("R");
        m_update_iplike = m_commandLine.hasOption("s");
        m_tomcat_conf = m_commandLine.getOptionValue("T", m_tomcat_conf);
        m_update_unicode = m_commandLine.hasOption("U");
        m_do_vacuum = m_commandLine.hasOption("v");
        m_webappdir = m_commandLine.getOptionValue("w", m_webappdir);
        m_installerDb.setDebug(m_commandLine.hasOption("x"));
        if (m_commandLine.hasOption("x")) {
            m_migrator.enableDebug();
        }
        m_fix_constraint_remove_rows = m_commandLine.hasOption("X");
        m_install_webapp = m_commandLine.hasOption("y");
        m_skip_upgrade_tools = m_commandLine.hasOption("S");

        if (m_commandLine.getArgList().size() > 0) {
            usage(options, m_commandLine,
                    "Unknown command-line arguments: " + Arrays.toString(m_commandLine.getArgs()), null);
            System.exit(1);
        }
    }

    /**
     * <p>verifyFilesAndDirectories</p>
     *
     * @throws java.io.FileNotFoundException if any.
     */
    public void verifyFilesAndDirectories() throws FileNotFoundException {
        if (m_update_database) {
            verifyFileExists(true, m_installerDb.getStoredProcedureDirectory(), "SQL directory",
                    "install.etc.dir property");

            verifyFileExists(false, m_installerDb.getCreateSqlLocation(), "create.sql", "install.etc.dir property");
        }

        if (m_tomcat_conf != null) {
            verifyFileExists(false, m_tomcat_conf, "Tomcat startup configuration file tomcat4.conf", "-T option");
        }

        if (m_install_webapp) {
            verifyFileExists(true, m_webappdir, "Tomcat context directory", "-w option");

            verifyFileExists(true, m_install_servletdir, "OpenNMS servlet directory",
                    "install.servlet.dir property");
        }
    }

    /**
     * <p>verifyFileExists</p>
     *
     * @param isDir a boolean.
     * @param file a {@link java.lang.String} object.
     * @param description a {@link java.lang.String} object.
     * @param option a {@link java.lang.String} object.
     * @throws java.io.FileNotFoundException if any.
     */
    public void verifyFileExists(boolean isDir, String file, String description, String option)
            throws FileNotFoundException {
        File f;

        if (file == null) {
            throw new FileNotFoundException("The user most provide the location of " + description
                    + ", but this is not specified.  Use the " + option + " to specify this file.");
        }

        System.out.print("- using " + description + "... ");

        f = new File(file);

        if (!f.exists()) {
            throw new FileNotFoundException(description + " does not exist at \"" + file + "\".  Use the " + option
                    + " to specify another location.");
        }

        if (!isDir) {
            if (!f.isFile()) {
                throw new FileNotFoundException(description + " not a file at \"" + file + "\".  Use the " + option
                        + " to specify another file.");
            }
        } else {
            if (!f.isDirectory()) {
                throw new FileNotFoundException(description + " not a directory at \"" + file + "\".  Use the "
                        + option + " to specify " + "another directory.");
            }
        }

        System.out.println(f.getAbsolutePath());
    }

    /**
     * <p>checkWebappOldOpennmsDir</p>
     *
     * @throws java.lang.Exception if any.
     */
    public void checkWebappOldOpennmsDir() throws Exception {
        File f = new File(m_webappdir + File.separator + "opennms");

        System.out.print("- Checking for old opennms webapp directory in " + f.getAbsolutePath() + "... ");

        if (f.exists()) {
            throw new Exception("Old OpenNMS web application exists: " + f.getAbsolutePath()
                    + ".  You need to remove this " + "before continuing.");
        }

        System.out.println("OK");
    }

    /**
     * <p>checkServerXmlOldOpennmsContext</p>
     *
     * @throws java.lang.Exception if any.
     */
    public void checkServerXmlOldOpennmsContext() throws Exception {
        String search_regexp = "(?ms).*<Context\\s+path=\"/opennms\".*";
        StringBuffer b = new StringBuffer();

        File f = new File(
                m_webappdir + File.separator + ".." + File.separator + "conf" + File.separator + "server.xml");

        System.out.print("- Checking for old opennms context in " + f.getAbsolutePath() + "... ");

        if (!f.exists()) {
            System.out.println("DID NOT CHECK (file does not exist)");
            return;
        }

        Reader fr = new InputStreamReader(new FileInputStream(f), "UTF-8");
        BufferedReader r = new BufferedReader(fr);
        String line;

        while ((line = r.readLine()) != null) {
            b.append(line);
            b.append("\n");
        }
        r.close();
        fr.close();

        if (b.toString().matches(search_regexp)) {
            throw new Exception("Old OpenNMS context found in " + f.getAbsolutePath()
                    + ".  You must remove this context from server.xml and re-run the installer.");
        }

        System.out.println("OK");

        return;
    }

    /**
     * <p>installWebApp</p>
     *
     * @throws java.lang.Exception if any.
     */
    public void installWebApp() throws Exception {
        System.out.println("- Install OpenNMS webapp... ");

        copyFile(m_install_servletdir + File.separator + "META-INF" + File.separator + "context.xml",
                m_webappdir + File.separator + "opennms.xml", "web application context", false);

        System.out.println("- Installing OpenNMS webapp... DONE");
    }

    /**
     * <p>copyFile</p>
     *
     * @param source a {@link java.lang.String} object.
     * @param destination a {@link java.lang.String} object.
     * @param description a {@link java.lang.String} object.
     * @param recursive a boolean.
     * @throws java.lang.Exception if any.
     */
    public void copyFile(String source, String destination, String description, boolean recursive)
            throws Exception {
        File sourceFile = new File(source);
        File destinationFile = new File(destination);

        if (!sourceFile.exists()) {
            throw new Exception("source file (" + source + ") does not exist!");
        }
        if (!sourceFile.isFile()) {
            throw new Exception("source file (" + source + ") is not a file!");
        }
        if (!sourceFile.canRead()) {
            throw new Exception("source file (" + source + ") is not readable!");
        }
        if (destinationFile.exists()) {
            System.out.print("  - " + destination + " exists, removing... ");
            if (destinationFile.delete()) {
                System.out.println("REMOVED");
            } else {
                System.out.println("FAILED");
                throw new Exception("unable to delete existing file: " + sourceFile);
            }
        }

        System.out.print("  - copying " + source + " to " + destination + "... ");
        if (!destinationFile.getParentFile().exists()) {
            if (!destinationFile.getParentFile().mkdirs()) {
                throw new Exception("unable to create directory: " + destinationFile.getParent());
            }
        }
        if (!destinationFile.createNewFile()) {
            throw new Exception("unable to create file: " + destinationFile);
        }
        FileChannel from = null;
        FileInputStream fisFrom = null;
        FileChannel to = null;
        FileOutputStream fisTo = null;
        try {
            fisFrom = new FileInputStream(sourceFile);
            from = fisFrom.getChannel();
            fisTo = new FileOutputStream(destinationFile);
            to = fisTo.getChannel();
            to.transferFrom(from, 0, from.size());
        } catch (FileNotFoundException e) {
            throw new Exception("unable to copy " + sourceFile + " to " + destinationFile, e);
        } finally {
            IOUtils.closeQuietly(fisTo);
            IOUtils.closeQuietly(to);
            IOUtils.closeQuietly(fisFrom);
            IOUtils.closeQuietly(from);
        }
        System.out.println("DONE");
    }

    /**
     * <p>installLink</p>
     *
     * @param source a {@link java.lang.String} object.
     * @param destination a {@link java.lang.String} object.
     * @param description a {@link java.lang.String} object.
     * @param recursive a boolean.
     * @throws java.lang.Exception if any.
     */
    public void installLink(String source, String destination, String description, boolean recursive)
            throws Exception {

        String[] cmd;
        ProcessExec e = new ProcessExec(System.out, System.out);

        if (new File(destination).exists()) {
            System.out.print("  - " + destination + " exists, removing... ");
            removeFile(destination, description, recursive);
            System.out.println("REMOVED");
        }

        System.out.print("  - creating link to " + destination + "... ");

        cmd = new String[4];
        cmd[0] = "ln";
        cmd[1] = "-sf";
        cmd[2] = source;
        cmd[3] = destination;

        if (e.exec(cmd) != 0) {
            throw new Exception("Non-zero exit value returned while " + "linking " + description + ", " + source
                    + " into " + destination);
        }

        System.out.println("DONE");
    }

    /**
     * <p>updateTomcatConf</p>
     *
     * @throws java.lang.Exception if any.
     */
    public void updateTomcatConf() throws Exception {
        File f = new File(m_tomcat_conf);

        // XXX give the user the option to set the user to something else?
        // if so, should we chown the appropriate OpenNMS files to the
        // tomcat user?
        //
        // XXX should we have the option to automatically try to determine
        // the tomcat user and chown the OpenNMS files to that user?

        System.out.print("- setting tomcat4 user to 'root'... ");

        BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(f), "UTF-8"));
        StringBuffer b = new StringBuffer();
        String line;

        while ((line = r.readLine()) != null) {
            if (line.startsWith("TOMCAT_USER=")) {
                b.append("TOMCAT_USER=\"root\"\n");
            } else {
                b.append(line);
                b.append("\n");
            }
        }
        r.close();

        if (!f.renameTo(new File(m_tomcat_conf + ".before-opennms-" + System.currentTimeMillis()))) {
            LOG.warn("Could not rename file: {}", f.getPath());
        }

        f = new File(m_tomcat_conf);
        PrintWriter w = new PrintWriter(new FileOutputStream(f));

        w.print(b.toString());
        w.close();

        System.out.println("DONE");
    }

    /**
     * <p>removeFile</p>
     *
     * @param destination a {@link java.lang.String} object.
     * @param description a {@link java.lang.String} object.
     * @param recursive a boolean.
     * @throws java.io.IOException if any.
     * @throws java.lang.InterruptedException if any.
     * @throws java.lang.Exception if any.
     */
    public void removeFile(String destination, String description, boolean recursive)
            throws IOException, InterruptedException, Exception {
        String[] cmd;
        ProcessExec e = new ProcessExec(System.out, System.out);

        if (recursive) {
            cmd = new String[3];
            cmd[0] = "rm";
            cmd[1] = "-r";
            cmd[2] = destination;
        } else {
            cmd = new String[2];
            cmd[0] = "rm";
            cmd[1] = destination;
        }
        if (e.exec(cmd) != 0) {
            throw new Exception("Non-zero exit value returned while " + "removing " + description + ", "
                    + destination + ", using \"" + StringUtils.arrayToDelimitedString(cmd, " ") + "\"");
        }

        if (new File(destination).exists()) {
            usage(options, m_commandLine, "Could not delete existing " + description + ": " + destination, null);
            System.exit(1);
        }
    }

    private void usage(Options options, CommandLine cmd) {
        usage(options, cmd, null, null);
    }

    private void usage(Options options, CommandLine cmd, String error, Exception e) {
        HelpFormatter formatter = new HelpFormatter();
        PrintWriter pw = new PrintWriter(System.out);

        if (error != null) {
            pw.println("An error occurred: " + error + "\n");
        }

        formatter.printHelp("usage: install [options]", options);

        if (e != null) {
            pw.println(e.getMessage());
            e.printStackTrace(pw);
        }

        pw.close();
    }

    /**
     * <p>main</p>
     *
     * @param argv an array of {@link java.lang.String} objects.
     * @throws java.lang.Exception if any.
     */
    public static void main(String[] argv) throws Exception {
        final Map<String, String> mdc = Logging.getCopyOfContextMap();
        Logging.putPrefix("install");
        new Installer().install(argv);
        Logging.setContextMap(mdc);
    }

    /**
     * <p>checkServerVersion</p>
     *
     * @return a {@link java.lang.String} object.
     * @throws java.io.IOException if any.
     */
    public String checkServerVersion() throws IOException {
        File catalinaHome = new File(m_webappdir).getParentFile();
        String readmeVersion = getTomcatVersion(new File(catalinaHome, "README.txt"));
        String runningVersion = getTomcatVersion(new File(catalinaHome, "RUNNING.txt"));

        if (readmeVersion == null && runningVersion == null) {
            return null;
        } else if (readmeVersion != null && runningVersion != null) {
            return readmeVersion; // XXX what should be done here?
        } else if (readmeVersion != null && runningVersion == null) {
            return readmeVersion;
        } else {
            return runningVersion;
        }
    }

    /**
     * <p>getTomcatVersion</p>
     *
     * @param file a {@link java.io.File} object.
     * @return a {@link java.lang.String} object.
     * @throws java.io.IOException if any.
     */
    public String getTomcatVersion(File file) throws IOException {
        if (file == null || !file.exists()) {
            return null;
        }
        Pattern p = Pattern.compile("The Tomcat (\\S+) Servlet/JSP Container");
        BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
        for (int i = 0; i < 5; i++) {
            String line = in.readLine();
            if (line == null) { // EOF
                in.close();
                return null;
            }
            Matcher m = p.matcher(line);
            if (m.find()) {
                in.close();
                return m.group(1);
            }
        }

        in.close();
        return null;
    }

    /**
     * <p>findLibrary</p>
     *
     * @param libname a {@link java.lang.String} object.
     * @param path a {@link java.lang.String} object.
     * @param isRequired a boolean.
     * @return a {@link java.lang.String} object.
     * @throws java.lang.Exception if any.
     */
    public String findLibrary(String libname, String path, boolean isRequired) throws Exception {
        String fullname = System.mapLibraryName(libname);

        ArrayList<String> searchPaths = new ArrayList<String>();

        if (path != null) {
            for (String entry : path.split(File.pathSeparator)) {
                searchPaths.add(entry);
            }
        }

        try {
            File confFile = new File(
                    m_opennms_home + File.separator + "etc" + File.separator + LIBRARY_PROPERTY_FILE);
            final Properties p = new Properties();
            final InputStream is = new FileInputStream(confFile);
            p.load(is);
            is.close();
            for (final Object k : p.keySet()) {
                String key = (String) k;
                if (key.startsWith("opennms.library")) {
                    String value = p.getProperty(key);
                    value = value.replaceAll(File.separator + "[^" + File.separator + "]*$", "");
                    searchPaths.add(value);
                }
            }
        } catch (Throwable e) {
            // ok if we can't read these, we'll try to find them
        }

        if (System.getProperty("java.library.path") != null) {
            for (final String entry : System.getProperty("java.library.path").split(File.pathSeparator)) {
                searchPaths.add(entry);
            }
        }

        if (!System.getProperty("os.name").contains("Windows")) {
            String[] defaults = { "/usr/lib/jni", "/usr/lib", "/usr/local/lib", "/opt/NMSjicmp/lib/32",
                    "/opt/NMSjicmp/lib/64", "/opt/NMSjicmp6/lib/32", "/opt/NMSjicmp6/lib/64" };
            for (final String entry : defaults) {
                searchPaths.add(entry);
            }
        }

        System.out.println("- searching for " + fullname + ":");
        for (String dirname : searchPaths) {
            File entry = new File(dirname);

            if (entry.isFile()) {
                // if they specified a file, try the parent directory instead
                dirname = entry.getParent();
            }

            String fullpath = dirname + File.separator + fullname;
            if (loadLibrary(fullpath)) {
                return fullpath;
            }

            if (fullname.endsWith(".dylib")) {
                final String fullPathOldExtension = fullpath.replace(".dylib", ".jnilib");
                if (loadLibrary(fullPathOldExtension)) {
                    return fullPathOldExtension;
                }
            }
        }

        if (isRequired) {
            StringBuffer buf = new StringBuffer();
            for (final String pathEntry : System.getProperty("java.library.path").split(File.pathSeparator)) {
                buf.append(" ");
                buf.append(pathEntry);
            }

            throw new Exception("Failed to load the required " + libname
                    + " library that is required at runtime.  By default, we search the Java library path:"
                    + buf.toString() + ".  For more information, see http://www.opennms.org/index.php/" + libname);
        } else {
            System.out.println("- Failed to load the optional " + libname + " library.");
            System.out.println(
                    "  - This error is not fatal, since " + libname + " is only required for optional features.");
            System.out.println("  - For more information, see http://www.opennms.org/index.php/" + libname);
        }

        return null;
    }

    /**
     * <p>loadLibrary</p>
     *
     * @param path a {@link java.lang.String} object.
     * @return a boolean.
     */
    public boolean loadLibrary(final String path) {
        try {
            System.out.print("  - trying to load " + path + ": ");
            System.load(path);
            System.out.println("OK");
            return true;
        } catch (final UnsatisfiedLinkError ule) {
            System.out.println("NO");
        }
        return false;
    }

    /**
     * <p>writeLibraryConfig</p>
     *
     * @param jicmp_path a {@link java.lang.String} object.
     * @param jicmp6_path TODO
     * @param jrrd_path a {@link java.lang.String} object.
     * @throws java.io.IOException if any.
     */
    public void writeLibraryConfig(final String jicmp_path, final String jicmp6_path, final String jrrd_path,
            final String jrrd2_path) throws IOException {
        Properties libraryProps = new Properties();

        if (jicmp_path != null && jicmp_path.length() != 0) {
            libraryProps.put("opennms.library.jicmp", jicmp_path);
        }

        if (jicmp6_path != null && jicmp6_path.length() != 0) {
            libraryProps.put("opennms.library.jicmp6", jicmp6_path);
        }

        if (jrrd_path != null && jrrd_path.length() != 0) {
            libraryProps.put("opennms.library.jrrd", jrrd_path);
        }

        if (jrrd2_path != null && jrrd2_path.length() != 0) {
            libraryProps.put("opennms.library.jrrd2", jrrd2_path);
        }

        File f = null;
        try {
            f = new File(m_opennms_home + File.separator + "etc" + File.separator + LIBRARY_PROPERTY_FILE);
            if (!f.createNewFile()) {
                LOG.warn("Could not create file: {}", f.getPath());
            }
            FileOutputStream os = new FileOutputStream(f);
            libraryProps.store(os, null);
        } catch (IOException e) {
            System.out.println("unable to write to " + f.getPath());
            throw e;
        }
    }

    /**
     * <p>pingLocalhost</p>
     *
     * @throws java.io.IOException if any.
     */
    public void pingLocalhost() throws Exception {
        String host = "127.0.0.1";

        java.net.InetAddress addr = null;
        try {
            addr = InetAddress.getByName(host);
        } catch (java.net.UnknownHostException e) {
            System.out.println("UnknownHostException when looking up " + host + ".");
            throw e;

        }

        Pinger pinger;
        try {

            pinger = PingerFactory.getInstance();

        } catch (UnsatisfiedLinkError e) {
            System.out.println("UnsatisfiedLinkError while creating an ICMP Pinger.  Most likely failed to load "
                    + "libjicmp.so.  Try setting the property 'opennms.library.jicmp' to point at the "
                    + "full path name of the libjicmp.so shared library or switch to using the JnaPinger "
                    + "(e.g. 'java -Dopennms.library.jicmp=/some/path/libjicmp.so ...')\n"
                    + "You can also set the 'opennms.library.jicmp6' property in the same manner to specify "
                    + "the location of the JICMP6 library.");
            throw e;
        } catch (NoClassDefFoundError e) {
            System.out.println(
                    "NoClassDefFoundError while creating an IcmpSocket.  Most likely failed to load libjicmp.so"
                            + "or libjicmp6.so.");
            throw e;
        } catch (Exception e) {
            System.out.println("Exception while creating an Pinger.");
            throw e;
        }

        // using regular InetAddress toString here since is just printed for the users benefit
        System.out.print("Pinging " + host + " (" + addr + ")...");

        Number rtt = pinger.ping(addr);

        if (rtt == null) {
            System.out.println("failed.!");
        } else {
            System.out.printf("successful.. round trip time: %.3f ms%n", rtt.doubleValue() / 1000.0);
        }

    }
}