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.
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 :
- 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.
-
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.
-
JSON : The REST services speaks JSON. Jackson library is used to do all the JSON processing.
-
Logging : It is done using Logback and SLF4J.
-
Hibernate Validator : Dropwizard uses Hibernate Validator API for declarative validation.
-
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:
- 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.
-
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.
-
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
- Basic Java knowledge is required.
-
Download and install the MongoDB database.
-
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.
-
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:
- 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.
-
The initialize method is called before running the service run method. We set the name of service to blog.
-
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/.
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 :
- A new Mongo instance is created using the BlogConfiguration object.
- A new instance of MongoManaged is created and added to the environment.
- 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
- Sign up for OpenShift Online and try this out yourself
- Promote and show off your awesome app in the OpenShift Application Gallery today.
Automatic Updates
Stay informed and learn more about OpenShift by receiving email updates.
{{cta('1ba92822-e866-48f0-8a92-ade9f0c3b6ca')}}
关于作者
产品
工具
试用购买与出售
沟通
关于红帽
我们是世界领先的企业开源解决方案供应商,提供包括 Linux、云、容器和 Kubernetes。我们致力于提供经过安全强化的解决方案,从核心数据中心到网络边缘,让企业能够更轻松地跨平台和环境运营。