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

reconf.client.config.update.ConfigurationRepositoryUpdater Maven / Gradle / Ivy

There is a newer version: 2.1.16
Show newest version
/*
 *    Copyright 1996-2014 UOL Inc
 *
 *   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 reconf.client.config.update;

import java.lang.reflect.*;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.*;
import org.apache.commons.lang.*;
import reconf.client.check.*;
import reconf.client.elements.*;
import reconf.client.locator.*;
import reconf.client.notification.*;
import reconf.client.proxy.*;
import reconf.infra.i18n.*;
import reconf.infra.log.*;
import reconf.infra.system.*;
import reconf.infra.throwables.*;


public class ConfigurationRepositoryUpdater extends ObservableThread {

    private static final MessagesBundle msg = MessagesBundle.getBundle(ConfigurationRepositoryUpdater.class);
    private final ConfigurationRepositoryElement cfgRepository;
    private final ConfigurationRepositoryData data;
    private Map methodValue = new ConcurrentHashMap();
    private final ConfigurationRepositoryFactory factory;
    private ServiceLocator locator;
    private Collection listeners = Collections.EMPTY_LIST;

    public ConfigurationRepositoryUpdater(ConfigurationRepositoryElement elem, ServiceLocator locator, ConfigurationRepositoryFactory factory) {
        setDaemon(true);
        this.locator = locator;
        this.factory = factory;
        cfgRepository = elem;
        setName(elem.getInterfaceClass().getName() + "_updater" + new Object().toString().replace("java.lang.Object", ""));
        data = new ConfigurationRepositoryData(elem, locator);
        listeners = elem.getCustomization().getConfigurationItemListeners();

        load();
        updateLastExecution();
        factory.setUpdater(this);
    }

    public void syncNow(Class cls) {
        sync(cls);
    }

    public void run() {
        try {
            while (!Thread.currentThread().isInterrupted()) {
                getReloadTimeUnit().sleep(getReloadRate());
                updateLastExecution();
                update();
            }
        } catch (InterruptedException e) {
            LoggerHolder.getLog().warn(msg.format("interrupted.thread", getName()));
            Thread.currentThread().interrupt();

        } catch (Throwable t) {
            LoggerHolder.getLog().error(msg.format("error.reloading.all.items", getName()), t);
        }
    }

    private void load() {
        CountDownLatch latch = new CountDownLatch(data.getAll().size() + data.getAtomicReload().size());
        List toExecute = new ArrayList();

        Map remote = new ConcurrentHashMap();
        Map local = new ConcurrentHashMap();

        try {
            for (MethodConfiguration config : data.getAll()) {
                toExecute.add(locator.configurationUpdaterFactory().syncRemote(remote, config, latch));
                toExecute.add(locator.configurationUpdaterFactory().syncLocal(local, config, latch));
            }
            for (ConfigurationUpdater thread : toExecute) {
                thread.start();
            }
            waitFor(latch);

        } catch (Exception ignored) {
            LoggerHolder.getLog().error(msg.format("error.load", getName()), ignored);

        } finally {
            interruptAll(toExecute);
        }

        if (ConfigurationItemUpdateResult.countSuccess(remote.values()) < ConfigurationItemUpdateResult.countSuccess(local.values())) {
            for (Entry each : local.entrySet()) {
                if (each.getValue().isSuccess()) {
                    methodValue.put(each.getKey(), each.getValue().getObject());
                }
            }
        } else {
            for (Entry each : remote.entrySet()) {
                if (each.getValue().isSuccess()) {
                    methodValue.put(each.getKey(), each.getValue().getObject());
                }
            }
        }
        Notifier.notify(listeners, toExecute, getName());
        validateLoadResult();
    }

    private void waitFor(CountDownLatch latch) {
        try {
            LoggerHolder.getLog().debug(msg.format("waiting.load", getName()));
            latch.await();
            LoggerHolder.getLog().info(msg.format("end.load", getName()));
        } catch (InterruptedException ignored) {
            LoggerHolder.getLog().error(msg.format("error.load", getName()), ignored);
        }
    }

    private void validateLoadResult() {
        if ((methodValue.size()) != data.getAll().size()) {
            throw new ReConfInitializationError(msg.format("error.missing.item", getName()));
        }

        for (MethodConfiguration config : data.getAll()) {
            if (null == config.getMethod()) {
                throw new ReConfInitializationError(msg.format("error.internal", getName()));
            }
        }
        commitTemporaryDatabaseChanges();
    }

    private void update() {
        List toExecute = new ArrayList(data.getAtomicReload().size());
        CountDownLatch latch = new CountDownLatch(data.getAtomicReload().size());
        Map updated = new ConcurrentHashMap();

        try {
            for (MethodConfiguration config : data.getAtomicReload()) {
                ConfigurationUpdater t = locator.configurationUpdaterFactory().remote(updated, config, latch);
                toExecute.add(t);
                t.start();
            }
            waitFor(latch);
            methodValue = mergeAtomicMethodObjectWith(updated);
            Notifier.notify(listeners, toExecute, getName());

        } catch (Exception ignored) {
            LoggerHolder.getLog().error(msg.format("error.load", getName()));

        } finally {
            interruptAll(toExecute);
        }
    }

    private void interruptAll(List arg) {
        for (Thread t : arg) {
            try {
                t.interrupt();
            } catch (Exception ignored) { }
        }
    }

    private Map mergeAtomicMethodObjectWith(Map updated) {
        if (!shouldMerge(updated)) {
            return methodValue;
        }

        Map result = new ConcurrentHashMap();
        for (Entry each : methodValue.entrySet()) {
            ConfigurationItemUpdateResult updateResult = updated.get(each.getKey());
            if (updateResult == null || !updateResult.isSuccess() || updateResult.getType() != ConfigurationItemUpdateResult.Type.update) {
                result.put(each.getKey(), each.getValue());
            } else {
                result.put(each.getKey(), updateResult.getObject());
            }
        }
        commitTemporaryDatabaseChanges();
        return result;
    }

    private boolean shouldMerge(Map updated) {
        List notFound = new ArrayList();
        for (Entry each : methodValue.entrySet()) {
            if (updated.get(each.getKey()) == null) {
                notFound.add(msg.format("error.retrieving.item", getName(), each.getKey()));
            }
        }
        if (notFound.isEmpty()) {
            return true;
        }

        LoggerHolder.getLog().warn(StringUtils.join(notFound, LineSeparator.value()));
        LoggerHolder.getLog().warn(msg.format("error.retrieving.all.items", getName()));
        return false;
    }

    private void sync(Class cls) {
        LoggerHolder.getLog().info(msg.format("sync.start", getName()));
        List toExecute = new ArrayList();
        CountDownLatch latch = new CountDownLatch(data.getAll().size());
        Map updateAtomic = new ConcurrentHashMap();
        Map updateIndependent = new ConcurrentHashMap();

        try {
            for (MethodConfiguration config : data.getAll()) {
                toExecute.add(locator.configurationUpdaterFactory().syncRemote(updateAtomic, config, latch));
            }
            for (Thread thread : toExecute) {
                thread.start();
            }
            waitFor(latch);

        } catch (Exception ignored) {
            LoggerHolder.getLog().error(msg.format("sync.error", getName()), ignored);

        } finally {
            interruptAll(toExecute);
        }

        if (updateAtomic.size() + updateIndependent.size() != data.getAll().size()) {
            String error = msg.format("sync.error", getName());
            try {
                Constructor constructor = null;
                constructor = cls.getConstructor(String.class);
                constructor.setAccessible(true);
                throw cls.cast(constructor.newInstance(error));

            } catch (NoSuchMethodException ignored) {
                throw new UpdateConfigurationRepositoryException(error);
            } catch (InvocationTargetException ignored) {
                throw new UpdateConfigurationRepositoryException(error);
            } catch (InstantiationException ignored) {
                throw new UpdateConfigurationRepositoryException(error);
            } catch (IllegalAccessException ignored) {
                throw new UpdateConfigurationRepositoryException(error);
            }
        }
        finishSync(updateAtomic, updateIndependent);
        Notifier.notify(listeners, toExecute, getName());
        LoggerHolder.getLog().info(msg.format("sync.end", getName()));
    }

    private void finishSync(Map updateAtomic, Map updateIndependent) {
        Map mergedAtomic = new ConcurrentHashMap();
        for (Entry each : methodValue.entrySet()) {
            mergedAtomic.put(each.getKey(), (!updateAtomic.containsKey(each.getKey()) ? each.getValue() : updateAtomic.get(each.getKey()).getObject()));
        }

        this.methodValue = mergedAtomic;
        commitTemporaryDatabaseChanges();
    }

    private void commitTemporaryDatabaseChanges() {
        locator.databaseManagerLocator().find().commitTemporaryUpdate(cfgRepository.getFullProperties(), cfgRepository.getInterfaceClass());
    }

    public int getReloadRate() {
        return cfgRepository.getRate();
    }

    public TimeUnit getReloadTimeUnit() {
        return cfgRepository.getTimeUnit();
    }

    public Object getValueOf(Method m) {
        return methodValue.containsKey(m) ? methodValue.get(m) : null;
    }

    @Override
    public void stopIt() {
        try {
            super.interrupt();
        } catch (Exception ignored) { }
    }

    @Override
    public Object clone() {
        return new ConfigurationRepositoryUpdater(cfgRepository, locator, factory);
    }

    @Override
    public List getChildren() {
        return Collections.EMPTY_LIST;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy