From f6c8e44329dc72ba7f44d26edd1ac9a7f57e7ea2 Mon Sep 17 00:00:00 2001
From: meeh <meeh@mail.i2p>
Date: Tue, 1 May 2018 11:15:11 +0000
Subject: [PATCH] Jar deployment, SBT hacking, and more related to launcher
 code. Some refactoring, bugfixing, and self awareness of launcher jar.

---
 launchers/browserbundle/build.sbt             |  3 --
 .../scala/net/i2p/RouterLauncherApp.scala     |  6 +--
 launchers/build.sbt                           |  6 ++-
 .../net/i2p/launchers/OSXDeployment.scala     | 54 ++++++++++++++++---
 .../net/i2p/launchers/RouterLauncher.scala    | 18 +++++++
 launchers/macosx/build.sbt                    | 23 ++++----
 .../net/i2p/MacOSXRouterLauncherApp.scala     | 18 +++++--
 7 files changed, 95 insertions(+), 33 deletions(-)
 create mode 100644 launchers/common/src/main/scala/net/i2p/launchers/RouterLauncher.scala

diff --git a/launchers/browserbundle/build.sbt b/launchers/browserbundle/build.sbt
index ca570cb8fa..376dfad51d 100644
--- a/launchers/browserbundle/build.sbt
+++ b/launchers/browserbundle/build.sbt
@@ -1,8 +1,5 @@
 
 
-libraryDependencies ++= Seq(
-  "org.json4s" %% "json4s-native" % "3.5.3"
-)
 
 assemblyExcludedJars in assembly := {
   val donts = List(
diff --git a/launchers/browserbundle/src/main/scala/net/i2p/RouterLauncherApp.scala b/launchers/browserbundle/src/main/scala/net/i2p/RouterLauncherApp.scala
index ff1f6a53ff..28b232dd27 100644
--- a/launchers/browserbundle/src/main/scala/net/i2p/RouterLauncherApp.scala
+++ b/launchers/browserbundle/src/main/scala/net/i2p/RouterLauncherApp.scala
@@ -2,10 +2,8 @@ package net.i2p
 
 import java.io.{File, InputStream}
 
+//import net.i2p.Router
 import net.i2p.launchers.DeployProfile
-import net.i2p.router.Router
-import org.json4s._
-import org.json4s.native.JsonMethods._
 
 /**
   *
@@ -104,7 +102,7 @@ object RouterLauncherApp extends App {
 
   //ErrorUtils.printError(s"Starting up with arguments ${(args mkString ", ")}",":)")
 
-  Router.main(args)
+  //Router.main(args)
 }
 
 
diff --git a/launchers/build.sbt b/launchers/build.sbt
index d0853139a0..56b229a5b5 100644
--- a/launchers/build.sbt
+++ b/launchers/build.sbt
@@ -31,7 +31,10 @@ lazy val browserbundle = (project in file("browserbundle"))
     commonSettings,
     name         := "RouterLaunchApp",
     assemblyJarName in assembly := s"${name.value}-${version.value}.jar",
-    mainClass in assembly := Some("net.i2p.RouterLauncherApp")
+    mainClass in assembly := Some("net.i2p.RouterLauncherApp"),
+    libraryDependencies ++= Seq(
+      "org.json4s" %% "json4s-native" % "3.5.3"
+    )
   ).dependsOn(common)
 
 lazy val macosx = (project in file("macosx"))
@@ -52,3 +55,4 @@ fork := true
 
 run / javaOptions += "-Xmx512M"
 run / connectInput := true
+
diff --git a/launchers/common/src/main/scala/net/i2p/launchers/OSXDeployment.scala b/launchers/common/src/main/scala/net/i2p/launchers/OSXDeployment.scala
index 19f429105a..859d67d28c 100644
--- a/launchers/common/src/main/scala/net/i2p/launchers/OSXDeployment.scala
+++ b/launchers/common/src/main/scala/net/i2p/launchers/OSXDeployment.scala
@@ -1,7 +1,13 @@
 package net.i2p.launchers
 
 import java.io.{File, IOException}
+import java.nio.file.FileAlreadyExistsException
 import java.util.zip.ZipFile
+
+import java.nio.file.StandardCopyOption.REPLACE_EXISTING
+import java.nio.file.Files.copy
+import java.nio.file.Paths.get
+
 import collection.JavaConverters._
 
 /**
@@ -120,14 +126,19 @@ class OSXDeployment extends
     */
   def copyDirFromRes(dir: File): Unit = {
     // A small hack
-    val zipFile = new ZipFile(DeployProfile.executingJarFile.getFile)
-    zipFile.entries().asScala.toList.filter(_.toString.startsWith(dir.getPath)).filter(!_.isDirectory).map { entry =>
-      new File(DeployProfile.pathJoin(baseDir,entry.getName)).getParentFile.mkdirs()
-      if (entry.isDirectory) {
-        createFileOrDirectory(new File(DeployProfile.pathJoin(baseDir,entry.getName)), true)
-      } else {
-        copyBaseFileResToDisk(entry.getName, getClass.getResourceAsStream("/".concat(entry.getName)))
+    try {
+      val zipFile = new ZipFile(DeployProfile.executingJarFile.getFile)
+      zipFile.entries().asScala.toList.filter(_.toString.startsWith(dir.getPath)).filter(!_.isDirectory).map { entry =>
+        new File(DeployProfile.pathJoin(baseDir,entry.getName)).getParentFile.mkdirs()
+        if (entry.isDirectory) {
+          createFileOrDirectory(new File(DeployProfile.pathJoin(baseDir,entry.getName)), true)
+        } else {
+          copyBaseFileResToDisk(entry.getName, getClass.getResourceAsStream("/".concat(entry.getName)))
+        }
       }
+    } catch {
+      case ex:IOException => println(s"Error! Exception ${ex}")
+      case _:FileAlreadyExistsException => {} // Ignored
     }
   }
 
@@ -156,6 +167,7 @@ class OSXDeployment extends
         }
       } catch {
         case ex:IOException => println(s"Error! Exception ${ex}")
+        case _:FileAlreadyExistsException => {} // Ignored
       }
     }
   }
