package com.tesora.dve.singleton;
/*
* #%L
* Tesora Inc.
* Database Virtualization Engine
* %%
* Copyright (C) 2011 - 2014 Tesora Inc.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Modifier;
import java.util.concurrent.ConcurrentHashMap;
/**
*
*/
public class Singletons {
static final Logger logger = LoggerFactory.getLogger(Singletons.class);
static ConcurrentHashMap<Class,Object> singletons = new ConcurrentHashMap<>();
public static <S,C extends S> void register(Class<S> interfaceClazz, C concreteImpl){
innerPut(interfaceClazz, concreteImpl, false);
}
/**
* This method is like register, but is willing to swap out an existing instance. We are not protected from clients
* that have looked up the previous service and are making calls on it, so this is a bit of a hack. Currently it is
* required for the unit tests to work properly, but should be phased out when possible.
*/
public static <S,C extends S> void replace(Class<S> interfaceClazz, C concreteImpl){
innerPut(interfaceClazz, concreteImpl, true);
}
private static <S, C extends S> void innerPut(Class<S> interfaceClazz, C concreteImpl, boolean replace) {
if (!isLegalKey(interfaceClazz))
throw new IllegalArgumentException("Registry key must be an interface");
if (!interfaceClazz.isInstance(concreteImpl))
throw new IllegalArgumentException("Entry must implement the provided interface");
if (replace)
singletons.put(interfaceClazz,concreteImpl);
else {
Object existing = singletons.putIfAbsent(interfaceClazz,concreteImpl);
if (existing != null) {
String message = String.format("Entry for %s already present, concrete class is %s", interfaceClazz.getSimpleName(), existing.getClass().getSimpleName());
throw new IllegalStateException(message);
}
}
}
private static <S> boolean isLegalKey(Class<S> interfaceClazz) {
return interfaceClazz.isInterface() || Modifier.isAbstract(interfaceClazz.getModifiers());
}
public static <S> S unregister(Class<S> interfaceClazz){
if (! interfaceClazz.isInterface() )
throw new IllegalArgumentException("Registry key must be an interface");
return interfaceClazz.cast(singletons.remove(interfaceClazz));
}
public static <S> S lookup(Class<S> interfaceClazz){
return innerLookup(interfaceClazz,null, true);
}
public static <S> S lookup(Class<S> interfaceClazz, S defaultInstance){
return innerLookup(interfaceClazz,defaultInstance, true);
}
public static <S> S require(Class<S> interfaceClazz){
return innerLookup(interfaceClazz, null, false);
}
public static <S> S require(Class<S> interfaceClazz, S defaultInstance){
return innerLookup(interfaceClazz,defaultInstance, false);
}
private static <S> S innerLookup(Class<S> interfaceClazz, S defaultValue, boolean returnNullIfMissing) {
Object entry = singletons.get(interfaceClazz);
if (entry != null) {
return interfaceClazz.cast(entry);
} else if (defaultValue != null) {
return defaultValue;
} else {
if (returnNullIfMissing)
return null;
else
throw new IllegalStateException(String.format("Singleton for %s interface was not available",interfaceClazz.getSimpleName()));
}
}
}