package org.datatransferproject.bootstrap.vm;

import com.google.common.util.concurrent.UncaughtExceptionHandlers;
import org.datatransferproject.api.ApiMain;
import org.datatransferproject.api.launcher.Monitor;
import org.datatransferproject.api.launcher.Version;
import org.datatransferproject.transfer.WorkerMain;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.InputStream;
import java.security.KeyStore;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;

import static java.lang.String.format;
import static org.datatransferproject.launcher.monitor.MonitorLoader.loadMonitor;

/**
 * Bootstraps all services (Gateway and 1..N Workers) in a single VM.
 *
 * <p>Intended for demonstration purposes.
 */
public class SingleVMMain {
  private final Consumer<Exception> errorCallback;
  private ExecutorService executorService;

  public static void main(String[] args) {
    Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandlers.systemExit());

    SingleVMMain singleVMMain = new SingleVMMain(SingleVMMain::exitError);

    Runtime.getRuntime().addShutdownHook(new Thread(singleVMMain::shutdown));

    // TODO make number of workers configurable
    singleVMMain.initializeWorkers(1);
    singleVMMain.initializeGateway();
  }

  public SingleVMMain(Consumer<Exception> errorCallback) {
    this.errorCallback = errorCallback;
  }

  public void initializeGateway() {
    Monitor monitor = loadMonitor();

    String version = Version.getVersion();
    String hash = Version.getSourceHash();

    monitor.info(() -> format("Running version %s. Source repository hash %s", version, hash));

    ApiMain apiMain = new ApiMain(monitor);

    try (InputStream stream =
        ApiMain.class.getClassLoader().getResourceAsStream("demo-selfsigned-keystore.jks")) {
      if (stream == null) {
        throw new IllegalArgumentException("Demo keystore was not found");
      }

      // initialise the keystore
      char[] password = "password".toCharArray();
      KeyStore keyStore = KeyStore.getInstance("JKS");
      keyStore.load(stream, password);
      KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
      keyManagerFactory.init(keyStore, password);

      TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
      trustManagerFactory.init(keyStore);

      apiMain.initializeHttps(trustManagerFactory, keyManagerFactory, keyStore);

      apiMain.start();

    } catch (Exception e) {
      errorCallback.accept(e);
    }
  }

  public void initializeWorkers(int workers) {
    if (workers < 1) {
      errorCallback.accept(new IllegalArgumentException("Invalid number of workers: " + workers));
      return;
    }

    executorService = Executors.newFixedThreadPool(workers);

    for (int i = 0; i < workers; i++) {
      WorkerRunner workerRunner = new WorkerRunner();
      executorService.submit(workerRunner);
    }
  }

  public void shutdown() {
    if (executorService != null) {
      executorService.shutdownNow();
    }
  }

  private static void exitError(Exception exception) {
    exception.printStackTrace();
    System.err.println("Exiting abnormally");
    System.exit(-1);
  }

  private class WorkerRunner implements Runnable {
    public void run() {
      //noinspection InfiniteLoopStatement
      while (true) {
        WorkerMain workerMain = new WorkerMain();
        try {
          workerMain.initialize();
        } catch (Exception e) {
          errorCallback.accept(e);
        }
        try {
          workerMain.poll();
        } catch (Exception e) {
          // TODO Logger
          e.printStackTrace();
        }
      }
    }
  }
}

@@ -51,119 +51,53 @@
 ort 
-com.google.common.util.concurrent.UncaughtExceptionHandlers;%0Aimport org.datatransferproject.api.ApiMain;%0Aimport
+static java.lang.String.format;%0Aimport static
  org
@@ -109,36 +109,32 @@
 transferproject.
-api.
 launcher.Monitor
@@ -130,203 +130,110 @@
 her.
-M
+m
 onitor
-;%0Aimport org.datatransferproject.api.launcher.Version;%0Aimport org.datatransferproject.transfer.WorkerMain;%0A%0Aimport javax.net.ssl.KeyManagerFactory;%0Aimport javax.net.ssl.TrustManagerFactory
+.MonitorLoader.loadMonitor;%0A%0Aimport com.google.common.util.concurrent.UncaughtExceptionHandlers
 ;%0Aim
@@ -413,61 +413,140 @@
 er;%0A
-%0A
 import 
-static java.lang.String.format;%0Aimport static
+javax.net.ssl.KeyManagerFactory;%0Aimport javax.net.ssl.TrustManagerFactory;%0Aimport org.datatransferproject.api.ApiMain;%0Aimport
  org
@@ -558,32 +558,36 @@
 transferproject.
+api.
 launcher.monitor
@@ -583,41 +583,120 @@
 her.
-m
+M
 onitor
-.MonitorLoader.loadMonitor
+;%0Aimport org.datatransferproject.api.launcher.Version;%0Aimport org.datatransferproject.transfer.WorkerMain
 ;%0A%0A/
@@ -897,16 +897,49 @@
 llback;%0A
+  private final Monitor monitor;%0A
   privat
@@ -1158,31 +1158,8 @@
 ain(
-SingleVMMain::exitError
 );%0A%0A
@@ -1393,83 +1393,206 @@
 ain(
-Consumer%3CException%3E errorCallback) %7B%0A    this.errorCallback = errorCallback
+) %7B%0A    this.monitor = loadMonitor();%0A    this.errorCallback =%0A        (e) -%3E %7B%0A          monitor.severe(() -%3E format(%22Exiting abnormally, exception:%25n%25n%22 + e));%0A          System.exit(-1);%0A        %7D
 ;%0A  
@@ -1634,46 +1634,8 @@
 ) %7B%0A
-    Monitor monitor = loadMonitor();%0A%0A
     
@@ -3235,168 +3235,8 @@
  %7D%0A%0A
-  private static void exitError(Exception exception) %7B%0A    exception.printStackTrace();%0A    System.err.println(%22Exiting abnormally%22);%0A    System.exit(-1);%0A  %7D%0A%0A
   pr
@@ -3366,24 +3366,40 @@
 le (true) %7B%0A
+        try %7B%0A  
         Work
@@ -3440,36 +3440,22 @@
 ();%0A    
-    try %7B%0A
       
-    
 workerMa
@@ -3475,99 +3475,8 @@
 ();%0A
-        %7D catch (Exception e) %7B%0A          errorCallback.accept(e);%0A        %7D%0A        try %7B%0A
     
@@ -3546,51 +3546,30 @@
     
-// TODO Logger%0A          e.printStackTrace(
+errorCallback.accept(e
 );%0A 

add monitor-based logging to the demo server (#708)