uk.dsxt.voting.tests.TestsLauncher.java Source code

Java tutorial

Introduction

Here is the source code for uk.dsxt.voting.tests.TestsLauncher.java

Source

/******************************************************************************
 * e-voting system                                                            *
 * Copyright (C) 2016 DSX Technologies Limited.                               *
 * *
 * 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 2 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 can find copy of the GNU General Public License in LICENSE.txt file    *
 * at the top-level directory of this distribution.                           *
 * *
 * Removal or modification of this copyright notice is prohibited.            *
 * *
 ******************************************************************************/

package uk.dsxt.voting.tests;

import lombok.extern.log4j.Log4j2;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.joda.time.Instant;
import uk.dsxt.voting.client.VotingClientMain;
import uk.dsxt.voting.common.registries.RegistriesServer;
import uk.dsxt.voting.common.registries.RegistriesServerWeb;
import uk.dsxt.voting.common.utils.InternalLogicException;
import uk.dsxt.voting.common.utils.PropertiesHelper;
import uk.dsxt.voting.registriesserver.RegistriesServerMain;
import uk.dsxt.voting.resultsbuilder.ResultsBuilderMain;

import java.io.File;
import java.io.FileOutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

@Log4j2
public class TestsLauncher implements BaseTestsLauncher {

    private static Map<String, Process> processesByName = new HashMap<>();

    private static boolean startClientsAsProcesses = true;

    private static String masterAccount;
    private static String masterPassword;

    @FunctionalInterface
    public interface SimpleRequest {
        void run();
    }

