/* * #%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 . * #L% */ package org.alfresco.opencmis.dictionary; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.alfresco.opencmis.dictionary.CMISAbstractDictionaryService.DictionaryInitializer; import org.alfresco.opencmis.mapping.CMISMapping; import org.alfresco.repo.dictionary.CompiledModel; import org.alfresco.repo.tenant.TenantService; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.ClassDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.namespace.QName; import org.apache.chemistry.opencmis.commons.data.CmisExtensionElement; import org.apache.chemistry.opencmis.commons.impl.dataobjects.CmisExtensionElementImpl; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * CMIS Dictionary registry * * Index of CMIS Type Definitions * * @author sglover */ public class CMISDictionaryRegistryImpl implements CMISDictionaryRegistry { public static final String ALFRESCO_EXTENSION_NAMESPACE = "http://www.alfresco.org"; public static final String MANDATORY_ASPECTS = "mandatoryAspects"; public static final String MANDATORY_ASPECT = "mandatoryAspect"; // Logger protected static final Log logger = LogFactory.getLog(CMISDictionaryRegistryImpl.class); private CMISMapping cmisMapping; private DictionaryService dictionaryService; private String tenant; protected CMISAbstractDictionaryService cmisDictionaryService; private String parentTenant; private DictionaryInitializer dictionaryInitializer; // Type Definitions Index private Map typeDefsByQName = new HashMap(); private Map assocDefsByQName = new HashMap(); private Map typeDefsByTypeId = new HashMap(); private Map typeDefsByQueryName = new HashMap(); private List baseTypes = new ArrayList(); private Map propDefbyPropId = new HashMap(); private Map propDefbyQueryName = new HashMap(); private Map> children = new HashMap>(); public CMISDictionaryRegistryImpl(CMISAbstractDictionaryService cmisDictionaryService, CMISMapping cmisMapping, DictionaryService dictionaryService, DictionaryInitializer dictionaryInitializer) { this(cmisDictionaryService, "", null, cmisMapping, dictionaryService, dictionaryInitializer); } /* * Testing only. */ CMISDictionaryRegistryImpl() { } public CMISDictionaryRegistryImpl(CMISAbstractDictionaryService cmisDictionaryService, String tenant, String parentTenant, CMISMapping cmisMapping, DictionaryService dictionaryService, DictionaryInitializer dictionaryInitializer) { this.cmisDictionaryService = cmisDictionaryService; this.tenant = tenant; this.parentTenant = parentTenant; this.cmisMapping = cmisMapping; this.dictionaryService = dictionaryService; this.dictionaryInitializer = dictionaryInitializer; } protected CMISDictionaryRegistry getParent() { CMISDictionaryRegistry registry = null; if(parentTenant != null) { return cmisDictionaryService.getRegistry(parentTenant); } return registry; } @Override public String getTenant() { return tenant; } private List getChildrenImpl(String typeId) { return children.get(typeId); } @Override public List getChildren(String typeId) { List ret = new LinkedList<>(); List children = getChildrenImpl(typeId); if(children != null) { ret.addAll(children); } if(getParent() != null) { children = getParent().getChildren(typeId); if(children != null) { ret.addAll(children); } } return ret; } @Override public void setChildren(String typeId, List children) { this.children.put(typeId, children); } @Override public void addChild(String typeId, TypeDefinitionWrapper child) { List children = this.children.get(typeId); if(children == null) { children = new LinkedList(); this.children.put(typeId, children); } children.add(child); } @Override public TypeDefinitionWrapper getTypeDefByTypeId(String typeId) { return getTypeDefByTypeId(typeId, true); } @Override public TypeDefinitionWrapper getTypeDefByTypeId(String typeId, boolean includeParent) { TypeDefinitionWrapper typeDef = typeDefsByTypeId.get(typeId); if(typeDef == null && includeParent && getParent() != null) { typeDef = getParent().getTypeDefByTypeId(typeId); } return typeDef; } @Override public TypeDefinitionWrapper getAssocDefByQName(QName qname) { TypeDefinitionWrapper typeDef = assocDefsByQName.get(qname); if(typeDef == null && getParent() != null) { typeDef = getParent().getAssocDefByQName(qname); } return typeDef; } @Override public TypeDefinitionWrapper getTypeDefByQueryName(Object queryName) { TypeDefinitionWrapper typeDef = typeDefsByQueryName.get(queryName); if(typeDef == null && getParent() != null) { typeDef = getParent().getTypeDefByQueryName(queryName); } return typeDef; } @Override public TypeDefinitionWrapper getTypeDefByQName(QName qname) { TypeDefinitionWrapper typeDef = typeDefsByQName.get(qname); if(typeDef == null && getParent() != null) { typeDef = getParent().getTypeDefByQName(qname); } return typeDef; } @Override public PropertyDefinitionWrapper getPropDefByPropId(String propId) { PropertyDefinitionWrapper propDef = propDefbyPropId.get(propId); if(propDef == null && getParent() != null) { propDef = getParent().getPropDefByPropId(propId); } return propDef; } @Override public PropertyDefinitionWrapper getPropDefByQueryName(Object queryName) { PropertyDefinitionWrapper propDef = propDefbyQueryName.get(queryName); if(propDef == null && getParent() != null) { propDef = getParent().getPropDefByQueryName(queryName); } return propDef; } private Collection getTypeDefsImpl() { return typeDefsByTypeId.values(); } @Override public Collection getTypeDefs() { return getTypeDefs(true); } @Override public Collection getTypeDefs(boolean includeParent) { Collection ret = new LinkedList<>(); ret.addAll(getTypeDefsImpl()); if(includeParent && getParent() != null) { ret.addAll(getParent().getTypeDefs()); } return Collections.unmodifiableCollection(ret); } private Collection getAssocDefsImpl() { return assocDefsByQName.values(); } @Override public Collection getAssocDefs() { return getAssocDefs(true); } @Override public Collection getAssocDefs(boolean includeParent) { Collection ret = new LinkedList<>(); ret.addAll(getAssocDefsImpl()); if(includeParent && getParent() != null) { ret.addAll(getParent().getAssocDefs()); } return Collections.unmodifiableCollection(ret); } private void addTypeExtensions(TypeDefinitionWrapper td) { QName classQName = td.getAlfrescoClass(); ClassDefinition classDef = dictionaryService.getClass(classQName); if(classDef != null) { // add mandatory/default aspects List defaultAspects = classDef.getDefaultAspects(true); if(defaultAspects != null && defaultAspects.size() > 0) { List mandatoryAspectsExtensions = new ArrayList(); for(AspectDefinition aspectDef : defaultAspects) { QName aspectQName = aspectDef.getName(); TypeDefinitionWrapper aspectType = getTypeDefByQName(cmisMapping.getCmisType(aspectQName)); if (aspectType == null) { continue; } mandatoryAspectsExtensions.add(new CmisExtensionElementImpl(ALFRESCO_EXTENSION_NAMESPACE, MANDATORY_ASPECT, null, aspectType.getTypeId())); } if(!mandatoryAspectsExtensions.isEmpty()) { td.getTypeDefinition(true).setExtensions( Collections.singletonList((CmisExtensionElement) new CmisExtensionElementImpl( ALFRESCO_EXTENSION_NAMESPACE, MANDATORY_ASPECTS, null, mandatoryAspectsExtensions))); } } } } @Override public void addModel(CompiledModel model) { Collection types = dictionaryInitializer.createDefinitions(this, model); addTypes(types); for(AbstractTypeDefinitionWrapper type : types) { type.resolveInheritance(cmisMapping, this, dictionaryService); } } @Override public void updateModel(CompiledModel model) { // TODO } @Override public void removeModel(CompiledModel model) { // TODO } private void clear() { typeDefsByQName.clear(); assocDefsByQName.clear(); typeDefsByTypeId.clear(); typeDefsByQueryName.clear(); baseTypes.clear(); propDefbyPropId.clear(); propDefbyQueryName.clear(); children.clear(); } private void addTypes(Collection types) { // phase 1: construct type definitions and link them together for (AbstractTypeDefinitionWrapper objectTypeDef : types) { List children = objectTypeDef.connectParentAndSubTypes(cmisMapping, this, dictionaryService); setChildren(objectTypeDef.getTypeId(), children); } // phase 2: register base types and inherit property definitions for (AbstractTypeDefinitionWrapper typeDef : types) { if (typeDef.getTypeDefinition(false).getParentTypeId() == null || !tenant.equals(TenantService.DEFAULT_DOMAIN)) { if(tenant.equals(TenantService.DEFAULT_DOMAIN)) { baseTypes.add(typeDef); } typeDef.resolveInheritance(cmisMapping, this, dictionaryService); } } // phase 3: register properties for (AbstractTypeDefinitionWrapper typeDef : types) { registerPropertyDefinitions(typeDef); } // phase 4: assert valid for (AbstractTypeDefinitionWrapper typeDef : types) { typeDef.assertComplete(); addTypeExtensions(typeDef); } } public void init() { long start = System.currentTimeMillis(); if (logger.isDebugEnabled()) { logger.debug("Creating type definitions..."); } Collection types = dictionaryInitializer.createDefinitions(this); addTypes(types); long end = System.currentTimeMillis(); if (logger.isInfoEnabled()) { logger.info("Initialized CMIS Dictionary " + cmisMapping.getCmisVersion() + " tenant " + tenant + " in " + (end - start) + "ms. Types:" + typeDefsByTypeId.size() + ", Base Types:" + baseTypes.size()); } } private List getBaseTypesImpl() { return baseTypes; } @Override public List getBaseTypes() { return getBaseTypes(true); } @Override public List getBaseTypes(boolean includeParent) { List ret = new LinkedList(); List baseTypes = getBaseTypesImpl(); if(baseTypes != null) { ret.addAll(baseTypes); } if(includeParent && getParent() != null) { baseTypes = getParent().getBaseTypes(); if(baseTypes != null) { ret.addAll(baseTypes); } } return Collections.unmodifiableList(ret); } /** * Register type definition. * * @param typeDef AbstractTypeDefinitionWrapper */ @Override public void registerTypeDefinition(AbstractTypeDefinitionWrapper typeDef) { TypeDefinitionWrapper existingTypeDef = typeDefsByTypeId.get(typeDef.getTypeId()); if (existingTypeDef != null) { // throw new AlfrescoRuntimeException("Type " + typeDef.getTypeId() + " already registered"); if(logger.isWarnEnabled()) { logger.warn("Type " + typeDef.getTypeId() + " already registered"); } } typeDefsByTypeId.put(typeDef.getTypeId(), typeDef); QName typeQName = typeDef.getAlfrescoName(); if (typeQName != null) { if ((typeDef instanceof RelationshipTypeDefintionWrapper) && !typeDef.isBaseType()) { assocDefsByQName.put(typeQName, typeDef); } else { typeDefsByQName.put(typeQName, typeDef); } } typeDefsByQueryName.put(typeDef.getTypeDefinition(false).getQueryName(), typeDef); if (logger.isDebugEnabled()) { logger.debug("Registered type " + typeDef.getTypeId() + " (scope=" + typeDef.getBaseTypeId() + ")"); logger.debug(" QName: " + typeDef.getAlfrescoName()); logger.debug(" Table: " + typeDef.getTypeDefinition(false).getQueryName()); logger.debug(" Action Evaluators: " + typeDef.getActionEvaluators().size()); } } /** * Register property definitions. * * @param typeDef AbstractTypeDefinitionWrapper */ public void registerPropertyDefinitions(AbstractTypeDefinitionWrapper typeDef) { for (PropertyDefinitionWrapper propDef : typeDef.getProperties(false)) { if (propDef.getPropertyDefinition().isInherited()) { continue; } propDefbyPropId.put(propDef.getPropertyId(), propDef); propDefbyQueryName.put(propDef.getPropertyDefinition().getQueryName(), propDef); } } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("DictionaryRegistry["); builder.append("Types=").append(typeDefsByTypeId.size()).append(", "); builder.append("Base Types=").append(baseTypes.size()).append(", "); builder.append("]"); return builder.toString(); } }