diff --git a/app/pom.xml b/app/pom.xml
index a756d32..0905acb 100644
--- a/app/pom.xml
+++ b/app/pom.xml
@@ -45,20 +45,10 @@
net.kemitix.thorp
thorp-uishell
-
-
-
- org.scala-lang
- scala-library
-
-
- net.alchim31.maven
- scala-maven-plugin
-
org.apache.maven.plugins
maven-assembly-plugin
diff --git a/app/src/main/java/net/kemitix/thorp/Main.java b/app/src/main/java/net/kemitix/thorp/Main.java
new file mode 100644
index 0000000..276bed3
--- /dev/null
+++ b/app/src/main/java/net/kemitix/thorp/Main.java
@@ -0,0 +1,10 @@
+package net.kemitix.thorp;
+
+public class Main {
+
+ public static void main(String[] args) {
+ Program.run(args);
+ System.exit(0);
+ }
+
+}
diff --git a/app/src/main/java/net/kemitix/thorp/Program.java b/app/src/main/java/net/kemitix/thorp/Program.java
new file mode 100644
index 0000000..43333b4
--- /dev/null
+++ b/app/src/main/java/net/kemitix/thorp/Program.java
@@ -0,0 +1,100 @@
+package net.kemitix.thorp;
+
+import net.kemitix.thorp.cli.CliArgs;
+import net.kemitix.thorp.config.*;
+import net.kemitix.thorp.console.Console;
+import net.kemitix.thorp.domain.Counters;
+import net.kemitix.thorp.domain.RemoteObjects;
+import net.kemitix.thorp.domain.StorageEvent;
+import net.kemitix.thorp.domain.Terminal;
+import net.kemitix.thorp.domain.channel.Channel;
+import net.kemitix.thorp.domain.channel.Sink;
+import net.kemitix.thorp.lib.Archive;
+import net.kemitix.thorp.lib.LocalFileSystem;
+import net.kemitix.thorp.lib.UnversionedMirrorArchive;
+import net.kemitix.thorp.storage.Storage;
+import net.kemitix.thorp.uishell.UIEvent;
+import net.kemitix.thorp.uishell.UIShell;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public interface Program {
+
+ String version = "1.1.0-SNAPSHOT";
+ String versionLabel = String.format("%sThrop v%s%s",
+ Terminal.white, version, Terminal.reset);
+
+ static void run(String[] args) {
+ try {
+ ConfigOptions configOptions = CliArgs.parse(args);
+ Configuration configuration = ConfigurationBuilder.buildConfig(configOptions);
+ Console.putStrLn(versionLabel);
+ if (!ConfigQuery.showVersion(configOptions)) {
+ executeWithUi(configuration);
+ }
+ } catch (ConfigValidationException e) {
+ Console.putStrLn(String.format(
+ "Configuration error: %s", e.getErrors()));
+ } catch (IOException e) {
+ Console.putStrLn(String.format(
+ "Error loading configuration: %s", e.getMessage()));
+ } catch (InterruptedException e) {
+ Console.putStrLn(String.format(
+ "Program interrupted: %s", e.getMessage()));
+ }
+ }
+
+ static void executeWithUi(Configuration configuration) throws InterruptedException {
+ Channel
+ .create("ui")
+ .addListener(UIShell.listener(configuration))
+ .run(uiSink -> execute(configuration, uiSink))
+ .start()
+ .waitForShutdown();
+ }
+
+ static void execute(Configuration configuration, Sink uiSink) {
+ try {
+ uiSink.accept(UIEvent.showValidConfig());
+ RemoteObjects remoteObjects = Storage.getInstance().list(configuration.bucket, configuration.prefix);
+ Archive archive = UnversionedMirrorArchive.create();
+ List storageEvents = new ArrayList<>();
+ storageEvents.addAll(LocalFileSystem.scanCopyUpload(configuration, uiSink, remoteObjects, archive));
+ storageEvents.addAll(LocalFileSystem.scanDelete(configuration, uiSink, remoteObjects, archive));
+ Counters counters = countEvents(storageEvents);
+ uiSink.accept(UIEvent.showSummary(counters));
+ } catch (InterruptedException e) {
+ // do nothing
+ } finally {
+ Storage.getInstance().shutdown();
+ }
+ }
+
+ static Counters countEvents(List storageEvents) {
+ AtomicInteger uploads = new AtomicInteger();
+ AtomicInteger copies = new AtomicInteger();
+ AtomicInteger deletes = new AtomicInteger();
+ AtomicInteger errors = new AtomicInteger();
+ storageEvents.forEach(storageEvent -> {
+ if (storageEvent instanceof StorageEvent.UploadEvent) {
+ uploads.incrementAndGet();
+ } else if (storageEvent instanceof StorageEvent.CopyEvent) {
+ copies.incrementAndGet();
+ } else if (storageEvent instanceof StorageEvent.DeleteEvent) {
+ deletes.incrementAndGet();
+ } else if (storageEvent instanceof StorageEvent.ErrorEvent) {
+ errors.incrementAndGet();
+ }
+ });
+ return Counters.empty
+ .withUploaded(uploads.get())
+ .withCopied(copies.get())
+ .withDeleted(deletes.get())
+ .withErrors(errors.get());
+ }
+
+
+}
diff --git a/app/src/main/scala/net/kemitix/thorp/Main.scala b/app/src/main/scala/net/kemitix/thorp/Main.scala
deleted file mode 100644
index 4c97997..0000000
--- a/app/src/main/scala/net/kemitix/thorp/Main.scala
+++ /dev/null
@@ -1,10 +0,0 @@
-package net.kemitix.thorp
-
-object Main {
-
- def main(args: Array[String]): Unit = {
- Program.run(args.toList)
- System.exit(0)
- }
-
-}
diff --git a/app/src/main/scala/net/kemitix/thorp/Program.scala b/app/src/main/scala/net/kemitix/thorp/Program.scala
deleted file mode 100644
index 7cd2320..0000000
--- a/app/src/main/scala/net/kemitix/thorp/Program.scala
+++ /dev/null
@@ -1,106 +0,0 @@
-package net.kemitix.thorp
-
-import net.kemitix.thorp.cli.CliArgs
-import net.kemitix.thorp.config._
-import net.kemitix.thorp.console._
-import net.kemitix.thorp.domain.StorageEvent.{
- CopyEvent,
- DeleteEvent,
- ErrorEvent,
- UploadEvent
-}
-import net.kemitix.thorp.domain.channel.{Channel, Sink}
-import net.kemitix.thorp.domain.{Counters, StorageEvent}
-import net.kemitix.thorp.lib.{LocalFileSystem, UnversionedMirrorArchive}
-import net.kemitix.thorp.storage.Storage
-import net.kemitix.thorp.uishell.{UIEvent, UIShell}
-
-import scala.io.AnsiColor.{RESET, WHITE}
-import scala.jdk.CollectionConverters._
-
-trait Program {
-
- val version = "1.1.0-SNAPSHOT"
- lazy val versionLabel = s"${WHITE}Thorp v$version$RESET"
-
- def run(args: List[String]): Unit = {
- val cli = CliArgs.parse(args.toArray)
- val config = ConfigurationBuilder.buildConfig(cli)
- Console.putStrLn(versionLabel)
- if (!showVersion(cli)) {
- executeWithUI(config)
- }
- }
-
- private def showVersion: ConfigOptions => Boolean =
- cli => ConfigQuery.showVersion(cli)
-
- private def executeWithUI(configuration: Configuration): Unit = {
- Channel
- .create("ui")
- .addListener(UIShell.listener(configuration))
- .run(execute(configuration)(_))
- .start()
- .waitForShutdown()
- }
-
- private def execute(
- configuration: Configuration
- )(uiSink: Sink[UIEvent]): Unit = {
- try {
- showValidConfig(uiSink)
- val remoteObjects =
- fetchRemoteData(configuration, uiSink)
- val archive = UnversionedMirrorArchive.create
- val storageEvents = LocalFileSystem
- .scanCopyUpload(configuration, uiSink, remoteObjects, archive)
- val deleteEvents = LocalFileSystem
- .scanDelete(configuration, uiSink, remoteObjects, archive)
- showSummary(uiSink)(
- (storageEvents.asScala ++ deleteEvents.asScala).toList
- )
- } finally {
- Storage.getInstance().shutdown()
- }
- }
-
- private def showValidConfig(uiSink: Sink[UIEvent]): Unit =
- uiSink.accept(UIEvent.showValidConfig)
-
- private def fetchRemoteData(configuration: Configuration,
- uiSink: Sink[UIEvent]) = {
- val bucket = configuration.bucket
- val prefix = configuration.prefix
- val objects = Storage.getInstance().list(bucket, prefix)
- uiSink.accept(UIEvent.remoteDataFetched(objects.byKey.size))
- objects
- }
-
- //TODO not called
- private def logValidationErrors(throwable: Throwable) =
- throwable match {
- case validateError: ConfigValidationException =>
- validateError.getErrors.asScala
- .map(error => Console.putStrLn(s"- $error"))
- }
-
- private def showSummary(
- uiSink: Sink[UIEvent]
- )(events: Seq[StorageEvent]): Unit = {
- val counters = events.foldLeft(Counters.empty)(countActivities)
- uiSink.accept(UIEvent.showSummary(counters))
- }
-
- private def countActivities =
- (counters: Counters, s3Action: StorageEvent) => {
- s3Action match {
- case _: UploadEvent => counters.incrementUploaded()
- case _: CopyEvent => counters.incrementCopied()
- case _: DeleteEvent => counters.incrementDeleted()
- case _: ErrorEvent => counters.incrementErrors()
- case _ => counters
- }
- }
-}
-
-object Program extends Program
diff --git a/docs/images/reactor-graph.png b/docs/images/reactor-graph.png
index 9e9e45b..ccb083c 100644
Binary files a/docs/images/reactor-graph.png and b/docs/images/reactor-graph.png differ
diff --git a/domain/src/main/java/net/kemitix/thorp/domain/Terminal.java b/domain/src/main/java/net/kemitix/thorp/domain/Terminal.java
index d701e61..9cadf2d 100644
--- a/domain/src/main/java/net/kemitix/thorp/domain/Terminal.java
+++ b/domain/src/main/java/net/kemitix/thorp/domain/Terminal.java
@@ -66,9 +66,10 @@ public class Terminal {
public static String enableAlternateBuffer = csi + "?1049h";
public static String disableAlternateBuffer = csi + "?1049l";
+ public static String reset = "\u001B[0m";
public static String red = "\u001B[31m";
public static String green = "\u001B[32m";
- public static String reset = "\u001B[0m";
+ public static String white = "\u001B[37m";
private static Map getSubBars() {
Map subBars = new HashMap<>();
diff --git a/parent/pom.xml b/parent/pom.xml
index 8b4d558..bc80eb9 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -16,10 +16,8 @@
pom
- 4.4.0
2.17
2.7.0
- 2.13.3
1.18.12
2.2.0
5.6.2
@@ -111,71 +109,10 @@
assertj-core
${assertj.version}
-
-
-
- org.scala-lang
- scala-library
- ${scala-library.version}
-
-
-
- com.github.scopt
- scopt_2.13
- 4.0.0-RC2
-
-
-
- org.scalatest
- scalatest_2.13
- 3.0.8
-
-
- org.scalamock
- scalamock_2.13
- 4.4.0
-
-
-
-
- net.alchim31.maven
- scala-maven-plugin
- ${scala-maven-plugin.version}
-
-
- -Ywarn-unused:imports
- -Xfatal-warnings
- -feature
- -deprecation
- -unchecked
- -language:postfixOps
- -language:higherKinds
-
-
-
-
- scala-compile-first
- process-resources
-
- add-source
- compile
-
-
-
- scala-test-compile
- process-test-resources
-
- testCompile
-
-
-
-
-
-
io.repaint.maven