Getting Started
Getting Started will guide you through the process of creating a simple Dropwizard Project: Hello World. Along the way, we’ll explain the various underlying libraries and their roles, important concepts in Dropwizard, and suggest some organizational techniques to help you as your project grows. (Or you can just skip to the fun part.)
Overview
Dropwizard straddles the line between being a library and a framework. Its goal is to provide performant, reliable implementations of everything a production-ready web application needs. Because this functionality is extracted into a reusable library, your application remains lean and focused, reducing both time-to-market and maintenance burdens.
Jetty for HTTP
Because you can’t be a web application without HTTP, Dropwizard uses the Jetty HTTP library to
embed an incredibly tuned HTTP server directly into your project. Instead of handing your
application off to a complicated application server, Dropwizard projects have a main
method
which spins up an HTTP server. Running your application as a simple process eliminates a number of
unsavory aspects of Java in production (no PermGen issues, no application server configuration and
maintenance, no arcane deployment tools, no class loader troubles, no hidden application logs, no
trying to tune a single garbage collector to work with multiple application workloads) and allows
you to use all of the existing Unix process management tools instead.
Jersey for REST
For building RESTful web applications, we’ve found nothing beats Jersey (the JAX-RS reference
implementation) in terms of features or performance. It allows you to write clean, testable classes
which gracefully map HTTP requests to simple Java objects. It supports streaming output, matrix URI
parameters, conditional GET
requests, and much, much more.
Jackson for JSON
In terms of data formats, JSON has become the web’s lingua franca, and Jackson is the king of JSON on the JVM. In addition to being lightning fast, it has a sophisticated object mapper, allowing you to export your domain models directly.
Metrics for metrics
The Metrics library rounds things out, providing you with unparalleled insight into your code’s behavior in your production environment.
And Friends
In addition to Jetty, Jersey, and Jackson, Dropwizard also includes a number of libraries to help you ship more quickly and with fewer regrets.
Hibernate Validator, the JSR 349 reference implementation, provides an easy, declarative framework for validating user input and generating helpful and i18n-friendly error messages.
The Apache HttpClient and Jersey client libraries allow for both low- and high-level interaction with other web services.
JDBI is the most straightforward way to use a relational database with Java.
Liquibase is a great way to keep your database schema in check throughout your development and release cycles, applying high-level database refactorings instead of one-off DDL scripts.
Freemarker and Mustache are simple templating systems for more user-facing applications.
Now that you’ve gotten the lay of the land, let’s dig in!
Setting Up Using Maven
We recommend you use Maven for new Dropwizard applications. If you’re a big Ant / Ivy, Buildr, Gradle, SBT, Leiningen, or Gant fan, that’s cool, but we use Maven, and we’ll be using Maven as we go through this example application. If you have any questions about how Maven works, Maven: The Complete Reference should have what you’re looking for.
You have three alternatives from here:
Create a project using dropwizard-archetype:
mvn archetype:generate -DarchetypeGroupId=io.dropwizard.archetypes -DarchetypeArtifactId=java-simple -DarchetypeVersion=[REPLACE WITH A VALID DROPWIZARD VERSION]
Look at the dropwizard-example
Follow the tutorial below to see how you can include it in your existing project
Tutorial
First, add the dropwizard-bom
bill of materials (BOM) into the dependencyManagement
section of your POM
with the current version of Dropwizard (which is 4.0.13-SNAPSHOT):
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-bom</artifactId>
<version>4.0.13-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Add the dropwizard-core
library as a dependency:
<dependencies>
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-core</artifactId>
</dependency>
</dependencies>
Alright, that’s enough XML. We’ve got a Maven project set up now, and it’s time to start writing real code.
Creating A Configuration Class
Each Dropwizard application has its own subclass of the Configuration
class which specifies
environment-specific parameters. These parameters are specified in a YAML configuration file which
is deserialized to an instance of your application’s configuration class and validated.
The application we’ll be building is a high-performance Hello World service, and one of our requirements is that we need to be able to vary how it says hello from environment to environment. We’ll need to specify at least two things to begin with: a template for saying hello and a default name to use in case the user doesn’t specify their name.
Here’s what our configuration class will look like, full example conf here:
package com.example.helloworld;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.dropwizard.core.Configuration;
import jakarta.validation.constraints.NotEmpty;
public class HelloWorldConfiguration extends Configuration {
@NotEmpty
private String template;
@NotEmpty
private String defaultName = "Stranger";
@JsonProperty
public String getTemplate() {
return template;
}
@JsonProperty
public void setTemplate(String template) {
this.template = template;
}
@JsonProperty
public String getDefaultName() {
return defaultName;
}
@JsonProperty
public void setDefaultName(String name) {
this.defaultName = name;
}
}
There’s a lot going on here, so let’s unpack a bit of it.
When this class is deserialized from the YAML file, it will pull two root-level fields from the YAML
object: template
, the template for our Hello World saying, and defaultName
, the default name
to use. Both template
and defaultName
are annotated with @NotEmpty
, so if the YAML
configuration file has blank values for either or is missing template
entirely an informative
exception will be thrown, and your application won’t start.
Both the getters and setters for template
and defaultName
are annotated with
@JsonProperty
, which allows Jackson to both deserialize the properties from a YAML file but also
to serialize it.
Note
The mapping from YAML to your application’s Configuration
instance is done
by Jackson. This means your Configuration
class can use all of
Jackson’s object-mapping annotations. The validation of @NotEmpty
is
handled by Hibernate Validator, which has a
wide range of built-in constraints for you to use.
Our YAML file will then look like the below, full example yml here:
---
template: Hello, %s!
defaultName: Stranger
Dropwizard has many more configuration parameters than that, but they all have sane defaults so you can keep your configuration files small and focused.
So save that YAML file in the directory you plan to run the fat jar from (see below) as hello-world.yml
, because
we’ll be getting up and running pretty soon, and we’ll need it. Next up, we’re creating our application class!
Creating An Application Class
Combined with your project’s Configuration
subclass, its Application
subclass forms the core
of your Dropwizard application. The Application
class pulls together the various bundles and
commands which provide basic functionality. (More on that later.) For now, though, our
HelloWorldApplication
looks like this:
package com.example.helloworld;
import io.dropwizard.core.Application;
import io.dropwizard.core.setup.Bootstrap;
import io.dropwizard.core.setup.Environment;
public class HelloWorldApplicationStart extends Application<HelloWorldConfiguration> {
public static void main(String[] args) throws Exception {
new HelloWorldApplicationStart().run(args);
}
@Override
public String getName() {
return "hello-world";
}
@Override
public void initialize(Bootstrap<HelloWorldConfiguration> bootstrap) {
// nothing to do yet
}
@Override
public void run(HelloWorldConfiguration configuration, Environment environment) {
// nothing to do yet
}
}
As you can see, HelloWorldApplication
is parameterized with the application’s configuration
type, HelloWorldConfiguration
. An initialize
method is used to configure aspects of the
application required before the application is run, like bundles, configuration source providers,
etc. Also, we’ve added a static
main
method, which will be our application’s entry point.
Right now, we don’t have any functionality implemented, so our run
method is a little boring.
Let’s fix that!
Creating A Representation Class
Before we can get into the nuts-and-bolts of our Hello World application, we need to stop and think about our API. Luckily, our application needs to conform to an industry standard, RFC 1149, which specifies the following JSON representation of a Hello World saying:
{
"id": 1,
"content": "Hi!"
}
The id
field is a unique identifier for the saying, and content
is the textual
representation of the saying. (Thankfully, this is a fairly straight-forward industry standard.)
To model this representation, we’ll create a representation class:
package com.example.helloworld.api;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Saying {
private long id;
private String content;
public Saying() {
// Jackson deserialization
}
public Saying(long id, String content) {
this.id = id;
this.content = content;
}
@JsonProperty
public long getId() {
return id;
}
@JsonProperty
public String getContent() {
return content;
}
}
This is a pretty simple POJO, but there are a few things worth noting here.
First, it’s immutable. This makes Saying
instances very easy to reason about in multi-threaded
environments as well as single-threaded environments. Second, it uses the JavaBeans standard for the
id
and content
properties. This allows Jackson to serialize it to the JSON we need. The
Jackson object mapping code will populate the id
field of the JSON object with the return value
of #getId()
, likewise with content
and #getContent()
.
Note
The JSON serialization here is done by Jackson, which supports far more than simple JavaBean objects like this one. In addition to the sophisticated set of annotations, you can even write your custom serializers and deserializers.
Now that we’ve got our representation class, it makes sense to start in on the resource it represents.
Creating A Resource Class
Jersey resources are the meat-and-potatoes of a Dropwizard application. Each resource class is
associated with a URI template. For our application, we need a resource which returns new Saying
instances from the URI /hello-world
, so our resource class looks like this:
package com.example.helloworld.resources;
import com.codahale.metrics.annotation.Timed;
import com.example.helloworld.api.Saying;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;
@Path("/hello-world")
@Produces(MediaType.APPLICATION_JSON)
public class HelloWorldResource {
private final String template;
private final String defaultName;
private final AtomicLong counter;
public HelloWorldResource(String template, String defaultName) {
this.template = template;
this.defaultName = defaultName;
this.counter = new AtomicLong();
}
@GET
@Timed
public Saying sayHello(@QueryParam("name") Optional<String> name) {
final String value = String.format(template, name.orElse(defaultName));
return new Saying(counter.incrementAndGet(), value);
}
}
Finally, we’re in the thick of it! Let’s start from the top and work our way down.
HelloWorldResource
has two annotations: @Path
and @Produces
. @Path("/hello-world")
tells Jersey that this resource is accessible at the URI /hello-world
, and
@Produces(MediaType.APPLICATION_JSON)
lets Jersey’s content negotiation code know that this
resource produces representations which are application/json
.
HelloWorldResource
takes two parameters for construction: the template
it uses to produce
the saying and the defaultName
used when the user declines to tell us their name. An
AtomicLong
provides us with a cheap, thread-safe way of generating unique(ish) IDs.
Warning
Resource classes are used by multiple threads concurrently. In general, we recommend that resources be stateless/immutable, but it’s important to keep the context in mind.
#sayHello(Optional<String>)
is the meat of this class, and it’s a fairly simple method. The
@QueryParam("name")
annotation tells Jersey to map the name
parameter from the query string
to the name
parameter in the method. If the client sends a request to
/hello-world?name=Dougie
, sayHello
will be called with Optional.of("Dougie")
; if there
is no name
parameter in the query string, sayHello
will be called with
Optional.empty()
. (Support for Optional
is a little extra sauce that Dropwizard
adds to Jersey’s existing functionality.)
Note
If the client sends a request to /hello-world?name=
, sayHello
will be called with
Optional.of("")
. This may seem odd at first, but this follows the standards (an application
may have different behavior depending on if a parameter is empty vs nonexistent). You can swap
Optional<String>
parameter with NonEmptyStringParam
if you want /hello-world?name=
to return “Hello, Stranger!” For more information on resource parameters see
the documentation
Inside the sayHello
method, we increment the counter, format the template using
String.format(String, Object...)
, and return a new Saying
instance.
Because sayHello
is annotated with @Timed
, Dropwizard automatically records the duration and
rate of its invocations as a Metrics Timer.
Once sayHello
has returned, Jersey takes the Saying
instance and looks for a provider class
which can write Saying
instances as application/json
. Dropwizard has one such provider built
in which allows for producing and consuming Java objects as JSON objects. The provider writes out
the JSON and the client receives a 200 OK
response with a content type of application/json
.
Registering A Resource
Before that will actually work, though, we need to go back to HelloWorldApplication
and add this
new resource class. In its run
method we can read the template and default name from the
HelloWorldConfiguration
instance, create a new HelloWorldResource
instance, and then add
it to the application’s Jersey environment:
HelloWorldResource resource = new HelloWorldResource(
configuration.getTemplate(),
configuration.getDefaultName()
);
environment.jersey().register(resource);
When our application starts, we create a new instance of our resource class with the parameters from
the configuration file and hand it off to the Environment
, which acts like a registry of all the
things your application can do.
Note
A Dropwizard application can contain many resource classes, each corresponding to its own URI
pattern. Just add another @Path
-annotated resource class and call register
with an
instance of the new class.
Before we go too far, we should add a health check for our application.
Creating A Health Check
Health checks give you a way of adding small tests to your application to allow you to verify that your application is functioning correctly in production. We strongly recommend that all of your applications have at least a minimal set of health checks.
Note
We recommend this so strongly, in fact, that Dropwizard will nag you should you neglect to add a health check to your project.
Since formatting strings is not likely to fail while an application is running (unlike, say, a database connection pool), we’ll have to get a little creative here. We’ll add a health check to make sure we can actually format the provided template:
package com.example.helloworld.health;
import com.codahale.metrics.health.HealthCheck;
public class TemplateHealthCheck extends HealthCheck {
private final String template;
public TemplateHealthCheck(String template) {
this.template = template;
}
@Override
protected Result check() throws Exception {
final String saying = String.format(template, "TEST");
if (!saying.contains("TEST")) {
return Result.unhealthy("template doesn't include a name");
}
return Result.healthy();
}
}
TemplateHealthCheck
checks for two things: that the provided template is actually a well-formed
format string, and that the template actually produces output with the given name.
If the string is not a well-formed format string (for example, someone accidentally put
Hello, %s%
in the configuration file), then String.format(String, Object...)
will throw an
IllegalFormatException
and the health check will implicitly fail. If the rendered saying doesn’t
include the test string, the health check will explicitly fail by returning an unhealthy Result
.
Adding A Health Check
As with most things in Dropwizard, we create a new instance with the appropriate parameters and add
it to the Environment
:
TemplateHealthCheck healthCheck = new TemplateHealthCheck(configuration.getTemplate());
environment.healthChecks().register("template", healthCheck);
Now we’re almost ready to go!
Building Fat JARs
We recommend that you build your Dropwizard applications as “fat” JAR files — single .jar
files
which contain all of the .class
files required to run your application. This allows you to
build a single deployable artifact which you can promote from your staging environment to your QA
environment to your production environment without worrying about differences in installed
libraries. To start building our Hello World application as a fat JAR, we need to configure a Maven
plugin called maven-shade
. In the <build><plugins>
section of your pom.xml
file, add
this:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</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.example.helloworld.HelloWorldApplication</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
This configures Maven to do a couple of things during its package
phase:
Produce a
pom.xml
file which doesn’t include dependencies for the libraries whose contents are included in the fat JAR.Exclude all digital signatures from signed JARs. If you don’t, then Java considers the signature invalid and won’t load or run your JAR file.
Collate the various
META-INF/services
entries in the JARs instead of overwriting them. (Neither Dropwizard nor Jersey works without those.)Set
com.example.helloworld.HelloWorldApplication
as the JAR’sMainClass
. This will allow you to run the JAR usingjava -jar
.
Warning
If your application has a dependency which must be signed (e.g., a JCA/JCE provider or
other trusted library), you have to add an exclusion to the maven-shade-plugin
configuration for that library and include that JAR in the classpath.
Warning
Since Dropwizard is using the Java ServiceLoader functionality to register and load extensions, the minimizeJar option of the maven-shade-plugin will lead to non-working application JARs.
Versioning Your JARs
Dropwizard can also use the project version if it’s embedded in the JAR’s manifest as the
Implementation-Version
. To embed this information using Maven, add the following to the
<build><plugins>
section of your pom.xml
file:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
</archive>
</configuration>
</plugin>
This can be handy when trying to figure out what version of your application you have deployed on a machine.
Once you’ve got that configured, go into your project directory and run mvn package
(or run the
package
goal from your IDE). You should see something like this:
[INFO] Including org.eclipse.jetty:jetty-util:jar:7.6.0.RC0 in the shaded jar.
[INFO] Including com.google.guava:guava:jar:10.0.1 in the shaded jar.
[INFO] Including com.google.code.findbugs:jsr305:jar:1.3.9 in the shaded jar.
[INFO] Including org.hibernate:hibernate-validator:jar:4.2.0.Final in the shaded jar.
[INFO] Including javax.validation:validation-api:jar:1.0.0.GA in the shaded jar.
[INFO] Including org.yaml:snakeyaml:jar:1.9 in the shaded jar.
[INFO] Replacing original artifact with shaded artifact.
[INFO] Replacing /Users/yourname/Projects/hello-world/target/hello-world-0.0.1-SNAPSHOT.jar with /Users/yourname/Projects/hello-world/target/hello-world-0.0.1-SNAPSHOT-shaded.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.415s
[INFO] Finished at: Fri Dec 02 16:26:42 PST 2011
[INFO] Final Memory: 11M/81M
[INFO] ------------------------------------------------------------------------
Congratulations! You’ve built your first Dropwizard project! Now it’s time to run it!
Running Your Application
Now that you’ve built a JAR file, it’s time to run it.
In your project directory, run this:
java -jar target/hello-world-0.0.1-SNAPSHOT.jar
You should see something like the following:
usage: java -jar hello-world-0.0.1-SNAPSHOT.jar
[-h] [-v] {server} ...
positional arguments:
{server} available commands
optional arguments:
-h, --help show this help message and exit
-v, --version show the service version and exit
Dropwizard takes the first command line argument and dispatches it to a matching command. In this
case, the only command available is server
, which runs your application as an HTTP server. The
server
command requires a configuration file, so let’s go ahead and give it
the YAML file we previously saved:
java -jar target/hello-world-0.0.1-SNAPSHOT.jar server hello-world.yml
You should see something like the following:
INFO [2011-12-03 00:38:32,927] io.dropwizard.core.cli.ServerCommand: Starting hello-world
INFO [2011-12-03 00:38:32,931] org.eclipse.jetty.server.Server: jetty-7.x.y-SNAPSHOT
INFO [2011-12-03 00:38:32,936] org.eclipse.jetty.server.handler.ContextHandler: started o.e.j.s.ServletContextHandler{/,null}
INFO [2011-12-03 00:38:32,999] com.sun.jersey.server.impl.application.WebApplicationImpl: Initiating Jersey application, version 'Jersey: 1.10 11/02/2011 03:53 PM'
INFO [2011-12-03 00:38:33,041] io.dropwizard.core.setup.Environment:
GET /hello-world (com.example.helloworld.resources.HelloWorldResource)
INFO [2011-12-03 00:38:33,215] org.eclipse.jetty.server.handler.ContextHandler: started o.e.j.s.ServletContextHandler{/,null}
INFO [2011-12-03 00:38:33,235] org.eclipse.jetty.server.AbstractConnector: Started BlockingChannelConnector@0.0.0.0:8080 STARTING
INFO [2011-12-03 00:38:33,238] org.eclipse.jetty.server.AbstractConnector: Started SocketConnector@0.0.0.0:8081 STARTING
Your Dropwizard application is now listening on ports 8080
for application requests and 8081
for administration requests. If you press ^C
, the application will shut down gracefully, first
closing the server socket, then waiting for in-flight requests to be processed, then shutting down
the process itself.
However, while it’s up, let’s give it a whirl! Click here to say hello! Click here to get even friendlier!
So, we’re generating sayings. Awesome. But that’s not all your application can do. One of the main reasons for using Dropwizard is the out-of-the-box operational tools it provides, all of which can be found on the admin port.
If you click through to the metrics resource, you can see all of your application’s metrics represented as a JSON object.
The threads resource allows you to quickly get a thread dump of all the threads running in that process.
Hint
When a Jetty worker thread is handling an incoming HTTP request, the thread name is set to the method and URI of the request. This can be very helpful when debugging a poorly-behaving request.
The healthcheck resource runs the health check class we wrote. You should see something like this:
* deadlocks: OK
* template: OK
template
here is the result of your TemplateHealthCheck
, which unsurprisingly passed.
deadlocks
is a built-in health check which looks for deadlocked JVM threads and prints out a
listing if any are found.
Next Steps
Well, congratulations. You’ve got a Hello World application ready for production (except for the lack of tests) that’s capable of doing 30,000-50,000 requests per second. Hopefully, you’ve gotten a feel for how Dropwizard combines Jetty, Jersey, Jackson, and other stable, mature libraries to provide a phenomenal platform for developing RESTful web applications.
There’s a lot more to Dropwizard than is covered here (commands, bundles, servlets, advanced configuration, validation, HTTP clients, database clients, views, etc.), all of which is covered by the User Manual.