Add caching to config invocation handler (#788)

This commit is contained in:
sdburns1998
2019-06-28 09:36:55 +02:00
committed by James
parent 2f36af4bf1
commit 5d47520071
2 changed files with 91 additions and 40 deletions

View File

@@ -28,6 +28,8 @@ import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
@@ -36,26 +38,49 @@ class ConfigInvocationHandler implements InvocationHandler
{
private final ConfigManager manager;
// Caches for annotation values
private static final Map<Class<?>, String> groupValueCache = new HashMap<>();
private static final Map<Method, String> methodKeyNameCache = new HashMap<>();
public ConfigInvocationHandler(ConfigManager manager)
{
this.manager = manager;
}
private static String groupValueFromProxy(Class<?> proxyClass)
{
Class<?> iface = proxyClass.getInterfaces()[0];
ConfigGroup group = iface.getAnnotation(ConfigGroup.class);
return group == null ? null : group.value();
}
private static String keyNameFromMethod(Method method)
{
ConfigItem item = method.getAnnotation(ConfigItem.class);
return item == null ? null : item.keyName();
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
Class<?> iface = proxy.getClass().getInterfaces()[0];
ConfigGroup group = iface.getAnnotation(ConfigGroup.class);
ConfigItem item = method.getAnnotation(ConfigItem.class);
if (group == null)
String itemKeyName, groupValue;
try
{
groupValue = groupValueCache.computeIfAbsent(proxy.getClass(), ConfigInvocationHandler::groupValueFromProxy);
}
catch (NullPointerException e)
{
log.warn("Configuration proxy class {} has no @ConfigGroup!", proxy.getClass());
return null;
}
if (item == null)
try
{
itemKeyName = methodKeyNameCache.computeIfAbsent(method, ConfigInvocationHandler::keyNameFromMethod);
}
catch (NullPointerException e)
{
log.warn("Configuration method {} has no @ConfigItem!", method);
return null;
@@ -64,38 +89,43 @@ class ConfigInvocationHandler implements InvocationHandler
if (args == null)
{
// Getting configuration item
String value = manager.getConfiguration(group.value(), item.keyName());
if (value == null)
return manager.getConfigObjectFromCacheOrElse(groupValue, itemKeyName, (value) ->
{
if (method.isDefault())
try
{
return callDefaultMethod(proxy, method, null);
value = manager.getConfiguration(value);
if (value == null)
{
if (method.isDefault())
{
return callDefaultMethod(proxy, method, null);
}
return null;
}
// Convert value to return type
Class<?> returnType = method.getReturnType();
try
{
return ConfigManager.stringToObject(value, returnType);
}
catch (Exception e)
{
log.warn("Unable to unmarshal {}.{} ", groupValue, itemKeyName, e);
if (method.isDefault())
{
return callDefaultMethod(proxy, method, null);
}
return null;
}
}
return null;
}
// Convert value to return type
Class<?> returnType = method.getReturnType();
try
{
return ConfigManager.stringToObject(value, returnType);
}
catch (Exception e)
{
log.warn("Unable to unmarshal {}.{} ", group.value(), item.keyName(), e);
if (method.isDefault())
catch (Throwable throwable)
{
Object defaultValue = callDefaultMethod(proxy, method, null);
manager.setConfiguration(group.value(), item.keyName(), defaultValue);
return defaultValue;
log.error("Unable to resolve configuration value {}.{}", groupValue, itemKeyName, throwable);
return null;
}
return null;
}
});
}
else
{
@@ -103,13 +133,13 @@ class ConfigInvocationHandler implements InvocationHandler
if (args.length != 1)
{
throw new RuntimeException("Invalid number of arguents to configuration method");
throw new RuntimeException("Invalid number of arguments to configuration method");
}
Object newValue = args[0];
Class<?> type = method.getParameterTypes()[0];
Object oldValue = manager.getConfiguration(group.value(), item.keyName(), type);
Object oldValue = manager.getConfiguration(groupValue, itemKeyName, type);
if (Objects.equals(oldValue, newValue))
{
@@ -124,19 +154,19 @@ class ConfigInvocationHandler implements InvocationHandler
if (Objects.equals(newValue, defaultValue))
{
// Just unset if it goes back to the default
manager.unsetConfiguration(group.value(), item.keyName());
manager.unsetConfiguration(groupValue, itemKeyName);
return null;
}
}
if (newValue == null)
{
manager.unsetConfiguration(group.value(), item.keyName());
manager.unsetConfiguration(groupValue, itemKeyName);
}
else
{
String newValueStr = ConfigManager.objectToString(newValue);
manager.setConfiguration(group.value(), item.keyName(), newValueStr);
manager.setConfiguration(groupValue, itemKeyName, newValueStr);
}
return null;
}

View File

@@ -55,6 +55,7 @@ import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -81,6 +82,7 @@ public class ConfigManager
private final ScheduledExecutorService executor;
private final ConfigInvocationHandler handler = new ConfigInvocationHandler(this);
private final Properties properties = new Properties();
private final Map<String, Object> configObjectCache = new HashMap<>();
private final Map<String, String> pendingChanges = new HashMap<>();
@Inject
@@ -220,6 +222,20 @@ public class ConfigManager
}
}
// Attempts to fetch the config value from the cache if present. Otherwise it calls the get value function and caches the result
Object getConfigObjectFromCacheOrElse(String groupName, String key, Function<String, Object> getValue)
{
String configItemKey = groupName + "." + key;
return configObjectCache.computeIfAbsent(configItemKey, getValue);
}
// Posts the configchanged event to the event bus and remove the changed key from the cache
private void postConfigChanged(ConfigChanged configChanged)
{
configObjectCache.remove(configChanged.getGroup() + "." + configChanged.getKey());
eventBus.post(configChanged);
}
public <T> T getConfig(Class<T> clazz)
{
if (!Modifier.isPublic(clazz.getModifiers()))
@@ -245,6 +261,11 @@ public class ConfigManager
return properties.getProperty(groupName + "." + key);
}
public String getConfiguration(String propertyKey)
{
return properties.getProperty(propertyKey);
}
public <T> T getConfiguration(String groupName, String key, Class<T> clazz)
{
String value = getConfiguration(groupName, key);
@@ -284,7 +305,7 @@ public class ConfigManager
configChanged.setOldValue(oldValue);
configChanged.setNewValue(value);
eventBus.post(configChanged);
postConfigChanged(configChanged);
}
public void setConfiguration(String groupName, String key, Object value)