www.apress.com

23/08/2018

Creating a JAX-RS Web Service

by Josh Juneau

In this post, I am going to show you how to create a simple Jakarta EE web service.  As you may know, Java EE is currently being transitioned from Oracle Corporation to an open source platform under the Eclipse Foundation. That open platform will be known as Jakarta EE. Hence, I am going to show you how to develop a simple web service using JAX-RS, which is currently an EE4J project. (EE4J is an umbrella project hosting all Jakarta EE specifications).

You can find the sources for JAX-RS on GitHub using the link at the end of this post. To develop the service quickly, I recommend using Apache NetBeans 9.0 along with some tricks that I’ll show you in this post. I will utilize GlassFish 5 for this post, along with an Apache Derby database. Payara 5 is a drop-in replacement for GlassFish 5, so you can use either as you follow along.

First, the service will read from a database table that is created with the following (created for Apache Derby):


    /**
       * Author:  Josh Juneau
       * Created: Aug 15, 2018
       */
    create table book(
    id  numeric,
    title   varchar(100),
    publish_date date,
    author_first varchar(50),
    author_last varchar(50));

    insert into book values(
    1,
    'Java EE 8 Recipes',
    date('2018-07-30'),
    'JOSH',
    'JUNEAU');


To get started with the service, create a new Maven Web Project using Apache NetBeans. In this example, I named the service JavaBookService. Once the Maven Web Project has been created, right-click on it to open the POM file. Paste the following dependencies into the POM file. Doing so will load all of Java EE 8, since the initial release of Jakarta EE will be in parity with Java EE 8. The truly necessary dependencies though, are JPA (Java Persistence API), Bean Validation, and JAX-RS (RESTful service API). Here is the code:


    <dependency>
          <groupId>org.eclipse.persistence</groupId>
          <artifactId>eclipselink</artifactId>
          <version>2.5.2</version>
          <scope>provided</scope>
      </dependency>
      <dependency>
          <groupId>org.eclipse.persistence</groupId>

    <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId>
          <version>2.5.2</version>
          <scope>provided</scope>
      </dependency>
      <dependency>
          <groupId>javax</groupId>
          <artifactId>javaee-web-api</artifactId>
          <version>8.0</version>
          <scope>provided</scope>
      </dependency>

     

Before we can begin to work with mapping any database tables, we need to add a persistence unit (mapping XML for a database connection) by right-clicking the project and choosing New->Persistence Unit. For the purposes of this blog post, I am going to use a default connection that is provided by Payara Server, which is a GlassFish look-alike.  That data source is jdbc/__derby, which connects to the default Apache Derby database.

My persistence unit is in the file persistence.xml, and looks as follows:


    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.1"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence 
    http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
        <persistence-unit name="JavaBookService_PU" transaction-type="JTA">
          <jta-data-source>jdbc/__derby</jta-data-source>
          <properties/>
        </persistence-unit>
    </persistence>


Next, right-click on the project and create a new Java class named Book.  The class should go into the org.javabook package.  If you wish to use the Apache NetBeans Entity Classes from Database wizard, you can do that as well.  Copy the following sources into the Book class:


    package org.javabook;

    import java.io.Serializable;
    import java.time.LocalDate;
    import javax.persistence.Basic;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.persistence.Table;
    import javax.validation.constraints.NotNull;
    import javax.xml.bind.annotation.XmlRootElement;

    /**
      *
      * @author Josh Juneau
      */
    @Entity
    @Table(name="BOOK")
    @XmlRootElement
    public class Book implements Serializable {
        private static final long serialVersionUID = 1L;
        @Id
        @Basic(optional = false)
        @NotNull
        @Column(name = "ID")
        private Integer id;

        @Column(name="TITLE")
        private String title;

        @Column(name="PUBLISH_DATE")
        private LocalDate publishDate;
 

        @Column(name="AUTHOR_FIRST")
        private String authorFirst;

        @Column (name="AUTHOR_LAST")
        private String authorLast;

         // getters and setters

    }


