package net.peierls.util;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Scope;
import com.google.inject.ProvisionException;
import com.google.inject.spi.DefaultBindingScopingVisitor;
public class ConcurrentSingletonScope implements Scope {
public static void install(Binder binder) {
Scope scope = new ConcurrentSingletonScope();
binder.bindScope(ConcurrentSingleton.class, scope);
binder.requestInjection(scope);
}
public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) {
return new Provider<T>() {
public T get() {
ConcurrentMap<Key<T>, Future<T>> futures = futures();
Future<T> f = futures.get(key);
if (f == null) {
FutureTask<T> creator = new FutureTask<T>(new Callable<T>() {
public T call() {
return unscoped.get();
}
});
f = futures.putIfAbsent(key, creator);
if (f == null) {
f = creator;
creator.run();
}
}
try {
return f.get();
} catch (ExecutionException e) {
throw (RuntimeException) e.getCause();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new ProvisionException("interrupted during provision");
}
}
};
}
private ConcurrentSingletonScope() {
}
@Inject private void initializeBindingsInThisScope(Injector injector) {
final Scope thisScope = this;
final ExecutorService pool = Executors.newCachedThreadPool();
for (final Binding<?> binding : injector.getBindings().values()) {
binding.acceptScopingVisitor(new DefaultBindingScopingVisitor<Void>() {
@Override public Void visitScope(Scope scope) {
if (scope == thisScope) {
pool.execute(new Runnable() {
public void run() {
binding.getProvider().get();
}
});
}
return null;
}
});
}
pool.shutdown();
}
@SuppressWarnings("unchecked")
<T> ConcurrentMap<Key<T>, Future<T>> futures() {
return (ConcurrentMap<Key<T>, Future<T>>) futures;
}
private final ConcurrentMap futures = new ConcurrentHashMap();
}