All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zalando.baigan.proxy.handler.ContextAwareConfigurationMethodInvocationHandler Maven / Gradle / Ivy

package org.zalando.baigan.proxy.handler;

import com.google.common.base.Supplier;
import com.google.common.primitives.Primitives;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
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.Preconditions.checkState;
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 implements BeanFactoryAware {

    private final Logger LOG = LoggerFactory
            .getLogger(ConfigurationMethodInvocationHandler.class);

    private Supplier configurationRepository;

    private Supplier conditionsProcessor;

    private Supplier contextProviderRetriever;

    /**
     * We have to defer dependency injection and bean resolution as this bean is required by the
     * {@link org.zalando.baigan.proxy.ConfigurationServiceBeanFactory}, which is loaded very
     * early by {@link org.springframework.beans.factory.config.BeanPostProcessor}s.
     */
    @Override
    public void setBeanFactory(final BeanFactory beanFactory) throws BeansException {
        this.configurationRepository = memoize(() -> beanFactory.getBean(ConfigurationRepository.class));
        this.conditionsProcessor = memoize(() -> beanFactory.getBean(ConditionsProcessor.class));
        this.contextProviderRetriever = memoize(() -> beanFactory.getBean(ContextProviderRetriever.class));
    }

    @Override
    protected Object handleInvocation(Object proxy, Method method,
            Object[] args) throws Throwable {
        final String methodName = method.getName();
        final String nameSpace = getNamespace(proxy);

        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;
                    }
                }
                LOG.warn("Unable to map [{}] to enum type [{}].", result, declaredReturnType.getName());
                return null;
            } 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 String getNamespace(final Object proxy) {
        final Class[] interfaces = proxy.getClass().getInterfaces();
        checkState(interfaces.length == 1, "Expected exactly one interface on proxy object.");
        return interfaces[0].getSimpleName();
    }

    private Object getConfig(final String key) {

        final Optional optional = configurationRepository.get().get(key);
        if (!optional.isPresent()) {
            return null;
        }

        final Map context = new HashMap<>();

        final ContextProviderRetriever contextProviderRetriever = this.contextProviderRetriever.get();
        for (final String param : contextProviderRetriever.getContextParameterKeys()) {
            Collection 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);

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy