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.