/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.jooby.mongodb; import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import org.jooby.Env; import org.jooby.Env.ServiceKey; import org.jooby.Jooby; import com.google.inject.Binder; import com.mongodb.DB; import com.mongodb.MongoClient; import com.mongodb.MongoClientOptions; import com.mongodb.MongoClientOptions.Builder; import com.mongodb.MongoClientURI; import com.mongodb.client.MongoDatabase; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; /** * Exposes {@link MongoClient} and a {@link DB}. * * <h1>usage</h1> * * <p> * application.conf: * </p> * * <pre> * db = "mongodb://localhost/mydb" * </pre> * * <pre> * { * use(new Mongodb()); * * get("/", req {@literal ->} { * MongoClient client = req.require(MongoClient.class); * // work with client * DB = req.require(DB.class); * // work with mydb * }); * } * </pre> * * Default URI connection property is <code>db</code> but of course you can use any other name: * * <p> * application.conf: * </p> * * <pre> * mydb = "mongodb://localhost/mydb" * </pre> * * <pre> * { * use(new Mongodb("mydb")); * * get("/", req {@literal ->} { * DB mydb = req.require(DB.class); * // work with mydb * }); * } * </pre> * * <h1>options</h1> * <p> * Options can be set via <code>.conf</code> file: * </p> * * <pre> * mongodb.connectionsPerHost = 100 * </pre> * * <p> * or programmatically: * </p> * * <pre> * { * use(new Mongodb() * .options((options, config) {@literal ->} { * options.connectionsPerHost(100); * }) * ); * } * </pre> * * <h2>connection URI</h2> * <p> * Default connection URI is defined by the <code>db</code> property. Mongodb URI looks like: * </p> * * <pre> * mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database[.collection]][?options]] * </pre> * * For more detailed information please check: {@link MongoClientURI}. * * <h1>two or more connections</h1> * * <pre> * db1 = "mongodb://localhost/mydb1" * db2 = "mongodb://localhost/mydb2" * </pre> * * <pre> * { * use(new Mongodb("db1")); * use(new Mongodb("db2")); * * get("/", req {@literal ->} { * MongoClient client1 = req.require("mydb1", MongoClient.class); * // work with mydb1 * MongoClient client2 = req.require("mydb2", MongoClient.class); * // work with mydb1 * }); * } * </pre> * * @author edgar * @since 0.5.0 */ public class Mongodb implements Jooby.Module { private final String db; private BiConsumer<Builder, Config> options; /** * Creates a new {@link Mongodb} module. * * @param db Name of the property with the connection URI. */ public Mongodb(final String db) { this.db = requireNonNull(db, "A mongo db is required."); } /** * Creates a new {@link Mongodb} using the default property: <code>db</code>. */ public Mongodb() { this("db"); } @Override public void configure(final Env env, final Config config, final Binder binder) { configure(env, config, binder, (uri, mongo) -> { }); } protected void configure(final Env env, final Config config, final Binder binder, final BiConsumer<MongoClientURI, MongoClient> callback) { MongoClientOptions.Builder options = options(mongodb(config)); if (this.options != null) { this.options.accept(options, config); } MongoClientURI uri = new MongoClientURI(config.getString(db), options); String database = uri.getDatabase(); checkArgument(database != null, "Database not found: " + uri); MongoClient client = new MongoClient(uri); ServiceKey serviceKey = env.serviceKey(); serviceKey.generate(MongoClientURI.class, database, k -> binder.bind(k).toInstance(uri)); serviceKey.generate(MongoClient.class, database, k -> binder.bind(k).toInstance(client)); MongoDatabase mongodb = client.getDatabase(database); serviceKey.generate(MongoDatabase.class, database, k -> binder.bind(k).toInstance(mongodb)); env.onStop(client::close); callback.accept(uri, client); } /** * Set an options callback. * * <pre> * { * use(new Mongodb() * .options((options, config) {@literal ->} { * options.connectionsPerHost(100); * }) * ); * } * </pre> * * @param options Configure callback. * @return This module */ public Mongodb options(final BiConsumer<MongoClientOptions.Builder, Config> options) { this.options = requireNonNull(options, "Options callback is required."); return this; } @Override public Config config() { return ConfigFactory.parseResources(Mongodb.class, "mongodb.conf"); } private MongoClientOptions.Builder options(final Config config) { MongoClientOptions.Builder builder = MongoClientOptions.builder(); builder.connectionsPerHost(config.getInt("connectionsPerHost")); builder.threadsAllowedToBlockForConnectionMultiplier( config.getInt("threadsAllowedToBlockForConnectionMultiplier")); builder.maxWaitTime((int) config.getDuration("maxWaitTime", TimeUnit.MILLISECONDS)); builder.connectTimeout((int) config.getDuration("connectTimeout", TimeUnit.MILLISECONDS)); builder.socketTimeout((int) config.getDuration("socketTimeout", TimeUnit.MILLISECONDS)); builder.socketKeepAlive(config.getBoolean("socketKeepAlive")); builder.cursorFinalizerEnabled(config.getBoolean("cursorFinalizerEnabled")); builder.alwaysUseMBeans(config.getBoolean("alwaysUseMBeans")); builder.heartbeatFrequency(config.getInt("heartbeatFrequency")); builder.minHeartbeatFrequency(config.getInt("minHeartbeatFrequency")); builder.heartbeatConnectTimeout( (int) config.getDuration("heartbeatConnectTimeout", TimeUnit.MILLISECONDS)); builder.heartbeatSocketTimeout( (int) config.getDuration("heartbeatSocketTimeout", TimeUnit.MILLISECONDS)); return builder; } private Config mongodb(final Config config) { Config $mongodb = config.getConfig("mongodb"); if (config.hasPath("mongodb." + db)) { $mongodb = config.getConfig("mongodb." + db).withFallback($mongodb); } return $mongodb; } }