org.blocks4j.reconf.client.proxy.ConfigurationRepositoryFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.blocks4j.reconf.client.proxy.ConfigurationRepositoryFactory.java

Source

/*
 *   Copyright 2013-2015 Blocks4J Team (www.blocks4j.org)
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package org.blocks4j.reconf.client.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.collections4.CollectionUtils;
import org.blocks4j.reconf.client.annotations.ConfigurationItem;
import org.blocks4j.reconf.client.annotations.UpdateConfigurationRepository;
import org.blocks4j.reconf.client.config.update.ConfigurationRepositoryUpdater;
import org.blocks4j.reconf.client.elements.ConfigurationItemElement;
import org.blocks4j.reconf.client.elements.ConfigurationRepositoryElement;
import org.blocks4j.reconf.client.factory.ConfigurationRepositoryElementFactory;
import org.blocks4j.reconf.client.locator.ServiceLocator;
import org.blocks4j.reconf.client.notification.ConfigurationItemListener;
import org.blocks4j.reconf.client.setup.Environment;
import org.blocks4j.reconf.infra.i18n.MessagesBundle;
import org.blocks4j.reconf.infra.log.LoggerHolder;
import org.blocks4j.reconf.infra.system.LineSeparator;

public class ConfigurationRepositoryFactory implements InvocationHandler {

    private static final MessagesBundle msg = MessagesBundle.getBundle(ConfigurationRepositoryFactory.class);
    private ConfigurationRepositoryUpdater updater;
    private static ConfigurationRepositoryElementFactory factory;
    private static final ReentrantLock lock = new ReentrantLock();
    private static ConcurrentMap<String, Object> cache = new ConcurrentHashMap<String, Object>();
    private static ConcurrentMap<String, Collection<? extends ConfigurationItemListener>> listenerCache = new ConcurrentHashMap<String, Collection<? extends ConfigurationItemListener>>();

    public static synchronized <T> T get(Class<T> arg) {
        return get(arg, null, null);
    }

    public static synchronized <T> T get(Class<T> arg, Customization customization) {
        return get(arg, customization, null);
    }

    public static synchronized <T> T get(Class<T> arg,
            Collection<? extends ConfigurationItemListener> configurationItemListeners) {
        return get(arg, null, configurationItemListeners);
    }

    public static synchronized <T> T get(Class<T> arg, Customization customization,
            Collection<? extends ConfigurationItemListener> configurationItemListeners) {
        setUpIfNeeded();

        if (customization == null) {
            customization = new Customization();
        }
        if (configurationItemListeners == null) {
            configurationItemListeners = Collections.EMPTY_LIST;
        }

        String key = arg.getName() + customization;
        if (cache.containsKey(key)) {
            if (CollectionUtils.isEqualCollection(configurationItemListeners, listenerCache.get(key))) {
                LoggerHolder.getLog().info(msg.format("cached.instance", arg.getName()));
                return (T) cache.get(key);
            }

            throw new IllegalArgumentException(msg.format("error.customization", arg.getName()));
        }

        ConfigurationRepositoryElement repo = Environment.getFactory().create(arg);
        repo.setCustomization(customization);
        repo.setComponent(customization.getCustomComponent(repo.getComponent()));
        repo.setProduct(customization.getCustomProduct(repo.getProduct()));
        if (configurationItemListeners != null) {
            for (ConfigurationItemListener listener : configurationItemListeners) {
                repo.addConfigurationItemListener(listener);
            }
        }

        for (ConfigurationItemElement item : repo.getConfigurationItems()) {
            item.setProduct(repo.getProduct());
            item.setComponent(customization.getCustomComponent(item.getComponent()));
            item.setValue(customization.getCustomItem(item.getValue()));
        }

        LoggerHolder.getLog().info(msg.format("new.instance", LineSeparator.value(), repo.toString()));

        Object result = newInstance(arg, repo);
        cache.put(key, result);
        listenerCache.put(key, ((configurationItemListeners != null) ? configurationItemListeners
                : CollectionUtils.EMPTY_COLLECTION));
        return (T) result;
    }

    private static synchronized void setUpIfNeeded() {
        if (factory != null) {
            return;
        }
        boolean locked = lock.tryLock();
        if (!locked) {
            return;
        }
        try {
            Environment.setUp();
            factory = Environment.getFactory();
        } finally {
            lock.unlock();
        }
    }

    private static synchronized <T> T newInstance(Class<T> arg, ConfigurationRepositoryElement repo) {
        ConfigurationRepositoryFactory factory = new ConfigurationRepositoryFactory();
        ConfigurationRepositoryUpdater thread = new ConfigurationRepositoryUpdater(repo,
                ServiceLocator.defaultImplementation, factory);
        Environment.addThreadToCheck(thread);
        thread.start();
        return (T) Proxy.newProxyInstance(arg.getClassLoader(), new Class<?>[] { arg }, factory);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        boolean updateAnnotationPresent = method.isAnnotationPresent(UpdateConfigurationRepository.class);
        boolean configurationAnnotationPresent = method.isAnnotationPresent(ConfigurationItem.class);

        if (!configurationAnnotationPresent && !updateAnnotationPresent) {
            return method.invoke(this, args);
        }

        if (updateAnnotationPresent) {
            updater.syncNow(method.getAnnotation(UpdateConfigurationRepository.class).onErrorThrow());
        }

        Object configValue = null;

        if (configurationAnnotationPresent) {
            configValue = updater.getValueOf(method);
        }

        return configValue;
    }

    public void setUpdater(ConfigurationRepositoryUpdater updater) {
        this.updater = updater;
    }
}