package com.brightgenerous.datasource;
import java.lang.ref.SoftReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.brightgenerous.lang.Args;
import com.google.inject.Injector;
public abstract class DataSource {
private static volatile Class<? extends DataSource> dataSourceClazz;
private static volatile DataSource instance;
private static final Map<Class<? extends DataSource>, SoftReference<? extends DataSource>> cache = new ConcurrentHashMap<>();
private transient volatile InjectorFactory injectorFactory;
private transient volatile Initializer initializer;
private transient volatile Destroyer destroyer;
protected DataSource() {
}
public static void set(Class<? extends DataSource> clazz) {
Args.notNull(clazz, "clazz");
if (dataSourceClazz != clazz) {
synchronized (DataSource.class) {
if (dataSourceClazz != clazz) {
checkAndGetConstructor(clazz);
dataSourceClazz = clazz;
instance = null;
}
}
}
}
public static DataSource get() {
DataSource ret = instance;
if (ret == null) {
synchronized (DataSource.class) {
ret = instance;
if (ret == null) {
if (dataSourceClazz == null) {
throw new IllegalStateException(
"the Data Source Class yet has not been set.");
}
instance = get(dataSourceClazz);
ret = instance;
}
}
}
return ret;
}
public static DataSource get(Class<? extends DataSource> clazz) {
if (clazz == null) {
return get();
}
DataSource ret = null;
{
SoftReference<? extends DataSource> ref = cache.get(clazz);
if (ref != null) {
ret = ref.get();
}
}
if (ret == null) {
synchronized (cache) {
SoftReference<? extends DataSource> ref = cache.get(clazz);
if (ref != null) {
ret = ref.get();
}
if (ret == null) {
cache.remove(clazz);
Set<Class<? extends DataSource>> dels = new HashSet<>();
for (Entry<Class<? extends DataSource>, SoftReference<? extends DataSource>> entry : cache
.entrySet()) {
if (entry.getValue().get() == null) {
dels.add(entry.getKey());
}
}
if (!dels.isEmpty()) {
for (Class<? extends DataSource> del : dels) {
cache.remove(del);
}
}
ret = newInstance(clazz);
cache.put(clazz, new SoftReference<>(ret));
}
}
}
return ret;
}
private static DataSource newInstance(Class<? extends DataSource> clazz) {
try {
Constructor<? extends DataSource> cons = checkAndGetConstructor(clazz);
return cons.newInstance();
} catch (SecurityException | InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private static Constructor<? extends DataSource> checkAndGetConstructor(
Class<? extends DataSource> clazz) {
try {
Constructor<? extends DataSource> cons = clazz.getDeclaredConstructor();
if (!Modifier.isPublic(cons.getModifiers()) && !cons.isAccessible()) {
cons.setAccessible(true);
}
return cons;
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException e) {
throw new RuntimeException(e);
}
}
protected abstract InjectorFactory createInjectorFactory();
protected Initializer createInitializer() {
return null;
}
protected Destroyer createDestroyer() {
return null;
}
protected InjectorFactory getInjectorFactory() {
if (injectorFactory == null) {
synchronized (this) {
if (injectorFactory == null) {
injectorFactory = createInjectorFactory();
}
}
}
return injectorFactory;
}
public void initialize() {
getInjectorFactory().initialize();
if (initializer == null) {
synchronized (this) {
if (initializer == null) {
initializer = createInitializer();
}
}
}
if (initializer != null) {
initializer.initialize(getInjector());
}
getInjectorFactory().verify();
}
public void destroy() {
if (destroyer == null) {
synchronized (this) {
if (destroyer == null) {
destroyer = createDestroyer();
}
}
}
if (destroyer != null) {
destroyer.destroy(getInjector());
}
getInjectorFactory().destroy();
}
public <T> T instance(Class<T> clazz) {
return instance(clazz, false);
}
public <T> T instance(Class<T> clazz, boolean rollback) {
return getInjector(rollback).getInstance(clazz);
}
protected Injector getInjector() {
return getInjector(false);
}
protected Injector getInjector(boolean rollback) {
return getInjectorFactory().getInjector(rollback);
}
}