/******************************************************************************* * Copyright (c) 2013 aegif. * * This file is part of NemakiWare. * * NemakiWare is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * NemakiWare 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along with NemakiWare. * If not, see <http://www.gnu.org/licenses/>. * * Contributors: * linzhixing(https://github.com/linzhixing) - initial API and implementation ******************************************************************************/ package jp.aegif.nemaki.cmis.service.impl; import java.math.BigInteger; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import jp.aegif.nemaki.businesslogic.ContentService; import jp.aegif.nemaki.businesslogic.TypeService; import jp.aegif.nemaki.cmis.aspect.ExceptionService; import jp.aegif.nemaki.cmis.aspect.type.TypeManager; import jp.aegif.nemaki.cmis.factory.info.RepositoryInfo; import jp.aegif.nemaki.cmis.factory.info.RepositoryInfoMap; import jp.aegif.nemaki.cmis.service.RepositoryService; import jp.aegif.nemaki.model.NemakiPropertyDefinition; import jp.aegif.nemaki.model.NemakiPropertyDefinitionCore; import jp.aegif.nemaki.model.NemakiPropertyDefinitionDetail; import jp.aegif.nemaki.model.NemakiTypeDefinition; import jp.aegif.nemaki.util.constant.DomainType; import org.apache.chemistry.opencmis.commons.data.ExtensionsData; import org.apache.chemistry.opencmis.commons.definitions.DocumentTypeDefinition; import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition; import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition; import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer; import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList; import org.apache.chemistry.opencmis.commons.definitions.TypeMutability; import org.apache.chemistry.opencmis.commons.impl.dataobjects.AbstractTypeDefinition; import org.apache.chemistry.opencmis.commons.server.CallContext; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.springframework.beans.factory.InitializingBean; public class RepositoryServiceImpl implements RepositoryService, InitializingBean { private RepositoryInfoMap repositoryInfoMap; private TypeManager typeManager; private TypeService typeService; private ContentService contentService; private ExceptionService exceptionService; @Override public boolean hasThisRepositoryId(String repositoryId) { return (repositoryInfoMap.get(repositoryId) != null); } @Override public RepositoryInfo getRepositoryInfo(String repositoryId) { RepositoryInfo info = repositoryInfoMap.get(repositoryId); info.setLatestChangeLogToken(contentService.getLatestChangeToken(repositoryId)); return info; } public List<org.apache.chemistry.opencmis.commons.data.RepositoryInfo> getRepositoryInfos(){ List<org.apache.chemistry.opencmis.commons.data.RepositoryInfo> result = new ArrayList<org.apache.chemistry.opencmis.commons.data.RepositoryInfo>(); for(String key : repositoryInfoMap.keys()){ result.add(repositoryInfoMap.get(key)); } return result; } /** * CMIS Service method */ @Override public TypeDefinitionList getTypeChildren(CallContext callContext, String repositoryId, String typeId, Boolean includePropertyDefinitions, BigInteger maxItems, BigInteger skipCount) { return typeManager.getTypesChildren(callContext, repositoryId, typeId, includePropertyDefinitions, maxItems, skipCount); } @Override public List<TypeDefinitionContainer> getTypeDescendants( CallContext callContext, String repositoryId, String typeId, BigInteger depth, Boolean includePropertyDefinitions) { return typeManager.getTypesDescendants(repositoryId, typeId, depth, includePropertyDefinitions); } @Override public TypeDefinition getTypeDefinition(CallContext callContext, String repositoryId, String typeId) { TypeDefinition typeDefinition = typeManager.getTypeDefinition(repositoryId, typeId); exceptionService.objectNotFound(DomainType.OBJECT_TYPE, typeDefinition, typeId); return typeDefinition; } @Override public TypeDefinition createType(CallContext callContext, String repositoryId, TypeDefinition type, ExtensionsData extension) { // ////////////////// // General Exception // ////////////////// exceptionService.perimissionAdmin(callContext, repositoryId); exceptionService.invalidArgumentRequired("typeDefinition", type); exceptionService.invalidArgumentCreatableType(repositoryId, type); exceptionService.constraintDuplicatePropertyDefinition(repositoryId, type); // ////////////////// // Body of the method // ////////////////// // Attributes NemakiTypeDefinition ntd = setNemakiTypeDefinitionAttributes(repositoryId, type); // Property definitions List<String> systemIds = typeManager.getSystemPropertyIds(); Map<String, PropertyDefinition<?>> propDefs = type .getPropertyDefinitions(); ntd.setProperties(new ArrayList<String>()); if (MapUtils.isNotEmpty(propDefs)) { for (String key : propDefs.keySet()) { PropertyDefinition<?> propDef = propDefs.get(key); if (!systemIds.contains(key)) { //Check PropertyDefinition exceptionService.constraintQueryName(propDef); exceptionService.constraintPropertyDefinition(type, propDef); NemakiPropertyDefinition create = new NemakiPropertyDefinition( propDef); NemakiPropertyDefinitionDetail created = typeService .createPropertyDefinition(repositoryId, create); List<String> l = ntd.getProperties(); l.add(created.getId()); ntd.setProperties(l); } } } // Create NemakiTypeDefinition created = typeService.createTypeDefinition(repositoryId, ntd); typeManager.refreshTypes(); // Sort the order of properties return sortPropertyDefinitions(repositoryId, created, type); } @Override public void deleteType(CallContext callContext, String repositoryId, String typeId, ExtensionsData extension) { // ////////////////// // General Exception // ////////////////// exceptionService.perimissionAdmin(callContext, repositoryId); exceptionService.invalidArgumentRequiredString("typeId", typeId); exceptionService.invalidArgumentDoesNotExistType(repositoryId, typeId); exceptionService.invalidArgumentDeletableType(repositoryId, typeId); exceptionService.constraintOnlyLeafTypeDefinition(repositoryId, typeId); exceptionService.constraintObjectsStillExist(repositoryId, typeId); // ////////////////// // Body of the method // ////////////////// typeService.deleteTypeDefinition(repositoryId, typeId); typeManager.refreshTypes(); } @Override public TypeDefinition updateType(CallContext callContext, String repositoryId, TypeDefinition type, ExtensionsData extension) { // ////////////////// // General Exception // ////////////////// exceptionService.perimissionAdmin(callContext, repositoryId); exceptionService.invalidArgumentRequired("typeDefinition", type); exceptionService.invalidArgumentDoesNotExistType(repositoryId, type.getId()); exceptionService.invalidArgumentUpdatableType(type); exceptionService.constraintOnlyLeafTypeDefinition(repositoryId, type.getId()); exceptionService.constraintDuplicatePropertyDefinition(repositoryId, type); // ////////////////// // Body of the method // ////////////////// NemakiTypeDefinition existingType = typeService .getTypeDefinition(repositoryId, type.getId()); // Attributes NemakiTypeDefinition ntd = setNemakiTypeDefinitionAttributes(repositoryId, type); // Property definitions List<String> systemIds = typeManager.getSystemPropertyIds(); Map<String, PropertyDefinition<?>> propDefs = type .getPropertyDefinitions(); ntd.setProperties(new ArrayList<String>()); if (MapUtils.isNotEmpty(propDefs)) { for (String key : propDefs.keySet()) { PropertyDefinition<?> propDef = propDefs.get(key); if (systemIds.contains(key)) continue; List<String> existingPropertyNodeIds = (CollectionUtils .isEmpty(existingType.getProperties())) ? new ArrayList<String>() : existingType.getProperties(); String propNodeId = typeService.getPropertyDefinitionCoreByPropertyId(repositoryId, key).getId(); if (existingPropertyNodeIds.contains(propNodeId)) { // update PropertyDefinition<?> oldPropDef = typeManager.getTypeDefinition(repositoryId, existingType.getTypeId()).getPropertyDefinitions().get(propNodeId); exceptionService.constraintUpdatePropertyDefinition(propDef, oldPropDef); exceptionService.constraintQueryName(propDef); exceptionService.constraintPropertyDefinition(type, propDef); NemakiPropertyDefinition _update = new NemakiPropertyDefinition( propDef); NemakiPropertyDefinitionCore core = typeService.getPropertyDefinitionCoreByPropertyId(repositoryId, _update.getPropertyId()); NemakiPropertyDefinitionDetail update = new NemakiPropertyDefinitionDetail(_update, core.getId()); typeService.updatePropertyDefinitionDetail(repositoryId, update); } else { // create exceptionService.constraintQueryName(propDef); NemakiPropertyDefinition create = new NemakiPropertyDefinition( propDef); NemakiPropertyDefinitionDetail created = typeService .createPropertyDefinition(repositoryId, create); List<String> l = ntd.getProperties(); l.add(created.getId()); ntd.setProperties(l); } } } // Update NemakiTypeDefinition updated = typeService.updateTypeDefinition(repositoryId, ntd); typeManager.refreshTypes(); // Sort return sortPropertyDefinitions(repositoryId, updated, type); } private NemakiTypeDefinition setNemakiTypeDefinitionAttributes( String repositoryId, TypeDefinition typeDefinition) { NemakiTypeDefinition ntd = new NemakiTypeDefinition(); // To avoid the conflict of typeId, add suffix if (typeManager.getTypeById(repositoryId, typeDefinition.getId()) == null) { ntd.setTypeId(typeDefinition.getId()); } else { ntd.setTypeId(typeDefinition.getId() + "_" + String.valueOf(System.currentTimeMillis())); } ntd.setLocalName(typeDefinition.getLocalName()); ntd.setLocalNameSpace(typeDefinition.getLocalNamespace()); ntd.setQueryName(typeDefinition.getQueryName()); ntd.setDisplayName(typeDefinition.getDisplayName()); ntd.setBaseId(typeDefinition.getBaseTypeId()); ntd.setParentId(typeDefinition.getParentTypeId()); ntd.setDescription(typeDefinition.getDescription()); ntd.setCreatable(typeDefinition.isCreatable()); ntd.setFilable(typeDefinition.isFileable()); ntd.setQueryable(typeDefinition.isQueryable()); ntd.setControllablePolicy(typeDefinition.isControllablePolicy()); ntd.setControllableACL(typeDefinition.isControllableAcl()); ntd.setFulltextIndexed(typeDefinition.isFulltextIndexed()); ntd.setIncludedInSupertypeQuery(typeDefinition .isIncludedInSupertypeQuery()); TypeMutability typeMutability = typeDefinition.getTypeMutability(); if (typeMutability != null) { ntd.setTypeMutabilityCreate(typeMutability.canCreate()); ntd.setTypeMutabilityDelete(typeMutability.canDelete()); ntd.setTypeMutabilityUpdate(typeMutability.canUpdate()); } else { // These default values are repository-specific. ntd.setTypeMutabilityCreate(true); ntd.setTypeMutabilityDelete(true); ntd.setTypeMutabilityUpdate(true); } //specific to DocumentTypeDefinition if(typeDefinition instanceof DocumentTypeDefinition){ DocumentTypeDefinition dtdf = (DocumentTypeDefinition)typeDefinition; ntd.setVersionable(dtdf.isVersionable()); ntd.setContentStreamAllowed(dtdf.getContentStreamAllowed()); } return ntd; } private TypeDefinition sortPropertyDefinitions( String repositoryId, NemakiTypeDefinition nemakiTypeDefinition, TypeDefinition criterion) { AbstractTypeDefinition tdf = typeManager .buildTypeDefinitionFromDB(repositoryId, nemakiTypeDefinition); Map<String, PropertyDefinition<?>> propDefs = tdf .getPropertyDefinitions(); LinkedHashMap<String, PropertyDefinition<?>> map = new LinkedHashMap<String, PropertyDefinition<?>>(); LinkedHashMap<String, PropertyDefinition<?>> sorted = new LinkedHashMap<String, PropertyDefinition<?>>(); if (MapUtils.isNotEmpty(criterion.getPropertyDefinitions())) { // Not updated property definitions for (Entry<String, PropertyDefinition<?>> propDef : propDefs .entrySet()) { if (!criterion.getPropertyDefinitions().containsKey( propDef.getKey())) { map.put(propDef.getKey(), propDef.getValue()); } } // Sorted updated property definitions for (Entry<String, PropertyDefinition<?>> entry : criterion .getPropertyDefinitions().entrySet()) { sorted.put(entry.getKey(), entry.getValue()); } // Merge for (Entry<String, PropertyDefinition<?>> entry : sorted.entrySet()) { map.put(entry.getKey(), entry.getValue()); } tdf.setPropertyDefinitions(map); } return tdf; } /** * Sets CMIS optional capabilities for Nemaki repository. */ @Override public void afterPropertiesSet() throws Exception { } public void setRepositoryInfoMap(RepositoryInfoMap repositoryInfoMap) { this.repositoryInfoMap = repositoryInfoMap; } public void setTypeManager(TypeManager typeManager) { this.typeManager = typeManager; } public void setExceptionService(ExceptionService exceptionService) { this.exceptionService = exceptionService; } public void setTypeService(TypeService typeService) { this.typeService = typeService; } public void setContentService(ContentService contentService) { this.contentService = contentService; } }