Tutorial – REST API design and implementation in Java with Jersey and Spring


Codever Logo

(P) Codever is an open source bookmarks and snippets manager for developers & co. See our How To guides to help you get started. Public bookmarks repos on Github ⭐🙏


Looking to REST in Java? Then you’ve come to the right place, because in the blog post I will present you how to “beautifully” design a REST API and also, how to implement it in Java with the Jersey framework. The RESTful API developed in this tutorial will demonstrate a complete Create,_read,_update_and_delete (CRUD) functionality for podcast resources stored in a MySql database.

Contents

1. The example

1.1. Why?

Before we start, let me tell you why I’ve written this post – well, my intention is to offer in the future a REST API for Podcastpedia.org. Of course I could use Spring’s own REST implementation, as I currently do for the AJAX calls, but I wanted also to see how the “official” implementation looks like. So, the best way to get to know the technology is to build a prototype with it. That is what I did and what I am presenting here, and I can say that I am pretty damn satisfied with Jersey. Read along to understand why!!!

Note: You can visit my post Autocomplete search box with jQuery and Spring MVC to see how Spring handles REST requests.

1.2. What does it do?

The resource managed in this tutorial are podcasts. The REST API will allow creation, retrieval, update and deletion of such resources.

1.3. Architecture and technologies

Architecture diagram

The demo application uses a multi-layered architecture, based on the “Law of Demeter (LoD) or principle of least knowledge”[16]:

  • the first layer is the REST support implemented with Jersey, has the role of a facade and delegates the logic to the business layer
  • the business layer is where the logic happens
  • the data access layer is where the communcation with the pesistence storage (in our case the MySql database) takes place

A few words on the technologies/frameworks used:

1.3.1. Jersey (Facade)

The Jersey RESTful Web Services framework is open source, production quality, framework for developing RESTful Web Services in Java that provides support for JAX-RS APIs and serves as a JAX-RS (JSR 311 & JSR 339) Reference Implementation.

1.3.2. Spring (Business layer)

I like glueing stuff together with Spring, and this example makes no exception. In my opinion there’s no better way to make POJOs with different functionalities. You’ll find out in the tutorial what it takes to integrate Jersey 2 with Spring.

1.3.3. JPA 2 / Hibernate (Persistence layer)

For the persistence layer I still use a DAO pattern, even though for implementing it I am using JPA 2, which, as some people say, should make DAOs superfluous (I, for one, don’t like my service classes cluttered with EntityManager/JPA specific code). AS supporting framework for JPA 2 I am using Hibernate.

See my post Java Persistence Example with Spring, JPA2 and Hibernate for an interesting discussion around persistence thema in Java.

1.3.4. Web Container

Everything gets packaged with Maven as a .war file and can be deployed on any web container – I used Tomcat and Jetty but, it could also be Glassfih, Weblogic, JBoss or WebSphere.

1.3.5. MySQL

The sample data is stored in a MySQL table:

database schema

1.3.6. Technology versions

  1. Jersey 2.9
  2. Spring 4.0.3
  3. Hibernate 4
  4. Maven 3
  5. Tomcat 7
  6. Jetty 9
  7. MySql 5.6

Note: The main focus in the post will be on the REST api design and its implementation with the Jersey JAX-RS implementation, all the other technologies/layers are considered as enablers.

1.4. Source code

The source code for the project presented here is available on GitHub, with complete instructions on how to install and run the project:

2. Configuration

Before I start presenting the design and implementation of the REST API, we need to do a little configuration so that all these wonderful technologies can come and play together

2.1. Project dependencies

The Jersey Spring extension must be present in your project’s classpath. If you are using Maven add it to the pom.xml file of your project:

<dependency>
        <groupId>org.glassfish.jersey.ext</groupId>
        <artifactId>jersey-spring3</artifactId>
        <version>${jersey.version}</version>
        <exclusions>
            <exclusion>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.springframework</groupId>
                <artifactId>spring-web</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>2.4.1</version>
    </dependency>

Note: The jersey-spring3.jar, uses its own version for Spring libraries, so to use the ones you want (Spring 4.0.3.Release in this case), you need to exclude these libraries manually.

Code alert: If you want to see what other dependencies are needed (e.g. Spring, Hibernate, Jetty maven plugin, testing etc.) in the project you can have a look at the the complete pom.xml file available on GitHub.

2.2. web.xml

