Day 13: Dropwizard — The Awesome Java REST Server Stack

I have mainly been a Java guy throughout my 8 years as a software developer. For most of the applications I have written, I used the Spring framework or Java EE. Lately, I am spending time learning web development in Python, and one thing that has impressed me a lot is the Flask framework. The Flask framework is a micro-framework which makes it very easy to write REST backends. Today for my 30 day challenge, I decided to find a Java alternative to Python’s Flask framework. After doing some research, I discovered that the Dropwizard framework can help me achieve the same productivity as the Flask framework. In this blog, we will learn how to build a RESTful Java MongoDB application using Dropwizard.

Dropwizard logo

What is Dropwizard?

Dropwizard is an open source Java framework for developing ops-friendly, high-performance RESTful backends. It was developed by Yammer to power their JVM based backend.

Dropwizard provides best-of-breed Java libraries into one embedded application package. It consists of following components :

  1. Embedded Jetty : Every application is packaged as a jar instead of war file and starts its own embedded jetty container. There is no WAR file and no external servlet container.

  2. JAX-RS : Jersey(the reference implementation for JAX-RS) is used to write RESTful web services. So, your existing JAX-RS knowledge is not wasted.

  3. JSON : The REST services speaks JSON. Jackson library is used to do all the JSON processing.

  4. Logging : It is done using Logback and SLF4J.

  5. Hibernate Validator : Dropwizard uses Hibernate Validator API for declarative validation.

  6. Metrics : Dropwizard has support for monitoring using the metrics library. It provides unparalleled insight into what our code does in production.

Dropwizard also uses some other libraries apart from the one mentioned above. You can find the full list here.

Why Dropwizard?

The reasons I decided to learn Dropwizard are mentioned below:

  1. Quick Project Bootstrap : If you have worked with Spring or Java EE, you will understand the pain a developer has to go through to bootstrap a project. With Dropwizard, you just have to add one dependency in your pom.xml file, and you are done.

  2. Application Metrics : Dropwizard comes with support for application metrics. It provides very useful information like request/response time etc. We just have to put @Timed annotation to get the method execution time.

  3. Productivity : Every Dropwizard application has one main program which starts the jetty container. This means that we can run and debug the application as a main program from within the IDE. There is no need to recompile or redeploy the WAR file.

Github Repository

The code for today’s demo application is available on github: day13-dropwizard-mongodb-demo-app.

Prerequisite

  1. Basic Java knowledge is required.

  2. Download and install the MongoDB database.

  3. Install the latest Java Development Kit (JDK) on your operating system. We can either install OpenJDK 7 or Oracle JDK 7. OpenShift supports both OpenJDK 6 and 7. In this blog, we will be using JDK 7.

  4. Download the latest Eclipse package for your operating system from the official Eclipse website. At the time of writing this blog, the latest Eclipse package is Kepler.

It is very easy to install eclipse, just extract the downloaded package, and we are done. On the linux or mac machines, open a new command line terminal and type the following command:

$ tar -xzvf eclipse-jee-kepler-R-*.tar.gz 

On windows, we can extract the zip file using winzip or 7-zip or any other software.
After we have extracted Eclipse, there will be a folder named eclipse in the directory where we extracted the download. We can optionally create a shortcut of the executable file.

Step 1: Create new Maven project

Open the Eclipse IDE and then navigate to the project workspace. To create a new project, Go to File > New > Maven Project. Choose the maven-archetype-quickstart, then enter Ground Id and Artifact Id, and finally press Finish.

Step 2 : Update the pom.xml

Now update the pom.xml file to include the dropwizard-core maven dependency. We will also update the Maven project to use Java version 1.7. After updating the pom.xml file, update the Maven Project(Right Click > Maven > Update Project)

<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.shekhar</groupId>
    <artifactId>blog</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
 
    <name>blog</name>
    <url>http://maven.apache.org</url>
 
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>com.yammer.dropwizard</groupId>
            <artifactId>dropwizard-core</artifactId>
            <version>0.6.2</version>
        </dependency>
 
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
 
 
        </plugins>
    </build>
</project>

Step 3 : Create Configuration class

Every Dropwizard application has one configuration class which specifies the environment specific parameters. Later in this blog post, we will add MongoDB configuration parameters like host, port, and db name to it.
This class extends the com.yammer.dropwizard.config.Configuration class.

import com.yammer.dropwizard.config.Configuration;
 
public class BlogConfiguration extends Configuration{
 
}

Step 4 : Create a Service class

