Building OpenWhisk actions with Java and Gradle
In this post, using Gradle as a build tool, I will demonstrate how to build Apache OpenWhisk actions in Java.
(If you are in a hurry, all the code discussed in this post is available on GitHub.)
There are scenarios, however, in which Java is still the most appropriate choice–for example, if you are breaking down an existing application into small, serverless components and want to reuse code to the extent possible; or you simply want to leverage libraries from the very rich Java ecosystem.
For all OpenWhisk actions, regardless of the implementation language, OpenWhisk accepts and returns JSON objects.
The entry point of a Java-defined action has a signature that is almost familiar:
public static JsonObject main(JsonObject args);
The class JsonObject in the signature refers to the Google GSON library. In the absence of an officially sanctioned JSON library in the Java standard library, OpenWhisk relies on one of the most popular. Note that you can also declare the method to throw any exception: Java exceptions are caught by the runtime and translate into action invocation errors.
The full source code of a “hello world” Java action is given below:
One major difference between Java and scripting languages is that the actions need to be compiled into a .jar file before they are uploaded to OpenWhisk. In our examples, we use Gradle, as it is relatively lightweight and simple to configure.
We set up a project with following filesystem layout:
The only important lines relate to the resolution of dependencies: because our Java action relies on the GSON library, it needs to be included in the classpath. Gradle automatically fetches the library from Maven Central (or other software repositories you may have configured locally).
We can build the project with a single command, which we run from within the hello directory (where build.gradleresides):
This compiles our action into a .class file, and packages it as a .jar file. We can look at the contents of this archive as follows:
Observe that the archive contains only our class, and not the GSON dependency. This is not an issue, as the OpenWhisk runtime provides this dependency.
We can now deploy our action:
The action creation process is similar to other runtimes, with the notable difference that one must provide the name of the main class with the –main flag. We can finally confirm that our Java action is working as expected:
On the shoulders of giants
Our previous example demonstrates the very first steps in running Java actions. As mentioned above, however, part of the appeal of Java is the rich ecosystem of libraries. To demonstrate the use of external dependencies, we build an OpenWhisk action to generate QR codes. Specifically, the action accepts text as an argument, and returns a base64-encoded PNG image or a QR code encoding the text. With the ZXinglibrary, the action code is relatively short:
If we build the code above as we did the first example, our .jar file will contain a single class Generate.class. The action will not run on OpenWhisk, since it will be missing the com.google.zxing dependencies. One way to address this issue is to create a so-called “fat jar” (or “über-jar”): a single archive containing, in addition to the action code, all classes from all dependencies.
We can configure Gradle to build such a file. Below is the build.gradle for the QR code action:
(The code above was modeled after this blog post.)
The Gradle configuration file does two important things:
It declares a provided configuration in addition to the default compile and runtime configurations. We use providedfor dependencies that are required at build time but that should not be included in the final fat jar. In our case, this concerns the GSON library which, as we know, is already provided by the OpenWhisk runtime.
It overrides the jar task to include all classes found in all dependencies, except those marked as provided.
With this new configuration in place, we can build our .jar file:
If we now inspect the contents of build/libs/qr-1.0.jar, we see over 600 included classes (and no GSON).
We can finally deploy our serverless QR generator: