org.rhq.server.metrics.migrator.DataMigratorRunner.java Source code

Java tutorial

Introduction

Here is the source code for org.rhq.server.metrics.migrator.DataMigratorRunner.java

Source

/*
 * RHQ Management Platform
 * Copyright 2011, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation version 2 of the License.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package org.rhq.server.metrics.migrator;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.ProtocolOptions.Compression;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.NoHostAvailableException;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.hibernate.ejb.Ejb3Configuration;

import org.rhq.core.util.obfuscation.PicketBoxObfuscator;
import org.rhq.server.metrics.migrator.DataMigrator.DatabaseType;
import org.rhq.server.metrics.migrator.workers.AggregateDataMigrator;
import org.rhq.server.metrics.migrator.workers.DeleteAllData;
import org.rhq.server.metrics.migrator.workers.MetricsIndexMigrator;
import org.rhq.server.metrics.migrator.workers.RawDataMigrator;

/**
 * @author Stefan Negrea
 *
 * Only postgres is supported by the runner, however the data migrator itself can run
 * with any database.
 *
 * Maven command to run this from the command line:
 *
 * mvn install -DskipTests exec:java -Dexec.mainClass="org.rhq.server.metrics.DataMigratorRunner"
 *
 *
 */
@SuppressWarnings({ "static-access", "deprecation" })
public class DataMigratorRunner {

    private static final int DEFAULT_CASSANDRA_PORT = 9142;
    private final Log log = LogFactory.getLog(DataMigratorRunner.class);

    //Cassandra
    private final Option cassandraUserOption = OptionBuilder.withLongOpt("cassandra-user").hasArg()
            .withType(String.class).withDescription("Cassandra user (default: rhqadmin)").create();
    private final Option cassandraPasswordOption = OptionBuilder.withLongOpt("cassandra-password").hasArg()
            .withDescription("Cassandra password (default: rhqadmin)").withType(String.class).create();
    private final Option cassandraHostsOption = OptionBuilder.withLongOpt("cassandra-hosts").hasArg()
            .withType(String.class)
            .withDescription("Cassandra hosts, format host_ip_1,host_ip_2,... (default: 127.0.0.1").create();
    private final Option cassandraPortOption = OptionBuilder.withLongOpt("cassandra-port").hasArg()
            .withType(Integer.class).withDescription("Cassandra native binary protocol port (default: 9142)")
            .create();

    //SQL
    private final Option sqlUserOption = OptionBuilder.withLongOpt("sql-user").hasArg().withType(String.class)
            .withDescription("SQL server user (default: rhqadmin)").create();
    private final Option sqlPasswordOption = OptionBuilder.withLongOpt("sql-password").hasArg()
            .withType(String.class).withDescription("SQL server password (default: rhqadmin)").create();
    private final Option sqlConnectionUrlOption = OptionBuilder.withLongOpt("sql-connection-url").hasArg()
            .withType(String.class)
            .withDescription(
                    "SQL connection url. Not used by default. If specified will override host, port and db SQL options.")
            .create();
    private final Option sqlHostOption = OptionBuilder.withLongOpt("sql-host").hasArg().withType(String.class)
            .withDescription("SQL server host address (default: localhost)").create();
    private final Option sqlPortOption = OptionBuilder.withLongOpt("sql-port").hasArg().withType(String.class)
            .withDescription("SQL server port (default: 5432)").create();
    private Option sqlDBOption = OptionBuilder.withLongOpt("sql-db").hasArg().withType(String.class)
            .withDescription("SQL database (default: rhq)").create();

    private final Option sqlServerTypeOption = OptionBuilder.withLongOpt("sql-server-type").hasArg()
            .withType(String.class)
            .withDescription("SQL server type, only postgres and oracle are supported (default: postgres)")
            .create();
    private final Option sqlPostgresServerOption = OptionBuilder.withLongOpt("sql-server-postgres").hasOptionalArg()
            .withType(Boolean.class).withDescription("Postgres SQL server.").create();
    private final Option sqlOracleServerOption = OptionBuilder.withLongOpt("sql-server-oracle").hasOptionalArg()
            .withType(Boolean.class).withDescription("Oracle SQL server.").create();

