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

org.deephacks.tools4j.config.internal.core.runtime.DefaultBeanManager Maven / Gradle / Ivy

There is a newer version: 0.15.0
Show newest version
package org.deephacks.tools4j.config.internal.core.runtime;

import com.google.common.base.Optional;
import org.deephacks.tools4j.config.admin.query.BeanQuery;
import org.deephacks.tools4j.config.model.AbortRuntimeException;
import org.deephacks.tools4j.config.model.Bean;
import org.deephacks.tools4j.config.model.Bean.BeanId;
import org.deephacks.tools4j.config.model.Events;
import org.deephacks.tools4j.config.model.Schema;
import org.deephacks.tools4j.config.spi.BeanManager;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.deephacks.tools4j.config.model.Events.*;

/**
 * In memory BeanManager.
 */
public class DefaultBeanManager extends BeanManager {
    private static final HashMap beans = new HashMap<>();

    @Override
    public Optional getEager(BeanId id) {
        Map all = readValuesAsMap();
        return getEagerly(id, all);
    }

    private Optional getEagerly(BeanId id, Map all) {
        Bean result = all.get(id);
        if (result == null) {
            return Optional.absent();
        }
        // bean found, initalize references.
        for (BeanId ref : result.getReferences()) {
            if (ref.getBean() != null) {
                continue;
            }
            Bean refBean = all.get(ref);
            if (refBean == null) {
                throw CFG301_MISSING_RUNTIME_REF(result.getId(), ref);
            }
            ref.setBean(refBean);
            getEagerly(ref, all);
        }
        return Optional.of(result);
    }

    @Override
    public Optional getLazy(BeanId id) throws AbortRuntimeException {
        Map all = readValuesAsMap();
        Bean bean = all.get(id);
        if (bean == null) {
            return Optional.absent();
        }
        for (BeanId ref : bean.getReferences()) {
            Bean refBean = all.get(ref);
            if (refBean == null) {
                throw CFG301_MISSING_RUNTIME_REF(ref);
            }
            ref.setBean(refBean);
        }
        return Optional.of(bean);
    }

    /**
     * The direct, but no further, successors that references this bean will also be
     * fetched and initalized with their direct, but no further, predecessors.
     */
    @Override
    public Map getBeanToValidate(Collection beans) throws AbortRuntimeException {
        Map beansToValidate = new HashMap<>();
        for (Bean bean : beans) {
            Map predecessors = new HashMap<>();
            // beans read from xml storage will only have their basic properties initalized...
            Map all = readValuesAsMap();
            // ... but we also need set the direct references/predecessors for beans to validate
            Map beansToValidateSubset = getDirectSuccessors(bean, all);
            beansToValidateSubset.put(bean.getId(), bean);
            for (Bean toValidate : beansToValidateSubset.values()) {
                predecessors.putAll(getDirectPredecessors(toValidate, all));
            }

            for (Bean predecessor : predecessors.values()) {
                for (BeanId ref : predecessor.getReferences()) {
                    Bean b = all.get(ref);
                    if (b == null) {
                        throw CFG301_MISSING_RUNTIME_REF(predecessor.getId());
                    }
                    ref.setBean(b);
                }
            }
            for (Bean toValidate : beansToValidateSubset.values()) {
                // list references of beansToValidate should now
                // be available in predecessors.
                for (BeanId ref : toValidate.getReferences()) {
                    Bean predecessor = predecessors.get(ref);
                    if (predecessor == null) {
                        throw new IllegalStateException("Bug in algorithm. Reference [" + ref
                                + "] of [" + toValidate.getId()
                                + "] should be available in predecessors.");
                    }
                    ref.setBean(predecessor);
                }
            }
            beansToValidate.putAll(predecessors);
        }
        return beansToValidate;
    }

    private Map getDirectPredecessors(Bean bean, Map all) {
        Map predecessors = new HashMap<>();
        for (BeanId ref : bean.getReferences()) {
            Bean predecessor = all.get(ref);
            if (predecessor == null) {
                throw CFG304_BEAN_DOESNT_EXIST(ref);
            }
            predecessors.put(predecessor.getId(), predecessor);
        }
        return predecessors;
    }

    private Map getDirectSuccessors(Bean bean, Map all) {
        Map successors = new HashMap<>();
        for (Bean b : all.values()) {
            List refs = b.getReferences();
            if (refs.contains(bean.getId())) {
                successors.put(b.getId(), b);
            }
        }
        return successors;
    }

    @Override
    public Optional getSingleton(String schemaName) throws IllegalArgumentException {
        Map all = readValuesAsMap();
        for (Bean bean : all.values()) {
            if (bean.getId().getSchemaName().equals(schemaName)) {
                if (!bean.getId().isSingleton()) {
                    throw new IllegalArgumentException("Schema [" + schemaName
                            + "] is not a get.");
                }
                BeanId singletonId = bean.getId();
                return getEagerly(singletonId, all);
            }
        }
        return Optional.of(Bean.create(BeanId.createSingleton(schemaName)));
    }

    @Override
    public Map list(String name) {
        Map all = readValuesAsMap();
        Map result = new HashMap<>();
        for (Bean b : all.values()) {
            if (b.getId().getSchemaName().equals(name)) {
                Optional bean = getEagerly(b.getId(), all);
                result.put(bean.get().getId(), bean.get());
            }
        }
        return result;
    }

    @Override
    public Map list(String schemaName, Collection ids)
            throws AbortRuntimeException {
        Map result = new HashMap<>();
        for (Bean bean : beans.values()) {
            String schema = bean.getId().getSchemaName();
            if (!schema.equals(schemaName)) {
                continue;
            }
            for (String id : ids) {
                if (bean.getId().getInstanceId().equals(id)) {
                    result.put(bean.getId(), bean);
                }
            }
        }
        return result;
    }

    @Override
    public void create(Bean bean) {
        Map values = readValuesAsMap();
        checkReferencesExist(bean, values);
        if (!bean.isDefault()) {
            checkUniquness(bean, values);
            values.put(bean.getId(), bean);
        } else {
            Bean stored = values.get(bean.getId());
            if (stored == null) {
                values.put(bean.getId(), bean);
            }
        }
        writeValues(values);
    }

    @Override
    public void create(Collection set) {
        Map beans = readValuesAsMap();
        // first check uniquness towards storage
        for (Bean bean : set) {
            checkUniquness(bean, beans);
        }
        // TODO: check that provided beans are unique among themselves.

        // references may not exist in storage, but are provided
        // as part of the transactions, so add them before validating references.
        for (Bean bean : set) {
            beans.put(bean.getId(), bean);
        }
        for (Bean bean : set) {
            checkReferencesExist(bean, beans);
        }
        writeValues(beans);

    }

    @Override
    public void createSingleton(BeanId singleton) {
        Map values = readValuesAsMap();
        Bean bean = Bean.create(singleton);
        try {
            checkUniquness(bean, values);
        } catch (AbortRuntimeException e) {
            // ignore and return silently.
            return;
        }
        values.put(singleton, bean);
        writeValues(values);
    }

    @Override
    public void set(Bean bean) {
        Map values = readValuesAsMap();
        Bean existing = values.get(bean.getId());
        if (existing == null) {
            throw CFG304_BEAN_DOESNT_EXIST(bean.getId());

        }
        checkReferencesExist(bean, values);
        checkInstanceExist(bean, values);
        values.put(bean.getId(), bean);
        writeValues(values);
    }

    @Override
    public void set(Collection set) {
        Map beans = readValuesAsMap();
        // TODO: check that provided beans are unique among themselves.

        // references may not exist in storage, but are provided
        // as part of the transactions, so add them before validating references.
        for (Bean bean : set) {
            Bean existing = beans.get(bean.getId());
            if (existing == null) {
                throw CFG304_BEAN_DOESNT_EXIST(bean.getId());
            }
            beans.put(bean.getId(), bean);
        }
        for (Bean bean : set) {
            checkReferencesExist(bean, beans);
        }

        writeValues(beans);
    }

    @Override
    public void merge(Bean bean) {
        Map beans = readValuesAsMap();
        Bean b = beans.get(bean.getId());
        if (b == null) {
            throw CFG304_BEAN_DOESNT_EXIST(bean.getId());
        }
        replace(b, bean, beans);
        writeValues(beans);
    }

    @Override
    public void merge(Collection bean) {
        Map beans = readValuesAsMap();
        for (Bean replace : bean) {
            Bean target = beans.get(replace.getId());
            if (target == null) {
                throw Events.CFG304_BEAN_DOESNT_EXIST(replace.getId());
            }
            replace(target, replace, beans);
        }
        writeValues(beans);
    }

