Apply standard scala formatting. (#2650)
Formats all .scala files according to `scalafmt`'s (opinionated) style.
Adds Travis checks for correctly formatted code.
diff --git a/.scalafmt.conf b/.scalafmt.conf
new file mode 100644
index 0000000..0dc41ec
--- /dev/null
+++ b/.scalafmt.conf
@@ -0,0 +1,6 @@
+style = intellij
+danglingParentheses = false
+maxColumn = 120
+docstrings = JavaDoc
+rewrite.rules = [SortImports]
+project.git = true
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..300a1ce
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,13 @@
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath "cz.alenkacz:gradle-scalafmt:${gradle.scalafmt.version}"
+ }
+}
+
+subprojects {
+ apply plugin: 'scalafmt'
+ scalafmt.configFilePath = gradle.scalafmt.config
+}
diff --git a/settings.gradle b/settings.gradle
index 375b6de..9a27b31 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -26,3 +26,8 @@
version: '2.11.8',
compileFlags: ['-feature', '-unchecked', '-deprecation', '-Xfatal-warnings', '-Ywarn-unused-import']
]
+
+gradle.ext.scalafmt = [
+ version: '1.5.0',
+ config: new File(rootProject.projectDir, '.scalafmt.conf')
+]
diff --git a/tests/src/test/scala/actionContainers/ActionContainer.scala b/tests/src/test/scala/actionContainers/ActionContainer.scala
index e9cc939..63da8c7 100644
--- a/tests/src/test/scala/actionContainers/ActionContainer.scala
+++ b/tests/src/test/scala/actionContainers/ActionContainer.scala
@@ -46,124 +46,127 @@
* container as blocking method calls of this interface.
*/
trait ActionContainer {
- def init(value: JsValue): (Int, Option[JsObject])
- def run(value: JsValue): (Int, Option[JsObject])
+ def init(value: JsValue): (Int, Option[JsObject])
+ def run(value: JsValue): (Int, Option[JsObject])
}
trait ActionProxyContainerTestUtils extends FlatSpec with Matchers {
- import ActionContainer.{ filterSentinel, sentinel }
+ import ActionContainer.{filterSentinel, sentinel}
- def initPayload(code: String, main: String = "main") = {
- JsObject("value" -> JsObject(
- "code" -> { if (code != null) JsString(code) else JsNull },
- "main" -> JsString(main),
- "binary" -> JsBoolean(Exec.isBinaryCode(code))))
+ def initPayload(code: String, main: String = "main") = {
+ JsObject(
+ "value" -> JsObject(
+ "code" -> { if (code != null) JsString(code) else JsNull },
+ "main" -> JsString(main),
+ "binary" -> JsBoolean(Exec.isBinaryCode(code))))
+ }
+
+ def runPayload(args: JsValue, other: Option[JsObject] = None) = {
+ JsObject(Map("value" -> args) ++ (other map { _.fields } getOrElse Map()))
+ }
+
+ def checkStreams(out: String, err: String, additionalCheck: (String, String) => Unit, sentinelCount: Int = 1) = {
+ withClue("expected number of stdout sentinels") {
+ sentinelCount shouldBe StringUtils.countMatches(out, sentinel)
+ }
+ withClue("expected number of stderr sentinels") {
+ sentinelCount shouldBe StringUtils.countMatches(err, sentinel)
}
- def runPayload(args: JsValue, other: Option[JsObject] = None) = {
- JsObject(Map("value" -> args) ++ (other map { _.fields } getOrElse Map()))
- }
-
- def checkStreams(out: String, err: String, additionalCheck: (String, String) => Unit, sentinelCount: Int = 1) = {
- withClue("expected number of stdout sentinels") {
- sentinelCount shouldBe StringUtils.countMatches(out, sentinel)
- }
- withClue("expected number of stderr sentinels") {
- sentinelCount shouldBe StringUtils.countMatches(err, sentinel)
- }
-
- val (o, e) = (filterSentinel(out), filterSentinel(err))
- o should not include (sentinel)
- e should not include (sentinel)
- additionalCheck(o, e)
- }
+ val (o, e) = (filterSentinel(out), filterSentinel(err))
+ o should not include (sentinel)
+ e should not include (sentinel)
+ additionalCheck(o, e)
+ }
}
object ActionContainer {
- private lazy val dockerBin: String = {
- List("/usr/bin/docker", "/usr/local/bin/docker").find { bin =>
- new File(bin).isFile()
- }.getOrElse(???) // This fails if the docker binary couldn't be located.
+ private lazy val dockerBin: String = {
+ List("/usr/bin/docker", "/usr/local/bin/docker")
+ .find { bin =>
+ new File(bin).isFile()
+ }
+ .getOrElse(???) // This fails if the docker binary couldn't be located.
+ }
+
+ private lazy val dockerCmd: String = {
+ val hostStr = if (WhiskProperties.onMacOSX()) {
+ s" --host tcp://${WhiskProperties.getMainDockerEndpoint()} "
+ } else {
+ " "
+ }
+ s"$dockerBin $hostStr"
+ }
+
+ private def docker(command: String): String = s"$dockerCmd $command"
+
+ // Runs a process asynchronously. Returns a future with (exitCode,stdout,stderr)
+ private def proc(cmd: String): Future[(Int, String, String)] = Future {
+ blocking {
+ val out = new ByteArrayOutputStream
+ val err = new ByteArrayOutputStream
+ val outW = new PrintWriter(out)
+ val errW = new PrintWriter(err)
+ val v = cmd ! (ProcessLogger(outW.println, errW.println))
+ outW.close()
+ errW.close()
+ (v, out.toString, err.toString)
+ }
+ }
+
+ // Tying it all together, we have a method that runs docker, waits for
+ // completion for some time then returns the exit code, the output stream
+ // and the error stream.
+ private def awaitDocker(cmd: String, t: Duration): (Int, String, String) = {
+ Await.result(proc(docker(cmd)), t)
+ }
+
+ // Filters out the sentinel markers inserted by the container (see relevant private code in Invoker.scala)
+ val sentinel = "XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX"
+ def filterSentinel(str: String) = str.replaceAll(sentinel, "").trim
+
+ def withContainer(imageName: String, environment: Map[String, String] = Map.empty)(code: ActionContainer => Unit)(
+ implicit actorSystem: ActorSystem): (String, String) = {
+ val rand = { val r = Random.nextInt; if (r < 0) -r else r }
+ val name = imageName.toLowerCase.replaceAll("""[^a-z]""", "") + rand
+ val envArgs = environment.toSeq.map {
+ case (k, v) => s"-e ${k}=${v}"
+ } mkString (" ")
+
+ // We create the container...
+ val runOut = awaitDocker(s"run --name $name $envArgs -d $imageName", 10 seconds)
+ assert(runOut._1 == 0, "'docker run' did not exit with 0: " + runOut)
+
+ // ...find out its IP address...
+ val ipOut = awaitDocker(s"""inspect --format '{{.NetworkSettings.IPAddress}}' $name""", 10 seconds)
+ assert(ipOut._1 == 0, "'docker inspect did not exit with 0")
+ val ip = ipOut._2.replaceAll("""[^0-9.]""", "")
+
+ // ...we create an instance of the mock container interface...
+ val mock = new ActionContainer {
+ def init(value: JsValue) = syncPost(ip, 8080, "/init", value)
+ def run(value: JsValue) = syncPost(ip, 8080, "/run", value)
}
- private lazy val dockerCmd: String = {
- val hostStr = if (WhiskProperties.onMacOSX()) {
- s" --host tcp://${WhiskProperties.getMainDockerEndpoint()} "
- } else {
- " "
- }
- s"$dockerBin $hostStr"
+ try {
+ // ...and finally run the code with it.
+ code(mock)
+ // I'm told this is good for the logs.
+ Thread.sleep(100)
+ val (_, out, err) = awaitDocker(s"logs $name", 10 seconds)
+ (out, err)
+ } finally {
+ awaitDocker(s"kill $name", 10 seconds)
+ awaitDocker(s"rm $name", 10 seconds)
}
+ }
- private def docker(command: String): String = s"$dockerCmd $command"
+ private def syncPost(host: String, port: Int, endPoint: String, content: JsValue): (Int, Option[JsObject]) = {
+ whisk.core.containerpool.docker.HttpUtils.post(host, port, endPoint, content)
+ }
- // Runs a process asynchronously. Returns a future with (exitCode,stdout,stderr)
- private def proc(cmd: String): Future[(Int, String, String)] = Future {
- blocking {
- val out = new ByteArrayOutputStream
- val err = new ByteArrayOutputStream
- val outW = new PrintWriter(out)
- val errW = new PrintWriter(err)
- val v = cmd ! (ProcessLogger(outW.println, errW.println))
- outW.close()
- errW.close()
- (v, out.toString, err.toString)
- }
- }
-
- // Tying it all together, we have a method that runs docker, waits for
- // completion for some time then returns the exit code, the output stream
- // and the error stream.
- private def awaitDocker(cmd: String, t: Duration): (Int, String, String) = {
- Await.result(proc(docker(cmd)), t)
- }
-
- // Filters out the sentinel markers inserted by the container (see relevant private code in Invoker.scala)
- val sentinel = "XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX"
- def filterSentinel(str: String) = str.replaceAll(sentinel, "").trim
-
- def withContainer(imageName: String, environment: Map[String, String] = Map.empty)(
- code: ActionContainer => Unit)(implicit actorSystem: ActorSystem): (String, String) = {
- val rand = { val r = Random.nextInt; if (r < 0) -r else r }
- val name = imageName.toLowerCase.replaceAll("""[^a-z]""", "") + rand
- val envArgs = environment.toSeq.map {
- case (k, v) => s"-e ${k}=${v}"
- } mkString (" ")
-
- // We create the container...
- val runOut = awaitDocker(s"run --name $name $envArgs -d $imageName", 10 seconds)
- assert(runOut._1 == 0, "'docker run' did not exit with 0: " + runOut)
-
- // ...find out its IP address...
- val ipOut = awaitDocker(s"""inspect --format '{{.NetworkSettings.IPAddress}}' $name""", 10 seconds)
- assert(ipOut._1 == 0, "'docker inspect did not exit with 0")
- val ip = ipOut._2.replaceAll("""[^0-9.]""", "")
-
- // ...we create an instance of the mock container interface...
- val mock = new ActionContainer {
- def init(value: JsValue) = syncPost(ip, 8080, "/init", value)
- def run(value: JsValue) = syncPost(ip, 8080, "/run", value)
- }
-
- try {
- // ...and finally run the code with it.
- code(mock)
- // I'm told this is good for the logs.
- Thread.sleep(100)
- val (_, out, err) = awaitDocker(s"logs $name", 10 seconds)
- (out, err)
- } finally {
- awaitDocker(s"kill $name", 10 seconds)
- awaitDocker(s"rm $name", 10 seconds)
- }
- }
-
- private def syncPost(host: String, port: Int, endPoint: String, content: JsValue): (Int, Option[JsObject]) = {
- whisk.core.containerpool.docker.HttpUtils.post(host, port, endPoint, content)
- }
-
- private class ActionContainerImpl() extends ActionContainer {
- override def init(value: JsValue) = ???
- override def run(value: JsValue) = ???
- }
+ private class ActionContainerImpl() extends ActionContainer {
+ override def init(value: JsValue) = ???
+ override def run(value: JsValue) = ???
+ }
}
diff --git a/tests/src/test/scala/actionContainers/JavaActionContainerTests.scala b/tests/src/test/scala/actionContainers/JavaActionContainerTests.scala
index fe81e3e..efa2743 100644
--- a/tests/src/test/scala/actionContainers/JavaActionContainerTests.scala
+++ b/tests/src/test/scala/actionContainers/JavaActionContainerTests.scala
@@ -32,28 +32,30 @@
@RunWith(classOf[JUnitRunner])
class JavaActionContainerTests extends FlatSpec with Matchers with WskActorSystem with ActionProxyContainerTestUtils {
- // Helpers specific to java actions
- def withJavaContainer(code: ActionContainer => Unit, env: Map[String, String] = Map.empty) = withContainer("java8action", env)(code)
+ // Helpers specific to java actions
+ def withJavaContainer(code: ActionContainer => Unit, env: Map[String, String] = Map.empty) =
+ withContainer("java8action", env)(code)
- override def initPayload(mainClass: String, jar64: String) = JsObject(
- "value" -> JsObject(
- "name" -> JsString("dummyAction"),
- "main" -> JsString(mainClass),
- "code" -> JsString(jar64)))
+ override def initPayload(mainClass: String, jar64: String) =
+ JsObject(
+ "value" -> JsObject("name" -> JsString("dummyAction"), "main" -> JsString(mainClass), "code" -> JsString(jar64)))
- behavior of "Java action"
+ behavior of "Java action"
- it should s"run a java snippet and confirm expected environment variables" in {
- val props = Seq("api_host" -> "xyz",
- "api_key" -> "abc",
- "namespace" -> "zzz",
- "action_name" -> "xxx",
- "activation_id" -> "iii",
- "deadline" -> "123")
- val env = props.map { case (k, v) => s"__OW_${k.toUpperCase}" -> v }
- val (out, err) = withJavaContainer({ c =>
- val jar = JarBuilder.mkBase64Jar(
- Seq("example", "HelloWhisk.java") -> """
+ it should s"run a java snippet and confirm expected environment variables" in {
+ val props = Seq(
+ "api_host" -> "xyz",
+ "api_key" -> "abc",
+ "namespace" -> "zzz",
+ "action_name" -> "xxx",
+ "activation_id" -> "iii",
+ "deadline" -> "123")
+ val env = props.map { case (k, v) => s"__OW_${k.toUpperCase}" -> v }
+ val (out, err) =
+ withJavaContainer(
+ { c =>
+ val jar = JarBuilder.mkBase64Jar(
+ Seq("example", "HelloWhisk.java") -> """
| package example;
|
| import com.google.gson.JsonObject;
@@ -72,25 +74,26 @@
| }
""".stripMargin.trim)
- val (initCode, _) = c.init(initPayload("example.HelloWhisk", jar))
- initCode should be(200)
+ val (initCode, _) = c.init(initPayload("example.HelloWhisk", jar))
+ initCode should be(200)
- val (runCode, out) = c.run(runPayload(JsObject(), Some(props.toMap.toJson.asJsObject)))
- runCode should be(200)
- props.map {
- case (k, v) => out.get.fields(k) shouldBe JsString(v)
+ val (runCode, out) = c.run(runPayload(JsObject(), Some(props.toMap.toJson.asJsObject)))
+ runCode should be(200)
+ props.map {
+ case (k, v) => out.get.fields(k) shouldBe JsString(v)
- }
- }, env.take(1).toMap)
+ }
+ },
+ env.take(1).toMap)
- out.trim shouldBe empty
- err.trim shouldBe empty
- }
+ out.trim shouldBe empty
+ err.trim shouldBe empty
+ }
- it should "support valid flows" in {
- val (out, err) = withJavaContainer { c =>
- val jar = JarBuilder.mkBase64Jar(
- Seq("example", "HelloWhisk.java") -> """
+ it should "support valid flows" in {
+ val (out, err) = withJavaContainer { c =>
+ val jar = JarBuilder.mkBase64Jar(
+ Seq("example", "HelloWhisk.java") -> """
| package example;
|
| import com.google.gson.JsonObject;
@@ -105,26 +108,26 @@
| }
""".stripMargin.trim)
- val (initCode, _) = c.init(initPayload("example.HelloWhisk", jar))
- initCode should be(200)
+ val (initCode, _) = c.init(initPayload("example.HelloWhisk", jar))
+ initCode should be(200)
- val (runCode1, out1) = c.run(runPayload(JsObject("name" -> JsString("Whisk"))))
- runCode1 should be(200)
- out1 should be(Some(JsObject("greeting" -> JsString("Hello Whisk!"))))
+ val (runCode1, out1) = c.run(runPayload(JsObject("name" -> JsString("Whisk"))))
+ runCode1 should be(200)
+ out1 should be(Some(JsObject("greeting" -> JsString("Hello Whisk!"))))
- val (runCode2, out2) = c.run(runPayload(JsObject("name" -> JsString("ksihW"))))
- runCode2 should be(200)
- out2 should be(Some(JsObject("greeting" -> JsString("Hello ksihW!"))))
- }
-
- out.trim shouldBe empty
- err.trim shouldBe empty
+ val (runCode2, out2) = c.run(runPayload(JsObject("name" -> JsString("ksihW"))))
+ runCode2 should be(200)
+ out2 should be(Some(JsObject("greeting" -> JsString("Hello ksihW!"))))
}
- it should "handle unicode in source, input params, logs, and result" in {
- val (out, err) = withJavaContainer { c =>
- val jar = JarBuilder.mkBase64Jar(
- Seq("example", "HelloWhisk.java") -> """
+ out.trim shouldBe empty
+ err.trim shouldBe empty
+ }
+
+ it should "handle unicode in source, input params, logs, and result" in {
+ val (out, err) = withJavaContainer { c =>
+ val jar = JarBuilder.mkBase64Jar(
+ Seq("example", "HelloWhisk.java") -> """
| package example;
|
| import com.google.gson.JsonObject;
@@ -141,39 +144,38 @@
| }
""".stripMargin)
- val (initCode, _) = c.init(initPayload("example.HelloWhisk", jar))
- val (runCode, runRes) = c.run(runPayload(JsObject("delimiter" -> JsString("❄"))))
- runRes.get.fields.get("winter") shouldBe Some(JsString("❄ ☃ ❄"))
- }
-
- out should include("❄ ☃ ❄")
- err.trim shouldBe empty
+ val (initCode, _) = c.init(initPayload("example.HelloWhisk", jar))
+ val (runCode, runRes) = c.run(runPayload(JsObject("delimiter" -> JsString("❄"))))
+ runRes.get.fields.get("winter") shouldBe Some(JsString("❄ ☃ ❄"))
}
- it should "fail to initialize with bad code" in {
- val (out, err) = withJavaContainer { c =>
- // This is valid zip file containing a single file, but not a valid
- // jar file.
- val brokenJar = (
- "UEsDBAoAAAAAAPxYbkhT4iFbCgAAAAoAAAANABwAbm90YWNsYXNzZmlsZVV" +
- "UCQADzNPmVszT5lZ1eAsAAQT1AQAABAAAAABzYXVjaXNzb24KUEsBAh4DCg" +
- "AAAAAA/FhuSFPiIVsKAAAACgAAAA0AGAAAAAAAAQAAAKSBAAAAAG5vdGFjb" +
- "GFzc2ZpbGVVVAUAA8zT5lZ1eAsAAQT1AQAABAAAAABQSwUGAAAAAAEAAQBT" +
- "AAAAUQAAAAAA")
+ out should include("❄ ☃ ❄")
+ err.trim shouldBe empty
+ }
- val (initCode, _) = c.init(initPayload("example.Broken", brokenJar))
- initCode should not be (200)
- }
+ it should "fail to initialize with bad code" in {
+ val (out, err) = withJavaContainer { c =>
+ // This is valid zip file containing a single file, but not a valid
+ // jar file.
+ val brokenJar = ("UEsDBAoAAAAAAPxYbkhT4iFbCgAAAAoAAAANABwAbm90YWNsYXNzZmlsZVV" +
+ "UCQADzNPmVszT5lZ1eAsAAQT1AQAABAAAAABzYXVjaXNzb24KUEsBAh4DCg" +
+ "AAAAAA/FhuSFPiIVsKAAAACgAAAA0AGAAAAAAAAQAAAKSBAAAAAG5vdGFjb" +
+ "GFzc2ZpbGVVVAUAA8zT5lZ1eAsAAQT1AQAABAAAAABQSwUGAAAAAAEAAQBT" +
+ "AAAAUQAAAAAA")
- // Somewhere, the logs should contain an exception.
- val combined = out + err
- combined.toLowerCase should include("exception")
+ val (initCode, _) = c.init(initPayload("example.Broken", brokenJar))
+ initCode should not be (200)
}
- it should "return some error on action error" in {
- val (out, err) = withJavaContainer { c =>
- val jar = JarBuilder.mkBase64Jar(
- Seq("example", "HelloWhisk.java") -> """
+ // Somewhere, the logs should contain an exception.
+ val combined = out + err
+ combined.toLowerCase should include("exception")
+ }
+
+ it should "return some error on action error" in {
+ val (out, err) = withJavaContainer { c =>
+ val jar = JarBuilder.mkBase64Jar(
+ Seq("example", "HelloWhisk.java") -> """
| package example;
|
| import com.google.gson.JsonObject;
@@ -185,24 +187,24 @@
| }
""".stripMargin.trim)
- val (initCode, _) = c.init(initPayload("example.HelloWhisk", jar))
- initCode should be(200)
+ val (initCode, _) = c.init(initPayload("example.HelloWhisk", jar))
+ initCode should be(200)
- val (runCode, runRes) = c.run(runPayload(JsObject()))
- runCode should not be (200)
+ val (runCode, runRes) = c.run(runPayload(JsObject()))
+ runCode should not be (200)
- runRes shouldBe defined
- runRes.get.fields.get("error") shouldBe defined
- }
-
- val combined = out + err
- combined.toLowerCase should include("exception")
+ runRes shouldBe defined
+ runRes.get.fields.get("error") shouldBe defined
}
- it should "support application errors" in {
- val (out, err) = withJavaContainer { c =>
- val jar = JarBuilder.mkBase64Jar(
- Seq("example", "Error.java") -> """
+ val combined = out + err
+ combined.toLowerCase should include("exception")
+ }
+
+ it should "support application errors" in {
+ val (out, err) = withJavaContainer { c =>
+ val jar = JarBuilder.mkBase64Jar(
+ Seq("example", "Error.java") -> """
| package example;
|
| import com.google.gson.JsonObject;
@@ -216,24 +218,24 @@
| }
""".stripMargin.trim)
- val (initCode, _) = c.init(initPayload("example.Error", jar))
- initCode should be(200)
+ val (initCode, _) = c.init(initPayload("example.Error", jar))
+ initCode should be(200)
- val (runCode, runRes) = c.run(runPayload(JsObject()))
- runCode should be(200) // action writer returning an error is OK
+ val (runCode, runRes) = c.run(runPayload(JsObject()))
+ runCode should be(200) // action writer returning an error is OK
- runRes shouldBe defined
- runRes.get.fields.get("error") shouldBe defined
- }
-
- val combined = out + err
- combined.trim shouldBe empty
+ runRes shouldBe defined
+ runRes.get.fields.get("error") shouldBe defined
}
- it should "survive System.exit" in {
- val (out, err) = withJavaContainer { c =>
- val jar = JarBuilder.mkBase64Jar(
- Seq("example", "Quitter.java") -> """
+ val combined = out + err
+ combined.trim shouldBe empty
+ }
+
+ it should "survive System.exit" in {
+ val (out, err) = withJavaContainer { c =>
+ val jar =
+ JarBuilder.mkBase64Jar(Seq("example", "Quitter.java") -> """
| package example;
|
| import com.google.gson.*;
@@ -246,24 +248,24 @@
| }
""".stripMargin.trim)
- val (initCode, _) = c.init(initPayload("example.Quitter", jar))
- initCode should be(200)
+ val (initCode, _) = c.init(initPayload("example.Quitter", jar))
+ initCode should be(200)
- val (runCode, runRes) = c.run(runPayload(JsObject()))
- runCode should not be (200)
+ val (runCode, runRes) = c.run(runPayload(JsObject()))
+ runCode should not be (200)
- runRes shouldBe defined
- runRes.get.fields.get("error") shouldBe defined
- }
-
- val combined = out + err
- combined.toLowerCase should include("system.exit")
+ runRes shouldBe defined
+ runRes.get.fields.get("error") shouldBe defined
}
- it should "enforce that the user returns an object" in {
- withJavaContainer { c =>
- val jar = JarBuilder.mkBase64Jar(
- Seq("example", "Nuller.java") -> """
+ val combined = out + err
+ combined.toLowerCase should include("system.exit")
+ }
+
+ it should "enforce that the user returns an object" in {
+ withJavaContainer { c =>
+ val jar =
+ JarBuilder.mkBase64Jar(Seq("example", "Nuller.java") -> """
| package example;
|
| import com.google.gson.*;
@@ -275,20 +277,20 @@
| }
""".stripMargin.trim)
- val (initCode, _) = c.init(initPayload("example.Nuller", jar))
- initCode should be(200)
+ val (initCode, _) = c.init(initPayload("example.Nuller", jar))
+ initCode should be(200)
- val (runCode, runRes) = c.run(runPayload(JsObject()))
- runCode should not be (200)
+ val (runCode, runRes) = c.run(runPayload(JsObject()))
+ runCode should not be (200)
- runRes shouldBe defined
- runRes.get.fields.get("error") shouldBe defined
- }
+ runRes shouldBe defined
+ runRes.get.fields.get("error") shouldBe defined
}
+ }
- val dynamicLoadingJar = JarBuilder.mkBase64Jar(
- Seq(
- Seq("example", "EntryPoint.java") -> """
+ val dynamicLoadingJar = JarBuilder.mkBase64Jar(
+ Seq(
+ Seq("example", "EntryPoint.java") -> """
| package example;
|
| import com.google.gson.*;
@@ -316,7 +318,7 @@
| }
| }
|""".stripMargin.trim,
- Seq("example", "DynamicClass.java") -> """
+ Seq("example", "DynamicClass.java") -> """
| package example;
|
| public class DynamicClass {
@@ -326,25 +328,25 @@
| }
|""".stripMargin.trim))
- def classLoaderTest(param: String) = {
- val (out, err) = withJavaContainer { c =>
- val (initCode, _) = c.init(initPayload("example.EntryPoint", dynamicLoadingJar))
- initCode should be(200)
+ def classLoaderTest(param: String) = {
+ val (out, err) = withJavaContainer { c =>
+ val (initCode, _) = c.init(initPayload("example.EntryPoint", dynamicLoadingJar))
+ initCode should be(200)
- val (runCode, runRes) = c.run(runPayload(JsObject("classLoader" -> JsString(param))))
- runCode should be(200)
+ val (runCode, runRes) = c.run(runPayload(JsObject("classLoader" -> JsString(param))))
+ runCode should be(200)
- runRes shouldBe defined
- runRes.get.fields.get("message") shouldBe Some(JsString("dynamic!"))
- }
- (out ++ err).trim shouldBe empty
+ runRes shouldBe defined
+ runRes.get.fields.get("message") shouldBe Some(JsString("dynamic!"))
}
+ (out ++ err).trim shouldBe empty
+ }
- it should "support loading classes from the current classloader" in {
- classLoaderTest("local")
- }
+ it should "support loading classes from the current classloader" in {
+ classLoaderTest("local")
+ }
- it should "support loading classes from the Thread classloader" in {
- classLoaderTest("thread")
- }
+ it should "support loading classes from the Thread classloader" in {
+ classLoaderTest("thread")
+ }
}
diff --git a/tests/src/test/scala/actionContainers/ResourceHelpers.scala b/tests/src/test/scala/actionContainers/ResourceHelpers.scala
index 39d963b..3041e0e 100644
--- a/tests/src/test/scala/actionContainers/ResourceHelpers.scala
+++ b/tests/src/test/scala/actionContainers/ResourceHelpers.scala
@@ -38,152 +38,153 @@
* on file contents.
*/
object ResourceHelpers {
- /** Creates a zip file based on the contents of a top-level directory. */
- object ZipBuilder {
- def mkBase64Zip(sources: Seq[(Seq[String], String)]): String = {
- val (tmpDir, _) = writeSourcesToTempDirectory(sources)
- val archive = makeZipFromDir(tmpDir)
- readAsBase64(archive)
- }
+
+ /** Creates a zip file based on the contents of a top-level directory. */
+ object ZipBuilder {
+ def mkBase64Zip(sources: Seq[(Seq[String], String)]): String = {
+ val (tmpDir, _) = writeSourcesToTempDirectory(sources)
+ val archive = makeZipFromDir(tmpDir)
+ readAsBase64(archive)
+ }
+ }
+
+ /**
+ * A convenience object to compile and package Java sources into a JAR, and to
+ * encode that JAR as a base 64 string. The compilation options include the
+ * current classpath, which is why Google GSON is readily available (though not
+ * packaged in the JAR).
+ */
+ object JarBuilder {
+ def mkBase64Jar(sources: Seq[(Seq[String], String)]): String = {
+ // Note that this pipeline doesn't delete any of the temporary files.
+ val binDir = compile(sources)
+ val jarPath = makeJarFromDir(binDir)
+ val base64 = readAsBase64(jarPath)
+ base64
}
- /**
- * A convenience object to compile and package Java sources into a JAR, and to
- * encode that JAR as a base 64 string. The compilation options include the
- * current classpath, which is why Google GSON is readily available (though not
- * packaged in the JAR).
- */
- object JarBuilder {
- def mkBase64Jar(sources: Seq[(Seq[String], String)]): String = {
- // Note that this pipeline doesn't delete any of the temporary files.
- val binDir = compile(sources)
- val jarPath = makeJarFromDir(binDir)
- val base64 = readAsBase64(jarPath)
- base64
- }
+ def mkBase64Jar(source: (Seq[String], String)): String = {
+ mkBase64Jar(Seq(source))
+ }
- def mkBase64Jar(source: (Seq[String], String)): String = {
- mkBase64Jar(Seq(source))
- }
+ private def compile(sources: Seq[(Seq[String], String)]): Path = {
+ require(!sources.isEmpty)
- private def compile(sources: Seq[(Seq[String], String)]): Path = {
- require(!sources.isEmpty)
+ // The absolute paths of the source file
+ val (srcDir, srcAbsPaths) = writeSourcesToTempDirectory(sources)
- // The absolute paths of the source file
- val (srcDir, srcAbsPaths) = writeSourcesToTempDirectory(sources)
+ // A temporary directory for the destination files.
+ val binDir = Files.createTempDirectory("bin").toAbsolutePath()
- // A temporary directory for the destination files.
- val binDir = Files.createTempDirectory("bin").toAbsolutePath()
+ // Preparing the compiler
+ val compiler = ToolProvider.getSystemJavaCompiler()
+ val fileManager = compiler.getStandardFileManager(null, null, StandardCharsets.UTF_8)
- // Preparing the compiler
- val compiler = ToolProvider.getSystemJavaCompiler()
- val fileManager = compiler.getStandardFileManager(null, null, StandardCharsets.UTF_8)
+ // Collecting all files to be compiled
+ val compUnit = fileManager.getJavaFileObjectsFromFiles(srcAbsPaths.map(_.toFile).asJava)
- // Collecting all files to be compiled
- val compUnit = fileManager.getJavaFileObjectsFromFiles(srcAbsPaths.map(_.toFile).asJava)
+ // Setting the options
+ val compOptions = Seq("-d", binDir.toAbsolutePath().toString(), "-classpath", buildClassPath())
+ val compTask = compiler.getTask(null, fileManager, null, compOptions.asJava, null, compUnit)
- // Setting the options
- val compOptions = Seq(
- "-d", binDir.toAbsolutePath().toString(),
- "-classpath", buildClassPath())
- val compTask = compiler.getTask(null, fileManager, null, compOptions.asJava, null, compUnit)
+ // ...and off we go.
+ compTask.call()
- // ...and off we go.
- compTask.call()
+ binDir
+ }
- binDir
- }
+ private def buildClassPath(): String = {
+ val bcp = System.getProperty("java.class.path")
- private def buildClassPath(): String = {
- val bcp = System.getProperty("java.class.path")
+ val list = this.getClass().getClassLoader() match {
+ case ucl: URLClassLoader =>
+ bcp :: ucl.getURLs().map(_.getFile().toString()).toList
- val list = this.getClass().getClassLoader() match {
- case ucl: URLClassLoader =>
- bcp :: ucl.getURLs().map(_.getFile().toString()).toList
+ case _ =>
+ List(bcp)
+ }
- case _ =>
- List(bcp)
+ list.mkString(System.getProperty("path.separator"))
+ }
+ }
+
+ /**
+ * Creates a temporary directory and reproduces the desired file structure
+ * in it. Returns the path of the temporary directory and the path of each
+ * file as represented in it.
+ */
+ private def writeSourcesToTempDirectory(sources: Seq[(Seq[String], String)]): (Path, Seq[Path]) = {
+ // A temporary directory for the source files.
+ val srcDir = Files.createTempDirectory("src").toAbsolutePath()
+
+ val srcAbsPaths = for ((sourceName, sourceContent) <- sources) yield {
+ // The relative path of the source file
+ val srcRelPath = Paths.get(sourceName.head, sourceName.tail: _*)
+ // The absolute path of the source file
+ val srcAbsPath = srcDir.resolve(srcRelPath)
+ // Create parent directories if needed.
+ Files.createDirectories(srcAbsPath.getParent)
+ // Writing contents
+ Files.write(srcAbsPath, sourceContent.getBytes(StandardCharsets.UTF_8))
+
+ srcAbsPath
+ }
+
+ (srcDir, srcAbsPaths)
+ }
+
+ private def makeZipFromDir(dir: Path): Path = makeArchiveFromDir(dir, ".zip")
+
+ private def makeJarFromDir(dir: Path): Path = makeArchiveFromDir(dir, ".jar")
+
+ /**
+ * Compresses all files beyond a directory into a zip file.
+ * Note that Jar files are just zip files.
+ */
+ private def makeArchiveFromDir(dir: Path, extension: String): Path = {
+ // Any temporary file name for the archive.
+ val arPath = Files.createTempFile("output", extension).toAbsolutePath()
+
+ // We "mount" it as a filesystem, so we can just copy files into it.
+ val dstUri = new URI("jar:" + arPath.toUri().getScheme(), arPath.toAbsolutePath().toString(), null)
+ // OK, that's a hack. Doing this because newFileSystem wants to create that file.
+ arPath.toFile().delete()
+ val fs = FileSystems.newFileSystem(dstUri, Map(("create" -> "true")).asJava)
+
+ // Traversing all files in the bin directory...
+ Files.walkFileTree(
+ dir,
+ new SimpleFileVisitor[Path]() {
+ override def visitFile(path: Path, attributes: BasicFileAttributes) = {
+ // The path relative to the src dir
+ val relPath = dir.relativize(path)
+
+ // The corresponding path in the zip
+ val arRelPath = fs.getPath(relPath.toString())
+
+ // If this file is not top-level in the src dir...
+ if (relPath.getParent() != null) {
+ // ...create the directory structure if it doesn't exist.
+ if (!Files.exists(arRelPath.getParent())) {
+ Files.createDirectories(arRelPath.getParent())
}
+ }
- list.mkString(System.getProperty("path.separator"))
+ // Finally we can copy that file.
+ Files.copy(path, arRelPath)
+
+ FileVisitResult.CONTINUE
}
- }
+ })
- /**
- * Creates a temporary directory and reproduces the desired file structure
- * in it. Returns the path of the temporary directory and the path of each
- * file as represented in it.
- */
- private def writeSourcesToTempDirectory(sources: Seq[(Seq[String], String)]): (Path, Seq[Path]) = {
- // A temporary directory for the source files.
- val srcDir = Files.createTempDirectory("src").toAbsolutePath()
+ fs.close()
- val srcAbsPaths = for ((sourceName, sourceContent) <- sources) yield {
- // The relative path of the source file
- val srcRelPath = Paths.get(sourceName.head, sourceName.tail: _*)
- // The absolute path of the source file
- val srcAbsPath = srcDir.resolve(srcRelPath)
- // Create parent directories if needed.
- Files.createDirectories(srcAbsPath.getParent)
- // Writing contents
- Files.write(srcAbsPath, sourceContent.getBytes(StandardCharsets.UTF_8))
+ arPath
+ }
- srcAbsPath
- }
-
- (srcDir, srcAbsPaths)
- }
-
- private def makeZipFromDir(dir: Path): Path = makeArchiveFromDir(dir, ".zip")
-
- private def makeJarFromDir(dir: Path): Path = makeArchiveFromDir(dir, ".jar")
-
- /**
- * Compresses all files beyond a directory into a zip file.
- * Note that Jar files are just zip files.
- */
- private def makeArchiveFromDir(dir: Path, extension: String): Path = {
- // Any temporary file name for the archive.
- val arPath = Files.createTempFile("output", extension).toAbsolutePath()
-
- // We "mount" it as a filesystem, so we can just copy files into it.
- val dstUri = new URI("jar:" + arPath.toUri().getScheme(), arPath.toAbsolutePath().toString(), null)
- // OK, that's a hack. Doing this because newFileSystem wants to create that file.
- arPath.toFile().delete()
- val fs = FileSystems.newFileSystem(dstUri, Map(("create" -> "true")).asJava)
-
- // Traversing all files in the bin directory...
- Files.walkFileTree(dir, new SimpleFileVisitor[Path]() {
- override def visitFile(path: Path, attributes: BasicFileAttributes) = {
- // The path relative to the src dir
- val relPath = dir.relativize(path)
-
- // The corresponding path in the zip
- val arRelPath = fs.getPath(relPath.toString())
-
- // If this file is not top-level in the src dir...
- if (relPath.getParent() != null) {
- // ...create the directory structure if it doesn't exist.
- if (!Files.exists(arRelPath.getParent())) {
- Files.createDirectories(arRelPath.getParent())
- }
- }
-
- // Finally we can copy that file.
- Files.copy(path, arRelPath)
-
- FileVisitResult.CONTINUE
- }
- })
-
- fs.close()
-
- arPath
- }
-
- /** Reads the contents of a (possibly binary) file into a base64-encoded String */
- def readAsBase64(path: Path): String = {
- val encoder = Base64.getEncoder()
- new String(encoder.encode(Files.readAllBytes(path)), StandardCharsets.UTF_8)
- }
+ /** Reads the contents of a (possibly binary) file into a base64-encoded String */
+ def readAsBase64(path: Path): String = {
+ val encoder = Base64.getEncoder()
+ new String(encoder.encode(Files.readAllBytes(path)), StandardCharsets.UTF_8)
+ }
}