org.alfresco.repo.dictionary.DictionaryDAOImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.repo.dictionary.DictionaryDAOImpl.java

Source

/*
 * #%L
 * Alfresco Data model classes
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */
package org.alfresco.repo.dictionary;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryException;
import org.alfresco.service.cmr.dictionary.ModelDefinition;
import org.alfresco.service.cmr.dictionary.NamespaceDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

/**
 * Default implementation of the Dictionary.
 * 
 * @author David Caruana, janv, sglover
 * 
 */
// TODO deal with destroy of core dictionary registry i.e. do we remove all
// tenant dictionary registries too?
public class DictionaryDAOImpl implements DictionaryDAO, NamespaceDAO, ApplicationListener<ApplicationEvent> {
    // Tenant Service
    private TenantService tenantService;

    // used to reset the cache
    private ThreadLocal<Map<String, DictionaryRegistry>> dictionaryRegistryThreadLocal = new ThreadLocal<Map<String, DictionaryRegistry>>();

    // Internal cache (clusterable)
    private CompiledModelsCache dictionaryRegistryCache;

    // Static list of registered dictionary listeners
    private List<DictionaryListener> dictionaryListeners = new ArrayList<DictionaryListener>();
    private ReadWriteLock dictionaryListenersLock = new ReentrantReadWriteLock();

    // Logger
    private static Log logger = LogFactory.getLog(DictionaryDAO.class);

    private String defaultAnalyserResourceBundleName;

    private ClassLoader resourceClassLoader;

    // inject dependencies

    public void setTenantService(TenantService tenantService) {
        this.tenantService = tenantService;
    }

    public void setDictionaryRegistryCache(CompiledModelsCache dictionaryRegistryCache) {
        this.dictionaryRegistryCache = dictionaryRegistryCache;
    }

    @Override
    public String getDefaultAnalyserResourceBundleName() {
        return defaultAnalyserResourceBundleName;
    }

    public void setDefaultAnalyserResourceBundleName(String defaultAnalyserResourceBundleName) {
        this.defaultAnalyserResourceBundleName = defaultAnalyserResourceBundleName;
    }

    /**
     * Construct
     * 
     */
    public DictionaryDAOImpl() {
    }

    /**
     * Register listener with the Dictionary
     * <p>
     *     This method is <b>deprecated</b>, use {@link #registerListener(DictionaryListener dictionaryListener)} instead.
     * </p>
     * @param dictionaryListener
     */
    @Override
    @Deprecated
    public void register(DictionaryListener dictionaryListener) {
        registerListener(dictionaryListener);
    }

    /**
     * Register with the Dictionary
     */
    @Override
    public void registerListener(DictionaryListener dictionaryListener) {
        this.dictionaryListenersLock.writeLock().lock();
        try {
            if (!dictionaryListeners.contains(dictionaryListener)) {
                dictionaryListeners.add(dictionaryListener);
            }
        } finally {
            this.dictionaryListenersLock.writeLock().unlock();
        }
    }

    @Override
    public List<DictionaryListener> getDictionaryListeners() {
        // need to hold read lock here
        this.dictionaryListenersLock.readLock().lock();
        try {
            return new ArrayList<DictionaryListener>(dictionaryListeners);
        } finally {
            this.dictionaryListenersLock.readLock().unlock();
        }
    }

    private Map<String, DictionaryRegistry> getThreadLocal() {
        Map<String, DictionaryRegistry> map = dictionaryRegistryThreadLocal.get();
        if (map == null) {
            map = new HashMap<String, DictionaryRegistry>();
            dictionaryRegistryThreadLocal.set(map);
        }
        return map;
    }

    private DictionaryRegistry createCoreDictionaryRegistry() {
        DictionaryRegistry dictionaryRegistry = new CoreDictionaryRegistryImpl(this);
        getThreadLocal().put("", dictionaryRegistry);
        dictionaryRegistry.init();
        getThreadLocal().remove("");
        return dictionaryRegistry;
    }

    private DictionaryRegistry createTenantDictionaryRegistry(final String tenant) {
        DictionaryRegistry result = AuthenticationUtil.runAs(new RunAsWork<DictionaryRegistry>() {
            public DictionaryRegistry doWork() {
                DictionaryRegistry dictionaryRegistry = new TenantDictionaryRegistryImpl(DictionaryDAOImpl.this,
                        tenant);
                getThreadLocal().put(tenant, dictionaryRegistry);
                dictionaryRegistry.init();
                getThreadLocal().remove(tenant);
                return dictionaryRegistry;
            }
        }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenant));

        return result;
    }

    @Override
    public void init() {
        String tenant = tenantService.getCurrentUserDomain();

        dictionaryRegistryCache.forceInChangesForThisUncommittedTransaction(tenant);

        if (logger.isDebugEnabled()) {
            logger.debug("Triggered immediate reload of dictionary for tenant " + tenant);
        }
    }

    @Override
    public void destroy() {
        String tenant = tenantService.getCurrentUserDomain();

        // TODO Should be reworked when ACE-2001 will be implemented
        dictionaryRegistryCache.remove(tenant);
        dictionaryRegistryCache.refresh(tenant);

        if (logger.isDebugEnabled()) {
            logger.debug("Dictionary destroyed for tenant " + tenant);
        }
    }

    @Override
    public void reset() {
        String tenant = tenantService.getCurrentUserDomain();
        if (logger.isDebugEnabled()) {
            logger.debug("Resetting dictionary for tenant " + tenant);
        }

        destroy();
        // Ensure that we have a dictionary available right now
        getDictionaryRegistry(tenant);

        if (logger.isDebugEnabled()) {
            logger.debug("Dictionary reset complete for tenant " + tenant);
        }
    }

    @Override
    public QName putModel(M2Model model) {
        // the core registry is not yet initialised so put it in the core registry
        QName ret = putModelImpl(model, true);
        return ret;
    }

    @Override
    public QName putModelIgnoringConstraints(M2Model model) {
        return putModelImpl(model, false);
    }

    private QName putModelImpl(M2Model model, boolean enableConstraintClassLoading) {
        // Compile model definition
        CompiledModel compiledModel = model.compile(this, this, enableConstraintClassLoading);
        QName modelName = compiledModel.getModelDefinition().getName();

        getTenantDictionaryRegistry().putModel(compiledModel);

        if (logger.isTraceEnabled()) {
            logger.trace("Registered core model: " + modelName.toPrefixString(this));
            for (M2Namespace namespace : model.getNamespaces()) {
                logger.trace("Registered core namespace: '" + namespace.getUri() + "' (prefix '"
                        + namespace.getPrefix() + "')");
            }
        }

        return modelName;
    }

    /**
     * @see org.alfresco.repo.dictionary.DictionaryDAO#removeModel(org.alfresco.service.namespace.QName)
     */
    public void removeModel(QName modelName) {
        getTenantDictionaryRegistry().removeModel(modelName);
    }

    private DictionaryRegistry getTenantDictionaryRegistry() {
        String tenantDomain = tenantService.getCurrentUserDomain();
        return getDictionaryRegistry(tenantDomain);
    }

    /**
     * @param modelName
     *            the model name
     * @return the compiled model of the given name
     */
    public CompiledModel getCompiledModel(QName modelName) {
        return getTenantDictionaryRegistry().getModel(modelName);
    }

    @Override
    public DataTypeDefinition getDataType(QName typeName) {
        DataTypeDefinition dataTypeDef = null;

        if (typeName != null) {
            dataTypeDef = getTenantDictionaryRegistry().getDataType(typeName);
        }

        return dataTypeDef;
    }

    @SuppressWarnings("rawtypes")
    @Override
    public DataTypeDefinition getDataType(Class javaClass) {
        return getTenantDictionaryRegistry().getDataType(javaClass);
    }

    @Override
    public Collection<DataTypeDefinition> getDataTypes(QName modelName) {
        CompiledModel model = getCompiledModel(modelName);
        return model.getDataTypes();
    }

    @Override
    public TypeDefinition getType(QName typeName) {
        TypeDefinition typeDef = null;

        if (typeName != null) {
            typeDef = getTenantDictionaryRegistry().getType(typeName);
        }

        return typeDef;
    }

    @Override
    public Collection<QName> getSubTypes(QName superType, boolean follow) {
        // note: could be optimised further, if compiled into the model

        // Get all types (with parent type) for all models
        Map<QName, QName> allTypesAndParents = new HashMap<QName, QName>(); // name,
                                                                            // parent

        for (CompiledModel model : getCompiledModels(true).values()) {
            for (TypeDefinition type : model.getTypes()) {
                allTypesAndParents.put(type.getName(), type.getParentName());
            }
        }

        // Get sub types
        HashSet<QName> subTypes = new HashSet<QName>();
        for (QName type : allTypesAndParents.keySet()) {
            if (follow) {
                // all sub types
                QName current = type;
                while ((current != null) && !current.equals(superType)) {
                    current = allTypesAndParents.get(current); // get parent
                }
                if (current != null) {
                    subTypes.add(type);
                }
            } else {
                // immediate sub types only
                QName typesSuperType = allTypesAndParents.get(type);
                if (typesSuperType != null && typesSuperType.equals(superType)) {
                    subTypes.add(type);
                }
            }

        }
        return subTypes;
    }

    @Override
    public AspectDefinition getAspect(QName aspectName) {
        AspectDefinition aspectDef = null;

        if (aspectName != null) {
            aspectDef = getTenantDictionaryRegistry().getAspect(aspectName);
        }

        return aspectDef;
    }

    @Override
    public Collection<QName> getSubAspects(QName superAspect, boolean follow) {
        // note: could be optimised further, if compiled into the model

        // Get all aspects (with parent aspect) for all models
        Map<QName, QName> allAspectsAndParents = new HashMap<QName, QName>(); // name,
                                                                              // parent

        for (CompiledModel model : getCompiledModels(true).values()) {
            for (AspectDefinition aspect : model.getAspects()) {
                allAspectsAndParents.put(aspect.getName(), aspect.getParentName());
            }
        }

        // Get sub aspects
        HashSet<QName> subAspects = new HashSet<QName>();
        for (QName aspect : allAspectsAndParents.keySet()) {
            if (follow) {
                // all sub aspects
                QName current = aspect;
                while ((current != null) && !current.equals(superAspect)) {
                    current = allAspectsAndParents.get(current); // get parent
                }
                if (current != null) {
                    subAspects.add(aspect);
                }
            } else {
                // immediate sub aspects only
                QName typesSuperAspect = allAspectsAndParents.get(aspect);
                if (typesSuperAspect != null && typesSuperAspect.equals(superAspect)) {
                    subAspects.add(aspect);
                }
            }
        }
        return subAspects;
    }

    @Override
    public ClassDefinition getClass(QName className) {
        ClassDefinition classDef = null;

        if (className != null) {
            classDef = getTenantDictionaryRegistry().getClass(className);
        }

        return classDef;
    }

    @Override
    public PropertyDefinition getProperty(QName propertyName) {
        PropertyDefinition propertyDef = null;

        if (propertyName != null) {
            propertyDef = getTenantDictionaryRegistry().getProperty(propertyName);
        }

        return propertyDef;
    }

    @Override
    public ConstraintDefinition getConstraint(QName constraintQName) {
        ConstraintDefinition constraintDef = null;

        if (constraintQName != null) {
            constraintDef = getTenantDictionaryRegistry().getConstraint(constraintQName);
        }

        return constraintDef;
    }

    @Override
    public AssociationDefinition getAssociation(QName assocName) {
        return getTenantDictionaryRegistry().getAssociation(assocName);
    }

    public Collection<AssociationDefinition> getAssociations(QName modelName) {
        CompiledModel model = getCompiledModel(modelName);
        return model.getAssociations();
    }

    public Collection<QName> getModels(boolean includeInherited) {
        // get all models - including inherited models, if applicable
        return getCompiledModels(includeInherited).keySet();
    }

    @Override
    public Collection<QName> getModels() {
        // get all models - including inherited models, if applicable
        return getModels(true);
    }

    public Collection<QName> getTypes(boolean includeInherited) {
        return getTenantDictionaryRegistry().getTypes(includeInherited);
    }

    public Collection<QName> getAssociations(boolean includeInherited) {
        return getTenantDictionaryRegistry().getAssociations(includeInherited);
    }

    public Collection<QName> getAspects(boolean includeInherited) {
        return getTenantDictionaryRegistry().getAspects(includeInherited);
    }

    // MT-specific
    public boolean isModelInherited(QName modelName) {
        return getTenantDictionaryRegistry().isModelInherited(modelName);
    }

    private Map<QName, CompiledModel> getCompiledModels(boolean includeInherited) {
        return getTenantDictionaryRegistry().getCompiledModels(includeInherited);
    }

    @Override
    public ModelDefinition getModel(QName name) {
        CompiledModel model = getCompiledModel(name);
        return model.getModelDefinition();
    }

    @Override
    public Collection<TypeDefinition> getTypes(QName modelName) {
        CompiledModel model = getCompiledModel(modelName);
        return model.getTypes();
    }

    @Override
    public Collection<AspectDefinition> getAspects(QName modelName) {
        CompiledModel model = getCompiledModel(modelName);
        return model.getAspects();
    }

    @Override
    public TypeDefinition getAnonymousType(QName type, Collection<QName> aspects) {
        TypeDefinition typeDef = getType(type);
        if (typeDef == null) {
            throw new DictionaryException("d_dictionary.model.err.type_not_found", type);
        }
        Collection<AspectDefinition> aspectDefs = new ArrayList<AspectDefinition>();
        if (aspects != null) {
            for (QName aspect : aspects) {
                AspectDefinition aspectDef = getAspect(aspect);
                if (aspectDef == null) {
                    throw new DictionaryException("d_dictionary.model.err.aspect_not_found", aspect);
                }
                aspectDefs.add(aspectDef);
            }
        }
        return new M2AnonymousTypeDefinition(typeDef, aspectDefs);
    }

    @Override
    public Collection<PropertyDefinition> getProperties(QName modelName) {
        CompiledModel model = getCompiledModel(modelName);
        return model.getProperties();
    }

    @Override
    public Collection<PropertyDefinition> getProperties(QName modelName, QName dataType) {
        HashSet<PropertyDefinition> properties = new HashSet<PropertyDefinition>();

        Collection<PropertyDefinition> props = getProperties(modelName);
        for (PropertyDefinition prop : props) {
            if ((dataType == null) || prop.getDataType().getName().equals(dataType)) {
                properties.add(prop);
            }
        }
        return properties;
    }

    @Override
    public Collection<PropertyDefinition> getPropertiesOfDataType(QName dataType) {
        Collection<PropertyDefinition> properties = new HashSet<PropertyDefinition>();

        Collection<QName> modelNames = getModels();
        for (QName modelName : modelNames) {
            properties.addAll(getProperties(modelName, dataType));
        }

        return properties;
    }

    @Override
    public Collection<NamespaceDefinition> getNamespaces(QName modelName) {
        CompiledModel model = getCompiledModel(modelName);
        ModelDefinition modelDef = model.getModelDefinition();
        return modelDef.getNamespaces();
    }

    @Override
    public Collection<ConstraintDefinition> getConstraints(QName modelName) {
        return getConstraints(modelName, false);
    }

    public Collection<ConstraintDefinition> getConstraints(QName modelName, boolean referenceableDefsOnly) {
        CompiledModel model = getCompiledModel(modelName);
        if (referenceableDefsOnly) {
            return getReferenceableConstraintDefs(model);
        } else {
            return model.getConstraints();
        }
    }

    private Collection<ConstraintDefinition> getReferenceableConstraintDefs(CompiledModel model) {
        Collection<ConstraintDefinition> conDefs = model.getConstraints();
        Collection<PropertyDefinition> propDefs = model.getProperties();
        for (PropertyDefinition propDef : propDefs) {
            for (ConstraintDefinition conDef : propDef.getConstraints()) {
                conDefs.remove(conDef);
            }
        }

        return conDefs;
    }

    // re-entrant (eg. via reset)
    @Override
    public DictionaryRegistry getDictionaryRegistry(String tenantDomain) {
        DictionaryRegistry dictionaryRegistry = null;

        if (tenantDomain == null) {
            throw new AlfrescoRuntimeException("Tenant must be set");
        }

        // check threadlocal first - return if set
        dictionaryRegistry = getThreadLocal().get(tenantDomain);
        if (dictionaryRegistry == null) {
            dictionaryRegistry = dictionaryRegistryCache.get(tenantDomain);
        }

        return dictionaryRegistry;
    }

    /**
     * For cache use only.
     * 
     * @param tenantDomain String
     * @return constructed DictionaryRegistry
     */
    public DictionaryRegistry initDictionaryRegistry(final String tenantDomain) {
        return AuthenticationUtil.runAs(new RunAsWork<DictionaryRegistry>() {
            public DictionaryRegistry doWork() {
                DictionaryRegistry dictionaryRegistry = null;
                if (tenantDomain.equals(TenantService.DEFAULT_DOMAIN)) {
                    dictionaryRegistry = createCoreDictionaryRegistry();
                } else {
                    dictionaryRegistry = createTenantDictionaryRegistry(tenantDomain);
                }

                getThreadLocal().put(tenantDomain, dictionaryRegistry);
                dictionaryRegistry.init();
                getThreadLocal().remove(tenantDomain);

                return dictionaryRegistry;
            }
        }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain));
    }

    /**
     * Return diffs between input model and model in the Dictionary.
     * 
     * If the input model does not exist in the Dictionary then no diffs will be
     * returned.
     * 
     * @param model M2Model
     * @return model diffs (if any)
     */
    public List<M2ModelDiff> diffModel(M2Model model) {
        return diffModel(model, true);
    }

    public List<M2ModelDiff> diffModelIgnoringConstraints(M2Model model) {
        return diffModel(model, false);
    }

    /**
     * Return diffs between input model and model in the Dictionary.
     * 
     * If the input model does not exist in the Dictionary then no diffs will be
     * returned.
     * 
     * @param model M2Model
     * @param enableConstraintClassLoading boolean
     * @return model diffs (if any)
     */
    public List<M2ModelDiff> diffModel(M2Model model, boolean enableConstraintClassLoading) {
        // Compile model definition
        CompiledModel compiledModel = model.compile(this, this, enableConstraintClassLoading);
        QName modelName = compiledModel.getModelDefinition().getName();

        CompiledModel previousVersion = null;
        try {
            previousVersion = getCompiledModel(modelName);
        } catch (DictionaryException e) {
            // ignore missing model, there's no need to warn about this.
            logger.debug(e);
        }

        if (previousVersion == null) {
            return new ArrayList<M2ModelDiff>(0);
        } else {
            return diffModel(previousVersion, compiledModel);
        }
    }

    /**
     * Return diffs between two compiled models.
     * 
     * note: - checks classes (types & aspects) for incremental updates - checks
     * properties for incremental updates, but does not include the diffs -
     * checks assocs & child assocs for incremental updates, but does not
     * include the diffs - incremental updates include changes in
     * title/description, property default value, etc - ignores changes in model
     * definition except name (ie. title, description, author, published date,
     * version are treated as an incremental update)
     * 
     * TODO - imports - namespace - datatypes - constraints (including property
     * constraints - references and inline)
     * 
     * @param previousVersion CompiledModel
     * @param model CompiledModel
     * @return model diffs (if any)
     */
    /* package */List<M2ModelDiff> diffModel(CompiledModel previousVersion, CompiledModel model) {
        List<M2ModelDiff> M2ModelDiffs = new ArrayList<M2ModelDiff>();

        if (previousVersion != null) {
            Collection<TypeDefinition> previousTypes = previousVersion.getTypes();
            Collection<AspectDefinition> previousAspects = previousVersion.getAspects();
            Collection<ConstraintDefinition> previousConDefs = getReferenceableConstraintDefs(previousVersion);
            Collection<NamespaceDefinition> previousImportedNamespaces = previousVersion.getModelDefinition()
                    .getImportedNamespaces();

            if (model == null) {
                // delete model
                for (TypeDefinition previousType : previousTypes) {
                    M2ModelDiffs.add(new M2ModelDiff(previousType.getName(), M2ModelDiff.TYPE_TYPE,
                            M2ModelDiff.DIFF_DELETED));
                }
                for (AspectDefinition previousAspect : previousAspects) {
                    M2ModelDiffs.add(new M2ModelDiff(previousAspect.getName(), M2ModelDiff.TYPE_ASPECT,
                            M2ModelDiff.DIFF_DELETED));
                }
                for (ConstraintDefinition previousConDef : previousConDefs) {
                    M2ModelDiffs.add(new M2ModelDiff(previousConDef.getName(), M2ModelDiff.TYPE_CONSTRAINT,
                            M2ModelDiff.DIFF_DELETED));
                }
            } else {
                // update model
                Collection<TypeDefinition> types = model.getTypes();
                Collection<AspectDefinition> aspects = model.getAspects();
                Collection<ConstraintDefinition> conDefs = getReferenceableConstraintDefs(model);
                Collection<NamespaceDefinition> importedNamespaces = model.getModelDefinition()
                        .getImportedNamespaces();

                if (previousTypes.size() != 0) {
                    M2ModelDiffs
                            .addAll(M2ClassDefinition.diffClassLists(new ArrayList<ClassDefinition>(previousTypes),
                                    new ArrayList<ClassDefinition>(types), M2ModelDiff.TYPE_TYPE));
                } else {
                    for (TypeDefinition type : types) {
                        M2ModelDiffs.add(
                                new M2ModelDiff(type.getName(), M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_CREATED));
                    }
                }

                if (previousAspects.size() != 0) {
                    M2ModelDiffs.addAll(
                            M2ClassDefinition.diffClassLists(new ArrayList<ClassDefinition>(previousAspects),
                                    new ArrayList<ClassDefinition>(aspects), M2ModelDiff.TYPE_ASPECT));
                } else {
                    for (AspectDefinition aspect : aspects) {
                        M2ModelDiffs.add(new M2ModelDiff(aspect.getName(), M2ModelDiff.TYPE_ASPECT,
                                M2ModelDiff.DIFF_CREATED));
                    }
                }

                if (previousConDefs.size() != 0) {
                    M2ModelDiffs.addAll(M2ConstraintDefinition.diffConstraintLists(
                            new ArrayList<ConstraintDefinition>(previousConDefs),
                            new ArrayList<ConstraintDefinition>(conDefs)));
                } else {
                    for (ConstraintDefinition conDef : conDefs) {
                        M2ModelDiffs.add(new M2ModelDiff(conDef.getName(), M2ModelDiff.TYPE_CONSTRAINT,
                                M2ModelDiff.DIFF_CREATED));
                    }
                }

                if (previousImportedNamespaces.size() != 0) {
                    M2ModelDiffs.addAll(M2NamespaceDefinition.diffNamespaceDefinitionLists(
                            new ArrayList<NamespaceDefinition>(previousImportedNamespaces),
                            new ArrayList<NamespaceDefinition>(importedNamespaces)));
                } else {
                    for (NamespaceDefinition namespaceDefinition : importedNamespaces) {
                        M2ModelDiffs.add(new M2ModelDiff(namespaceDefinition.getModel().getName(),
                                namespaceDefinition, M2ModelDiff.TYPE_NAMESPACE, M2ModelDiff.DIFF_CREATED));
                    }
                }
            }
        } else {
            if (model != null) {
                // new model
                Collection<TypeDefinition> types = model.getTypes();
                Collection<AspectDefinition> aspects = model.getAspects();

                for (TypeDefinition type : types) {
                    M2ModelDiffs
                            .add(new M2ModelDiff(type.getName(), M2ModelDiff.TYPE_TYPE, M2ModelDiff.DIFF_CREATED));
                }

                for (AspectDefinition aspect : aspects) {
                    M2ModelDiffs.add(
                            new M2ModelDiff(aspect.getName(), M2ModelDiff.TYPE_ASPECT, M2ModelDiff.DIFF_CREATED));
                }
            } else {
                // nothing to diff
            }
        }

        return M2ModelDiffs;
    }

    @Override
    public ClassLoader getResourceClassLoader() {
        return resourceClassLoader;
    }

    @Override
    public void setResourceClassLoader(ClassLoader resourceClassLoader) {
        this.resourceClassLoader = resourceClassLoader;
    }

    @Override
    public String getNamespaceURI(String prefix) {
        return getTenantDictionaryRegistry().getNamespaceURI(prefix);
    }

    @Override
    public Collection<String> getPrefixes(String URI) {
        return getTenantDictionaryRegistry().getPrefixes(URI);
    }

    @Override
    public void addURI(String uri) {
        getTenantDictionaryRegistry().addURI(uri);
    }

    @Override
    public Collection<String> getPrefixes() {
        return Collections.unmodifiableCollection(getTenantDictionaryRegistry().getPrefixesCache().keySet());
    }

    @Override
    public Collection<String> getURIs() {
        return Collections.unmodifiableCollection(getTenantDictionaryRegistry().getUrisCache());
    }

    @Override
    public void removeURI(String uri) {
        getTenantDictionaryRegistry().removeURI(uri);
    }

    @Override
    public void addPrefix(String prefix, String uri) {
        getTenantDictionaryRegistry().addPrefix(prefix, uri);
    }

    @Override
    public void removePrefix(String prefix) {
        getTenantDictionaryRegistry().removePrefix(prefix);
    }

    private AtomicBoolean contextRefreshed = new AtomicBoolean(false);

    @Override
    public boolean isContextRefreshed() {
        return contextRefreshed.get();
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            contextRefreshed.set(true);
        }
    }
}