This is an automated email from the ASF dual-hosted git repository.

dgrove pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openwhisk-runtime-java.git


The following commit(s) were added to refs/heads/master by this push:
     new e366197  Support array result include sequence action (#140)
e366197 is described below

commit e366197d4c13c409602d3dbea6c9e45190b0b644
Author: ningyougang <[email protected]>
AuthorDate: Mon Aug 15 22:54:46 2022 +0800

    Support array result include sequence action (#140)
    
    * Support array result
    * Use go1.18 to build actionloop
    * Use actionloop 1.20.0
---
 README.md                                          | 30 ++++++++++++
 .../openwhisk/runtime/java/action/JarLoader.java   | 32 ++++++-------
 .../openwhisk/runtime/java/action/Proxy.java       | 39 ++++++++++++++--
 core/java8actionloop/Dockerfile                    |  8 ++--
 core/java8actionloop/lib/src/Launcher.java         | 52 ++++++++++++++++-----
 .../JavaActionContainerTests.scala                 | 54 ++++++++++++++++++++--
 6 files changed, 178 insertions(+), 37 deletions(-)

diff --git a/README.md b/README.md
index 58f46c9..c7ce6ca 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,36 @@ If needed you can also customize the method name of your 
Java action. This
 can be done by specifying the Java fully-qualified method name of your action,
 e.q., `--main com.example.MyMain#methodName`
 
+Not only support return JsonObject but also support return JsonArray, the main 
function would be:
+
+```java
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+
+public class HelloArray {
+    public static JsonArray main(JsonObject args) {
+        JsonArray jsonArray = new JsonArray();
+        jsonArray.add("a");
+        jsonArray.add("b");
+        return jsonArray;
+    }
+}
+```
+
+And support array result for sequence action as well, the first action's array 
result can be used as next action's input parameter.
+
+So the function would be:
+
+```java
+import com.google.gson.JsonArray;
+
+public class Sort {
+    public static JsonArray main(JsonArray args) {
+        return args;
+    }
+}
+```
+
 ### Create the Java Action
 To use as a docker action:
 ```
diff --git 
a/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/JarLoader.java
 
b/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/JarLoader.java
index e17d0a9..fa45197 100644
--- 
a/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/JarLoader.java
+++ 
b/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/JarLoader.java
@@ -32,11 +32,13 @@ import java.util.Base64;
 import java.util.Collections;
 import java.util.Map;
 
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 
 public class JarLoader extends URLClassLoader {
-    private final Class<?> mainClass;
-    private final Method mainMethod;
+    public final Class<?> mainClass;
+    public final String entrypointMethodName;
 
     public static Path saveBase64EncodedFile(InputStream encoded) throws 
Exception {
         Base64.Decoder decoder = Base64.getDecoder();
@@ -58,26 +60,24 @@ public class JarLoader extends URLClassLoader {
 
         final String[] splittedEntrypoint = entrypoint.split("#");
         final String entrypointClassName = splittedEntrypoint[0];
-        final String entrypointMethodName = splittedEntrypoint.length > 1 ? 
splittedEntrypoint[1] : "main";
 
         this.mainClass = loadClass(entrypointClassName);
-
-        Method m = mainClass.getMethod(entrypointMethodName, 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("main");
+        this.entrypointMethodName = splittedEntrypoint.length > 1 ? 
splittedEntrypoint[1] : "main";
+        Method[] methods = mainClass.getDeclaredMethods();
+        Boolean existMain = false;
+        for(Method method: methods) {
+            if (method.getName().equals(this.entrypointMethodName)) {
+                existMain = true;
+                break;
+            }
+        }
+        if (!existMain) {
+            throw new NoSuchMethodException(this.entrypointMethodName);
         }
-        this.mainMethod = m;
-    }
-
-    public JsonObject invokeMain(JsonObject arg, Map<String, String> env) 
throws Exception {
-        augmentEnv(env);
-        return (JsonObject) mainMethod.invoke(null, arg);
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
-    private static void augmentEnv(Map<String, String> newEnv) {
+    public static void augmentEnv(Map<String, String> newEnv) {
         try {
             for (Class cl : Collections.class.getDeclaredClasses()) {
                 if 
("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
diff --git 
a/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/Proxy.java
 
b/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/Proxy.java
index 4c0dc21..b8519ec 100644
--- 
a/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/Proxy.java
+++ 
b/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/Proxy.java
@@ -24,6 +24,8 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.net.InetSocketAddress;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
@@ -31,6 +33,7 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 
+import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
@@ -139,7 +142,17 @@ public class Proxy {
                 InputStream is = t.getRequestBody();
                 JsonParser parser = new JsonParser();
                 JsonObject body = parser.parse(new BufferedReader(new 
InputStreamReader(is, StandardCharsets.UTF_8))).getAsJsonObject();
-                JsonObject inputObject = body.getAsJsonObject("value");
+                JsonParser json = new JsonParser();
+                JsonObject payloadForJsonObject = 
json.parse("{}").getAsJsonObject();
+                JsonArray payloadForJsonArray = 
json.parse("[]").getAsJsonArray();
+                Boolean isJsonObjectParam = true;
+                JsonElement inputJsonElement = body.get("value");
+                if (inputJsonElement.isJsonObject()) {
+                    payloadForJsonObject = inputJsonElement.getAsJsonObject();
+                } else {
+                    payloadForJsonArray = inputJsonElement.getAsJsonArray();
+                    isJsonObjectParam = false;
+                }
 
                 HashMap<String, String> env = new HashMap<String, String>();
                 Set<Map.Entry<String, JsonElement>> entrySet = body.entrySet();
@@ -153,8 +166,28 @@ public class Proxy {
                 Thread.currentThread().setContextClassLoader(loader);
                 System.setSecurityManager(new WhiskSecurityManager());
 
-                // User code starts running here.
-                JsonObject output = loader.invokeMain(inputObject, env);
+                Method mainMethod = null;
+                String mainMethodName = loader.entrypointMethodName;
+                if (isJsonObjectParam) {
+                    mainMethod = loader.mainClass.getMethod(mainMethodName, 
new Class[] { JsonObject.class });
+                } else {
+                    mainMethod = loader.mainClass.getMethod(mainMethodName, 
new Class[] { JsonArray.class });
+                }
+                mainMethod.setAccessible(true);
+                int modifiers = mainMethod.getModifiers();
+                if ((mainMethod.getReturnType() != JsonObject.class && 
mainMethod.getReturnType() != JsonArray.class) || !Modifier.isStatic(modifiers) 
|| !Modifier.isPublic(modifiers)) {
+                    throw new NoSuchMethodException(mainMethodName);
+                }
+
+                // User code starts running here. the return object supports 
JsonObject and JsonArray both.
+                Object output;
+                if (isJsonObjectParam) {
+                    loader.augmentEnv(env);
+                    output = mainMethod.invoke(null, payloadForJsonObject);
+                } else {
+                    loader.augmentEnv(env);
+                    output = mainMethod.invoke(null, payloadForJsonArray);
+                }
                 // User code finished running here.
 
                 if (output == null) {
diff --git a/core/java8actionloop/Dockerfile b/core/java8actionloop/Dockerfile
index 510393d..e5931a4 100644
--- a/core/java8actionloop/Dockerfile
+++ b/core/java8actionloop/Dockerfile
@@ -16,7 +16,7 @@
 #
 
 # build go proxy from source
-FROM golang:1.16 AS builder_source
+FROM golang:1.18 AS builder_source
 ARG GO_PROXY_GITHUB_USER=apache
 ARG GO_PROXY_GITHUB_BRANCH=master
 RUN git clone --branch ${GO_PROXY_GITHUB_BRANCH} \
@@ -25,13 +25,13 @@ RUN git clone --branch ${GO_PROXY_GITHUB_BRANCH} \
    mv proxy /bin/proxy
 
 # or build it from a release
-FROM golang:1.16 AS builder_release
-ARG [email protected]
+FROM golang:1.18 AS builder_release
+ARG [email protected]
 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
+  && GO111MODULE=on CGO_ENABLED=0 go build -o /bin/proxy
 
 # Use AdoptOpenJDK's JDK8, OpenJ9, ubuntu
 FROM ibm-semeru-runtimes:open-8u332-b09-jdk-focal
diff --git a/core/java8actionloop/lib/src/Launcher.java 
b/core/java8actionloop/lib/src/Launcher.java
index ef571e9..f0659d2 100644
--- a/core/java8actionloop/lib/src/Launcher.java
+++ b/core/java8actionloop/lib/src/Launcher.java
@@ -59,18 +59,22 @@ class Launcher {
         }
 
         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)) {
+        Method[] methods = mainClass.getDeclaredMethods();
+        Boolean existMain = false;
+        for(Method method: methods) {
+            if (method.getName().equals(mainMethodName)) {
+                existMain = true;
+                break;
+            }
+        }
+        if (!existMain) {
             throw new NoSuchMethodException(mainMethodName);
         }
-        mainMethod = m;
     }
 
-    private static JsonObject invokeMain(JsonObject arg, Map<String, String> 
env) throws Exception {
+    private static Object invokeMain(JsonElement arg, Map<String, String> env) 
throws Exception {
         augmentEnv(env);
-        return (JsonObject) mainMethod.invoke(null, arg);
+        return mainMethod.invoke(null, arg);
     }
 
     private static SecurityManager defaultSecurityManager = null;
@@ -119,7 +123,9 @@ class Launcher {
                 new OutputStreamWriter(
                         new FileOutputStream("/dev/fd/3"), "UTF-8"));
         JsonParser json = new JsonParser();
-        JsonObject empty = json.parse("{}").getAsJsonObject();
+        JsonObject emptyForJsonObject = json.parse("{}").getAsJsonObject();
+        JsonArray emptyForJsonArray = json.parse("[]").getAsJsonArray();
+        Boolean isJsonObjectParam = true;
         String input = "";
         while (true) {
             try {
@@ -127,14 +133,19 @@ class Launcher {
                 if (input == null)
                     break;
                 JsonElement element = json.parse(input);
-                JsonObject payload = empty.deepCopy();
+                JsonObject payloadForJsonObject = 
emptyForJsonObject.deepCopy();
+                JsonArray payloadForJsonArray = emptyForJsonArray.deepCopy();
                 HashMap<String, String> env = new HashMap<String, String>();
                 if (element.isJsonObject()) {
                     // collect payload and environment
                     for (Map.Entry<String, JsonElement> entry : 
element.getAsJsonObject().entrySet()) {
                         if (entry.getKey().equals("value")) {
                             if (entry.getValue().isJsonObject())
-                                payload = entry.getValue().getAsJsonObject();
+                                payloadForJsonObject = 
entry.getValue().getAsJsonObject();
+                            else {
+                                payloadForJsonArray = 
entry.getValue().getAsJsonArray();
+                                isJsonObjectParam = false;
+                            }
                         } else {
                             env.put(String.format("__OW_%s", 
entry.getKey().toUpperCase()),
                                     entry.getValue().getAsString());
@@ -142,7 +153,26 @@ class Launcher {
                     }
                     augmentEnv(env);
                 }
-                JsonElement response = invokeMain(payload, env);
+
+                Method m = null;
+                if (isJsonObjectParam) {
+                    m = mainClass.getMethod(mainMethodName, new Class[] { 
JsonObject.class });
+                } else {
+                    m = mainClass.getMethod(mainMethodName, new Class[] { 
JsonArray.class });
+                }
+                m.setAccessible(true);
+                int modifiers = m.getModifiers();
+                if ((m.getReturnType() != JsonObject.class && 
m.getReturnType() != JsonArray.class) || !Modifier.isStatic(modifiers) || 
!Modifier.isPublic(modifiers)) {
+                    throw new NoSuchMethodException(mainMethodName);
+                }
+                mainMethod = m;
+
+                Object response;
+                if (isJsonObjectParam) {
+                    response = invokeMain(payloadForJsonObject, env);
+                } else {
+                    response = invokeMain(payloadForJsonArray, env);
+                }
                 out.println(response.toString());
             } catch(NullPointerException npe) {
                 System.out.println("the action returned null");
diff --git 
a/tests/src/test/scala/actionContainers/JavaActionContainerTests.scala 
b/tests/src/test/scala/actionContainers/JavaActionContainerTests.scala
index 357885e..c09de29 100644
--- a/tests/src/test/scala/actionContainers/JavaActionContainerTests.scala
+++ b/tests/src/test/scala/actionContainers/JavaActionContainerTests.scala
@@ -157,9 +157,7 @@ class JavaActionContainerTests extends 
BasicActionRunnerTests with WskActorSyste
 
         val expected = m match {
           case c if c == "x" || c == "!" => s"$errPrefix 
java.lang.ClassNotFoundException: example.HelloWhisk$c"
-          case "#bogus" =>
-            s"$errPrefix java.lang.NoSuchMethodException: 
example.HelloWhisk.bogus(com.google.gson.JsonObject)"
-          case _ => s"$errPrefix java.lang.NoSuchMethodException: 
example.HelloWhisk.main(com.google.gson.JsonObject)"
+          case _                         => s"$errPrefix 
java.lang.NoSuchMethodException"
         }
 
         val error = out.get.fields.get("error").get.toString()
@@ -301,6 +299,56 @@ class JavaActionContainerTests extends 
BasicActionRunnerTests with WskActorSyste
     })
   }
 
+  it should "support return array result" in {
+    val (out, err) = withActionContainer() { c =>
+      val jar = JarBuilder.mkBase64Jar(
+        Seq("", "HelloArrayWhisk.java") ->
+          """
+            | import com.google.gson.JsonArray;
+            | import com.google.gson.JsonObject;
+            |
+            | public class HelloArrayWhisk {
+            |     public static JsonArray main(JsonObject args) throws 
Exception {
+            |         JsonArray jsonArray = new JsonArray();
+            |         jsonArray.add("a");
+            |         jsonArray.add("b");
+            |         return jsonArray;
+            |     }
+            | }
+          """.stripMargin.trim)
+
+      val (initCode, _) = c.init(initPayload(jar, "HelloArrayWhisk"))
+      initCode should be(200)
+
+      val (runCode, runRes) = c.runForJsArray(runPayload(JsObject()))
+      runCode should be(200)
+      runRes shouldBe Some(JsArray(JsString("a"), JsString("b")))
+    }
+  }
+
+  it should "support array as input param" in {
+    val (out, err) = withActionContainer() { c =>
+      val jar = JarBuilder.mkBase64Jar(
+        Seq("", "HelloArrayWhisk.java") ->
+          """
+            | import com.google.gson.JsonArray;
+            |
+            | public class HelloArrayWhisk {
+            |     public static JsonArray main(JsonArray args) throws 
Exception {
+            |         return args;
+            |     }
+            | }
+          """.stripMargin.trim)
+
+      val (initCode, _) = c.init(initPayload(jar, "HelloArrayWhisk"))
+      initCode should be(200)
+
+      val (runCode, runRes) = 
c.runForJsArray(runPayload(JsArray(JsString("a"), JsString("b"))))
+      runCode should be(200)
+      runRes shouldBe Some(JsArray(JsString("a"), JsString("b")))
+    }
+  }
+
   it should "survive System.exit" in {
     val (out, err) = withActionContainer() { c =>
       val jar = JarBuilder.mkBase64Jar(

Reply via email to