Access any version of MongoDB with Open Liberty using CDI

Using MongoDB with Open Liberty previously meant enabling the mongodb-2.0 feature and configuring several elements in server.xml, but was limited to certain versions of MongoDB. Now, with improvements in CDI and the introduction of MicroProfile Config, you can easily configure access to any version of MongoDB with a CDI producer (for an introduction to using CDI producers, see the Injecting Dependencies into Microservices guide).

A CDI producer for MongoDB

With a CDI producer, you are no longer restricted to MongoDB 2.X versions, and can easily provide a MongoDatabase to your application. This example demonstrates how to create a CDI producer to inject a MongoDatabase:

@ApplicationScoped
public class MongoProducer {

    @Produces
    public MongoClient createMongo() {
        return new MongoClient(new ServerAddress(), new MongoClientOptions.Builder().build());
    }

    @Produces
    public MongoDatabase createDB(MongoClient client) {
        return client.getDatabase("testdb");
    }

    public void close(@Disposes MongoClient toClose) {
        toClose.close();
    }
}

Here is an example of using the CDI producer to inject a MongoDatabase in a JAX-RS application:

@Inject
MongoDatabase db;

@POST
@Path("/add")
@Consumes(MediaType.APPLICATION_JSON)
public void add(CrewMember crewMember) {
    MongoCollection<Document> crew = db.getCollection("Crew");
    Document newCrewMember = new Document();
    newCrewMember.put("Name",crewMember.getName());
    newCrewMember.put("Rank",crewMember.getRank());
    newCrewMember.put("CrewID",crewMember.getCrewID());
    crew.insertOne(newCrewMember);
}

Enhancing the CDI producer

One of the advantages of using a CDI producer is that it can be tailored to your needs. For improved security, the createMongo method can be enhanced to include authentication with a user name and an encoded password:

@Produces
public MongoClient createMongo() {
    String password = PasswordUtil.passwordDecode(encodedPass);
    MongoCredential creds = MongoCredential.createCredential(user, dbName, password.toCharArray());
    return new MongoClient(new ServerAddress(), creds, new MongoClientOptions.Builder().build());
}

This requires passwordUtilities-1.0, available as a Maven dependency:

<dependency>
    <groupId>io.openliberty.features</groupId>
    <artifactId>passwordUtilities-1.0</artifactId>
    <version>18.0.0.4</version>
</dependency>

You also need to enable the feature in server.xml:

<feature>passwordUtilities-1.0</feature>

You can go further and enable SSL, changing the createMongo method to look something like this:

@Produces
public MongoClient createMongo() throws SSLException {
    String password = PasswordUtil.passwordDecode(encodedPass);
    MongoCredential creds = MongoCredential.createCredential(user, dbName, password.toCharArray());
    SSLContext sslContext = JSSEHelper.getInstance().getSSLContext("defaultSSLConfig", Collections.emptyMap(), null);
    return new MongoClient(new ServerAddress(hostname, port), creds, new MongoClientOptions.Builder()
                           .sslEnabled(true)
                           .sslContext(sslContext)
                           .build());
}

Configure with MicroProfile Config

Now that you have a more advanced CDI producer, it would be nice to add configurability to some of the variables, like the user and password. Using MicroProfile Config makes configuring the MongoDB driver simple. You can add the following to your CDI producer to add configuration:

@Inject
@ConfigProperty(name = "mongo.hostname", defaultValue = "localhost")
String hostname;

@Inject
@ConfigProperty(name = "mongo.port", defaultValue = "27017")
int port;

@Inject
@ConfigProperty(name = "mongo.dbname", defaultValue = "testdb")
String dbName;

@Inject
@ConfigProperty(name = "mongo.user")
String user;

@Inject
@ConfigProperty(name = "mongo.pass.encoded")
String encodedPass;

Now, by placing the following snippet in your microprofile-config.properties or server.env file, the values for user and encodedPass will be pulled into the MongoProducer class:

mongo.user=sampleUser
mongo.pass.encoded={aes}APtt+/vYxxPa0jE1rhmZue9wBm3JGqFK3JR4oJdSDGWM1wLr1ckvqkqKjSB2Voty8g==

No need for a MongoDB feature

Previously, using MongoDB required enabling the mongodb-2.0 feature, which was limited to certain MongoDB 2.X versions. By using a CDI producer, any version of MongoDB can be used, with no need for a specific MongoDB feature. Even if the MongoDB Java Driver API changes, simple updates to your CDI producer will allow it to continue to work. You should remove the mongodb-2.0 feature from your server.xml when using newer versions of MongoDB with a CDI producer.

The MongoDB driver should be bundled in your application. To do this with Maven you can use a dependency:

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId>
    <version>X.X.X</version>
</dependency>

If you have multiple applications accessing MongoDB, instead of bundling the MongoDB driver, you can configure a shared library in your server.xml like this:

<library id="MongoLib">
    <file name="${shared.resource.dir}/mongo-java-driver-3.8.0.jar" />
</library>

<webApplication location="MongoDBSample1.war">
    <classloader commonLibraryRef="MongoLib" />
</webApplication>

<webApplication location="MongoDBSample2.war">
    <classloader commonLibraryRef="MongoLib" />
</webApplication>

This illustrates how easy it is to create a CDI producer for MongoDB, configure it with MicroProfile Config, and use it to access a MongoDatabase in your application. The full sample is available on GitHub here: https://github.com/OpenLiberty/sample-mongodb

Share this post: