From 9a5bba9c049338f0ff06b39e2f686fcbbf53c862 Mon Sep 17 00:00:00 2001 From: Clinton Date: Wed, 22 Mar 2017 14:08:23 +0000 Subject: [PATCH] Created a Kotlin node runner (#367) * Added a cross platform node runner to replace scripts. * Added runnodes shell and batch files back. * Node runner supports new webserver format. * Runnodes now runs correctly on OSX * Intermediate commit. Squash me. * Upgdated gradle plugins version number to 0.10.1 * Further inlined more functions. * Text display changes for noderunner. --- gradle-plugins/cordformation/build.gradle | 44 +++++++++++ .../groovy/net/corda/plugins/Cordform.groovy | 10 ++- .../main/resources/net/corda/plugins/runnodes | 53 +------------ .../resources/net/corda/plugins/runnodes.bat | 9 +-- .../kotlin/net/corda/plugins/NodeRunner.kt | 79 +++++++++++++++++++ publish.properties | 2 +- 6 files changed, 133 insertions(+), 64 deletions(-) mode change 100755 => 100644 gradle-plugins/cordformation/src/main/resources/net/corda/plugins/runnodes create mode 100644 gradle-plugins/cordformation/src/noderunner/kotlin/net/corda/plugins/NodeRunner.kt diff --git a/gradle-plugins/cordformation/build.gradle b/gradle-plugins/cordformation/build.gradle index 396d6da35a..007a1553b5 100644 --- a/gradle-plugins/cordformation/build.gradle +++ b/gradle-plugins/cordformation/build.gradle @@ -1,4 +1,18 @@ +buildscript { + // TODO: Unify with the one in the main project + ext.kotlin_version = '1.0.5-2' + + repositories { + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + apply plugin: 'groovy' +apply plugin: 'kotlin' apply plugin: 'net.corda.plugins.publish-utils' description 'A small gradle plugin for adding some basic Quasar tasks and configurations to reduce build.gradle bloat.' @@ -7,12 +21,42 @@ repositories { mavenCentral() } +configurations { + noderunner + compile.extendsFrom noderunner +} + +sourceSets { + runnodes { + kotlin { + srcDir file('src/noderunner/kotlin') + compileClasspath += configurations.noderunner + } + } +} + dependencies { compile gradleApi() compile localGroovy() + noderunner "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + // TypeSafe Config: for simple and human friendly config files. // TODO: Add a common versions file between Corda and gradle plugins to de-duplicate this version number compile "com.typesafe:config:1.3.1" } +task createNodeRunner(type: Jar, dependsOn: [classes]) { + manifest { + attributes('Main-Class': 'net.corda.plugins.NodeRunnerKt') + } + baseName = project.name + '-fatjar' + from { configurations.noderunner.collect { it.isDirectory() ? it : zipTree(it) } } + from sourceSets.runnodes.output +} + +jar { + from(createNodeRunner) { + rename { 'net/corda/plugins/runnodes.jar' } + } +} \ No newline at end of file diff --git a/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Cordform.groovy b/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Cordform.groovy index 70384e9846..df353e7a3f 100644 --- a/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Cordform.groovy +++ b/gradle-plugins/cordformation/src/main/groovy/net/corda/plugins/Cordform.groovy @@ -65,10 +65,14 @@ class Cordform extends DefaultTask { * Installs the run script into the nodes directory. */ protected void installRunScript() { + project.copy { + from Cordformation.getPluginFile(project, "net/corda/plugins/runnodes.jar") + fileMode 0755 + into "${directory}/" + } + project.copy { from Cordformation.getPluginFile(project, "net/corda/plugins/runnodes") - filter { String line -> line.replace("NODEJAR_NAME", Node.NODEJAR_NAME) } - filter { String line -> line.replace("WEBJAR_NAME", Node.WEBJAR_NAME) } // Replaces end of line with lf to avoid issues with the bash interpreter and Windows style line endings. filter(FixCrLfFilter.class, eol: FixCrLfFilter.CrLf.newInstance("lf")) fileMode 0755 @@ -77,8 +81,6 @@ class Cordform extends DefaultTask { project.copy { from Cordformation.getPluginFile(project, "net/corda/plugins/runnodes.bat") - filter { String line -> line.replace("NODEJAR_NAME", Node.NODEJAR_NAME) } - filter { String line -> line.replace("WEBJAR_NAME", Node.WEBJAR_NAME) } into "${directory}/" } } diff --git a/gradle-plugins/cordformation/src/main/resources/net/corda/plugins/runnodes b/gradle-plugins/cordformation/src/main/resources/net/corda/plugins/runnodes old mode 100755 new mode 100644 index e1cce32921..e2dfcb4d0a --- a/gradle-plugins/cordformation/src/main/resources/net/corda/plugins/runnodes +++ b/gradle-plugins/cordformation/src/main/resources/net/corda/plugins/runnodes @@ -1,62 +1,13 @@ #!/usr/bin/env bash -# Will attempt to execute a corda node within all subdirectories in the current working directory. set -euo pipefail -export CAPSULE_CACHE_DIR=cache # Allow the script to be run from outside the nodes directory. basedir=$( dirname "$0" ) cd "$basedir" if which osascript >/dev/null; then - # MacOS X: open each node in a in new tab. - first=true - script='tell app "Terminal" - activate' - rootdir=`pwd` - for dir in `ls`; do - if [ -d $dir ]; then - cmd="bash -c 'cd $rootdir/$dir; exec ./NODEJAR_NAME && exit'" - script="$script - tell application \"System Events\" to tell process \"Terminal\" to keystroke \"t\" using command down - delay 0.5 - do script \"$cmd\" in window 1" - first=false - fi - done - - # Now do the web servers. - if [[ "${NO_WEB_SERVER:-}" == "" ]]; then - for dir in `ls`; do - if [ -d $dir ]; then - cmd="bash -c 'cd $rootdir/$dir; /usr/libexec/java_home -v 1.8 --exec java -jar WEBJAR_NAME && exit'" - script="$script - tell application \"System Events\" to tell process \"Terminal\" to keystroke \"t\" using command down - delay 0.5 - do script \"$cmd\" in window 1" - first=false - fi - done - fi - script="$script -end tell" - osascript -e "$script" + /usr/libexec/java_home -v 1.8 --exec java -jar runnodes.jar else - # Some other UNIX, probably Linux (does not support msys or cygwin) - # TODO: msys and cygwin support - # - # If it is set, it means that java overrides the default system java, which is what we want. - if [ -n "${JAVA_HOME-}" ]; then - export PATH="$JAVA_HOME/bin:$PATH" - fi - for dir in `ls`; do - if [ -d $dir ]; then - pushd $dir >/dev/null - xterm -T "`basename $dir`" -e './NODEJAR_NAME' & - if [[ "${NO_WEB_SERVER:-}" == "" ]]; then - xterm -T "`basename $dir` Web Server" -e 'java -jar WEBJAR_NAME' & - fi - popd >/dev/null - fi - done + java -jar runnodes.jar fi diff --git a/gradle-plugins/cordformation/src/main/resources/net/corda/plugins/runnodes.bat b/gradle-plugins/cordformation/src/main/resources/net/corda/plugins/runnodes.bat index 968655aadf..b16fb270ba 100644 --- a/gradle-plugins/cordformation/src/main/resources/net/corda/plugins/runnodes.bat +++ b/gradle-plugins/cordformation/src/main/resources/net/corda/plugins/runnodes.bat @@ -3,13 +3,6 @@ REM Change to the directory of this script (%~dp0) Pushd %~dp0 -FOR /D %%G in (.\*) DO ( - Pushd %%G - start java -jar NODEJAR_NAME - IF NOT DEFINED NO_WEB_SERVER ( - start java -jar WEBJAR_NAME - ) - Popd -) +java -jar runnodes.jar Popd \ No newline at end of file diff --git a/gradle-plugins/cordformation/src/noderunner/kotlin/net/corda/plugins/NodeRunner.kt b/gradle-plugins/cordformation/src/noderunner/kotlin/net/corda/plugins/NodeRunner.kt new file mode 100644 index 0000000000..fe2a53d529 --- /dev/null +++ b/gradle-plugins/cordformation/src/noderunner/kotlin/net/corda/plugins/NodeRunner.kt @@ -0,0 +1,79 @@ +package net.corda.plugins + +import java.awt.GraphicsEnvironment +import java.io.File +import java.nio.file.Files +import java.nio.file.Paths +import java.util.Locale + +private val nodeJarName = "corda.jar" +private val webJarName = "corda-webserver.jar" +private val nodeConfName = "node.conf" + +fun main(args: Array) { + val startedProcesses = mutableListOf() + val headless = (GraphicsEnvironment.isHeadless() || (!args.isEmpty() && (args[0] == "--headless"))) + val runJar = getJarRunner(headless) + val workingDir = Paths.get(System.getProperty("user.dir")).toFile() + val javaArgs = listOf() // TODO: Add args passthrough + println("Starting nodes in $workingDir") + + workingDir.list().map { File(workingDir, it) }.forEach { + if (isNode(it)) { + println("Starting node in $it") + startedProcesses.add(runJar(nodeJarName, it, javaArgs)) + } + + if (isWebserver(it)) { + println("Starting webserver in $it") + startedProcesses.add(runJar(webJarName, it, javaArgs)) + } + } + + println("Started ${startedProcesses.size} processes") + println("Finished starting nodes") +} + +private fun isNode(maybeNodeDir: File) = maybeNodeDir.isDirectory + && File(maybeNodeDir, nodeJarName).exists() + && File(maybeNodeDir, webJarName).exists() + && File(maybeNodeDir, nodeConfName).exists() + +private fun isWebserver(maybeWebserverDir: File) = isNode(maybeWebserverDir) && hasWebserverPort(maybeWebserverDir) + +// TODO: Add a webserver.conf, or use TypeSafe config instead of this hack +private fun hasWebserverPort(nodeConfDir: File) = Files.readAllLines(File(nodeConfDir, nodeConfName).toPath()).joinToString { it }.contains("webAddress") + +private fun getJarRunner(headless: Boolean): (String, File, List) -> Process = if (headless) ::execJar else ::execJarInTerminalWindow + +private fun execJar(jarName: String, dir: File, args: List = listOf()): Process { + val nodeName = dir.toPath().fileName + val separator = System.getProperty("file.separator") + val path = System.getProperty("java.home") + separator + "bin" + separator + "java" + val builder = ProcessBuilder(listOf(path, "-Dname=$nodeName", "-jar", jarName) + args) + builder.redirectError(Paths.get("error.${dir.toPath().fileName}.log").toFile()) + builder.inheritIO() + builder.directory(dir) + return builder.start() +} + +private fun execJarInTerminalWindow(jarName: String, dir: File, args: List = listOf()): Process { + val javaCmd = "java -jar $jarName " + args.joinToString(" ") { it } + val nodeName = dir.toPath().fileName + " " + jarName + val osName = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH) + val cmd = if ((osName.indexOf("mac") >= 0) || (osName.indexOf("darwin") >= 0)) { + """osascript -e "tell app "Terminal +activate +tell application \"System Events\" to tell process \"Terminal\" to keystroke \"t\" using command down +delay 0.5 +do script "bash -c 'cd $dir; /usr/libexec/java_home -v 1.8 --exec $javaCmd && exit'" in window 1" +""" + } else if (osName.indexOf("win") >= 0) { + """cmd /C "start $javaCmd"""" + } else { + // Assume Linux + """xterm -T "$nodeName" -e $javaCmd""" + } + + return Runtime.getRuntime().exec(cmd, null, dir) +} \ No newline at end of file diff --git a/publish.properties b/publish.properties index b304ba76e9..cad85d5f8f 100644 --- a/publish.properties +++ b/publish.properties @@ -1 +1 @@ -gradlePluginsVersion=0.10.0 +gradlePluginsVersion=0.10.1