The Dropwizard project is bootstrapped by a service class. This class pulls together various bundles and commands which provide basic functionality. It also starts the embedded jetty server and extends com.yammer.dropwizard.Service.

import com.yammer.dropwizard.Service;
import com.yammer.dropwizard.config.Bootstrap;
import com.yammer.dropwizard.config.Environment;
 
public class BlogService extends Service<BlogConfiguration> {
 
    public static void main(String[] args) throws Exception {
        new BlogService().run(new String[] { "server" });
    }
 
    @Override
    public void initialize(Bootstrap<BlogConfiguration> bootstrap) {
        bootstrap.setName("blog");
    }
 
    @Override
    public void run(BlogConfiguration configuration, Environment environment) throws Exception {
 
    }
 
}

The service class shown above does the following:

  1. The class has a main method which acts as our service entry point. Inside the main method, we create an instance of BlogService and call the run method. We pass the server command as argument. The server command will start the embedded jetty server.

  2. The initialize method is called before running the service run method. We set the name of service to blog.

  3. Next, we have the run method which will be called when service runs. Later in this blog, We will add JAX-RS resources inside this method.

Step 5 : Write IndexResource

Let us write our first resource which will be invoked when a GET request is made to the ‘/’ url. Create a new JAX-RS resource as shown below. This resource will list all the blogs.

import java.util.Arrays;
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.yammer.metrics.annotation.Timed;
 
@Path("/")
public class IndexResource {
 
    @GET
    @Produces(value = MediaType.APPLICATION_JSON)
    @Timed
    public List<Blog> index() {
        return Arrays.asList(new Blog("Day 12: OpenCV--Face Detection for Java Developers",
                "https://www.openshift.com/blogs/day-12-opencv-face-detection-for-java-developers"));
    }
}

The code shown above is a standard JAX-RS resource class. It is annotated with @Path annotation and defines index() method. The index() returns a collection of blogs. These blogs will be converted to JSON document. The @Timed annotation makes sure that Dropwizard infrastructure time this method.

The above mentioned IndexResource used a blog representation. It is shown as below. The Blog representation uses hibernate validator annotations to make sure the content is valid. For example, we use @URL annotation to make sure only valid URLs are stored in the MongoDB database.

import java.util.Date;
import java.util.UUID;
 
import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.URL;
 
public class Blog {
 
    private String id = UUID.randomUUID().toString();
 
    @NotBlank
    private String title;
 
    @URL
    @NotBlank
    private String url;
 
    private final Date publishedOn = new Date();
 
    public Blog() {
    }
 
    public Blog(String title, String url) {
        super();
        this.title = title;
        this.url = url;
    }
 
    public String getId() {
        return id;
    }
 
    public String getTitle() {
        return title;
    }
 
    public String getUrl() {
        return url;
    }
 
    public Date getPublishedOn() {
        return publishedOn;
    }
}

Next, we register IndexResource in the service class run method. Update the run method in BlogService with the one shown below.

@Override
public void run(BlogConfiguration configuration, Environment environment) throws Exception {
   environment.addResource(new IndexResource());
}

Now we can run the BlogService class as a main program i.e. Right Click > Run As > Java Application. It will start the embedded jetty container and we can see the application running at http://localhost:8080/.

$ curl http://localhost:8080
 
[{"id":"9bb43d53-5436-4dac-abaa-ac530c833df1","title":"Day 12: OpenCV--Face Detection for Java Developers","url":"https://www.openshift.com/blogs/day-12-opencv-face-detection-for-java-developers","publishedOn":1384090975372}]

The administrative interface is available at http://localhost:8081/.

Dropwizard Administrative View

We can check the metrics of the IndexResource by clicking Metrics. The data is available in JSON format.

"com.shekhar.blog.IndexResource" : {
    "index" : {
      "type" : "timer",
      "duration" : {
        "unit" : "milliseconds",
        "min" : 17.764,
        "max" : 17.764,
        "mean" : 17.764,
        "std_dev" : 0.0,
        "median" : 17.764,
        "p75" : 17.764,
        "p95" : 17.764,
        "p98" : 17.764,
        "p99" : 17.764,
        "p999" : 17.764
      },
      "rate" : {
        "unit" : "seconds",
        "count" : 1,
        "mean" : 7.246537731991882E-4,
        "m1" : 2.290184897291144E-12,
        "m5" : 3.551918562683463E-5,
        "m15" : 2.445031498756583E-4
      }
    }
  },

Step 6 : Configuring MongoDB

Add the mongo-jackson-mapper dependency in pom.xml.

<dependency>
    <groupId>net.vz.mongodb.jackson</groupId>
    <artifactId>mongo-jackson-mapper</artifactId>
    <version>1.4.2</version>
