Use Piranha Embedded as a light-weight Servlet runner

Running a Servlet using Piranha is very easy and fun! For this blog entry we will show you how you can use Piranha Embedded, coupled with the DefaultHttpServer to run a HelloWorldServlet.

Follow steps mentioned below.

  1. Write the HelloWorldServlet
  2. Create the HelloWorldAppplication
  3. Add the module-info
  4. Add the POM file
  5. Build JLink image

Write the HelloWorldServlet

The code for the HelloWorldServlet will be similar to the snippet below.

package helloworld;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HelloWorldServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, 
            HttpServletResponse response) throws IOException, ServletException {
        PrintWriter writer = response.getWriter();
        writer.println("Hello World");
    }
}

Create the HelloWorldApplication

The code for the HelloWorldApplication class will be similar to the snippet below.

package helloworld;

import cloud.piranha.embedded.EmbeddedPiranha;
import cloud.piranha.embedded.EmbeddedPiranhaBuilder;
import cloud.piranha.http.impl.DefaultHttpServer;
import cloud.piranha.http.webapp.HttpWebApplicationServerProcessor;

public class HelloWorldApplication {

    public static void main(String[] arguments) throws Exception {
        EmbeddedPiranha piranha = new EmbeddedPiranhaBuilder()
                .servlet("HelloWorld", HelloWorldServlet.class)
                .servletMapping("HelloWorld", "/*")
                .buildAndStart();
        
        DefaultHttpServer server = new DefaultHttpServer(8080, 
                new HttpWebApplicationServerProcessor(piranha), false);
        
        server.start();
    }
}

Note you can swap out DefaultHttpServer with Grizzly, JDK HTTP server, Undertow or Netty. All it takes is using the appropriate module and adjusting the class name.

Add the module-info

module helloworld {

    exports helloworld;
    requires cloud.piranha.embedded;
    requires cloud.piranha.http.api;
    requires cloud.piranha.http.impl;
    requires cloud.piranha.http.webapp;
    requires jakarta.servlet;
}

Add the POM file

The POM file below contains the plumbing to build the project using Maven.

<?xml version="1.0" encoding="UTF-8"?>

<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>embedded</groupId>
    <artifactId>servlet-runner</artifactId>
    <version>1-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>Piranha Embedded Servlet runner application</name>
    <properties>
        <piranha.version>23.1.0</piranha.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.10.1</version>
                <configuration>
                    <release>17</release>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.4.0</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/modules</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <mainClass>helloworld.HelloWorldApplication</mainClass>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.3.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>helloworld.HelloWorldApplication</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin> 
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.3.0</version>
                <executions>
                    <execution>
                        <id>copy-resources</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/modules</outputDirectory>
                            <resources>          
                                <resource>
                                    <directory>${project.build.directory}</directory>
                                    <filtering>false</filtering>
                                    <includes>
                                        <include>*.jar</include>
                                    </includes>
                                </resource>
                            </resources>              
                        </configuration>            
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.moditect</groupId>
                <artifactId>moditect-maven-plugin</artifactId>
                <version>1.0.0.RC1</version>
                <executions>
                    <execution>
                        <id>create-runtime-image</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>create-runtime-image</goal>
                        </goals>
                        <configuration>
                            <modulePath>
                                <path>${project.build.directory}/modules</path>
                            </modulePath>
                            <modules>
                                <module>helloworld</module>
                            </modules>
                            <launcher>
                                <name>helloworld</name>
                                <module>helloworld</module>
                            </launcher>
                            <noManPages>true</noManPages>
                            <noHeaderFiles>true</noHeaderFiles>
                            <compression>2</compression>
                            <outputDirectory>${project.build.directory}/jlink</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>cloud.piranha</groupId>
            <artifactId>piranha-embedded</artifactId>
            <version>${piranha.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>cloud.piranha.http</groupId>
            <artifactId>piranha-http-impl</artifactId>
            <version>${piranha.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>cloud.piranha.http</groupId>
            <artifactId>piranha-http-webapp</artifactId>
            <version>${piranha.version}</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>            

The next step is to build the JLink image.

            
mvn verify
        

And voila the jlink/target/jlink directory will contain the JLink native image. You now have a quick recipe to write Servlets using Piranha Embedded.

Enjoy!

Posted March 24th, 2023

Up