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