</dependency>

Update the BlogConfiguration class with MongoDB database details i.e. host, port, and database name.

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
 
import org.codehaus.jackson.annotate.JsonProperty;
import org.hibernate.validator.constraints.NotEmpty;
 
import com.yammer.dropwizard.config.Configuration;
 
public class BlogConfiguration extends Configuration {
 
    @JsonProperty
    @NotEmpty
    public String mongohost = "localhost";
 
    @JsonProperty
    @Min(1)
    @Max(65535)
    public int mongoport = 27017;
 
    @JsonProperty
    @NotEmpty
    public String mongodb = "mydb";
}

Next we will create a new class named MongoManaged which allows us to manage resources on application start and stop. This implements com.yammer.dropwizard.lifecycle.Managed.

import com.mongodb.Mongo;
import com.yammer.dropwizard.lifecycle.Managed;
 
public class MongoManaged implements Managed {
 
    private Mongo mongo;
 
    public MongoManaged(Mongo mongo) {
        this.mongo = mongo;
    }
 
    @Override
    public void start() throws Exception {
    }
 
    @Override
    public void stop() throws Exception {
        mongo.close();
    }
 
}

In the code shown above, we close the MongoDB connections in stop method.

Next we will write a MongoHealthCheck which will check if MongoDB is connected or not. A health check is Dropwizard feature to do a runtime test to verify the service’s behaviour in production environment.

import com.mongodb.Mongo;
import com.yammer.metrics.core.HealthCheck;
 
public class MongoHealthCheck extends HealthCheck {
 
    private Mongo mongo;
 
    protected MongoHealthCheck(Mongo mongo) {
        super("MongoDBHealthCheck");
        this.mongo = mongo;
    }
 
    @Override
    protected Result check() throws Exception {
        mongo.getDatabaseNames();
        return Result.healthy();
    }
 
}

Now we will update the BlogService class to include the MongoDB configuration.

package com.shekhar.blog;
 
import com.mongodb.Mongo;
import com.yammer.dropwizard.Service;
import com.yammer.dropwizard.config.Bootstrap;
import com.yammer.dropwizard.config.Environment;
 
public class BlogService extends Service<BlogConfiguration> {
 
    public static void main(String[] args) throws Exception {
        new BlogService().run(new String[] { "server" });
    }
 
    @Override
    public void initialize(Bootstrap<BlogConfiguration> bootstrap) {
        bootstrap.setName("blog");
    }
 
    @Override
    public void run(BlogConfiguration configuration, Environment environment) throws Exception {
        Mongo mongo = new Mongo(configuration.mongohost, configuration.mongoport);
        MongoManaged mongoManaged = new MongoManaged(mongo);
        environment.manage(mongoManaged);
 
        environment.addHealthCheck(new MongoHealthCheck(mongo));
 
        environment.addResource(new IndexResource());
    }
 
}

In the code shown above :

  1. A new Mongo instance is created using the BlogConfiguration object.
  2. A new instance of MongoManaged is created and added to the environment.
  3. A health check is added.

Run the application as a main program. You can check if MongoDB is running or not by going to the HealtCheck page loacated at http://localhost:8081/healthcheck. If MongoDB is not running, you will see an exception stacktrace.

! MongoDBHealthCheck: ERROR
!  can't call something : Shekhars-MacBook-Pro.local/192.168.1.101:27017/admin
 
com.mongodb.MongoException$Network: can't call something : Shekhars-MacBook-Pro.local/192.168.1.101:27017/admin
    at com.mongodb.DBTCPConnector.call(DBTCPConnector.java:227)
    at com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:305)
    at com.mongodb.DB.command(DB.java:160)
    at com.mongodb.DB.command(DB.java:183)
    at com.mongodb.Mongo.getDatabaseNames(Mongo.java:327)
    at com.shekhar.blog.MongoHealthCheck.check(MongoHealthCheck.java:17)
    at com.yammer.metrics.core.HealthCheck.execute(HealthCheck.java:195)
    at 
Caused by: java.io.IOException: couldn't connect to [Shekhars-MacBook-Pro.local/192.168.1.101:27017] bc:java.net.ConnectException: Connection refused
    at com.mongodb.DBPort._open(DBPort.java:228)
    at com.mongodb.DBPort.go(DBPort.java:112)
    at com.mongodb.DBPort.call(DBPort.java:79)
    at com.mongodb.DBTCPConnector.call(DBTCPConnector.java:218)
    ... 33 more
 
* deadlocks: OK

Now start MongoDB and you should see the following.