    //Migration
    private final Option disableRawOption = OptionBuilder.withLongOpt("disable-raw-migration").hasOptionalArg()
            .withType(Boolean.class).withDescription("Disable raw table migration (default: false)").create();
    private final Option disable1HOption = OptionBuilder.withLongOpt("disable-1h-migration").hasOptionalArg()
            .withType(Boolean.class).withDescription("Disable 1 hour aggregates table migration (default: false)")
            .create();
    private final Option disable6HOption = OptionBuilder.withLongOpt("disable-6h-migration").hasOptionalArg()
            .withType(Boolean.class).withDescription("Disable 6 hours aggregates table migration (default: false)")
            .create();
    private final Option disable1DOption = OptionBuilder.withLongOpt("disable-1d-migration").hasOptionalArg()
            .withType(Boolean.class).withDescription("Disable 24 hours aggregates table migration (default: false)")
            .create();
    private final Option deleteDataOption = OptionBuilder.withLongOpt("delete-data").hasOptionalArg()
            .withType(Boolean.class).withDescription("Delete SQL data at the end of migration (default: false)")
            .create();
    private final Option estimateOnlyOption = OptionBuilder.withLongOpt("estimate-only").hasOptionalArg()
            .withType(Boolean.class)
            .withDescription("Only estimate how long the migration will take (default: false)").create();
    private final Option deleteOnlyOption = OptionBuilder.withLongOpt("delete-only").hasOptionalArg()
            .withType(Boolean.class)
            .withDescription(
                    "Only delete data from the old SQL server, no migration will be performed (default: false)")
            .create();
    private final Option experimentalExportOption = OptionBuilder.withLongOpt("experimental-export")
            .hasOptionalArg().withType(Boolean.class)
            .withDescription(
                    "Enable experimental bulk export for Postgres, option ignored for Oracle migration (default: false)")
            .create();

    //Runner
    private final Option helpOption = OptionBuilder.withLongOpt("help").create("h");
    private final Option debugLogOption = OptionBuilder.withLongOpt("debugLog")
            .withDescription(
                    "Enable debug level logs for the communication with Cassandra and SQL Server (default: false)")
            .create("X");
    private final Option configFileOption = OptionBuilder.withLongOpt("config-file").hasArg().withDescription(
            "Configuration file. All the command line options can be set in a typical properties file. "
                    + "Command line arguments take precedence over default, RHQ server properties,  and configuration file options.")
            .create();
    private final Option serverPropertiesFileOption = OptionBuilder.withLongOpt("rhq-server-properties-file")
            .hasArg()
            .withDescription(
                    "RHQ Server configuration file (rhq-server.properties). The RHQ server properties will be used for SQL server configuration. "
                            + "Command line arguments take precedence over default, RHQ server properties, and configuration file options.")
            .create();

    private Map<Object, Object> configuration = new HashMap<Object, Object>();
    private Options options;

    /**
     * @param args
     * @throws ParseException
     */
    public static void main(String[] args) throws Exception {
        initLogging();
        try {
            DataMigratorRunner runner = new DataMigratorRunner();
            runner.configure(args);
            runner.run();
        } catch (HelpRequestedException h) {
            //do nothing
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }

        System.exit(0);
    }