    public static void main(String[] args) {
        try {
            log.debug("Starting module {}...", MODULE_NAME);
            //delete old files
            deleteNxtFiles();
            //read configuration
            Properties properties = PropertiesHelper.loadProperties(MODULE_NAME);
            int votingDuration = Integer.valueOf(properties.getProperty("voting.duration.minutes"));
            String testingType = properties.getProperty("testing.type");
            log.info("Testing type is {}", testingType);
            String registriesServerUrl = properties.getProperty("register.server.url");
            int connectionTimeout = Integer.parseInt(properties.getProperty("http.connection.timeout"));
            int readTimeout = Integer.parseInt(properties.getProperty("http.read.timeout"));
            int resultsCheckPeriod = Integer.parseInt(properties.getProperty("results.check.period"));
            int clientAggregationPeriod = Integer
                    .parseInt(properties.getProperty("client.results.aggregation.period"));
            String allowedHosts = properties.getProperty("allowed.hosts");

            masterAccount = properties.getProperty("master.address");
            masterPassword = properties.getProperty("master.passphrase");

            NXTAccount[] nxtAccounts = PropertiesHelper.loadResource("json/nxtAccounts.json", NXTAccount[].class);
            log.info("Found {} accounts.", nxtAccounts.length);

            //json file configuration for clients
            ClientConfiguration[] configurations = PropertiesHelper.loadResource(properties, testingType,
                    "client.config.file", ClientConfiguration[].class);
            startClientsAsProcesses = Boolean.parseBoolean(properties.getProperty("testing.clients_as_processes"));

            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < configurations.length; i++) {
                if (configurations[i].isVictim())
                    ;
                builder.append(nxtAccounts[i].getAccount());
                builder.append(";");
            }
            String victimAccounts = builder.toString();
            if (victimAccounts.length() > 0)
                victimAccounts = victimAccounts.substring(0, victimAccounts.length() - 1);

            //starting single modules
            startSingleModule(RegistriesServerMain.MODULE_NAME,
                    () -> RegistriesServerMain.main(new String[] { testingType, String.valueOf(votingDuration) }));
            startSingleModule(ResultsBuilderMain.MODULE_NAME,
                    () -> ResultsBuilderMain.main(new String[] { String.valueOf(resultsCheckPeriod) }));
            //load properties and set master node to offline mode

            Properties nxtProperties = PropertiesHelper.loadProperties("nxt-default");
            nxtProperties.setProperty("nxt.enablePeerServerDoSFilter", "true");
            nxtProperties.setProperty("nxt.peerServerDoSFilter.maxRequestMs", "300000");
            nxtProperties.setProperty("nxt.peerServerDoSFilter.delayMs", "1000");
            nxtProperties.setProperty("nxt.peerServerDoSFilter.maxRequestsPerSec", "3000");
            nxtProperties.setProperty("nxt.sendToPeersLimit", "1000");
            nxtProperties.setProperty("nxt.testUnconfirmedTransactions", "true");
            nxtProperties.setProperty("nxt.pushThreshold", "0");

            nxtProperties.setProperty("nxt.evt.sendNxtBlackList", String.format("%s", masterAccount));

            final String propertiesPath = createWalletPropertiesFile(MASTER_NAME, 7872, nxtProperties,
                    allowedHosts);
            //starting clients
            long start = Instant.now().getMillis();
            log.debug("Starting {} instances of {}", configurations.length, VotingClientMain.MODULE_NAME);
            int startPort = 9000;
            for (int i = 0; i < configurations.length; i++) {
                final int ii = i;
                ClientConfiguration conf = configurations[i];
                String clientName = String.format("nxt-node-%s", conf.getHolderId());
                String blackList = !conf.isHonestParticipant() ? victimAccounts : "";
                nxtProperties.setProperty("nxt.evt.blackList", blackList);
                String clientPropertiesPath = createWalletPropertiesFile(clientName, startPort + 2 * i,
                        nxtProperties, allowedHosts);
                String walletOffSchedule = conf.getDisconnectMask() == null ? ";" : conf.getDisconnectMask();
                startClient(ii, configurations, clientPropertiesPath, walletOffSchedule, clientAggregationPeriod,
                        nxtAccounts);
            }
            log.info("{} instances of {} started in {} ms", configurations.length, RegistriesServerMain.MODULE_NAME,
                    Instant.now().getMillis() - start);
            //need to wait until voting is complete
            RegistriesServer regServer = new RegistriesServerWeb(registriesServerUrl, connectionTimeout,
                    readTimeout);
            /*
            Voting[] votings = regServer.getVotings();
            if (votings.length > 1) {
            log.error("There is more than one voting. Stopping {}", MODULE_NAME);
            return;
            }
            Voting currentVoting = regServer.getVotings()[0];
            long sleepPeriod = currentVoting.getEndTimestamp() - Instant.now().getMillis();
            log.info("Waiting {} seconds while voting ends", sleepPeriod / 1000);
            Thread.sleep(sleepPeriod);
            */
            log.info("----------------------------- TEST FINISHED -----------------------------");
            // TODO: check that results builder has finished calculating results
            Thread.sleep(60 * 60 * 1000);
            stopAllProcesses();
            //stop jetty servers
            RegistriesServerMain.shutdown();
            ResultsBuilderMain.shutdown();
            log.info("Testing finished");
        } catch (Exception e) {
            log.error(String.format("Error occurred in module %s", MODULE_NAME), e);
        }
    }

    private static String createWalletPropertiesFile(String nodeName, int port, Properties nxtProperties,
            String allowedHosts) {
        String clientPropertiesPath = String.format("conf/%s.properties", nodeName);
        final Path path = Paths.get(".", DB_FOLDER, nodeName);
        nxtProperties.setProperty("nxt.apiServerPort", String.valueOf(port));
        nxtProperties.setProperty("nxt.peerServerPort", String.valueOf(port + 1));
        nxtProperties.setProperty("nxt.defaultTestnetPeers", DEFAULT_TESTNET_PEERS);
        nxtProperties.setProperty("nxt.isOffline", "false");
        nxtProperties.setProperty("nxt.isTestnet", "true");
        nxtProperties.setProperty("nxt.testDbDir", path.toString());
        nxtProperties.setProperty("nxt.minNeedBlocks", "1");
        nxtProperties.setProperty("nxt.testnetGuaranteedBalanceConfirmations", "1");
        nxtProperties.setProperty("nxt.allowedUserHosts", allowedHosts);
        nxtProperties.setProperty("nxt.allowedBotHosts", allowedHosts);
        nxtProperties.setProperty("nxt.apiServerHost", "0.0.0.0");
        nxtProperties.setProperty("nxt.forceScan", "true");
        //nxtProperties.setProperty("nxt.disableAdminPassword", "true");
        //nxtProperties.setProperty("nxt.forgingSpeedup", "10");
        saveProperties(clientPropertiesPath, nxtProperties);
        return clientPropertiesPath;
    }

    private static void startClient(int idx, ClientConfiguration[] configurations, String clientPropertiesPath,
            String walletOffSchedule, int clientAggregationPeriod, NXTAccount[] nxtAccounts) {
        ClientConfiguration conf = configurations[idx];
        final String password = nxtAccounts[idx].getPassword();
        if (startClientsAsProcesses) {
            startProcess("Client" + idx, CLIENT_JAR_PATH, new String[] { clientPropertiesPath, masterAccount,
                    password, conf.getHolderId(), conf.getPrivateKey(),
                    conf.getVote() == null || conf.getVote().isEmpty() ? "#" : conf.getVote(), walletOffSchedule,
                    String.valueOf(clientAggregationPeriod), idx == 0 ? "true" : "false" });
        } else {
            VotingClientMain.main(new String[] { clientPropertiesPath, masterAccount, password, conf.getHolderId(),
                    conf.getPrivateKey(), conf.getVote(), walletOffSchedule,
                    String.valueOf(clientAggregationPeriod) });
        }
    }

    private static void startSingleModule(String name, SimpleRequest request) {
        log.debug("Starting {}", name);
        long start = Instant.now().getMillis();
        request.run();
        log.info("{} started in {} ms", name, Instant.now().getMillis() - start);
    }

    private static void saveProperties(String path, Properties properties) {
        try (FileOutputStream fos = new FileOutputStream(path)) {
            properties.store(fos, "");
        } catch (Exception e) {
            String errorMessage = String.format("Can't save property. Error: %s", e.getMessage());
            log.error(errorMessage, e);
            throw new RuntimeException(errorMessage);
        }
    }

    private static void deleteNxtFiles() throws Exception {
        File file = new File(System.getProperty("user.dir"));
        if (file.listFiles() == null) {
            throw new InternalLogicException(
                    String.format("Couldn't delete files - the path is incorrect: %s", file.getAbsolutePath()));
        }
        for (File c : file.listFiles()) {
            if (c.getName().contains("nxt")) {
                log.info("Deleting {}...", c.getName());
                if (c.isDirectory()) {
                    FileUtils.deleteDirectory(c);
                } else {
                    c.delete();
                }
            }
        }
    }

    private static void startProcess(String name, String jarPath, String[] params) {
        if (processesByName.containsKey(name)) {
            stopProcess(name);
        }
        try {
            List<String> cmd = new ArrayList<>();
            cmd.add("java");
            cmd.add("-jar");
            cmd.add(jarPath);
            Collections.addAll(cmd, params);

            log.debug("Start process command: {}", StringUtils.join(cmd, " "));

            ProcessBuilder processBuilder = new ProcessBuilder();
            processBuilder.command(cmd);
            processBuilder.redirectError(new File(LOGS_FOLDER, String.format("error_%s.log", name)));
            processBuilder.redirectOutput(new File(LOGS_FOLDER, String.format("output_%s.log", name)));
            Process process = processBuilder.start();
            processesByName.put(name, process);
            log.info("Process {} started", name);
        } catch (Exception e) {
            log.error(String.format("Can't run process %s", name), e);
        }
    }

    private static void stopAllProcesses() {
        for (Map.Entry<String, Process> processEntry : processesByName.entrySet()) {
            try {
                processEntry.getValue().destroy();
                log.info("Process {} killed", processEntry.getKey());
            } catch (Exception e) {
                log.error(String.format("Can't kill process %s", processEntry.getKey()), e);
            }
        }
    }

    private static void stopProcess(String name) {
        Process process = processesByName.get(name);
        if (process == null) {
            return;
        }
        try {
            process.destroy();
            log.info("Process {} killed", name);
        } catch (Exception e) {
            log.error(String.format("Can't kill process %s", name), e);
        }
        processesByName.remove(name);
    }

    private static NXTAccount[] loadNxtAccounts(String content) {
        List<NXTAccount> accs = new ArrayList<>();
        String[] lines = content.split(System.lineSeparator());
        for (String line : lines) {
            String[] account = line.split(":");
            accs.add(new NXTAccount(account[0], account[1]));
        }
        return accs.toArray(new NXTAccount[accs.size()]);
    }
}