* MongoDBHealthCheck: OK
* deadlocks: OK

Step 7 : Create BlogResource

Now we will write the BlogResource class which will be responsible for creating the blog entries.

import java.util.ArrayList;
import java.util.List;
 
import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
 
import net.vz.mongodb.jackson.DBCursor;
import net.vz.mongodb.jackson.JacksonDBCollection;
 
import com.yammer.metrics.annotation.Timed;
 
@Path("/blogs")
@Produces(value = MediaType.APPLICATION_JSON)
@Consumes(value = MediaType.APPLICATION_JSON)
public class BlogResource {
 
    private JacksonDBCollection<Blog, String> collection;
 
    public BlogResource(JacksonDBCollection<Blog, String> blogs) {
        this.collection = blogs;
    }
 
    @POST
    @Timed
    public Response publishNewBlog(@Valid Blog blog) {
        collection.insert(blog);
        return Response.noContent().build();
    }
}

Next we will update the BlogService run method to add BlogResource as well.

 @Override
    public void run(BlogConfiguration configuration, Environment environment) throws Exception {
        Mongo mongo = new Mongo(configuration.mongohost, configuration.mongoport);
        MongoManaged mongoManaged = new MongoManaged(mongo);
        environment.manage(mongoManaged);
 
        environment.addHealthCheck(new MongoHealthCheck(mongo));
 
        DB db = mongo.getDB(configuration.mongodb);
        JacksonDBCollection<Blog, String> blogs = JacksonDBCollection.wrap(db.getCollection("blogs"), Blog.class, String.class);
 
        environment.addResource(new IndexResource());
 
        environment.addResource(new BlogResource(blogs));
    }

Run the BlogService class as a Java application. To test the BlogResource, make a curl request as shown below:

$ curl -i -X POST -H "Content-Type: application/json" -d '{"title":"Day 12: OpenCV--Face Detection for Java Developers","url":"https://www.openshift.com/blogs/day-12-opencv-face-detection-for-java-developers"}' http://localhost:8080/blogs
 
 
HTTP/1.1 204 No Content
Date: Sun, 10 Nov 2013 14:08:03 GMT
Content-Type: application/json

Step 8 : Update IndexResource

Now we will update the IndexResource index() method to get all the blog documents from MongoDB.

import java.util.ArrayList;
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 net.vz.mongodb.jackson.DBCursor;
import net.vz.mongodb.jackson.JacksonDBCollection;
 
import com.yammer.metrics.annotation.Timed;
 
@Path("/")
public class IndexResource {
 
    private JacksonDBCollection<Blog, String> collection;
 
    public IndexResource(JacksonDBCollection<Blog, String> blogs) {
        this.collection = blogs;
    }
 
    @GET
    @Produces(value = MediaType.APPLICATION_JSON)
    @Timed
    public List<Blog> index() {
        DBCursor<Blog> dbCursor = collection.find();
        List<Blog> blogs = new ArrayList<>();
        while (dbCursor.hasNext()) {
            Blog blog = dbCursor.next();
            blogs.add(blog);
        }
        return blogs;
    }
 
}

We will update the BlogService run method to pass the blogs collection to the IndexResource.

 @Override
    public void run(BlogConfiguration configuration, Environment environment) throws Exception {
        Mongo mongo = new Mongo(configuration.mongohost, configuration.mongoport);
        MongoManaged mongoManaged = new MongoManaged(mongo);
        environment.manage(mongoManaged);
 
        environment.addHealthCheck(new MongoHealthCheck(mongo));
 
        DB db = mongo.getDB(configuration.mongodb);
        JacksonDBCollection<Blog, String> blogs = JacksonDBCollection.wrap(db.getCollection("blogs"), Blog.class, String.class);
 
        environment.addResource(new IndexResource(blogs));
 
        environment.addResource(new BlogResource(blogs));
    }

Run the BlogService class as a Java application. To test the BlogResource, make a curl request as shown below:

$ curl http://localhost:8080
 
[{"id":"527f9806300462bbd300687e","title":"Day 12: OpenCV--Face Detection for Java Developers","url":"https://www.openshift.com/blogs/day-12-opencv-face-detection-for-java-developers","publishedOn":1384093702592}]

Step 9 : Deploy to the cloud

There is a community blog post which shows how we can deploy Dropwizard applications on OpenShift. You can read the blog here.

That’s it for today. Keep giving feedback.

Next Steps

Automatic Updates

Stay informed and learn more about OpenShift by receiving email updates.

Categories
Java, OpenShift Online
Tags
,
Comments are closed.