This repository has been archived by the owner on Aug 18, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/develop' into develop
- Loading branch information
Showing
12 changed files
with
563 additions
and
202 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import java.io.File | ||
|
||
import CLI._ | ||
|
||
/** | ||
* The bootstrap launcher downloads all required libraries and starts chat overflow with the correct parameters. | ||
*/ | ||
object Bootstrap { | ||
|
||
// Java home path (jre installation folder) | ||
private val javaHomePath: String = System.getProperty("java.home") | ||
|
||
// Chat Overflow Launcher / Main class (should not change anymore) | ||
private val chatOverflowMainClass = "org.codeoverflow.chatoverflow.Launcher" | ||
|
||
/** | ||
* Launcher entry point. | ||
* Validates installation, downloads dependencies and start ChatOverflow. | ||
* | ||
* @param args the arguments, which are passed to ChatOverflow | ||
*/ | ||
def main(args: Array[String]): Unit = { | ||
val conf: Config = ArgsParser.parse(args, Config()) match { | ||
case Some(value) => value | ||
case None => System.exit(1); null | ||
} | ||
|
||
if (testValidity(conf.directory)) { | ||
println("Valid ChatOverflow installation. Checking libraries...") | ||
|
||
val deps = new DependencyDownloader(conf.directory).fetchDependencies().map(u => new File(u.getFile)) | ||
if (deps.nonEmpty) { | ||
val javaPath = createJavaPath() | ||
if (javaPath.isDefined) { | ||
println("Found java installation. Starting ChatOverflow...") | ||
|
||
// Start chat overflow! | ||
val command = List(javaPath.get, "-cp", s"bin/*${deps.mkString(File.pathSeparator, File.pathSeparator, "")}", chatOverflowMainClass) ++ args | ||
val processBuilder = new java.lang.ProcessBuilder(command: _*) | ||
.inheritIO().directory(new File(conf.directory)) | ||
|
||
processBuilder.environment().put("CHATOVERFLOW_BOOTSTRAP", "true") | ||
|
||
val process = processBuilder.start() | ||
|
||
val exitCode = process.waitFor() | ||
println(s"ChatOverflow stopped with exit code: $exitCode") | ||
} else { | ||
println("Unable to find java installation. Unable to start.") | ||
} | ||
} else { | ||
println("Error: Problem with libraries. Unable to start.") | ||
} | ||
} else { | ||
println("Error: Invalid ChatOverflow installation. Please extract all provided files properly. Unable to start.") | ||
} | ||
} | ||
|
||
/** | ||
* Takes the java home path of the launcher and tries to find the java(.exe) | ||
* | ||
* @return the path to the java runtime or none, if the file was not found | ||
*/ | ||
private def createJavaPath(): Option[String] = { | ||
|
||
// Check validity of java.home path first | ||
if (!new File(javaHomePath).exists()) { | ||
None | ||
} else { | ||
|
||
// Check for windows and unix java versions | ||
// This should work on current and older java JRE/JDK installations, | ||
// see: https://stackoverflow.com/questions/52584888/how-to-use-jdk-without-jre-in-java-11 | ||
val javaExePath = s"$javaHomePath/bin/java.exe" | ||
val javaPath = s"$javaHomePath/bin/java" | ||
|
||
if (new File(javaExePath).exists()) { | ||
Some(javaExePath) | ||
} else if (new File(javaPath).exists()) { | ||
Some(javaPath) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
} | ||
|
||
/** | ||
* Checks, if the installation is valid | ||
*/ | ||
private def testValidity(currentFolderPath: String): Boolean = { | ||
// The first check is the existence of a bin folder | ||
val binDir = new File(s"$currentFolderPath/bin") | ||
check(binDir.exists() && binDir.isDirectory, "The bin directory doesn't exist") && { | ||
// Next are the existence of a framework, api and gui jar | ||
val jars = binDir.listFiles().filter(_.getName.endsWith(".jar")) | ||
|
||
check(jars.exists(_.getName.toLowerCase.startsWith("chatoverflow_")), "There is no api jar in the bin directory.") && | ||
check(jars.exists(_.getName.toLowerCase.startsWith("chatoverflow-api")), "There is no api jar in the bin directory.") && | ||
check(jars.exists(_.getName.toLowerCase.startsWith("chatoverflow-gui")), | ||
"Note: No gui jar detected. The ChatOverflow gui won't be usable.", required = false) | ||
} | ||
} | ||
|
||
/** | ||
* Helper method for [[Bootstrap.testValidity()]]. Checks condition, prints description if the condition is false and | ||
* returns false if the condition is false and the check is required. | ||
*/ | ||
private def check(condition: Boolean, description: String, required: Boolean = true): Boolean = { | ||
if (condition) { | ||
true | ||
} else { | ||
println(description) | ||
!required | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import java.io.File | ||
import java.nio.file.Paths | ||
|
||
|
||
object CLI { | ||
|
||
/** | ||
* Everything in here also has to be defined in the CLI class of the framework, because | ||
* all arguments including bootstrap specific ones are passed through to the framework. | ||
* Filtering these options out would be way too difficult, because you need to know if a option | ||
* is a simple flag or is followed by a value. Scopt doesn't expose anything to get this so we would | ||
* need to use reflect, which is very ugly. | ||
* This, while not that elegant as I would like it to be, is just simple and works. | ||
*/ | ||
object ArgsParser extends scopt.OptionParser[Config]("ChatOverflow Launcher") { | ||
opt[File]("directory") | ||
.action((x, c) => c.copy(directory = x.getAbsolutePath)) | ||
.text("The directory in which ChatOverflow will be executed") | ||
.validate(f => | ||
if (!f.exists()) | ||
Left("Directory doesn't exist") | ||
else if (!f.isDirectory) | ||
Left("Path isn't a directory") | ||
else | ||
Right() | ||
) | ||
|
||
override def errorOnUnknownArgument: Boolean = false | ||
|
||
override def reportWarning(msg: String): Unit = () | ||
} | ||
|
||
case class Config(directory: String = Paths.get("").toAbsolutePath.toString) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.