Writing Serverless Functions for JVM with Fission.io

Introduction

The Java Virtual Machine (JVM) is one of the most popular application frameworks, particularly when it comes to enterprise software development - due to the maturity of JVM, the breadth of integrated developer tools and the vibrant community, and the extension of JVM to additional languages.

The historic data from TIOBE index also shows how popular JVM and Java have been through the years. In the last decade or so Scala and data-related technologies have made great progress using JVM as the base framework. Most recently Kotlin has seen great progress and also got blessings of Google as an official language for Android development.

All of this make Java and JVM great candidates for Serverless Functions, and for a Function-as-a-Service (FaaS) platform such as Fission.

This tutorial will introduce the JVM environment in Fission and walk through a simple demo.

If you want to follow along the tutorial, it will be a good idea to install Fission on a Kubernetes cluster of your choice.

JVM in Fission

The new JVM environment support allows you to use Java functions in Fission.

This tutorial explains how Java functions work in Fission and guides you through building a simple “Hello World” function with the JVM environment, so you can see the Java support in action. This example can also be found in our examples directory on GitHub.

JVM Environment: Insider View

The JVM environment in Fission is based on Spring boot and Spring web frameworks.

Spring boot and Spring web is already loaded in JVM. If you are using this dependency, you can mark it at provided scope. The environment loads the function code from the JAR file during specialization and then executes it.

Fission Contract

A function needs to implement the io.fission.Function class and override the call method. The call method receives the RequestEntity and Context as inputs and needs to return ResponseEntity object. Both RequestEntity and ResponseEntity are from org.springframework.http package and provide a fairly high level and rich API to interact with request and response.

ResponseEntity call(RequestEntity req, Context context);

The Context object is a placeholder to interact with JVM and Fission and provides information about the framework to the code. This also serves as an extension mechanism to provide more information to runtime code in future.

Building a Function

Source Code and Test

The function code responds with “Hello World” in response body.

public class HelloWorld implements Function {

	@Override
	public ResponseEntity<?> call(RequestEntity req, Context context) {
		return ResponseEntity.ok("Hello World!");
	}

}

Project and Dependencies with Maven

First you have to define the the basic information about the function:

	<modelVersion>4.0.0</modelVersion>
	<groupId>io.fission</groupId>
	<artifactId>hello-world</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>JAR</packaging>

	<name>hello-world</name>

You will have to add two dependencies - specifications below - which are provided by the function runtime.

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<version>2.0.1.RELEASE</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>io.fission</groupId>
			<artifactId>fission-java-core</artifactId>
			<version>0.0.2-SNAPSHOT</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>

One of the key things when packaging the Java function is to package it as a uber/fat JAR so that the class and all other dependencies are packaged with the function. You can use maven-assembly-plugin for that:

<execution>
	<id>make-assembly</id> <!-- this is used for inheritance merges -->
	<phase>package</phase> <!-- bind to the packaging phase -->
	<goals>
		<goal>single</goal>
	</goals>
</execution>

Lastly, since the fission-java-core is currently in the snapshot release, you need to explicitly add the sonatype repository where it is published.

	<repositories>
		<repository>
			<id>fission-java-core</id>
			<name>fission-java-core-snapshot</name>
			<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
		</repository>
	</repositories>

Building the Package

For building the source Java code with Maven, you either need Maven and Java installed locally or you can use the build.sh helper script which builds the code inside a docker image that has those dependencies.

docker run -it --rm  -v "$(pwd)":/usr/src/mymaven -w /usr/src/mymaven maven:3.5-jdk-8 mvn clean package

At this stage we assume that the build was successful and that you have the JAR file of the function ready.

Note, you can also use the Fission builder for building the JAR file, which comes with a pre-packaged Maven-based builder. (more on that in a separate tutorial, so stay tuned).

Deploying the Function

First you will need to create an environment. The keeparchive flag is important for Java-based applications that are packaged as JAR file. This flag will ensure that the fetcher won’t extract the JAR file into a directory. Currently, the JVM environment supports version 2 and above, so we specify the environment version as 2.

$ fission env create --name jvm --image fission/jvm-env --version 2 --keeparchive

When creating the function we provide the JAR file we built and the environment. The entrypoint signifies the fully qualified name of the class which implements the Fission’s Function interface.

$ fission fn create --name hello --deploy target/hello-world-1.0-SNAPSHOT-JAR-with-dependencies.JAR --env jvm --entrypoint io.fission.HelloWorld

Now you can create a route and test that the function works!

$ fission route create --function hello --url /hellon --method GET

$ fission function test --name hello 

OR

$ curl $FISSION_ROUTER/hello
Hello World!

Want more?

Follow us on Twitter for more updates! @Fissionio


Author: