Skip to content

Add OpenJDK 11 action loop runtime #82

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
24 changes: 24 additions & 0 deletions core/java11actionloop/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!--
#
# 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.
#
-->

# 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
66 changes: 66 additions & 0 deletions core/java11actionloop/Dockerfile
Original file line number Diff line number Diff line change
@@ -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
72 changes: 72 additions & 0 deletions core/java11actionloop/Makefile
Original file line number Diff line number Diff line change
@@ -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"}'
129 changes: 129 additions & 0 deletions core/java11actionloop/bin/compile
Original file line number Diff line number Diff line change
@@ -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 <main>.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 --add-opens java.base/java.util=ALL-UNNAMED --illegal-access=permit -Dfile.encoding=UTF-8 -cp "%s" Launcher "%s" "$@"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

From java 9 version, it outputs below warning messages when accessing encapsulated field by reflection.

WARNING: An illegal reflective access operation has occurred
WARNING: Please consider reporting this to the maintainers of Main
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

Source code using reflection

  • core/java11actionloop/lib/src/Launcher.java
if ("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
    Field field = cl.getDeclaredField("m");
    field.setAccessible(true); // <- here

So, I added --add-opens and --illegal-access VM options to access java.util.Collections$UnmodifiableMap.

--add-opens java.base/java.util=ALL-UNNAMED 
--illegal-access=permit

https://stackoverflow.com/a/46230678

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed that is an horrible and ugly hack that unfortunately is the only way to set environment variables with jdk8. I was kinda of "forced" to add that in actionloop because I tried to make the actionloop 100% compatible but I really would like to change the API, and not use environment variables but system properties to pass environment variables.

""" %( 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: <main-class> <source-dir> <target-dir>\n")
sys.exit(1)
logging.basicConfig(filename="/var/log/compile.log")
assemble(sys.argv)
19 changes: 19 additions & 0 deletions core/java11actionloop/build.gradle
Original file line number Diff line number Diff line change
@@ -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'
Loading