<?xml version="1.0" encoding="UTF-8"?>
    <web-app version="3.0" xmlns="https://java.sun.com/xml/ns/javaee"
    	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    	<display-name>Demo - Restful Web Application</display-name>

    	<listener>
    		<listener-class>
    			org.springframework.web.context.ContextLoaderListener
    		</listener-class>
    	</listener>

    	<context-param>
    		<param-name>contextConfigLocation</param-name>
    		<param-value>classpath:spring/applicationContext.xml</param-value>
    	</context-param>

    	<servlet>
    		<servlet-name>jersey-serlvet</servlet-name>
    		<servlet-class>
    			org.glassfish.jersey.servlet.ServletContainer
    		</servlet-class>
    		<init-param>
    			<param-name>javax.ws.rs.Application</param-name>
    			<param-value>org.codingpedia.demo.rest.RestDemoJaxRsApplication</param-value>
    		</init-param>
    		<load-on-startup>1</load-on-startup>
    	</servlet>

    	<servlet-mapping>
    		<servlet-name>jersey-serlvet</servlet-name>
    		<url-pattern>/*</url-pattern>
    	</servlet-mapping>

    	<resource-ref>
            <description>Database resource rest demo web application </description>
            <res-ref-name>jdbc/restDemoDB</res-ref-name>
            <res-type>javax.sql.DataSource</res-type>
            <res-auth>Container</res-auth>
        </resource-ref>
    </web-app>
  

2.2.1. Jersey-servlet

Notice the Jersey servlet configuration [lines 18-33]. The javax.ws.rs.core.Application class defines the components (root resource and provider classes,) of the JAX-RS application. I used ResourceConfig, which is Jersey’s own implementation of the class Application, and which provides advanced capabilites to simplify registration of JAX-RS components. Check out the JAX-RS Application Model in the documentation for more possibilities.

My implementation of the ResourceConfig class, ``<p style="text-align: justify;"> Looking to REST in Java? Then you’ve come to the right place, because in the blog post I will present you how to “beautifully” design a REST API and also, how to implement it in Java with the Jersey framework. The RESTful API developed in this tutorial will demonstrate a complete Create,_read,_update_and_delete (CRUD) functionality for podcast resources stored in a MySql database. </p>

My implementation of the ResourceConfig class,`` registers application resources, filters, exception mappers and feature :

package org.codingpedia.demo.rest.service;

    //imports omitted for brevity

    /**
     * Registers the components to be used by the JAX-RS application
     *
     * @author ama
     *
     */
    public class RestDemoJaxRsApplication extends ResourceConfig {

    	/**
    	 * Register JAX-RS application components.
    	 */
    	public RestDemoJaxRsApplication() {
    		// register application resources
    		register(PodcastResource.class);
    		register(PodcastLegacyResource.class);

    		// register filters
    		register(RequestContextFilter.class);
    		register(LoggingResponseFilter.class);
    		register(CORSResponseFilter.class);

    		// register exception mappers
    		register(GenericExceptionMapper.class);
    		register(AppExceptionMapper.class);
    		register(NotFoundExceptionMapper.class);

    		// register features
    		register(JacksonFeature.class);
    		register(MultiPartFeature.class);
    	}
    }

