|
import java.io.IOException; |
|
import java.io.InputStream; |
|
import java.lang.invoke.MethodHandles; |
|
import java.lang.invoke.MethodType; |
|
import java.lang.reflect.Constructor; |
|
import java.lang.reflect.Method; |
|
import java.util.Properties; |
|
import org.apache.commons.lang3.StringUtils; |
|
import org.slf4j.Logger; |
|
import org.slf4j.LoggerFactory; |
|
|
|
interface Configuration { |
|
|
|
Logger LOGGER = LoggerFactory.getLogger(Configuration.class); |
|
|
|
enum Name implements NameEnumeration { |
|
CONFIGURATION_CLASS, |
|
CONFIGURATION_FILE, |
|
DB_DRIVER_CLASS, |
|
DB_DRIVER_URL, |
|
DB_DRIVER_USER, |
|
DB_DRIVER_PASS; |
|
} |
|
|
|
default Class dbDriver() { |
|
return Name.DB_DRIVER_CLASS.as(Class.class); |
|
} |
|
|
|
default String dbDriverUrl() { |
|
return Name.DB_DRIVER_URL.as(String.class); |
|
} |
|
|
|
default String dbDriverUser() { |
|
return Name.DB_DRIVER_USER.as(String.class); |
|
} |
|
|
|
default String dbDriverPass() { |
|
return Name.DB_DRIVER_PASS.as(String.class); |
|
} |
|
|
|
static Configuration getInstance() { |
|
return Builder.getInstance(); |
|
} |
|
|
|
@SuppressWarnings("unchecked") |
|
interface NameEnumeration { |
|
default <T> T as(final Class<T> as) { |
|
final String raw = getString(); |
|
if (as.isArray()) { |
|
final String[] arr = raw.split(","); |
|
return ConversionUtils.stringArrayToTypeArray(arr, as); |
|
} |
|
return ConversionUtils.stringToType(raw, as); |
|
} |
|
|
|
default String getString() { |
|
if (!"".equals(System.getProperty(this.toString(), ""))) { |
|
return System.getProperty(this.toString()); |
|
} |
|
if (System.getenv(this.toString()) != null) { |
|
return System.getenv(this.toString()); |
|
} |
|
if (Builder.defaults != null && Builder.defaults.containsKey(this.toString())) { |
|
return Builder.defaults.getProperty(this.toString()); |
|
} |
|
return null; |
|
} |
|
} |
|
|
|
final class Builder { |
|
private static Configuration instance = null; |
|
private static Properties defaults = null; |
|
|
|
private Builder() {} |
|
|
|
static Configuration getInstance() { |
|
if (instance != null) { |
|
return instance; |
|
} |
|
|
|
final Class<?> configClass; |
|
if (Name.CONFIGURATION_CLASS.as(Class.class) != null) { |
|
configClass = Name.CONFIGURATION_CLASS.as(Class.class); |
|
} else { |
|
configClass = Configuration.class; |
|
} |
|
|
|
// Build the configuration instance |
|
instance = (Configuration) proxy(configClass); |
|
|
|
// Get the configuration defaults |
|
final String configurationFile = StringUtils.defaultIfBlank( |
|
Name.CONFIGURATION_FILE.as(String.class), |
|
"/defaults.properties" |
|
); |
|
try (InputStream is = Configuration.class.getResourceAsStream(configurationFile)) { |
|
defaults = new Properties(); |
|
defaults.load(is); |
|
} catch (IOException ex) { |
|
LOGGER.error(ex.getMessage(), ex); |
|
throw new RuntimeException(ex); |
|
} |
|
return instance; |
|
} |
|
|
|
@SuppressWarnings("unchecked") |
|
private static <T> T proxy(final Class<T> type) { |
|
return (T) java.lang.reflect.Proxy.newProxyInstance( |
|
Configuration.class.getClassLoader(), |
|
new Class[]{type}, |
|
(proxy, method, args) -> { |
|
if (JavaUtils.getMajorVersion() == JavaUtils.VERSION_9) { |
|
return call(MethodHandles.lookup(), method, type, proxy, args); |
|
} |
|
|
|
try { |
|
Constructor<MethodHandles.Lookup> constructor |
|
= MethodHandles.Lookup.class.getDeclaredConstructor(Class.class); |
|
constructor.setAccessible(true); |
|
constructor.newInstance(type); |
|
call(constructor.newInstance(type), method, type, proxy, args); |
|
} catch (Throwable ex) { |
|
LOGGER.error(ex.getMessage(), ex); |
|
} |
|
|
|
try { |
|
call(MethodHandles.privateLookupIn( |
|
type, MethodHandles.lookup()), method, type, proxy, args); |
|
} catch (Throwable ex) { |
|
LOGGER.error(ex.getMessage(), ex); |
|
} |
|
|
|
throw new IllegalStateException("Does not support Java " + JavaUtils.getMajorVersion()); |
|
} |
|
); |
|
} |
|
|
|
private static Object call(final MethodHandles.Lookup lookup, |
|
final Method method, |
|
final Class<?> type, |
|
final Object proxy, |
|
final Object[] args) |
|
throws Throwable { |
|
if (JavaUtils.getMajorVersion() == JavaUtils.VERSION_8) { |
|
return lookup.findSpecial( |
|
type, method.getName(), MethodType.methodType(void.class, new Class[0]), type) |
|
.bindTo(proxy) |
|
.invokeWithArguments(args); |
|
} |
|
|
|
if (JavaUtils.getMajorVersion() >= JavaUtils.VERSION_9) { |
|
return lookup.in(type) |
|
.unreflectSpecial(type.getMethod(method.getName()), type) |
|
.bindTo(proxy) |
|
.invokeWithArguments(args); |
|
} |
|
|
|
throw new IllegalStateException("Does not support Java " + JavaUtils.getMajorVersion()); |
|
} |
|
} |
|
} |