├── .gitignore ├── Dockerfile ├── README.md ├── build.sbt ├── project ├── Dependencies.scala ├── assembly.sbt └── build.properties └── src ├── main └── scala │ └── bootstrap │ └── Main.scala └── test └── scala └── example └── HelloSpec.scala /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | 4 | # sbt specific 5 | .cache/ 6 | .history/ 7 | .lib/ 8 | dist/* 9 | target/ 10 | lib_managed/ 11 | src_managed/ 12 | project/boot/ 13 | project/plugins/project/ 14 | 15 | # Scala-IDE specific 16 | .scala_dependencies 17 | .worksheet 18 | .idea -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM oracle/graalvm-ce:1.0.0-rc15 2 | RUN yum install -y java-1.8.0-openjdk-headless \ 3 | && update-alternatives --set java $JAVA_HOME/bin/java \ 4 | && mv $JAVA_HOME/jre/lib/security/cacerts $JAVA_HOME/jre/lib/security/cacerts.bak \ 5 | && ln -s /usr/lib/jvm/jre-1.8.0/lib/security/cacerts $JAVA_HOME/jre/lib/security/cacerts 6 | 7 | CMD tail -f /dev/null -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scala-graalvm-lambda 2 | 3 | Sample application of Native Scala with GraalVM. 4 | 5 | 6 | ## How to deploy to AWS Lambda 7 | 8 | 1. Run `$ sbt nativeCompile` 9 | 1. Upload zipped code (target/bundle.zip) into AWS Lambda 10 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | import Dependencies._ 2 | import scala.sys.process._ 3 | 4 | lazy val buildContainer = taskKey[Unit]("Build docker image of GraalVM") 5 | lazy val runBuildServer = taskKey[Unit]("Run GraalVM server for build native image.") 6 | lazy val nativeCompile = taskKey[Unit]("Build native image compiled by GraalVM") 7 | 8 | lazy val root = (project in file(".")). 9 | settings( 10 | inThisBuild(List( 11 | organization := "io.github.todokr", 12 | scalaVersion := "2.12.5", 13 | version := "0.1.0-SNAPSHOT" 14 | )), 15 | name := "Hello", 16 | libraryDependencies ++= Seq( 17 | "io.spray" %% "spray-json" % "1.3.5", 18 | "org.scalaj" %% "scalaj-http" % "2.4.1", 19 | scalaTest % Test 20 | ), 21 | mainClass in assembly := Some("bootstrap.Main"), 22 | assemblyJarName in assembly := s"scala-graalvm-lamda_${version.value}.jar", 23 | test in assembly := {}, 24 | buildContainer := { 25 | val exitCode = ("docker images" #| "grep graal-build-img").!(ProcessLogger(_ => ())) 26 | if (exitCode == 1) { 27 | println("Build GraalVM container...") 28 | "docker build -f Dockerfile -t graal-build-img .".! 29 | } else println("Container is already built.") 30 | }, 31 | runBuildServer := { 32 | buildContainer.value 33 | val exitCode = ("docker ps" #| "grep graal-builder$").!(ProcessLogger(_ => ())) 34 | if (exitCode == 1) { 35 | println("Start build server...") 36 | "docker run --name graal-builder -dt graal-build-img:latest".! 37 | } else println("Build server is already running.") 38 | }, 39 | nativeCompile := { 40 | clean.value 41 | assembly.value 42 | runBuildServer.value 43 | val jarName = (assemblyJarName in assembly).value 44 | (s"docker cp target/scala-2.12/$jarName graal-builder:server.jar" #&& 45 | "time docker exec graal-builder native-image -H:+ReportUnsupportedElementsAtRuntime -H:EnableURLProtocols=http,https -J-Xmx3G -J-Xms3G --no-server -jar server.jar" #&& 46 | "docker cp graal-builder:server target/bootstrap" #&& 47 | "docker cp graal-builder:/opt/graalvm-ce-1.0.0-rc15/jre/lib/amd64/libsunec.so target/libsunec.so" #&& 48 | "zip -j target/bundle.zip target/bootstrap target/libsunec.so").! 49 | } 50 | ) -------------------------------------------------------------------------------- /project/Dependencies.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | 3 | object Dependencies { 4 | lazy val scalaTest = "org.scalatest" %% "scalatest" % "3.0.5" 5 | } 6 | -------------------------------------------------------------------------------- /project/assembly.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9") -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.1.1 2 | -------------------------------------------------------------------------------- /src/main/scala/bootstrap/Main.scala: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import spray.json._ 4 | import scalaj.http._ 5 | 6 | object Main { 7 | 8 | def main(args: Array[String]): Unit = { 9 | val runtime = System.getenv("AWS_LAMBDA_RUNTIME_API") 10 | 11 | while (true) { 12 | val HttpResponse(body, _, headers) = Http(s"http://$runtime/2018-06-01/runtime/invocation/next").asString 13 | val requestId = headers("lambda-runtime-aws-request-id").head 14 | 15 | try { 16 | val name = body.parseJson.asJsObject.getFields("name").headOption match { 17 | case Some(JsString(value)) => value 18 | case _ => throw new Exception("unable to parse json") 19 | } 20 | val responseJson = JsObject("message" -> JsString(s"hello, $name!")).compactPrint 21 | Http(s"http://$runtime/2018-06-01/runtime/invocation/$requestId/response").postData(responseJson).asString 22 | } catch { 23 | case e: Exception => 24 | Http(s"http://$runtime/2018-06-01/runtime/invocation/$requestId/error").postData(e.getMessage).asString 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/scala/example/HelloSpec.scala: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import org.scalatest._ 4 | 5 | class HelloSpec extends FlatSpec with Matchers { 6 | "The Hello object" should "say hello" in { 7 | Hello.greeting shouldEqual "hello" 8 | } 9 | } 10 | --------------------------------------------------------------------------------