    private static void initLogging() {
        Logger root = Logger.getRootLogger();
        if (!root.getAllAppenders().hasMoreElements()) {
            root.addAppender(new ConsoleAppender(new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN)));
            setLogLevel(Level.ERROR);
        }
    }

    @SuppressWarnings("rawtypes")
    private static void setLogLevel(Level level) {
        Logger root = Logger.getRootLogger();
        root.setLevel(level);

        Logger cassandraLogging = root.getLoggerRepository()
                .getLogger("log4j.logger.org.apache.cassandra.cql.jdbc");
        cassandraLogging.setLevel(level);

        Logger cassandraDriverLogging = root.getLoggerRepository().getLogger("com.datastax.driver");
        cassandraDriverLogging.setLevel(level);

        Logger hibernateLogging = root.getLoggerRepository().getLogger("org.hibernate");
        hibernateLogging.setLevel(Level.ERROR);

        Logger migratorLogging = root.getLoggerRepository().getLogger("org.rhq");
        if (Level.DEBUG.equals(level)) {
            migratorLogging.setLevel(Level.ALL);
        } else {
            migratorLogging.setLevel(level);
        }

        //force change some of the logger levels
        Class[] clazzes = new Class[] { DataMigratorRunner.class, DataMigrator.class, RawDataMigrator.class,
                DeleteAllData.class, AggregateDataMigrator.class, MetricsIndexMigrator.class };
        for (Class clazz : clazzes) {
            migratorLogging = root.getLogger(clazz);
            if (Level.DEBUG.equals(level)) {
                migratorLogging.setLevel(Level.ALL);
            } else {
                migratorLogging.setLevel(level);
            }
        }
    }

    private void configure(String args[]) throws Exception {
        options = new Options();

        options.addOption(cassandraUserOption);
        options.addOption(cassandraPasswordOption);
        options.addOption(cassandraHostsOption);
        options.addOption(cassandraPortOption);

        options.addOption(sqlUserOption);
        options.addOption(sqlPasswordOption);
        options.addOption(sqlHostOption);
        options.addOption(sqlPortOption);
        options.addOption(sqlDBOption);
        options.addOption(sqlServerTypeOption);
        options.addOption(sqlPostgresServerOption);
        options.addOption(sqlOracleServerOption);
        options.addOption(sqlConnectionUrlOption);

        options.addOption(disableRawOption);
        options.addOption(disable1HOption);
        options.addOption(disable6HOption);
        options.addOption(disable1DOption);
        options.addOption(deleteDataOption);
        options.addOption(estimateOnlyOption);
        options.addOption(deleteOnlyOption);
        options.addOption(experimentalExportOption);

        options.addOption(helpOption);
        options.addOption(debugLogOption);
        options.addOption(configFileOption);
        options.addOption(serverPropertiesFileOption);

        CommandLine commandLine;
        try {
            CommandLineParser parser = new PosixParser();
            commandLine = parser.parse(options, args);
        } catch (Exception e) {
            HelpFormatter formatter = new HelpFormatter();
            formatter.setWidth(120);
            formatter.printHelp("DataMigrationRunner", options);
            throw new Exception("Error parsing command line arguments");
        }

        if (commandLine.hasOption(helpOption.getLongOpt()) || commandLine.hasOption(helpOption.getOpt())) {
            HelpFormatter formatter = new HelpFormatter();
            formatter.printHelp("DataMigrationRunner", options);
            throw new HelpRequestedException();
        }

        if (commandLine.hasOption(debugLogOption.getLongOpt()) || commandLine.hasOption(debugLogOption.getOpt())) {
            DataMigratorRunner.setLogLevel(Level.DEBUG);
        }

        loadDefaultConfiguration();

        if (commandLine.hasOption(serverPropertiesFileOption.getLongOpt())) {
            log.debug("Server configuration file option enabled. Loading server configuration from file: "
                    + serverPropertiesFileOption.getLongOpt());
            loadConfigurationFromServerPropertiesFile(
                    commandLine.getOptionValue(serverPropertiesFileOption.getLongOpt()));
            log.debug(
                    "Server configuration file from system properties will not be loaded even if set because of the manual override.");
        } else if (System.getProperty("rhq.server.properties-file") != null) {
            log.debug("Server configuration file system property detected. Loading the file: "
                    + System.getProperty("rhq.server.properties-file"));
            loadConfigurationFromServerPropertiesFile(System.getProperty("rhq.server.properties-file"));
            log.debug("Server configuration file loaded based on system properties options.");
        }

        if (commandLine.hasOption(configFileOption.getLongOpt())) {
            loadConfigFile(commandLine.getOptionValue(configFileOption.getLongOpt()));
        }

        parseCassandraOptions(commandLine);
        parseSQLOptions(commandLine);
        parseMigrationOptions(commandLine);

        if (commandLine.hasOption(debugLogOption.getLongOpt()) || commandLine.hasOption(debugLogOption.getOpt())) {
            printOptions();
        }
    }

    /**
     * Add default configuration options to the configuration store.
     * @throws Exception
     */
    private void loadDefaultConfiguration() throws Exception {
        //default Cassandra configuration
        configuration.put(cassandraUserOption, "rhqadmin");
        configuration.put(cassandraPasswordOption, "rhqadmin");
        configuration.put(cassandraHostsOption, new String[] { InetAddress.getLocalHost().getHostAddress() });
        configuration.put(cassandraPortOption, DEFAULT_CASSANDRA_PORT);

        //default SQL configuration
        configuration.put(sqlUserOption, "rhqadmin");
        configuration.put(sqlPasswordOption, "rhqadmin");
        configuration.put(sqlHostOption, "localhost");
        configuration.put(sqlPortOption, "5432");
        configuration.put(sqlDBOption, "rhq");
        configuration.put(sqlServerTypeOption, DatabaseType.Postgres);

        //default runner options
        configuration.put(disableRawOption, false);
        configuration.put(disable1HOption, false);
        configuration.put(disable6HOption, false);
        configuration.put(disable1DOption, false);
        configuration.put(estimateOnlyOption, false);
        configuration.put(deleteDataOption, false);
        configuration.put(deleteOnlyOption, false);
        configuration.put(experimentalExportOption, false);
    }

    private void loadConfigurationFromServerPropertiesFile(String file) throws Exception {
        File configFile = new File(file);
        if (!configFile.exists()) {
            throw new FileNotFoundException("RHQ server properties file not found! File: " + file);
        }

        Properties serverProperties = new Properties();
        FileInputStream stream = new FileInputStream(configFile);
        serverProperties.load(stream);
        stream.close();

        //SQL options
        String dbType = serverProperties.getProperty("rhq.server.database.type-mapping");
        DatabaseType databaseType = DatabaseType.Postgres;
        if (dbType != null && dbType.toLowerCase().contains("oracle")) {
            databaseType = databaseType.Oracle;
        }

        configuration.put(sqlServerTypeOption, databaseType);
        configuration.put(sqlUserOption, serverProperties.getProperty("rhq.server.database.user-name"));
        String dbPasswordProperty = serverProperties.getProperty("rhq.server.database.password");
        configuration.put(sqlPasswordOption, PicketBoxObfuscator.decode(dbPasswordProperty));
        configuration.put(sqlConnectionUrlOption,
                serverProperties.getProperty("rhq.server.database.connection-url"));

        //Storage Node options
        configuration.put(cassandraUserOption, serverProperties.getProperty("rhq.storage.username"));
        String cassandraPasswordProperty = serverProperties.getProperty("rhq.storage.password");
        configuration.put(cassandraPasswordOption, PicketBoxObfuscator.decode(cassandraPasswordProperty));

        if (serverProperties.getProperty("rhq.storage.nodes") != null
                && !serverProperties.getProperty("rhq.storage.nodes").trim().isEmpty()) {
            String[] storageNodes = serverProperties.getProperty("rhq.storage.nodes").split(",");
            configuration.put(cassandraHostsOption, storageNodes);
        }

        if (serverProperties.getProperty("rhq.storage.cql-port") != null
                && !serverProperties.getProperty("rhq.storage.cql-port").trim().isEmpty()) {
            Integer cassandraPort = Integer.parseInt(serverProperties.getProperty("rhq.storage.cql-port"));
            configuration.put(cassandraPortOption, cassandraPort);
        }
    }

    /**
     * Load the configuration options from file and overlay them on top of the default
     * options.
     *
     * @param file config file
     */
    private void loadConfigFile(String file) {
        try {
            File configFile = new File(file);
            if (!configFile.exists()) {
                throw new FileNotFoundException("Configuration file not found!");
            }

            Properties configProperties = new Properties();
            FileInputStream stream = new FileInputStream(configFile);
            configProperties.load(stream);
            stream.close();

            for (Object optionObject : options.getOptions()) {
                Option option = (Option) optionObject;
                Object optionValue;

                if ((optionValue = configProperties.get(option.getLongOpt())) != null) {
                    log.debug("Configuration option loaded: " + option.getLongOpt() + " (" + option.getType()
                            + ") -> " + optionValue);

                    if (option.equals(cassandraHostsOption)) {
                        String[] cassandraHosts = parseCassandraHosts(optionValue.toString());
                        configuration.put(option, cassandraHosts);
                    } else if (option.equals(sqlServerTypeOption)) {
                        if ("oracle".equals(optionValue)) {
                            configuration.put(option, DatabaseType.Oracle);
                        } else {
                            configuration.put(option, DatabaseType.Postgres);
                        }
                    } else if (option.equals(sqlPostgresServerOption)) {
                        boolean value = tryParseBoolean(optionValue.toString(), true);
                        if (value == true) {
                            configuration.put(sqlServerTypeOption, DatabaseType.Postgres);
                        }
                    } else if (option.equals(sqlOracleServerOption)) {
                        boolean value = tryParseBoolean(optionValue.toString(), true);
                        if (value == true) {
                            configuration.put(sqlServerTypeOption, DatabaseType.Oracle);
                        }
                    } else if (option.getType().equals(Boolean.class)) {
                        configuration.put(option, tryParseBoolean(optionValue.toString(), true));
                    } else if (option.getType().equals(Integer.class)) {
                        configuration.put(option, tryParseInteger(optionValue.toString(), 0));
                    } else {
                        configuration.put(option, optionValue.toString());
                    }
                }
            }
        } catch (Exception e) {
            log.error("Unable to load or process the configuration file.", e);
            System.exit(1);
        }
    }

    /**
     * Parse command line options for Cassandra.
     *
     * @param commandLine command line
     * @throws NoHostAvailableException
     */
    private void parseCassandraOptions(CommandLine commandLine) throws Exception {
        if (commandLine.hasOption(cassandraUserOption.getLongOpt())) {
            configuration.put(cassandraUserOption, commandLine.getOptionValue(cassandraUserOption.getLongOpt()));
        }

        if (commandLine.hasOption(cassandraPasswordOption.getLongOpt())) {
            configuration.put(cassandraPasswordOption,
                    commandLine.getOptionValue(cassandraPasswordOption.getLongOpt()));
        }

        if (commandLine.hasOption(cassandraHostsOption.getLongOpt())) {
            String[] cassandraHosts = parseCassandraHosts(
                    commandLine.getOptionValue(cassandraHostsOption.getLongOpt()));
            configuration.put(cassandraHostsOption, cassandraHosts);
        }

        if (commandLine.hasOption(cassandraPortOption.getLongOpt())) {
            Integer cassandraPort = tryParseInteger(commandLine.getOptionValue(cassandraPortOption.getLongOpt()),
                    DEFAULT_CASSANDRA_PORT);
            configuration.put(cassandraPortOption, cassandraPort);
        }
    }

    /**
     * Parse command line options for SQL.
     *
     * @param commandLine command line
     * @throws NoHostAvailableException
     */
    private void parseSQLOptions(CommandLine commandLine) throws NoHostAvailableException {
        if (commandLine.hasOption(sqlUserOption.getLongOpt())) {
            configuration.put(sqlUserOption, commandLine.getOptionValue(sqlUserOption.getLongOpt()));
        }

        if (commandLine.hasOption(sqlPasswordOption.getLongOpt())) {
            configuration.put(sqlPasswordOption, commandLine.getOptionValue(sqlPasswordOption.getLongOpt()));
        }

        if (commandLine.hasOption(sqlHostOption.getLongOpt())) {
            configuration.put(sqlHostOption, commandLine.getOptionValue(sqlHostOption.getLongOpt()));
        }

        if (commandLine.hasOption(sqlPortOption.getLongOpt())) {
            configuration.put(sqlPortOption, commandLine.getOptionValue(sqlPortOption.getLongOpt()));
        }

        if (commandLine.hasOption(sqlDBOption.getLongOpt())) {
            configuration.put(sqlDBOption, commandLine.getOptionValue(sqlDBOption.getLongOpt()));
        }

        if (commandLine.hasOption(sqlConnectionUrlOption.getLongOpt())) {
            configuration.put(sqlConnectionUrlOption,
                    commandLine.getOptionValue(sqlConnectionUrlOption.getLongOpt()));
        }

        if (commandLine.hasOption(sqlServerTypeOption.getLongOpt())) {
            if ("oracle".equals(commandLine.getOptionValue(sqlServerTypeOption.getLongOpt()))) {
                configuration.put(sqlServerTypeOption, DatabaseType.Oracle);
            } else {
                configuration.put(sqlServerTypeOption, DatabaseType.Postgres);
            }
        } else if (commandLine.hasOption(sqlPostgresServerOption.getLongOpt())) {
            configuration.put(sqlServerTypeOption, DatabaseType.Postgres);
        } else if (commandLine.hasOption(sqlOracleServerOption.getLongOpt())) {
            configuration.put(sqlServerTypeOption, DatabaseType.Oracle);
        }
    }

    /**
     * Parse command line options for the actual migration progress.
     *
     * @param commandLine
     */
    private void parseMigrationOptions(CommandLine commandLine) {
        boolean value;

        if (commandLine.hasOption(disableRawOption.getLongOpt())) {
            value = tryParseBoolean(commandLine.getOptionValue(disableRawOption.getLongOpt()), true);
            configuration.put(disableRawOption, value);
        }

        if (commandLine.hasOption(disable1HOption.getLongOpt())) {
            value = tryParseBoolean(commandLine.getOptionValue(disable1HOption.getLongOpt()), true);
            configuration.put(disable1HOption, value);
        }

        if (commandLine.hasOption(disable6HOption.getLongOpt())) {
            value = tryParseBoolean(commandLine.getOptionValue(disable6HOption.getLongOpt()), true);
            configuration.put(disable6HOption, value);
        }

        if (commandLine.hasOption(disable1DOption.getLongOpt())) {
            value = tryParseBoolean(commandLine.getOptionValue(disable1DOption.getLongOpt()), true);
            configuration.put(disable1DOption, value);
        }

        if (commandLine.hasOption(deleteDataOption.getLongOpt())) {
            value = tryParseBoolean(commandLine.getOptionValue(deleteDataOption.getLongOpt()), true);
            configuration.put(deleteDataOption, value);
        }

        if (commandLine.hasOption(deleteOnlyOption.getLongOpt())) {
            value = tryParseBoolean(commandLine.getOptionValue(deleteOnlyOption.getLongOpt()), true);
            configuration.put(deleteOnlyOption, value);
        }

        if (commandLine.hasOption(estimateOnlyOption.getLongOpt())) {
            value = tryParseBoolean(commandLine.getOptionValue(estimateOnlyOption.getLongOpt()), true);
            configuration.put(estimateOnlyOption, value);
        }

        if (commandLine.hasOption(experimentalExportOption.getLongOpt())) {
            value = tryParseBoolean(commandLine.getOptionValue(experimentalExportOption.getLongOpt()), true);
            configuration.put(experimentalExportOption, value);
        }
    }

    private void run() throws Exception {
        log.debug("Creating Entity Manager");
        EntityManagerFactory entityManagerFactory = this.createEntityManagerFactory();
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        log.debug("Done creating Entity Manager");

        log.debug("Creating Cassandra session");
        Session cassandraSession = this.createCassandraSession();
        log.debug("Done creating Cassandra session");

        DatabaseType databaseType = DatabaseType.Postgres;
        if (configuration.get(sqlServerTypeOption) != null) {
            databaseType = (DatabaseType) configuration.get(sqlServerTypeOption);
        }

        DataMigrator migrator = new DataMigrator(entityManager, cassandraSession, databaseType,
                tryParseBoolean(configuration.get(experimentalExportOption), false));

        if (!(Boolean) configuration.get(deleteOnlyOption)) {
            if ((Boolean) configuration.get(deleteDataOption)) {
                migrator.deleteAllDataAtEndOfMigration();
            } else {
                migrator.preserveData();
            }

            migrator.runRawDataMigration(!(Boolean) configuration.get(disableRawOption));
            migrator.run1HAggregateDataMigration(!(Boolean) configuration.get(disable1HOption));
            migrator.run6HAggregateDataMigration(!(Boolean) configuration.get(disable6HOption));
            migrator.run1DAggregateDataMigration(!(Boolean) configuration.get(disable1DOption));

            System.out.println("Estimation process - starting\n");
            long estimate = migrator.estimate();
            System.out.println("The migration process will take approximately: "
                    + TimeUnit.MILLISECONDS.toMinutes(estimate) + " minutes (or " + estimate + " milliseconds)\n");
            System.out.println("Estimation process - ended\n\n");

            if (!(Boolean) configuration.get(estimateOnlyOption)) {
                System.out.println("Migration process - starting\n");
                long startTime = System.currentTimeMillis();
                migrator.migrateData();
                long duration = System.currentTimeMillis() - startTime;
                System.out.println("The migration process took: " + TimeUnit.MILLISECONDS.toMinutes(duration)
                        + " minutes (or " + duration + " milliseconds)\n");
                System.out.println("Migration process - ended\n");
            }
        } else {
            migrator.deleteAllDataAtEndOfMigration();
            migrator.runRawDataMigration(false);
            migrator.run1HAggregateDataMigration(false);
            migrator.run6HAggregateDataMigration(false);
            migrator.run1DAggregateDataMigration(false);

            System.out.println("Estimation process - starting\n");
            long estimate = migrator.estimate();
            System.out.println("The deletion of old data will take approximately: "
                    + TimeUnit.MILLISECONDS.toMinutes(estimate) + " minutes (or " + estimate + " milliseconds)\n");
            System.out.println("Estimation process - ended\n\n");

            if (!(Boolean) configuration.get(estimateOnlyOption)) {
                migrator.runRawDataMigration(true);
                migrator.run1HAggregateDataMigration(true);
                migrator.run6HAggregateDataMigration(true);
                migrator.run1DAggregateDataMigration(true);

                System.out.println("Old data deletion process - starting\n");
                long startTime = System.currentTimeMillis();
                migrator.deleteOldData();
                long duration = System.currentTimeMillis() - startTime;
                System.out.println("The deletion process took: " + TimeUnit.MILLISECONDS.toMinutes(duration)
                        + " minutes (or " + duration + " milliseconds)\n");
                System.out.println("Old data deletion process - ended\n");
            }
        }

        this.closeCassandraSession(cassandraSession);
        this.closeEntityManagerFactory(entityManagerFactory);
    }

    /**
     * Closes an existing cassandra session.
     *
     * @param session
     */
    private void closeCassandraSession(Session session) {
        session.getCluster().shutdown();
    }

    /**
     * Closes an existing entity manager factory
     *
     * @param entityManagerFactory
     */
    private void closeEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
        if (entityManagerFactory != null) {
            entityManagerFactory.close();
        }
    }

    /**
     * Create a Cassandra session based on configuration options.
     *
     * @return Cassandra session
     * @throws Exception
     */
    private Session createCassandraSession() throws Exception {
        Cluster cluster = Cluster.builder().addContactPoints((String[]) configuration.get(cassandraHostsOption))
                .withPort((Integer) configuration.get(cassandraPortOption)).withCompression(Compression.NONE)
                .withoutMetrics().withCredentials((String) configuration.get(cassandraUserOption),
                        (String) configuration.get(cassandraPasswordOption))
                .build();

        try {
            return cluster.connect("rhq");
        } catch (Exception e) {
            log.debug("Failed to connect to the storage cluster.", e);
            cluster.shutdown();
            throw e;
        }
    }

    /**
     * Create an entity manager factory with the SQL configuration from properties.
     *
     * @return an entity manager factory
     * @throws Exception
     */
    private EntityManagerFactory createEntityManagerFactory() throws Exception {
        Properties properties = new Properties();
        properties.put("javax.persistence.provider", "org.hibernate.ejb.HibernatePersistence");
        properties.put("hibernate.connection.username", (String) configuration.get(sqlUserOption));
        properties.put("hibernate.connection.password", (String) configuration.get(sqlPasswordOption));
        properties.put("javax.persistence.query.timeout", DataMigrator.SQL_TIMEOUT);
        properties.put("hibernate.c3p0.timeout", DataMigrator.SQL_TIMEOUT);

        if (DatabaseType.Oracle.equals(configuration.get(sqlServerTypeOption))) {
            String driverClassName = "oracle.jdbc.driver.OracleDriver";

            try {
                //Required to preload the driver manually.
                //Without this the driver load will fail due to the packaging.
                Class.forName(driverClassName);
            } catch (ClassNotFoundException e) {
                log.debug(e);
                throw new Exception(
                        "Oracle SQL Driver class could not be loaded. Missing class: " + driverClassName);
            }

            properties.put("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
            properties.put("hibernate.driver_class", driverClassName);

            if (configuration.get(sqlConnectionUrlOption) != null) {
                properties.put("hibernate.connection.url", (String) configuration.get(sqlConnectionUrlOption));
            } else {
                properties.put("hibernate.connection.url",
                        "jdbc:oracle:thin:@" + (String) configuration.get(sqlHostOption) + ":"
                                + (String) configuration.get(sqlPortOption) + ":"
                                + (String) configuration.get(sqlDBOption));
                properties.put("hibernate.default_schema", (String) configuration.get(sqlDBOption));
            }

            properties.put("hibernate.connection.oracle.jdbc.ReadTimeout", DataMigrator.SQL_TIMEOUT);
        } else {
            String driverClassName = "org.postgresql.Driver";

            try {
                //Required to preload the driver manually.
                //Without this the driver load will fail due to the packaging.
                Class.forName(driverClassName);
            } catch (ClassNotFoundException e) {
                log.debug(e);
                throw new Exception(
                        "Postgres SQL Driver class could not be loaded. Missing class: " + driverClassName);
            }

            properties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
            properties.put("hibernate.driver_class", driverClassName);

            if (configuration.get(sqlConnectionUrlOption) != null) {
                properties.put("hibernate.connection.url", (String) configuration.get(sqlConnectionUrlOption));
            } else {
                properties.put("hibernate.connection.url",
                        "jdbc:postgresql://" + (String) configuration.get(sqlHostOption) + ":"
                                + (String) configuration.get(sqlPortOption) + "/"
                                + (String) configuration.get(sqlDBOption));
            }
        }

        log.debug("Creating entity manager with the following configuration:");
        log.debug(properties);

        Ejb3Configuration configuration = new Ejb3Configuration();
        configuration.setProperties(properties);
        EntityManagerFactory factory = configuration.buildEntityManagerFactory();
        return factory;
    }

    /**
      * Print the options used to run the migration process
      */
    private void printOptions() {
        log.debug("Running migration with the following optons: ");
        for (Entry<Object, Object> configOption : this.configuration.entrySet()) {
            Option option = (Option) configOption.getKey();
            if (option.getLongOpt() != null && !option.getLongOpt().contains("pass")) {
                if (!(configOption.getValue() instanceof Object[])) {
                    log.debug("  " + option.getLongOpt() + " : " + configOption.getValue());
                } else {
                    StringBuffer arrayProperty = new StringBuffer();
                    arrayProperty.append("  ").append(option.getLongOpt()).append(" : [");
                    boolean first = true;
                    for (Object value : (Object[]) configOption.getValue()) {
                        if (!first) {
                            arrayProperty.append(", ");
                        }
                        arrayProperty.append(value);
                        first = false;
                    }
                    arrayProperty.append("]");

                    log.debug(arrayProperty.toString());
                }
            } else {
                log.debug("  " + option.getLongOpt() + " : <obscured value>");
            }
        }
    }

    /**
     * Parse Cassandra host information submitted in the form:
     * host_addres,jmx_port,native_port|host_address_2,jmx_port,native_port
     *
     * @param stringValue
     * @return
     */
    private String[] parseCassandraHosts(String stringValue) {
        String[] seeds = stringValue.split(",");
        return seeds;
    }

    /**
     * @param value object value to parse
     * @param defaultValue default value
     * @return
     */
    private boolean tryParseBoolean(Object value, boolean defaultValue) {
        try {
            return Boolean.parseBoolean(value.toString());
        } catch (Exception e) {
            return defaultValue;
        }
    }

    /**
     * @param value object value to parse
     * @param defaultValue default value
     * @return
     */
    private Integer tryParseInteger(Object value, int defaultValue) {
        try {
            return Integer.parseInt(value.toString());
        } catch (Exception e) {
            return defaultValue;
        }
    }

    @SuppressWarnings("serial")
    private class HelpRequestedException extends Exception {
        public HelpRequestedException() {
            super("Help Requested");
        }
    }
}