package org.zalando.baigan.proxy.handler;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.primitives.Primitives;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.zalando.baigan.context.ContextProviderRetriever;
import org.zalando.baigan.model.Configuration;
import org.zalando.baigan.provider.ContextProvider;
import org.zalando.baigan.proxy.ProxyUtils;
import org.zalando.baigan.service.ConditionsProcessor;
import org.zalando.baigan.service.ConfigurationRepository;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import static com.google.common.base.Suppliers.memoize;
/**
* This class provides a concrete implementation for the Method invocation
* handler.
*
* @author mchand
*
*/
@Service
public class ContextAwareConfigurationMethodInvocationHandler
extends ConfigurationMethodInvocationHandler {
private final Logger LOG = LoggerFactory
.getLogger(ConfigurationMethodInvocationHandler.class);
private Supplier<ConfigurationRepository> configurationRepository;
private Supplier<ConditionsProcessor> conditionsProcessor;
private Supplier<ContextProviderRetriever> contextProviderRetriever;
@Autowired
public ContextAwareConfigurationMethodInvocationHandler(
final ObjectFactory<ConfigurationRepository> configurationRepository,
final ObjectFactory<ConditionsProcessor> conditionsProcessor,
final ObjectFactory<ContextProviderRetriever> contextProviderRetriever) {
this.configurationRepository = memoize(configurationRepository::getObject);;
this.conditionsProcessor = memoize(conditionsProcessor::getObject);;
this.contextProviderRetriever = memoize(contextProviderRetriever::getObject);;
}
@Override
protected Object handleInvocation(Object proxy, Method method,
Object[] args) throws Throwable {
final String methodName = method.getName();
final String nameSpace = method.getDeclaringClass().getSimpleName();
final String key = ProxyUtils.dottify(nameSpace) + "."
+ ProxyUtils.dottify(methodName);
final Object result = getConfig(key);
if (result == null) {
LOG.warn("Configuration not found for key: {}", key);
return null;
}
final Class<?> declaredReturnType = method.getReturnType();
try {
Constructor<?> constructor;
if (declaredReturnType.isInstance(result)) {
return result;
} else if (declaredReturnType.isPrimitive()) {
final Class<?> resultClass = result.getClass();
constructor = Primitives.wrap(declaredReturnType)
.getDeclaredConstructor(resultClass);
} else if (declaredReturnType.isEnum()) {
for (Object t : Arrays
.asList(declaredReturnType.getEnumConstants())) {
if (result.toString().equalsIgnoreCase(t.toString())) {
return t;
}
}
return result;
} else {
constructor = declaredReturnType
.getDeclaredConstructor(result.getClass());
}
return constructor.newInstance(result);
} catch (Exception exception) {
LOG.warn(
"Wrong or Incompatible configuration. Cannot find a constructor to create object of type "
+ declaredReturnType
+ " for value of the configuration key " + key,
exception);
}
return null;
}
private Object getConfig(final String key) {
final Optional<Configuration> optional = configurationRepository.get().get(key);
if (!optional.isPresent()) {
return null;
}
final Map<String, String> context = new HashMap<>();
final ContextProviderRetriever contextProviderRetriever = this.contextProviderRetriever.get();
for (final String param : contextProviderRetriever.getContextParameterKeys()) {
Collection<ContextProvider> providers = contextProviderRetriever.getProvidersFor(param);
if (CollectionUtils.isEmpty(providers)) {
continue;
}
final ContextProvider provider = providers.iterator().next();
context.put(param, provider.getContextParam(param));
}
return conditionsProcessor.get().process(optional.get(), context);
}
}