strat.mining.stratum.proxy.configuration.ConfigurationManager.java Source code

Java tutorial

Introduction

Here is the source code for strat.mining.stratum.proxy.configuration.ConfigurationManager.java

Source

/**
 * stratum-proxy is a proxy supporting the crypto-currency stratum pool mining
 * protocol.
 * Copyright (C) 2014-2015  Stratehm (stratehm@hotmail.com)
 *
 * 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; either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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 multipool-stats-backend. If not, see <http://www.gnu.org/licenses/>.
 */
package strat.mining.stratum.proxy.configuration;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.RollingFileAppender;
import org.kohsuke.args4j.CmdLineException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;

import strat.mining.stratum.proxy.Launcher;
import strat.mining.stratum.proxy.cli.CommandLineOptions;
import strat.mining.stratum.proxy.configuration.model.Configuration;
import strat.mining.stratum.proxy.constant.Constants;
import strat.mining.stratum.proxy.exception.BadParameterException;
import strat.mining.stratum.proxy.manager.strategy.PriorityFailoverStrategyManager;
import strat.mining.stratum.proxy.pool.Pool;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * Manage the configuration
 * 
 * @author Strat
 * 
 */
public class ConfigurationManager {

    private static Logger logger;

    private static ConfigurationManager instance;

    private static String version;

    private File configurationFile;

    private List<Pool> pools;

    private File logDirectory;
    private Level logLevel = Level.INFO;
    private Level apiLogLevel = null;
    private boolean disableLogAppend = false;

    private Integer numberOfSubmit = 1;
    private Integer stratumListeningPort = Constants.DEFAULT_STRATUM_LISTENING_PORT;
    private String stratumBindAddress = Constants.DEFAULT_STRATUM_LISTENING_ADDRESS;
    private Integer restListenPort = Constants.DEFAULT_REST_LISTENING_PORT;
    private String restBindAddress = Constants.DEFAULT_REST_LISTENING_ADDRESS;
    private Integer getworkListenPort = Constants.DEFAULT_GETWORK_LISTENING_PORT;
    private String getworkBindAddress = Constants.DEFAULT_GETWORK_LISTENING_ADDRESS;

    private Integer poolConnectionRetryDelay = Constants.DEFAULT_POOL_CONNECTION_RETRY_DELAY;
    private Integer poolReconnectStabilityPeriod = Constants.DEFAULT_POOL_RECONNECTION_STABILITY_PERIOD;;
    private Integer poolNoNotifyTimeout = Constants.DEFAULT_NOTIFY_NOTIFICATION_TIMEOUT;
    private boolean isRejectReconnect = false;

    private Integer poolHashrateSamplingPeriod = Constants.DEFAULT_POOL_HASHRATE_SAMPLING_PERIOD;
    private Integer userHashrateSamplingPeriod = Constants.DEFAULT_USER_HASHRATE_SAMPLING_PERIOD;
    private Integer connectionHashrateSamplingPeriod = Constants.DEFAULT_WORKER_CONNECTION_HASHRATE_SAMPLING_PERIOD;
    private boolean isScrypt = false;

    private File databaseDirectory;
    private Integer hashrateDatabaseSamplingPeriod = Constants.DEFAULT_HASHRATE_DATABASE_SAMPLING_PERIOD;
    private Integer hashrateDatabaseHistoryDepth = Constants.DEFAULT_HASHRATE_DATABASE_HISTORY_DEPTH;

    private boolean noMidsate = false;
    private boolean validateGetworkShares = false;

    private String poolSwitchingStrategy = PriorityFailoverStrategyManager.NAME;

    private Integer weightedRoundRobinRoundDuration = Constants.DEFAULT_WEIGHTED_ROUND_ROBIN_ROUND_DURATION;

    private boolean disableGetwork = false;
    private boolean disableStratum = false;
    private boolean disableApi = false;

    private String apiUser;
    private String apiPassword;
    private Boolean apiReadOnlyAccessEnabled = false;
    private Boolean apiEnableSsl;

    private String ipVersion = Constants.IP_VERSION_AUTO;

    private Double suggestedPoolDifficulty;

    private Boolean logRealShareDifficulty = false;

    private Integer extranonce1TailSize = Constants.DEFAULT_EXTRANONCE1_TAIL_SIZE;

    private ObjectMapper jsonParser;

    public static ConfigurationManager getInstance() {
        if (instance == null) {
            instance = new ConfigurationManager();
        }
        return instance;
    }

