Get Started With Microservices: A Dropwizard Tutorial
Dropwizard allows developers to quickly bootstrap their projects and package applications as easily deployable standalone services. It also happens to be relatively simple to use and implement.
In this tutorial, Toptal Freelance Software Engineer Dusan Simonovic will introduce you to Dropwizard and demonstrate how you can use this powerful framework to create RESTful web services with ease.
Dropwizard allows developers to quickly bootstrap their projects and package applications as easily deployable standalone services. It also happens to be relatively simple to use and implement.
In this tutorial, Toptal Freelance Software Engineer Dusan Simonovic will introduce you to Dropwizard and demonstrate how you can use this powerful framework to create RESTful web services with ease.
Dusan is a back-end Java developer with eight years of experience in Java development and a number of large projects under his belt.
PREVIOUSLY AT
We’re all witnessing a rise in the popularity of microservice architectures. In a microservice architecture, Dropwizard commands a very important place. It is a framework for building RESTful web services or, more precisely, a set of tools and frameworks for building RESTful web services.
It allows developers quick project bootstrapping. This helps you package your applications to be easily deployable in a production environment as standalone services. If you have ever been in a situation where you need to bootstrap a project in the Spring framework, for example, you probably know how painful it can be.
With Dropwizard, it's just a matter of adding one Maven dependency.
In this blog, I will guide you through the complete process of writing a simple Dropwizard RESTful service. After we’re done, we will have a service for basic CRUD operations on “parts.” It doesn’t really matter what “part” is; it can be anything. It just came to mind first.
We will store the data in a MySQL database, using JDBI for querying it, and will use following endpoints:
-
GET /parts
-to retrieve all parts from DB -
GET /part/{id}
to get a particular part from DB -
POST /parts
-to create new part -
PUT /parts/{id}
-to edit an existing part -
DELETE /parts/{id}
-to delete the part from a DB
We will use OAuth to authenticate our service, and finally, add some unit tests to it.
Default Dropwizard Libraries
Instead of including all libraries needed to build a REST service separately and configuring each of them, Dropwizard does that for us. Here is the list of libraries that come with Dropwizard by default:
- Jetty: You would require HTTP for running a web application. Dropwizard embeds the Jetty servlet container for running web applications. Instead of deploying your applications to an application server or web server, Dropwizard defines a main method that invokes the Jetty server as a standalone process. As of now, Dropwizard recommends only running the application with Jetty; other web services like Tomcat are not officially supported.
- Jersey: Jersey is one of the best REST API implementations on the market. Also, it follows the standard JAX-RS specification, and it’s the reference implementation for the JAX-RS specification. Dropwizard uses Jersey as the default framework for building RESTful web applications.
- Jackson: Jackson is the de facto standard for JSON format handling. It is one of the best object mapper APIs for the JSON format.
- Metrics: Dropwizard has its own metrics module for exposing the application metrics through HTTP endpoints.
- Guava: In addition to highly optimized immutable data structures, Guava provides a growing number of classes to speed up development in Java.
- Logback and Slf4j: These two are used for better logging mechanisms.
- Freemarker and Mustache: Choosing template engines for your application is one of the key decisions. The chosen template engine has to be more flexible for writing better scripts. Dropwizard uses well-known and popular template engines Freemarker and Mustache for building the user interfaces.
Apart from the above list, there are many other libraries like Joda Time, Liquibase, Apache HTTP Client, and Hibernate Validator used by Dropwizard for building REST services.
Maven Configuration
Dropwizard officially supports Maven. Even if you can use other build tools, most of the guides and documentation uses Maven, so we’re going to use it too here. If you’re not familiar with Maven, you could check out this Maven tutorial.
This is the first step in creating your Dropwizard application. Please add the following entry in your Maven’s pom.xml
file:
<dependencies>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>${dropwizard.version}</version>
</dependency>
</dependencies>
Before adding the above entry, you could add the dropwizard.version
as below:
<properties>
<dropwizard.version>1.1.0</dropwizard.version>
</properties>
That’s it. You’re done writing the Maven configuration. This will download all the required dependencies to your project. The current Dropwizard version is 1.1.0, so we will be using it this guide.
Now, we can move on to writing our first real Dropwizard application.
Define Configuration Class
Dropwizard stores configurations in YAML files. You will need to have the file configuration.yml
in your application root folder. This file will then be deserialized to an instance of your application’s configuration class and validated. Your application’s configuration file is the subclass of the Dropwizard’s configuration class (io.dropwizard.Configuration
).
Let’s create a simple configuration class:
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.dropwizard.Configuration;
import io.dropwizard.db.DataSourceFactory;
public class DropwizardBlogConfiguration extends Configuration {
private static final String DATABASE = "database";
@Valid
@NotNull
private DataSourceFactory dataSourceFactory = new DataSourceFactory();
@JsonProperty(DATABASE)
public DataSourceFactory getDataSourceFactory() {
return dataSourceFactory;
}
@JsonProperty(DATABASE)
public void setDataSourceFactory(final DataSourceFactory dataSourceFactory) {
this.dataSourceFactory = dataSourceFactory;
}
}
The YAML configuration file would look like this:
database:
driverClass: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost/dropwizard_blog
user: dropwizard_blog
password: dropwizard_blog
maxWaitForConnection: 1s
validationQuery: "SELECT 1"
validationQueryTimeout: 3s
minSize: 8
maxSize: 32
checkConnectionWhileIdle: false
evictionInterval: 10s
minIdleTime: 1 minute
checkConnectionOnBorrow: true
The above class will be deserialized from the YAML file and put the values from the YAML file to this object.
Define an Application Class
We should now go and create the main application class. This class will bring all the bundles together and bring the application up and get it running for use.
Here is an example of an application class in Dropwizard:
import io.dropwizard.Application;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.oauth.OAuthCredentialAuthFilter;
import io.dropwizard.setup.Environment;
import javax.sql.DataSource;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import org.skife.jdbi.v2.DBI;
import com.toptal.blog.auth.DropwizardBlogAuthenticator;
import com.toptal.blog.auth.DropwizardBlogAuthorizer;
import com.toptal.blog.auth.User;
import com.toptal.blog.config.DropwizardBlogConfiguration;
import com.toptal.blog.health.DropwizardBlogApplicationHealthCheck;
import com.toptal.blog.resource.PartsResource;
import com.toptal.blog.service.PartsService;
public class DropwizardBlogApplication extends Application<DropwizardBlogConfiguration> {
private static final String SQL = "sql";
private static final String DROPWIZARD_BLOG_SERVICE = "Dropwizard blog service";
private static final String BEARER = "Bearer";
public static void main(String[] args) throws Exception {
new DropwizardBlogApplication().run(args);
}
@Override
public void run(DropwizardBlogConfiguration configuration, Environment environment) {
// Datasource configuration
final DataSource dataSource =
configuration.getDataSourceFactory().build(environment.metrics(), SQL);
DBI dbi = new DBI(dataSource);
// Register Health Check
DropwizardBlogApplicationHealthCheck healthCheck =
new DropwizardBlogApplicationHealthCheck(dbi.onDemand(PartsService.class));
environment.healthChecks().register(DROPWIZARD_BLOG_SERVICE, healthCheck);
// Register OAuth authentication
environment.jersey()
.register(new AuthDynamicFeature(new OAuthCredentialAuthFilter.Builder<User>()
.setAuthenticator(new DropwizardBlogAuthenticator())
.setAuthorizer(new DropwizardBlogAuthorizer()).setPrefix(BEARER).buildAuthFilter()));
environment.jersey().register(RolesAllowedDynamicFeature.class);
// Register resources
environment.jersey().register(new PartsResource(dbi.onDemand(PartsService.class)));
}
}
What’s actually done above is override the Dropwizard run method. In this method, we’re instantiating a DB connection, registering our custom health check (we’ll talk about it later), initializing OAuth authentication for our service, and finally, registering a Dropwizard resource.
All of these will be explained later on.
Define a Representation Class
Now we have to start thinking about our REST API and what will be the representation of our resource. We have to design the JSON format and the corresponding representation class that converts to the desired JSON format.
Let’s look at the sample JSON format for this simple representation class example:
{
"code": 200,
"data": {
"id": 1,
"name": "Part 1",
"code": "PART_1_CODE"
}
}
For the above JSON format, we would create the representation class as below:
import org.hibernate.validator.constraints.Length;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Representation<T> {
private long code;
@Length(max = 3)
private T data;
public Representation() {
// Jackson deserialization
}
public Representation(long code, T data) {
this.code = code;
this.data = data;
}
@JsonProperty
public long getCode() {
return code;
}
@JsonProperty
public T getData() {
return data;
}
}
This is fairly simple POJO.
Defining a Resource Class
A resource is what REST services are all about. It is nothing but an endpoint URI for accessing the resource on the server. In this example, we’ll have a resource class with few annotations for request URI mapping. Since Dropwizard uses the JAX-RS implementation, we will define the URI path using the @Path
annotation.
Here is a resource class for our Dropwizard example:
import java.util.List;
import javax.annotation.security.RolesAllowed;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
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;
import org.eclipse.jetty.http.HttpStatus;
import com.codahale.metrics.annotation.Timed;
import com.toptal.blog.model.Part;
import com.toptal.blog.representation.Representation;
import com.toptal.blog.service.PartsService;
@Path("/parts")
@Produces(MediaType.APPLICATION_JSON)
@RolesAllowed("ADMIN")
public class PartsResource {
private final PartsService partsService;;
public PartsResource(PartsService partsService) {
this.partsService = partsService;
}
@GET
@Timed
public Representation<List<Part>> getParts() {
return new Representation<List<Part>>(HttpStatus.OK_200, partsService.getParts());
}
@GET
@Timed
@Path("{id}")
public Representation<Part> getPart(@PathParam("id") final int id) {
return new Representation<Part>(HttpStatus.OK_200, partsService.getPart(id));
}
@POST
@Timed
public Representation<Part> createPart(@NotNull @Valid final Part part) {
return new Representation<Part>(HttpStatus.OK_200, partsService.createPart(part));
}
@PUT
@Timed
@Path("{id}")
public Representation<Part> editPart(@NotNull @Valid final Part part,
@PathParam("id") final int id) {
part.setId(id);
return new Representation<Part>(HttpStatus.OK_200, partsService.editPart(part));
}
@DELETE
@Timed
@Path("{id}")
public Representation<String> deletePart(@PathParam("id") final int id) {
return new Representation<String>(HttpStatus.OK_200, partsService.deletePart(id));
}
}
You can see all of the endpoints are actually defined in this class.
Registering a Resource
I would go back now to the main application class. You can see at the end of that class that we have registered our resource to be initialized with the service run. We need to do so with all resources we might have in our application. This is the code snippet responsible for that:
// Register resources
environment.jersey().register(new PartsResource(dbi.onDemand(PartsService.class)));
Service Layer
For proper exception handling and the ability to be independent of the data storage engine, we will introduce a “mid-layer” service class. This is the class we will be calling from our resource layer, and we don’t care what is underlying. That’s why we have this layer in between of resource and DAO layers. Here is our service class:
import java.util.List;
import java.util.Objects;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response.Status;
import org.skife.jdbi.v2.exceptions.UnableToExecuteStatementException;
import org.skife.jdbi.v2.exceptions.UnableToObtainConnectionException;
import org.skife.jdbi.v2.sqlobject.CreateSqlObject;
import com.toptal.blog.dao.PartsDao;
import com.toptal.blog.model.Part;
public abstract class PartsService {
private static final String PART_NOT_FOUND = "Part id %s not found.";
private static final String DATABASE_REACH_ERROR =
"Could not reach the MySQL database. The database may be down or there may be network connectivity issues. Details: ";
private static final String DATABASE_CONNECTION_ERROR =
"Could not create a connection to the MySQL database. The database configurations are likely incorrect. Details: ";
private static final String DATABASE_UNEXPECTED_ERROR =
"Unexpected error occurred while attempting to reach the database. Details: ";
private static final String SUCCESS = "Success...";
private static final String UNEXPECTED_ERROR = "An unexpected error occurred while deleting part.";
@CreateSqlObject
abstract PartsDao partsDao();
public List<Part> getParts() {
return partsDao().getParts();
}
public Part getPart(int id) {
Part part = partsDao().getPart(id);
if (Objects.isNull(part)) {
throw new WebApplicationException(String.format(PART_NOT_FOUND, id), Status.NOT_FOUND);
}
return part;
}
public Part createPart(Part part) {
partsDao().createPart(part);
return partsDao().getPart(partsDao().lastInsertId());
}
public Part editPart(Part part) {
if (Objects.isNull(partsDao().getPart(part.getId()))) {
throw new WebApplicationException(String.format(PART_NOT_FOUND, part.getId()),
Status.NOT_FOUND);
}
partsDao().editPart(part);
return partsDao().getPart(part.getId());
}
public String deletePart(final int id) {
int result = partsDao().deletePart(id);
switch (result) {
case 1:
return SUCCESS;
case 0:
throw new WebApplicationException(String.format(PART_NOT_FOUND, id), Status.NOT_FOUND);
default:
throw new WebApplicationException(UNEXPECTED_ERROR, Status.INTERNAL_SERVER_ERROR);
}
}
public String performHealthCheck() {
try {
partsDao().getParts();
} catch (UnableToObtainConnectionException ex) {
return checkUnableToObtainConnectionException(ex);
} catch (UnableToExecuteStatementException ex) {
return checkUnableToExecuteStatementException(ex);
} catch (Exception ex) {
return DATABASE_UNEXPECTED_ERROR + ex.getCause().getLocalizedMessage();
}
return null;
}
private String checkUnableToObtainConnectionException(UnableToObtainConnectionException ex) {
if (ex.getCause() instanceof java.sql.SQLNonTransientConnectionException) {
return DATABASE_REACH_ERROR + ex.getCause().getLocalizedMessage();
} else if (ex.getCause() instanceof java.sql.SQLException) {
return DATABASE_CONNECTION_ERROR + ex.getCause().getLocalizedMessage();
} else {
return DATABASE_UNEXPECTED_ERROR + ex.getCause().getLocalizedMessage();
}
}
private String checkUnableToExecuteStatementException(UnableToExecuteStatementException ex) {
if (ex.getCause() instanceof java.sql.SQLSyntaxErrorException) {
return DATABASE_CONNECTION_ERROR + ex.getCause().getLocalizedMessage();
} else {
return DATABASE_UNEXPECTED_ERROR + ex.getCause().getLocalizedMessage();
}
}
}
The last part of it is actually a health check implementation, which we will be talking about later.
DAO layer, JDBI, and Mapper
Dropwizard supports JDBI and Hibernate. It’s separate Maven module, so let’s first add it as a dependency as well as the MySQL connector:
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-jdbi</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connector.version}</version>
</dependency>
For a simple CRUD service, I personally prefer JDBI, as it is simpler and much faster to implement. I have created a simple MySQL schema with one table only to be used in our example. You can find the init script for the schema within the source. JDBI offers simple query writing by using annotations such as @SqlQuery for reading and @SqlUpdate for writing data. Here is our DAO interface:
import java.util.List;
import org.skife.jdbi.v2.sqlobject.Bind;
import org.skife.jdbi.v2.sqlobject.BindBean;
import org.skife.jdbi.v2.sqlobject.SqlQuery;
import org.skife.jdbi.v2.sqlobject.SqlUpdate;
import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapper;
import com.toptal.blog.mapper.PartsMapper;
import com.toptal.blog.model.Part;
@RegisterMapper(PartsMapper.class)
public interface PartsDao {
@SqlQuery("select * from parts;")
public List<Part> getParts();
@SqlQuery("select * from parts where id = :id")
public Part getPart(@Bind("id") final int id);
@SqlUpdate("insert into parts(name, code) values(:name, :code)")
void createPart(@BindBean final Part part);
@SqlUpdate("update parts set name = coalesce(:name, name), code = coalesce(:code, code) where id = :id")
void editPart(@BindBean final Part part);
@SqlUpdate("delete from parts where id = :id")
int deletePart(@Bind("id") final int id);
@SqlQuery("select last_insert_id();")
public int lastInsertId();
}
As you can see, it’s fairly simple. However, we need to map our SQL result sets to a model, which we do by registering a mapper class. Here is our mapper class:
import java.sql.ResultSet;
import java.sql.SQLException;
import org.skife.jdbi.v2.StatementContext;
import org.skife.jdbi.v2.tweak.ResultSetMapper;
import com.toptal.blog.model.Part;
public class PartsMapper implements ResultSetMapper<Part> {
private static final String ID = "id";
private static final String NAME = "name";
private static final String CODE = "code";
public Part map(int i, ResultSet resultSet, StatementContext statementContext)
throws SQLException {
return new Part(resultSet.getInt(ID), resultSet.getString(NAME), resultSet.getString(CODE));
}
}
And our model:
import org.hibernate.validator.constraints.NotEmpty;
public class Part {
private int id;
@NotEmpty
private String name;
@NotEmpty
private String code;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public Part() {
super();
}
public Part(int id, String name, String code) {
super();
this.id = id;
this.name = name;
this.code = code;
}
}
Dropwizard Health Check
Dropwizard offers native support for health checking. In our case, we would probably like to check if the database is up and running before saying that our service is healthy. What we do is actually perform some simple DB action like getting parts from the DB and handling the potential outcomes (successful or exceptions).
Here is our health check implementation in Dropwizard:
import com.codahale.metrics.health.HealthCheck;
import com.toptal.blog.service.PartsService;
public class DropwizardBlogApplicationHealthCheck extends HealthCheck {
private static final String HEALTHY = "The Dropwizard blog Service is healthy for read and write";
private static final String UNHEALTHY = "The Dropwizard blog Service is not healthy. ";
private static final String MESSAGE_PLACEHOLDER = "{}";
private final PartsService partsService;
public DropwizardBlogApplicationHealthCheck(PartsService partsService) {
this.partsService = partsService;
}
@Override
public Result check() throws Exception {
String mySqlHealthStatus = partsService.performHealthCheck();
if (mySqlHealthStatus == null) {
return Result.healthy(HEALTHY);
} else {
return Result.unhealthy(UNHEALTHY + MESSAGE_PLACEHOLDER, mySqlHealthStatus);
}
}
}
Adding Authentication
Dropwizard supports basic authentication and OAuth. Here. I will show you how to protect your service with OAuth. However, due to complexity, I have omitted an underlying DB structure and just showed how it is wrapped. Implementing to full scale should not be an issue starting from here. Dropwizard has two important interfaces we need to implement.
The first one is Authenticator. Our class should implement the authenticate
method, which should check if the given access token is a valid one. So I would call this as a first gate to the application. If succeeded, it should return a principal. This principal is our actual user with its role. The role is important for another Dropwizard interface we need to implement. This one is Authorizer, and it is responsible for checking whether the user has sufficient permissions to access a certain resource. So, if you go back and check our resource class, you will see that it requires the admin role for accessing its endpoints. These annotations can be per method also. Dropwizard authorization support is a separate Maven module, so we need to add it to dependencies:
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-auth</artifactId>
<version>${dropwizard.version}</version>
</dependency>
Here are the classes from our example that doesn’t actually do anything smart, but it’s a skeleton for a full-scale OAuth authorization:
import java.util.Optional;
import io.dropwizard.auth.AuthenticationException;
import io.dropwizard.auth.Authenticator;
public class DropwizardBlogAuthenticator implements Authenticator<String, User> {
@Override
public Optional<User> authenticate(String token) throws AuthenticationException {
if ("test_token".equals(token)) {
return Optional.of(new User());
}
return Optional.empty();
}
}
import java.util.Objects;
import io.dropwizard.auth.Authorizer;
public class DropwizardBlogAuthorizer implements Authorizer<User> {
@Override
public boolean authorize(User principal, String role) {
// Allow any logged in user.
if (Objects.nonNull(principal)) {
return true;
}
return false;
}
}
import java.security.Principal;
public class User implements Principal {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String getName() {
return username;
}
}
Unit Tests in Dropwizard
Let’s add some unit tests to our application. I will stick to testing Dropwizard specific parts of the code, in our case Representation and Resource. We will need to add following dependencies to our Maven file:
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-testing</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
For testing representation, we will also need a sample JSON file to test against. So let’s create fixtures/part.json
under src/test/resources
:
{
"id": 1,
"name": "testPartName",
"code": "testPartCode"
}
And here is the JUnit test class:
import static io.dropwizard.testing.FixtureHelpers.fixture;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.toptal.blog.model.Part;
import io.dropwizard.jackson.Jackson;
public class RepresentationTest {
private static final ObjectMapper MAPPER = Jackson.newObjectMapper();
private static final String PART_JSON = "fixtures/part.json";
private static final String TEST_PART_NAME = "testPartName";
private static final String TEST_PART_CODE = "testPartCode";
@Test
public void serializesToJSON() throws Exception {
final Part part = new Part(1, TEST_PART_NAME, TEST_PART_CODE);
final String expected =
MAPPER.writeValueAsString(MAPPER.readValue(fixture(PART_JSON), Part.class));
assertThat(MAPPER.writeValueAsString(part)).isEqualTo(expected);
}
@Test
public void deserializesFromJSON() throws Exception {
final Part part = new Part(1, TEST_PART_NAME, TEST_PART_CODE);
assertThat(MAPPER.readValue(fixture(PART_JSON), Part.class).getId()).isEqualTo(part.getId());
assertThat(MAPPER.readValue(fixture(PART_JSON), Part.class).getName())
.isEqualTo(part.getName());
assertThat(MAPPER.readValue(fixture(PART_JSON), Part.class).getCode())
.isEqualTo(part.getCode());
}
}
When it comes to testing resources, the main point of testing Dropwizard is that you’re actually behaving as an HTTP client, sending HTTP requests against resources. So, you’re not testing methods as you would usually do in a common case. Here is the example for our PartsResource
class:
public class PartsResourceTest {
private static final String SUCCESS = "Success...";
private static final String TEST_PART_NAME = "testPartName";
private static final String TEST_PART_CODE = "testPartCode";
private static final String PARTS_ENDPOINT = "/parts";
private static final PartsService partsService = mock(PartsService.class);
@ClassRule
public static final ResourceTestRule resources =
ResourceTestRule.builder().addResource(new PartsResource(partsService)).build();
private final Part part = new Part(1, TEST_PART_NAME, TEST_PART_CODE);
@Before
public void setup() {
when(partsService.getPart(eq(1))).thenReturn(part);
List<Part> parts = new ArrayList<>();
parts.add(part);
when(partsService.getParts()).thenReturn(parts);
when(partsService.createPart(any(Part.class))).thenReturn(part);
when(partsService.editPart(any(Part.class))).thenReturn(part);
when(partsService.deletePart(eq(1))).thenReturn(SUCCESS);
}
@After
public void tearDown() {
reset(partsService);
}
@Test
public void testGetPart() {
Part partResponse = resources.target(PARTS_ENDPOINT + "/1").request()
.get(TestPartRepresentation.class).getData();
assertThat(partResponse.getId()).isEqualTo(part.getId());
assertThat(partResponse.getName()).isEqualTo(part.getName());
assertThat(partResponse.getCode()).isEqualTo(part.getCode());
verify(partsService).getPart(1);
}
@Test
public void testGetParts() {
List<Part> parts =
resources.target(PARTS_ENDPOINT).request().get(TestPartsRepresentation.class).getData();
assertThat(parts.size()).isEqualTo(1);
assertThat(parts.get(0).getId()).isEqualTo(part.getId());
assertThat(parts.get(0).getName()).isEqualTo(part.getName());
assertThat(parts.get(0).getCode()).isEqualTo(part.getCode());
verify(partsService).getParts();
}
@Test
public void testCreatePart() {
Part newPart = resources.target(PARTS_ENDPOINT).request()
.post(Entity.entity(part, MediaType.APPLICATION_JSON_TYPE), TestPartRepresentation.class)
.getData();
assertNotNull(newPart);
assertThat(newPart.getId()).isEqualTo(part.getId());
assertThat(newPart.getName()).isEqualTo(part.getName());
assertThat(newPart.getCode()).isEqualTo(part.getCode());
verify(partsService).createPart(any(Part.class));
}
@Test
public void testEditPart() {
Part editedPart = resources.target(PARTS_ENDPOINT + "/1").request()
.put(Entity.entity(part, MediaType.APPLICATION_JSON_TYPE), TestPartRepresentation.class)
.getData();
assertNotNull(editedPart);
assertThat(editedPart.getId()).isEqualTo(part.getId());
assertThat(editedPart.getName()).isEqualTo(part.getName());
assertThat(editedPart.getCode()).isEqualTo(part.getCode());
verify(partsService).editPart(any(Part.class));
}
@Test
public void testDeletePart() {
assertThat(resources.target(PARTS_ENDPOINT + "/1").request()
.delete(TestDeleteRepresentation.class).getData()).isEqualTo(SUCCESS);
verify(partsService).deletePart(1);
}
private static class TestPartRepresentation extends Representation<Part> {
}
private static class TestPartsRepresentation extends Representation<List<Part>> {
}
private static class TestDeleteRepresentation extends Representation<String> {
}
}
Build Your Dropwizard Application
Best practice is to build the single FAT JAR file which contains all of the .class files required to run your application. The same JAR file can be deployed to the different environment from testing to the production without any change in the dependency libraries. To start building our example application as a fat JAR, we need to configure a Maven plugin called maven-shade. You have to add the following entries in the plugins section of your pom.xml file.
Here is the sample Maven configuration for building the JAR file.
<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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.endava</groupId>
<artifactId>dropwizard-blog</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Dropwizard Blog example</name>
<properties>
<dropwizard.version>1.1.0</dropwizard.version>
<mockito.version>2.7.12</mockito.version>
<mysql.connector.version>6.0.6</mysql.connector.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-jdbi</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-auth</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-testing</artifactId>
<version>${dropwizard.version}</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connector.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.endava.blog.DropwizardBlogApplication</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Running Your Application
Now, we should be able to run the service. If you have successfully built your JAR file, all you need to do is to open the command prompt and just run the following command to execute your JAR file:
java -jar target/dropwizard-blog-1.0.0.jar server configuration.yml
If all went OK, then you would see something like this:
INFO [2017-04-23 22:51:14,471] org.eclipse.jetty.util.log: Logging initialized @962ms to org.eclipse.jetty.util.log.Slf4jLog
INFO [2017-04-23 22:51:14,537] io.dropwizard.server.DefaultServerFactory: Registering jersey handler with root path prefix: /
INFO [2017-04-23 22:51:14,538] io.dropwizard.server.DefaultServerFactory: Registering admin handler with root path prefix: /
INFO [2017-04-23 22:51:14,681] io.dropwizard.server.DefaultServerFactory: Registering jersey handler with root path prefix: /
INFO [2017-04-23 22:51:14,681] io.dropwizard.server.DefaultServerFactory: Registering admin handler with root path prefix: /
INFO [2017-04-23 22:51:14,682] io.dropwizard.server.ServerFactory: Starting DropwizardBlogApplication
INFO [2017-04-23 22:51:14,752] org.eclipse.jetty.setuid.SetUIDListener: Opened application@7d57dbb5{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
INFO [2017-04-23 22:51:14,752] org.eclipse.jetty.setuid.SetUIDListener: Opened admin@630b6190{HTTP/1.1,[http/1.1]}{0.0.0.0:8081}
INFO [2017-04-23 22:51:14,753] org.eclipse.jetty.server.Server: jetty-9.4.2.v20170220
INFO [2017-04-23 22:51:15,153] io.dropwizard.jersey.DropwizardResourceConfig: The following paths were found for the configured resources:
GET /parts (com.toptal.blog.resource.PartsResource)
POST /parts (com.toptal.blog.resource.PartsResource)
DELETE /parts/{id} (com.toptal.blog.resource.PartsResource)
GET /parts/{id} (com.toptal.blog.resource.PartsResource)
PUT /parts/{id} (com.toptal.blog.resource.PartsResource)
INFO [2017-04-23 22:51:15,154] org.eclipse.jetty.server.handler.ContextHandler: Started i.d.j.MutableServletContextHandler@58fa5769{/,null,AVAILABLE}
INFO [2017-04-23 22:51:15,158] io.dropwizard.setup.AdminEnvironment: tasks =
POST /tasks/log-level (io.dropwizard.servlets.tasks.LogConfigurationTask)
POST /tasks/gc (io.dropwizard.servlets.tasks.GarbageCollectionTask)
INFO [2017-04-23 22:51:15,162] org.eclipse.jetty.server.handler.ContextHandler: Started i.d.j.MutableServletContextHandler@3fdcde7a{/,null,AVAILABLE}
INFO [2017-04-23 22:51:15,176] org.eclipse.jetty.server.AbstractConnector: Started application@7d57dbb5{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
INFO [2017-04-23 22:51:15,177] org.eclipse.jetty.server.AbstractConnector: Started admin@630b6190{HTTP/1.1,[http/1.1]}{0.0.0.0:8081}
INFO [2017-04-23 22:51:15,177] org.eclipse.jetty.server.Server: Started @1670ms
Now you have your own Dropwizard application listening on ports 8080 for application requests and 8081 for administration requests.
Note that server configuration.yml
is used for starting the HTTP server and passing the YAML configuration file location to the server.
Excellent! Finally, we have implemented a microservice using Dropwizard framework. Now let’s go for a break and have a cup of tea. You have done really good job.
Accessing Resources
You can use any HTTP client like POSTMAN or any else. You should be able to access your server by hitting http://localhost:8080/parts
. You should be receiving a message that the credentials are required to access the service. To authenticate, add Authorization
header with bearer test_token
value. If done successfully, you should see something like:
{
"code": 200,
"data": []
}
meaning that your DB is empty. Create your first part by switching HTTP method from GET to POST, and supply this payload:
{
"name":"My first part",
"code":"code_of_my_first_part"
}
All other endpoints work in the same manner, so keep playing and enjoy.
How to Change Context Path
By default, Dropwizard application will start and running in the /
. For example, if you are not mentioning anything about the context path of the application, by default, the application can be accessed from the URL http://localhost:8080/
. If you want to configure your own context path for your application, then please add the following entries to your YAML file.
server:
applicationContextPath: /application
Wrapping up our Dropwizard Tutorial
Now when you have your Dropwizard REST service up and running, let’s summarize some key advantages or disadvantages of using Dropwizard as a REST framework. It’s absolutely obvious from this post that Dropwizard offers extremely fast bootstrap of your project. And that’s probably the biggest advantage of using Dropwizard.
Also, it will include all the cutting-edge libraries/tools that you will ever need in developing your service. So you definitely do not need to worry about that. It also gives you very nice configuration management. Of course, Dropwizard has some disadvantages as well. By using Dropwizard, you’re kind of restricted to using what Dropwizard offers or supports. You lose some of the freedom you may be used to when developing. But still, I wouldn’t even call it a disadvantage, as this is exactly what makes Dropwizard what it is—easy to set up, easy to develop, but yet a very robust and high-performance REST framework.
In my opinion, adding complexity to the framework by supporting more and more third party libraries would also introduce unnecessary complexity in development.
Understanding the basics
What is the meaning of representational state transfer (REST)?
Representational State Transfer (REST) is a style of architecture (not to be mixed with the set of standards) based on a set of principles that describe how networked resources are defined and addressed.
What is a REST end point?
A REST endpoint is an exposed HTTP entry point of a REST service. In above example, GET /parts is one endpoint while POST /parts is another one.
What is a REST web service?
A REST web service is any web application that is structured following the REST architecture style. It listens on HTTP or HTTPs port for requests and has its endpoints exposed to the client.
What is a microservice architecture?
It is an architecture of loosely coupled services. However, these services together implement the business logic of the entire system. The biggest benefit of microservices lies in the fact that each small service is easier to develop, understand, and deploy or maintain without affecting the rest of the system.
Dusan Simonovic
Belgrade, Serbia
Member since February 8, 2017
About the author
Dusan is a back-end Java developer with eight years of experience in Java development and a number of large projects under his belt.
PREVIOUSLY AT