You are on page 1of 121

Restful Services

Page 1 of 121
Table of Contents (Use Ctrl+Left Click of Mouse to go to topic)
“Rest WebServices”...................................................................................................................................5
 01 Section 1 - Introduction.........................................................................................................5
 02 REST and HTTP.....................................................................................................................6
 Addresses................................................................................................................................6
 HTTP Methods........................................................................................................................7
 Metadata..................................................................................................................................7
 HTTP Status Codes..............................................................................................7
 Message Headers..................................................................................................7
 Content-Type...............................................................................................7
 Summary.................................................................................................................................7
 03 Resource URIs........................................................................................................................7
 URI..........................................................................................................................................7
 Resource Relations................................................................................................................10
 04 Collection URIs....................................................................................................................13
 05 HTTP Methods.....................................................................................................................15
 06 Method Idempotence............................................................................................................21
 07 Rest Response.......................................................................................................................26
 08 HATEOS...............................................................................................................................31
 09 The Richardson Maturity Model..........................................................................................35
 10 Section 2 - What is JAX-RS.................................................................................................37
 11 Setting Up.............................................................................................................................38
 12 Understanding the Application Structure.............................................................................41
 13 Creating a Resource..............................................................................................................42
 14 Returning XML Response....................................................................................................42
 15 Installing a REST Api Client................................................................................................45
 16 Building Service Stubs.........................................................................................................45
 17 Accessing Path Params.........................................................................................................47
 18 Returning JSON Response...................................................................................................48
 19 Implementing POST Method................................................................................................49
 20 Implementing Update and Delete.........................................................................................51
 21 Implementing Profile Resource............................................................................................54
Page 2 of 121
 22 Pagination and Filtering........................................................................................................57
 23 The Param Annotations........................................................................................................60
 24 Using Context and BeanParam Annotations........................................................................61
 25 Implementing SubResources................................................................................................64
 26 Sending Status Codes and Location Headers.......................................................................71
 27 Handling Exceptions.............................................................................................................73
 28 Using WebApplicationException.........................................................................................79
 29 HATEOS (Part 1) & 30 HATEOS (Part 2)...........................................................................82
 31 Content Negotiation..............................................................................................................86
“Rest WebServices - Advanced”..............................................................................................................89
 Unit 1 :: The Application Class.................................................................................................89
 01 Introduction.....................................................................................................................89
 02 Tomcat Setup..................................................................................................................89
 03 Setting up pom.xml.........................................................................................................89
 04 Setting up a JAX-RS application....................................................................................90
 05 The Application Class.....................................................................................................92
 Unit 1 :: JAX-RS Extensions.....................................................................................................92
 06 Resource Lifecycle..........................................................................................................92
 07 Param Annotations and Member Variables.....................................................................93
 08 Param Converters............................................................................................................95
 09 Implementing Custom Param Converters.......................................................................97
 10 MessageBodyReaders and MessageBodyWriters...........................................................99
 11 Implementing a MessageBodyWriter............................................................................101
 12 Custom Media Types....................................................................................................103
 Unit 3 :: JAX-RS Client...........................................................................................................104
 13 JAX-RS Client..............................................................................................................104
 14 Writing a JAX-RS Client & 17 Making a Post Request..............................................105
 18 Creating Invocations.....................................................................................................106
 19 Handling Generic Types................................................................................................107
 Unit 4 :: Rest Api Authentication............................................................................................109
 20 Unit Introduction...........................................................................................................109
 21 Implementing Filters.....................................................................................................109
 22 Rest API Authentication Mechanisms...........................................................................112
Page 3 of 121
 Basic Auth........................................................................................................113
 23 Sending Basic Auth Requests.......................................................................................115
 HMAC.................................................................................................................................116
 In Java...............................................................................................................116
 In Python..........................................................................................................116
 24 Implementing Rest API Authorization..........................................................................116
 Unit 5 :: Odds and Ends...........................................................................................................118
 25 Filters and Interceptors..................................................................................................118
 26 JAX-RS and EJBs.........................................................................................................118
 27 Conclusion....................................................................................................................118

Page 4 of 121
“Rest WebServices”
 01 Section 1 - Introduction
 One – Many Relationship Examples
o One Person has many Addresses
o One Owner has many Pets
o One Owner has many Vechicles
o One Vendor has many Inventory

CREATE TABLE Vendor(


VendorNumber number(4) PRIMARY KEY,
Name varchar2(20),
Address varchar2(20),
City varchar2(15),
Street varchar2(2),
ZipCode varchar2(10),
Contact varchar2(16),
PhoneNumber varchar2(12),
Status varchar2(8),
StampDate date
);

CREATE TABLE Inventory(


Item varchar2(6) PRIMARY KEY,
Description varchar2(30),
CurrentQuantity number(4) NOT NULL,
VendorNumber number(2) REFERENCES Vendor(VendorNumber),
ReorderQuantity number(3) NOT NULL
);

o One Customer has many Orders


o One Student can work on nany Projects but it can also be vice versa.

Page 5 of 121
 02 REST and HTTP
 Addresses

 Addresses are resource based and not action based.


o For eg. A weather lookup based on zip code based
 on action could look as shown below


 This is more action based since it says that there is a weatherLookup.do
 Which takes a zipcode with value 12345 and returns the weather details
 The resource based weather lookup would be as shown below

 You are not making the server do anything, rather just looking up
something that already exists.
 Ideally we are saying weatherapp.com get me the weather for zipcode
12345
 As if that thing is already there and we are just looking it up based on the
zipcode, rather than doing any processing.
 The server might be processing but the url doesn’t show that.
 The weather lookup for a country could be
Page 6 of 121

 HTTP Methods
 GET, POST, PUT, DELETE

 Metadata
HTTP Status Codes
 When you make a request to an ADDRESS along with the HTTP Method you get back some
data and along with the data you get back some METADATA like Http Status Codes.
o That indicates a response was successful or an error.
o Popular examples are –
 200 -- Ok
 500 – Server Error
 404 – Resource Not Found Error.
Message Headers
 Content-Type
 The format the message is in.
 Common examples are
o text/xml -- for xml content
o application/json -- for json content
 Summary

 03 Resource URIs
 URI
 The Messenger application we are going to build would have some of the features shown below

Page 7 of 121
 The ER Diagram is as shown below

 Let’s look at the URIs we would design for this application.


o Typical URLs to Get a message with id 10 would be as shown below

o Because this is a web application, the end user would be presented a page and he would
navigate from one page to another using hyperlink and hence the URL in a typical web
application can be anything as shown above.
o Where as in a REST Api the developer would have to know the URL to retrieve the code
Page 8 of 121
so the convention or Resource URI takes extreme importance.
 So think of these Resource URLs as equivalent to the old fashioned URLs
referring to static pages.
 Say I have 4 static profile pages as shown below

 I will not put them in the root directory, so I will create a profiles directory and
put them there as shown below

 So if I have to access the profile of koushik, the url would be


profiles/koushik.html.
 Now drop the .html extension you would have the Resource Based URI

 To make it generic the same URL would be as shown below

 The message URL would be as shown below

Page 9 of 121
 Notice that these URI contains nouns and not verbs.
 The second thing is the first part of the URLs are plural not singular-profiles and
messasges


 Resource Relations

 One Message can have multiple comments. It is 1-M relationship.


 URI for comments would be

 Thinking in terms of static pages.

Page 10 of 121
o I would have a messasges folder and within it I would have 1.html and 2.html

o Now when I store the comments I will not put all the comments in a single folder. I will
lose the association between the Messages and Comments.
o So what I would ideally do is create a sub-folder under the messages folder to store the
comments

o If I have to access comment 2 of message 1 then the URL would be as shown below.

o Similarly other URLs

Page 11 of 121
o The 1-M relationship
 The 1 side would be the root of the url and the MANY side would be inside.
 The Message-Comments is 1-M
 The url has the messages first followed by the messageId followed by the
many part the comments followed by the commented.

 Now you might be wondering why Messages are not related to Profiles. Why are they both first
level entities?
o If you think about it Messages are posted by somebody with a Profile.
o So there is a 1-M relationship between Profile and Messages.
o So you could have a messages URL as shown below

o But in this case, I decided to have them separate. I decided to have a Message
independent of the Profile. Because I felt that they are not tightly coupled together as
Messages and Comments or Messages or Likes or Shares.
o The client would only read a message and not bother who the author of the message is
when they are accessing the message.
o I wanted them to say hello messageId now give me the message
o Without having to know who the author is – profileId.
o This depends on the business you are modeling and should be taken on a case by case
Page 12 of 121
basis.
 04 Collection URIs
 Resource URIs are of 2 types
o Instance Resource URIs
o Collection URIs
 Instance Resource URIs

o They return a single instance of comment or like or share or message or profile.


 Getting a message

 Getting all the messages

o This is analogous to a simple static site with html pages.


 Accessing a directory would give you all the files present in the directory.


 So /messages is like accessing the top level directory which would give you all
the messages.
 Getting all comments for message id 2

Page 13 of 121
o
 These URIs are different from the URIs we saw in the previous section

o These do not represent a single resource.


o They represent a collection/list of resources.
o So they are called Collection URIs.
o Some more examples of Collection URIs.

o How about getting all the comments?

Page 14 of 121
 If you think you need all the comments then you will not be able to filter the
comments based on the message as the comment table would not have a
messageId.
 And if the comment is associated to a messageId then it makes no sense to fetch
all the comments irrespective of the messageId.
 But then who would want all the comments. None.
 So this is a trade off you need to make while designing your system.

 Filtering collection URIs


o Use query parameters.
o Pagination

o Retrieve all the message for year 2014

o Retrieve all the message for the year 2014 and return the 4th page with page size 10.

 05 HTTP Methods
 We have seen action based URIs and its equivalent resource based URIs.

Page 15 of 121
 Now look at the action based uri and its equivalent resource based uri

o We don’t have the get in the resource based.


o You could argue that “/messages/10” would fetch the message with id 10.
o But then what about the following URIs

o Or the following URI

o They all would have the same URI


 The get message with id 10
Page 16 of 121
 The delete message with id 10
 The post message with id 10
 So how can the same URI do these different operations?
 How does it know when to do what? The answer is HTTP Methods.


 Some examples
o Getting a message

Page 17 of 121
o Updating a message

o Deleting a message with id 10

Page 18 of 121
o Creating a message
 Would probably have the URL /messages/{messageId}
 But this is a new message and so it will not have an Id associated with it.
 The application would associate the id only after the resource has been created.
 So for creating a new message we would not have the messageId. So the URL
would be as /messages/{messageId}
 So the URI would be /message  the plural form similar to getting all the
messages.

 Once the resource has been created the id of the resource would be returned back
so that for subsequent access to the message we would have the id.
 So the response would be as shown below.

Page 19 of 121
 Collection URI scenarios
o The same concepts would apply to the collection uri scenarios

o Post and Put

Page 20 of 121
o All the above examples are for CRUD operations,
 but what if you need something like archive or run a service job, etc.
 What HTTP methods would you use to map to these operations?
 We will see this in a later tutorial…

 06 Method Idempotence
 Let’s categorize the common http methods

o One way of classifying these methods is shown above in the picture.


 The GET is a read-only method which reads the data from the db.
 The other 3 http methods write back data to the db or update the db.
o There is another way of categorizing the http methods

Page 21 of 121
 Now we can say that since the GET is a read-only method it is safe to call it as
many times as we want and nothing changes on the server. So GET method is
safe. It is safe to make multiple GET requests and not worry about the data on
the server.
 The PUT, POST and DELETE methods write/update the data on the server/db.
 So they are not safe to call them multiple times or are they safe?
 Look at the following code example

o Now the second and third assignments they assign a value to


count but in effect they are redundant and have no effect.
Because the count value is 100 on the first assignment and the
second and third assignments don’t alter that.
o So this nature that allows some operations to be repeatable is
important consideration/property.

 Common HTTP Methods

Page 22 of 121
o Get is repeatable without any side effects.
o But what about the DELETE, PUT and POST?
 DELETE Method

 PUT Method

Page 23 of 121
 POST Method

Page 24 of 121
 Wikipedia Defintion of Idempotent

 Client Safeguards

Page 25 of 121
 Another safeguard is when you use GET Http Method the content is cached so
using the appropriate http method is very very essential.

 07 Rest Response
 We have learnt
