Java - Minimal Undertow RESTeasy and Weld CDI Setup
Before we start
What is Undertow?
Quoted from the project page:
Undertow is a flexible performant web server written in java, providing both blocking and non-blocking API’s based on NIO.
Undertow has a composition based architecture that allows you to build a web server by combining small single purpose handlers. The gives you the flexibility to choose between a full Java EE servlet 4.0 container, or a low level non-blocking handler, to anything in between.
Undertow is designed to be fully embeddable, with easy to use fluent builder APIs. Undertow’s lifecycle is completely controlled by the embedding application.
Undertow is sponsored by JBoss and is the default web server in the Wildfly Application Server.
Alternatives include Oracle Grizzly2, Netty, Eclipse Jetty and Apache Tomcat (has non-embedded version)
In a nutshell, Undertow is a embedded web server used that supports both static and dynamic content delivery.
It is notable that in my tests done in a side benchmarking project that Undertow seems to perform much better than the alternatives mentioned for simple use especially combined with the RESTeasy JAX-RS framework. You could try to download the latest artifacts on the project to test it out yourself.
What is RESTeasy?
Quoted from the project page:
RESTEasy is a JBoss project that provides various frameworks to help you build RESTful Web Services and RESTful Java applications. It is a fully certified and portable implementation of the JAX-RS 2.1 specification, a JCP specification that provides a Java API for RESTful Web Services over the HTTP protocol.
In a nutshell RESTeasy is a JAX-RS (Java API for RESTful Web Services) implementation that provides frameworks aiding in exposing REST (REpresentational State Transfer) endpoints, especially for CRUD (Create, Read, Update and Delete) operations.
Alternatives include Oracle Jersey
RESTeasy is also developed by RedHat and has better "native" integration with Undertow than Jersey.
What is Weld?
What is CDI?
As quoted from another good read Baeldung: Java EE CDI:
It allows us to manage the lifecycle of stateful components via domain-specific lifecycle contexts and inject components (services) into client objects in a type-safe way.
In layman terms, CDI something like a factory that manages the life of Java objects during the life of the application, this is especially important in a multi-threaded environment like a web server. Some examples include:
- Objects that only need one instance throughout the runtime of the application (database connections, etc.)
- Objects that only need to be unique and persist in a session
- Objects that only need to be unique and persist in a single request for web applications.
It is possible to code CDI yourself as mentioned in the Baeldung article, but since implementations are already freely available, you are able to use them and skip boilerplate codes by using annotation interfaces.
Weld
Quoted from the project page:
Weld is the reference implementation of CDI for the Java EE Platform - a JCP standard for dependency injection and contextual lifecycle management and one of the most important and popular parts of the Java EE. Weld is integrated into many Java EE application servers such as WildFly, JBoss Enterprise Application Platform, GlassFish, Oracle WebLogic Server, WebSphere Application Server and others. Weld can also be used in plain servlet containers (Tomcat, Jetty) or Java SE.
Base setup
Maven pom.xml
We will be using Maven to manage our Java libraries that we will depend on, you can use Gradle if you wish.
<dependencies> <!-- UNDERTOW --> <dependency> <groupId>io.undertow</groupId> <artifactId>undertow-servlet</artifactId> <version>2.0.23.Final</version> </dependency> <!-- RESTEASY --> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-undertow</artifactId> <version>4.1.1.Final</version> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-cdi</artifactId> <version>4.1.1.Final</version> </dependency> <!-- WELD --> <dependency> <groupId>org.jboss.weld.servlet</groupId> <artifactId>weld-servlet-core</artifactId> <version>3.1.1.Final</version> </dependency> <dependency> <groupId>org.jboss.weld</groupId> <artifactId>weld-core-impl</artifactId> <version>3.1.1.Final</version> </dependency> <dependency> <groupId>org.jboss.weld</groupId> <artifactId>weld-api</artifactId> <version>3.1.Final</version> </dependency> </dependencies>
/src/resources/META-INF/beans.xml
This file located in the resources META-INF folder is required to configure Weld
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" version="1.2" bean-discovery-mode="annotated"> </beans>
You can change "annotated" to "all" if you wish for all Java classes to be managed. Note that it would impact startup time slightly.
/src/main/java/.../MyResource.java
Also known as a controller in PHP, defines routes.
@RequestScoped @Path("/") public class MyResource { @Inject BeanManager manager; @Path("/") @GET public String getManager() { return manager.toString(); } }
/src/main/java/.../MyApplication.java
To specify which resources and middleware for web servers to deploy along with the base path.
@ApplicationPath("/") public class MyApplication extends Application { @Override public Set<Class<?>> getClasses() { Set<Class<?>> classes = new HashSet<>(); classes.add(MyResource.class); // Resource implemented in the previous section return classes; } }
Main function
public static void main(String[] args) { UndertowJaxrsServer server = new UndertowJaxrsServer(); ResteasyDeployment deployment = new ResteasyDeploymentImpl(); deployment.setApplicationClass(MyApplication.class.getName()); // Application implemented in the previous section deployment.setInjectorFactoryClass("org.jboss.resteasy.cdi.CdiInjectorFactory"); // set CDI injector factory DeploymentInfo deploymentInfo = server.undertowDeployment(deployment, "/"); deploymentInfo.setClassLoader(Application.class.getClassLoader()); deploymentInfo.setDeploymentName("Minimal Undertow RESTeasy and Weld CDI Setup"); // set name of deployment deploymentInfo.setContextPath("/"); deploymentInfo.addListener(Servlets.listener(Listener.class)); // add Weld listener to deployment server.deploy(deploymentInfo); Undertow.Builder builder = Undertow.builder() .addHttpListener(8080, "localhost"); // access the server on http://localhost:8080, note that "localhost" should be "0.0.0.0" if you wish for others in the network to connect. server.start(builder); }
Since Undertow is an embedded server, you will just need to run the application and it will expose the server on the specified IP and port.
After running, if you attempt to access your web server at http://localhost:8080 and it returns "Weld BeanManager" with a bean count above 0, you have successfully configured the setup.
What's next?
Modify MyResource.java to play around with scoping
@Path("/") @RequestScoped // try changing this to "@ApplicationScoped" along with the commented modifications below public class MyResource { @Inject BeanManager manager; private int count = 0; // add a counter variable @Path("/") @GET public String getManager() { count++; // increment counter return manager + ". Current count: " + count; // show current count ; } }
When it is application scoped, the count should increase every time you refresh the endpoint, vice versa for request scoped.
This is because application scoped will re-initialize the count to 0 every request, whereas request scoped will not.
You can research more about scoping in Weld in the documentation
Further reading
Do read the documentations on Undertow, RESTeasy and Weld.
Carry on with your own projects
Now that you are equipped with the basic knowledge on how to implement Weld CDI on Undertow RESTeasy, go ahead and code your own projects! Be sure to update the Maven configuration if you are using them and the versions used are outdated.