Notice! This post is more than a year old. It may be outdated.
This article was originally published on SitePoint on December 12, 2016. For more interesting content about Java, check out SitePoint’s Java channel.
Dropwizard is a Java framework for building RESTful web services. In essence, it is a glue framework which bundles together popular and battle-tested Java libraries and frameworks to make it easier to start building new RESTful web services. This post explores how to implement create, read, update, delete operations (CRUD) on a resource.
Last time we set out on a journey to build a simple RESTful web service with Dropwizard.
The goal was to build a back-end for a hypothetical events application that would allow you to search for events based on your search criteria.
It should be able to provide a list of events, add new events and modify existing ones.
Make sure you read the previous post because we will be continuing were we left off.
If you wish to follow along, clone the GitHub repository and check out the register-resource
tag.
Quick recap
In the previous article we created a new Dropwizard project, added custom configuration, created a representation and a very basic resource.
Because we didn’t have a data store in place, clients were only able to make a GET request to /events
and receive hard-coded data.
In this follow-up tutorial we’re going to expand the feature set of the application to meet the requirements we initially set.
Introducing a data store
This tutorial is going to cover how to implement CRUD functionality for our application.
To keep things simple and not bog you down in database specifics, I’m not going to set one up.
Instead we’re going to implement a very basic in-memory database backed by java.util.List
.
To achieve better code reuse, it’s better to depend on abstractions, which in Java terms means not depending on concrete types. We will hence declare the public API of our in-memory data store in an interface.
Create a new interface in the com.mycompany.core
package called EventRepository
.
We will expand this interface throughout the rest of the article, adding methods as we need them.
Implementing EventRepository
We have created an interface that defines the public API of EventRepository
.
Now we need to come up with an implementation.
Note that the following class is by no means a solution that you’d deploy to production.
For starters, because we’re going to store events in a list, we’re going to run into concurrency issues.
The list is a shared resource which means it has to be thread safe.
Since concurrency is not the topic of this article, pretend that you are the only user.
The idea is not to worry about the storage of data but rather how to create methods in EventResource
so we could meet the requirements we initially set.
Create a new class called DummyEventRepository
in com.mycompany.core
package and make it implement the EventRepository
interface.
When DummyEventRepository
is instantiated, initData
is called.
This method reads a JSON file containing dummy data which will be used to populate our in-memory data store.
The JSON file is read with Guava’s Resource
class and is parsed with Jackson’s ObjectMapper
class.
Create a new JSON document called dummy_data.json
and place it in src/main/resources
.
The following is the data I will be using.
EventResource, EventRepository and DummyEventRepository
In the following diagram you can see the three types in action.
EventResource is responsible for for handling HTTP requests.
When it needs to access or modify data, it delegates this concern to an instance of EventRepository
.
DummyEventRepository
is an implementation of EventRepository
and it will act as an in-memory data store.
EventResource
needs a reference to an implementation of EventRepository
to do its job.
I don’t want to instantiate DummyEventRepository
in the EventResource
class directly.
This would be a violation of the dependency inversion principle.
The less it knows about concrete implementations of other classes the better.
Therefore I added a constructor parameter to the EventResource
class which can be used to inject a concrete implementation of EventRepository
.
Finally, we need to wire together our object graph.
In our application’s run
method, get a reference to an instance of DummyEventRepository
and pass it to EventResource
.
Retrieving all events
We can start using our newly created EventRepository
by refactoring the allEvents
method in EventResource
class.
At the moment it returns a list of events which are created on the fly.
But it would make more sense if it returned all events from the in-memory data store.
So let’s make the required modifications to make that happen.
Modify the EventRepository
interface and declare a method for finding all events as well.
Create an implementation of the method declared in the interface in the DummyEventRepository
class.
Returning the list of events is all you need to do for now.
Now modify EventResource
to delegate the search of all events to an implementation of EventRepository
.
When running the application, instead of getting hard-coded data as we did in the previous post, you should see a list of events that are retrieved from EventRepository
when visiting http://localhost:8080/api/events
.
In reality, you don’t want to get all the data at once. There could be thousands of records and retrieving all of them would create unnecessary overhead. In the front-end, it is reasonable to show only a small selection of events. For example, when you do a Google search, you’ll get results that are distributed among multiple pages. This act is called pagination. Although an important feature, we’re going to cover it in a future article.
Retrieving a single event
In a RESTful API, retrieving a single resource is usually done via its ID.
For example, if we’re interested in reading the state of an event with an ID of 123, we should make a GET request to /events/123
.
But how can we easily access the ID without doing complex string manipulation?
The solution is the @PathParam
annotation.
Let’s have look at how to use it in code.
Add the following method to EventResource
.
First we declare that this method should respond to GET requests that include an ID in the HTTP path.
Remember that at the beginning of the EventResource
class we used @Path("events")
to indicate that the path of the resource starts with /events
.
Annotating a method with the @Path
annotation adds another segment to the resource path.
The curly braces around the id
indicate that this should be treated as a path parameter.
Using the LongParam
type ensures that the value of the id
read from the path must be of type Long
.
Otherwise Dropwizard will return an HTTP 400 response.
Finding an event by ID is relatively easy.
Add another method to EventRepository
interface.
The method returns an Optional
of type Event
.
I’m using Optionals to handle the possible case where the event with the given ID might not exist.
The following is the implementation of this method in DummyEventRepository
.
Now let’s get back to finishing the event
method in the EventResource
class.
Since there is a possibility that the requested event does not exist, we should be ready to return an appropriate result to the client.
Using Optionals makes it really easy and concise to handle the case where the retrieved object might be null
.
In this example, if the event exists, it is returned to the client.
Otherwise a new WebApplicationException
, a run-time exception from JavaEE, is thrown.
Dropwizard takes care of handling the exception and returns an error message to the client.
Creating a new Event
Creating a new event is done using the HTTP POST verb. To get things rolling, let’s first write the required code to add a new event to our data store. Because we currently store records in a list, the addition of a new event is straightforward.
Add a new method called save
to the EventRepository
interface.
The repository will assign an ID to the event being saved and we want to communicate it back to the client.
Therefore the return type should be set to Event
.
The following is an implementation of the save method which you should place in the DummyEventRepository
class.
As we’re trying to avoid using an actual database for demonstration purposes, we have to do some of the work ourselves that the database would otherwise do for us. For instance, when creating a new event, we don’t know it’s ID beforehand. Therefore I’m using the Streams API to find the current maximum ID value, increment it by one to get the next ID and assign it to the new event object. When there’s no events stored, a new event will get an ID of 1. Of course, databases do a much better job at generating the next value for an ID but since this little demo application is probably never going to see large amounts of data, it should be fine.
Once we’re done with the saving of a new event, let’s do the required modifications in the EventRepository
class.
Add the following method.
To enable Jersey to receive an HTTP POST request, all we need to do is annotate a method with @POST
in our resource class.
Reading the request entity is done for you as well.
Adding an argument of type Event
to the method declaration informs Jersey to parse the body of the request and map it to an instance of Event
.
Keep in mind that no validation is performed.
If none of the fields in the request body match the fields in the Event
class, a new instance of Event
will still be created but all of its instance fields will be null
(except for id
which is 0L since it is a primitive type).
For testing I’m going to use the curl command line utility.
The response to that command should be the following.
The HTTP POST method is not idempotent. This means that if you execute the same request multiple times, you’re going to create new state on the server with each request.
Modifying an existing event
To update an existing event, we’re going to use the HTTP PUT method.
PUT is an idempotent method which means that executing the same request multiple times does not create additional state on the server.
As with retrieving a single event, we’re going to need to read the event’s ID from the path using the @PathParam
annotation.
Before we’re going to make the required modifications in our resource class, let’s implement the event update logic.
Declare an update
method in EventRepository
interface.
We should also communicate the updated entity back to the client.
There is a possibility that the event with the given ID does not exist.
Therefore I’m using Optional
of type Event
as the return type.
To make updating an event easier, I created a new method updateExceptId
that updates the name, description, location, and date of the event that it is called on to those of the one that was given to the method.
The following is the resulting implementation of the update
method which you should place in DummyEventRepository
.
Next, you’ll see how to implement the method that should receive the PUT request in the EventResource
class.
Technically there should be nothing new for you by now.
When we were looking at how to retrieve a single event, we looked at how to read the ID from the path using the @PathParam
annotation.
Later, when we implemented the addition of a new event, you saw how Jersey is able to map the JSON body into the Event entity.
Deleting an event
Although we did not set it as a goal in the initial requirements, let’s look at how to delete an event. The HTTP specification defines the DELETE method which is used to delete a resource. It is an idempotent operation.
First of all, I’m going to implement the deletion logic. Our repository interface is going to need an additional method.
Next, we need to implement it in DummyEventRepository
.
Now let’s move on to implementing a REST endpoint for deleting an event.
Add the following method to EventResource
class.
Just like with reading a single event, we’re using the @PathParam
annotation to get the ID of the event from the HTTP path.
But what’s different now is the return type.
Since there’s no event to return, the method has a return type of Response
.
This way we can send back an HTTP 200 OK response with no content.
Summary
Congratulations! Now you should have a good understanding of what Dropwizard is about. In the previous article, we looked at how to create a new Dropwizard application and configure it. Also a quick overview of the application structure was given. In this article you learned how to implement a simple RESTful API which enables you to create, read, update, and delete events. We have almost reached our goal. With the exception of search, we have implemented all the features we initially set out to accomplish. I encourage you to play with this application and implement a feature you think is missing.
Dropwizard has more advanced features we were not able to cover. For instance, every Dropwizard application has an admin interface which allows you to monitor your application using health checks. Additionally, a very important part of application development is testing. We’re going to have a look at these and much more in a future article.