@@ -164,6 +176,29 @@ class OSXDeployment extends
     new File(baseDir).mkdirs()
   }
 
+  implicit def toPath (filename: String) = get(filename)
+
+  val selfFile = new File(DeployProfile.executingJarFile.getFile)
+  val selfDir = selfFile.getParentFile
+  val resDir = new File(selfDir.getParent, "Resources")
+  val i2pBaseBundleDir = new File(resDir, "i2pbase")
+  val i2pBundleJarDir = new File(i2pBaseBundleDir, "lib")
+
+  val i2pBaseDir = OSXDefaults.getOSXBaseDirectory
+  val i2pDeployJarDir = new File(i2pBaseDir, "lib")
+  if (!i2pDeployJarDir.exists()) {
+    i2pDeployJarDir.mkdirs()
+    i2pBundleJarDir.list().toList.map {
+      jar => {
+        copy (
+          DeployProfile.pathJoin(i2pBundleJarDir.getAbsolutePath, jar),
+          DeployProfile.pathJoin(i2pDeployJarDir.getAbsolutePath, jar),
+          REPLACE_EXISTING)
+        println(s"Copied ${jar} to right place")
+      }
+    }
+  }
+
   /**
     * Please note that in Scala, the constructor body is same as class body.
     * What's defined outside of methods is considered constructor code and
@@ -184,12 +219,15 @@ class OSXDeployment extends
         // Case subject is a file/resource
       case Left(is) => {
         // Write file
-        if (!new File(DeployProfile.pathJoin(baseDir, fd.getPath)).exists()) {
+        val f = DeployProfile.pathJoin(baseDir, fd.getPath)
+        println(s"f: ${f.toString}")
+        if (!new File(f).exists()) {
           //println(s"copyBaseFileResToDisk(${fd.getPath})")
           try {
             copyBaseFileResToDisk(fd.getPath, is)
           } catch {
             case ex:IOException => println(s"Error! Exception ${ex}")
+            case _:FileAlreadyExistsException => {} // Ignored
           }
         }
       }
diff --git a/launchers/common/src/main/scala/net/i2p/launchers/RouterLauncher.scala b/launchers/common/src/main/scala/net/i2p/launchers/RouterLauncher.scala
new file mode 100644
index 0000000000..139a7d5646
--- /dev/null
+++ b/launchers/common/src/main/scala/net/i2p/launchers/RouterLauncher.scala
@@ -0,0 +1,18 @@
+package net.i2p.launchers
+
+import java.net.URL
+
+/**
+  * A abstract class is kind of like an java interface.
+  *
+  * @author Meeh
+  * @since 0.9.35
+  */
+abstract class RouterLauncher {
+
+  def getClassLoader: ClassLoader
+
+  def addJarToClassPath(url: URL): Boolean
+
+  def runRouter(args: Array[String]): Unit
+}
diff --git a/launchers/macosx/build.sbt b/launchers/macosx/build.sbt
index 36e47863d2..9c50ff70ee 100644
--- a/launchers/macosx/build.sbt
+++ b/launchers/macosx/build.sbt
@@ -31,24 +31,17 @@ lazy val jarsForCopy = i2pBuildDir.list.filter { f => f.endsWith(".jar") }
 // Pointing the resources directory to the "installer" directory
 resourceDirectory in Compile := baseDirectory.value / ".." / ".." / "installer" / "resources"
 