From the top, the @Entity annotation denotes the class as an entity class, and the @Table annotation is used to list the table name to which the class will map. The rest of the fields in the class are mapped to database columns via the @Column annotation, and the primary key is specified with @Id.

Next, create a new Java class in a package named org.javabook.service, and name that new class ApplicationConfig. This will be the class that is used to configure JAX-RS for the application. Copy the following sources into the class:


    package org.javabook.service;
    import javax.ws.rs.ApplicationPath;
    import javax.ws.rs.core.Application;

    /**
      *
      * @author Josh Juneau
      */
   @ApplicationPath("rest")
    public class ApplicationConfig extends Application {

    }


The @ApplicationPath annotation in this code denotes a URI path for which the RESTful web services will be made available.

Lastly, utilize the NetBeans IDE to automatically create a REST service class for the entity class.  Right click on the project and choose New->RESTful Service from Entity Classes. Then select the org.javabook.Book class, and click Finish (Figure 1).  Apache NetBeans will automatically generate a RESTful, service class defining standard services such as findAll() to retrieve all records.


New Content Item

Figure 1.  Apache NetBeans: New RESTful Web Service from Entity Classes


Following are the sources for BookFacadeREST, the new service, as generated by Apache NetBeans:


    package org.javabook.service;

    import java.util.List;
    import javax.ejb.Stateless;
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    import javax.ws.rs.Consumes;
    import javax.ws.rs.DELETE;
    import javax.ws.rs.GET;
    import javax.ws.rs.POST;
    import javax.ws.rs.PUT;
    import javax.ws.rs.Path;
    import javax.ws.rs.PathParam;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.MediaType;
    import org.javabook.Book;

    /**
      *
      * @author Josh Juneau
      */
    @Stateless
    @Path("org.javabook.book")
    public class BookFacadeREST extends AbstractFacade<Book> {

        @PersistenceContext(unitName = "JavaBookService_PU")
        private EntityManager em;

        public BookFacadeREST() {
            super(Book.class);
        }

        @POST
        @Override
        @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
        public void create(Book entity) {
            super.create(entity);
        }

        @PUT
        @Path("{id}")
        @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
        public void edit(@PathParam("id") Integer id, Book entity) {
            super.edit(entity);
        }

        @DELETE
        @Path("{id}")
        public void remove(@PathParam("id") Integer id) {
        super.remove(super.find(id));
        }

        @GET
        @Path("{id}")
        @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
        public Book find(@PathParam("id") Integer id) {
            return super.find(id);
        }

        @GET
        @Override
        @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
        public List<Book> findAll() {
            return super.findAll();
        }

        @GET
        @Path("{from}/{to}")
        @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) 
        public List<Book> findRange(@PathParam("from") Integer from, @PathParam("to") Integer to) {
            return super.findRange(new int[]{from, to});
        }

        @GET
        @Path("count")
        @Produces(MediaType.TEXT_PLAIN) 
        public String countREST() {
            return String.valueOf(super.count());

        }

        @Override
        protected EntityManager getEntityManager() {
            return em;
        }

    }


The service should now be fully functional.  The overall project structure should resemble that shown in Figure 2. 


New Content Item

Figure 2.  JAX-RS Service Project Structure


The project can now be compiled and deployed to a compliant application server.  In this case, I choose to deploy to Payara 5, as it is Java EE 8 compliant.  However, you can deploy as a microservice by choosing a smaller application server container, such as Payara Micro.



About the Author

Josh Juneau is an application developer and systems analyst, a contributor to the Chicago Java User Group, an Apache NetBeans committer, and a Java Champion. He participates in the JCP and has been a part of the JSF expert group for the development of Java EE 8.

Josh is author of the following recent books on Java programming:

To hear more from Josh, follow him at Josh’s Dev Blog, and on Twitter via @javajuneau.

This article was contributed by Josh Juneau, author of Java EE 8 Recipes - A Problem-Solution Approach.