    private void replace(Bean target, Bean replace, Map all) {
        if (target == null) {
            // bean did not exist in storage, create it.
            target = replace;
        }
        checkReferencesExist(replace, all);
        for (String name : replace.getPropertyNames()) {
            List values = replace.getValues(name);
            if (values == null || values.size() == 0) {
                // null/empty indicates a remove/reset-to-default op
                target.remove(name);
            } else {
                target.setProperty(name, replace.getValues(name));
            }

        }

        for (String name : replace.getReferenceNames()) {
            List values = replace.getReference(name);
            if (values == null || values.size() == 0) {
                // null/empty indicates a remove/reset-to-default op
                target.remove(name);
            } else {
                target.setReferences(name, values);
            }

        }
    }

    @Override
    public void delete(BeanId id) {
        Map beans = readValuesAsMap();
        checkNoReferencesExist(id, beans);
        checkDeleteDefault(beans.get(id));
        beans.remove(id);
        writeValues(beans);
    }

    @Override
    public void delete(String schemaName, Collection instanceIds) {
        Map beans = readValuesAsMap();
        for (String instance : instanceIds) {
            checkDeleteDefault(beans.get(BeanId.create(instance, schemaName)));
            checkNoReferencesExist(BeanId.create(instance, schemaName), beans);
            beans.remove(BeanId.create(instance, schemaName));
        }
        writeValues(beans);
    }

    @Override
    public BeanQuery newQuery(Schema schema) {
        throw new UnsupportedOperationException("");
    }

    private static void checkNoReferencesExist(BeanId deleted, Map storage) {
        Collection hasReferences = new ArrayList<>();
        for (Bean b : storage.values()) {
            if (hasReferences(b, deleted)) {
                hasReferences.add(b.getId());
            }
        }
        if (hasReferences.size() > 0) {
            throw CFG302_CANNOT_DELETE_BEAN(Arrays.asList(deleted));
        }
    }

    private static void checkReferencesExist(final Bean bean, final Map storage) {

        ArrayList allRefs = new ArrayList<>();
        for (String name : bean.getReferenceNames()) {
            if (bean.getReference(name) == null) {
                // the reference is about to be removed.
                continue;
            }
            for (BeanId beanId : bean.getReference(name)) {
                allRefs.add(beanId);
            }
        }

        Collection missingReferences = new ArrayList<>();

        for (BeanId beanId : allRefs) {
            if (beanId.getInstanceId() == null) {
                continue;
            }
            Bean b = storage.get(beanId);
            if (b == null) {
                missingReferences.add(beanId);
            }
        }
        if (missingReferences.size() > 0) {
            throw CFG301_MISSING_RUNTIME_REF(bean.getId(), missingReferences);
        }
    }

    private static void checkInstanceExist(Bean bean, Map storage) {
        Collection beans = storage.values();
        for (Bean existingBean : beans) {
            if (existingBean.getId().equals(bean.getId())) {
                return;
            }
        }
        throw CFG304_BEAN_DOESNT_EXIST(bean.getId());

    }

    private static void checkUniquness(Bean bean, Map storage) {
        Collection beans = storage.values();

        for (Bean existing : beans) {
            if (bean.getId().equals(existing.getId())) {
                throw CFG303_BEAN_ALREADY_EXIST(bean.getId());
            }
        }
    }

    private static void checkDeleteDefault(Bean bean) {
        if (bean == null) {
            return;
        }
        if (bean.isDefault()) {
            throw CFG311_DEFAULT_REMOVAL(bean.getId());
        }
    }

    /**
     * Returns the a list of property names of the target bean that have
     * references to the bean id.
     */
    private static boolean hasReferences(Bean target, BeanId reference) {
        for (String name : target.getReferenceNames()) {
            for (BeanId ref : target.getReference(name)) {
                if (ref.equals(reference)) {
                    return true;
                }
            }
        }
        return false;
    }
    private Map readValuesAsMap() {
        Map map = new HashMap<>();
        for (Bean bean : beans.values()) {
            map.put(bean.getId(), bean);
        }
        return map;

    }

    private void writeValues(Map map) {
        writeValues(new ArrayList<>(map.values()));
    }

    private void writeValues(List beans) {
        for (Bean bean : beans) {
            this.beans.put(bean.getId(), bean);
        }
    }

    public static void clear() {
        beans.clear();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy