org.openbase.bco.manager.util.launch.BCOSystemValidator.java Source code

Java tutorial

Introduction

Here is the source code for org.openbase.bco.manager.util.launch.BCOSystemValidator.java

Source

package org.openbase.bco.manager.util.launch;

/*-
 * #%L
 * BCO Manager Utility
 * %%
 * Copyright (C) 2015 - 2018 openbase.org
 * %%
 * 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 this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */

import org.apache.commons.collections.comparators.BooleanComparator;
import org.openbase.bco.dal.lib.layer.unit.UnitRemote;
import org.openbase.bco.dal.remote.unit.Units;
import org.openbase.bco.registry.lib.BCO;
import org.openbase.bco.registry.lib.com.AbstractVirtualRegistryRemote;
import org.openbase.bco.registry.remote.Registries;
import org.openbase.jps.core.JPService;
import org.openbase.jps.preset.JPDebugMode;
import org.openbase.jps.preset.JPVerbose;
import org.openbase.jul.exception.CouldNotPerformException;
import org.openbase.jul.exception.FatalImplementationErrorException;
import org.openbase.jul.exception.printer.ExceptionPrinter;
import org.openbase.jul.pattern.Remote;
import org.openbase.jul.pattern.Remote.ConnectionState;
import org.openbase.jul.processing.StringProcessor;
import org.openbase.jul.processing.StringProcessor.Alignment;
import org.openbase.jul.storage.registry.RegistryRemote;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.*;

/**
 * @author <a href="mailto:divine@openbase.org">Divine Threepwood</a>
 */
public class BCOSystemValidator {

    public static final String OK = "OK";
    public static final int STATE_RANGE = 12;
    public static final int LABEL_RANGE = 22;
    public static final long DELAYED_TIME = TimeUnit.MILLISECONDS.toMillis(500);
    public static final long DEFAULT_UNIT_POOL_DELAY_TIME = TimeUnit.SECONDS.toMillis(5);
    public static final long REQUEST_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
    public static final DecimalFormat pingFormat = new DecimalFormat("#.###");
    protected static final Logger LOGGER = LoggerFactory.getLogger(BCOSystemValidator.class);
    private static int errorCounter = 0;
    private static double globalPingAverage = 0;
    private static double globalPingComputations = 0;

    public static void countError() {
        ++errorCounter;
    }

    public static void main(String[] args) {
        BCO.printLogo();
        JPService.setApplicationName("bco-validate");
        JPService.registerProperty(JPDebugMode.class);
        JPService.registerProperty(JPVerbose.class);
        JPService.parseAndExitOnError(args);

        try {
            System.out.println("==================================================");
            System.out.println("BaseCubeOne - System Validator");
            System.out.println("==================================================");
            System.out.println();

            System.out.println("=== " + AnsiColor.colorize("Check Registries", AnsiColor.ANSI_BLUE) + " ===\n");

            final BooleanComparator trueFirstBooleanComparator = new BooleanComparator(true);
            final BooleanComparator falseFirstBooleanComparator = new BooleanComparator(false);
            // check
            List<RegistryRemote> registries = Registries.getRegistries(false);
            registries.sort((registryRemote, t1) -> falseFirstBooleanComparator.compare(
                    registryRemote instanceof AbstractVirtualRegistryRemote,
                    t1 instanceof AbstractVirtualRegistryRemote));
            for (final RegistryRemote registry : registries) {
                if (!check(registry, TimeUnit.SECONDS.toMillis(2))) {
                    if (registry.isConsistent()) {
                        System.out.println(
                                StringProcessor.fillWithSpaces(registry.getName(), LABEL_RANGE, Alignment.RIGHT)
                                        + "  " + AnsiColor.colorize(OK, AnsiColor.ANSI_GREEN));
                    } else {
                        System.out.println(
                                StringProcessor.fillWithSpaces(registry.getName(), LABEL_RANGE, Alignment.RIGHT)
                                        + "  " + AnsiColor.colorize("INCONSISTENT", AnsiColor.ANSI_RED));
                    }
                }
            }
            System.out.println();

            System.out.println("=== " + AnsiColor.colorize("Check Units", AnsiColor.ANSI_BLUE) + " ===\n");
            Future<List<UnitRemote<?>>> futureUnits = Units.getFutureUnits(false);

            System.out.println(StringProcessor.fillWithSpaces("Unit Pool", LABEL_RANGE, Alignment.RIGHT) + "  "
                    + check(futureUnits, DEFAULT_UNIT_POOL_DELAY_TIME));
            System.out.println();

            if (futureUnits.isCancelled()) {
                System.out.println(AnsiColor.colorize(
                        "Connection could not be established, please make sure BaseCubeOne is up and running!\n",
                        AnsiColor.ANSI_YELLOW));
                try {
                    futureUnits.get();
                } catch (ExecutionException | CancellationException ex) {
                    ExceptionPrinter.printHistory("Error Details", ex, System.err);
                }
            } else {
                boolean printed = false;
                final List<UnitRemote<?>> unitList = new ArrayList<>(futureUnits.get());
                unitList.sort((unitRemote, t1) -> {
                    try {
                        return trueFirstBooleanComparator.compare(unitRemote.isDalUnit(), t1.isDalUnit());
                    } catch (CouldNotPerformException ex) {
                        LOGGER.warn("Could not compare unit[" + unitRemote + "] and unit[" + t1 + "]", ex);
                        return 0;
                    }
                });
                for (final UnitRemote<?> unit : unitList) {
                    printed = check(unit) || printed;
                }
                if (!printed) {
                    System.out.println(
                            StringProcessor.fillWithSpaces("Unit Connections", LABEL_RANGE, Alignment.RIGHT) + "  "
                                    + AnsiColor.colorize(OK, AnsiColor.ANSI_GREEN));
                }
            }
        } catch (InterruptedException ex) {
            System.out.println("killed");
            System.exit(253);
            return;
        } catch (Exception ex) {
            ExceptionPrinter.printHistory(new CouldNotPerformException("Could not validate system!", ex),
                    System.err);
            System.exit(254);
        }

        System.out.println();
        System.out.println("==============================================================");
        System.out.print("===  ");
        switch (errorCounter) {
        case 0:
            System.out.print(AnsiColor.colorize("VALIDATION SUCCESSFUL", AnsiColor.ANSI_GREEN));
            break;
        default:
            System.out.print(errorCounter + " " + AnsiColor
                    .colorize("ERROR" + (errorCounter > 1 ? "S" : "") + " DETECTED", AnsiColor.ANSI_RED));
            break;
        }
        System.out.println(" average ping is "
                + AnsiColor.colorize(pingFormat.format(getGlobalPing()), AnsiColor.ANSI_CYAN) + " milli");
        System.out.println("==============================================================");
        System.out.println();
        System.exit(Math.min(errorCounter, 200));
    }

    public static String check(final Future future) {
        return check(future, DELAYED_TIME, null);
    }

    public static String check(final Future future, final long delayTime) {
        return check(future, delayTime, null);
    }

    public static String check(final Future future, final String suffixCallable) {
        return check(future, DELAYED_TIME, null);
    }

    public static String check(final Future future, final long delayTime, final Callable<String> suffixCallable) {
        try {
            try {
                future.get(delayTime, TimeUnit.MILLISECONDS);
            } catch (TimeoutException e) {
                future.get(REQUEST_TIMEOUT, TimeUnit.MILLISECONDS);
                return AnsiColor.colorize(StringProcessor.fillWithSpaces("DELAYED", STATE_RANGE, Alignment.LEFT),
                        AnsiColor.ANSI_GREEN);
            }
        } catch (InterruptedException ex) {
            countError();
            return AnsiColor.colorize(StringProcessor.fillWithSpaces("INTERRUPTED", STATE_RANGE, Alignment.LEFT),
                    AnsiColor.ANSI_YELLOW);
        } catch (ExecutionException ex) {
            countError();
            return AnsiColor.colorize(StringProcessor.fillWithSpaces("FAILED", STATE_RANGE, Alignment.LEFT),
                    AnsiColor.ANSI_RED);
        } catch (TimeoutException ex) {
            countError();
            return AnsiColor.colorize(StringProcessor.fillWithSpaces("TIMEOUT", STATE_RANGE, Alignment.LEFT),
                    AnsiColor.ANSI_CYAN);
        }

        String suffix;
        try {
            suffix = (suffixCallable != null ? suffixCallable.call() : "");
        } catch (Exception e) {
            suffix = "?";
        }

        return AnsiColor.colorize(StringProcessor.fillWithSpaces("OK" + suffix, STATE_RANGE, Alignment.LEFT),
                AnsiColor.ANSI_GREEN);
    }

    public static boolean check(final Remote<?> remote) {
        return check(remote, DELAYED_TIME);
    }

    public static boolean check(final Remote<?> remote, final long delayTime) {

        boolean printed = false;

        Future<Long> futurePing = remote.ping();

        try {
            printed |= print(remote, StringProcessor.fillWithSpaces("Ping", LABEL_RANGE, Alignment.RIGHT) + "  "
                    + check(futurePing, delayTime, () -> {
                        if (futurePing.isDone() && !futurePing.isCancelled()) {
                            BCOSystemValidator.computeGlobalPing(futurePing.get());
                            return " (" + futurePing.get() + ")";
                        } else {
                            return "";
                        }
                    }));
        } catch (CancellationException ex) {
            throw ex;
        } catch (Exception ex) {
            ExceptionPrinter.printHistory(new FatalImplementationErrorException(BCOSystemValidator.class, ex),
                    LOGGER);
        }

        boolean online = futurePing.isDone() && !futurePing.isCancelled();

        if (online) {
            // ping does not cause the connection state to be connected, this is done by a data update, therefore wait a bit for the connection state
            try {
                remote.waitForConnectionState(ConnectionState.CONNECTED, delayTime);
            } catch (org.openbase.jul.exception.TimeoutException ex) {
                // just continue and print error for next step
            } catch (CouldNotPerformException ex) {
                ExceptionPrinter.printHistory(ex, LOGGER);
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
            final ConnectionState connectionState = remote.getConnectionState();
            String connectionDescription = StringProcessor.fillWithSpaces("Connection", LABEL_RANGE,
                    Alignment.RIGHT) + "  ";
            switch (connectionState) {
            case CONNECTED:
                connectionDescription += AnsiColor.colorize(
                        StringProcessor.fillWithSpaces(OK, STATE_RANGE, Alignment.LEFT), AnsiColor.ANSI_GREEN);
                break;
            case DISCONNECTED:
            case CONNECTING:
                countError();
                connectionDescription += AnsiColor.colorize(
                        StringProcessor.fillWithSpaces(connectionState.name(), STATE_RANGE, Alignment.LEFT),
                        AnsiColor.ANSI_RED);
                break;
            case RECONNECTING:
                connectionDescription += AnsiColor.colorize(
                        StringProcessor.fillWithSpaces(connectionState.name(), STATE_RANGE, Alignment.LEFT),
                        AnsiColor.ANSI_YELLOW);
                break;
            default:
                connectionDescription += AnsiColor.colorize(
                        StringProcessor.fillWithSpaces("UNKNOWN", STATE_RANGE, Alignment.LEFT), AnsiColor.ANSI_RED);
                countError();
                break;
            }
            printed |= print(remote, connectionDescription);
        }

        if (online) {
            printed |= print(remote, StringProcessor.fillWithSpaces("Data Cache", LABEL_RANGE, Alignment.RIGHT)
                    + "  " + check(remote.getDataFuture(), delayTime));
        } else {
            printed |= print(remote,
                    StringProcessor.fillWithSpaces("Data Cache", LABEL_RANGE, Alignment.RIGHT) + "  "
                            + (remote.isDataAvailable()
                                    ? AnsiColor.colorize(
                                            StringProcessor.fillWithSpaces("OFFLINE", STATE_RANGE, Alignment.LEFT),
                                            AnsiColor.ANSI_YELLOW)
                                    : AnsiColor.colorize(
                                            StringProcessor.fillWithSpaces("EMPTY", STATE_RANGE, Alignment.LEFT),
                                            AnsiColor.ANSI_RED)));
        }

        if (online) {
            try {
                printed |= print(remote,
                        StringProcessor.fillWithSpaces("Synchronization", LABEL_RANGE, Alignment.RIGHT) + "  "
                                + check(remote.requestData(), delayTime));
            } catch (CouldNotPerformException e) {
                countError();
                printed |= print(remote,
                        StringProcessor.fillWithSpaces("Synchronization", LABEL_RANGE, Alignment.RIGHT) + "  "
                                + AnsiColor.colorize(
                                        StringProcessor.fillWithSpaces("CANCELED", STATE_RANGE, Alignment.LEFT),
                                        AnsiColor.ANSI_RED));
            }
        }

        // add new line separator for better overview
        if (printed) {
            System.out.println();
        }

        return printed;
    }

    public static boolean print(final Remote remote, final String result) {
        // only print in error case or if verbose mode is enabled.
        if (!result.contains(OK) || JPService.verboseMode()) {
            System.out.println(result + "[" + remote + "]");
            return true;
        }
        return false;
    }

    public synchronized static double computeGlobalPing(long ping) {

        // skip 0 ping.
        if (ping <= 0) {
            return globalPingAverage;
        }
        globalPingAverage = (globalPingComputations * globalPingAverage + ping) / (globalPingComputations + 1);
        globalPingComputations++;
        return globalPingAverage;
    }

    private static double getGlobalPing() {
        return globalPingAverage;
    }

    public enum AnsiColor {

        // todo: move to jul
        ANSI_RESET("\u001B[0m"), ANSI_BLACK("\u001B[30m"), ANSI_RED("\u001B[31m"), ANSI_GREEN(
                "\u001B[32m"), ANSI_YELLOW("\u001B[33m"), ANSI_BLUE(
                        "\u001B[34m"), ANSI_PURPLE("\u001B[35m"), ANSI_CYAN("\u001B[36m"), ANSI_WHITE("\u001B[37m");

        private String color;

        private AnsiColor(String color) {
            this.color = color;
        }

        public static String colorize(final String text, final AnsiColor color) {
            return color.getColor() + text + ANSI_RESET.getColor();
        }

        public static String colorizeRegex(final String text, final String regex, final AnsiColor color) {
            return text.replaceAll(regex, colorize(regex, color));
        }

        public String getColor() {
            return color;
        }
    }
}