Allow multiple inits of java runtime to enable cache priming (#289)
diff --git a/knative-build/runtimes/java/WhiskSim/.classpath b/knative-build/runtimes/java/WhiskSim/.classpath
index f0257c5..71f5fef 100644
--- a/knative-build/runtimes/java/WhiskSim/.classpath
+++ b/knative-build/runtimes/java/WhiskSim/.classpath
@@ -13,7 +13,7 @@
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
diff --git a/knative-build/runtimes/java/core/java8/proxy/compileClassCache.sh b/knative-build/runtimes/java/core/java8/proxy/compileClassCache.sh
index 503adfc..5ecc9b0 100755
--- a/knative-build/runtimes/java/core/java8/proxy/compileClassCache.sh
+++ b/knative-build/runtimes/java/core/java8/proxy/compileClassCache.sh
@@ -40,6 +40,8 @@
JAVA_VERBOSE_OPTIONS="-verbose:class -verbose:sizes"
JAVA_JVM_KILL_DELAY=5s
+export OW_ALLOW_MULTIPLE_INIT=true
+
echo "Creating shared class cache with Proxy and 'base' profile libraries..."
java $JAVA_VERBOSE_OPTIONS $JAVA_STANDARD_OPTIONS $JAVA_EXTENDED_OPTIONS "-jar" "/javaAction/build/libs/javaAction-all.jar" &
HTTP_PID=$!
diff --git a/knative-build/runtimes/java/core/java8/proxy/profiles/base/tests/exec_tests.sh b/knative-build/runtimes/java/core/java8/proxy/profiles/base/tests/exec_tests.sh
new file mode 100755
index 0000000..5db373a
--- /dev/null
+++ b/knative-build/runtimes/java/core/java8/proxy/profiles/base/tests/exec_tests.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# 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.
+#
+set +x
+
+for f in *; do
+ # if the file is a directory
+ if [ -d ${f} ]; then
+ echo "Updating 'code' payload with base64 encoded archive data: ${f}"
+ cd $f
+ sed "s#BASE64_ENCODED_JAR#$(cat hello.jar.base64)#" openwhisk-data-init.json.tmpl > openwhisk-data-init.json
+ sed "s#BASE64_ENCODED_JAR#$(cat hello.jar.base64)#" knative-data-init.json.tmpl > knative-data-init.json
+ sed "s#BASE64_ENCODED_JAR#$(cat hello.jar.base64)#" knative-data-init-run.json.tmpl > native-data-init-run.json
+ sed "s#BASE64_ENCODED_JAR#$(cat hello.jar.base64)#" payload-knative-init.http.tmpl > payload-knative-init.http
+ sed "s#BASE64_ENCODED_JAR#$(cat hello.jar.base64)#" payload-knative-init-run.http.tmpl > payload-knative-init-run.http
+ sed "s#BASE64_ENCODED_JAR#$(cat hello.jar.base64)#" payload-openwhisk-init.http.tmpl > payload-openwhisk-init.http
+ cd ..
+ fi
+done
diff --git a/knative-build/runtimes/java/core/java8/proxy/profiles/base/tests/helloworld/native-data-init-run.json b/knative-build/runtimes/java/core/java8/proxy/profiles/base/tests/helloworld/native-data-init-run.json
new file mode 100644
index 0000000..30282d3
--- /dev/null
+++ b/knative-build/runtimes/java/core/java8/proxy/profiles/base/tests/helloworld/native-data-init-run.json
@@ -0,0 +1,18 @@
+{
+ "init": {
+ "name": "java-helloworld",
+ "main": "Hello",
+ "binary": true,
+ "code" : "UEsDBBQACAgIAA18LU8AAAAAAAAAAAAAAAAJAAQATUVUQS1JTkYv/soAAAMAUEsHCAAAAAACAAAAAAAAAFBLAwQUAAgICAANfC1PAAAAAAAAAAAAAAAAFAAAAE1FVEEtSU5GL01BTklGRVNULk1G803My0xLLS7RDUstKs7Mz7NSMNQz4OVyLkpNLElN0XWqBAlY6BnEGxkYKWi4JudkFhSnKvgXpOZ5WWrycvFyAQBQSwcIMFMkp0EAAABBAAAAUEsDBBQACAgIAA18LU8AAAAAAAAAAAAAAAALAAAASGVsbG8uY2xhc3N9UmtP02AUfg67tHSdzAJD0CHzRjcGVREvjJCIiTFmKMkMiX57t72pxV6WriP6U/wX+mUkkvgD/E3GeN7GaYKDJj2X9znnec572h+/vn0H8ABNAxYqOpZ1XDegYUWZqoEbuKnhlkoYvK3hjgFDhavK2zpqytcVvqahoWGdkN/xQi/ZJWTs2iEh+yzqScJMywvlq2HQkfEb0fH5JBsILyRs261uFDhuFLm+dNxBFDov2bzuHMlu0qxdBBKK7UR0P+yLfsqpYYNgtKNh3JXPPaVhvJC+H20ciWNhYhZzBH2QxCJ0ZcwDhCKQJhzcNXEP9zVsmryJLcLS+ZpM4MZSJl7oEhYUr+MzndNOYj7aG3p+T1HnU90VEw/xiEBVE4+xZeIJtgm5FCOU/nWPuUtnCXmJ78WAMG+3zkLN2juC5crk6UCNdxB7gZd4x3zpzUnFE/f4t4lXWbmwgFBIpcZjzdm1/0X43qLfl2GPsD5xhnP2xY16Eo2pC6LXO4ijvoyTT4TVCUQTqA9RxWX+hdUzBVLfmu08ZxX2xD5XPwF95YBQZptPD2cxjQVc+VP6EVlk2O9aU626lRkh+xnFNMiNkG+dQnt7An2/YU2fwuCwYJkjFNf4tS6xGWFmhFLjSzqC0lhiFTBzhjOTdcqstIxF7DCyyIgO+omyxhnhatp17TdQSwcIxFn0If4BAACRAwAAUEsBAhQAFAAICAgADXwtTwAAAAACAAAAAAAAAAkABAAAAAAAAAAAAAAAAAAAAE1FVEEtSU5GL/7KAABQSwECFAAUAAgICAANfC1PMFMkp0EAAABBAAAAFAAAAAAAAAAAAAAAAAA9AAAATUVUQS1JTkYvTUFOSUZFU1QuTUZQSwECFAAUAAgICAANfC1PxFn0If4BAACRAwAACwAAAAAAAAAAAAAAAADAAAAASGVsbG8uY2xhc3NQSwUGAAAAAAMAAwC2AAAA9wIAAAAA"
+ },
+ "activation": {
+ "namespace": "default",
+ "action_name": "java-helloworld",
+ "api_host": "",
+ "api_key": "",
+ "activation_id": "",
+ "deadline": "4102498800000"
+ },
+ "value": {
+ }
+}
diff --git a/knative-build/runtimes/java/core/java8/proxy/profiles/base/tests/helloworldwithparams/native-data-init-run.json b/knative-build/runtimes/java/core/java8/proxy/profiles/base/tests/helloworldwithparams/native-data-init-run.json
new file mode 100644
index 0000000..752b692
--- /dev/null
+++ b/knative-build/runtimes/java/core/java8/proxy/profiles/base/tests/helloworldwithparams/native-data-init-run.json
@@ -0,0 +1,20 @@
+{
+ "init": {
+ "name" : "java-helloworld-with-params",
+ "main" : "Hello",
+ "binary": true,
+ "code" : "UEsDBBQACAgIAA18LU8AAAAAAAAAAAAAAAAJAAQATUVUQS1JTkYv/soAAAMAUEsHCAAAAAACAAAAAAAAAFBLAwQUAAgICAANfC1PAAAAAAAAAAAAAAAAFAAAAE1FVEEtSU5GL01BTklGRVNULk1G803My0xLLS7RDUstKs7Mz7NSMNQz4OVyLkpNLElN0XWqBAlY6BnEGxkYKWi4JudkFhSnKvgXpOZ5WWrycvFyAQBQSwcIMFMkp0EAAABBAAAAUEsDBBQACAgIAA18LU8AAAAAAAAAAAAAAAALAAAASGVsbG8uY2xhc3N9Uu1uEkEUPcPXLssiuG2t1KJQtQUKRW39KrW1mhhjoDbBNFHjjwHG7db9IMtS46P4BP7VPzSxiQ/gO2m8s4omlXZn986dO2fPOXMz339+/QZgDdsaZlBQUVSxoOKqhiSuyXBdwyKWVJQUlOWaIBUFyxp0mVblXFOxoqIu0xsSclPBLQWrDIkNy7WCTYZoqbzHEHvs9QRDpmm5YmfodIT/gndsqsQcbrkM66Vm13PqpueZtqibA8+tP6PwvHMgukGjfNYmQ7od8O67Fu+HnArWGLS2N/S74oklNbSnwra9lQN+yHVcwCyDOgh87prCZ0gOPEe83xe+NONyR+i4jTs67uKegvs61kEK8b7NuwSYO90HkZq+EIHlmgyzUqtuk0S9HfhUejS07J6US4ReCjo28IBWhbe+5xQYWFHHJho6tvCQ5EIQQ/YfzVgke5KZOrzPBwwzpebJrUb5FYNhimB7IH3u+pZjBdYhnWN1Enhik//+RF3InwlgSIVSY1vTpfL/InRk3u8Lt8dQm+jhlMbRj2rgjalTvNfb9b2+8IMPDEsTiCZQ76GIabro8omAyYtA8SKt8jQzmuOVI7AvlDDkKCbC4hTd6jlc+gP9hBgN4I0RaRrRVsWIjRD/iHSYJEZQmhVDHZfU36XWMZIvj6Dt1IzUMXRK08a5ETLL9BlZClWZnKcwgjHCVO1z6FBaWCRxkOcouU2Q3wyZyNFYIEtVMrWGebwm//OE0hH5gZyCPL0G2b0cslz5BVBLBwistPL2OQIAAOYDAABQSwECFAAUAAgICAANfC1PAAAAAAIAAAAAAAAACQAEAAAAAAAAAAAAAAAAAAAATUVUQS1JTkYv/soAAFBLAQIUABQACAgIAA18LU8wUySnQQAAAEEAAAAUAAAAAAAAAAAAAAAAAD0AAABNRVRBLUlORi9NQU5JRkVTVC5NRlBLAQIUABQACAgIAA18LU+stPL2OQIAAOYDAAALAAAAAAAAAAAAAAAAAMAAAABIZWxsby5jbGFzc1BLBQYAAAAAAwADALYAAAAyAwAAAAA="
+ },
+ "activation": {
+ "namespace": "default",
+ "action_name": "java-helloworld-with-params",
+ "api_host": "",
+ "api_key": "",
+ "activation_id": "",
+ "deadline": "4102498800000"
+ },
+ "value": {
+ "name" : "Joe",
+ "place" : "TX"
+ }
+}
diff --git a/knative-build/runtimes/java/core/java8/proxy/profiles/base/tests/helloworldwithparamsfromenv/native-data-init-run.json b/knative-build/runtimes/java/core/java8/proxy/profiles/base/tests/helloworldwithparamsfromenv/native-data-init-run.json
new file mode 100644
index 0000000..ca94e97
--- /dev/null
+++ b/knative-build/runtimes/java/core/java8/proxy/profiles/base/tests/helloworldwithparamsfromenv/native-data-init-run.json
@@ -0,0 +1,20 @@
+{
+ "init": {
+ "name" : "java-helloworld-with-params-from-env",
+ "main" : "Hello",
+ "binary": true,
+ "code" : "UEsDBBQACAgIAA58LU8AAAAAAAAAAAAAAAAJAAQATUVUQS1JTkYv/soAAAMAUEsHCAAAAAACAAAAAAAAAFBLAwQUAAgICAAOfC1PAAAAAAAAAAAAAAAAFAAAAE1FVEEtSU5GL01BTklGRVNULk1G803My0xLLS7RDUstKs7Mz7NSMNQz4OVyLkpNLElN0XWqBAlY6BnEGxkYKWi4JudkFhSnKvgXpOZ5WWrycvFyAQBQSwcIMFMkp0EAAABBAAAAUEsDBBQACAgIAA58LU8AAAAAAAAAAAAAAAALAAAASGVsbG8uY2xhc3ONUttu00AUnNMmsesaWtzQGxQSLq3TNjW3Jwo8gIQQCgUpqBKPm/hgubJ3I9ep1L+ClxRRiQ/goxBnQ7kIAsKWZ2dnz5lZ2/v5y8dPAO7hrocLWHVxycNlrLm44uCqh6qVGg6aHlxLr9nxuosbLm5aum5LNhyEDlqE2oNUp+UjwnTY2idUnpiYCXOdVPPeMO9x8Vr1MlEquUo14X7Y6Zs8SoxJMo6SQ6Oj5wIvewfcL3db/1okeF0zLPr8NLWG3jPOMrNzoI6UjwALEqFVzg42fWxhm1AdZKovhat/9yS4ScFcpjohLFmrKFM6ibplIdLjYZrFXMg7jqMaPtrYkVnjbWHyBoGaPiLc8nEbdyRuXESY/2nzPeQXqXt8WHIuJgmXrI8I62Hn91z5DH9I0qEGA9Yxof1fHWebl0a3NN8kQj2caD2r4vhVYQZclMeEjQn+ExL30cS8nB97TYHsLxCsy2xNRpKxunkCei+EcFGwNhYXMINFLJ2VMipyAw+DqQ+Y7gQVwRenqL45QW2vHTincIXOBN4Is1vyBL7AtiXnBEY4P8Jc+92PiEU5mRBWE+ZLSB3L2MWKrC6Pd7nyFVBLBwhnUa6bvgEAAPsCAABQSwECFAAUAAgICAAOfC1PAAAAAAIAAAAAAAAACQAEAAAAAAAAAAAAAAAAAAAATUVUQS1JTkYv/soAAFBLAQIUABQACAgIAA58LU8wUySnQQAAAEEAAAAUAAAAAAAAAAAAAAAAAD0AAABNRVRBLUlORi9NQU5JRkVTVC5NRlBLAQIUABQACAgIAA58LU9nUa6bvgEAAPsCAAALAAAAAAAAAAAAAAAAAMAAAABIZWxsby5jbGFzc1BLBQYAAAAAAwADALYAAAC3AgAAAAA="
+ },
+ "activation": {
+ "namespace": "default",
+ "action_name": "java-helloworld-with-params-from-env",
+ "api_host": "",
+ "api_key": "",
+ "activation_id": "",
+ "deadline": "4102498800000"
+ },
+ "value": {
+ "name" : "Jess",
+ "place" : "OK"
+ }
+}
diff --git a/knative-build/runtimes/java/core/java8/proxy/profiles/base/tests/test-list-all.txt b/knative-build/runtimes/java/core/java8/proxy/profiles/base/tests/test-list-all.txt
deleted file mode 100644
index ea8521b..0000000
--- a/knative-build/runtimes/java/core/java8/proxy/profiles/base/tests/test-list-all.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-helloworld
-helloworldwithparams
-helloworldwithparamsfromenv
diff --git a/knative-build/runtimes/java/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/Debug.java b/knative-build/runtimes/java/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/Debug.java
index e32afc9..287107a 100644
--- a/knative-build/runtimes/java/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/Debug.java
+++ b/knative-build/runtimes/java/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/Debug.java
@@ -16,7 +16,7 @@
*/
package org.apache.openwhisk.runtime.java.action;
-import java.time.*;
+import java.util.Map;
public class Debug {
@@ -50,8 +50,6 @@
private static String FQ_METHOD = "";
private static String METHOD = "";
private static long currentTime = 0;
- //private static long startTime = 0;
- //private static long stopTime = 0;
private static long updateContext(){
currentTime = System.nanoTime();
@@ -99,11 +97,20 @@
public static long end() { return end("",-1);}
public static long end(long startTime) { return end("", startTime);}
- public static long end(String msg, long startTime){
+ public static long end(String msg, long startTime) {
currentTime = updateContext();
String formattedMsg = formatMessage(functionEndMarker, msg, startTime);
System.out.println(formattedMsg);
return currentTime;
}
+ public static void printEnv() {
+ Map<String, String> envVars = System.getenv();
+ long ts = System.nanoTime();
+ System.out.printf("%sEnvironment Variables (%d):%s\n", prefixFGColor, ts, bodyFGColor);
+ for (String key : envVars.keySet()) {
+ System.out.printf(">> %s=\"%s\"%n", key, envVars.get(key));
+ }
+ }
+
}
diff --git a/knative-build/runtimes/java/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/JarLoader.java b/knative-build/runtimes/java/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/JarLoader.java
index 03f7c90..21f4afb 100644
--- a/knative-build/runtimes/java/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/JarLoader.java
+++ b/knative-build/runtimes/java/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/JarLoader.java
@@ -35,8 +35,8 @@
import com.google.gson.JsonObject;
public class JarLoader extends URLClassLoader {
- private final Class<?> mainClass;
- private final Method mainMethod;
+ private Class<?> mainClass = null;
+ private Method mainMethod = null;
public static Path saveBase64EncodedFile(InputStream encoded) throws Exception {
Base64.Decoder decoder = Base64.getDecoder();
@@ -55,7 +55,10 @@
public JarLoader(Path jarPath, String entrypoint)
throws MalformedURLException, ClassNotFoundException, NoSuchMethodException, SecurityException {
super(new URL[] { jarPath.toUri().toURL() });
+ loadMainClassAndMethod(entrypoint);
+ }
+ public void loadMainClassAndMethod(String entrypoint) throws NoSuchMethodException, ClassNotFoundException {
final String[] splittedEntrypoint = entrypoint.split("#");
final String entrypointClassName = splittedEntrypoint[0];
final String entrypointMethodName = splittedEntrypoint.length > 1 ? splittedEntrypoint[1] : "main";
@@ -69,7 +72,20 @@
throw new NoSuchMethodException("main");
}
- this.mainMethod = m;
+ mainMethod = m;
+ }
+
+ public void addJAR(Path jarPath) throws MalformedURLException {
+ if(jarPath!=null){
+ try{
+ this.addURL(jarPath.toUri().toURL());
+ } catch (MalformedURLException e){
+ System.err.format("Invalid JAR file path. [%s]", jarPath);
+ throw e;
+ }
+ } else {
+ System.err.format("Invalid JAR file path. [%s]", jarPath);
+ }
}
public JsonObject invokeMain(JsonObject arg, Map<String, String> env) throws Exception {
diff --git a/knative-build/runtimes/java/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/Proxy.java b/knative-build/runtimes/java/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/Proxy.java
index cad2845..91b5c94 100644
--- a/knative-build/runtimes/java/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/Proxy.java
+++ b/knative-build/runtimes/java/core/java8/proxy/src/main/java/org/apache/openwhisk/runtime/java/action/Proxy.java
@@ -41,16 +41,35 @@
public class Proxy {
private HttpServer server;
private JarLoader loader = null;
+ private boolean allowMultipleInits = false;
public Proxy(int port) throws IOException {
long startTime = Debug.start();
+ Debug.printEnv();
this.server = HttpServer.create(new InetSocketAddress(port), -1);
this.server.createContext("/init", new InitHandler());
this.server.createContext("/run", new RunHandler());
this.server.setExecutor(null); // creates a default executor
+
+ // Default is false; used primarily for establishing boot shared class cache
+ checkMultipleInitEnabled();
+
Debug.end(startTime);
}
+ private void checkMultipleInitEnabled() {
+ String strMultipleInit = System.getenv("OW_ALLOW_MULTIPLE_INIT");
+ System.out.printf("OW_ALLOW_MULTIPLE_INIT=%s\n", strMultipleInit);
+
+ // Determine if we allow multiple "init" calls (i.e., Java container reuse); default:false
+ if(strMultipleInit!=null)
+ this.allowMultipleInits = Boolean.parseBoolean(strMultipleInit);
+
+ if(this.allowMultipleInits){
+ System.out.println("Multiple '/init' allowed.");
+ }
+ }
+
public void start() {
server.start();
}
@@ -79,7 +98,8 @@
private class InitHandler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
long startTime = Debug.start();
- if (loader != null) {
+
+ if (loader != null && !allowMultipleInits) {
String errorMessage = "Cannot initialize the action more than once.";
System.err.println(errorMessage);
Proxy.writeError(t, errorMessage);
@@ -108,7 +128,12 @@
// Start up the custom classloader. This also checks that the
// main method exists.
- loader = new JarLoader(jarPath, mainClass);
+ if( loader == null)
+ loader = new JarLoader(jarPath, mainClass);
+ else {
+ loader.addJAR(jarPath);
+ loader.loadMainClassAndMethod(mainClass);
+ }
Proxy.writeResponse(t, 200, "OK");
return;
@@ -151,7 +176,8 @@
for(Map.Entry<String, JsonElement> entry : entrySet){
try {
if(!entry.getKey().equalsIgnoreCase("value"))
- env.put(String.format("__OW_%s", entry.getKey().toUpperCase()), entry.getValue().getAsString());
+ env.put(String.format("__OW_%s", entry.getKey().toUpperCase()),
+ entry.getValue().getAsString());
} catch (Exception e) {}
}
@@ -169,12 +195,13 @@
Proxy.writeResponse(t, 200, output.toString());
return;
} catch (InvocationTargetException ite) {
- // These are exceptions from the action, wrapped in ite because
- // of reflection
+ // When you invoke a method using reflection (as we do for the Action function)
+ // and it throws an exception, you must check for it using InvocationTargetException as follows:
Throwable underlying = ite.getCause();
underlying.printStackTrace(System.err);
Proxy.writeError(t,
- "An error has occured while invoking the action (see logs for details): " + underlying);
+ "An error has occurred while invoking the action (see logs for details): "
+ + underlying);
} catch (Exception e) {
e.printStackTrace(System.err);
Proxy.writeError(t, "An error has occurred (see logs for details): " + e);