Professional Documents
Culture Documents
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
Page 5 of 121
02 REST and HTTP
Addresses
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
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
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
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.
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
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
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.
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
Some examples
o Getting a message
Page 17 of 121
o Updating a message
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
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
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
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 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 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 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
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.
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 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.
Page 35 of 121
The model also defines a Level 0 which is not a Restful API.
Richardson Maturity Model – Level 0
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
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
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 Message() {
}
MessageService.java
package com.samples.my.messenger.service;
import java.util.ArrayList;
import java.util.List;
import com.samples.my.messenger.model.Message;
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 {
@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 {
public Message() {
}
Page 44 of 121
this.created = new Date();
this.author = author;
}
}
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;
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;
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
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 {
@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
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 {
@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 {
@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
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 {
@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
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 {
@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;
import java.util.Date;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Profile {
public Profile() {
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 ProfileService() {
profiles.put("prem", new Profile(1L, "prem", "Prem", "Vinodh"));
}
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 {
@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);
}
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 MessageService() {
messages.put(1l, new Message(1l, "Hello World", "Prem"));
messages.put(2l, new Message(2l, "Hello Jersey", "Vinodh"));
}
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 {
@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);
}
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
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) {
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;
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 {
@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);
}
}
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 Comment() {
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 {
public Message() {
}
@XmlTransient
public Map<Long, Comment> getComments() {
return comments;
}
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import com.samples.my.messenger.database.DatabaseClass;
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"));
}
}
}
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 {
@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;
Page 69 of 121
comments.put(comment.getId(), comment);
DatabaseClass.getMessages();
return comment;
}
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 {
@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);
}
}
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 {
@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();
}
}
Page 73 of 121
27 Handling Exceptions
When we retrieve data for message id 200 which is not there we get 204 – No Content.
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 {
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"));
}
}
}
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
import javax.xml.bind.annotation.XmlRootElement;
public ErrorMessage() {}
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;
@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;
DatabaseClass.getMessages();
return comment;
}
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;
DatabaseClass.getMessages();
return comment;
}
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 {
public Message() {
}
@XmlTransient
public Map<Long, Comment> getComments() {
return comments;
}
Page 84 of 121
public void setLinks(List<Link> links) {
this.links = links;
}
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 {
@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;
}
// @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
02 Tomcat Setup
03 Setting up 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/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>
<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
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();
// }
Page 92 of 121
05 The Application Class
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 {
@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).
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 {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String testMethod() {
count = count + 1;
return "Hello Prem... This method was called " + count + " time(s).";
}
}
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 {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String testMethod() {
return "Hello Prem...! Path param used " + pathParamExample
+ " and Query param used " + queryParamExample;
}
}
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 {
Page 95 of 121
@GET
@Produces(MediaType.TEXT_PLAIN)
public String testMethod() {
return "Hello Prem...! Path param used " + pathParamExample
+ " and Query param used " + queryParamExample;
}
}
MyDate.java
package org.samples.my.rest;
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.;
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) {
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]
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
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
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();
}
}
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
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();
}
}
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
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 {
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;
Message newMessage = new Message(4, "My New message from JAX-RS client", "prem");
Response postResponse = messagesTarget.request().post(Entity.json(newMessage));
18 Creating Invocations
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;
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
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
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
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;
21 Implementing Filters
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
The filter is guaranteed to run for both the success and failure responses.
But this approach has a problem and that is Rest Apis are stateless
This is how it works – the client has to send the username/password everytime it access the rest
api. Its as simple as that.
Steps to set the username and password as the header in Basic Auth
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.
Postman
Click on Update Request after entering the username and the password
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
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)
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
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 {
@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();
}
Response unauthorizedStatus = Response
.status(Response.Status.UNAUTHORIZED)
.entity("User cannot access the resource.")
.build();
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
o Order of execution
27 Conclusion