    public ConfigurationManager() {
        this.jsonParser = new ObjectMapper();
        this.jsonParser.getFactory().configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_COMMENTS, true);
        this.jsonParser.getFactory()
                .configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        this.jsonParser.getFactory().configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_SINGLE_QUOTES,
                true);
    }

    /**
     * Try to load the configuration from command line or from configuration
     * file. Return an execption if configuration cannot be loaded.
     * 
     * @param cliArguments
     * @return
     */
    public void loadConfiguration(String[] cliArguments) throws Exception {

        CommandLineOptions cliParser = new CommandLineOptions();
        // Parse the command line options
        cliParser.parseArguments(cliArguments);

        if (cliParser.isHelpRequested()) {
            cliParser.printUsage();
            System.exit(0);
        } else if (cliParser.isVersionRequested()) {
            String version = "stratum-proxy by Stratehm. GPLv3 Licence. Version " + Constants.VERSION;
            System.out.println(version);
            System.exit(0);
        } else {

            // If -f is specified, use the configuration file
            if (cliParser.getConfigurationFile() != null) {
                // Use configuration file.
                configurationFile = cliParser.getConfigurationFile();
                if (configurationFile.isFile() && configurationFile.canRead()) {
                    useConfigurationFile();
                } else {
                    throw new FileNotFoundException("Configuration file " + configurationFile.getAbsolutePath()
                            + " does not exist or is not readable.");
                }
            } else {
                // Use parameters of the command line.
                useCommandLine(cliParser);
            }
            initDatabaseDirectory();
        }
    }

    /**
     * Set the configuration based on the configuration file.
     * 
     * @throws IOException
     * @throws JsonMappingException
     * @throws JsonParseException
     * @throws BadParameterException
     */
    private void useConfigurationFile()
            throws JsonParseException, JsonMappingException, IOException, BadParameterException {
        Configuration configuration = jsonParser.readValue(configurationFile, Configuration.class);

        logLevel = configuration.getLogLevel() != null ? Level.toLevel(configuration.getLogLevel()) : logLevel;
        apiLogLevel = configuration.getApiLogLevel() != null ? Level.toLevel(configuration.getApiLogLevel())
                : apiLogLevel;
        logDirectory = configuration.getLogDirectory() != null && !configuration.getLogDirectory().trim().isEmpty()
                ? new File(configuration.getLogDirectory())
                : logDirectory;
        disableLogAppend = configuration.isDisableLogAppend() != null ? configuration.isDisableLogAppend()
                : disableLogAppend;
        // Initialize the logging system
        initLogging();

        stratumListeningPort = configuration.getStratumListenPort() != null ? configuration.getStratumListenPort()
                : stratumListeningPort;
        stratumBindAddress = configuration.getStratumListenAddress() != null
                ? configuration.getStratumListenAddress()
                : stratumBindAddress;
        restListenPort = configuration.getApiListenPort() != null ? configuration.getApiListenPort()
                : restListenPort;
        restBindAddress = configuration.getApiListenAddress() != null ? configuration.getApiListenAddress()
                : restBindAddress;
        getworkListenPort = configuration.getGetworkListenPort() != null ? configuration.getGetworkListenPort()
                : getworkListenPort;
        getworkBindAddress = configuration.getGetworkListenAddress() != null
                ? configuration.getGetworkListenAddress()
                : getworkBindAddress;

        poolConnectionRetryDelay = configuration.getPoolConnectionRetryDelay() != null
                ? configuration.getPoolConnectionRetryDelay()
                : poolConnectionRetryDelay;
        poolReconnectStabilityPeriod = configuration.getPoolReconnectStabilityPeriod() != null
                ? configuration.getPoolReconnectStabilityPeriod()
                : poolReconnectStabilityPeriod;
        poolNoNotifyTimeout = configuration.getPoolNoNotifyTimeout() != null
                ? configuration.getPoolNoNotifyTimeout()
                : poolNoNotifyTimeout;
        isRejectReconnect = configuration.getRejectReconnectOnDifferentHost() != null
                ? configuration.getRejectReconnectOnDifferentHost()
                : isRejectReconnect;

        poolHashrateSamplingPeriod = configuration.getPoolHashrateSamplingPeriod() != null
                ? configuration.getPoolHashrateSamplingPeriod()
                : poolHashrateSamplingPeriod;
        userHashrateSamplingPeriod = configuration.getUserHashrateSamplingPeriod() != null
                ? configuration.getUserHashrateSamplingPeriod()
                : userHashrateSamplingPeriod;
        connectionHashrateSamplingPeriod = configuration.getConnectionHashrateSamplingPeriod() != null
                ? configuration.getConnectionHashrateSamplingPeriod()
                : connectionHashrateSamplingPeriod;
        isScrypt = configuration.getIsScrypt() != null ? configuration.getIsScrypt() : isScrypt;

        databaseDirectory = configuration.getDatabaseDirectory() != null
                && !configuration.getDatabaseDirectory().trim().isEmpty()
                        ? new File(configuration.getDatabaseDirectory())
                        : databaseDirectory;
        hashrateDatabaseSamplingPeriod = configuration.getHashrateDatabaseSamplingPeriod() != null
                ? configuration.getHashrateDatabaseSamplingPeriod()
                : hashrateDatabaseSamplingPeriod;
        hashrateDatabaseHistoryDepth = configuration.getHashrateDatabaseHistoryDepth() != null
                ? configuration.getHashrateDatabaseHistoryDepth()
                : hashrateDatabaseHistoryDepth;
        noMidsate = configuration.getNoMidstate() != null ? configuration.getNoMidstate() : noMidsate;
        validateGetworkShares = configuration.getValidateGetworkShares() != null
                ? configuration.getValidateGetworkShares()
                : validateGetworkShares;

        poolSwitchingStrategy = configuration.getPoolSwitchingStrategy() != null
                ? configuration.getPoolSwitchingStrategy()
                : poolSwitchingStrategy;
        weightedRoundRobinRoundDuration = configuration.getWeightedRoundRobinRoundDuration() != null
                ? configuration.getWeightedRoundRobinRoundDuration() * 60000
                : weightedRoundRobinRoundDuration;

        disableGetwork = configuration.isDisableGetwork() != null ? configuration.isDisableGetwork()
                : disableGetwork;
        disableStratum = configuration.isDisableStratum() != null ? configuration.isDisableStratum()
                : disableStratum;
        disableApi = configuration.isDisableApi() != null ? configuration.isDisableApi() : disableApi;

        apiEnableSsl = configuration.getApiEnableSsl();

        ipVersion = configuration.getIpVersion();

        suggestedPoolDifficulty = configuration.getSuggestedPoolDifficulty();

        logRealShareDifficulty = configuration.getLogRealShareDifficulty() != null
                ? configuration.getLogRealShareDifficulty()
                : logRealShareDifficulty;

        apiUser = configuration.getApiUser() != null ? configuration.getApiUser() : apiUser;
        if (apiUser != null) {
            apiReadOnlyAccessEnabled = configuration.getApiReadOnlyAccessEnabled() != null
                    ? configuration.getApiReadOnlyAccessEnabled()
                    : apiReadOnlyAccessEnabled;
            // If the apiUser is empty, set it to null.
            if (apiUser.trim().isEmpty()) {
                apiUser = null;
            } else if (apiEnableSsl == null) {
                // If the apiEnableSsl option is not set and the API
                // authentication is enable, enable SSl by default.
                apiEnableSsl = true;
            }
        }

        // If the apiEnableSsl is null, disable it.
        apiEnableSsl = apiEnableSsl == null ? false : apiEnableSsl;

        apiPassword = configuration.getApiPassword() != null ? configuration.getApiPassword() : apiPassword;
        if (apiPassword != null && apiPassword.trim().isEmpty()) {
            apiPassword = null;
        }

        defineExtranonce1TailSize(configuration.getWorkerNumberLimit());

        buildPoolsFromConfigurationFile(configuration);
    }

    /**
     * Build the pool list from the configuration file.
     * 
     * @param configuration
     */
    private void buildPoolsFromConfigurationFile(Configuration configuration) throws BadParameterException {
        pools = new ArrayList<Pool>();

        if (configuration.getPools() != null && configuration.getPools().size() > 0) {
            int counter = 0;
            for (strat.mining.stratum.proxy.configuration.model.Pool confPool : configuration.getPools()) {

                String poolName = confPool.getHost();
                String poolHost = null;
                String username = null;
                String password = Constants.DEFAULT_PASSWORD;
                Boolean isExtranonceSubscribe = Boolean.FALSE;
                Boolean isAppendWorkerNames = Boolean.FALSE;
                String workerNameSeparator = Constants.DEFAULT_WORKER_NAME_SEPARTOR;
                Boolean useWorkerPassword = Boolean.FALSE;
                Boolean isEnabled = Boolean.TRUE;
                Integer poolWeight = Constants.DEFAULT_POOL_WEIGHT;

                if (confPool.getName() != null && !confPool.getName().trim().isEmpty()) {
                    poolName = confPool.getName();
                }

                if (confPool.getHost() != null && !confPool.getHost().trim().isEmpty()) {
                    poolHost = confPool.getHost();
                } else {
                    throw new BadParameterException(
                            "Missing host for the pool number " + (counter + 1) + " in configuration file.");
                }

                if (confPool.getUser() != null && !confPool.getUser().trim().isEmpty()) {
                    username = confPool.getUser();
                } else {
                    throw new BadParameterException(
                            "Missing username for the pool with host " + poolHost + " in configuration file.");
                }

                if (confPool.getPassword() != null) {
                    password = confPool.getPassword();
                }

                if (confPool.getEnableExtranonceSubscribe() != null) {
                    isExtranonceSubscribe = confPool.getEnableExtranonceSubscribe();
                }

                if (confPool.getAppendWorkerNames() != null) {
                    isAppendWorkerNames = confPool.getAppendWorkerNames();
                }

                if (confPool.getWorkerNameSeparator() != null) {
                    workerNameSeparator = confPool.getWorkerNameSeparator();
                }

                if (confPool.getUseWorkerPassword() != null) {
                    useWorkerPassword = confPool.getUseWorkerPassword();
                }

                if (confPool.getIsEnabled() != null) {
                    isEnabled = confPool.getIsEnabled();
                }

                if (confPool.getWeight() != null) {
                    poolWeight = confPool.getWeight();
                }

                Pool pool = new Pool(poolName, poolHost, username, password);
                pool.setExtranonceSubscribeEnabled(isExtranonceSubscribe);
                pool.setNumberOfSubmit(numberOfSubmit);
                pool.setPriority(counter);
                pool.setConnectionRetryDelay(poolConnectionRetryDelay);
                pool.setReconnectStabilityPeriod(poolReconnectStabilityPeriod);
                pool.setNoNotifyTimeout(poolNoNotifyTimeout);
                pool.setRejectReconnect(isRejectReconnect);
                pool.setSamplingHashratePeriod(poolHashrateSamplingPeriod);
                pool.setAppendWorkerNames(isAppendWorkerNames);
                pool.setWorkerSeparator(workerNameSeparator);
                pool.setUseWorkerPassword(useWorkerPassword);
                pool.setWeight(poolWeight);
                try {
                    pool.setEnabled(isEnabled);
                } catch (Exception e) {
                    // Should never happens. Else, it is a bug.
                    System.err
                            .println("Error during creation of pool " + pool.getName() + " (cause: enabled value)");
                    e.printStackTrace();
                }
                pools.add(pool);

                counter++;
            }
        }
    }

    /**
     * Set the configuration based on the command line arguments.
     * 
     * @param cliParser
     */
    private void useCommandLine(CommandLineOptions cliParser) throws Exception {

        logLevel = cliParser.getLogLevel();
        logDirectory = cliParser.getLogDirectory();
        apiLogLevel = cliParser.getApiLogLevel();
        disableLogAppend = cliParser.isDisableLogAppend() != null ? cliParser.isDisableLogAppend()
                : disableLogAppend;
        // Initialize the logging system
        initLogging();

        numberOfSubmit = cliParser.getNumberOfSubmit() != null ? cliParser.getNumberOfSubmit() : numberOfSubmit;
        stratumListeningPort = cliParser.getStratumListeningPort() != null ? cliParser.getStratumListeningPort()
                : stratumListeningPort;
        stratumBindAddress = cliParser.getStratumBindAddress() != null ? cliParser.getStratumBindAddress()
                : stratumBindAddress;
        restListenPort = cliParser.getRestListenPort() != null ? cliParser.getRestListenPort() : restListenPort;
        restBindAddress = cliParser.getRestBindAddress() != null ? cliParser.getRestBindAddress() : restBindAddress;
        getworkListenPort = cliParser.getGetworkListenPort() != null ? cliParser.getGetworkListenPort()
                : getworkListenPort;
        getworkBindAddress = cliParser.getGetworkBindAddress() != null ? cliParser.getGetworkBindAddress()
                : getworkBindAddress;

        poolConnectionRetryDelay = cliParser.getPoolConnectionRetryDelay() != null
                ? cliParser.getPoolConnectionRetryDelay()
                : poolConnectionRetryDelay;
        poolReconnectStabilityPeriod = cliParser.getPoolReconnectStabilityPeriod() != null
                ? cliParser.getPoolReconnectStabilityPeriod()
                : poolReconnectStabilityPeriod;
        poolNoNotifyTimeout = cliParser.getPoolNoNotifyTimeout() != null ? cliParser.getPoolNoNotifyTimeout()
                : poolNoNotifyTimeout;
        isRejectReconnect = cliParser.isRejectReconnect() != null ? cliParser.isRejectReconnect()
                : isRejectReconnect;

        poolHashrateSamplingPeriod = cliParser.getPoolHashrateSamplingPeriod() != null
                ? cliParser.getPoolHashrateSamplingPeriod()
                : poolHashrateSamplingPeriod;
        userHashrateSamplingPeriod = cliParser.getUserHashrateSamplingPeriod() != null
                ? cliParser.getUserHashrateSamplingPeriod()
                : userHashrateSamplingPeriod;
        connectionHashrateSamplingPeriod = cliParser.getConnectionHashrateSamplingPeriod() != null
                ? cliParser.getConnectionHashrateSamplingPeriod()
                : connectionHashrateSamplingPeriod;
        isScrypt = cliParser.isScrypt() != null ? cliParser.isScrypt() : isScrypt;

        databaseDirectory = cliParser.getDatabaseDirectory() != null ? cliParser.getDatabaseDirectory()
                : databaseDirectory;
        hashrateDatabaseSamplingPeriod = cliParser.getHashrateDatabaseSamplingPeriod() != null
                ? cliParser.getHashrateDatabaseSamplingPeriod()
                : hashrateDatabaseSamplingPeriod;
        hashrateDatabaseHistoryDepth = cliParser.getHashrateDatabaseHistoryDepth() != null
                ? cliParser.getHashrateDatabaseHistoryDepth()
                : hashrateDatabaseHistoryDepth;
        noMidsate = cliParser.isNoMidstate() != null ? cliParser.isNoMidstate() : noMidsate;
        validateGetworkShares = cliParser.isValidateGetworkShares() != null ? cliParser.isValidateGetworkShares()
                : validateGetworkShares;

        poolSwitchingStrategy = cliParser.getPoolSwitchingStrategy() != null ? cliParser.getPoolSwitchingStrategy()
                : poolSwitchingStrategy;
        weightedRoundRobinRoundDuration = cliParser.getWeightedRoundRobinRoundDuration() != null
                ? cliParser.getWeightedRoundRobinRoundDuration() * 60000
                : weightedRoundRobinRoundDuration;

        disableGetwork = cliParser.isDisableGetwork() != null ? cliParser.isDisableGetwork() : disableGetwork;
        disableStratum = cliParser.isDisableStratum() != null ? cliParser.isDisableStratum() : disableStratum;
        disableApi = cliParser.isDisableApi() != null ? cliParser.isDisableApi() : disableApi;

        ipVersion = cliParser.getIpVersion();

        suggestedPoolDifficulty = cliParser.getSuggestedPoolDifficulty();

        logRealShareDifficulty = cliParser.getLogRealShareDifficulty() != null
                ? cliParser.getLogRealShareDifficulty()
                : logRealShareDifficulty;

        apiEnableSsl = cliParser.isApiEnableSsl();

        apiUser = cliParser.getApiUser() != null ? cliParser.getApiUser() : apiUser;
        if (apiUser != null) {
            apiReadOnlyAccessEnabled = cliParser.isApiReadOnlyAccessEnabled();
            // If the apiUser is empty, set it to null.
            if (apiUser.trim().isEmpty()) {
                apiUser = null;
            } else if (apiEnableSsl == null) {
                // If the apiEnableSsl option is not set and the API
                // authentication is enable, enable SSl by default.
                apiEnableSsl = true;
            }
        }

        // If the apiEnableSsl is null, disable it.
        apiEnableSsl = apiEnableSsl == null ? false : apiEnableSsl;

        apiPassword = cliParser.getApiPassword() != null ? cliParser.getApiPassword() : apiPassword;
        if (apiPassword != null && apiPassword.trim().isEmpty()) {
            apiPassword = null;
        }

        defineExtranonce1TailSize(cliParser.getWorkerNumberLimit());

        buildPoolsFromCommandLine(cliParser);
    }

    /**
     * Define the extranonce1 tail size based on the max number of workers
     * connected on the proxy.
     * 
     * @param workerNumberLimit
     */
    private void defineExtranonce1TailSize(Integer workerNumberLimit) {
        // If the limit is 65536, the extranonce1 tail size is 2 bytes => 65536
        // Else, if not set or with another value, use the default size: 1 byte
        // => 256
        if (workerNumberLimit != null) {
            if (workerNumberLimit == 1) {
                extranonce1TailSize = 0;
            } else if (workerNumberLimit == 65536) {
                extranonce1TailSize = 2;
            }
        }
    }

    /**
     * Return the list of pools given through the command line at startup
     * 
     * @return
     * @throws CmdLineException
     */
    public List<Pool> getPools() {
        return pools;
    }

    /**
     * Build the pools based on the command line parameters
     * 
     * @param cliParser
     * @throws Exception
     */
    private void buildPoolsFromCommandLine(CommandLineOptions cliParser) throws Exception {
        pools = new ArrayList<Pool>();
        if (cliParser.getPoolHosts() != null) {
            int index = 0;

            if (cliParser.getPoolHosts().size() > 0 && cliParser.getPoolUsers() != null
                    && cliParser.getPoolUsers().size() > 0 && cliParser.getPoolPasswords() != null
                    && cliParser.getPoolPasswords().size() > 0) {

                for (String poolHost : cliParser.getPoolHosts()) {
                    String poolName = poolHost;
                    String username = Constants.DEFAULT_USERNAME;
                    String password = Constants.DEFAULT_PASSWORD;
                    Boolean isExtranonceSubscribe = Boolean.FALSE;
                    Boolean isAppendWorkerNames = Boolean.FALSE;
                    String workerNameSeparator = Constants.DEFAULT_WORKER_NAME_SEPARTOR;
                    Boolean useWorkerPassword = Boolean.FALSE;
                    Integer poolWeight = Constants.DEFAULT_POOL_WEIGHT;

                    if (cliParser.getPoolNames() != null && cliParser.getPoolNames().size() > index) {
                        poolName = cliParser.getPoolNames().get(index);
                    }

                    if (cliParser.getPoolUsers() != null && cliParser.getPoolUsers().size() > index) {
                        username = cliParser.getPoolUsers().get(index);
                    } else {
                        username = cliParser.getPoolUsers().get(cliParser.getPoolUsers().size() - 1);
                        logger.warn("No user defined for pool {}. Using {}.", poolName, username);
                    }

                    if (cliParser.getPoolPasswords() != null && cliParser.getPoolPasswords().size() > index) {
                        password = cliParser.getPoolPasswords().get(index);
                    } else {
                        password = cliParser.getPoolPasswords().get(cliParser.getPoolPasswords().size() - 1);
                        logger.warn("No password defined for pool {}. Using {}.", poolName, password);
                    }

                    if (cliParser.getIsExtranonceSubscribeEnabled() != null
                            && cliParser.getIsExtranonceSubscribeEnabled().size() > index) {
                        isExtranonceSubscribe = cliParser.getIsExtranonceSubscribeEnabled().get(index);
                    }

                    if (cliParser.getPoolsAppendWorkerNames() != null
                            && cliParser.getPoolsAppendWorkerNames().size() > index) {
                        isAppendWorkerNames = cliParser.getPoolsAppendWorkerNames().get(index);
                    }

                    if (cliParser.getPoolsUseWorkerPassword() != null
                            && cliParser.getPoolsUseWorkerPassword().size() > index) {
                        useWorkerPassword = cliParser.getPoolsUseWorkerPassword().get(index);
                    }

                    if (cliParser.getPoolsWorkerNameSeparator() != null
                            && cliParser.getPoolsWorkerNameSeparator().size() > index) {
                        workerNameSeparator = cliParser.getPoolsWorkerNameSeparator().get(index);
                    }

                    if (cliParser.getPoolsWeight() != null && cliParser.getPoolsWeight().size() > index) {
                        poolWeight = cliParser.getPoolsWeight().get(index);
                    }

                    Pool pool = new Pool(poolName, poolHost, username, password);
                    pool.setExtranonceSubscribeEnabled(isExtranonceSubscribe);
                    pool.setNumberOfSubmit(numberOfSubmit);
                    pool.setPriority(index);
                    pool.setConnectionRetryDelay(poolConnectionRetryDelay);
                    pool.setReconnectStabilityPeriod(poolReconnectStabilityPeriod);
                    pool.setNoNotifyTimeout(poolNoNotifyTimeout);
                    pool.setRejectReconnect(isRejectReconnect);
                    pool.setSamplingHashratePeriod(poolHashrateSamplingPeriod);
                    pool.setAppendWorkerNames(isAppendWorkerNames);
                    pool.setWorkerSeparator(workerNameSeparator);
                    pool.setUseWorkerPassword(useWorkerPassword);
                    pool.setWeight(poolWeight);
                    pools.add(pool);

                    index++;
                }
            } else {
                throw new BadParameterException(
                        "At least one user/password (with -u and -p options) has to be provided if a pool host is specified.");
            }

        }
    }

    /**
     * 
     * @return
     */
    public File getLogDirectory() {
        return logDirectory;
    }

    public Integer getStratumListeningPort() {
        return stratumListeningPort;
    }

    public String getStratumBindAddress() {
        return stratumBindAddress;
    }

    public Integer getRestListenPort() {
        return restListenPort;
    }

    public String getRestBindAddress() {
        return restBindAddress;
    }

    public Level getLogLevel() {
        return logLevel;
    }

    public Integer getPoolConnectionRetryDelay() {
        return poolConnectionRetryDelay;
    }

    public Integer getPoolNoNotifyTimeout() {
        return poolNoNotifyTimeout;
    }

    public boolean isRejectReconnect() {
        return isRejectReconnect;
    }

    public Integer getUserHashrateSamplingPeriod() {
        return userHashrateSamplingPeriod;
    }

    public Integer getConnectionHashrateSamplingPeriod() {
        return connectionHashrateSamplingPeriod;
    }

    public Integer getGetworkListenPort() {
        return getworkListenPort;
    }

    public String getGetworkBindAddress() {
        return getworkBindAddress;
    }

    public boolean isScrypt() {
        return isScrypt;
    }

    public File getConfigurationFile() {
        return configurationFile;
    }

    /**
     * Initialize the logging system
     * 
     * @param cliParser
     */
    private void initLogging() {
        if (logDirectory == null) {
            System.err.println("Log directory not set. Use the tmp OS directory.");
            logDirectory = new File(System.getProperty("java.io.tmpdir"));
        } else {
            if (!logDirectory.exists()) {
                System.err
                        .println("Log directory " + logDirectory.getAbsolutePath() + "does not exist. Create it.");
                boolean isCreated = logDirectory.mkdirs();
                if (!isCreated) {
                    System.err.println("Failed to create the log directory " + logDirectory.getAbsolutePath()
                            + ". Use the tmp OS directory.");
                    logDirectory = new File(System.getProperty("java.io.tmpdir"));
                }
            }
        }

        System.out.println("Use log directory " + logDirectory.getAbsolutePath());

        // Set the directory used for logging.
        System.setProperty("log.directory.path", logDirectory.getAbsolutePath());

        // If disableLogAppend is true, do not use the rollingFileAppender
        // but a simple fileAppender with append to false.
        if (disableLogAppend) {
            RollingFileAppender rollingFileAppender = (RollingFileAppender) LogManager.getRootLogger()
                    .getAppender("file");
            LogManager.getRootLogger().removeAppender(rollingFileAppender);
            try {
                FileAppender fileAppender = new FileAppender(rollingFileAppender.getLayout(),
                        rollingFileAppender.getFile(), false);
                fileAppender.setName(rollingFileAppender.getName());
                LogManager.getRootLogger().addAppender(fileAppender);
            } catch (IOException e) {
                System.out.println("Failed to create the log appender with append=false.");
                e.printStackTrace();
            }
        }

        // Set the log level.
        String logLevelMessage = null;
        if (logLevel == null) {
            logLevel = Level.INFO;
            logLevelMessage = "LogLevel not set, using INFO.";
        } else {
            logLevelMessage = "Using " + logLevel.toString() + " LogLevel.";
        }
        LogManager.getRootLogger().setLevel(logLevel);

        logger = LoggerFactory.getLogger(Launcher.class);
        logger.info(logLevelMessage);

        // Set the API log level
        if (apiLogLevel == null) {
            logger.info("API log level not set. API logging disabled.");
        } else {
            if (apiLogLevel == Level.OFF) {
                logger.info("API log level set to OFF. API logging disabled.");
            } else {
                logger.info("API log level set to {}.", apiLogLevel.toString());
                // Remove existing handlers attached to j.u.l root logger
                SLF4JBridgeHandler.removeHandlersForRootLogger();

                // Add SLF4JBridgeHandler to j.u.l's root logger
                SLF4JBridgeHandler.install();

                // Set the log level of the log4j logger to match the log level
                // of the JUL logger.
                LogManager.getLogger("org.glassfish").setLevel(apiLogLevel);

                java.util.logging.LogManager.getLogManager().getLogger("")
                        .setLevel(getJULLevelFromLOG4JLevel(apiLogLevel));
            }

        }

    }

    /**
     * Check that the database directory is set and exists.. If not set, use the
     * default directory. If does not exist, create it.
     * 
     * @throws FileNotFoundException
     *             if the database directory cannot be created.
     */
    private void initDatabaseDirectory() throws FileNotFoundException {
        // When database directory is not given by the user.
        if (databaseDirectory == null) {
            databaseDirectory = new File(getInstallDirectory(), "database");

            logger.info("Database directory not specified. Using default one: {}.",
                    databaseDirectory.getAbsolutePath());

            if (!databaseDirectory.exists()) {
                logger.info("Default database directory does not exist. Create directory: {}.",
                        databaseDirectory.getAbsolutePath());
                if (!databaseDirectory.mkdirs()) {
                    throw new FileNotFoundException(
                            "Failed to create the directory " + databaseDirectory.getAbsolutePath());
                }
            }
        } else {
            logger.info("Using database directory: {}.", databaseDirectory.getAbsolutePath());
            if (!databaseDirectory.exists()) {
                logger.info("Database directory does not exist. Create directory: {}.",
                        databaseDirectory.getAbsolutePath());
                if (!databaseDirectory.mkdirs()) {
                    throw new FileNotFoundException(
                            "Failed to create the directory " + databaseDirectory.getAbsolutePath());
                }
            }
        }
    }

    /**
     * Return the version of the program
     * 
     * @return
     */
    public static String getVersion() {
        if (version == null) {
            version = "Dev";

            Class<Launcher> clazz = Launcher.class;
            String className = clazz.getSimpleName() + ".class";
            String classPath = clazz.getResource(className).toString();
            if (classPath.startsWith("jar")) {
                // Class not from JAR
                String manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1)
                        + "/META-INF/MANIFEST.MF";

                try {
                    Manifest manifest = new Manifest(new URL(manifestPath).openStream());
                    Attributes attr = manifest.getMainAttributes();
                    version = attr.getValue("Implementation-Version");
                } catch (IOException e) {
                    // Do nothing, just return Unknown as version
                    version = "Unknown";
                }
            }
        }

        return version;
    }

    /**
     * Return the installation directory of the application.
     */
    public static final String getInstallDirectory() {
        return getJarFile().getParent();
    }

    /**
     * Return the path of the application jar file.
     * 
     * @return
     */
    public static final String getJarFilePath() {
        return getJarFile().getAbsolutePath();
    }

    /**
     * Return the jar file.
     * 
     * @return
     */
    private static File getJarFile() {
        Path absolutePath = null;
        try {
            absolutePath = Paths
                    .get(ConfigurationManager.class.getProtectionDomain().getCodeSource().getLocation().toURI());
        } catch (URISyntaxException e) {
            String errorMessage = "Failed to get the installation directory.";
            if (logger != null) {
                logger.error(errorMessage, e);
            } else {
                System.err.println(e);
                e.printStackTrace();
            }
        }
        return absolutePath.toFile();
    }

    /**
     * Return the Java Util Logging level from the Log4J level.
     * 
     * @param level
     * @return
     */
    private static java.util.logging.Level getJULLevelFromLOG4JLevel(Level level) {
        java.util.logging.Level result = java.util.logging.Level.INFO;
        if (Level.ALL == level) {
            result = java.util.logging.Level.ALL;
        } else if (Level.FATAL == level) {
            result = java.util.logging.Level.SEVERE;
        } else if (Level.ERROR == level) {
            result = java.util.logging.Level.SEVERE;
        } else if (Level.WARN == level) {
            result = java.util.logging.Level.WARNING;
        } else if (Level.INFO == level) {
            result = java.util.logging.Level.INFO;
        } else if (Level.DEBUG == level) {
            result = java.util.logging.Level.FINE;
        } else if (Level.TRACE == level) {
            result = java.util.logging.Level.FINEST;
        } else if (Level.OFF == level) {
            result = java.util.logging.Level.OFF;
        }
        return result;
    }

    /**
     * Return true only if the application is running from the jar file.
     * 
     * @return
     */
    public static boolean isRunningFromJar() {
        String className = ConfigurationManager.class.getName().replace('.', '/');
        String classJar = ConfigurationManager.class.getResource("/" + className + ".class").toString();
        return classJar.startsWith("jar:");
    }

    public File getDatabaseDirectory() {
        return databaseDirectory;
    }

    public Integer getHashrateDatabaseSamplingPeriod() {
        return hashrateDatabaseSamplingPeriod;
    }

    public Integer getHashrateDatabaseHistoryDepth() {
        return hashrateDatabaseHistoryDepth;
    }

    public boolean isNoMidsate() {
        return noMidsate;
    }

    public boolean isValidateGetworkShares() {
        return validateGetworkShares;
    }

    public String getPoolSwitchingStrategy() {
        return poolSwitchingStrategy;
    }

    public Integer getWeightedRoundRobinRoundDuration() {
        return weightedRoundRobinRoundDuration;
    }

    public boolean isDisableGetwork() {
        return disableGetwork;
    }

    public boolean isDisableStratum() {
        return disableStratum;
    }

    public boolean isDisableApi() {
        return disableApi;
    }

    public boolean isDisableLogAppend() {
        return disableLogAppend;
    }

    public String getApiUser() {
        return apiUser;
    }

    public String getApiPassword() {
        return apiPassword;
    }

    public Boolean getApiReadOnlyAccessEnabled() {
        return apiReadOnlyAccessEnabled;
    }

    public Boolean getApiEnableSsl() {
        return apiEnableSsl;
    }

    public Boolean getLogRealShareDifficulty() {
        return logRealShareDifficulty;
    }

    public Integer getExtranonce1TailSize() {
        return extranonce1TailSize;
    }

    public String getIpVersion() {
        return ipVersion;
    }

    public Double getSuggestedPoolDifficulty() {
        return suggestedPoolDifficulty;
    }

}