In this article, I'll demonstrate how to develop a microservice using the Dropwizard framework,
which offers a wide range of tools for creating web applications. Among the libraries are some of the following:
Open-source web server Jetty: Server is small and simple to include in any application.
Jersey: A RESTful web service creation implementation.
Jackson: Provides conversion between POJOs and JSON objects.
Metrics: Provides information regarding the status of HTTP requests, database connections, queues, etc.
Guava: Supports a variety of classes to deal with strings, collections, and validations.
Hibernate Validator: Offers Java object validations as constraints.
JDBI: Offers a versatile way to work with relational databases by encasing JDBC.
Liquibase: Ideal for DDL updates and migrations.
the following command: mvn archetype:generate -DarchetypeGroupId=io.dropwizard.archetypes -DarchetypeArtifactId=java-simple -DgroupId=com.demo -DarchetypeVersion=1.3.5 Name: DropwizardMongoDBMicroservice -DartifactId=dropwizard-mongodb-ms -Dversion=1.0.0-SNAPSHOT
Make sure Java and Maven are installed and configured, then open a terminal and paste the command below:
I modified the configuration file by adding various configurations in configurations.yaml. It appears as follows:
server:
maximum512 threads
applicationContextPath: application/dropwizard-mongodb-ms
connectors:
- type: http
port: 8080
- type: http
port: 8081
administratorConnectors
level of logging: INFO
log-keepers: com.display: INFO
# You are free to select the user and password of your choice.
mongoDBConnection:
credentials:
"user_donuts" is the user name.
the term "pAsw0Rd"
seeds:
- "mongodb" as the host; port: 27017
the database: "donuts"
bravado:
baseResourcePackage: com.demo.resources
Path: /dropwizard-mongodb-ms
scan: true
information:
versions: "1.0.0"
"Donuts API CRUD" is the title.
"A straightforward API for exposing CRUD operations on MongoDB collections"
"http://swagger.io/terms/" is the terms of service.
contact:
"Donuts API" is its name.
license:
nickname: "Rich"
io.dropwizard is a class that extends from configuration.yml.Configuration and appears in the com.demo package;
Import the JsonProperty, the MongoDBConnection, and the FasterXML Jackson annotation.
io.federecio.dropwizard.swagger.SwaggerBundleConfiguration and io.dropwizard.Configuration imports are required.
A common class DropwizardMongoDBMicroserviceConfiguration goes further:
The configuration:
- MongoDBConnection, which is private;
- @JsonProperty("swagger")swaggerBundleConfiguration, a private Swagger configuration;
- Getter and setter functions.
A portion of the code for this microservice, which will offer a REST API based on a CRUD (Create, Read, Update, Delete) model, is available in my Github repository, which you can discover at the conclusion of this tutorial.
The com.mongodb.client is what I'm using. In order to manipulate the CRUD activities, I constructed a DAO (Data Access Object) based on the MongoCollection interface for access to the collection known as donuts.
com.demo.db.daos package; imports...
public class TheDonutDAO {
// The complete collection of donuts
private final MongoCollection<Document> donutCollection;
/**
* Constructor.
*/
public DonutDAO(final MongoCollection<Document> donutCollection) {
if (this.donutCollection.equals(donutCollection)) {
// ...
}
}
public List<Donut> getAll() {
final MongoCursor<Document> donuts = donutCollection.find().iterator();
final List<Donut> donutsList = new ArrayList<>();
try {
while (donuts.hasNext()) {
Document donut = donuts.next();
donutsList.add(DonutMapper.map(donut));
}
} finally {
donuts.close();
}
return donutsList;
}
// Other methods...
}
Utility Class for Donut Mapping
I use a utility class that filters the fields from MongoDB to a POJO to map the data from the database to a POJO.
package com.demo.util;
import org.bson.Document;
import com.demo.api.Donut;
public class DonutMapper {
public static Donut mapDocumentToDonut(Document donutDocument) {
Donut donut = new Donut();
donut.setId(donutDocument.getObjectId("_id"));
donut.setFlavor(donutDocument.getString("flavor"));
donut.setPrice(donutDocument.getDouble("price"));
return donut;
}
}
Donut Class for Manipulation
To manipulate the data in MongoDB, construct a POJO.
package com.demo.api;
import javax.validation.constraints.NotNull;
import org.bson.types.ObjectId;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
public class Donut implements Serializable {
@JsonSerialize(using = ObjectIdSerializer.class)
private ObjectId id;
@NotNull
private double price;
@NotNull
private String flavor;
public Donut() {
// Constructor
}
// Hashcode, equals, and toString methods
// Getters and setters
}
Response Class for API Response
This class is only used in response to the endpoint.
package com.demo.api;
// Imports...
public class Response {
/** The message. */
private String message;
public Response() {
// Constructor
}
// Getters & setters
// Hashcode, equals, and toString methods
}
Utility Class for ObjectId Serialization
Here are more utility classes for using Jackson to convert org.bson.types.ObjectId to String objects.
import java.io.IOException;
import org.bson.types.ObjectId;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class ObjectIdSerializer extends JsonSerializer<ObjectId> {
@Override
public void serialize(final ObjectId objectId, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(objectId.toString());
}
}
Converting String to Char Array
The com.demo.util package is used to convert a String object to a char array.
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class PasswordSerializer extends JsonSerializer<String> {
@Override
public void serialize(final String input, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(input.toCharArray(), 0, input.toCharArray().length);
}
}
Mapping Credentials to Configuration
Use these POJOs from the com.demo.db.configuration package to map information to the configuration.yml.
import java.util.Objects;
import java.util.Arrays;
import com.demo.util.PasswordSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
// Other relevant imports...
public class Credentials {
private String username; /** The user name. */
private char[] password;
@JsonSerialize(using = PasswordSerializer.class)
public char[] getPassword() {
return password;
}
// Other methods for getters, setters, hashcode, and equals.
}
Seed Class in com.demo.db.configuration
Bring up the java.util.Objects.
package com.demo.db.configuration;
import java.util.Objects;
public class Seed {
private String host; /** The host. */
private int port; /** The port. */
// Getters, setters, hashcode, and equals methods.
}
MongoDBConnection Class in com.demo.db.configuration
private Credentials, including the user name and password.
package com.demo.db.configuration;
import java.util.List;
public class MongoDBConnection {
private Credentials credentials; /** Including the user name and password. */
private List<Seed> seeds; /** The list of seeds. */
private String database; /** The database, denoted by the symbol /** The db. */. */
// Getters, setters, hashcode, and equals methods.
}
Managing Objects with Dropwizard's Managed Interface
The interface io.dropwizard.lifecycle.Managed and class io.dropwizard.lifecycle.MongoDBManaged in the Dropwizard life cycle can be used to manage objects.
package com.demo.db;
// Imports...
public class MongoDBManaged implements Managed {
private final MongoClient mongoClient;
public MongoDBManaged(final MongoClient mongoClient) {
this.mongoClient = mongoClient;
}
@Override
public void start() throws Exception {
// Do initialization or setup here
}
@Override
public void stop() throws Exception {
mongoClient.close();
}
}
Creating a Healthcheck
An additional crucial class is Healthcheck, which may be created by simply extending from com.codahale.metrics.health.HealthCheck and implementing the method check.
package com.demo.db;
import com.codahale.metrics.health.HealthCheck;
public class CustomHealthcheck extends HealthCheck {
@Override
protected Result check() throws Exception {
// Implement your health check logic here
// Return a Result indicating the health status
return Result.healthy();
}
}
Implementing a Health Check
The HealthCheck class in the com.demo.health package implements a health check for a MongoDB client.
package com.demo.health;
import com.codahale.metrics.health.HealthCheck;
import com.mongodb.client.MongoClient;
import org.bson.Document;
public class DropwizardMongoDBHealthCheck extends HealthCheck {
private final MongoClient mongoClient;
public DropwizardMongoDBHealthCheck(final MongoClient mongoClient) {
this.mongoClient = mongoClient;
}
@Override
protected Result check() throws Exception {
try {
final Document document = mongoClient.getDatabase("donuts").runCommand(new Document("buildInfo", 1));
if (document == null) {
return Result.unhealthy("Cannot perform operation buildInfo in Database.");
}
return Result.healthy();
} catch (final Exception e) {
return Result.unhealthy("Cannot get the information from database.");
}
}
}
Microservice Entry Point Class
The class com.demo serves as this microservice's entry point.
// Imports...
public class ApplicationDropwizardMongoDBMicroserviceConfiguration> implements DropwizardMongoDBMicroserviceApplication {
private static final Logger LOGGER = LoggerFactory.getLogger(DropwizardMongoDBMicroserviceApplication.class);
public static void main(final String[] args) throws Exception {
new DropwizardMongoDBMicroserviceApplication();
LOGGER.info("Start application.").run(args);
}
@Override
public String getName() {
return "DropwizardMongoDBMicroservice";
}
// Other methods...
}
Building and Running the Project
When running the command to build the project:
mvn clean package
The database is necessary before the application can run; you may get MongoDB by downloading it from the website or using a Docker image from DockerHub.
Start a new container once the MongoDB image has been downloaded:
docker run --name mongodb -p 27017:27017 mongo
NOTE: To ensure the durability of information, make sure to create a volume.
Type the following commands once inside the container:
docker exec -it [container name or id] /bin/bash
mongo
use donuts
db.createUser({user: "user_donuts", pwd: "pAsw0Rd", roles: [{role: "readWrite", db: "donuts"}]})
SCRAM is the authentication method that MongoDB chooses by default.
Lastly, run the program by using the following command:
java -jar server configuration.yml target/dropwizard-mongodb-ms-1.0.0-SNAPSHOT
Once the user has been created, and We may input the swagger documentation in the database by going to the following URL:
Click the button below to access the Swagger API documentation:
Docker gives us a special environment, and all developers maintain synchronization of dependencies, databases, web servers, etc. This is one of the reasons we use it every day. The quick approach to create and use artifacts speeds up delivery.
This is an example of a docker compose file, which I use to start the application and the MongoDB server. I also add the Nginx proxy to receive requests.
Dropwizard offers a variety of tools to simplify Microservices development, including integration with technologies like MongoDB, Docker, and Nginx. While specifics may vary based on requirements, this provides a great starting point to explore and share experiences.