Use CDI in Jetty with Apache OpenWebBeans

We talked about how to use CDI with tomcat in earlier post. What I didn’t mentioned there is, its now one of Jakarta EE specification. I assume that your are familiar with IOC (Inversion of Control) and DI (Dependency Injection) and you are trying to use it in application running on Jetty. I was also looking for such resource and couldn’t find newer and working guide. So, here goes my findings about using CDI in Jetty with Apache OpenWebBeans as CDI implementation.

Why Apache OpenWebBeans?

It is powerful, production ready and easy to integrate with various containers because of its pluggable architecture. Apache OpenWebBeans official website is not up to date, that is sad. But good news is they are keeping their pace to aligned with latest technologies. Good example for that is, they have released bom which supports Jakarta EE 9. I’ll discuss how was the integration experience later.

Why Jetty?

Fast startup. Compared to tomcat its startup is quick.

Use CDI in Jetty 9 with Apache OpenWebBeans

Upgrading your container from tomcat to Jetty requires CDI configuration if your app is using CDI. There are few resources out there if you prefer weld. But in my case its OpenWebBeans. The process was easier than I thought. You may be wondering why Jetty 9, since there are newer versions. First, it is stable and many people still use Jetty 9. Also for Jetty 11 you need to upgrade your app to Jakarta EE 9. What ever the reason for using Jetty 9, let’s give it a shot.

First download and unzip the Jetty distribution. I’m using version 9.4.39.v20210325. Open up your terminal and execute following commands. I hope you know the concept of jetty base. I’m using git bash (mingw) in windows pc.

JETTY_HOME=/c/jetty-distribution-9.4.39.v20210325/
mkdir jetty-base
cd jetty-base/
java -jar $JETTY_HOME/start.jar --add-to-start=http,deploy,cdi,annotations,cdi-decorate

After final command, you will see something like following.

INFO  : webapp          transitively enabled, ini template available with --add-to-start=webapp
INFO  : server          transitively enabled, ini template available with --add-to-start=server
INFO  : mail            transitively enabled
INFO  : servlet         transitively enabled
INFO  : cdi             initialized in ${jetty.base}\start.ini
INFO  : annotations     initialized in ${jetty.base}\start.ini
INFO  : cdi-decorate    initialized in ${jetty.base}\start.ini
INFO  : transactions    transitively enabled
INFO  : threadpool      transitively enabled, ini template available with --add-to-start=threadpool
INFO  : plus            transitively enabled
INFO  : deploy          initialized in ${jetty.base}\start.ini
INFO  : security        transitively enabled
INFO  : jndi            transitively enabled
INFO  : http            initialized in ${jetty.base}\start.ini
INFO  : bytebufferpool  transitively enabled, ini template available with --add-to-start=bytebufferpool
MKDIR : ${jetty.base}\webapps
INFO  : Base directory was modified

Let’s spin up Jetty now.

java -jar $JETTY_HOME/start.jar
2021-04-03 19:25:01.786:INFO::main: Logging initialized @2386ms to org.eclipse.jetty.util.log.StdErrLog
2021-04-03 19:25:02.281:INFO:oejs.Server:main: jetty-9.4.39.v20210325; built: 2021-03-25T14:42:11.471Z; git: 9fc7ca5a922f2a37b84ec9dbc26a5168cee7e667; jvm 13-ea+31
2021-04-03 19:25:02.329:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///C:/Users/Ruwanka/jetty-base/webapps/] at interval 1
2021-04-03 19:25:03.588:INFO:oejs.AbstractConnector:main: Started [email protected]{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
2021-04-03 19:25:03.589:INFO:oejs.Server:main: Started @4189ms
2021-04-03 19:26:05.786:INFO:oejs.AbstractConnector:JettyShutdownThread: Stopped [email protected]{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}

Now we need a application to deploy. I am going to use same application I used in earlier tomcat CDI article. But here we are not going to install owb (OpenWebBeans) jars to tomcat. Here we need to bundle them with the war file. Also we have to add entry in the web.xml for the owb servelet context listener implementation class.

Following pom.xml will bring all dependencies we need for the application including owb jars.

<?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>com.aptkode</groupId>
  <artifactId>jetty-cdi-demo</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>jetty-cdi-demo Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <openwebbeans-version>2.0.22</openwebbeans-version>
    <geronimo_annotation-version>1.0</geronimo_annotation-version>
    <geronimo_interceptor-version>1.0</geronimo_interceptor-version>
    <geronimo_cdi-version>1.0.1</geronimo_cdi-version>
    <geronimo_atinject-version>1.0</geronimo_atinject-version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.apache.openwebbeans</groupId>
      <artifactId>openwebbeans-spi</artifactId>
      <version>${openwebbeans-version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.openwebbeans</groupId>
      <artifactId>openwebbeans-impl</artifactId>
      <version>${openwebbeans-version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.openwebbeans</groupId>
      <artifactId>openwebbeans-web</artifactId>
      <version>${openwebbeans-version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.openwebbeans</groupId>
      <artifactId>openwebbeans-jetty9</artifactId>
      <version>${openwebbeans-version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-annotation_1.3_spec</artifactId>
      <version>${geronimo_annotation-version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-interceptor_1.2_spec</artifactId>
      <version>${geronimo_interceptor-version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-jcdi_2.0_spec</artifactId>
      <version>${geronimo_cdi-version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-atinject_1.0_spec</artifactId>
      <version>${geronimo_atinject-version}</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <finalName>jetty-cdi-demo</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

Following web.xml will make owb wired to jetty.

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <listener>
    <listener-class>org.apache.webbeans.servlet.WebBeansConfigurationListener</listener-class>
  </listener>
</web-app>

Build the app, then start using following command from the jetty base directory.

java -jar $JETTY_HOME/start.jar

As previous app, this app will print application initialized message triggered by following annotations.

package com.aptkode;

import javax.annotation.Priority;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Initialized;
import javax.enterprise.event.Observes;
import javax.interceptor.Interceptor;

@ApplicationScoped
public class StartupListener {
    public void onInit(@Observes
                       @Initialized(ApplicationScoped.class)
                       @Priority(Interceptor.Priority.APPLICATION) Object initContext) {
        System.out.println("#######################");
        System.out.println("application initialized");
        System.out.println("#######################");
    }
}

Compared to tomcat setup, this is fairly easy.

Use CDI in Jetty 10 with Apache OpenWebBeans

For Jetty 10 we don’t have to change anything in the application. Just the commands.

JETTY_HOME=/c/jetty-home-10.0.2/
mkdir jetty10-base
cd jetty10-base/
$ java -jar $JETTY_HOME/start.jar --add-module=http,deploy,cdi,annotations,cdi-decorate
INFO  : mkdir ${jetty.base}\start.d
INFO  : webapp          transitively enabled, ini template available with --add-module=webapp
INFO  : server          transitively enabled, ini template available with --add-module=server
INFO  : servlet         transitively enabled
INFO  : resources       transitively enabled
INFO  : cdi             initialized in ${jetty.base}\start.d\cdi.ini
INFO  : annotations     initialized in ${jetty.base}\start.d\annotations.ini
INFO  : cdi-decorate    initialized in ${jetty.base}\start.d\cdi-decorate.ini
INFO  : threadpool      transitively enabled, ini template available with --add-module=threadpool
INFO  : plus            transitively enabled
INFO  : deploy          initialized in ${jetty.base}\start.d\deploy.ini
INFO  : logging-jetty   transitively enabled
INFO  : security        transitively enabled
INFO  : jndi            transitively enabled
INFO  : http            initialized in ${jetty.base}\start.d\http.ini
INFO  : logging/slf4j   transitive provider of logging/slf4j for logging-jetty
INFO  : logging/slf4j   dynamic dependency of logging-jetty
INFO  : bytebufferpool  transitively enabled, ini template available with --add-module=bytebufferpool
INFO  : mkdir ${jetty.base}\resources
INFO  : mkdir ${jetty.base}\webapps
INFO  : copy ${jetty.home}\modules\logging\jetty\resources\jetty-logging.properties to ${jetty.base}\resources\jetty-logging.properties
INFO  : Base directory was modified
java -jar $JETTY_HOME/start.jar

Before the last command, you should copy your app to the webapps folder of jetty 10 base. That’s it for jetty 10. I guess you have noticed slight differences of the module enabling command. Other than that there is no difference. Jetty 10 is coming with more improvements and support for servlet api 4.0. Those are the few reasons that one might interested in jetty 10 over 9.

Use CDI in Jetty 11 with Apache OpenWebBeans

OpenWebBeans has published jars for Jakarta EE 9 support since version 2.0.15. For version 2.0.22 they have published bom for avoiding transitive dependency coming with the Jakarta owb jars. But I couldn’t get it working. So you can get your Jakarta EE 9 app working with following pom for Jetty 11. Since bom is not working, we have to manually exclude transitive non Jakarta dependencies coming from Jakarta flavor of owb.

<?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>com.aptkode</groupId>
  <artifactId>jetty11-cdi-demo</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>jetty11-cdi-demo Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
    <openwebbeans-version>2.0.22</openwebbeans-version>
  </properties>


  <dependencies>
    <dependency>
      <groupId>org.apache.openwebbeans</groupId>
      <artifactId>openwebbeans-spi</artifactId>
      <version>${openwebbeans-version}</version>
      <classifier>jakarta</classifier>
    </dependency>
    <dependency>
      <groupId>org.apache.openwebbeans</groupId>
      <artifactId>openwebbeans-impl</artifactId>
      <version>${openwebbeans-version}</version>
      <classifier>jakarta</classifier>
      <exclusions>
        <exclusion>
          <artifactId>openwebbeans-spi</artifactId>
          <groupId>org.apache.openwebbeans</groupId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.apache.openwebbeans</groupId>
      <artifactId>openwebbeans-web</artifactId>
      <version>${openwebbeans-version}</version>
      <classifier>jakarta</classifier>
      <exclusions>
        <exclusion>
          <artifactId>openwebbeans-el22</artifactId>
          <groupId>org.apache.openwebbeans</groupId>
        </exclusion>
        <exclusion>
          <artifactId>openwebbeans-impl</artifactId>
          <groupId>org.apache.openwebbeans</groupId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.apache.openwebbeans</groupId>
      <artifactId>openwebbeans-jetty9</artifactId>
      <version>${openwebbeans-version}</version>
      <classifier>jakarta</classifier>
      <exclusions>
        <exclusion>
          <artifactId>openwebbeans-impl</artifactId>
          <groupId>org.apache.openwebbeans</groupId>
        </exclusion>
        <exclusion>
          <artifactId>openwebbeans-web</artifactId>
          <groupId>org.apache.openwebbeans</groupId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>jakarta.annotation</groupId>
      <artifactId>jakarta.annotation-api</artifactId>
      <version>2.0.0</version>
    </dependency>
    <dependency>
      <groupId>jakarta.enterprise</groupId>
      <artifactId>jakarta.enterprise.cdi-api</artifactId>
      <version>3.0.0</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <finalName>jetty11-cdi-demo</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

Slight change to the StartupListener.java, other than that nothing is changed from Jetty 9 version.

package com.aptkode;

import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.Initialized;
import jakarta.enterprise.event.Observes;
import jakarta.interceptor.Interceptor;

@ApplicationScoped
public class StartupListener {
    public void onInit(@Observes
                       @Initialized(ApplicationScoped.class)
                       @Priority(Interceptor.Priority.APPLICATION) Object initContext) {
        System.out.println("#######################");
        System.out.println("application initialized");
        System.out.println("#######################");
    }
}

Final Words

We have explored using CDI in all of Jetty versions with Apache openwebbeans. If you have any questions please raise in comments section. To get your hands dirty source is in github, for Jetty 11, use jetty-11 branch. Thank you for reading.

Photo by cottonbro from Pexels

Leave a Comment

Your email address will not be published. Required fields are marked *