Please note the

  • org.glassfish.jersey.server.spring.scope.RequestContextFilter, which is a Spring filter that provides a bridge between JAX-RS and Spring request attributes
  • org.codingpedia.demo.rest.resource.PodcastsResource, which is the “facade” component that exposes the REST API via annotations and will be thouroughly presented later in the post
  • org.glassfish.jersey.jackson.JacksonFeature, which is a feature that registers Jackson JSON providers – you need it for the application to understand JSON data
  • 2.1.2.2. Spring application context configuration

    The Spring application context configuration is located in the classpath under spring/applicationContext.xml:

    <beans xmlns="https://www.springframework.org/schema/beans"
        	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
        	xmlns:context="https://www.springframework.org/schema/context"
        	xmlns:tx="https://www.springframework.org/schema/tx"
        	xsi:schemaLocation="
        		https://www.springframework.org/schema/beans
        		https://www.springframework.org/schema/beans/spring-beans.xsd
    
        		https://www.springframework.org/schema/tx
        		https://www.springframework.org/schema/tx/spring-tx.xsd
    
        		https://www.springframework.org/schema/context
        		https://www.springframework.org/schema/context/spring-context.xsd">
    
        	<context:component-scan base-package="org.codingpedia.demo.rest.*" />
    
        	<!-- ************ JPA configuration *********** -->
        	<tx:annotation-driven transaction-manager="transactionManager" />
            <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
                <property name="entityManagerFactory" ref="entityManagerFactory" />
            </bean>
            <bean id="transactionManagerLegacy" class="org.springframework.orm.jpa.JpaTransactionManager">
                <property name="entityManagerFactory" ref="entityManagerFactoryLegacy" />
            </bean>
            <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
                <property name="persistenceXmlLocation" value="classpath:config/persistence-demo.xml" />
                <property name="persistenceUnitName" value="demoRestPersistence" />
                <property name="dataSource" ref="restDemoDS" />
                <property name="packagesToScan" value="org.codingpedia.demo.*" />
                <property name="jpaVendorAdapter">
                    <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                        <property name="showSql" value="true" />
                        <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
                    </bean>
                </property>
            </bean>
            <bean id="entityManagerFactoryLegacy" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
                <property name="persistenceXmlLocation" value="classpath:config/persistence-demo.xml" />
                <property name="persistenceUnitName" value="demoRestPersistenceLegacy" />
                <property name="dataSource" ref="restDemoLegacyDS" />
                <property name="packagesToScan" value="org.codingpedia.demo.*" />
                <property name="jpaVendorAdapter">
                    <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                        <property name="showSql" value="true" />
                        <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
                    </bean>
                </property>
            </bean>
    
        	<bean id="podcastDao" class="org.codingpedia.demo.rest.dao.PodcastDaoJPA2Impl"/>
            <bean id="podcastService" class="org.codingpedia.demo.rest.service.PodcastServiceDbAccessImpl" />
            <bean id="podcastsResource" class="org.codingpedia.demo.rest.resource.PodcastsResource" />
            <bean id="podcastLegacyResource" class="org.codingpedia.demo.rest.resource.PodcastLegacyResource" />
    
        	<bean id="restDemoDS" class="org.springframework.jndi.JndiObjectFactoryBean" scope="singleton">
        	    <property name="jndiName" value="java:comp/env/jdbc/restDemoDB" />
        	    <property name="resourceRef" value="true" />
        	</bean>
        	<bean id="restDemoLegacyDS" class="org.springframework.jndi.JndiObjectFactoryBean" scope="singleton">
        	    <property name="jndiName" value="java:comp/env/jdbc/restDemoLegacyDB" />
        	    <property name="resourceRef" value="true" />
        	</bean>
        </beans>
      
    

    Nothing special here, it just defines the beans that are needed throughout the demo application (e.g. podcastsResource which is the entry point class for our REST API).

    3. The REST API (design & implementation)

    3.1. Resources

    3.1.1. Design

    As mentioned earlier, the demo application manages podcasts, which represent the resource in our REST API. Resources are the central concept in REST and are characterized by two main things:

    • each is referenced with a global identifier (e.g. a URI in HTTP).
    • has one or more representations, that they expose to the outer world and can be manipulated with (we’ll be working mostly with JSON representations in this example)

    Resources are usually represented in REST by nouns (podcasts, customers, user, accounts etc.) and not verbs (getPodcast, deleteUser etc.)

    The endpoints used throughout the tutorial are :

    • /podcasts(notice the plural) URI identifying a resource representing a collection of podcasts
    • /podcasts/{id} – URI identifying a podcast resource, by the podcast’s id

    3.1.2. Implementation

    For the sake of simplicity, a podcast will have only the following properties:

    • id – uniquely identifies the podcast
    • feed – url feed of the podcast
    • title – title of the podcast
    • linkOnPodcastpedia – where you can find the podcast on Podcastpedia.org
    • description – a short description of the podcast

    I could have used only one Java class for the representation of the podcast resource in the code, but in that case the class and its properties/methods would have gotten cluttered with both JPA and XML/JAXB/JSON annotations. I wanted to avoid that and I used two representations which have pretty much the same properties instead:

    • PodcastEntity.java – JPA annotated class used in the DB and business layers
    • Podcast.java – JAXB/JSON annotated class used in the facade and business layers

    Note: I am still trying to convince myself that this is the better approach, so if you have a suggestion on this please leave a comment.

    The Podcast.java classs look something like the following:

    package org.codingpedia.demo.rest.resource;
    
        //imports omitted for brevity
    
        /**
         * Podcast resource placeholder for json/xml representation
         *
         * @author ama
         *
         */
        @SuppressWarnings("restriction")
        @XmlRootElement
        @XmlAccessorType(XmlAccessType.FIELD)
        public class Podcast implements Serializable {
    
            private static final long serialVersionUID = -8039686696076337053L;
    
            /** id of the podcast */
            @XmlElement(name = "id")    
            private Long id;
            
            /** title of the podcast */
            @XmlElement(name = "title")    
            private String title;
                
            /** link of the podcast on Podcastpedia.org */
            @XmlElement(name = "linkOnPodcastpedia")    
            private String linkOnPodcastpedia;
            
            /** url of the feed */
            @XmlElement(name = "feed")    
            private String feed;
            
            /** description of the podcast */
            @XmlElement(name = "description")
            private String description;
                
            /** insertion date in the database */
            @XmlElement(name = "insertionDate")
            @XmlJavaTypeAdapter(DateISO8601Adapter.class)    
            @PodcastDetailedView
            private Date insertionDate;
    
            public Podcast(PodcastEntity podcastEntity){
                try {
                    BeanUtils.copyProperties(this, podcastEntity);
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            
            public Podcast(String title, String linkOnPodcastpedia, String feed,
                    String description) {
                
                this.title = title;
                this.linkOnPodcastpedia = linkOnPodcastpedia;
                this.feed = feed;
                this.description = description;
                
            }
            
            public Podcast(){}
    
        //getters and setters now shown for brevity
        }
      
    

    and translates into the following JSON representation, which is actually the de facto media type used with REST nowadays:

    {
        	"id":1,
        	"title":"Quarks & Co - zum Mitnehmen-modified",
        	"linkOnPodcastpedia":"https://github.com/CodepediaOrg/podcastpedia/podcasts/1/Quarks-Co-zum-Mitnehmen",
        	"feed":"https://podcast.wdr.de/quarks.xml",
        	"description":"Quarks & Co: Das Wissenschaftsmagazin",
        	"insertionDate":"2014-05-30T10:26:12.00+0200"
        }
    

    Even though JSON is becoming more and more the preffered representation in REST APIs, you shouldn’t neglect the XML representation, as most of the systems still use XML format for communication with other parties.

    The good thing is that in Jersey you can kill two rabbits with one shot – with JAXB beans (as used above) you will be able to use the same Java model to generate JSON as well as XML representations. Another advantage is simplicity of working with such a model and availability of the API in Java SE Platform.

    Note: Most of the methods defined in this tutorial will produce and consume also the application/xml media type, with application/json being the preferred way.

    3.2. Methods

    Before I present you the API, let me to tell you that

    • Create = POST
    • Read = GET
    • Update = PUT
    • Delete = DELETE

    and is not a strict 1:1 mapping. Why? Because you can also use PUT for Creation and POST for Update. This will be explained and demonstrated in the coming paragraphs.

    Note: For Read and Delete it is pretty clear, they map indeed one to one with the GET and DELETE HTTP operations. Anyway REST is an architectural style, is not a specification and you should adapt the architecture to your needs, but if you want to make your API public and have somebody willing to use it, you should follow some “best practices”.

    As already mentioned the PodcastRestResource class is the one handling all the rest requests:

    
        package org.codingpedia.demo.rest.resource;
        //imports
        ......................
        @Component
        @Path("/podcasts")
        public class PodcastResource {
            @Autowired
            private PodcastService podcastService;
            .....................
        }
      
    

    Notice the @Path("/podcasts") before the class definition – everything related to podcast resources will occur under this path. The @Path annotation’s value is a relative URI path. In the example above, the Java class will be hosted at the URI path /podcasts. The PodcastService interface exposes the business logic to the REST facade layer.

    Code alert: You can find the entire content of the class on GitHub – PodcastResource.java. We’ll be going through the file step by step and explain the different methods corresponding to the different operations.

    3.2.1. Create podcast(s)

    3.2.1.1. Design

    While the “most known” way for resource creation is by using POST, As mentioned before to create a new resource I could use both the POST and PUT methods, and I did just that:

      Description   URI   HTTP method
      HTTP Status response
     Add new podcast  /podcasts/

    POST

    201 Created

     Add new podcast (all values must be sent)  /podcasts/{id}

    PUT

    201 Created

    The big difference between using POST (not idempotent)

    “The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line[…] If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header” [1]

    and PUT (idempotent)

    “The PUT method requests that the enclosed entity be stored under the supplied Request-URI […] If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI. If a new resource is created, the origin server MUST inform the user agent via the 201 (Created) response.” [1]

    , is that for PUT you should know beforehand the location where the resource will be created and send all the possible values of the entry.

    3.2.1.2. Implementation
    3.2.1.2.1. Create a single resource with POST
    /**
         * Adds a new resource (podcast) from the given json format (at least title
         * and feed elements are required at the DB level)
         *
         * @param podcast
         * @return
         * @throws AppException
         */
        @POST
        @Consumes({ MediaType.APPLICATION_JSON })
        @Produces({ MediaType.TEXT_HTML })
        public Response createPodcast(Podcast podcast) throws AppException {
        	Long createPodcastId = podcastService.createPodcast(podcast);
        	return Response.status(Response.Status.CREATED)// 201
        			.entity("A new podcast has been created")
        			.header("Location",
        					"http://localhost:8888/demo-rest-jersey-spring/podcasts/"
        							+ String.valueOf(createPodcastId)).build();
        }

    Annotations

    • @POST – indicates that the method responds to HTTP POST requests
    • @Consumes({MediaType.APPLICATION_JSON}) – defines the media type, the method accepts, in this case "application/json"
    • @Produces({MediaType.TEXT_HTML}) – defines the media type) that the method can produce, in this case "text/html".

    Response

    • on success: text/html document, with a HTTP status of 201 Created, and a Location header specifying where the resource has been created
    • on error:
      • 400 Bad request if not enough data is provided
      • 409 Conflict if on the server side is determined a podcast with the same feed exists
    3.2.1.2.2. Create a single resource (“podcast”) with PUT

    This will be treated in the Update Podcast section below.

    3.2.1.2.3. Bonus – Create a single resource (“podcast”) from form
    /**
       * Adds a new podcast (resource) from "form" (at least title and feed
       * elements are required at the DB level)
       *
       * @param title
       * @param linkOnPodcastpedia
       * @param feed
       * @param description
       * @return
       * @throws AppException
       */
      @POST
      @Consumes({ MediaType.APPLICATION_FORM_URLENCODED })
      @Produces({ MediaType.TEXT_HTML })
      @Transactional
      public Response createPodcastFromApplicationFormURLencoded(
      		@FormParam("title") String title,
      		@FormParam("linkOnPodcastpedia") String linkOnPodcastpedia,
      		@FormParam("feed") String feed,
      		@FormParam("description") String description) throws AppException {
    
      	Podcast podcast = new Podcast(title, linkOnPodcastpedia, feed,
      			description);
      	Long createPodcastid = podcastService.createPodcast(podcast);
    
      	return Response
      			.status(Response.Status.CREATED)// 201
      			.entity("A new podcast/resource has been created at /demo-rest-jersey-spring/podcasts/"
      					+ createPodcastid)
      			.header("Location",
      					"http://localhost:8888/demo-rest-jersey-spring/podcasts/"
      							+ String.valueOf(createPodcastid)).build();
      }
    

    Annotations

    • @POST – indicates that the method responds to HTTP POST requests
  • @Consumes({MediaType.APPLICATION_FORM_URLENCODED})– defines the media type, the method accepts, in this case"application/x-www-form-urlencoded"
    • @FormParam – present before the input parameters of the method, this annotation binds the value(s) of a form parameter contained within a request entity body to a resource method parameter. Values are URL decoded unless this is disabled using the Encoded annotation
  • @Produces({MediaType.TEXT_HTML}) – defines the media type that the method can produce, in this case “text/html”. The response will be a html document, with a status of 201, indicating to the caller that the request has been fulfilled and resulted in a new resource being created.
  • Response

    • on success: text/html document, with a HTTP status of 201 Created, and a Location header specifying where the resource has been created
    • on error:
      • 400 Bad request if not enough data is provided
      • 409 Conflict if on the server side is determined a podcast with the same feed exists

    3.2.2. Read podcast(s)

    3.2.2.1. Design

    The API supports two Read operations:

    • return a collection of podcasts
    • return a podcast identified by id
     Description  URI  HTTP method
     HTTP Status response
     Return all podcasts  /podcasts/?orderByInsertionDate={ASC|DESC}&numberDaysToLookBack={val}

    GET

    200 OK

     Add new podcast (all values must be sent)  /podcasts/{id}

    GET

    200 OK

    Notice the query parameters for the collection resource – orderByInsertionDate and numberDaysToLookBack. It makes perfect sense to add filters as query parameters in the URI and not be part of the path.

    3.2.2.2. Implementation
    3.2.2.2.1. Read all podcasts (“/”)
    /**
         * Returns all resources (podcasts) from the database
         *
         * @return
         * @throws IOException
         * @throws JsonMappingException
         * @throws JsonGenerationException
         * @throws AppException
         */
        @GET
        @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
        public List<Podcast> getPodcasts(
        		@QueryParam("orderByInsertionDate") String orderByInsertionDate,
        		@QueryParam("numberDaysToLookBack") Integer numberDaysToLookBack)
        		throws JsonGenerationException, JsonMappingException, IOException,
        		AppException {
        	List<Podcast> podcasts = podcastService.getPodcasts(
        			orderByInsertionDate, numberDaysToLookBack);
        	return podcasts;
        }

    Annotations

    *@GET – indicates that the method responds to HTTP GET requests

    • @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) – defines the media type) that the method can produce, in this case either "application/json" or "application/xml"(you need the @XmlRootElement in front of the Podcast class ). The response will be a list of podcasts either in JSON or XML format.

    Response

    • list of podcasts from the database and a HTTP Status of 200 OK
    3.2.2.2.1. Read one podcast
    @GET
        @Path("{id}")
        @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
        public Response getPodcastById(@PathParam("id") Long id)
        		throws JsonGenerationException, JsonMappingException, IOException,
        		AppException {
        	Podcast podcastById = podcastService.getPodcastById(id);
        	return Response.status(200).entity(podcastById)
        			.header("Access-Control-Allow-Headers", "X-extra-header")
        			.allow("OPTIONS").build();
        }

    Annotations

    • @GET – indicates that the method responds to HTTP GET requests
    • @Path("{id}") – identifies the URI path that the class method will serve requests for. The “id” value is an embedded variable making an URI path template. It is used in combination with the @PathParam variable.
    • @PathParam("id") – binds the value of a URI template parameter (“id”) to the resource method parameter. The value is URL decoded unless this is di sabled using the @Encoded annotation. A default value can be specified using the @DefaultValue annotation.
  • @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) – defines the media type) that the method can produce, in this case "application/json" or "application/xml"(you need the @XmlRootElement in front of the Podcast class ).
  • Response

    • on success: requested podcast with a 200 OK HTTP status. The format is either xml or JSON, depending on the Accept -header’s value sent by the client (might bet application/xml or application/json)
    • on error: 404 Not found if the podcast with the given id does not exist in the database

    3.2.3. Update podcast

    3.2.3.1. Design
    Description URI HTTP method
    HTTP Status response
     Update podcast (fully)  /podcasts/{id}

    PUT

    200 OK

     Update podcast (partially)  /podcasts/{id}

    POST

    200 OK

    In the REST arena you will be doing two kind of updates:

    1. full updates – that is where you will provide all the
    2. partial updates – when only some properties will be sent over the wire for update

    For full updates, it’s pretty clear you can use the PUT method and you are conform the method’s specification in the RFC 2616.

    Now for the partial update there’s a bunch of proposals/debate on what to use:

    1. via PUT
    2. via POST
    3. via PATCH

    Let me tell why I consider the first option (with PUT) is a NO GO. Well, accordingly to the specification

    If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server.“[1]

    if I would like to update just the title property of the podcast with the id 2

    PUT http://localhost:8888/demo-rest-jersey-spring/podcasts/2 HTTP/1.1
        Accept-Encoding: gzip,deflate
        Content-Type: application/json
        Content-Length: 155
        Host: localhost:8888
        Connection: Keep-Alive
        User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
    
        {
        	"title":"New Title"
        }
    

    then, accordingly to the specification the resource “stored” at the location should have only id and title, and clearly my intent was not that.

    The second option via POST… well we can “abuse” this one and that is exactly what I did in the implementation, but it does not seem conform to me, because the spec for POST states:

    The posted entity is subordinate to that URI in the same way that a file is subordinate to a directory containing it, a news article is subordinate to a newsgroup to which it is posted, or a record is subordinate to a database.“[1]

    That does not look like a partial update case to me…

    The third option is to use PATCH,  and I guess this is the main reason the method came to life:

    “Several applications extending the Hypertext Transfer Protocol (HTTP)
       require a feature to do partial resource modification.  The existing
       HTTP PUT method only allows a complete replacement of a document.
       This proposal adds a new HTTP method, PATCH, to modify an existing
       HTTP resource.”[2]

    I am pretty sure this will be used in the future for partial updates, but since is not yet part of the specification and not yet implemented in Jersey I chose to use the second option with POST for this demo. If you really want to implement partial update in Java with the PATCH check out this post  – Transparent PATCH support in JAX-RS 2.0

    3.2.3.1. Implementation
    3.2.3.1.1. Full Update
    @PUT
        @Path("{id}")
        @Consumes({ MediaType.APPLICATION_JSON })
        @Produces({ MediaType.TEXT_HTML })
        public Response putPodcastById(@PathParam("id") Long id, Podcast podcast)
        		throws AppException {
    
        	Podcast podcastById = podcastService.verifyPodcastExistenceById(id);
    
        	if (podcastById == null) {
        		// resource not existent yet, and should be created under the
        		// specified URI
        		Long createPodcastId = podcastService.createPodcast(podcast);
        		return Response
        				.status(Response.Status.CREATED)
        				// 201
        				.entity("A new podcast has been created AT THE LOCATION you specified")
        				.header("Location",
        						"http://localhost:8888/demo-rest-jersey-spring/podcasts/"
        								+ String.valueOf(createPodcastId)).build();
        	} else {
        		// resource is existent and a full update should occur
        		podcastService.updateFullyPodcast(podcast);
        		return Response
        				.status(Response.Status.OK)
        				// 200
        				.entity("The podcast you specified has been fully updated created AT THE LOCATION you specified")
        				.header("Location",
        						"http://localhost:8888/demo-rest-jersey-spring/podcasts/"
        								+ String.valueOf(id)).build();
        	}
        }
    

    Annotations

    • @PUT – indicates that the method responds to HTTP PUT requests
    • @Path("{id}") – identifies the URI path that the class method will serve requests for. The “id” value is an embedded variable making an URI path template. It is used in combination with the @PathParam variable.
      • @PathParam("id") – binds the value of a URI template parameter (“id”) to the resource method parameter. The value is URL decoded unless this is di sabled using the @Encoded annotation. A default value can be specified using the @DefaultValue annotation.
    • @Consumes({MediaType.APPLICATION_JSON}) – defines the media type, the method accepts, in this case "application/json"
    • @Produces({MediaType.TEXT_HTML}) – defines the media type) that the method can produce, in this case “text/html”.

    will be a html document containing different messages and stati depending on what action has been taken

    Respsonse

    • on creation

    • on success: 201 Created and in the Location header the specified location where the resource was created
    • on error: 400 Bad request if the minimum required properties are not provided for insertion

    • on full update
      • on success: 200 OK
      • on error: 400 Bad Request if not all properties are provided
    3.2.3.1.2. Partial Update
    //PARTIAL update
        @POST
        @Path("{id}")
        @Consumes({ MediaType.APPLICATION_JSON })
        @Produces({ MediaType.TEXT_HTML })
        public Response partialUpdatePodcast(@PathParam("id") Long id, Podcast podcast) throws AppException {
        	podcast.setId(id);
        	podcastService.updatePartiallyPodcast(podcast);
        	return Response.status(Response.Status.OK)// 200
        			.entity("The podcast you specified has been successfully updated")
        			.build();
        }

    Annotations

    • <code>@POST</code> – indicates that the method responds to HTTP POST requests
    • @Path("{id}") – identifies the URI path that the class method will serve requests for. The “id” value is an embedded variable making an URI path template. It is used in combination with the @PathParam variable.
      • @PathParam("id") – binds the value of a URI template parameter (“id”) to the resource method parameter. The value is URL decoded unless this is di sabled using the @Encoded annotation. A default value can be specified using the @DefaultValue annotation.
    • @Consumes({MediaType.APPLICATION_JSON}) – defines the media type, the method accepts, in this case "application/json"
    • @Produces({MediaType.TEXT_HTML}) – defines the media type) that the method can produce, in this case "text/html".

    Response

    • on success: 200 OK
    • on error: 404 Not Found, if there is no resource anymore available at the provided location

    3.2.4. Delete podcast

    3.2.4.1. Design
    Description URI HTTP method
    HTTP Status response
     Removes all podcasts  /podcasts/

    DELETE

    204 No content

     Removes podcast at the specified location  /podcasts/{id}

    DELETE

    204 No content

    3.2.4.2. Implementation
    3.2.4.2.1. Delete all resources
    @DELETE
        @Produces({ MediaType.TEXT_HTML })
        public Response deletePodcasts() {
        	podcastService.deletePodcasts();
        	return Response.status(Response.Status.NO_CONTENT)// 204
        			.entity("All podcasts have been successfully removed").build();
        }

    Annotations

    • @DELETE – indicates that the method responds to HTTP DELETE requests
    • @Produces({MediaType.TEXT_HTML}) – defines the media type that the method can produce, in this case “text/html”.

    Response

    • The response will be a html document, with a status of 204 No content, indicating to the caller that the request has been fulfilled.
    3.2.4.2.2. Delete one resource
      @DELETE
        @Path("{id}")
        @Produces({ MediaType.TEXT_HTML })
        public Response deletePodcastById(@PathParam("id") Long id) {
        	podcastService.deletePodcastById(id);
        	return Response.status(Response.Status.NO_CONTENT)// 204
        			.entity("Podcast successfully removed from database").build();
        }

    Annotations

    • @DELETE – indicates that the method responds to HTTP DELETE requests
  • @Path("{id}") – identifies the URI path that the class method will serve requests for. The “id” value is an embedded variable making an URI path template. It is used in combination with the @PathParam variable.
    • @PathParam("id") – binds the value of a URI template parameter (“id”) to the resource method parameter. The value is URL decoded unless this is di sabled using the @Encoded annotation. A default value can be specified using the @DefaultValue annotation.
  • @Produces({MediaType.TEXT_HTML}) – defines the media type that the method can produce, in this case “text/html”.
  • Response

    • on success: if the podcast is removed a 204 No Content success status is returned
    • on error: podcast is not available anymore and status of 404 Not found is returned

    4. Logging

    Every request’s path and the response’s entity will be logged when the logging level is set to DEBUG. It is developed like a wrapper, AOP-style functionality with the help of Jetty filters.

    See my post How to log in Spring with SLF4J and Logback for more details on the matter.

    5. Exception handling

    In case of errros, I decided to response with unified error message structure.  Here’s an example how an error response might look like:

    {
           "status": 400,
           "code": 400,
           "message": "Provided data not sufficient for insertion",
           "link": "https://www.codepedia.org/ama/tutorial-rest-api-design-and-implementation-with-jersey-and-spring",
           "developerMessage": "Please verify that the feed is properly generated/set"
        }

    A detailed explanation of how errors are handled in the REST api, you can find in the following post Error handling in REST API with Jersey

    5.1. Custom Reason Phrase in HTTP status error message response with JAX-RS (Jersey)

    If for some reason you need to produce a custom Reason Phrase in the HTTP status response delivered when an error occurs than you might want to check the following post

    https://www.codepedia.org/ama/custom-reason-phrase-in-http-status-error-message-response-with-jax-rs-jersey/

    6. Add CORS support on the server side

    I extended the capabilities of the API developed for the tutorial to support Cross-Origing Resource Sharing (CORS) on the server side.

    Please see my post How to add CORS support on the server side in Java with Jersey for more details on the matter.

    7. Testing

    7.1. Integration tests in Java

    To test the application I will use the Jersey Client and execute requests against a running Jetty server with the application deployed on it. For that I will use the Maven Failsafe Plugin.

    7.1.1. Configuration

    7.1.1.1 Jersey client dependency

    To build a Jersey client the jersey-client jar is required in the classpath. With Maven you can add it as a dependency to the pom.xml file:

    <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-client</artifactId>
            <version>${jersey.version}</version>
            <scope>test</scope>
        </dependency>
    7.1.1.2. Failsafe plugin

    The Failsafe Plugin is used during the integration-test and verify phases of the build lifecycle to execute the integration tests of the application. The Failsafe Plugin will not fail the build during the integration-test phase thus enabling the post-integration-test phase to execute.
    To use the Failsafe Plugin, you need to add the following configuration to your pom.xml

    <plugins>
        	[...]
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>2.16</version>
                <executions>
                    <execution>
                        <id>integration-test</id>
                        <goals>
                            <goal>integration-test</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>verify</id>
                        <goals>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        	[...]
        </plugins>
    7.1.1.2. Jetty Maven Plugin

    The integration tests will be executed against a running jetty server, that will be started only for the execution of the tests. For that you have to configure the following executionin the jetty-maven-plugin:

    <plugins>
        	<plugin>
        		<groupId>org.eclipse.jetty</groupId>
        		<artifactId>jetty-maven-plugin</artifactId>
        		<version>${jetty.version}</version>
        		<configuration>
        			<jettyConfig>${project.basedir}/src/main/resources/config/jetty9.xml</jettyConfig>
        			<stopKey>STOP</stopKey>
        			<stopPort>9999</stopPort>
        			<stopWait>5</stopWait>
        			<scanIntervalSeconds>5</scanIntervalSeconds>
        		[...]
        		</configuration>
        		<executions>
        			<execution>
        				<id>start-jetty</id>
        				<phase>pre-integration-test</phase>
        				<goals>
        					<!-- stop any previous instance to free up the port -->
        					<goal>stop</goal>
        					<goal>run-exploded</goal>
        				</goals>
        				<configuration>
        					<scanIntervalSeconds>0</scanIntervalSeconds>
        					<daemon>true</daemon>
        				</configuration>
        			</execution>
        			<execution>
        				<id>stop-jetty</id>
        				<phase>post-integration-test</phase>
        				<goals>
        					<goal>stop</goal>
        				</goals>
        			</execution>
        		</executions>
        	</plugin>
        	[...]
        </plugins>

    Note: In the pre-integration-test phase the Jetty server will be started, after stopping any running instance to free up the port, and in the post-integration-phase it will be stopped. The scanIntervalSeconds has to be set to 0, and daemon to true.

    Code alert: Find the complete pom.xml file on GitHub

    7.1.2. Build the integration tests

    I am using JUnit as the testing framework. By default, the Failsafe Plugin will automatically include all test classes with the following wildcard patterns:

    • <tt>"**/IT*.java"</tt> – includes all of its subdirectories and all java filenames that start with “IT”.
    • <tt>"**/*IT.java"</tt> – includes all of its subdirectories and all java filenames that end with “IT”.
    • <tt>"**/*ITCase.java"</tt> – includes all of its subdirectories and all java filenames that end with “ITCase”.

    I have created a single test class – RestDemoServiceIT – that will test the read (GET) methods, but the procedure should be the same for all the other:

    public class RestDemoServiceIT {
    
        	[....]
        	@Test
        	public void testGetPodcast() throws JsonGenerationException,
        			JsonMappingException, IOException {
    
        		ClientConfig clientConfig = new ClientConfig();
        		clientConfig.register(JacksonFeature.class);
    
        		Client client = ClientBuilder.newClient(clientConfig);
    
        		WebTarget webTarget = client
        				.target("http://localhost:8888/demo-rest-jersey-spring/podcasts/2");
    
        		Builder request = webTarget.request(MediaType.APPLICATION_JSON);
    
        		Response response = request.get();
        		Assert.assertTrue(response.getStatus() == 200);
    
        		Podcast podcast = response.readEntity(Podcast.class);
    
        		ObjectMapper mapper = new ObjectMapper();
        		System.out
        				.print("Received podcast from database *************************** "
        						+ mapper.writerWithDefaultPrettyPrinter()
        								.writeValueAsString(podcast));
    
        	}
        }

    Note:

    • I had to register the JacksonFeature for the client too so that I can marshall the podcast response in JSON format – response.readEntity(Podcast.class)
    • I am testing against a running Jetty on port 8888 – I will show you in the next section how to start Jetty on a desired port
    • I am expecting a 200 status for my request
    • With the help org.codehaus.jackson.map.ObjectMapper I am displaying the JSON response pretty formatted

    7.1.3. Running the integration tests

    The Failsafe Plugin can be invoked by calling the <tt>verify</tt> phase of the build lifecycle.

    mvn verify

    To start jetty on port 8888 you need to set the jetty.port property to 8888. In Eclipse I use the following configuration:

    Run integration tests from Eclipse

    Run integration tests from Eclipse

    7.2. Integration tests with SoapUI

    Recently I’ve rediscovered SoapUI after using it heavily for testing SOAP based web services. With the recent versions (at the time of writing latest is 5.0.0) it offers pretty good functionality to test REST based web services, and coming versions should improve on this. So unless you develop your own framework/infrastructure to test REST services, why not give it a try to SoapUI. I did, I was satisfied with the results so far and I’ve decided to do a video tutorial, that you can now find on YouTube on our channel:

    8. Versioning

    There are three major possibilities

    1. URL:  “/v1/podcasts/{id}”
    2. Accept/Content-type header: application/json; version=1

    Because I am a developer and not a RESTafarian yet I would do the URL option. All I would have to do on the implementation side for this example, would be to modify the @Path‘s value annotation on the PodcastResource class from to

    @Component
    @Path("/v1/podcasts")
    public class PodcastResource {...}

    Of course on a production application, you wouldn’t want every resource class preprefixed with the version number,  you’d want the version somehow treated through a filter in a AOP manner. Maybe something like this will come in a following post…

    Here are some great resources from people that understand better the matter:

    9. Compression

    There may be cases when your REST api provides responses that are very long, and we all know how important transfer speed and bandwidth still are on mobile devices/networks. I think this is the first performance optimization point one needs to address, when developing REST apis that support mobile apps. Guess what? Because responses are text, we can compress them. And with today’s power of smartphones and tablets uncompressing them on the client side should not be a big deal…

    On the server side, you can easily compress every response with Jersey’s EncodingFilter, which is a container filter that supports enconding-based content configuration. The filter examines what content encodings are supported by the container  and decides what encoding should be chosen based on the encodings listed in the Accept-Encoding request header and their associated quality values. To use particular content en/decoding, you need to register one or more content encoding providers  that provide specific encoding support; currently there are providers for GZIP and DEFLATE coding.

    All I had to do in this case is adding the following line of code to my JaxRs application class:

    public class RestDemoJaxRsApplication extends ResourceConfig {
        	/**
        	 * Register JAX-RS application components.
        	 */
        	public RestDemoJaxRsApplication() {
    
                packages("org.codingpedia.demo.rest");
        		register(EntityFilteringFeature.class);
        		EncodingFilter.enableFor(this, GZipEncoder.class);
    
        	}
        }

    10. Quick way to check if the REST API is alive

    There might be cases when you want to quickly verify if your REST API, that is deployed either on dev, test or prod environments, is reachable altogether. A common way to do this is by building a generic resource that delivers for example the version of the deployed API. You can trigger a request to this resource manually or, even better, have a Jenkings/Hudson job, which runs a checkup job after deployment. In the post Quick way to check if the REST API is alive – GET details from Manifest file, I present how to implement such a service that reads default implementation and specification details from the application’s manifest file.

    11. Security

    11.1. Basic Authentication

    Spring Security Framework’s powerful features were used to secure some REST resources (e.g. “/manifest”) with Basic Authentication. Read my following post How to secure Jersey REST services with Spring Security and Basic authentication to find out how.

    12. Summary

    Well, that’s it. I have to congratulate you, if you’ve come so far, but I hope you could learn something from this tutorial about REST, like designing a REST API, implementing a REST API in Java, testing a REST API and much more. If you did, I’d be very grateful if you helped it spread by leaving a comment or sharing it on Twitter, Google+ or Facebook. Thank you! Don’t forget also to check out Podcastpedia.org – you’ll find for sure interesting podcasts and episodes. We are grateful for your support.

    Resources

    Source Code

    Web resources

    1. HTTP – Hypertext Transfer Protocol — HTTP/1.1 – RFC2616
    2. rfc5789 – PATCH Method for HTTP
    3. Jersey User Guide
    4. HTTP Status Code Definitions
    5. REST – https://en.wikipedia.org/wiki/Representational_State_Transfer
    6. CRUD – https://en.wikipedia.org/wiki/Create,_read,_update_and_delete
    7. Java API for RESTful Services (JAX-RS)
    8. Jersey – RESTful Web Services in Java
    9. HTTP PUT, PATCH or POST – Partial updates or full replacement?
    10. Transparent PATCH support in JAX-RS 2.0
    11. Maven Failsafe Plugin
    12. Maven Failsafe Plugin Usage
    13. SoapUI 5.0 released today!
    14. SoapUI – Using Script Assertions
    15. [Video] REST+JSON API Design – Best Practices for Developers
    16. [Video] RESTful API Design – Second Edition
    17. Law of Demeter
    Podcastpedia image

    Adrian Matei

    Creator of Podcastpedia.org and Codepedia.org, computer science engineer, husband, father, curious and passionate about science, computers, software, education, economics, social equity, philosophy - but these are just outside labels and not that important, deep inside we are all just consciousness, right?
    Subscribe to our newsletter for more code resources and news

    Adrian Matei (aka adixchen)

    Adrian Matei (aka adixchen)
    Life force expressing itself as a coding capable human being

    routerLink with query params in Angular html template

    routerLink with query params in Angular html template code snippet Continue reading