From 2e48921a4862dc91ad2f2775a1ba8526113efca4 Mon Sep 17 00:00:00 2001 From: "Seonghyun, Oh" Date: Wed, 26 Feb 2020 15:06:42 +0900 Subject: [PATCH 1/7] Add java11actionloop --- core/java11actionloop/CHANGELOG.md | 24 +++ core/java11actionloop/Dockerfile | 66 +++++++ core/java11actionloop/Makefile | 72 +++++++ core/java11actionloop/build.gradle | 19 ++ core/java11actionloop/lib/src/Launcher.java | 177 ++++++++++++++++++ settings.gradle | 1 + .../Java11ActionLoopContainerTests.scala | 30 +++ 7 files changed, 389 insertions(+) create mode 100644 core/java11actionloop/CHANGELOG.md create mode 100644 core/java11actionloop/Dockerfile create mode 100644 core/java11actionloop/Makefile create mode 100644 core/java11actionloop/build.gradle create mode 100644 core/java11actionloop/lib/src/Launcher.java create mode 100644 tests/src/test/scala/actionContainers/Java11ActionLoopContainerTests.scala diff --git a/core/java11actionloop/CHANGELOG.md b/core/java11actionloop/CHANGELOG.md new file mode 100644 index 00000000..d64f5d7d --- /dev/null +++ b/core/java11actionloop/CHANGELOG.md @@ -0,0 +1,24 @@ + + +# Java 11 OpenWhisk Runtime Container + +## 1.14.0 + - Initial release of actionloop-based Java 11 Action + - adoptopenjdk/openjdk11-openj9:x86_64-ubuntu-jdk-11.0.6_10_openj9-0.18.0-slim diff --git a/core/java11actionloop/Dockerfile b/core/java11actionloop/Dockerfile new file mode 100644 index 00000000..d9e4aaac --- /dev/null +++ b/core/java11actionloop/Dockerfile @@ -0,0 +1,66 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# build go proxy from source +FROM golang:1.12 AS builder_source +RUN env CGO_ENABLED=0 go get github.com/apache/openwhisk-runtime-go/main && mv /go/bin/main /bin/proxy + +# or build it from a release +FROM golang:1.12 AS builder_release +ARG GO_PROXY_RELEASE_VERSION=1.12@1.15.0 +RUN curl -sL \ + https://github.com/apache/openwhisk-runtime-go/archive/{$GO_PROXY_RELEASE_VERSION}.tar.gz\ + | tar xzf -\ + && cd openwhisk-runtime-go-*/main\ + && GO111MODULE=on go build -o /bin/proxy + +# Use AdoptOpen JDK11, OpenJ9 release version 0.18.1 +FROM adoptopenjdk/openjdk11-openj9:x86_64-ubuntu-jdk-11.0.6_10_openj9-0.18.0-slim + +# select the builder to use +ARG GO_PROXY_BUILD_FROM=release + +RUN rm -rf /var/lib/apt/lists/* && apt-get clean && apt-get update \ + && apt-get install -y --no-install-recommends locales python vim \ + && rm -rf /var/lib/apt/lists/* \ + && locale-gen en_US.UTF-8 + +ENV LANG="en_US.UTF-8" \ + LANGUAGE="en_US:en" \ + LC_ALL="en_US.UTF-8" \ + VERSION=8 \ + UPDATE=222 \ + BUILD=10 + +RUN locale-gen en_US.UTF-8 ;\ + mkdir -p /javaAction/action /usr/java/src /usr/java/lib + +WORKDIR /javaAction + +COPY --from=builder_source /bin/proxy /bin/proxy_source +COPY --from=builder_release /bin/proxy /bin/proxy_release +RUN mv /bin/proxy_${GO_PROXY_BUILD_FROM} /bin/proxy + +ADD https://search.maven.org/remotecontent?filepath=com/google/code/gson/gson/2.8.5/gson-2.8.5.jar /usr/java/lib/gson-2.8.5.jar +ADD lib/src/Launcher.java /usr/java/src/Launcher.java +RUN cd /usr/java/src ;\ + javac -cp /usr/java/lib/gson-2.8.5.jar Launcher.java ;\ + jar cvf /usr/java/lib/launcher.jar *.class +ADD bin/compile /bin/compile +ENV OW_COMPILER=/bin/compile +ENV OW_SAVE_JAR=exec.jar +ENTRYPOINT /bin/proxy diff --git a/core/java11actionloop/Makefile b/core/java11actionloop/Makefile new file mode 100644 index 00000000..1402564b --- /dev/null +++ b/core/java11actionloop/Makefile @@ -0,0 +1,72 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +IMG=actionloop-java-v11:nightly +PREFIX=docker.io/openwhisk +INVOKE=python ../../tools/invoke.py +MAIN_JAR=../../example/main.jar + +build: + docker build -t $(IMG) . + +push: build + docker login + docker tag $(IMG) $(PREFIX)/$(IMG) + docker push $(PREFIX)/$(IMG) + +clean: + docker rmi -f $(IMG) + +start: build + docker run -p 8080:8080 -ti -v $(PWD):/proxy $(IMG) + +debug: build + docker run -p 8080:8080 -ti --entrypoint=/bin/bash -v $(PWD):/mnt -e OW_COMPILER=/mnt/bin/compile $(IMG) + +.PHONY: build push clean start debug + +$(MAIN_JAR): + $(MAKE) $< -C ../../example main.jar + +## You need to execute make start in another terminal + +test-source: + $(INVOKE) init ../../example/Main.java + $(INVOKE) run '{}' + $(INVOKE) run '{"name":"Mike"}' + +test-source-hello: + $(INVOKE) init action.Hello#hello ../../example/action/Hello.java + $(INVOKE) run '{}' + $(INVOKE) run '{"name":"Mike"}' + +test-jar: $(MAIN_JAR) + $(INVOKE) init action.Hello#hello $(MAIN_JAR) + $(INVOKE) run '{}' + $(INVOKE) run '{"name":"Mike"}' + +test-src-zip: + $(MAKE) -C ../../example src.zip + $(INVOKE) init ../../example/src.zip + $(INVOKE) run '{}' + $(INVOKE) run '{"name":"Mike"}' + +test-bin-zip: + $(MAKE) -C ../../example bin.zip + $(INVOKE) init ../../example/bin.zip + $(INVOKE) run '{}' + $(INVOKE) run '{"name":"Mike"}' diff --git a/core/java11actionloop/build.gradle b/core/java11actionloop/build.gradle new file mode 100644 index 00000000..3620a6ae --- /dev/null +++ b/core/java11actionloop/build.gradle @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +ext.dockerImageName = 'actionloop-java-v11' +apply from: '../../gradle/docker.gradle' diff --git a/core/java11actionloop/lib/src/Launcher.java b/core/java11actionloop/lib/src/Launcher.java new file mode 100644 index 00000000..ef571e97 --- /dev/null +++ b/core/java11actionloop/lib/src/Launcher.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.*; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import com.google.gson.*; +import java.security.Permission; +import java.lang.reflect.InvocationTargetException; + +class Launcher { + + private static String mainClassName = "Main"; + private static String mainMethodName = "main"; + private static Class mainClass = null; + private static Method mainMethod = null; + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static void augmentEnv(Map newEnv) { + try { + for (Class cl : Collections.class.getDeclaredClasses()) { + if ("java.util.Collections$UnmodifiableMap".equals(cl.getName())) { + Field field = cl.getDeclaredField("m"); + field.setAccessible(true); + Object obj = field.get(System.getenv()); + Map map = (Map) obj; + map.putAll(newEnv); + } + } + } catch (Exception e) {} + } + + private static void initMain(String[] args) throws Exception { + if(args.length > 0) + mainClassName = args[0]; + int pos = mainClassName.indexOf("#"); + if(pos != -1) { + if(pos + 1 != mainClassName.length()) + mainMethodName = args[0].substring(pos+1); + mainClassName = args[0].substring(0,pos); + } + + mainClass = Class.forName(mainClassName); + Method m = mainClass.getMethod(mainMethodName, new Class[] { JsonObject.class }); + m.setAccessible(true); + int modifiers = m.getModifiers(); + if (m.getReturnType() != JsonObject.class || !Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) { + throw new NoSuchMethodException(mainMethodName); + } + mainMethod = m; + } + + private static JsonObject invokeMain(JsonObject arg, Map env) throws Exception { + augmentEnv(env); + return (JsonObject) mainMethod.invoke(null, arg); + } + + private static SecurityManager defaultSecurityManager = null; + private static void installSecurityManager() { + defaultSecurityManager = System.getSecurityManager(); + System.setSecurityManager(new SecurityManager() { + @Override + public void checkPermission(Permission p) { + // Not throwing means accepting anything. + } + + @Override + public void checkPermission(Permission p, Object ctx) { + // Not throwing means accepting anything. + } + + @Override + public void checkExit(int status) { + super.checkExit(status); + throw new SecurityException("System.exit(" + status + ") called from within an action."); + } + }); + } + + private static void uninstallSecurityManager() { + if(defaultSecurityManager != null) { + System.setSecurityManager(defaultSecurityManager); + } + } + + public static void main(String[] args) throws Exception { + + initMain(args); + + // exit after main class loading if "exit" specified + // used to check healthy launch after init + if(args.length >1 && args[1] == "-exit") + System.exit(0); + + // install a security manager to prevent exit + installSecurityManager(); + + BufferedReader in = new BufferedReader( + new InputStreamReader(System.in, "UTF-8")); + PrintWriter out = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream("/dev/fd/3"), "UTF-8")); + JsonParser json = new JsonParser(); + JsonObject empty = json.parse("{}").getAsJsonObject(); + String input = ""; + while (true) { + try { + input = in.readLine(); + if (input == null) + break; + JsonElement element = json.parse(input); + JsonObject payload = empty.deepCopy(); + HashMap env = new HashMap(); + if (element.isJsonObject()) { + // collect payload and environment + for (Map.Entry entry : element.getAsJsonObject().entrySet()) { + if (entry.getKey().equals("value")) { + if (entry.getValue().isJsonObject()) + payload = entry.getValue().getAsJsonObject(); + } else { + env.put(String.format("__OW_%s", entry.getKey().toUpperCase()), + entry.getValue().getAsString()); + } + } + augmentEnv(env); + } + JsonElement response = invokeMain(payload, env); + out.println(response.toString()); + } catch(NullPointerException npe) { + System.out.println("the action returned null"); + npe.printStackTrace(System.err); + JsonObject error = new JsonObject(); + error.addProperty("error", "the action returned null"); + out.println(error.toString()); + out.flush(); + } catch(InvocationTargetException ite) { + Throwable ex = ite; + if(ite.getCause() != null) + ex = ite.getCause(); + ex.printStackTrace(System.err); + JsonObject error = new JsonObject(); + error.addProperty("error", ex.getMessage()); + out.println(error.toString()); + out.flush(); + } catch (Exception ex) { + ex.printStackTrace(System.err); + JsonObject error = new JsonObject(); + error.addProperty("error", ex.getMessage()); + out.println(error.toString()); + out.flush(); + } + out.flush(); + System.out.flush(); + System.err.flush(); + } + uninstallSecurityManager(); + } +} + diff --git a/settings.gradle b/settings.gradle index b9557da0..f5598f6f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -20,6 +20,7 @@ include 'tests' include 'core:java8' include 'core:java8:proxy' include 'core:java8actionloop' +include 'core:java11actionloop' rootProject.name = 'runtime-java' diff --git a/tests/src/test/scala/actionContainers/Java11ActionLoopContainerTests.scala b/tests/src/test/scala/actionContainers/Java11ActionLoopContainerTests.scala new file mode 100644 index 00000000..2469425c --- /dev/null +++ b/tests/src/test/scala/actionContainers/Java11ActionLoopContainerTests.scala @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package actionContainers + +import common.WskActorSystem +import org.junit.runner.RunWith +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class Java11ActionLoopContainerTests extends JavaActionContainerTests with WskActorSystem { + override val errPrefix = "\"Exception in thread \\\"main\\\"" + override val checkStreamsAtInit = false + override val image = "actionloop-java-v11" + override val runtimeDetectErrors = false +} From 1a49af44ecd3f2c34d7f621cace9d11efd0bdad5 Mon Sep 17 00:00:00 2001 From: "Seonghyun, Oh" Date: Wed, 26 Feb 2020 15:12:18 +0900 Subject: [PATCH 2/7] Add compile script --- core/java11actionloop/bin/compile | 129 ++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100755 core/java11actionloop/bin/compile diff --git a/core/java11actionloop/bin/compile b/core/java11actionloop/bin/compile new file mode 100755 index 00000000..6f992b62 --- /dev/null +++ b/core/java11actionloop/bin/compile @@ -0,0 +1,129 @@ +#!/usr/bin/env python +"""Java Action Builder +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +""" + +from __future__ import print_function +from os.path import abspath, exists, dirname +import os, sys, codecs, subprocess, shutil, logging + +def copy(src, dst): + with codecs.open(src, 'r', 'utf-8') as s: + body = s.read() + with codecs.open(dst, 'w', 'utf-8') as d: + d.write(body) + +def find_with_ext(basedir, ext): + result = [] + for root, _, files in os.walk(basedir, topdown=False): + for name in files: + if name.endswith(ext): + result.append(os.path.join(root,name)) + return result + +def javac(sources, classpath, target_dir): + cmd = [ "javac", + "-encoding", "UTF-8", + "-cp", ":".join(classpath), + "-d", target_dir + ]+sources + #print(cmd) + logging.info(" ".join(cmd)) + p = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (o, e) = p.communicate() + if isinstance(o, bytes) and not isinstance(o, str): + o = o.decode('utf-8') + if isinstance(e, bytes) and not isinstance(e, str): + e = e.decode('utf-8') + ok = True + if o: + ok = False + sys.stdout.write(o) + sys.stdout.flush() + if e: + ok = False + sys.stderr.write(e) + sys.stderr.flush() + return ok + +def build(source_dir, classpath, target_dir, mainClass): + + # copy exec to
.java if it is there + src = "%s/exec" % source_dir + if os.path.isfile(src): + main_java = "%s/%s.java" % (source_dir, mainClass.split(".")[-1]) + copy(src,main_java) + logging.info("renamed exec to %s", main_java) + + # look for sources and compile + sources = find_with_ext(source_dir, ".java") + if len(sources) > 0: + jars = find_with_ext(source_dir, ".jar") + logging.info("compiling %d sources with %d jars", len(sources),len(jars)) + return javac(sources, classpath+jars, source_dir) + + return True + +def write_exec(target_dir, classpath, main): + launcher = "%s/exec" % target_dir + jars = find_with_ext(target_dir, ".jar") + jars.append(target_dir) + cmd = """#!/bin/bash +cd "%s" +/opt/java/openjdk/bin/java -Dfile.encoding=UTF-8 -cp "%s" Launcher "%s" "$@" +""" %( target_dir, ":".join(classpath+jars), main) + with codecs.open(launcher, 'w', 'utf-8') as d: + d.write(cmd) + os.chmod(launcher, 0o755) + +def parseMain(main): + if main == "main": + return "Main", "main" + a = main.split("#") + if(len(a)==1): + return a[0], "main" + return a[0], a[1] + +def assemble(argv): + mainClass, mainMethod = parseMain(argv[1]) + logging.info("%s %s", mainClass, mainMethod) + source_dir = os.path.abspath(argv[2]) + target_dir = os.path.abspath(argv[3]) + classpath = ["/usr/java/lib/launcher.jar", "/usr/java/lib/gson-2.8.5.jar"] + + # build + if build(source_dir, classpath, target_dir, mainClass): + shutil.rmtree(target_dir) + shutil.move(source_dir, target_dir) + logging.info("moved %s to %s", source_dir, target_dir) + # write the launcher is it is there + write_exec(target_dir, classpath, "%s#%s" % (mainClass, mainMethod)) + # launch it to check it can load with immediate exit - it should not produce any output + subprocess.call(["%s/exec" % target_dir, "-exit"]) + + sys.stdout.flush() + sys.stderr.flush() + +if __name__ == '__main__': + if len(sys.argv) < 4: + sys.stdout.write("usage: \n") + sys.exit(1) + logging.basicConfig(filename="/var/log/compile.log") + assemble(sys.argv) From 7f9c13db2410c173d510fd259ff5f14c03af3260 Mon Sep 17 00:00:00 2001 From: "Seonghyun, Oh" Date: Wed, 26 Feb 2020 15:26:36 +0900 Subject: [PATCH 3/7] Add java options for version 11 --- core/java8actionloop/bin/compile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java8actionloop/bin/compile b/core/java8actionloop/bin/compile index 6f992b62..033775a3 100755 --- a/core/java8actionloop/bin/compile +++ b/core/java8actionloop/bin/compile @@ -87,7 +87,7 @@ def write_exec(target_dir, classpath, main): jars.append(target_dir) cmd = """#!/bin/bash cd "%s" -/opt/java/openjdk/bin/java -Dfile.encoding=UTF-8 -cp "%s" Launcher "%s" "$@" +/opt/java/openjdk/bin/java --add-opens java.base/java.util=ALL-UNNAMED --illegal-access=permit -Dfile.encoding=UTF-8 -cp "%s" Launcher "%s" "$@" """ %( target_dir, ":".join(classpath+jars), main) with codecs.open(launcher, 'w', 'utf-8') as d: d.write(cmd) From 2124d0170771a996e69d9c6d9835400c172055dd Mon Sep 17 00:00:00 2001 From: "Seonghyun, Oh" Date: Wed, 26 Feb 2020 15:28:47 +0900 Subject: [PATCH 4/7] Add java11 source tests --- core/java11actionloop/bin/compile | 2 +- core/java8actionloop/bin/compile | 2 +- .../Java11ActionLoopSourceTests.scala | 31 +++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 tests/src/test/scala/actionContainers/Java11ActionLoopSourceTests.scala diff --git a/core/java11actionloop/bin/compile b/core/java11actionloop/bin/compile index 6f992b62..033775a3 100755 --- a/core/java11actionloop/bin/compile +++ b/core/java11actionloop/bin/compile @@ -87,7 +87,7 @@ def write_exec(target_dir, classpath, main): jars.append(target_dir) cmd = """#!/bin/bash cd "%s" -/opt/java/openjdk/bin/java -Dfile.encoding=UTF-8 -cp "%s" Launcher "%s" "$@" +/opt/java/openjdk/bin/java --add-opens java.base/java.util=ALL-UNNAMED --illegal-access=permit -Dfile.encoding=UTF-8 -cp "%s" Launcher "%s" "$@" """ %( target_dir, ":".join(classpath+jars), main) with codecs.open(launcher, 'w', 'utf-8') as d: d.write(cmd) diff --git a/core/java8actionloop/bin/compile b/core/java8actionloop/bin/compile index 033775a3..6f992b62 100755 --- a/core/java8actionloop/bin/compile +++ b/core/java8actionloop/bin/compile @@ -87,7 +87,7 @@ def write_exec(target_dir, classpath, main): jars.append(target_dir) cmd = """#!/bin/bash cd "%s" -/opt/java/openjdk/bin/java --add-opens java.base/java.util=ALL-UNNAMED --illegal-access=permit -Dfile.encoding=UTF-8 -cp "%s" Launcher "%s" "$@" +/opt/java/openjdk/bin/java -Dfile.encoding=UTF-8 -cp "%s" Launcher "%s" "$@" """ %( target_dir, ":".join(classpath+jars), main) with codecs.open(launcher, 'w', 'utf-8') as d: d.write(cmd) diff --git a/tests/src/test/scala/actionContainers/Java11ActionLoopSourceTests.scala b/tests/src/test/scala/actionContainers/Java11ActionLoopSourceTests.scala new file mode 100644 index 00000000..a08baec8 --- /dev/null +++ b/tests/src/test/scala/actionContainers/Java11ActionLoopSourceTests.scala @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package actionContainers + +import common.WskActorSystem +import org.junit.runner.RunWith +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class Java11ActionLoopSourceTests extends JavaActionLoopSourceTests with WskActorSystem { + + override val image = "actionloop-java-v11" + + behavior of "Java 11 actionloop" + +} From 162c53daa762934dcd3e67538a77566da89d529b Mon Sep 17 00:00:00 2001 From: "Seonghyun, Oh" Date: Wed, 26 Feb 2020 16:47:46 +0900 Subject: [PATCH 5/7] Update scala version --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index f5598f6f..da9292be 100644 --- a/settings.gradle +++ b/settings.gradle @@ -29,7 +29,7 @@ gradle.ext.openwhisk = [ ] gradle.ext.scala = [ - version: '2.12.7', + version: '2.12.10', compileFlags: ['-feature', '-unchecked', '-deprecation', '-Xfatal-warnings', '-Ywarn-unused-import'] ] From ba3ca156240b22f64d42405541818b44d3338568 Mon Sep 17 00:00:00 2001 From: "Seonghyun, Oh" Date: Wed, 26 Feb 2020 16:47:56 +0900 Subject: [PATCH 6/7] Update README.md --- README.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 65750d93..fa92f573 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ ## Changelogs - [Java 8 CHANGELOG.md](core/java8/CHANGELOG.md) +- [Java 8 ActionLoop CHANGELOG.md](core/java8actionloop/CHANGELOG.md) +- [Java 11 ActionLoop CHANGELOG.md](core/java11actionloop/CHANGELOG.md) ## Quick Java Action @@ -103,30 +105,36 @@ wsk action invoke --result helloJava --param name World 1. Start Docker Desktop (i.e., Docker daemon) -2. Build the Docker runtime image locally using Gradle: +2. Build the Docker runtime image locally using Gradle for the different runtime versions: ``` ./gradlew core:java8:distDocker +./gradlew core:java8actionloop:distDocker +./gradlew core:java11actionloop:distDocker ``` -This will produce the image `whisk/java8action` and push it to the local Docker Desktop registry with the `latest` tag. +This will produce the image `whisk/java8action`, `whisk/actionloop-java-v8`, and `whisk/actionloop-java-v11`, and will push it to the local Docker Desktop registry with the `latest` tag. 3. Verify the image was registered: ``` $ docker images whisk/* -REPOSITORY TAG IMAGE ID CREATED SIZE -whisk/java8action latest 35f90453905a 7 minutes ago 521MB +REPOSITORY TAG IMAGE ID CREATED SIZE +whisk/java8action latest e92776cb3b81 3 hours ago 587MB +whisk/actionloop-java-v8 latest aca22c730f31 3 hours ago 426MB +whisk/actionloop-java-v11 latest 921f4868f087 15 minutes ago 450MB ``` ### Build and Push image to a remote Docker registry -Build the Docker runtime image locally using Gradle supplying the image Prefix and Registry domain (default port): +Build the Docker runtime images locally using Gradle supplying the image Prefix and Registry domain (default port): ``` docker login ./gradlew core:java8:distDocker -PdockerImagePrefix=$prefix-user -PdockerRegistry=docker.io +./gradlew core:java8actionloop:distDocker -PdockerImagePrefix=$prefix-user -PdockerRegistry=docker.io +./gradlew core:java11actionloop:distDocker -PdockerImagePrefix=$prefix-user -PdockerRegistry=docker.io ``` ## Deploying the Java runtime image to OpenWhisk -Deploy OpenWhisk using ansible environment that contains the kind `java:8` +Deploy OpenWhisk using ansible environment that contains the kind `java:8` and `java:11` Assuming you have OpenWhisk already deployed locally and `OPENWHISK_HOME` pointing to root directory of OpenWhisk core repository. Set `ROOTDIR` to the root directory of this repository. From 8654b2be3988d7cc72a04ab4d05b2ec2f126e026 Mon Sep 17 00:00:00 2001 From: "Seonghyun, Oh" Date: Wed, 26 Feb 2020 17:00:39 +0900 Subject: [PATCH 7/7] Add test case for java11 action --- .../Java11ActionLoopSourceTests.scala | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/src/test/scala/actionContainers/Java11ActionLoopSourceTests.scala b/tests/src/test/scala/actionContainers/Java11ActionLoopSourceTests.scala index a08baec8..f70add6e 100644 --- a/tests/src/test/scala/actionContainers/Java11ActionLoopSourceTests.scala +++ b/tests/src/test/scala/actionContainers/Java11ActionLoopSourceTests.scala @@ -20,6 +20,7 @@ package actionContainers import common.WskActorSystem import org.junit.runner.RunWith import org.scalatest.junit.JUnitRunner +import spray.json.{JsArray, JsNumber, JsObject} @RunWith(classOf[JUnitRunner]) class Java11ActionLoopSourceTests extends JavaActionLoopSourceTests with WskActorSystem { @@ -28,4 +29,50 @@ class Java11ActionLoopSourceTests extends JavaActionLoopSourceTests with WskActo behavior of "Java 11 actionloop" + it should s"support local-variable syntax for lambda parameters (JEP 323)" in { + val config = TestConfig( + """ + |package example; + | + |import com.google.gson.JsonArray; + |import com.google.gson.JsonObject; + |import java.util.stream.IntStream; + | + |/** + | * Test JEP 323: Local-Variable Syntax for Lambda Parameters + | * http://openjdk.java.net/jeps/323 + | */ + |public class Java11Jep323Test { + | public static JsonObject main(JsonObject args) throws Exception { + | JsonObject response = new JsonObject(); + | JsonArray list = new JsonArray(); + | + | IntStream.range(1, 5) + | // local-variable syntax + | .filter((var i) -> i % 2 == 0) + | .forEach(list::add); + | + | response.add("list", list); + | return response; + | } + |} + """.stripMargin, + main = "example.Java11Jep323Test") + + val (out, err) = withActionContainer() { c => + val (initCode, _) = c.init(initPayload(config.code, config.main)) + initCode should be(200) + + val response = JsObject("list" -> JsArray(JsNumber(2), JsNumber(4))) + val (runCode, out) = c.run(JsObject.empty) + runCode should be(200) + out should be(Some(response)) + } + + checkStreams(out, err, { + case (o, e) => + o shouldBe empty + e shouldBe empty + }) + } }