o where to make the request to (Resource URI) and
o how to make the request (HTTP Method)
o Now let’s look at the response

o For a traditional web application the response would be an HTML Page

o But for a Restful Web Service the response can be anything – for eg. JSON/XML

Page 26 of 121
 Format of an entity class in JSON
o Let’s say we have an entity class called MessageEntity

o The Json response for MessageEntity with id 10

o The XML response for MessageEntity with id 10

o The Json representation and the XML representation are clearly different but they
represent the same resource. They represent a particular instance of a message.

Page 27 of 121
o In other words, the responses are different representations of the same resource.
o So it is very important to remember, when you make REST Api calls you are
sending/receiving representations of the resource.
o Different representations would have different formats even though the underlying
resource is the same.
o That is why REST is called Representational State Transfer. So you are transferring the
Representational State.

o Now how does the client know what format the response is in? If the Rest API can
support any format.
 The client can request the data in a particular format but there is no guarantee
that the server will send the data in that particular format.

Page 28 of 121
 So how the client figure out what all formats are supported by the Restful
Service? Using the Content-Type header of the Http Message.

 When the client receives the content as part of the response it also gets the
content-type header.
 The client examines this header content-type and based on the value it parses the
data and renders the data accordingly either in json or xml, etc.
 Status Codes
o Think about the error message in an application
o In a typical web application we send back a 404 error for Page Not Found and show
some fancy message with colouring, etc.
o In Restful API we need a similar mechanism to tell that client that something went
wrong along with a message or if everything went right.
o We do this using Status Codes.
 This should be the very first line of a http response.
 The status code along with a description.

 The various categories of the Status Codes along with popular status codes in
each cateogy.

Page 29 of 121
 The Status Codes for CRUD Operations

Page 30 of 121
 08 HATEOS
 Hypermedia As The Engine Of Application State.
 No Service definition is required when using HATEOS.
 We visit websites all the time and when was the last time we looked up documentation for a
website.
 We just go to the homepage and we find links to other pages and we just navigate from there.
 What if we can add the hyperlinks to the Response object.

o This is the Response for the message object


 Along with that there is a commentsUri to access the comments of this message.
Page 31 of 121
 There is the likesUri to access the likes for this message.
 There is this sharesUri to access the shares for this message.
o This is similar to hyperlinks in websites.
 Lets walk thru a scenario.

o The json representation for the above Message object would be like this

o When you access the /messages – collection uri you basically get a collection of such
message resources

o You get 3 message represantations as a list.

Page 32 of 121
o If the client was to access the first resource – the message with id 1.
 They need to know what the instance uri is.
 We already designed the uri to be /messages/id
 They have to pick the id from the resource

 So instead of the client preparing the uri manually why not as the service
implementor give the uri in the reponse so that the client and use it directly.

 We may have to provide many links like


 Link to a profile
 Link to the comments of this message
 Link to the shares of this message
 Link to the likes of this message and so on and so forth.

o Now the client does not have to remember the URIs but they have to remember all the
properties or property names of the message resource which can become very difficult

Page 33 of 121
or tedious.
o There must be some better way to do this. Yes and we are going to use the “rel”
attribute which we encounter in html.
 It specifies the relationship between the link document and the current
document.

o The json response with the rel attribute in the links.

o The complete response object for Message resource would be like this.

Page 34 of 121
o The “rel” attribute specifies the relationship of the uri in the href with the message
resource.
 If you do this the client does not have to remember the link property names of
the resource.
 He just has to find the link with the right url from the links and look up the href
for that.
 There are some limitations here
o There is no standard format for specifying the HATEOS links, the above is just one way
from a multitude of different ways.
o The “rel” attribute is part of the http specification – so there are only certain standard
values that are allowed here. Refer the link for more information.

 Summary
o HATEOS is a way to provide links to resources in API response.
o So that the client does not have to construct the URI to fetch something related to the
resource.
o They just make a request and the response will have the links for the next steps.

 09 The Richardson Maturity Model


 Is this API is fully RESTful?
o There is one way to know and that’s a model by a guy named Leonard Richardson.
o Its called the Richardson Maturity Model
 And it breaks all the concepts we learnt so far into 3 definite levels.
 Every Rest API belongs to these 3 levels.
 Level 3, Level 2, Level1.

Page 35 of 121
 The model also defines a Level 0 which is not a Restful API.
 Richardson Maturity Model – Level 0

o One URI or the Endpoint


 So a message request would go to this single URI
 Comment requests would go to the same URI
 A profile request would go to the same URI
o Request body contains all the details – both the operation that needs to be performed and
the data that’s needed for the operation.
o For eg.,
 The xml here could create a message.

 There is a create-message portion in the request itself.


 The xml here could delete the comment.

 There is a delete comment portion in the request itself


o Since the action is part of the request itself, the same URI could work.
o And in fact the same http message could be used for each operation, because all the
details of the operation is present in the request body.
o In fact that’s what soap does it always post and the post body would have all the
information.
 This is Level 0 in the Richardson Maturity Model
o It is often called Swamp of POX (Plain Old XML)
o Everything is defined in the XML

Page 36 of 121
oNo http concepts are leveraged for communicating this information – since everything is
in the XML there is no use of the HTTP constructs to communicate the information
between the server and the client.
 Now let’s refine this
o Level 1 = Level 0 + introduce the concept of Resource URI
 Individual URIs for each resource. If you just did this then you are at Level
1.
 Now you would have Message requests going to one URI
 Comments requests would go to another URI
 Profile requests going to some other URI
 There would still be information about the operation inside these requests about
what needs to be done.
 Because a single message URI would probably need
o adding a message,
o reading a message
o updating a message and so on.
o Level 2 = Level 1 + Introduce HTTP Methods
 The URI specifies the Resource that being operated upon
 The HTTP method specifies what the operation is
 An API on level 2 uses the standard HTTP methods like
 GET, POST, PUT and DELETE to do different operations on the
resource URIs.
 There should also be better use of status codes and the right use of
idempotent and non-idempotent methods for an API and if happens then
the API is at level 2
o Level 3 = Level 2 + HATEOS
 That is the responses have links that the clients can use.
 The client doesn’t have to be aware of all the URIs because that would be sent as
part of the response.
 Summary
Level 0 Single URI for all resources + XML Request Body (OPERATION+DATA)
Level 1 Level 0 + Resource URI for each resource
Level 2 Level 1 + HTTP Methods
Level 3 Level 2 + HATEOS

Page 37 of 121
 10 Section 2 - What is JAX-RS

 JAX-RS has only interfaces and annotations


 Jersey/RestEasy provide the implementation classes for the JAX-RS interfaces and annotations.
 Now during deployement
o we don’t have to deploy the JAX-RS api –
o Jersey/RestEasy have a copy of the JAX-RS interfaces and annotations within them
o so just deploying Jersey/RestEasy should suffice.

o Jersey is called the “Reference Implemenation” for the JAX-RS specification.


 11 Setting Up
 Creating the Project
o In Eclipse File New Maven Project Use the Default Workspace Location
Next Select the Archetype ::- jersey-quickstart-webapp
o If you don’t see this archetype then you need Add the archetype by clicking on “Add
Archetype”
 Archetype Group Id ::- org.glassfish.jersey.archetypes
 Archetype Artifact Id ::- jersey-quickstart-webapp
 Archetype Version ::- 2.16 -- Finish
 Now the Archetype ‘jersey-quickstart-webapp’ is downloaded.
o Select the Archetype “jersey-quickstart-webapp”  Next
o Project Details
 Group Id :: com.samples.my
 ArtifactId :: messenger Finish
Physical Location ::- D:\MyApps\Eclipse\Workspaces\sts-3.9.2Ws\messenger
Project Structure ::

Page 38 of 121
Project Code ::-
o MyResource.java
package com.samples.my.messenger;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

/**
* Root resource (exposed at "myresource" path)
*/
@Path("myresource")
public class MyResource {

/**
* Method handling HTTP GET requests. The returned object will be sent
* to the client as "text/plain" media type.
*
* @return String that will be returned as a text/plain response.
*/
@GET
@Produces(MediaType.TEXT_PLAIN)
public String getIt() {
return "Got it!";
}
}
o Web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- This web.xml file is not required when using Servlet 3.0 container,
see implementation details http://jersey.java.net/nonav/documentation/latest/jax-
rs.html -->
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://
www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.samples.my.messenger</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Page 39 of 121
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/webapi/*</url-pattern>
</servlet-mapping>
</web-app>
o index.jsp
<html>
<body>
<h2>Jersey RESTful Web Application!</h2>
<p><a href="webapi/myresource">Jersey resource</a>
<p>Visit <a href="http://jersey.java.net">Project Jersey website</a>
for more information on Jersey!
</body>
</html>
o pom.xml
<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/
maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.samples.my</groupId>
<artifactId>messenger</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>messenger</name>

<build>
<finalName>messenger</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<inherited>true</inherited>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
Page 40 of 121
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<!-- use the following artifactId if you don't need servlet 2.x compatibility
-->
<!-- artifactId>jersey-container-servlet</artifactId -->
</dependency>
<!-- uncomment this to get JSON support
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
</dependency>
-->
</dependencies>
<properties>
<jersey.version>2.16</jersey.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
 Adding a Server / Tomcat Server

 Click Next  Select “messenger” from Available  Add  Finish


 RCL on the server and click on Start  The Server Starts
 RCL the project “messenger”  Run As  Run on Server Finish.
o Displays the webpage http://localhost:8080/messenger/

o Click on the Jersey resource link and it displays the following

Page 41 of 121
 12 Understanding the Application Structure
 http://localhost:8080/messenger/webapi/myresource
o http is the protocol
o localhost:8080 is where the tomcat is configured to run
o messenger is the web application context
o webapi – this web application messenger is configured to accept rest api requests at
/webapi uri.
 Any rest api request to this application is pre-configured to go thru this /webapi
uri.
 You can see this in the web.xml file.
 You will see a url-pattern for /webapi/*
 You will see that the servlet-class is
o org.glassfish.jersey.servlet.ServletContainer
o which handles the http requests.
o myresource is the resource in our application
 13 Creating a Resource
 http://localhost:8080/messenger/webapi/messages
 Step 1 ::- Create a new class ‘MessageResource’ for the /messages
o @Path(/messages) – there may be multiple classes in this package (specified in the init-
param of the ServletContainerin web.xml) how does jersey know which class to map
the /messages to ?.
 Jersey knows this by looking at the @Path(“/messages”)
o @GET – the http method to use for this method
o @Produces() – tells jersey what the return content-type is
 MessageResource.java

package com.samples.my.messenger.resources;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/messages")
public class MessasgeResource {

@GET
@Produces(MediaType.TEXT_PLAIN)
public String getMessages() {
return "Hello World...";
}
}
 Start Tomcat and access the url http://localhost:8080/messenger/webapi/messages

Page 42 of 121
 14 Returning XML Response
 Message.java

package com.samples.my.messenger.model;

import java.util.Date;

public class Message {


private long id;
private String message;
private Date created;
private String author;

public Message() {
}

public Message(long id, String message, String author) {


super();
this.id = id;
this.message = message;
this.created = new Date();
this.author = author;
}

// getters and setters


}

 MessageService.java
package com.samples.my.messenger.service;

import java.util.ArrayList;
import java.util.List;

import com.samples.my.messenger.model.Message;

public class MessageService {

public List<Message> getAllMessages() {


Message m1 = new Message(1L, "Hello World", "Prem");
Message m2 = new Message(2L, "Hello Jersey", "Prem");

List<Message> messages = new ArrayList<Message>();


messages.add(m1);
messages.add(m2);

return messages;
}
}
 MessageResource.java
package com.samples.my.messenger.resources;

Page 43 of 121
import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.samples.my.messenger.model.Message;
import com.samples.my.messenger.service.MessageService;

@Path("/messages")
public class MessasgeResource {

MessageService msgService = new MessageService();

@GET
@Produces(MediaType.APPLICATION_XML)
public List<Message> getMessages() {
return msgService.getAllMessages();
}
}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
 Access the url ::- http://localhost:8080/messenger/webapi/messages
 You get the following error:-
SEVERE: MessageBodyWriter not found for media type=application/xml, type=class
java.util.ArrayList, genericType=java.util.List<com.samples.my.messenger.model.Message>.
 Jersey internally uses JAXB to convert the messages to XML and it is not sure
how to convert it.
 We need to give the JAXB some clues to convert messages response to xml.

package com.samples.my.messenger.model;

import java.util.Date;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Message {

private long id;

private String message;

private Date created;

private String author;

public Message() {
}

public Message(long id, String message, String author) {


super();
this.id = id;
this.message = message;

Page 44 of 121
this.created = new Date();
this.author = author;
}
}

 Start Tomcat and access the URL


o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
 Access the url ::- http://localhost:8080/messenger/webapi/messages

 15 Installing a REST Api Client


 Using a browser you can only send GET requests and you get a response. You cannot send a
POST/DELETE/PUT request in a browser. Postman app in Chrome browser is a Rest client.
 Tool that lets you send a Rest API request (GET / POST / PUT / DELETE) and see the
response.
 16 Building Service Stubs
 DatabaseClass.java

package com.samples.my.messenger.database;

import java.util.HashMap;
import java.util.Map;

import com.samples.my.messenger.model.Message;
import com.samples.my.messenger.model.Profile;

public class DatabaseClass {

private static Map<Long, Message> messages = new HashMap<>();


private static Map<Long, Profile> profiles = new HashMap<>();

public static Map<Long, Message> getMessages() {


return messages;
}

public static Map<Long, Profile> getProfiles() {


return profiles;
}
Page 45 of 121
}
 MessageService.java

package com.samples.my.messenger.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.samples.my.messenger.database.DatabaseClass;
import com.samples.my.messenger.model.Message;

public class MessageService {

private Map<Long, Message> messages = DatabaseClass.getMessages();

// Added temporarily as there is no data...


public MessageService() {
Message msg1 = new Message(1, "Hello World", "Prem");
Message msg2 = new Message(1, "Hello Jersey", "Prem");
messages.put(msg1.getId(), msg1);
messages.put(msg2.getId(), msg2);
}

public List<Message> getAllMessages() {


return new ArrayList<Message>(messages.values());
}

public Message getMessage(long id) {


return messages.get(id);
}

public Message addMessage(Message message) {


message.setId(messages.size() + 1);
messages.put(message.getId(), message);
return message;
}

public Message updateMessage(Message message) {


if (message.getId() <= 0) {
return null;
}
messages.put(message.getId(), message);
return message;
}

public Message removeMessage(long id) {


return messages.remove(id);
}
}

Page 46 of 121
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
 Access the url in Postman ::- http://localhost:8080/messenger/webapi/messages

 17 Accessing Path Params


 MessageResource.java

package com.samples.my.messenger.resources;

import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.samples.my.messenger.model.Message;
import com.samples.my.messenger.service.MessageService;

@Path("/messages")
public class MessasgeResource {

MessageService msgService = new MessageService();

@GET
@Produces(MediaType.APPLICATION_XML)
public List<Message> getMessages() {
return msgService.getAllMessages();
}

@GET
@Path("/{messageId}")
@Produces(MediaType.APPLICATION_XML)
public Message getMessage(@PathParam("messageId") long id) {
Page 47 of 121
return msgService.getMessage(id);
}
}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
 Access the url in Postman ::-
http://localhost:8080/messenger/webapi/messages/1

 Access the url in Postman ::-


http://localhost:8080/messenger/webapi/messages/3
 You get HTTP Status Code – 204 No Content
 As there is no record with id 3.
 Access the url in Postman ::-
http://localhost:8080/messenger/webapi/messages/abc
 You get HTTP Status Code – 404 Not Found
 Conversion of path param ‘abc’ to long fails.
 You could have multiple path params within the url

 You can also use Regular Expressions


 18 Returning JSON Response
 MessageResource.java

package com.samples.my.messenger.resources;

import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.samples.my.messenger.model.Message;
import com.samples.my.messenger.service.MessageService;
Page 48 of 121
@Path("/messages")
public class MessasgeResource {

MessageService msgService = new MessageService();

@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Message> getMessages() {
return msgService.getAllMessages();
}

@GET
@Path("/{messageId}")
@Produces(MediaType.APPLICATION_JSON)
public Message getMessage(@PathParam("messageId") long id) {
return msgService.getMessage(id);
}
}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
 Access the url in Postman ::-
http://localhost:8080/messenger/webapi/messages/1
 You get the following error ::-
SEVERE: MessageBodyWriter not found for media type=application/json, type=class
com.samples.my.messenger.model.Message, genericType=class
com.samples.my.messenger.model.Message.
 Remember for XML we added an @XmlRootElement because that was JAXB
support of java was used.
 For json we don’t have any equivalent, so we need to add a third party jar to do
the conversion for us. The pom dependency for the same is
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
</dependency>
o The above jar has an the implementation class for the interface MessageBodyReader
and MessageBodyWriter which is used to convert java object to json.
o That was what was missing and so we got an error on the MessageBodyWriter not
found.
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
 Access the url in Postman ::- http://localhost:8080/messenger/webapi/messages
 It displays the messages in json format. (Verify this later as now there is no internet and so it is
not working properly as the jar hasn’t downloaded.)

Page 49 of 121
 19 Implementing POST Method
 REMEMBER ::- Always set the Content-type header (to “application/json”) when sending a
POST request.
 MessageResource.java

package com.samples.my.messenger.resources;

import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.samples.my.messenger.model.Message;
import com.samples.my.messenger.service.MessageService;

@Path("/messages")
public class MessasgeResource {

MessageService msgService = new MessageService();

@GET
@Produces(MediaType.APPLICATION_XML)
public List<Message> getMessages() {
return msgService.getAllMessages();
}

@GET
@Path("/{messageId}")
@Produces(MediaType.APPLICATION_XML)
public Message getMessage(@PathParam("messageId") long id) {
return msgService.getMessage(id);
}

@POST
@Produces(MediaType.APPLICATION_XML)
@Consumes(MediaType.APPLICATION_XML)
public Message addMessage(Message message) {
Page 50 of 121
return msgService.addMessage(message);
}
}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
 Access the url in Postman ::- http://localhost:8080/messenger/webapi/messages

 Now do a get request on the url http://localhost:8080/messenger/webapi/messages


o It displays 3 messages now.

 20 Implementing Update and Delete


 MessageResource.java

package com.samples.my.messenger.resources;

Page 51 of 121
import java.util.List;

import javax.ws.rs.Consumes;
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 com.samples.my.messenger.model.Message;
import com.samples.my.messenger.service.MessageService;

@Path("/messages")
public class MessasgeResource {

MessageService msgService = new MessageService();

@GET
@Produces(MediaType.APPLICATION_XML)
public List<Message> getMessages() {
return msgService.getAllMessages();
}

@GET
@Path("/{messageId}")
@Produces(MediaType.APPLICATION_XML)
public Message getMessage(@PathParam("messageId") long id) {
return msgService.getMessage(id);
}

@POST
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public Message addMessage(Message message) {
return msgService.addMessage(message);
}

@PUT
@Path("/{messageId}")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public Message updateMessage(@PathParam("messageId") long id, Message message) {
message.setId(id);
return msgService.updateMessage(message);
}

@DELETE

Page 52 of 121
@Path("/{messageId}")
@Produces(MediaType.APPLICATION_XML)
public void deleteMessage(@PathParam("messageId") long id) {
msgService.removeMessage(id);
}
}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
 Access the url in Postman ::-
http://localhost:8080/messenger/webapi/messages/3

 Delete operation

 Refactor the MessasgeResource.java


o It has @Produces and @Consumes at each and every method.
o Move them to the class level and remove them from each method

package com.samples.my.messenger.resources;

import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
Page 53 of 121
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 com.samples.my.messenger.model.Message;
import com.samples.my.messenger.service.MessageService;

@Path("/messages")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
public class MessasgeResource {

MessageService msgService = new MessageService();

@GET
public List<Message> getMessages() {
return msgService.getAllMessages();
}

@GET
@Path("/{messageId}")
public Message getMessage(@PathParam("messageId") long id) {
return msgService.getMessage(id);
}

@POST
public Message addMessage(Message message) {
return msgService.addMessage(message);
}

@PUT
@Path("/{messageId}")
public Message updateMessage(@PathParam("messageId") long id, Message message) {
message.setId(id);
return msgService.updateMessage(message);
}

@DELETE
@Path("/{messageId}")
public void deleteMessage(@PathParam("messageId") long id) {
msgService.removeMessage(id);
}
}
 21 Implementing Profile Resource
 Database.java
Page 54 of 121
package com.samples.my.messenger.database;

import java.util.HashMap;
import java.util.Map;

import com.samples.my.messenger.message.Message;
import com.samples.my.messenger.profile.Profile;

public class DatabaseClass {

private static Map<Long, Message> messages = new HashMap<>();


private static Map<String, Profile> profiles = new HashMap<>();

public static Map<Long, Message> getMessages() {


return messages;
}

public static Map<String, Profile> getProfiles() {


return profiles;
}
}
 Profile.java
package com.samples.my.messenger.profile;

import java.util.Date;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Profile {

private long id;


private String profileName;
private String firstName;
private String lastName;
private Date created;

public Profile() {

public Profile(long id, String profileName, String firstName, String lastName) {


this.id = id;
this.profileName = profileName;
this.firstName = firstName;
this.lastName = lastName;
}

// getters and setters


}

 ProfileService.java
package com.samples.my.messenger.profile;

Page 55 of 121
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.samples.my.messenger.database.DatabaseClass;

public class ProfileService {

private Map<String, Profile> profiles = DatabaseClass.getProfiles();

public ProfileService() {
profiles.put("prem", new Profile(1L, "prem", "Prem", "Vinodh"));
}

public List<Profile> getAllProfiles() {


return new ArrayList<Profile>(profiles.values());
}

public Profile getProfile(String profileName) {


return profiles.get(profileName);
}

public Profile addProfile(Profile profile) {


profile.setId(profiles.size() + 1);
profiles.put(profile.getProfileName(), profile);
return profile;
}

public Profile updateProfile(Profile profile) {


if (profile.getProfileName().isEmpty()) {
return null;
}
profiles.put(profile.getProfileName(), profile);
return profile;
}

public Profile removeProfile(String profileName) {


return profiles.remove(profileName);
}

 ProfileResource.java
package com.samples.my.messenger.profile;

import java.util.List;

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;

@Path("/profiles")
Page 56 of 121
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class ProfileResource {

private ProfileService profileService = new ProfileService();

@GET
public List<Profile> getProfiles() {
return profileService.getAllProfiles();
}

@POST
public Profile addProfile(Profile profile) {
return profileService.addProfile(profile);
}

@GET
@Path("/{profileName}")
public Profile getProfile(@PathParam("profileName") String profileName) {
return profileService.getProfile(profileName);
}

@PUT
@Path("/{profileName}")
public Profile updateProfile(@PathParam("profileName") String profileName, Profile
profile) {
profile.setProfileName(profileName);
return profileService.updateProfile(profile);
}

@DELETE
@Path("/{profileName}")
public void deleteProfile(@PathParam("profileName") String profileName) {
profileService.removeProfile(profileName);
}

 Start Tomcat and access the URL


o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/messenger/webapi/profiles/
Displays  [{"firstName":"Prem","id":1,"lastName":"Vinodh","profileName":"prem"}]

Similarly perform the other opertions – POST, PUT and DELETE

 22 Pagination and Filtering


 MessageService.java

package com.samples.my.messenger.message;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;

Page 57 of 121
import com.samples.my.messenger.database.DatabaseClass;

public class MessageService {

private Map<Long, Message> messages = DatabaseClass.getMessages();

public MessageService() {
messages.put(1l, new Message(1l, "Hello World", "Prem"));
messages.put(2l, new Message(2l, "Hello Jersey", "Vinodh"));
}

public List<Message> getAllMessages() {


return new ArrayList<Message>(messages.values());
}

public List<Message> getAllMessagesForYear(int year) {


List<Message> messagesForYear = new ArrayList<>();
Calendar cal = Calendar.getInstance();
for (Message message : messages.values()) {
cal.setTime(message.getCreated());
if (cal.get(Calendar.YEAR) == year) {
messagesForYear.add(message);
}
}
return messagesForYear;
}

public List<Message> getAllMessagesPaginated(int start, int size) {


ArrayList<Message> list = new ArrayList<Message>(messages.values());
if (start + size > list.size())
return new ArrayList<Message>();
return list.subList(start, start + size);
}

public Message getMessage(long id) {


return messages.get(id);
}

public Message addMessage(Message message) {


message.setId(messages.size() + 1);
messages.put(message.getId(), message);
return message;
}

public Message updateMessage(Message message) {


if (message.getId() <= 0) {
return null;
}
messages.put(message.getId(), message);
return message;
}

public Message removeMessage(long id) {


return messages.remove(id);
}

Page 58 of 121
 MessageResource.java

package com.samples.my.messenger.message;

import java.util.List;

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.QueryParam;
import javax.ws.rs.core.MediaType;

@Path("/messages")
public class MessageResource {

MessageService messageSvc = new MessageService();

@GET
public List<Message> getMessages(@QueryParam("year") int year,
@QueryParam("start") int start,
@QueryParam("size") int size) {

if (year > 0) {
return messageSvc.getAllMessagesForYear(year);
}
if (start >= 0 && size > 0) {
return messageSvc.getAllMessagesPaginated(start, size);
}
return messageSvc.getAllMessages();
}

@GET
@Path("/{messageId}")
@Produces(MediaType.APPLICATION_JSON)
public Message getMessage(@PathParam("messageId") long id) {
return messageSvc.getMessage(id);
}

@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Message addMessage(Message message) {
return messageSvc.addMessage(message);
}

@PUT
@Path("/{messageId}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Message updateMessage(@PathParam("messageId") long id, Message message) {
message.setId(id);
return messageSvc.updateMessage(message);
Page 59 of 121
}

@DELETE
@Path("/{messageId}")
@Consumes(MediaType.APPLICATION_JSON)
public void deleteMessage(@PathParam("messageId") long id) {
messageSvc.removeMessage(id);
}

 Start Tomcat and access the URL


o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/messenger/webapi/messages/?start=1&size=1
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<messages>
<message>
<author>Vinodh</author>
<created>2021-02-26T22:53:37.747+05:30</created>
<id>2</id>
<message>Hello Jersey</message>
</message>
</messages>

Access the url in Postman ::- http://localhost:8080/messenger/webapi/messages?year=2021


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<messages>
<message>
<author>Prem</author>
<created>2021-02-26T22:54:27.712+05:30</created>
<id>1</id>
<message>Hello World</message>
</message>
<message>
<author>Vinodh</author>
<created>2021-02-26T22:54:27.712+05:30</created>
<id>2</id>
<message>Hello Jersey</message>
</message>
</messages>

Access the url in Postman ::- http://localhost:8080/messenger/webapi/messages?year=2005


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<messages></messages>

 23 The Param Annotations


 Till now we have looked at PathParam and QueryParam (Refer MessageResource.java)
 In this section we will look at some more of these Param annotations
 InjectDemoResource.java

Page 60 of 121
package com.samples.my.messenger.message;

import javax.ws.rs.Consumes;
import javax.ws.rs.CookieParam;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.MatrixParam;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/injectdemo")
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
public class InjectDemoResource {

@GET
@Path("annotations")
public String getParamsUsingAnnotations(@MatrixParam("param") String matrixParam,
@HeaderParam("authSessionID") String header, @CookieParam("cookieName") String
cookie) {
return "Matrix param: " + matrixParam + " Header param: " + header + " Cookie
param: " + cookie;
}

}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- Header and Matrix Param
http://localhost:8080/messenger/webapi/injectdemo/context;param=heyprem
o Matrix param is similar to Query param instead of “?” we use “;” a semicolon in the url

o Setting Cookies in postman


Try using the Headers feature with a key of Cookie and a value of cookieName={{yourVariable}}
This is not working – check how to send Cookies as part of request using postman

 We still have FormParam and BeanParam

Page 61 of 121
 24 Using Context and BeanParam Annotations
 InjectDemoResource.java

package com.samples.my.messenger.message;

import javax.ws.rs.Consumes;
import javax.ws.rs.CookieParam;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.MatrixParam;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;

@Path("/injectdemo")
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.TEXT_PLAIN)
public class InjectDemoResource {

@GET
@Path("annotations")
public String getParamsUsingAnnotations(@MatrixParam("param") String matrixParam,
@HeaderParam("customHeader") String header, @CookieParam("customCookie") String
cookie) {
return "Matrix param: " + matrixParam + " Header param: " + header + " Cookie
param: " + cookie;
}

@GET
@Path("context")
public String getParamsUsingContext(@Context UriInfo uriInfo, @Context HttpHeaders
headers) {

String path = uriInfo.getAbsolutePath().toString();


String customHeader = headers.getHeaderString("customHeader");
String cookies = headers.getCookies().toString();
return "Path : " + path + " Header param: " + customHeader + " Cookies: " + cook-
ies;
}
}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- Header and Matrix Param
http://localhost:8080/messenger/webapi/injectdemo/context;param=heyprem

Page 62 of 121
 The method getMessages of MessageResource takes in a bunch of QueryParam’s.
o You can use a BeanParam here.
o You can move these bunch of QueryParam’s into a separate class with getters and setters
o MessageFilterBean.java

package com.samples.my.messenger.message;

import javax.ws.rs.QueryParam;

public class MessageFilterBean {

private @QueryParam("year") int year;


private @QueryParam("start") int start;
private @QueryParam("size") int size;

//getters and setters


}

o MessageResource.java

package com.samples.my.messenger.message;

import java.util.List;

import javax.ws.rs.BeanParam;
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;

@Path("/messages")
public class MessageResource {

MessageService messageSvc = new MessageService();

@GET
public List<Message> getMessages(@BeanParam MessageFilterBean filterBean) {

Page 63 of 121
if (filterBean.getYear() > 0) {
return messageSvc.getAllMessagesForYear(filterBean.getYear());
}
if (filterBean.getStart() >= 0 && filterBean.getSize() > 0) {
return messageSvc.getAllMessagesPaginated(filterBean.getStart(), filter-
Bean.getSize());
}
return messageSvc.getAllMessages();
}

@GET
@Path("/{messageId}")
@Produces(MediaType.APPLICATION_JSON)
public Message getMessage(@PathParam("messageId") long id) {
return messageSvc.getMessage(id);
}

@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Message addMessage(Message message) {
return messageSvc.addMessage(message);
}

@PUT
@Path("/{messageId}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Message updateMessage(@PathParam("messageId") long id, Message message) {
message.setId(id);
return messageSvc.updateMessage(message);
}

@DELETE
@Path("/{messageId}")
@Consumes(MediaType.APPLICATION_JSON)
public void deleteMessage(@PathParam("messageId") long id) {
messageSvc.removeMessage(id);
}
}

 Start Tomcat and access the URL


o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/messenger/webapi/messages/?start=1&size=1

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>


<messages>
<message>
<author>Vinodh</author>
<created>2021-02-26T23:56:09.405+05:30</created>
<id>2</id>
<message>Hello Jersey</message>
</message>
</messages>
Page 64 of 121
 25 Implementing SubResources
 Looking at the comments resource
o The url is

o So if you have to look at comment Id = 20 under messageId =2 you would write the url
as shown above.
o Now what the above url also means is that comments is a resource which is dependent
on the messages and so its implementation would go into the resource class which
has /messages mapped to it – in this case the MessageResource.
o Now when you look at the MessageResource class it has methods related to messages
with GET/POST/PUI/DELETE.
o So cluttering the MessageResource with comments related methods is cumbersome.
o So here we could use a subresource.
 So in effect we are telling JAX-RS whenever you see a url as
/messages/{messageId}/comments
 Goto the MessageResource because it is mapped to /messages
 Goto the method getCommentResource because it is mapped to the
url /{messageId}/comments and delegate/hand over the responsibility to
the other resource returned by this method to fetch the comments for this
messageId.

 Comment.java
package com.samples.my.messenger.message;

import java.util.Date;

public class Comment {

private long id;


private String comment;
private Date created;
private String author;

public Comment() {

public Comment(long id, String comment, String author) {


this.id = id;
this.comment = comment;
this.author = author;
this.created = new Date();
}

//getters and setters


}

 Message.java
package com.samples.my.messenger.message;

Page 65 of 121
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

@XmlRootElement
public class Message {

private long id;


private String message;
private Date created;
private String author;
private Map<Long, Comment> comments = new HashMap<>();

public Message() {
}

public Message(long id, String message, String author) {


super();
this.id = id;
this.message = message;
this.created = new Date();
this.author = author;
}

// getters and setters for id, message, created and author

@XmlTransient
public Map<Long, Comment> getComments() {
return comments;
}

public void setComments(Map<Long, Comment> comments) {


this.comments = comments;
}
}
 MessageService.java
package com.samples.my.messenger.message;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;

import com.samples.my.messenger.database.DatabaseClass;

public class MessageService {


Page 66 of 121
private Map<Long, Message> messages = DatabaseClass.getMessages();

public MessageService() {
if (messages == null || messages.isEmpty()) {
messages.put(1l, new Message(1l, "Hello World", "Prem"));
messages.put(2l, new Message(2l, "Hello Jersey", "Vinodh"));
}
}

public List<Message> getAllMessages() {


return new ArrayList<Message>(messages.values());
}

public List<Message> getAllMessagesForYear(int year) {


List<Message> messagesForYear = new ArrayList<>();
Calendar cal = Calendar.getInstance();
for (Message message : messages.values()) {
cal.setTime(message.getCreated());
if (cal.get(Calendar.YEAR) == year) {
messagesForYear.add(message);
}
}
return messagesForYear;
}

public List<Message> getAllMessagesPaginated(int start, int size) {


ArrayList<Message> list = new ArrayList<Message>(messages.values());
if (start + size > list.size())
return new ArrayList<Message>();
return list.subList(start, start + size);
}

public Message getMessage(long id) {


return messages.get(id);
}

public Message addMessage(Message message) {


message.setId(messages.size() + 1);
messages.put(message.getId(), message);
return message;
}

public Message updateMessage(Message message) {


if (message.getId() <= 0) {
return null;
}
messages.put(message.getId(), message);
return message;
Page 67 of 121
}

public Message removeMessage(long id) {


return messages.remove(id);
}

}
 MessageResource.java
package com.samples.my.messenger.message;

import java.util.List;

import javax.ws.rs.BeanParam;
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;

@Path("/messages")
public class MessageResource {

private MessageService messageSvc = new MessageService();

@GET
public List<Message> getMessages(@BeanParam MessageFilterBean filterBean) {

if (filterBean.getYear() > 0) {
return messageSvc.getAllMessagesForYear(filterBean.getYear());
}
if (filterBean.getStart() >= 0 && filterBean.getSize() > 0) {
return messageSvc.getAllMessagesPaginated(filterBean.getStart(), filterBean.getSize());
}
return messageSvc.getAllMessages();
}

@GET
@Path("/{messageId}")
@Produces(MediaType.APPLICATION_JSON)
public Message getMessage(@PathParam("messageId") long id) {
return messageSvc.getMessage(id);
}

@POST
@Consumes(MediaType.APPLICATION_JSON)
Page 68 of 121
@Produces(MediaType.APPLICATION_JSON)
public Message addMessage(Message message) {
return messageSvc.addMessage(message);
}

@PUT
@Path("/{messageId}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Message updateMessage(@PathParam("messageId") long id, Message message) {
message.setId(id);
return messageSvc.updateMessage(message);
}

@DELETE
@Path("/{messageId}")
@Consumes(MediaType.APPLICATION_JSON)
public void deleteMessage(@PathParam("messageId") long id) {
messageSvc.removeMessage(id);
}

@Path("/{messageId}/comments")
public CommentResource getCommentResource() {
return new CommentResource();
}
}
 CommentService.java
package com.samples.my.messenger.message;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.samples.my.messenger.database.DatabaseClass;

public class CommentService {

private Map<Long, Message> messages = DatabaseClass.getMessages();

public List<Comment> getAllComments(long messageId) {


Map<Long, Comment> comments = messages.get(messageId).getComments();
return new ArrayList<Comment>(comments.values());
}

public Comment getComment(long messageId, long commentId) {


Message message = messages.get(messageId);
Map<Long, Comment> comments = messages.get(messageId).getComments();
return comments.get(commentId);
}

public Comment addComment(long messageId, Comment comment) {


Map<Long, Comment> comments = messages.get(messageId).getComments();
comment.setId(comments.size() + 1);

Page 69 of 121
comments.put(comment.getId(), comment);

DatabaseClass.getMessages();
return comment;
}

public Comment updateComment(long messageId, Comment comment) {


Map<Long, Comment> comments = messages.get(messageId).getComments();
if (comment.getId() <= 0) {
return null;
}
comments.put(comment.getId(), comment);
return comment;
}

public Comment removeComment(long messageId, long commentId) {


Map<Long, Comment> comments = messages.get(messageId).getComments();
return comments.remove(commentId);
}
}

 CommentResource.java
package com.samples.my.messenger.message;

import java.util.List;

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;

@Path("/")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class CommentResource {

private CommentService commentService = new CommentService();

@GET
public List<Comment> getAllComments(@PathParam("messageId") long messageId) {
return commentService.getAllComments(messageId);
}

@POST
public Comment addComment(@PathParam("messageId") long messageId, Comment comment) {
return commentService.addComment(messageId, comment);
}

@PUT
@Path("/{commentId}")
public Comment updateComment(@PathParam("messageId") long messageId, @PathParam("com-
mentId") long id,
Comment comment) {
Page 70 of 121
comment.setId(id);
return commentService.updateComment(messageId, comment);
}

@DELETE
@Path("/{commentId}")
public void deleteComment(@PathParam("messageId") long messageId, @PathParam("commen-
tId") long commentId) {
commentService.removeComment(messageId, commentId);
}

@GET
@Path("/{commentId}")
public Comment getMessage(@PathParam("messageId") long messageId, @PathParam("commen-
tId") long commentId) {
return commentService.getComment(messageId, commentId);
}
}

 Start Tomcat and access the URL


o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/messenger/webapi/messages/1/comments/

Similarly access the other http methods GET/PUT/DELETE


http://localhost:8080/messenger/webapi/messages/1/comments/1
http://localhost:8080/messenger/webapi/messages/1/comments

 26 Sending Status Codes and Location Headers


 In this section we will look at how to get more control over the response.
 If you look at the MessageResource we been sending the actual entity(s).
o For eg.

Page 71 of 121
In the addMessage  we return the actual Message and depend on Jersey to wrap it in
o
a response and send it over to the client.
o Now Jersey wraps it and send it over to the client with a standard status code say 200 for
a POST operation.
o What if I want to send 201 - Created after creation of a new message?
o What if I want to send some custom header in the response?
o How to do them? How to get more control over the response?
 By returning an instance of Response class.
 MessageResource.java

package com.samples.my.messenger.message;

import java.net.URI;
import java.util.List;

import javax.ws.rs.BeanParam;
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.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

@Path("/messages")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class MessageResource {

private MessageService messageSvc = new MessageService();

@GET
public List<Message> getMessages(@BeanParam MessageFilterBean filterBean) {

Page 72 of 121
if (filterBean.getYear() > 0) {
return messageSvc.getAllMessagesForYear(filterBean.getYear());
}
if (filterBean.getStart() >= 0 && filterBean.getSize() > 0) {
return messageSvc.getAllMessagesPaginated(filterBean.getStart(), filter-
Bean.getSize());
}
return messageSvc.getAllMessages();
}

@GET
@Path("/{messageId}")
public Message getMessage(@PathParam("messageId") long id) {
return messageSvc.getMessage(id);
}

// @POST
// Returns a standard 200 status code with no custom headers
// public Message addMessage(Message message) {
// return messageSvc.addMessage(message);
// }

@POST
public Response addMessage(Message message, @Context UriInfo uriInfo) {
Message msg = messageSvc.addMessage(message);
// Response.status(Status.CREATED).entity(msg) .build();
String newId = String.valueOf(msg.getId());
URI uri = uriInfo.getAbsolutePathBuilder().path(newId).build();
return Response.created(uri).entity(msg).build();
}

@PUT
@Path("/{messageId}")
public Message updateMessage(@PathParam("messageId") long id, Message message) {
message.setId(id);
return messageSvc.updateMessage(message);
}

@DELETE
@Path("/{messageId}")
public void deleteMessage(@PathParam("messageId") long id) {
messageSvc.removeMessage(id);
}

@Path("/{messageId}/comments")
public CommentResource getCommentResource() {
return new CommentResource();
}
}

 Start Tomcat and access the URL


o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/messenger/webapi/messages/

Page 73 of 121
 27 Handling Exceptions
 When we retrieve data for message id 200 which is not there we get 204 – No Content.

 Instead I want to throw an exception here…. When there is no Message.


 DataNotFoundException.java
package com.samples.my.messenger.exception;

public class DataNotFoundException extends RuntimeException {

private static final long serialVersionUID = -6328286661536343936L;

public DataNotFoundException(String message) {


super(message);
}
}

 MessageService.java
package com.samples.my.messenger.message;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;

import com.samples.my.messenger.database.DatabaseClass;
import com.samples.my.messenger.exception.DataNotFoundException;

Page 74 of 121
public class MessageService {

private Map<Long, Message> messages = DatabaseClass.getMessages();

public MessageService() {
if (messages == null || messages.isEmpty()) {
messages.put(1l, new Message(1l, "Hello World", "Prem"));
messages.put(2l, new Message(2l, "Hello Jersey", "Vinodh"));
}
}

public List<Message> getAllMessages() {


return new ArrayList<Message>(messages.values());
}

public List<Message> getAllMessagesForYear(int year) {


List<Message> messagesForYear = new ArrayList<>();
Calendar cal = Calendar.getInstance();
for (Message message : messages.values()) {
cal.setTime(message.getCreated());
if (cal.get(Calendar.YEAR) == year) {
messagesForYear.add(message);
}
}
return messagesForYear;
}

public List<Message> getAllMessagesPaginated(int start, int size) {


ArrayList<Message> list = new ArrayList<Message>(messages.values());
if (start + size > list.size())
return new ArrayList<Message>();
return list.subList(start, start + size);
}

public Message getMessage(long id) {


Message message = messages.get(id);
if (message == null) {
throw new DataNotFoundException("Message with id " + id + " not found");
}
return message;
}

public Message addMessage(Message message) {


message.setId(messages.size() + 1);
messages.put(message.getId(), message);
return message;
}

public Message updateMessage(Message message) {


Page 75 of 121
if (message.getId() <= 0) {
return null;
}
messages.put(message.getId(), message);
return message;
}

public Message removeMessage(long id) {


return messages.remove(id);
}

}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/messenger/webapi/messages/200
We see our DataNotFoundException thrown but Tomcat has caught the error and is displaying it

o We throw this exception from the Service but we did not write anything to handle the
exception.
 The MessageService throws the exception and
 the MessageResource did not handle the exception and it is thrown again
 then JAX-RS did not handle the exception and it is thrown again
 The Tomcat receives the exception and its error page displays it.
o We need to intercept this bubbling of the exception and
 let JAX-RS handle it by returning a JSON response
 so that it does not go on bubbling till the tomcat servlet container.
o How do we do return a JSON response when an exception is thrown
 First step – prepare the json response
 Second step – map it to the exception
 We tell Jersey hey when this exception is thrown return this json
response.
Page 76 of 121
o The concept is similar to mapping url patterns to a response

o Here we are mapping an exception to a response.


 So first lets create the payload ErrorMessage.java which we want to return
when there is an exception.
package com.samples.my.messenger.exception;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement // Because I want this object to be converted to a json.


public class ErrorMessage {

private String errorMessage;


private int errorCode;
private String documentation;

public ErrorMessage() {}

public ErrorMessage(String errorMessage, int errorCode, String documentation) {


super();
this.errorMessage = errorMessage;
this.errorCode = errorCode;
this.documentation = documentation;
}

// getters and setters


}

 Second, we need to map the exception DataNotFoundException to the response.


 In JAX-RS we use an ExceptionMapper
DataNotFoundExceptionMapper to map an exception to a response.

package com.samples.my.messenger.exception;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Provider
public class DataNotFoundExceptionMapper implements ExceptionMapper<DataNotFoundException>
{

@Override
public Response toResponse(DataNotFoundException ex) {
return Response.status(Status.NOT_FOUND).build();
}
}
 Start Tomcat and access the URL

Page 77 of 121
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/messenger/webapi/messages/200

 We see the 404 status code that we are returning but still the Tomcat server error page – What
happened? We can fix that by returning our own ErrorMessage in the
DataNotFoundExceptionMapper.java

package com.samples.my.messenger.exception;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Provider
public class DataNotFoundExceptionMapper implements ExceptionMapper<DataNotFoundException>
{

@Override
public Response toResponse(DataNotFoundException ex) {
ErrorMessage errorMessage = new ErrorMessage(ex.getMessage(), 404, "http://mickey.
mouse.org");
return Response.status(Status.NOT_FOUND)
.entity(errorMessage)
.build();
}
}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/messenger/webapi/messages/200

Page 78 of 121
 We can create different ExceptionMappers for each of the exceptions thrown or we can create a
GenericExceptionMapper.java to handle all exceptions as shown below.

package com.samples.my.messenger.exception;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

// This class intentionally doesn't have the @Provider annotation.


// It has been disabled in order to try out other ways of throwing exceptions in JAX-RS

@Provider
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {

@Override
public Response toResponse(Throwable ex) {
ErrorMessage errorMessage = new ErrorMessage(ex.getMessage(), 500, "http://mini.-
moue.org");
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(errorMessage).build();
}
}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/messenger/webapi/mess
Notice that status code is 500 of GenericExceptionMapper and not 404 of DataNotFoundException

Page 79 of 121
Comment out the @Provider in the GenericExceptionMapper class once this example is over.
 28 Using WebApplicationException
 The ExceptionMapper lets you map exceptions (custom or standard) to your own response.
 But why did we need an exception mapper in the first place?
o Because Jersey has no context or no information about the kind of exceptions that your
application could throw.
 It could be a custom excepton,
 it should be a NullPointerException
 JAX-RS doesn’t know what to do with them
 So JAX-RS says you give me the mapper that I can map an exception to
a response.
o JAX-RS has its own set of exceptions that it does map to a status and a response.
 Since JAX-RS knows what those exceptions are, you don’t need to write an
exception mapper for them.
 This exception is WebApplicationException
 MessageService.java

package com.samples.my.messenger.message;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response.Status;

import com.samples.my.messenger.database.DatabaseClass;

public class CommentService {

private Map<Long, Message> messages = DatabaseClass.getMessages();

public List<Comment> getAllComments(long messageId) {


Map<Long, Comment> comments = messages.get(messageId).getComments();
return new ArrayList<Comment>(comments.values());
}

public Comment getComment(long messageId, long commentId) {


Message message = messages.get(messageId);
if (message == null) {
throw new WebApplicationException(Status.NOT_FOUND);
}
Map<Long, Comment> comments = messages.get(messageId).getComments();
Comment comment = comments.get(commentId);
if (comment == null) {
throw new WebApplicationException(Status.NOT_FOUND);
}
return comment;
Page 80 of 121
}

public Comment addComment(long messageId, Comment comment) {


Map<Long, Comment> comments = messages.get(messageId).getComments();
comment.setId(comments.size() + 1);
comments.put(comment.getId(), comment);

DatabaseClass.getMessages();
return comment;
}

public Comment updateComment(long messageId, Comment comment) {


Map<Long, Comment> comments = messages.get(messageId).getComments();
if (comment.getId() <= 0) {
return null;
}
comments.put(comment.getId(), comment);
return comment;
}

public Comment removeComment(long messageId, long commentId) {


Map<Long, Comment> comments = messages.get(messageId).getComments();
return comments.remove(commentId);
}
}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/messenger/webapi/messages/100/comments/1
o Notice we did not create an exception mapper for this WebApplicationException
o The status 404 is thrown
o The exception bubbled up to the tomcat servlet container and is thrown on its error page.
 This is because we have not set the response in the WebApplicationException
throw new WebApplicationException(Status.NOT_FOUND);

Page 81 of 121
 Sending the response as part of the WebApplicationException
CommentService.java
package com.samples.my.messenger.message;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import com.samples.my.messenger.database.DatabaseClass;
import com.samples.my.messenger.exception.ErrorMessage;

public class CommentService {

private Map<Long, Message> messages = DatabaseClass.getMessages();

public List<Comment> getAllComments(long messageId) {


Map<Long, Comment> comments = messages.get(messageId).getComments();
return new ArrayList<Comment>(comments.values());
}

public Comment getComment(long messageId, long commentId) {


ErrorMessage errorMessage = new ErrorMessage("Not found", 404, "http://huey.duck-
.org");
Response response = Response.status(Status.NOT_FOUND).entity(errorMes-
sage).build();

Message message = messages.get(messageId);


if (message == null) {
throw new WebApplicationException(response);
}
Map<Long, Comment> comments = messages.get(messageId).getComments();
Comment comment = comments.get(commentId);
Page 82 of 121
if (comment == null) {
throw new WebApplicationException(response);
}
return comment;
}

public Comment addComment(long messageId, Comment comment) {


Map<Long, Comment> comments = messages.get(messageId).getComments();
comment.setId(comments.size() + 1);
comments.put(comment.getId(), comment);

DatabaseClass.getMessages();
return comment;
}

public Comment updateComment(long messageId, Comment comment) {


Map<Long, Comment> comments = messages.get(messageId).getComments();
if (comment.getId() <= 0) {
return null;
}
comments.put(comment.getId(), comment);
return comment;
}

public Comment removeComment(long messageId, long commentId) {


Map<Long, Comment> comments = messages.get(messageId).getComments();
return comments.remove(commentId);
}
}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/messenger/webapi/messages/100/comments/1
o Notice we did not create an exception mapper for this WebApplicationException
o The status 404 is thrown
o The exception did not bubble up to the tomcat servlet container
 This is because we have set the response in the WebApplicationException
throw new WebApplicationException(response);

 29 HATEOS (Part 1) & 30 HATEOS (Part 2)


 Sending hyperlinks in the response informing the different ways we can access the resource.
 Making the response navigable.
Page 83 of 121
 The obvious way to do this is to enhance the model.
 Link.java
package com.samples.my.messenger.message;

public class Link {


private String link;
private String rel;

// getters and setters


}
 Message.java
package com.samples.my.messenger.message;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

@XmlRootElement
public class Message {

private long id;


private String message;
private Date created;
private String author;
private Map<Long, Comment> comments = new HashMap<>();
private List<Link> links = new ArrayList<>();

public Message() {
}

public Message(long id, String message, String author) {


super();
this.id = id;
this.message = message;
this.created = new Date();
this.author = author;
}

// getters and setters for id, message, created and author

@XmlTransient
public Map<Long, Comment> getComments() {
return comments;
}

public void setComments(Map<Long, Comment> comments) {


this.comments = comments;
}

public List<Link> getLinks() {


return links;
}

Page 84 of 121
public void setLinks(List<Link> links) {
this.links = links;
}

public void addLink(String url, String rel) {


Link link = new Link();
link.setLink(url);
link.setRel(rel);
links.add(link);
}
}
 MessageResource.java
package com.samples.my.messenger.message;

import java.net.URI;
import java.util.List;

import javax.ws.rs.BeanParam;
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.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

import com.samples.my.messenger.profile.ProfileResource;

@Path("/messages")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class MessageResource {

private MessageService messageSvc = new MessageService();

@GET
public List<Message> getMessages(@BeanParam MessageFilterBean filterBean) {

if (filterBean.getYear() > 0) {
return messageSvc.getAllMessagesForYear(filterBean.getYear());
}
if (filterBean.getStart() >= 0 && filterBean.getSize() > 0) {
return messageSvc.getAllMessagesPaginated(filterBean.getStart(), filter-
Bean.getSize());
}
return messageSvc.getAllMessages();
}

@GET
@Path("/{messageId}")
public Message getMessage(@PathParam("messageId") long id, @Context UriInfo uriInfo) {
Message message = messageSvc.getMessage(id);
Page 85 of 121
message.addLink(getUriForSelf(uriInfo, message), "self");
message.addLink(getUriForProfile(uriInfo, message), "profile");
message.addLink(getUriForComments(uriInfo, message), "comments");
return message;
}

private String getUriForComments(UriInfo uriInfo, Message message) {


URI uri = uriInfo.getBaseUriBuilder()
.path(MessageResource.class)
.path(MessageResource.class, "getCommentResource") // pull the remaining of
the path from the method getCommentResource
.path(CommentResource.class) // this is just a path with a / but this could
change later
// replace the variable {messageId} from the path "/{messageId}/comments"
of getCommentResource with a value
.resolveTemplate("messageId", message.getId()).build();
return uri.toString();
}

private String getUriForProfile(UriInfo uriInfo, Message message) {


URI uri = uriInfo.getBaseUriBuilder()
.path(ProfileResource.class)
.path(message.getAuthor())
.build();
return uri.toString();
}

private String getUriForSelf(UriInfo uriInfo, Message message) {


URI uri = uriInfo.getBaseUriBuilder()
.path(MessageResource.class)
.path(Long.toString(message.getId()))
.build();
return uri.toString();
}

// @POST
// public Message addMessage(Message message) {
// return messageSvc.addMessage(message);
// }

@POST
public Response addMessage(Message message, @Context UriInfo uriInfo) {
Message msg = messageSvc.addMessage(message);
// Response.status(Status.CREATED).entity(msg) .build();
String newId = String.valueOf(msg.getId());
URI uri = uriInfo.getAbsolutePathBuilder().path(newId).build();
return Response.created(uri).entity(msg).build();
}

@PUT
@Path("/{messageId}")
public Message updateMessage(@PathParam("messageId") long id, Message message) {
message.setId(id);
return messageSvc.updateMessage(message);
}

@DELETE
@Path("/{messageId}")

Page 86 of 121
public void deleteMessage(@PathParam("messageId") long id) {
messageSvc.removeMessage(id);
}

@Path("/{messageId}/comments")
public CommentResource getCommentResource() {
return new CommentResource();
}

}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/messenger/webapi/messages/1

 31 Content Negotiation
 Content Negotiation is the client can ask a server for a particular content type in the response.
 The client can also send different content types to the server.

 The client can ask for a specific content ype by sending an Accept header.
o The value of this header would be say for eg.

Page 87 of 121
 Application/json if the client needs to get back json response
 text/xml if the client needs to get back xml response.

 At present the MessageResource can only send back JSON responses so that is
why it threw the error 406 – Not Acceptable

 would solve the issue.
 Now this @Produces can be put for a single method and that would have
the request for both json and xml or they can be put against different
methods one to handle json request and the other method to handle xml
request.

Page 88 of 121
 The media types are actually just strings

Page 89 of 121
“Rest WebServices - Advanced”
 Unit 1 :: The Application Class
 01 Introduction

 Nothing to document here.

 02 Tomcat Setup

 I know this setup well so did not document it.

 03 Setting up pom.xml

 We boostrapped our Jersey application with a servlet provided by Jersey in web.xml


o The rest api request made by the client would first be handled by the jersey servlet and
then the control is passed to our classes.
 Now what happens if I want to use Apache CXF/Rest Easy in my application and not Jersey.
o I would have to change the servlet configured in web.xml with the servlet provided by
Apache CXF or Rest Easy
o Further is forces me to use a servlet container – tomcat here, which I don’t want to do.
o I just want to create an app without having to use a servlet container that just runs as a
simple java class.
o Now there is a cooler way we can do this without having to change the servlet each time
I change from one vendor to another.
 Jersey has this class called Application we can use and run our classes as a
simple java application and not as a web application.
 Creating the Project
o In Eclipse File New Maven Project Use the Default Workspace Location
Next Select the Archetype ::- maven-archetype-webapp
o If you don’t see this archetype then you need Add the archetype by clicking on “Add
Archetype”
 Archetype Group Id ::- org.apache.maven.archetypes
 Archetype Artifact Id ::- maven-archetype-webapp
 Archetype Version ::- 1.4
 Now the Archetype ‘maven-archetype-webapp’ is downloaded.
o Select the Archetype “maven-archetype-webapp”  Next
o Project Details
 Group Id :: com.samples.my
 ArtifactId :: advanced-jaxrs-01 Finish
Physical Location ::- C:\Users\ADMIN\Documents\workspace-sts-3.9.12.RELEASE\ adv-jaxrs-01
 Pom.xml
<?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">

Page 90 of 121
<modelVersion>4.0.0</modelVersion>

<groupId>com.samples.my</groupId>
<artifactId>adv-jaxrs-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>

<name>adv-jaxrs-01 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>
<jersey.version>2.16</jersey.version>
</properties>

<dependencies>
<dependency>
<groupId>org.glassfish.jersey.bundles</groupId>
<artifactId>jaxrs-ri</artifactId>
<version>2.16</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>2.16</version>
</dependency>
</dependencies>

<build>
<finalName>adv-jaxrs-01</finalName>

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<inherited>true</inherited>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
 04 Setting up a JAX-RS application

 RCL on the project and -> Maven -> Update Project


o This updates all the dependencies
 RCL on the project and New  Folder  Select the src/main and give the name as java
o Creates a folder under src/main/java and marks it as a source folder.
 First Step
o Create a new resource class MyResource.java under package com.samples.my.rest

Page 91 of 121
package org.samples.my.rest;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("test")
public class MyResource {

@GET
@Produces(MediaType.TEXT_PLAIN)
public String testMethod() {
return "Hello Prem...";
}
}
 Second Step
o In the previous courses we configured a Jersey Servlet in the web.xml
o Now we have to do something similar but not in the web.xml but in java
 MyRestApiApp.java and it should extend Application
 Next I need to map it to a url pattern similar to /webapi/* in web.xml of previous
examples
 and you do this using @ApplicationPath
package org.samples.my.rest;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("webapi")
public class MyRestApiApp extends Application {

// This is where I configure all my resource classes that are not singletons.
// public Set<Class<?>> getClasses() {
// return Collections.emptySet();
// }

// This is where I configure all my resource classes that are singletons.


// public Set<Object> getSingletons() {
// return Collections.emptySet();
// }

 Start Tomcat and access the URL


o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/adv-jaxrs-01/webapi/test
Displays - Hello Prem…

Page 92 of 121
 05 The Application Class

 Unit 1 :: JAX-RS Extensions


 06 Resource Lifecycle

 MyResource.java

package org.samples.my.rest;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("test")
public class MyResource {

private int count;

@GET
@Produces(MediaType.TEXT_PLAIN)
public String testMethod() {
count = count + 1;
return "Hello Prem... This method was called "+count+" time(s).";
}
}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/adv-jaxrs-01/webapi/test
Displays - Hello Prem... This method was called 1 time(s).

Access the url http://localhost:8080/adv-jaxrs-01/webapi/test for a second time


Displays - Hello Prem... This method was called 1 time(s).

 Now what happened? It should have printed “This method was called 2 time(s)” but it didn’t.
o This is because every time you make the api request to the url http://localhost:8080/adv-
jaxrs-01/webapi/test JAX-RS is creating a new instance of this MyResource clas.
Page 93 of 121
o This is the default behavior for each and every resource in JAX-RS. They are said to be
request-scoped.
o So how do I print the message “This method was called 2 time(s)”
 You can do that by making the MyResource class as a singleton.
 You can do this by configuring this MyResource class in the
getSingletons method of the class that extends Application. In our case
this is the MyRestApiApp.
 You can add @Singleton to MyResource.java class.

package org.samples.my.rest;

import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("test")
@Singleton
public class MyResource {

private int count;

@GET
@Produces(MediaType.TEXT_PLAIN)
public String testMethod() {
count = count + 1;
return "Hello Prem... This method was called " + count + " time(s).";
}
}

 Start Tomcat and access the URL


o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/adv-jaxrs-01/webapi/test
Displays - Hello Prem... This method was called 1 time(s).

Access the url http://localhost:8080/adv-jaxrs-01/webapi/test for a second time


Displays - Hello Prem... This method was called 2 time(s).

 07 Param Annotations and Member Variables

 MyResource.java

package org.samples.my.rest;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
Page 94 of 121
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

@Path("{pathParam}/test")
public class MyResource {

@PathParam("pathParam") private String pathParamExample;


@QueryParam("query") private String queryParamExample;

@GET
@Produces(MediaType.TEXT_PLAIN)
public String testMethod() {
return "Hello Prem...! Path param used " + pathParamExample
+ " and Query param used " + queryParamExample;
}
}

 Start Tomcat and access the URL


o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/adv-jaxrs-01/webapi/value/test?query=queryValue
Displays - Hello Prem...! Path param used value and Query param used queryValue
 Think about why this works?
This works because the MyResource instance will be created after you make the request
o
to the url http://localhost:8080/adv-jaxrs-01/webapi/value/test?query=queryValue
o So there is a new instance per request.
o So when the request gets to the server JAX-RS creates a new instance of the
MyResource class
 The member variables would be initialized to blank values.
 But when you have the @PathParam or @QueryParam annotations for the
member variables
 , JAX-RS is going set the values of those member variables (which have
the @PathParam and @QueryParm ) by picking their values from the
request.
 Since the instance of MyResource class is created after the request invocation
JAX-RS has the values ready to be substituted against the member variables.
 This is not very different from having the @PathParam or @QueryParam as part
of the testMethod() itself as method arguments.
 Now what happens when MyResource.java class is made a singleton by adding @Singleton
package org.samples.my.rest;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

@Path("{pathParam}/test")
@Singleton
public class MyResource {

@PathParam("pathParam") private String pathParamExample;


@QueryParam("query") private String queryParamExample;

Page 95 of 121
@GET
@Produces(MediaType.TEXT_PLAIN)
public String testMethod() {
return "Hello Prem...! Path param used " + pathParamExample
+ " and Query param used " + queryParamExample;
}
}

 Start Tomcat and access the URL


o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.

org.glassfish.jersey.server.model.ModelValidationException: Validation of the application resource


model has failed during application initialization.
[[FATAL] Parameter pathParamExample of private java.lang.String
org.samples.my.rest.MyResource.pathParamExample cannot be injected into singleton resource.;
o Errors are thrown during tomcat container startup-Why?
 Now this instance of MyResource.java is created before the request.
 Singleton resource are instantiated during application startup. So you cannot
inject request-specific information to their member variables.
 Comment out the @Singleton annotation in the MyResource.java class.
 08 Param Converters

 Think about passing a PathParam or a QueryParam which is an integer


o The path param or query param is sent over http as a string.
o JAX-RS inspects the method argument which is annotated as @PathParam or
@QueryParam and see its datatype and if that is an integer
o It converts the string to an integer and passing the value to the method.
o This conversion of string to an integer behind the scenes is done by ParamConverters.
 Jersey has a bunch of ParamConverters which convert the string to the primitive data types.
 Now what if you have custom datatype?
o Jersey will not know how to convert the string to the custom datatype.
o In this case we would have to create our own ParamConverter to do the conversion.

 MyDate.java
package org.samples.my.rest;

public class MyDate {

private int date;


private int month;
private int year;
// getters and setters

Page 96 of 121
@Override
public String toString() {
return "MyDate [date=" + date + ", month=" + month + ", year=" + year + "]";
}
}
 DateResource.java
package org.samples.my.rest;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("date/{dateString}")
public class DateResource {

@GET
@Produces(MediaType.TEXT_PLAIN)
public String getRequestedDate(@PathParam("dateString") String myDateString) {
return "Got " + myDateString;
}
}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::-
http://localhost:8080/adv-jaxrs-01/webapi/date/today Displays  Got today
http://localhost:8080/adv-jaxrs-01/webapi/date/tomorrow Displays  Got tomorrow
http://localhost:8080/adv-jaxrs-01/webapi/date/yesterday Displays  Got yesterday
 Now change the datatype of the method argument of getRequestedDate to MyDate from String.
o During tomcat startup it throws an exception
org.glassfish.jersey.server.model.ModelValidationException: Validation of the application
resource model has failed during application initialization.
[[FATAL] No injection source found for a parameter of type public java.lang.String
org.samples.my.rest.DateResource.getRequestedDate(org.samples.my.rest.MyDate) at index 0.;

 Now how to tell JAX-RS how to convert the string to MyDate.


o Using the ParamConverter and the ParamConverterProvider

o The ParamConverter has the methods to convert


 the string to the custom datatype

Page 97 of 121
 custom datatype to string
o The ParamConverterProvider
 You need to tell JAX-RS that there is a converter that can convert string to
MyDate and you tell that using the ParamConverterProvider.
o When the application starts up
 you have a bunch of these Provider’s that are registered with Jersey and
 when jersey finds a type it does not know how to convert
 it is going to ask each of those ParamConverterProviders (annotated with
@Provider)
 hey ParamConverterProvider I have this type can you give me a ParamConverter
 If the Provider says no I cannot, it will ask the next Provider and so on, till it
finds a Provider that says I got a ParamConverter for that type.

 If none of the Providers give a ParamConverter for the type then jersey throws
the exception we have already seen.
 09 Implementing Custom Param Converters

 DateResource.java
package org.samples.my.rest;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("date/{dateString}")
public class DateResource {

@GET
@Produces(MediaType.TEXT_PLAIN)
public String getRequestedDate(@PathParam("dateString") MyDate myDate) {
return "Got " + myDate.toString();
}
}
 MyDateConverterProvider.java
package org.samples.my.rest;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Calendar;
Page 98 of 121
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
import javax.ws.rs.ext.Provider;
@Provider
public class MyDateConverterProvider implements ParamConverterProvider {

@Override
public <T> ParamConverter<T> getConverter(final Class<T> rawType, Type genericType,
Annotation[] annotations) {
if (rawType.getName().equals(MyDate.class.getName())) {
return new ParamConverter<T>() {

@Override
public T fromString(String value) {

Calendar requestedDate = Calendar.getInstance();


if ("tomorrow".equalsIgnoreCase(value)) {
requestedDate.add(Calendar.DATE, 1);
}
else if ("yesterday".equalsIgnoreCase(value)) {
requestedDate.add(Calendar.DATE, -1);
}
MyDate myDate = new MyDate();
myDate.setDate(requestedDate.get(Calendar.DATE));
myDate.setMonth(requestedDate.get(Calendar.MONTH));
myDate.setYear(requestedDate.get(Calendar.YEAR));

return rawType.cast(myDate);
}

@Override
public String toString(T myBean) {
if (myBean == null) {
return null;
}
return myBean.toString();
}
};
}
return null;
}
}
Start Tomcat and access the URL

o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- Month is Zero index based
http://localhost:8080/adv-jaxrs-01/webapi/date/today
Displays  Got MyDate [date=28, month=1, year=2021]
http://localhost:8080/adv-jaxrs-01/webapi/date/tomorrow
Page 99 of 121
Displays  Got MyDate [date=1, month=2, year=2021]
http://localhost:8080/adv-jaxrs-01/webapi/date/yesterday
Displays  Got MyDate [date=27, month=1, year=2021]

 10 MessageBodyReaders and MessageBodyWriters

 The MessageBodyReaders and MessageBodyWriters are what converts the raw content that is
sent over the request/response to our java types and vice versa.
 For eg., you want to send an object as a response for a resource
o You annotate it with @Produces(“application/json”)
o There is something that has to take the raw object and convert it to json and
o Send it to the client.
o This conversion happens thanks to a MessageBodyWriter.
o True to its name it is writing to a message body.
o Whenever you need to send some object in the message body the conversion to a
string/json/xml payload happens due to a MessageBodyWriter.
o Similarly when you are accepting input in json/xml thru a post/put request.
 That request will have a payload that will be converted to a java type using a
MessageBodyReader.
 Its reading from a Message Body.
 These two classes are very important when you are doing marshaling/unmarshaling objects in
java.
 This is one thing that confuses peope and that is the difference between

o The difference is actually in the name

o The MessageBody is basically dealing with the content of the message


 If you were to make a PUT request, the request itself will contain some payload
Page 100 of 121
in the request body
 This is different from Params
 We looked at various Param annotations - @PathParam, @QueryParam, etc.
 Basically these are parameters to the request and not the content of the request
itself.
 MessageBody deals with the content of the request/response body – we are
dealing with MessageBodyReaders and MessageBodyWriters
 But if you are sending Parameters to a request like the @HeaderParam,
@CookieParam, @PathParam, @QueryParam those are ParamConverters.
o Let’s see some examples to make this distinction clear
 Example 1
 The PathParam would need the ParamConverter
 The MessageBody would need the
MessageBodyReader/MessageBodyWriter.

 Example 2
 There are is no body here as this is a GET request and hence no
messagebodyreader/messagebodywriter is required here
 We still need the ParamConverters t convert the PathParam and
QueryParam

 Example 3
Page 101 of 121
 The Header Param would need the ParamConverter
 The MessageBody would need the
MessageBodyReader/MessageBodyWriter.

 11 Implementing a MessageBodyWriter

 Showcasing the need for a MessageBodyWriter


o Returing a Date in the MyResource.java  testMethod()

package org.samples.my.rest;

import java.util.Calendar;
import java.util.Date;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("test")
public class MyResource {

@GET
@Produces(MediaType.TEXT_PLAIN)
public Date testMethod() {
return Calendar.getInstance().getTime();
}
}

 Start Tomcat and access the URL


o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/adv-jaxrs-01/webapi/test

And it turns out that Jersey does not know how to convert Date object to plain text and hence
Throws the exception with Status Code :: 500
Mar 03, 2021 11:06:06 PM
Page 102 of 121
org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor
aroundWriteTo
SEVERE: MessageBodyWriter not found for media type=text/plain, type=class java.util.Date,
genericType=class java.util.Date.
 So we need to add a MessageBodyWriter – DateMessageBodyWriter.java
o That converts the Date object to plain text.

package org.samples.my.rest;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Date;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

@Provider
@Produces(MediaType.TEXT_PLAIN)
public class DateMessageBodyWriter implements MessageBodyWriter<Date> {

@Override
public long getSize(Date arg0, Class<?> arg1, Type arg2, Annotation[] arg3,
MediaType arg4) {

return -1;
}

@Override
public boolean isWriteable(Class<?> type, Type arg1, Annotation[] arg2,
MediaType arg3) {
return Date.class.isAssignableFrom(type);
}

@Override
public void writeTo(Date date,
Class<?> type,
Type type1,
Annotation[] antns,
MediaType mt,
MultivaluedMap<String, Object> mm,
OutputStream out) throws IOException, WebApplicationException {

out.write(date.toString().getBytes());
Page 103 of 121
}

}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/adv-jaxrs-01/webapi/test
Displays -> Wed Mar 03 23:47:33 IST 2021

 12 Custom Media Types

 Not only can we create MessageBodyReader’s and MessageBodyWriter’s for known Media
Types we can also create them for customer Media Types.
 The MediaType.APPLICATION_JSON is nothing but an arbitrary string as shown
public final static String APPLICATION_JSON = "application/json";
 So we can create our own Media type say “text/shortdate” and implement MessageBodyReader
and MessageBodyWriter for it.
 MyResource.java

package org.samples.my.rest;

import java.util.Calendar;
import java.util.Date;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("test")
public class MyResource {

@GET
@Produces("text/shortdate")
public Date testMethod() {
return Calendar.getInstance().getTime();
}
}

 Start Tomcat and access the URL


o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/adv-jaxrs-01/webapi/test

And it turns out that Jersey does not know how to convert Date object to “text/shortdate” and hence
Throws the exception with Status Code :: 500
Mar 03, 2021 11:06:06 PM
org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor
aroundWriteTo
SEVERE: MessageBodyWriter not found for media type=text/shortdate, type=class java.util.Date,
genericType=class java.util.Date.
 So we need to add a MessageBodyWriter – ShortDateMessageBodyWriter.java

Page 104 of 121


o That converts the Date object to custom media type “text/shortdate”

package org.samples.my.rest;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Date;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

@Provider
@Produces("text/shortdate")
public class ShortDateMessageBodyWriter implements MessageBodyWriter<Date> {

@Override
public long getSize(Date arg0, Class<?> arg1, Type arg2, Annotation[] arg3, MediaType
arg4) {
return -1;
}

@Override
public boolean isWriteable(Class<?> type, Type arg1, Annotation[] arg2, MediaType
arg3) {
return Date.class.isAssignableFrom(type);
}

@Override
public void writeTo(Date date, Class<?> type, Type type1, Annotation[] antns, Medi-
aType mt,
MultivaluedMap<String, Object> mm, OutputStream out) throws IOException, WebAp-
plicationException {

String shortDate = date.getDate() + "-" + date.getMonth() + "-" + date.getYear();


out.write(shortDate.getBytes());
}
}
 Start Tomcat and access the URL
o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/adv-jaxrs-01/webapi/test
Displays  4-2-121

 Unit 3 :: JAX-RS Client


 13 JAX-RS Client

 So far we have used Postman to make the rest api calls.


 What if you wanted to do the same programmatically – we want to access rest api using java.
Page 105 of 121
 One more thing we need not concern ourselves with the technology being used on the server – it
could be java, spring, jersey or c#.
o All we care is we are going to make the call
o Get a response from the server.
 We will use the messenger rest api we developed in the “Rest WebServices”.

 14 Writing a JAX-RS Client & 17 Making a Post Request

 Start Tomcat with messenger and access the URL


o RCL on Tomcat and Click on Start  The Server starts.
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/messenger/webapi/messages
Displays the following ::
[
{"author":"Prem",
"created":"2021-03-04T01:01:45.686",
"id":1, "links":[],"message":"Helloorld"},
{"author":"Vinodh",
"created":"2021-03-04T01:01:45.686",
"id":2,"links":[],"message":"Hello Jersey"}
]
 RestApiClient.java
package org.samples.my.client;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

public class RestApiClient {

public static void main(String[] args) {

Client client = ClientBuilder.newClient();

WebTarget baseTarget = client.target("http://localhost:8080/messenger/webapi/");


WebTarget messagesTarget = baseTarget.path("messages");
WebTarget singleMessageTarget = messagesTarget.path("{messageId}");

Message message1 = singleMessageTarget


.resolveTemplate("messageId", "1")
.request(MediaType.APPLICATION_JSON)
.get(Message.class);
System.out.println("Message with id 1 :"+message1.getMessage());

Message message2 = singleMessageTarget


.resolveTemplate("messageId", "2")
.request(MediaType.APPLICATION_JSON)
.get(Message.class);
System.out.println("Message with id 2 :"+message2.getMessage());

Message newMessage = new Message(4, "My New message from JAX-RS client", "prem");
Response postResponse = messagesTarget.request().post(Entity.json(newMessage));

Page 106 of 121


if (postResponse.getStatus() != 201) {
System.out.println("Error");
}
Message createdMessage = postResponse.readEntity(Message.class);
System.out.println(createdMessage.getMessage());
}
}
 Start the tomcat server with the messenger app
 RCL on RestApiClient and - Run as Java Application
Displays the following output ::
Message with id 1 :Hello World
Message with id 1 as string :{
"author":"Prem", "id":1, "message":"Hello World"
"created":"2021-03-04T01:01:45.686"}
Message with id 2 :Hello Jersey
My New message from JAX-RS client

 18 Creating Invocations

 The idea behind invocations is this


o You can prepare a request and you can put in all the things you need that the request
should do.
 You know where the target is,
 what the format is
 what the body of the request is
 the http method
o The idea is to create a request object with all the information – it is ready to go, only
that you haven’t made the call yet.
o You hold on to the object and you can pass it around and have some other code to issue
the request without having to know any details about the request.
 InvocationDemo.java

package org.samples.my.client;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

public class InvocationDemo {

public static void main(String[] args) {

InvocationDemo demo = new InvocationDemo();


Invocation invocation = demo.prepareRequestForMessagesByYear(2015);
Response response = invocation.invoke();
System.out.println(response.getStatus());

Page 107 of 121


}

public Invocation prepareRequestForMessagesByYear(int year) {


Client client = ClientBuilder.newClient();

return client.target("http://localhost:8080/messenger/webapi/")
.path("messages")
.queryParam("year", year)
.request(MediaType.APPLICATION_JSON)
.buildGet();
}
}
 Start the tomcat server with the messenger app
 RCL on InvocationDemo and - Run as Java Application
Displays the following output :: 200

 19 Handling Generic Types

 We have seen how we can use certain signatures of your client api in order to cast to a response
to the type that you need.
 For eg.,
o Give me back an instance of response

Client client = ClientBuilder.newClient();

WebTarget baseTarget = client.target("http://localhost:8080/messenger/webapi/");


WebTarget messagesTarget = baseTarget.path("messages");
WebTarget singleMessageTarget = messagesTarget.path("{messageId}");

Message message2 = singleMessageTarget


.resolveTemplate("messageId", "2")
.request(MediaType.APPLICATION_JSON)
.get();

o Give me back an instance of Message


Message message2 = singleMessageTarget
.resolveTemplate("messageId", "2")
.request(MediaType.APPLICATION_JSON)
.get(Message.class);

oWhich says hey JAX-RS client I expect the message as a response and JAX-RS is smart
enough to give you a Message instance.
o You got the response and sets the right instance value and gives it back to you.
 Now this is cool but we do face one particular problem with Generics.
o If you are dealing with a JAX-RS client which returns a Generic type - like List

List response1 = client.target("http://localhost:8080/messenger/webapi/")


.path("messages")
.queryParam("year", 2015)
.request(MediaType.APPLICATION_JSON)
.get(List.class);
System.out.println("List : " + response1);

Page 108 of 121


o This throws the following exception when executed
Exception in thread "main"
org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader
not found for media type=application/json, type=interface java.util.List, genericType=interface
java.util.List.
 Here we need to use the GenericType

package org.samples.my.client;

import java.util.List;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;

public class GenericDemo {

public static void main(String[] args) {


Client client = ClientBuilder.newClient();

// If I want a list of messages can I say .get(List.class)


List response1 = client.target("http://localhost:8080/messenger/webapi/")
.path("messages")
.queryParam("year", 2021)
.request(MediaType.APPLICATION_JSON)
.get(new GenericType<List<Message>>() { });
System.out.println("List : " + response1);
}
}

 Start the tomcat server with the messenger app


 RCL on GenericDemo and - Run as Java Application
Displays the following output ::
List : [org.samples.my.client.Message@1972e513, org.samples.my.client.Message@7ae0a9ec]

Page 109 of 121


 Unit 4 :: Rest Api Authentication
 20 Unit Introduction

 In this section we are going to look at Authentication


o How to restrict access to our rest api to only select users
o by allowing them to login using a username and a password.

 21 Implementing Filters

 In this section I am going to talk about filters in JAX-RS


 The best way to implement Authentication in JAX-RS is by using filters.
 Filter is a way to take some of the cross cutting concerns/logic out of your individual resources
into a common filter class.
 The idea is if you have some logic you have to apply to different apis/resources rather than
make a copy of the logic in each of the resources. You take that logic out and create one
common class and then you apply it to different requests.
 You create a filter with the cross cutting logic and configure what are the apis this filter should
be applied to. Eg. Would be logging.
 There are 2 types of filters

Page 110 of 121


 MyApp.java
package org.samples.my;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("webapi")
public class MyApp extends Application {

}
 MyResource.java
package org.samples.my;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("test")
public class MyResource {

@GET
@Produces(MediaType.TEXT_PLAIN)
public String testMethod() {
return "It works!";
}
}

 PoweredByResponseFilter
package org.samples.my;

import java.io.IOException;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;

@Provider
public class PoweredByResponseFilter implements ContainerResponseFilter {

@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext)
throws IOException {
responseContext.getHeaders().add("X-Powered-By", "Prem Vinodh");
}
}

 LoggingFilter.java
package org.samples.my;

import java.io.IOException;
Page 111 of 121
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;

@Provider
public class LoggingFilter implements ContainerRequestFilter, ContainerResponseFilter {

@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
System.out.println("Request filter");
System.out.println("Headers: " + requestContext.getHeaders());
}

@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext)
throws IOException {
System.out.println("Response filter");
System.out.println("Headers: " + responseContext.getHeaders());
}
}
 Start Tomcat and access the URL
o RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/adv-jaxrs-01/webapi/test
Displays  It Works
In Response Headers -> x-powered-by Powered By RocketScience

On the console  It prints the following

 The filter is guaranteed to run for both the success and failure responses.

Page 112 of 121


 22 Rest API Authentication Mechanisms

 But this approach has a problem and that is Rest Apis are stateless

o Stateless means the server does not maintain any state.


o Ideally when a client makes the request to a rest api
 The rest api does what it needs to do to serve that request
 It does all the processing and sends the response
 And then completely forgets about the request.
o That is how Rest Apis should be, they should not remember a previous interaction
between the client and the server.
o And if this is true, then we have a problem – session bassed Authentication does not
work anymore.
 There is no session on the server.
 So the client cannot send now a session token back because the server does not
know what that it or to whom it belongs.

Page 113 of 121


o This is the problem with Rest Api’s and there are a many ways to solve this problem.
 There are bunch of different authentication mechanisms that are stateless s and
we could use here for our rest api’s
 The most basic type of Authentication is the – Basic Auth.
Basic Auth

 This is how it works – the client has to send the username/password everytime it access the rest
api. Its as simple as that.

 You send the username/password in the header

 Steps to set the username and password as the header in Basic Auth

 On the server side

Page 114 of 121


 Encoding is not encryption

oAnybody who has access to this encode string can decode it and get the username and
password.
 So we should always make sure to send this Basic Auth over https.
 Why encode if there is no security and anyone can still retrieve the username:password?
o We are encoding to convert any non http compatible characters to http.

 Advantages and Disadvantages

Page 115 of 121


 Better Solutions

 23 Sending Basic Auth Requests

 Postman

 Click on Update Request after entering the username and the password

Page 116 of 121


 HMAC

In Java
https://stackoverflow.com/questions/39355241/compute-hmac-sha512-with-secret-key-in-java

In Python
 Hmac_example.py
import hmac
import time

def make_digest(message, key):


key = bytes(key, ‘UTF-8’)
message = bytes(message, ‘UTF-8’)

digester = hmac.new(key, message, hashlib.sha256)


digest = disgeter.digest()
encoded_digest = base64.urlsafe_b64encode(digest)

return str(encoded_digest, ‘UTF-8’)

http_action = ‘POST’
requested_resource = ‘abc_method’
secret =’some_long_string’

for in in range(1):
utc_now = datetime.datetime.utcnow()
msg = f’{ http_action }+{ requested_resource }+utc_now’
result = make_digest(msg, secret)
print(f’{i}) {msg} => HMAC {result} | Date {utc_now}’)
time.sleep(0.01)

 24 Implementing Rest API Authorization

 SecuredResource.java

package org.samples.my.rest;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("secured")
public class SecuredResource {

@GET

Page 117 of 121


@Path("message")
@Produces(MediaType.TEXT_PLAIN)
public String securedMethod() {
return "This API is secured";
}
}
 SecurityFilter.java

package org.samples.my.rest;

import java.io.IOException;
import java.util.List;
import java.util.StringTokenizer;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import org.glassfish.jersey.internal.util.Base64;

@Provider
public class SecurityFilter implements ContainerRequestFilter {

private static final String AUTHORIZATION_HEADER_KEY = "Authorization";


private static final String AUTHORIZATION_HEADER_PREFIX = "Basic ";
private static final String SECURED_URL_PREFIX = "secured";

@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
if (requestContext.getUriInfo().getPath().contains(SECURED_URL_PREFIX)) {
List<String> authHeader =
requestContext.getHeaders().get(AUTHORIZATION_HEADER_KEY);
if (authHeader != null && authHeader.size() > 0) {
String authToken = authHeader.get(0);
authToken = authToken.replaceFirst(AUTHORIZATION_HEADER_PREFIX, "");
String decodedString = Base64.decodeAsString(authToken);
StringTokenizer tokenizer = new StringTokenizer(decodedString, ":");
String username = tokenizer.nextToken();
String password = tokenizer.nextToken();

if ("user".equals(username) && "password".equals(password)) {


return;
}

}
Response unauthorizedStatus = Response
.status(Response.Status.UNAUTHORIZED)
.entity("User cannot access the resource.")
.build();

Page 118 of 121


requestContext.abortWith(unauthorizedStatus);
}
}
}
 RCL on the project  Click Run As  Run on Server Finish.
Access the url in Postman ::- http://localhost:8080/adv-jaxrs-01/webapi/secured/message
Displays  For valid username/password

Displays  For invalid/no username/password

 Unit 5 :: Odds and Ends


 25 Filters and Interceptors

Filters Interceptors
Page 119 of 121
Filters manipulate the headers or meta-data Interceptors manipulate the actual body of the
information request/response.
Two Kinds of Filters Two Kinds of Interceptors
 ContainerRequestFilter  ReaderInterceptor
 ContainerResponseFilter  WriterInterceptor
In case of Filters the request and the response In case of Interceptors it is the request/response
information like headers, params, etc are body that is manipulated.
manipulated

 Interceptor Example for JAX-RS documentation

Page 120 of 121


 Client Side Filters and Interceptors

o Order of execution

 27 Conclusion

Page 121 of 121

You might also like