Writing a Serverless HelloWorld function

If you have been around a while in JavaEE / JakartaEE land you are aware of the Servlet API. In this example we are going to leverage the Servlet API, which will be delivered by Manorrock Piranha, to write a 'Hello World' function. Then we will use GraalVM to create a native image that can be used by your Serverless framework.

Create the Hello World function

We will start by creating a Java file named HelloWorldFunction.java in the helloworld package and add the content as described below.


public class HelloWorldFunction extends HttpServlet {

    /**
     * Stores the boolean to terminate.
     */
    private boolean done = false;
   
    /**
     * Process the request.
     *
     * @param request the request.
     * @param response the response.
     * @throws IOException when an I/O error occurs.
     * @throws ServletException when a Servlet error occurs.
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        response.setContentType("text/plain");
        PrintWriter writer = response.getWriter();
        writer.println("Hello World");
        done = true;
    }

    /**
     * Are we done?
     * 
     * @return true if we are, false otherwise.
     */
    public boolean isDone() {
        return done;
    }
}
        

For anyone familiar writing Servlets there is nothing really exciting happening.

Add Manorrock Piranha boot strapping

Now we are going to make it a bit more exciting by adding the main method as shown below.


    /**
     * Main method.
     *
     * @param arguments the arguments.
     */
    public static void main(String[] arguments) {
        DefaultWebApplicationServer server = new DefaultWebApplicationServer();
        HttpServer httpServer = new DefaultHttpServer(8080, server);
        DefaultWebApplication webApp = new DefaultWebApplication();
        webApp.setContextPath("");
        webApp.addServlet("function", new Function());
        webApp.addServletMapping("function", "/*");
        server.addWebApplication(webApp);
        server.initialize();
        server.start();
        httpServer.start();
        while (true) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException ie) {

            }
            if (function.isDone()) {
                break;
            }
        }
        System.exit(0);
    }
        

The code above exposes the Servlet on port 8080 and waits for the a client to connect to it and once the response is done it will terminate.

Create the Maven project to create a standalone JAR

Now we are going create a Maven project where we create a JAR file with all dependencies into one JAR so we can process it later with the GraalVM native image tool.


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>demo</groupId>
    <artifactId>function</artifactId>
    <version>1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>A 'Hello World' function</name>
    <build>
        <finalName>helloworld</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals> 
                            <goal>single</goal> 
                        </goals>
                        <configuration>
                            <archive>
                                <manifest>
                                    <mainClass>helloworld.HelloWorldFunction</mainClass>
                                </manifest>
                            </archive>
                            <descriptorRefs>
                                <descriptorRef>jar-with-dependencies</descriptorRef>
                            </descriptorRefs>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>com.manorrock.piranha</groupId>
            <artifactId>piranha</artifactId>
            <version>4.0.5.0</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
</project>
        

Create the standalone executable using GraalVM

To create the standalone executable we will process it with GraalVM native-image binary. Add the snippet below to the Maven POM file.


<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-antrun-plugin</artifactId>
    <version>1.8</version>
    <executions>
        <execution>
            <phase>package</phase>
            <configuration>
                <target>
                    <exec dir="target" executable="${GRAALVM_HOME}/bin/native-image">
                        <arg value="-cp"/>
                        <arg value="../target/helloworld-jar-with-dependencies.jar"/>
                        <arg value="-H:Name=helloWorld"/>
                        <arg value="-H:Class=helloworld.HelloWorldFunction"/>
                        <arg value="-H:+ReportUnsupportedElementsAtRuntime"/>
                    </exec>
                </target>
            </configuration>
            <goals>
                <goal>run</goal>
            </goals>
        </execution>
    </executions>
</plugin>            
        

Note the GRAALVM_HOME environment variable should be set to point to the GraalVM SDK on your machine.

Building the function binary

Now it is time to generate the binary by executing the command line below.


    mvn clean install
        

Once the build is done you will find the helloWorld binary in the target directory.

Testing the function binary

From the main project directory you can start it using the command line below


    ./target/helloWorld            
        

Point your browser to http://localhost:8080/ and you should see the response as shown as below.


    Hello World
        

You will also notice the helloWorld binary terminates after serving the response. If you do not want that to happen remove the code that deals with and it will stay running to serve more requests until you kill it or your Serverless framework kills it for you.

And voila you now have a simple starting point for writing more Servlet API based serverless functions.

Project resources

If you want a copy of the entire project click here to download it.

Note also read the follow up that changes this example a bit to not host a port, but instead consumes standard input and produces standard output.

Posted December 15th, 2018

Up