bekwam courses

WildFly RESTful Todo Demo

February 14, 2019

This article presents a RESTful web service implemented in the WildFly AS 16 (Beta) app server. This service provides a back-end for some of the VueJS articles on the site. WildFlyAS ships with the H2 in-memory database which lets the VueJS interact with a store that survives browser refreshes. An Enterprise Java Bean (EJB) is used with Java Persistence Architecture (JPA) to manipulate and query the H2 data.

The service manages simple Todo items. A Todo item is a text field describing a task. There is a status flag "done" which marks whether or not the item is done. A Todo is identified by its id field which is generated by the database. There are 5 operation in the service listed below with their HTTP methods.

This ReadyAPI screenshot shows the Add Todo operation. A JSON payload is prepared on the left Request tab containing a field "text". On the right, a complete Todo is returned which includes the passed-in text field, a generated id, and the default done value of "false".

Screenshot of ReadyAPI
An Add Todo Operation

The Add Todo operation inserts a record in the database. The following Get All Todos operation retrieves this record and any others that have been added since the app server last started.

Screenshot of ReadyAPI
Get All Todos Operation

Project

This demo was prepared in IntelliJ as a Maven project. The project produces a WAR which will be deployed in WildFlyAS. The only dependency needed is the JavaEE 8 API since the runtime JARs are included in the app server. The pom for the project follows.


<?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>org.bekwam</groupId>
    <artifactId>wf15-vuejs-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>8.0</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.2.2</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

The packaging is set to WAR. The single dependency on the API JAR is listed along with a code specifier to use Java 8. I'm not specifying a web.xml config file so this needs the special WAR Plugin directive.

Design

The RESTful service consists of a JAX-RS class TodosResource and an EJB TodoService. TodosResource, along with the APIApplication registration, translates HTTP methods into Java methods. The EJB handles the business logic and data persistence. Since only a single table is involved, there isn't any business logic. The EJB delegates to a JPA class called an EntityManager that maps the Java object to database rows.

H2 is an in-memory database that's preconfigured with WildFly. While this doesn't save to disk like an Oracle or SQL Server database, it does give web applications the correct response by saving data between browser refreshes.

This class diagram shows the arrangement of the Java classes that make up the RESTful application.

UML Diagram
Class Diagram of RESTful Application

Code

The Todo class serves two purposes. It is the object which is mapped to the database row in the EJB code. It is also the transport converted to and from JSON for use in the web service. JPA pulls a record from the database and converts it to a Todo Java object. If returned from the EJB into the web service for relay back to the caller, it is translated into JSON through the MediaType annotation. Similarly, incoming JSON is converted to the Todo Java object and passed along to the EJB.


package vuejsdemo.ejb;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Version;
import java.io.Serializable;

@Entity
public class Todo implements Serializable  {

    private static final long serialVersionUID = -2441270608947243729L;

    @Id
    @GeneratedValue
    private Long id;

    private String text;

    private Boolean done = false;

    @Version
    private Long version;

    public Todo() { }

    public Todo(String text) {
        this.text = text;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public Boolean getDone() {
        return done;
    }

    public void setDone(Boolean done) {
        this.done = done;
    }
}

The id field is set by the database as a generated value. version is also set by the database to enable Optimistic Locking. Optimistic Locking isn't discussed here, but it's a technique to prevent two callers from unknowlingly writing out the same record.

EJB

Working from the back-end outward, the next class to look at is TodoService. This is a Stateless EJB. It uses an EntityManager to interact with the ORM subsystem.

Although, I mentioned JPA as the persistence mechanism, the actual implementation for WildFly is Hibernate. JPA is a standardized interface that allows you to swap to non-Hibernate providers without changing code if needed.


package vuejsdemo.ejb;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import java.util.List;
import java.util.Optional;

@Stateless
public class TodoService {

    @PersistenceContext
    private EntityManager em;

    public List<Todo> getAllTodos() {
        TypedQuery<Todo> q = em.createQuery("SELECT t FROM Todo t ORDER BY t.id", Todo.class);
        return q.getResultList();
    }

    public Todo addTodo(Todo t) {
        em.persist( t );
        return t;  // has id set
    }

    public Optional<Todo> findTodoById(Long id) {
        return Optional.ofNullable( em.find( Todo.class, id ) );
    }

    public Boolean deleteTodo(Long id) {
        boolean wasDeleted = false;
        Optional<Todo> fromDB = findTodoById(id);
        if( fromDB.isPresent() ) {
            em.remove(fromDB.get());
            wasDeleted = true;
        }
        return wasDeleted;
    }

    public Boolean updateTodo(Todo t) {
        Optional<Todo> fromDB = findTodoById(t.getId());
        boolean wasUpdated = false;
        if( fromDB.isPresent() ) {
            Todo fd = fromDB.get();
            fd.setText( t.getText() );
            fd.setDone( t.getDone() );
            wasUpdated = true;
        }
        return wasUpdated;
    }
}

The EJB is standard JPA. I'm using methods like createQuery(), find(), persist(), and remove() to interact with the database. em.createQuery() and em.find() translate to SELECT statements. em.remove() corresponds to a DELETE statement(). em.persist() in this context saves a new record. The updateTodo() method doesn't explicitly interact with the EntityManager to update the record. Rather, it retrieves a record, manipulates the fields and a commit occurs after the method is exited.

I use a Java Optional as a return value from the finder method. I prefer this to null or to throwing an Exception because it's intent is clearer and I don't have to write try / catches in callers. I return Booleans from my delete and update methods as error handling instead of throwing NoResultExceptions. These elements are a matter of style.

RESTful Web Service

TodosResource is a JAX-RS class that marshalls the arguments from the HTTP call and invokes EJB methods.

    
package vuejsdemo.rs;

import vuejsdemo.ejb.Todo;
import vuejsdemo.ejb.TodoService;

import javax.ejb.EJB;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
import java.util.Optional;

@Path("/todos")
public class TodosResource {

    @EJB
    private TodoService ejb;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public List<Todo> getTodos() {
        return ejb.getAllTodos();
    }

    @GET
    @Path("/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Todo getTodo(@PathParam("id") Long id) {
        Optional<Todo> todo = ejb.findTodoById(id);
        if( !todo.isPresent() ) {
            throw new WebApplicationException("id=" + id + " not found", Response.Status.NOT_FOUND);
        }
        return todo.get();
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Todo createTodo(Todo t) {
        return ejb.addTodo(new Todo(t.getText()) );
    }

    @PUT
    @Path("/{id}")
    @Consumes(MediaType.APPLICATION_JSON)
    public void updateTodo(@PathParam("id") Long id, Todo t) {
        t.setId( id );
        boolean wasUpdated = ejb.updateTodo(t);
        if( !wasUpdated ) {
            throw new WebApplicationException("id=" + t.getId() + " not found", Response.Status.NOT_FOUND);
        }
    }

    @DELETE
    @Path("/{id}")
    public void deleteTodo(@PathParam("id") Long id) {
        boolean wasDeleted = ejb.deleteTodo(id);
        if( !wasDeleted ) {
            throw new WebApplicationException("id=" + id + " not found", Response.Status.NOT_FOUND);
        }
    }
}    
    

Overall, there's a strong affinity between the HTTP request and the EJB method. That is, one HTTP method maps to one JAX-RS method which maps to one EJB method. Usually, you can expect business logic to accrue at the EJB layer. I generally keep the JAX-RS layer as simple as possible. The ideal is one-line calls to EJBs.

The final class is called a JAX-RS Application. It's included here for completeness because it registers the TodosResource with the WildFly container.


package vuejsdemo.rs;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;

@ApplicationPath("/api")
public class APIApplication extends Application {

    @Override
    public Set<Class<?>> getClasses() {

        Set<Class<?>> classes = new HashSet<>();

        classes.add( ProductsResource.class );
        classes.add( TodosResource.class );
        classes.add( NoResultExceptionMapper.class );

        return classes;
    }
}

This article presented a RESTful web service implemented using JAX-RS, and EJB, and JPA. This is a typical JavaEE back-end implementation and it's made for the WildFly application server. I've had a lot of success in recent years working with the WildFly app server and upcoming blog posts will use projects like this.


Headshot of Carl Walker

By Carl Walker

President and Principal Consultant of Bekwam, Inc