+// Unmanaged base will be included in a fat jar
+unmanagedBase in Compile := baseDirectory.value / ".." / ".." / "build"
+
 // Unmanaged classpath will be available at compile time
 unmanagedClasspath in Compile ++= Seq(
-  baseDirectory.value / ".." / ".." / "build" / "*.jar",
-  baseDirectory.value / ".." / ".." / "router" / "java" / "src"
-)
-
-// Please note the difference between browserbundle, this has
-// the "in Compile" which limit it's scope to that.
-//unmanagedBase in Compile := baseDirectory.value / ".." / ".." / "build"
-
-libraryDependencies ++= Seq(
-  "net.i2p" % "router" % i2pVersion % Compile
+  baseDirectory.value / ".." / ".." / "build" / "*.jar"
 )
 
-
 //assemblyOption in assembly := (assemblyOption in assembly).value.copy(prependShellScript = Some(defaultShellScript))
 
-assemblyJarName in assembly := s"${name.value}-${version.value}"
+assemblyJarName in assembly := s"OSXLauncher"
 
 
 // TODO: MEEH: Add assemblyExcludedJars and load the router from own jar files, to handle upgrades better.
@@ -71,6 +64,10 @@ buildAppBundleTask := {
   paths.map { case (s,p) => p.mkdirs() }
   val dirsToCopy = List("certificates","locale","man")
 
+  val launcherBinary = Some(assembly.value)
+  launcherBinary.map { l => IO.copyFile( new File(l.toString), new File(paths.get("execBundlePath").get, "I2P") ) }
+
+
   /**
     *
     * First of, if "map" is unknown for you - shame on you :p
@@ -85,5 +82,5 @@ buildAppBundleTask := {
     */
   dirsToCopy.map { d => IO.copyDirectory( new File(resDir, d), new File(paths.get("i2pbaseBunldePath").get, d) ) }
   warsForCopy.map { w => IO.copyFile( new File(i2pBuildDir, w), new File(paths.get("webappsBunldePath").get, w) ) }
-  warsForCopy.map { j => IO.copyFile( new File(i2pBuildDir, j), new File(paths.get("i2pJarsBunldePath").get, j) ) }
+  jarsForCopy.map { j => IO.copyFile( new File(i2pBuildDir, j), new File(paths.get("i2pJarsBunldePath").get, j) ) }
 }
diff --git a/launchers/macosx/src/main/scala/net/i2p/MacOSXRouterLauncherApp.scala b/launchers/macosx/src/main/scala/net/i2p/MacOSXRouterLauncherApp.scala
index b0870be20b..78bf3ea729 100644
--- a/launchers/macosx/src/main/scala/net/i2p/MacOSXRouterLauncherApp.scala
+++ b/launchers/macosx/src/main/scala/net/i2p/MacOSXRouterLauncherApp.scala
@@ -1,9 +1,10 @@
 package net.i2p
 
-import net.i2p.router.Router
-import net.i2p.launchers.{OSXDefaults, OSXDeployment}
 import java.io.File
 
+import collection.JavaConverters._
+import net.i2p.launchers.{OSXDefaults, OSXDeployment}
+
 /**
   *
   * For java developers:
@@ -34,12 +35,21 @@ import java.io.File
   */
 object MacOSXRouterLauncherApp extends App {
 
-  val i2pBaseDir = new File(OSXDefaults.getOSXBaseDirectory)
+  val i2pBaseDir = OSXDefaults.getOSXBaseDirectory
+
 
   new OSXDeployment()
 
   // Change directory to base dir
   System.setProperty("user.dir", i2pBaseDir.getAbsolutePath)
 
-  Router.main(args)
+  val i2pJarDir = new File(i2pBaseDir.getAbsolutePath, "lib")
+  i2pJarDir.list().toList.map { jar => {
+    val jarFile = new File(i2pJarDir, jar)
+    println(s"Loading jar: ${jarFile.toURI.toURL} => ${MacOSXRouterLauncher.addJarToClassPath(jarFile.toURI.toURL)}")
+
+  } }
+
+  MacOSXRouterLauncher.runRouter(args)
+  //net.i2p.Router.main(args)
 }
-- 
GitLab