package com.mastfrog.acteur.mongo;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.MongoClient;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Intercepts MongoClient and collection creation calls. Note: This class
* registers itself in the superclass constructor - it's best of constructors of
* subclasses do nothing of significance.
* <p/>
* Note that instances should not be stateful - if the connection is lost, it is
* entirely possible for an initializer to be called more than once for the same
* purpose. Implementations should be stateless.
* <p/>
* To register your MongoInitializer, simply bind it as an eager singleton.
*
* @author Tim Boudreau
*/
public abstract class MongoInitializer {
@Inject
protected MongoInitializer(Registry registry) {
registry.register(this);
}
protected void onBeforeCreateMongoClient(String host, int port) {
}
/**
* Called once the client is created
*
* @param client
*/
protected void onMongoClientCreated(MongoClient client) {
}
/**
* Override to ensure indexes, etc..
*
* @param collection
*/
protected void onCreateCollection(DBCollection collection) {
}
/**
* Called to set up parameters for a collection which is being created
*/
protected void onBeforeCreateCollection(String name, BasicDBObject params) {
}
@Singleton
protected static final class Registry implements Iterable<MongoInitializer> {
private final List<MongoInitializer> initializers = Collections.synchronizedList(new ArrayList<MongoInitializer>());
private Reference<MongoClient> client = null;
private final ScheduledExecutorService tp = Executors.newScheduledThreadPool(1);
void register(MongoInitializer init) {
initializers.add(init);
MongoClient client = null;
synchronized (this) {
if (this.client != null) {
client = this.client.get();
}
}
if (client != null) {
// XXX this is so the wrong way to get out of a chicken-and-the-egg situation
tp.schedule(new RunInit(client, init), 200, TimeUnit.MILLISECONDS);
}
}
static class RunInit implements Runnable {
private final MongoClient client;
private final MongoInitializer init;
public RunInit(MongoClient client, MongoInitializer init) {
this.client = client;
this.init = init;
}
@Override
public void run() {
synchronized (init) {
init.onMongoClientCreated(client);
}
}
}
protected void onBeforeCreateMongoClient(String host, int port) {
for (MongoInitializer ini : initializers) {
ini.onBeforeCreateMongoClient(host, port);
}
}
@Override
public Iterator<MongoInitializer> iterator() {
return Collections.unmodifiableList(initializers).iterator();
}
void onBeforeCreateCollection(String name, BasicDBObject params) {
for (MongoInitializer mi : initializers) {
mi.onBeforeCreateCollection(name, params);
}
}
void onCreateCollection(DBCollection collection) {
for (MongoInitializer mi : initializers) {
mi.onCreateCollection(collection);
}
}
synchronized void onMongoClientCreated(MongoClient client) {
this.client = new WeakReference<>(client);
for (MongoInitializer mi : initializers) {
synchronized (mi) {
mi.onMongoClientCreated(client);
}
}
}
}
}