Developing RESTful Web Services with JAX-RS (Jersey)
Java, Maven, Jersey, JAX-RS, REST, Java EE
The REST architectural style describes six constraints. These constraints, applied to the architecture, were originally communicated by Roy Fielding in his doctoral dissertation and defines the basis of RESTful-style.
Introduction
REST stands for REpresentational State Transfer. The REST architectural style describes six constraints. These constraints, applied to the architecture, were originally communicated by Roy Fielding in his doctoral dissertation and defines the basis of RESTful-style.
The six constraints are: (click on the constraint below to read more)
JAX-RS (JSR-339) as a specification defines a set of Java APIs for the development of Web services built according to the REpresentational State Transfer (REST) architectural style.
Jersey RESTful Web Services framework is an open source, production quality, framework for developing RESTful Web Services in Java that provides support for JAX-RS APIs and serves as a JAX-RS (JSR 311 & JSR 339) Reference Implementation.
Project Structure
At the end of this guide our folder structure will look similar to the following:
.
|__src/
| |__main/
| | |__java/
| | | |__com/
| | | | |__juliuskrah
| | | | | |__App.java
| | | | | |__Resource.java
| | | | | |__ResourceService.java
|__pom.xml
Prerequisites
To follow along this guide, you should have the following set up:
Optional
Creating Project Template
To create the project structure, we would use the Maven Archetype
to generate a project skeleton. In the directory
where you want to generate your project, run the following commands:
You will get a list of archetypes to select from. Select 1002: org.apache.maven.archetypes:maven-archetype-quickstart (An archetype which contains a sample Maven project.)
maven quickstart archetype. This is a simple maven project with just one class.
Type 1002
and hit enter.
Hit enter to accept the default of 1.1
.
Type group Id e.g. com.juliuskrah
and hit enter
Type artifact Id e.g. rest-example
and hit enter
Hit enter to accept 1.0-SNAPSHOT
and proceed
Hit enter to accept default and proceed
Review your selection and hit enter to proceed. At this stage your project is generated in rest-example
.
Setting up dependencies
To build a RESTful webservice with Jersey, we must add the Jersey dependencies to our pom.xml
:
file:
pom.xml
In the snippet above we have specified the dependencyManagement
so that we can omit the version of
jersey-container-netty-http
.
What we will Create
HTTP Method | URI | Action |
---|---|---|
GET | /api/v1.0/resources/ |
Retrieve list of resources |
GET | /api/v1.0/resources/[resource_id] |
Retrieve a resource |
POST | /api/v1.0/resources/ |
Create a new resource |
PUT | /api/v1.0/resources/[resource_id] |
Update an existing resource |
DELETE | /api/v1.0/resources/[resource_id] |
Delete a resource |
The HTTP GET method is used to read (or retrieve) a representation of a resource. In the “happy”
(or non-error)
path, GET returns a representation in XML
or JSON
and an HTTP response code of 200
(OK). In an error case, it
most often returns a 404
(NOT FOUND) or 400
(BAD REQUEST).
According to the design of the HTTP specification, GET (along with HEAD) requests are used only to read data and not
change it. Therefore, when used this way, they are considered safe. That is, they can be called without risk of data
modification or corruption — calling it once has the same effect as calling it 10 times, or none at all. Additionally,
GET (and HEAD) is idempotent, which means that making multiple identical requests ends up having the same result as a
single request.
Do not expose unsafe operations via GET — it should never modify any resources on the server.
Examples:
- GET http://www.example.com/resources/1
- GET http://www.example.com/resources/1/items
The POST verb is most-often utilized to create new resources. In particular, it’s used to create subordinate
resources. That is, subordinate to some other (e.g. parent) resource. In other words, when creating a new resource,
POST to the parent and the service takes care of associating the new resource with the parent, assigning an ID (new
resource URI), etc.
On successful creation, return HTTP status 201
, returning a Location
header with a link to the newly-created
resource with the 201 HTTP status.
POST is neither safe nor idempotent. It is therefore recommended for non-idempotent resource requests. Making two
identical POST requests will most-likely result in two resources containing the same information.
Examples:
- POST http://www.example.com/resources
- POST http://www.example.com/resources/1/items
PUT is most-often utilized for update capabilities, PUT-ing to a known resource URI with the request body
containing the newly-updated representation of the original resource.
However, PUT can also be used to create a resource in the case where the resource ID is chosen by the client instead
of by the server. In other words, if the PUT is to a URI that contains the value of a non-existent resource ID. Again,
the request body contains a resource representation. Many feel this is convoluted and confusing. Consequently, this
method of creation should be used sparingly, if at all.
Alternatively, use POST to create new resources and provide the client-defined ID in the body
representation—presumably to a URI that doesn’t include the ID of the resource (see POST above).
On successful update, return 200
(or 204
if not returning any content in the body) from a PUT. If using PUT for
create, return HTTP status 201
on successful creation. A body in the response is optional — providing one consumes
more bandwidth. It is not necessary to return a link via a Location header in the creation case since the client
already set the resource ID.
PUT is not a safe operation, in that it modifies (or creates) state on the server, but it is idempotent. In other
words, if you create or update a resource using PUT and then make that same call again, the resource is still there
and still has the same state as it did with the first call.
If, for instance, calling PUT on a resource increments a counter within the resource, the call is no longer
idempotent. Sometimes that happens and it may be enough to document that the call is not idempotent. However, it’s
recommended to keep PUT requests idempotent. It is strongly recommended to use POST for non-idempotent requests.
Examples:
- PUT http://www.example.com/resources/1
- PUT http://www.example.com/resources/1/items/1
DELETE is pretty easy to understand. It is used to delete a resource identified by a URI.
On successful deletion, return HTTP status 200
(OK) along with a response body, perhaps the representation of the
deleted item (often demands too much bandwidth), or a wrapped response. Either that or return HTTP status 204
(NO
CONTENT) with no response body. In other words, a 204
status with no body, or the JSEND-style response and HTTP
status 200
are the recommended responses.
HTTP-spec-wise, DELETE operations are idempotent. If you DELETE a resource, it’s removed. Repeatedly calling DELETE
on that resource ends up the same: the resource is gone. If calling DELETE say, decrements a counter (within the
resource), the DELETE call is no longer idempotent. As mentioned previously, usage statistics and measurements may be
updated while still considering the service idempotent as long as no resource data is changed. Using POST for
non-idempotent resource requests is recommended.
There is a caveat about DELETE idempotence, however. Calling DELETE on a resource a second time will often return a
404
(NOT FOUND) since it was already removed and therefore is no longer findable. This, by some opinions, makes
DELETE operations no longer idempotent, however, the end-state of the resource is the same. Returning a 404 is
acceptable and communicates accurately the status of the call.
Examples:
- DELETE http://www.example.com/resources/1
- DELETE http://www.example.com/resources/1/items
Building Resources
We will create a POJO
to represent our REST resource.
file:
src/main/java/com/juliuskrah/Resource.java
The Resource POJO
above consists of self explanatory feilds. The @XmlRootElement
is put on the class to instruct
jersey on how to represent the resource as XML.
The next thing is to wire up a ResourceService
.
file:
src/main/java/com/juliuskrah/ResourceService.java
In an actual production ready application, you will connect to a database. For the purpose of this tutorial, we will use a
static
list.
In the ResourceService
we have specified the root context path we are going to access the service
(/api/v1.0/resources
).
In the same service class we have also created a GET
endpoint which returns a list of all resources available
on the server. The resource will be represented as JSON
or XML
identified by
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
.
We will setup a Netty
server to serve our requests.
file:
src/main/java/com/juliuskrah/App.java
With the server setup we can test our REST resource. Start the server by running the following command:
With the Netty server up and running, open another shell window and execute the following cURL
command:
We can request for the same resource represented as XML
:
Let us write the REST operation for getting a specific resource /api/v1.0/resources/[resource_id]
:
file:
src/main/java/com/juliuskrah/ResourceService.java
The @Path
annotation takes a variable (denoted by {
and }
) passed by the client, which is interpreted by Jersey
in the @PathParam
and cast to a Long
as id
. The : [0-9]+
is a regular expression which constraint the client
to use only positive whole numbers otherwise return 404
to the client.
If the client passes the path parameter in the format the server accepts, the id
of the resource will be searched
from within the static
resources field. If it exist return the response to the client or else return a 404
.
Test this resource by running:
Now let us write our POST
method that creates a new resource:
file:
src/main/java/com/juliuskrah/ResourceService.java
What is happening in the above snippet is a POST
request that returns a 201
status code and a Location
header
with the location of the newly created resource.
If the resource being created already exists on the server an error code of 409
is returned by the server.
For those not using Windows, you should omit the escape \
.
The remaining two methods of our webservice is shown below:
file:
src/main/java/com/juliuskrah/ResourceService.java
Test the PUT
endpoint with the following command:
Test DELETE
endpoint with the following command:
Deploying to Heroku
To deploy this application to Heroku, we must ensure we have a Heroku account and Heroku CLI
. Navigate to
the root directory of your application and execute the following command from your terminal:
This creates a heroku app called murmuring-shore-59920
. Heroku defines an environment variable $PORT
which is
the HTTP port exposed over firewall for HTTP traffic. We will modify our source file to read this variable:
file:
src/main/java/com/juliuskrah/App.java
Heroku needs to be able to run our web service with all the dependencies. We add the maven dependency plugin to our project:
file:
pom.xml
Next create a Procfile
. This is a plain text file called Procfile
not procfile
or
procfile.txt
but just Procfile
.
In this file we will create a web
process type. The web
process type is a special process type that listens for
HTTP traffic.
file:
Procfile
web: java -cp target/classes:target/dependency/* com.juliuskrah.App
The application is ready to be deployed to Heroku with git add . && git push heroku master
. But first let us
test the application locally to make sure everything works. To test locally create an .env
file:
file:
.env
PORT=2222
This creates an environment variable for local testing:
Now that we are confident everything works, we will deploy to heroku:
We scale up a dyno with the following command:
The last command opens the heroku app in your default browser. If you get a 404
, that is normal because you
haven’t mapped any resource to /
. Just append the url with /api/v1.0/resources
to start hacking.
Conclusion
In this post we learned REST and touched a little on the constraints. We learned how it is HTTP
based.
We also talked about the basic methods
in HTTP and how to leverage them in a RESTful application. We also learned
how to deploy a REST application to Heroku.
As usual you can find the full example to this guide in the github repository. Until the next post, keep doing cool things .