diff --git a/source/java/org/alfresco/repo/virtual/ActualEnvironment.java b/source/java/org/alfresco/repo/virtual/ActualEnvironment.java new file mode 100644 index 0000000000..fddca06311 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ActualEnvironment.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual; + +import java.io.InputStream; +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.service.cmr.dictionary.InvalidAspectException; +import org.alfresco.service.cmr.dictionary.InvalidTypeException; +import org.alfresco.service.cmr.model.FileExistsException; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.QNamePattern; + +/** + * Dependency inversion facade of the Alfresco repository environment. It offers + * an interface to Alfresco repository capabilities needed for virtualization. + * Implementors should consider loose repository beans coupling when + * implementing the environment operations. + * + * @author Bogdan Horje + */ +public interface ActualEnvironment +{ + + QName getType(NodeRef nodeRef); + + boolean isSubClass(QName className, QName ofClassName); + + NodeRef getTargetAssocs(NodeRef nodeRef, QName aspectTypeQName); + + Serializable getProperty(NodeRef nodeRef, QName qname) throws ActualEnvironmentException; + + Map getProperties(NodeRef nodeRef); + + boolean hasAspect(NodeRef nodeRef, QName aspectTypeQName); + + Set getAspects(NodeRef nodeRef); + + String getCurrentUser(); + + Path getPath(NodeRef nodeRef); + + ChildAssociationRef getPrimaryParent(NodeRef nodeRef); + + List getChildAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern, + int maxResults, boolean preload) throws InvalidNodeRefException; + + NodeRef getChildByName(NodeRef nodeRef, QName assocTypeQName, String childName); + + NamespacePrefixResolver getNamespacePrefixResolver(); + + InputStream openContentStream(NodeRef nodeRef) throws ActualEnvironmentException; + + InputStream openContentStream(String classpath) throws ActualEnvironmentException; + + ResultSet query(SearchParameters searchParameters); + + Object executeScript(String classpath, Map model) throws ActualEnvironmentException; + + Object executeScript(NodeRef templateNodeRef, Map model) throws ActualEnvironmentException; + + Object createScriptVirtualContext(VirtualContext context) throws ActualEnvironmentException; + + NodeRef findNodeRef(String referenceType, String[] reference); + + NodeRef findQNamePath(String[] patheElements); + + boolean exists(NodeRef nodeRef); + + boolean exists(String classpath); + + void delete(NodeRef nodeRef); + + FileInfo create(NodeRef parentNodeRef, String name, QName typeQName) throws FileExistsException; + + ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update) throws InvalidNodeRefException, + InvalidTypeException; + + void addAspect(NodeRef nodeRef, QName aspectTypeQName, Map aspectProperties) + throws InvalidNodeRefException, InvalidAspectException; + +} diff --git a/source/java/org/alfresco/repo/virtual/ActualEnvironmentException.java b/source/java/org/alfresco/repo/virtual/ActualEnvironmentException.java new file mode 100644 index 0000000000..1b8aba90e0 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ActualEnvironmentException.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual; + +public class ActualEnvironmentException extends VirtualizationException +{ + + /** + * + */ + private static final long serialVersionUID = 6411382816440744840L; + + public ActualEnvironmentException() + { + super(); + } + + public ActualEnvironmentException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) + { + super(message, + cause, + enableSuppression, + writableStackTrace); + } + + public ActualEnvironmentException(String message, Throwable cause) + { + super(message, + cause); + } + + public ActualEnvironmentException(String message) + { + super(message); + } + + public ActualEnvironmentException(Throwable cause) + { + super(cause); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/AlfrescoAPIFacet.java b/source/java/org/alfresco/repo/virtual/AlfrescoAPIFacet.java new file mode 100644 index 0000000000..ad11040e62 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/AlfrescoAPIFacet.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual; + +import org.alfresco.service.NotAuditable; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.ScriptService; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.PermissionService; + +/** + * Dependency inversion facade of the Alfresco services. It provides access to + * the implementations of Alfresco services. + * + * @author Bogdan Horje + */ +public interface AlfrescoAPIFacet +{ + + @NotAuditable + ScriptService getScriptService(); + + @NotAuditable + NodeService getNodeService(); + + @NotAuditable + ContentService getContentService(); + + @NotAuditable + SearchService getSearchService(); + + @NotAuditable + DictionaryService getDictionaryService(); + + @NotAuditable + FileFolderService getFileFolderService(); + + @NotAuditable + PermissionService getPermissionService(); + +} diff --git a/source/java/org/alfresco/repo/virtual/AlfrescoEnviroment.java b/source/java/org/alfresco/repo/virtual/AlfrescoEnviroment.java new file mode 100644 index 0000000000..9029a15881 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/AlfrescoEnviroment.java @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual; + +import java.io.InputStream; +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.jscript.ClasspathScriptLocation; +import org.alfresco.repo.model.Repository; +import org.alfresco.repo.virtual.config.NodeRefResolver; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.dictionary.InvalidAspectException; +import org.alfresco.service.cmr.dictionary.InvalidTypeException; +import org.alfresco.service.cmr.model.FileExistsException; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.QNamePattern; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; + +public class AlfrescoEnviroment implements ActualEnvironment +{ + private AlfrescoAPIFacet apiFacet; + + private ServiceRegistry serviceRegistry; + + private NamespacePrefixResolver namespacePrefixResolver; + + private Repository repositoryHelper; + + private NodeRefResolver nodeRefResolver; + + public void setNodeRefResolver(NodeRefResolver nodeRefResolver) + { + this.nodeRefResolver = nodeRefResolver; + } + + public void setAlfrescoAPIFacet(AlfrescoAPIFacet apiFacet) + { + this.apiFacet = apiFacet; + } + + public void setServiceRegistry(ServiceRegistry serviceRegistry) + { + this.serviceRegistry = serviceRegistry; + } + + public void setRepositoryHelper(Repository repository) + { + this.repositoryHelper = repository; + } + + @Override + public Object executeScript(String classpath, Map model) + { + + return apiFacet.getScriptService().executeScript(new ClasspathScriptLocation(classpath.substring(1)), + model); + } + + @Override + public Object executeScript(NodeRef templateNodeRef, Map model) + { + + return apiFacet.getScriptService().executeScript(templateNodeRef, + null, + model); + } + + @Override + public boolean hasAspect(final NodeRef nodeRef, final QName aspectTypeQName) + { + return apiFacet.getNodeService().hasAspect(nodeRef, + aspectTypeQName); + } + + @Override + public Set getAspects(NodeRef nodeRef) + { + NodeService nodeService = apiFacet.getNodeService(); + return nodeService.getAspects(nodeRef); + } + + @Override + public NodeRef getTargetAssocs(NodeRef nodeRef, QName associationQName) + { + List assocs = apiFacet.getNodeService().getTargetAssocs(nodeRef, + associationQName); + + if (assocs != null && assocs.size() >= 1) + { + AssociationRef associationRef = assocs.get(0); + NodeRef targetRef = associationRef.getTargetRef(); + return targetRef; + } + else + { + return null; + } + } + + @Override + public Serializable getProperty(NodeRef nodeRef, QName qname) + { + return apiFacet.getNodeService().getProperty(nodeRef, + qname); + } + + @Override + public Map getProperties(NodeRef nodeRef) + { + return apiFacet.getNodeService().getProperties(nodeRef); + } + + @Override + public InputStream openContentStream(NodeRef nodeRef) throws ActualEnvironmentException + { + ContentReader contentReader = apiFacet.getContentService().getReader(nodeRef, + ContentModel.PROP_CONTENT); + return contentReader.getContentInputStream(); + } + + @Override + public InputStream openContentStream(String classpath) throws ActualEnvironmentException + { + return getClass().getResourceAsStream(classpath); + } + + @Override + public ResultSet query(SearchParameters searchParameters) + { + return apiFacet.getSearchService().query(searchParameters); + } + + @Override + public Object createScriptVirtualContext(VirtualContext context) throws ActualEnvironmentException + { + return new AlfrescoScriptVirtualContext(context, + serviceRegistry); + } + + @Override + public QName getType(final NodeRef nodeRef) + { + return apiFacet.getNodeService().getType(nodeRef); + } + + @Override + public boolean isSubClass(QName className, QName ofClassName) + { + return apiFacet.getDictionaryService().isSubClass(className, + ofClassName); + } + + public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) + { + this.namespacePrefixResolver = namespacePrefixResolver; + } + + @Override + public NamespacePrefixResolver getNamespacePrefixResolver() + { + return namespacePrefixResolver; + } + + @Override + public String getCurrentUser() + { + return serviceRegistry.getAuthenticationService().getCurrentUserName(); + } + + @Override + public Path getPath(NodeRef nodeRef) + { + return apiFacet.getNodeService().getPath(nodeRef); + } + + @Override + public ChildAssociationRef getPrimaryParent(NodeRef nodeRef) + { + NodeService nodeService = apiFacet.getNodeService(); + return nodeService.getPrimaryParent(nodeRef); + } + + @Override + public List getChildAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, + QNamePattern qnamePattern, int maxResults, boolean preload) throws InvalidNodeRefException + { + NodeService nodeService = apiFacet.getNodeService(); + return nodeService.getChildAssocs(nodeRef, + typeQNamePattern, + qnamePattern, + maxResults, + preload); + } + + @Override + public NodeRef findNodeRef(String referenceType, String[] reference) + { + return repositoryHelper.findNodeRef(referenceType, + reference); + } + + @Override + public boolean exists(NodeRef nodeRef) + { + return apiFacet.getNodeService().exists(nodeRef); + } + + @Override + public NodeRef getChildByName(NodeRef nodeRef, QName assocTypeQName, String childName) + { + return apiFacet.getNodeService().getChildByName(nodeRef, + assocTypeQName, + childName); + } + + @Override + public void delete(NodeRef nodeRef) + { + apiFacet.getNodeService().deleteNode(nodeRef); + } + + @Override + public FileInfo create(NodeRef parentNodeRef, String name, QName typeQName) throws FileExistsException + { + return apiFacet.getFileFolderService().create(parentNodeRef, + name, + typeQName); + } + + @Override + public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update) + throws InvalidNodeRefException, InvalidTypeException + { + return apiFacet.getContentService().getWriter(nodeRef, + propertyQName, + update); + } + + @Override + public void addAspect(NodeRef nodeRef, QName aspectTypeQName, Map aspectProperties) + throws InvalidNodeRefException, InvalidAspectException + { + apiFacet.getNodeService().addAspect(nodeRef, + aspectTypeQName, + aspectProperties); + } + + @Override + public NodeRef findQNamePath(String[] patheElements) + { + return nodeRefResolver.resolveQNameReference(patheElements); + } + + @Override + public boolean exists(String classpath) + { + ClassLoader cl = this.getClass().getClassLoader(); + ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl); + Resource resource = resolver.getResource("classpath:" + classpath); + return resource.exists(); + } +} diff --git a/source/java/org/alfresco/repo/virtual/AlfrescoScriptVirtualContext.java b/source/java/org/alfresco/repo/virtual/AlfrescoScriptVirtualContext.java new file mode 100644 index 0000000000..81a8035eee --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/AlfrescoScriptVirtualContext.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.repo.jscript.BaseScopableProcessorExtension; +import org.alfresco.repo.jscript.ScriptNode; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.ServiceRegistry; + +/** + * JavaScript API {@link VirtualContext} adapter. + * + * @author Bogdan Horje + */ +public class AlfrescoScriptVirtualContext extends BaseScopableProcessorExtension +{ + // TODO: extract placeholder interface. make placeholders configurable. + + public static final String CURRENT_USER_PH = "CURRENT_USER"; + + public static final String ACTUAL_PATH_PH = "ACTUAL_PATH"; + + private VirtualContext context; + + private ServiceRegistry serviceRegistry; + + private Map placeholders; + + public AlfrescoScriptVirtualContext(VirtualContext context, ServiceRegistry serviceRegistry) + { + super(); + this.context = context; + this.serviceRegistry = serviceRegistry; + this.placeholders = createPlaceHolders(); + } + + // TODO: extract placeholder interface. make placeholders configurable. + private Map createPlaceHolders() + { + Map newPlaceholders = new HashMap<>(); + String user = AuthenticationUtil.getFullyAuthenticatedUser(); + + // TODO: extract open-close-configurable placeholders + newPlaceholders.put(CURRENT_USER_PH, + user); + // TODO: can we replace getQnamePath usage + newPlaceholders.put(ACTUAL_PATH_PH, + getActualNode().getQnamePath()); + return newPlaceholders; + } + + public Map getPlaceholders() + { + return placeholders; + } + + public synchronized ScriptNode getActualNode() + { + return new ScriptNode(context.getActualNodeRef(), + serviceRegistry, + getScope()); + } + + public Object getParameter(String param) + { + return context.getParameter(param); + } +} diff --git a/source/java/org/alfresco/repo/virtual/CoreAPIFacet.java b/source/java/org/alfresco/repo/virtual/CoreAPIFacet.java new file mode 100644 index 0000000000..e953f4630d --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/CoreAPIFacet.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual; + +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.ScriptService; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.PermissionService; + +/** + * Implementation of the {@link AlfrescoAPIFacet} interface that provides access + * to "core" Alfresco services, i.e. services in a lower abstraction layer than + * the ones provided in the Java API, by the {@link ServiceRegistry}. + * + * @author Bogdan Horje + */ +public class CoreAPIFacet implements AlfrescoAPIFacet +{ + + private ScriptService scriptService; + + private NodeService nodeService; + + private ContentService contentService; + + private SearchService searchService; + + private DictionaryService dictionaryService; + + private FileFolderService fileFolderService; + + private PermissionService permissionService; + + public void setScriptService(ScriptService scriptService) + { + this.scriptService = scriptService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + public void setSearchService(SearchService searchService) + { + this.searchService = searchService; + } + + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + public void setFileFolderService(FileFolderService fileFolderService) + { + this.fileFolderService = fileFolderService; + } + + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + @Override + public ScriptService getScriptService() + { + return scriptService; + } + + @Override + public NodeService getNodeService() + { + return nodeService; + } + + @Override + public ContentService getContentService() + { + return contentService; + } + + @Override + public SearchService getSearchService() + { + return searchService; + } + + @Override + public DictionaryService getDictionaryService() + { + return dictionaryService; + } + + @Override + public FileFolderService getFileFolderService() + { + return fileFolderService; + } + + @Override + public PermissionService getPermissionService() + { + return permissionService; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/PublicAPIFacet.java b/source/java/org/alfresco/repo/virtual/PublicAPIFacet.java new file mode 100644 index 0000000000..46e4b6d410 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/PublicAPIFacet.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual; + +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.ScriptService; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.PermissionService; + +/** + * Implementation of the {@link AlfrescoAPIFacet} interface that provides access + * to Alfresco services at the level of the Java API, through its + * {@link ServiceRegistry} instance. + * + * @author Bogdan Horje + */ +public class PublicAPIFacet implements AlfrescoAPIFacet +{ + private ServiceRegistry serviceRegistry; + + public void setServiceRegistry(ServiceRegistry serviceRegistry) + { + this.serviceRegistry = serviceRegistry; + } + + @Override + public ScriptService getScriptService() + { + return serviceRegistry.getScriptService(); + } + + @Override + public NodeService getNodeService() + { + return serviceRegistry.getNodeService(); + } + + @Override + public ContentService getContentService() + { + return serviceRegistry.getContentService(); + } + + @Override + public SearchService getSearchService() + { + return serviceRegistry.getSearchService(); + } + + @Override + public DictionaryService getDictionaryService() + { + return serviceRegistry.getDictionaryService(); + } + + @Override + public FileFolderService getFileFolderService() + { + return serviceRegistry.getFileFolderService(); + } + + @Override + public PermissionService getPermissionService() + { + return serviceRegistry.getPermissionService(); + } +} diff --git a/source/java/org/alfresco/repo/virtual/VirtualContentModel.java b/source/java/org/alfresco/repo/virtual/VirtualContentModel.java new file mode 100644 index 0000000000..701923a497 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/VirtualContentModel.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual; + +import org.alfresco.service.namespace.QName; + +/** + * Virtual Content Model Constants + * + * @author Bogdan Horje + */ +public interface VirtualContentModel +{ + static final String VIRTUAL_CONTENT_MODEL_1_0_URI = "http://www.alfresco.org/model/content/virtual/1.0"; + + static final QName ASPECT_VIRTUAL = QName.createQName(VIRTUAL_CONTENT_MODEL_1_0_URI, + "virtual"); + + static final QName ASPECT_VIRTUAL_DOCUMENT = QName.createQName(VIRTUAL_CONTENT_MODEL_1_0_URI, + "virtual-document"); + + static final QName TYPE_VIRTUAL_FOLDER_TEMPLATE = QName.createQName(VIRTUAL_CONTENT_MODEL_1_0_URI, + "virtualFolderTemplate"); +} diff --git a/source/java/org/alfresco/repo/virtual/VirtualContext.java b/source/java/org/alfresco/repo/virtual/VirtualContext.java new file mode 100644 index 0000000000..7f42748a3e --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/VirtualContext.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * The context in which a virtualization process takes place. + * + * @author Bogdan Horje + */ +public class VirtualContext +{ + + public static final String CONTEXT_PARAM = "context"; + + public static final String PLACEHOLDERS_PARAM = "placeholders"; + + private Map parameters; + + private NodeRef actualNodeRef; + + private ActualEnvironment actualEnviroment; + + public VirtualContext(VirtualContext context) + { + this(context.actualEnviroment, + context.actualNodeRef, + new HashMap(context.parameters)); + } + + public VirtualContext(ActualEnvironment actualEnviroment, NodeRef actualNodeRef) + { + this(actualEnviroment, + actualNodeRef, + new HashMap()); + } + + public VirtualContext(ActualEnvironment actualEnviroment, NodeRef actualNodeRef, Map parameters) + { + this.parameters = parameters; + this.parameters.put(CONTEXT_PARAM, + this); + this.actualEnviroment = actualEnviroment; + this.actualNodeRef = actualNodeRef; + } + + public ActualEnvironment getActualEnviroment() + { + return actualEnviroment; + } + + public NodeRef getActualNodeRef() + { + return actualNodeRef; + } + + public void setParameter(String parameter, Object value) + { + parameters.put(parameter, + value); + } + + public Map getParameters() + { + return new HashMap<>(parameters); + } + + @Override + public int hashCode() + { + return actualNodeRef.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (obj instanceof VirtualContext) + { + VirtualContext ctxObj = (VirtualContext) obj; + return actualNodeRef.equals(ctxObj.actualNodeRef); + } + else + { + return false; + } + } + + @Override + public String toString() + { + return actualNodeRef.toString(); + } + + public Object getParameter(String param) + { + return parameters.get(param); + } +} diff --git a/source/java/org/alfresco/repo/virtual/VirtualizationException.java b/source/java/org/alfresco/repo/virtual/VirtualizationException.java new file mode 100644 index 0000000000..17af6188e5 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/VirtualizationException.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual; + +public class VirtualizationException extends RuntimeException +{ + + /** + * + */ + private static final long serialVersionUID = -5117143626926061650L; + + public VirtualizationException() + { + super(); + } + + public VirtualizationException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) + { + super(message, + cause, + enableSuppression, + writableStackTrace); + } + + public VirtualizationException(String message, Throwable cause) + { + super(message, + cause); + } + + public VirtualizationException(String message) + { + super(message); + } + + public VirtualizationException(Throwable cause) + { + super(cause); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/bundle/FileInfoPropsComparator.java b/source/java/org/alfresco/repo/virtual/bundle/FileInfoPropsComparator.java new file mode 100644 index 0000000000..77644ab7ec --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/bundle/FileInfoPropsComparator.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.bundle; + +import java.text.Collator; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.AlfrescoCollator; +import org.alfresco.util.Pair; +import org.springframework.extensions.surf.util.I18NUtil; + +public class FileInfoPropsComparator implements Comparator +{ + private List> sortProps; + + private Collator collator; + + public FileInfoPropsComparator(List> sortProps) + { + this.sortProps = sortProps; + this.collator = AlfrescoCollator.getInstance(I18NUtil.getContentLocale()); + } + + @Override + public int compare(FileInfo n1, FileInfo n2) + { + return compareImpl(n1, + n2, + sortProps); + } + + private int compareImpl(FileInfo node1In, FileInfo node2In, List> sortProps) + { + Object pv1 = null; + Object pv2 = null; + + QName sortPropQName = (QName) sortProps.get(0).getFirst(); + boolean sortAscending = sortProps.get(0).getSecond(); + + FileInfo node1 = node1In; + FileInfo node2 = node2In; + + if (sortAscending == false) + { + node1 = node2In; + node2 = node1In; + } + + int result = 0; + + pv1 = node1.getProperties().get(sortPropQName); + pv2 = node2.getProperties().get(sortPropQName); + + if (pv1 == null) + { + if (pv2 == null && sortProps.size() > 1) + { + return compareImpl(node1In, + node2In, + sortProps.subList(1, + sortProps.size())); + } + else + { + return (pv2 == null ? 0 : -1); + } + } + else if (pv2 == null) + { + return 1; + } + + if (pv1 instanceof String) + { + result = collator.compare((String) pv1, + (String) pv2); // TODO: use collation keys + // (re: performance) + } + else if (pv1 instanceof Date) + { + result = (((Date) pv1).compareTo((Date) pv2)); + } + else if (pv1 instanceof Long) + { + result = (((Long) pv1).compareTo((Long) pv2)); + } + else if (pv1 instanceof Integer) + { + result = (((Integer) pv1).compareTo((Integer) pv2)); + } + else if (pv1 instanceof QName) + { + result = (((QName) pv1).compareTo((QName) pv2)); + } + else if (pv1 instanceof Boolean) + { + result = (((Boolean) pv1).compareTo((Boolean) pv2)); + } + else + { + throw new RuntimeException("Unsupported sort type: " + pv1.getClass().getName()); + } + + if ((result == 0) && (sortProps.size() > 1)) + { + return compareImpl(node1In, + node2In, + sortProps.subList(1, + sortProps.size())); + } + + return result; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/virtual/bundle/GetPathMethod.java b/source/java/org/alfresco/repo/virtual/bundle/GetPathMethod.java new file mode 100644 index 0000000000..bac01a4b7d --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/bundle/GetPathMethod.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.bundle; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ref.AbstractProtocolMethod; +import org.alfresco.repo.virtual.ref.GetActualNodeRefMethod; +import org.alfresco.repo.virtual.ref.NodeProtocol; +import org.alfresco.repo.virtual.ref.ProtocolMethodException; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.ref.ReferenceEncodingException; +import org.alfresco.repo.virtual.ref.VirtualProtocol; +import org.alfresco.repo.virtual.store.VirtualStore; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.Path; + +public class GetPathMethod extends AbstractProtocolMethod +{ + private VirtualStore virtualStore; + + private ActualEnvironment environment; + + public GetPathMethod(VirtualStore virtualStore, ActualEnvironment actualEnvironment) + { + super(); + this.virtualStore = virtualStore; + this.environment = actualEnvironment; + } + + @Override + public Path execute(VirtualProtocol virtualProtocol, Reference reference) throws ProtocolMethodException + { + try + { + + NodeRef actualNodeRef = reference.execute(new GetActualNodeRefMethod(environment)); + + Path path = null; + if (actualNodeRef == null) + { + // Although not a feature yet, pure-virtual-references should + // use an empty path as root since pure-virtual-references have + // no actual peer to use. + path = new Path(); + } + else + { + path = environment.getPath(actualNodeRef); + } + Path virtualPath = virtualStore.getPath(reference); + return path.append(virtualPath); + } + catch (ReferenceEncodingException e) + { + throw new ProtocolMethodException(e); + } + } + + @Override + public Path execute(NodeProtocol protocol, Reference reference) throws ProtocolMethodException + { + Reference parent = protocol.getVirtualParentReference(reference); + NodeRef nodeRef = protocol.getNodeRef(reference); + Path nodeRefPath = environment.getPath(nodeRef); + Path parentPath = parent.execute(this); + parentPath.append(nodeRefPath.last()); + return parentPath; + } +} diff --git a/source/java/org/alfresco/repo/virtual/bundle/VirtualAccessControlListDAOExtension.java b/source/java/org/alfresco/repo/virtual/bundle/VirtualAccessControlListDAOExtension.java new file mode 100644 index 0000000000..9c888f77e3 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/bundle/VirtualAccessControlListDAOExtension.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.bundle; + +import org.alfresco.repo.domain.permissions.Acl; +import org.alfresco.repo.domain.permissions.traitextender.AccessControlListDAOExtension; +import org.alfresco.repo.domain.permissions.traitextender.AccessControlListDAOTrait; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ref.GetActualNodeRefMethod; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.traitextender.SpringBeanExtension; + +public class VirtualAccessControlListDAOExtension + extends SpringBeanExtension + implements AccessControlListDAOExtension +{ + private ActualEnvironment environment; + + public VirtualAccessControlListDAOExtension() + { + super(AccessControlListDAOTrait.class); + } + + public void setEnvironment(ActualEnvironment environment) + { + this.environment = environment; + } + + @Override + public Acl getAccessControlList(NodeRef nodeRef) + { + + if (Reference.isReference(nodeRef)) + { + Reference vRef = Reference.fromNodeRef(nodeRef); + NodeRef actual = vRef.execute(new GetActualNodeRefMethod(environment)); + return getTrait().getAccessControlList(actual); + } + return getTrait().getAccessControlList(nodeRef); + + } +} diff --git a/source/java/org/alfresco/repo/virtual/bundle/VirtualCheckOutCheckInServiceExtension.java b/source/java/org/alfresco/repo/virtual/bundle/VirtualCheckOutCheckInServiceExtension.java new file mode 100644 index 0000000000..44f6003a9b --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/bundle/VirtualCheckOutCheckInServiceExtension.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.bundle; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.repo.coci.traitextender.CheckOutCheckInServiceExtension; +import org.alfresco.repo.coci.traitextender.CheckOutCheckInServiceTrait; +import org.alfresco.repo.virtual.ref.GetParentReferenceMethod; +import org.alfresco.repo.virtual.ref.NodeProtocol; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.store.VirtualStore; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.traitextender.SpringBeanExtension; + +public class VirtualCheckOutCheckInServiceExtension extends + SpringBeanExtension implements + CheckOutCheckInServiceExtension +{ + + public VirtualCheckOutCheckInServiceExtension() + { + super(CheckOutCheckInServiceTrait.class); + } + + private VirtualStore virtualStore; + + public void setVirtualStore(VirtualStore virtualStore) + { + this.virtualStore = virtualStore; + } + + @Override + public NodeRef checkout(NodeRef nodeRef, NodeRef destinationParentNodeRef, QName destinationAssocTypeQName, + QName destinationAssocQName) + { + CheckOutCheckInServiceTrait theTrait = getTrait(); + NodeRef materialNodeRef = virtualStore.materializeIfPossible(nodeRef); + NodeRef materialDestination = virtualStore.materializeIfPossible(destinationParentNodeRef); + NodeRef workingCopy = theTrait.checkout(materialNodeRef, + materialDestination, + destinationAssocTypeQName, + destinationAssocQName); + + if (Reference.isReference(destinationParentNodeRef)) + { + Reference parentReference = Reference.fromNodeRef(destinationParentNodeRef); + Reference workingCopyReference = NodeProtocol.newReference(workingCopy, + parentReference); + return workingCopyReference.toNodeRef(workingCopy.getStoreRef()); + } + else + { + return workingCopy; + } + + } + + @Override + public NodeRef checkout(NodeRef nodeRef) + { + CheckOutCheckInServiceTrait theTrait = getTrait(); + NodeRef materialNodeRef = virtualStore.materializeIfPossible(nodeRef); + NodeRef workingCopy = theTrait.checkout(materialNodeRef); + + return virtualizeOriginalIfNeeded(nodeRef, + workingCopy); + } + + @Override + public NodeRef checkin(NodeRef workingCopyNodeRef, Map versionProperties, String contentUrl, + boolean keepCheckedOut) + { + CheckOutCheckInServiceTrait theTrait = getTrait(); + NodeRef materialWorkingCopy = virtualStore.materializeIfPossible(workingCopyNodeRef); + NodeRef materialOriginalNode = theTrait.checkin(materialWorkingCopy, + versionProperties, + contentUrl, + keepCheckedOut); + return virtualizeOriginalIfNeeded(workingCopyNodeRef, + materialOriginalNode); + } + + @Override + public NodeRef checkin(NodeRef workingCopyNodeRef, Map versionProperties, String contentUrl) + { + CheckOutCheckInServiceTrait theTrait = getTrait(); + NodeRef materialWorkingCopy = virtualStore.materializeIfPossible(workingCopyNodeRef); + NodeRef materialOriginalNode = theTrait.checkin(materialWorkingCopy, + versionProperties, + contentUrl); + + return virtualizeOriginalIfNeeded(workingCopyNodeRef, + materialOriginalNode); + } + + private NodeRef virtualizeOriginalIfNeeded(NodeRef workingCopyNodeRef, NodeRef materialOriginalNode) + { + if (materialOriginalNode != null && Reference.isReference(workingCopyNodeRef)) + { + Reference workingCopyReference = Reference.fromNodeRef(workingCopyNodeRef); + Reference parentReference = workingCopyReference.execute(new GetParentReferenceMethod()); + Reference originalReference = NodeProtocol.newReference(materialOriginalNode, + parentReference); + return originalReference.toNodeRef(materialOriginalNode.getStoreRef()); + } + else + { + return materialOriginalNode; + } + } + + @Override + public NodeRef checkin(NodeRef workingCopyNodeRef, Map versionProperties) + { + CheckOutCheckInServiceTrait theTrait = getTrait(); + NodeRef materialWorkingCopy = virtualStore.materializeIfPossible(workingCopyNodeRef); + NodeRef materialOriginalNode = theTrait.checkin(materialWorkingCopy, + versionProperties); + + return virtualizeOriginalIfNeeded(workingCopyNodeRef, + materialOriginalNode); + } + + @Override + public NodeRef cancelCheckout(NodeRef workingCopyNodeRef) + { + return getTrait().cancelCheckout(virtualStore.materializeIfPossible(workingCopyNodeRef)); + } + + @Override + public NodeRef getWorkingCopy(NodeRef nodeRef) + { + CheckOutCheckInServiceTrait theTrait = getTrait(); + NodeRef materialWorkingCopy = theTrait.getWorkingCopy(virtualStore.materializeIfPossible(nodeRef)); + + return virtualizeVersionIfNeeded(nodeRef, + materialWorkingCopy); + } + + private NodeRef virtualizeVersionIfNeeded(NodeRef originalNodeRef, NodeRef materialVersion) + { + if (materialVersion != null && Reference.isReference(originalNodeRef) + && !Reference.isReference(materialVersion)) + { + Reference reference = Reference.fromNodeRef(originalNodeRef); + Reference parentReference = reference.execute(new GetParentReferenceMethod()); + Reference workingCopyReference = NodeProtocol.newReference(materialVersion, + parentReference); + return workingCopyReference.toNodeRef(materialVersion.getStoreRef()); + } + else + { + return materialVersion; + } + } + + @Override + public NodeRef getCheckedOut(NodeRef nodeRef) + { + CheckOutCheckInServiceTrait theTrait = getTrait(); + NodeRef materialChekedOut = theTrait.getCheckedOut(virtualStore.materializeIfPossible(nodeRef)); + return virtualizeVersionIfNeeded(nodeRef, + materialChekedOut); + } + + @Override + public boolean isWorkingCopy(NodeRef nodeRef) + { + return getTrait().isWorkingCopy(virtualStore.materializeIfPossible(nodeRef)); + } + + @Override + public boolean isCheckedOut(NodeRef nodeRef) + { + return getTrait().isCheckedOut(virtualStore.materializeIfPossible(nodeRef)); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/bundle/VirtualDefaultCopyBehaviourCallbackExtension.java b/source/java/org/alfresco/repo/virtual/bundle/VirtualDefaultCopyBehaviourCallbackExtension.java new file mode 100644 index 0000000000..177b47af88 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/bundle/VirtualDefaultCopyBehaviourCallbackExtension.java @@ -0,0 +1,40 @@ +package org.alfresco.repo.virtual.bundle; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.copy.CopyDetails; +import org.alfresco.repo.copy.traitextender.DefaultCopyBehaviourCallbackExtension; +import org.alfresco.repo.copy.traitextender.DefaultCopyBehaviourCallbackTrait; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.VirtualContentModel; +import org.alfresco.service.namespace.QName; +import org.alfresco.traitextender.SpringBeanExtension; + +public class VirtualDefaultCopyBehaviourCallbackExtension extends +SpringBeanExtension implements +DefaultCopyBehaviourCallbackExtension +{ + private ActualEnvironment environment; + + public VirtualDefaultCopyBehaviourCallbackExtension() + { + super(DefaultCopyBehaviourCallbackTrait.class); + } + + public void setEnvironment(ActualEnvironment environment) + { + this.environment = environment; + } + + @Override + public boolean getMustCopy(QName classQName, CopyDetails copyDetails) + { + if(environment.isSubClass(classQName, ContentModel.TYPE_FOLDER)){ + if(copyDetails.getSourceNodeAspectQNames().contains(VirtualContentModel.ASPECT_VIRTUAL)){ + return false; + } + } + return getTrait().getMustCopy(classQName, + copyDetails); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/bundle/VirtualFileFolderServiceExtension.java b/source/java/org/alfresco/repo/virtual/bundle/VirtualFileFolderServiceExtension.java new file mode 100644 index 0000000000..6a8852b352 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/bundle/VirtualFileFolderServiceExtension.java @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.bundle; + +import java.io.Serializable; +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.model.filefolder.traitextender.FileFolderServiceExtension; +import org.alfresco.repo.model.filefolder.traitextender.FileFolderServiceTrait; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.repo.virtual.page.PageCollationException; +import org.alfresco.repo.virtual.page.PageCollator; +import org.alfresco.repo.virtual.page.PageCollator.PagingResultsSource; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.ref.ReferenceEncodingException; +import org.alfresco.repo.virtual.store.VirtualStore; +import org.alfresco.service.cmr.model.FileExistsException; +import org.alfresco.service.cmr.model.FileFolderServiceType; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.model.FileNotFoundException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.traitextender.SpringBeanExtension; +import org.alfresco.util.Pair; + +public class VirtualFileFolderServiceExtension extends SpringBeanExtension + implements FileFolderServiceExtension +{ + private VirtualStore virtualStore; + + private ActualEnvironment environment; + + public VirtualFileFolderServiceExtension() + { + super(FileFolderServiceTrait.class); + } + + public void setVirtualStore(VirtualStore virtualStore) + { + this.virtualStore = virtualStore; + } + + public void setEnvironment(ActualEnvironment environment) + { + this.environment = environment; + } + + public List asFileInfos(List references, VirtualStore virtualStore, + ActualEnvironment environment) throws VirtualizationException + { + List fileInfos = new LinkedList<>(); + for (Reference reference : references) + { + + FileInfo fileInfo = asFileInfo(virtualStore, + environment, + reference); + + fileInfos.add(fileInfo); + } + + return fileInfos; + } + + public FileInfo asFileInfo(VirtualStore virtualStore, ActualEnvironment environment, Reference reference) + throws VirtualizationException + { + Map properties = virtualStore.getProperties(reference); + QName qNameType = virtualStore.getType(reference); + FileFolderServiceType type = getTrait().getType(qNameType); + + boolean isFolder = type.equals(FileFolderServiceType.FOLDER); + + NodeRef nodeRef = reference.toNodeRef(); + + return getTrait().createFileInfo(nodeRef, + qNameType, + isFolder, + false, + properties); + } + + @Override + public List list(NodeRef contextNodeRef) + { + if (canVirtualize(contextNodeRef)) + { + Reference reference = virtualStore.virtualize(contextNodeRef); + + List virtualNodes = virtualStore.list(reference); + List searchResult = asFileInfos(virtualNodes, + virtualStore, + environment); + + if (mergeActualNode(reference)) + { + List actualSearch = getTrait().list(actualNodeFrom(reference)); + searchResult.addAll(actualSearch); + } + + return searchResult; + } + else + { + return getTrait().list(contextNodeRef); + } + } + + protected boolean mergeActualNode(Reference reference) throws VirtualizationException + { + return virtualStore.canMaterialize(reference); + } + + protected NodeRef actualNodeFrom(Reference reference) throws VirtualizationException + { + return virtualStore.materialize(reference); + } + + protected boolean canVirtualize(NodeRef nodeRef) throws VirtualizationException + { + return virtualStore.canVirtualize(nodeRef); + } + + private Set[] buildSearchAndIgnore(final boolean files, final boolean folders, Set ignoreQNames) + { + Set[] searchAndIgnore = (Set[]) Array.newInstance(Set.class, + 3); + + Pair, Set> searchTypesAndIgnoreAspects = getTrait() + .buildSearchTypesAndIgnoreAspects(files, + folders, + ignoreQNames); + if (searchTypesAndIgnoreAspects != null) + { + Set searchTypesQNames = searchTypesAndIgnoreAspects.getFirst(); + Set ignoreAspectsQNames = searchTypesAndIgnoreAspects.getSecond(); + + Set ignoreTypesQNames = null; + if ((searchTypesQNames != null || ignoreAspectsQNames != null) && ignoreQNames!=null) + { + ignoreTypesQNames = new HashSet<>(ignoreQNames); + if (searchTypesQNames != null) + { + ignoreTypesQNames.removeAll(searchTypesQNames); + } + if (ignoreAspectsQNames != null) + { + ignoreTypesQNames.removeAll(ignoreAspectsQNames); + } + } + searchAndIgnore[0] = searchTypesQNames; + searchAndIgnore[1] = ignoreTypesQNames; + searchAndIgnore[2] = ignoreAspectsQNames; + } + + return searchAndIgnore; + } + + @Override + public PagingResults list(final NodeRef contextNodeRef, final boolean files, final boolean folders, + final String pattern, final Set ignoreQNames, final List> sortProps, + final PagingRequest pagingRequest) + { + final FileFolderServiceTrait theTrait = getTrait(); + if (canVirtualize(contextNodeRef)) + { + + final Reference reference = virtualStore.virtualize(contextNodeRef); + + Set[] searchAndIgnore = buildSearchAndIgnore(files, + folders, + ignoreQNames); + if (mergeActualNode(reference)) + { + PagingResults virtualChildren = virtualStore.list(reference, + false, + true, + files, + folders, + pattern, + searchAndIgnore[1], + searchAndIgnore[2], + sortProps, + new PagingRequest(0)); + + PagingResultsSource superSource = new PagingResultsSource() + { + @Override + public PagingResults retrieve(PagingRequest pr) throws PageCollationException + { + try + { + PagingResults result = theTrait.list(actualNodeFrom(reference), + files, + folders, + pattern, + ignoreQNames, + sortProps, + pr); + return result; + } + catch (VirtualizationException e) + { + throw new PageCollationException(e); + } + + } + }; + + FileInfoPropsComparator comparator = (sortProps != null && !sortProps.isEmpty()) ? new FileInfoPropsComparator(sortProps) + : null; + + try + { + return new PageCollator().collate(asFileInfoResults(environment, + virtualChildren, + virtualStore).getPage(), + superSource, + pagingRequest, + comparator); + } + catch (PageCollationException error) + { + throw new VirtualizationException(error); + } + + } + else + { + + PagingResults children = virtualStore.list(reference, + true, + true, + files, + folders, + pattern, + searchAndIgnore[1], + searchAndIgnore[2], + sortProps, + pagingRequest); + + return asFileInfoResults(environment, + children, + virtualStore); + + } + + } + else + { + return theTrait.list(contextNodeRef, + files, + folders, + pattern, + ignoreQNames, + sortProps, + pagingRequest); + } + } + + public PagingResults asFileInfoResults(ActualEnvironment environment, + final PagingResults results, VirtualStore store) throws ReferenceEncodingException, + VirtualizationException + { + + List virtualPage = results.getPage(); + + final LinkedList page = new LinkedList(); + + for (Reference ref : virtualPage) + { + + FileInfo fileInfo = asFileInfo(store, + environment, + ref); + page.add(fileInfo); + } + + final boolean hasMoreItems = results.hasMoreItems(); + final Pair totalResultCount = results.getTotalResultCount(); + final String queryExecutionId = results.getQueryExecutionId(); + + return new PagingResults() + { + + @Override + public List getPage() + { + return page; + } + + @Override + public String getQueryExecutionId() + { + return queryExecutionId; + } + + @Override + public Pair getTotalResultCount() + { + return totalResultCount; + } + + @Override + public boolean hasMoreItems() + { + return hasMoreItems; + } + }; + } + + @Override + public PagingResults list(final NodeRef rootNodeRef, final Set searchTypeQNames, + final Set ignoreAspectQNames, final List> sortProps, + final PagingRequest pagingRequest) + { + if (canVirtualize(rootNodeRef)) + { + final Reference reference = virtualStore.virtualize(rootNodeRef); + List> sortingPropoerties = sortProps; + if (sortingPropoerties == null || sortingPropoerties.isEmpty()) + { + sortingPropoerties = Arrays.asList(new Pair(ContentModel.PROP_NAME, + true)); + } + + if (mergeActualNode(reference)) + { + PagingResults virtualChildren = virtualStore.list(reference, + false, + true, + searchTypeQNames, + Collections. emptySet(), + ignoreAspectQNames, + sortProps, + new PagingRequest(0)); + + PagingResultsSource superSource = new PagingResultsSource() + { + @Override + public PagingResults retrieve(PagingRequest pr) throws PageCollationException + { + try + { + PagingResults result = getTrait().list(actualNodeFrom(reference), + searchTypeQNames, + ignoreAspectQNames, + sortProps, + pr); + return result; + } + catch (VirtualizationException e) + { + throw new PageCollationException(e); + } + + } + }; + + FileInfoPropsComparator comparator = new FileInfoPropsComparator(sortingPropoerties); + + try + { + return new PageCollator().collate(asFileInfoResults(environment, + virtualChildren, + virtualStore).getPage(), + superSource, + pagingRequest, + comparator); + } + catch (PageCollationException error) + { + throw new VirtualizationException(error); + } + + } + else + { + PagingResults children = virtualStore.list(reference, + true, + true, + searchTypeQNames, + Collections. emptySet(), + ignoreAspectQNames, + sortingPropoerties, + pagingRequest); + + return asFileInfoResults(environment, + children, + virtualStore); + } + } + + return getTrait().list(rootNodeRef, + searchTypeQNames, + ignoreAspectQNames, + sortProps, + pagingRequest); + } + + @Override + public List search(NodeRef contextNodeRef, String namePattern, boolean includeSubFolders) + { + return search(contextNodeRef, + namePattern, + true, + true, + false); + } + + @Override + public List search(NodeRef contextNodeRef, String namePattern, boolean fileSearch, boolean folderSearch, + boolean includeSubFolders) + { + // We merge the virtual search results wit actual results only in case + // that + // namePattern is null or *, since the right search results are obtained + // from + // searchService.selectNodes() that uses getChildAssociation from + // VirtualNodeService witch was implemented for implementing the + // download-as-zip feature issue + + if (namePattern == null || namePattern.equals("*")) + { + if (canVirtualize(contextNodeRef)) + { + Reference reference = virtualStore.virtualize(contextNodeRef); + List virtualNodes = Collections.emptyList(); + if (!includeSubFolders) + { + virtualNodes = virtualStore.search(reference, + namePattern, + fileSearch, + folderSearch, + false); + } + List searchResult = asFileInfos(virtualNodes, + virtualStore, + environment); + + if (mergeActualNode(reference)) + { + List actualSearch = getTrait().search(actualNodeFrom(reference), + namePattern, + fileSearch, + folderSearch, + includeSubFolders); + searchResult.addAll(actualSearch); + } + + return searchResult; + } + } + return getTrait().search(contextNodeRef, + namePattern, + fileSearch, + folderSearch, + includeSubFolders); + } + + @Override + public FileInfo rename(NodeRef sourceNodeRef, String newName) throws FileExistsException, FileNotFoundException + { + return getTrait().rename(virtualStore.materializeIfPossible(sourceNodeRef), + newName); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/bundle/VirtualLockServiceExtension.java b/source/java/org/alfresco/repo/virtual/bundle/VirtualLockServiceExtension.java new file mode 100644 index 0000000000..0ed2c01bd3 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/bundle/VirtualLockServiceExtension.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.bundle; + +import java.util.Collection; + +import org.alfresco.repo.lock.mem.Lifetime; +import org.alfresco.repo.lock.mem.LockState; +import org.alfresco.repo.lock.traitextender.LockServiceExtension; +import org.alfresco.repo.lock.traitextender.LockServiceTrait; +import org.alfresco.repo.virtual.store.VirtualStore; +import org.alfresco.service.cmr.lock.LockStatus; +import org.alfresco.service.cmr.lock.LockType; +import org.alfresco.service.cmr.lock.UnableToAquireLockException; +import org.alfresco.service.cmr.lock.UnableToReleaseLockException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.traitextender.SpringBeanExtension; + +public class VirtualLockServiceExtension extends SpringBeanExtension + implements LockServiceExtension +{ + private VirtualStore virtualStore; + + public VirtualLockServiceExtension() + { + super(LockServiceTrait.class); + } + + public void setVirtualStore(VirtualStore virtualStore) + { + this.virtualStore = virtualStore; + } + + @Override + public void lock(NodeRef nodeRef, LockType lockType) throws UnableToAquireLockException + { + getTrait().lock(virtualStore.materializeIfPossible(nodeRef), + lockType); + } + + @Override + public void lock(NodeRef nodeRef, LockType lockType, int timeToExpire) throws UnableToAquireLockException + { + getTrait().lock(virtualStore.materializeIfPossible(nodeRef), + lockType, + timeToExpire); + } + + @Override + public void lock(NodeRef nodeRef, LockType lockType, int timeToExpire, Lifetime lifetime) + throws UnableToAquireLockException + { + getTrait().lock(virtualStore.materializeIfPossible(nodeRef), + lockType, + timeToExpire, + lifetime); + } + + @Override + public void lock(NodeRef nodeRef, LockType lockType, int timeToExpire, Lifetime lifetime, String additionalInfo) + throws UnableToAquireLockException + { + getTrait().lock(virtualStore.materializeIfPossible(nodeRef), + lockType, + timeToExpire, + lifetime, + additionalInfo); + } + + @Override + public void lock(NodeRef nodeRef, LockType lockType, int timeToExpire, boolean lockChildren) + throws UnableToAquireLockException + { + getTrait().lock(virtualStore.materializeIfPossible(nodeRef), + lockType, + timeToExpire, + lockChildren); + } + + @Override + public void lock(Collection nodeRefs, LockType lockType, int timeToExpire) + throws UnableToAquireLockException + { + getTrait().lock(virtualStore.materializeIfPossible(nodeRefs), + lockType, + timeToExpire); + } + + @Override + public void unlock(NodeRef nodeRef) throws UnableToReleaseLockException + { + getTrait().unlock(virtualStore.materializeIfPossible(nodeRef)); + } + + @Override + public void unlock(NodeRef nodeRef, boolean lockChildren) throws UnableToReleaseLockException + { + getTrait().unlock(virtualStore.materializeIfPossible(nodeRef), + lockChildren); + } + + @Override + public void unlock(NodeRef nodeRef, boolean lockChildren, boolean allowCheckedOut) + throws UnableToReleaseLockException + { + getTrait().unlock(virtualStore.materializeIfPossible(nodeRef), + lockChildren, + allowCheckedOut); + } + + @Override + public void unlock(Collection nodeRefs) throws UnableToReleaseLockException + { + getTrait().unlock(virtualStore.materializeIfPossible(nodeRefs)); + } + + @Override + public LockStatus getLockStatus(NodeRef nodeRef) + { + return getTrait().getLockStatus(virtualStore.materializeIfPossible(nodeRef)); + } + + @Override + public LockStatus getLockStatus(NodeRef nodeRef, String userName) + { + return getTrait().getLockStatus(virtualStore.materializeIfPossible(nodeRef), + userName); + } + + @Override + public LockType getLockType(NodeRef nodeRef) + { + return getTrait().getLockType(virtualStore.materializeIfPossible(nodeRef)); + } + + @Override + public void checkForLock(NodeRef nodeRef) + { + getTrait().checkForLock(virtualStore.materializeIfPossible(nodeRef)); + } + + @Override + public void suspendLocks() + { + getTrait().suspendLocks(); + } + + @Override + public void enableLocks() + { + getTrait().enableLocks(); + } + + @Override + public String getAdditionalInfo(NodeRef nodeRef) + { + return getTrait().getAdditionalInfo(virtualStore.materializeIfPossible(nodeRef)); + } + + @Override + public LockState getLockState(NodeRef nodeRef) + { + return getTrait().getLockState(virtualStore.materializeIfPossible(nodeRef)); + } + + @Override + public void setEphemeralExpiryThreshold(int threshSecs) + { + getTrait().setEphemeralExpiryThreshold(threshSecs); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/bundle/VirtualLockableAspectInterceptorExtension.java b/source/java/org/alfresco/repo/virtual/bundle/VirtualLockableAspectInterceptorExtension.java new file mode 100644 index 0000000000..f63a8bba7e --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/bundle/VirtualLockableAspectInterceptorExtension.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.bundle; + +import org.alfresco.repo.lock.mem.LockState; +import org.alfresco.repo.lock.traitextender.LockableAspectInterceptorExtension; +import org.alfresco.repo.lock.traitextender.LockableAspectInterceptorTrait; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ref.GetActualNodeRefMethod; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.traitextender.SpringBeanExtension; + +public class VirtualLockableAspectInterceptorExtension extends + SpringBeanExtension implements + LockableAspectInterceptorExtension +{ + + private ActualEnvironment environment; + + public VirtualLockableAspectInterceptorExtension() + { + super(LockableAspectInterceptorTrait.class); + } + + public void setEnvironment(ActualEnvironment environment) + { + this.environment = environment; + } + + @Override + public LockState getLockState(NodeRef nodeRef) + { + if(Reference.isReference(nodeRef)){ + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef actualNodeRef = reference.execute(new GetActualNodeRefMethod(environment)); + return getTrait().traitImplOf_getLockState(actualNodeRef); + } + return getTrait().traitImplOf_getLockState(nodeRef); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/bundle/VirtualNodeServiceExtension.java b/source/java/org/alfresco/repo/virtual/bundle/VirtualNodeServiceExtension.java new file mode 100644 index 0000000000..34171b5393 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/bundle/VirtualNodeServiceExtension.java @@ -0,0 +1,1522 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.bundle; + +import java.io.IOException; +import java.io.Serializable; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.download.DownloadModel; +import org.alfresco.repo.node.archive.NodeArchiveService; +import org.alfresco.repo.node.db.traitextender.NodeServiceExtension; +import org.alfresco.repo.node.db.traitextender.NodeServiceTrait; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ActualEnvironmentException; +import org.alfresco.repo.virtual.VirtualContentModel; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.repo.virtual.config.NodeRefExpression; +import org.alfresco.repo.virtual.ref.GetActualNodeRefMethod; +import org.alfresco.repo.virtual.ref.GetAspectsMethod; +import org.alfresco.repo.virtual.ref.GetParentReferenceMethod; +import org.alfresco.repo.virtual.ref.NodeProtocol; +import org.alfresco.repo.virtual.ref.Protocols; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.store.VirtualStore; +import org.alfresco.repo.virtual.template.FilingData; +import org.alfresco.service.cmr.dictionary.InvalidAspectException; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.InvalidChildAssociationRefException; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.InvalidStoreRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeRef.Status; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.repository.StoreExistsException; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.QNamePattern; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.traitextender.SpringBeanExtension; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class VirtualNodeServiceExtension extends SpringBeanExtension + implements NodeServiceExtension +{ + private static Log logger = LogFactory.getLog(VirtualNodeServiceExtension.class); + + private VirtualStore virtualStore; + + private ActualEnvironment environment; + + private NodeRefExpression downloadAssociationsFolder; + + public VirtualNodeServiceExtension() + { + super(NodeServiceTrait.class); + } + + public void setEnvironment(ActualEnvironment environment) + { + this.environment = environment; + } + + public void setVirtualStore(VirtualStore virtualStore) + { + this.virtualStore = virtualStore; + } + + @Override + public boolean hasAspect(NodeRef nodeRef, QName aspectQName) + { + if (Reference.isReference(nodeRef)) + { + if (VirtualContentModel.ASPECT_VIRTUAL.equals(aspectQName)) + { + return true; + } + else if (VirtualContentModel.ASPECT_VIRTUAL_DOCUMENT.equals(aspectQName) && Reference.fromNodeRef(nodeRef).getProtocol().equals(Protocols.NODE.protocol)) + { + return true; + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef actualNodeRef = reference.execute(new GetActualNodeRefMethod(environment)); + return getTrait().hasAspect(actualNodeRef, + aspectQName); + } + } + else + { + return getTrait().hasAspect(nodeRef, + aspectQName); + } + } + + @Override + public QName getType(NodeRef nodeRef) + { + if (Reference.isReference(nodeRef)) + { + QName type = virtualStore.getType(Reference.fromNodeRef(nodeRef)); + return type; + } + else + { + return getTrait().getType(nodeRef); + } + } + + private Map getVirtualProperties(Reference reference) + { + return virtualStore.getProperties(reference); + } + + @Override + public Map getProperties(NodeRef nodeRef) + { + if (Reference.isReference(nodeRef)) + { + return getVirtualProperties(Reference.fromNodeRef(nodeRef)); + + } + else + { + return getTrait().getProperties(nodeRef); + } + } + + @Override + public Serializable getProperty(NodeRef nodeRef, QName qname) + { + if (Reference.isReference(nodeRef)) + { + return getVirtualProperties(Reference.fromNodeRef(nodeRef)).get(qname); + } + else + { + return getTrait().getProperty(nodeRef, + qname); + } + } + + @Override + public Set getAspects(NodeRef nodeRef) + { + NodeServiceTrait theTrait = getTrait(); + if (Reference.isReference(nodeRef)) + { + Reference vRef = Reference.fromNodeRef(nodeRef); + GetAspectsMethod method = new GetAspectsMethod(theTrait, + environment); + + return vRef.execute(method); + } + else + { + return theTrait.getAspects(nodeRef); + } + } + + @Override + public Path getPath(NodeRef nodeRef) + { + if (Reference.isReference(nodeRef)) + { + return Reference.fromNodeRef(nodeRef).execute(new GetPathMethod(virtualStore, + environment)); + } + else + { + return getTrait().getPath(nodeRef); + } + } + + @Override + public List getPaths(NodeRef nodeRef, boolean primaryOnly) + { + if (Reference.isReference(nodeRef)) + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef actualNodeRef = reference.execute(new GetActualNodeRefMethod(environment)); + return getTrait().getPaths(actualNodeRef, + primaryOnly); + } + else + { + return getTrait().getPaths(nodeRef, + primaryOnly); + } + } + + @Override + public boolean exists(NodeRef nodeRef) + { + if (Reference.isReference(nodeRef)) + { + // For now references last forever (i.e. there is no expiration + // mechanism ) + return true; + } + else + { + return getTrait().exists(nodeRef); + } + } + + @Override + public ChildAssociationRef createNode(NodeRef parentRef, QName assocTypeQName, QName assocQName, + QName nodeTypeQName) + { + if (Reference.isReference(parentRef)) + { + return createNode(parentRef, + assocTypeQName, + assocQName, + nodeTypeQName, + Collections. emptyMap()); + } + else + { + QName materialAssocQName = materializeAssocQName(assocQName); + return getTrait().createNode(parentRef, + assocTypeQName, + materialAssocQName, + nodeTypeQName); + } + } + + private boolean isVirtualContextFolder(NodeRef nodeRef) + { + + boolean isReference=Reference.isReference(nodeRef); + boolean isFolder=environment.isSubClass(environment.getType(nodeRef), + ContentModel.TYPE_FOLDER); + boolean virtualContext=hasAspect(nodeRef,VirtualContentModel.ASPECT_VIRTUAL_DOCUMENT); + return isReference && isFolder && virtualContext; + } + + @Override + public ChildAssociationRef createNode(NodeRef parentRef, QName assocTypeQName, QName assocQName, + QName nodeTypeQName, Map properties) + { + NodeServiceTrait theTrait = getTrait(); + if (Reference.isReference(parentRef) && !isVirtualContextFolder(parentRef)) + { + // CM-533 Suppress options to create folders in a virtual folder + // (repo) + if (environment.isSubClass(nodeTypeQName, + ContentModel.TYPE_FOLDER)) + { + throw new VirtualizationException("The creation of folders within virtual folders is disabled."); + } + + try + { + Reference parentReference = Reference.fromNodeRef(parentRef); + FilingData filingData = virtualStore.createFilingData(parentReference, + assocTypeQName, + assocQName, + nodeTypeQName, + properties); + + NodeRef childParentNodeRef = filingData.getFilingNodeRef(); + + if (childParentNodeRef != null) + { + Map filingDataProperties = filingData.getProperties(); + QName changedAssocQName = assocQName; + if (filingDataProperties.containsKey(ContentModel.PROP_NAME)) + { + String fileName = (String) filingDataProperties.get(ContentModel.PROP_NAME); + String changedFileName = handleExistingFile(childParentNodeRef, + fileName); + if (!changedFileName.equals(fileName)) + { + filingDataProperties.put(ContentModel.PROP_NAME, + changedFileName); + QName filingDataAssocQName = filingData.getAssocQName(); + changedAssocQName = QName.createQName(filingDataAssocQName.getNamespaceURI(), + QName.createValidLocalName(changedFileName)); + } + } + ChildAssociationRef actualChildAssocRef = theTrait.createNode(childParentNodeRef, + filingData.getAssocTypeQName(), + changedAssocQName == null + ? filingData.getAssocQName() + : changedAssocQName, + filingData.getNodeTypeQName(), + filingDataProperties); + + Reference nodeProtocolChildRef = NodeProtocol.newReference(actualChildAssocRef.getChildRef(), + parentReference); + QName vChildAssocQName = QName + .createQNameWithValidLocalName(VirtualContentModel.VIRTUAL_CONTENT_MODEL_1_0_URI, + actualChildAssocRef.getQName().getLocalName()); + ChildAssociationRef childAssocRef = new ChildAssociationRef(actualChildAssocRef.getTypeQName(), + parentRef, + vChildAssocQName, + nodeProtocolChildRef.toNodeRef()); + Set aspects = filingData.getAspects(); + + for (QName aspect : aspects) + { + theTrait.addAspect(actualChildAssocRef.getChildRef(), + aspect, + filingDataProperties); + } + + return childAssocRef; + } + else + { + throw new InvalidNodeRefException("Can not create node using parent ", + parentRef); + } + + } + catch (VirtualizationException e) + { + throw new InvalidNodeRefException("Could not create node in virtual context.", + parentRef, + e); + } + } + else + { + QName materialAssocQName = materializeAssocQName(assocQName); + if(isVirtualContextFolder(parentRef)){ + parentRef=virtualStore.materializeIfPossible(parentRef); + } + return theTrait.createNode(parentRef, + assocTypeQName, + materialAssocQName, + nodeTypeQName, + properties); + } + } + + private QName materializeAssocQName(QName assocQName) + { + // Version nodes have too long assocQNames so we try + // to detect references with "material" protocols in order to + // replace the assocQNames with material correspondents. + try + { + String lName = assocQName.getLocalName(); + NodeRef nrAssocQName = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, + lName); + if (Reference.isReference(nrAssocQName)) + { + nrAssocQName = virtualStore.materializeIfPossible(nrAssocQName); + QName materialAssocQName = QName.createQName(assocQName.getNamespaceURI(), + nrAssocQName.getId()); + return materialAssocQName; + } + else + { + return assocQName; + } + } + catch (VirtualizationException e) + { + // We assume it can not be put through the + // isReference-virtualize-materialize. + if (logger.isDebugEnabled()) + { + logger.debug("Defaulting on materializeAssocQName due to error.", + e); + } + return assocQName; + } + } + + private String handleExistingFile(NodeRef parentNodeRef, String fileName) + { + NodeServiceTrait actualNodeService = getTrait(); + NodeRef existingFile = actualNodeService.getChildByName(parentNodeRef, + ContentModel.ASSOC_CONTAINS, + fileName); + if (existingFile != null) + { + int counter = 1; + int dotIndex; + String tmpFilename = ""; + final String dot = "."; + final String hyphen = "-"; + + while (existingFile != null) + { + int beforeCounter = fileName.lastIndexOf(hyphen); + dotIndex = fileName.lastIndexOf(dot); + if (dotIndex == 0) + { + // File didn't have a proper 'name' instead it had just a + // suffix and + // started with a ".", create "1.txt" + tmpFilename = counter + fileName; + } + else if (dotIndex > 0) + { + + if (beforeCounter > 0 && beforeCounter < dotIndex) + { + // does file have counter in it's name or it just + // contains -1 + String originalFileName = fileName.substring(0, + beforeCounter) + + fileName.substring(dotIndex); + boolean doesOriginalFileExist = actualNodeService.getChildByName(parentNodeRef, + ContentModel.ASSOC_CONTAINS, + originalFileName) != null; + if (doesOriginalFileExist) + { + String counterStr = fileName.substring(beforeCounter + 1, + dotIndex); + try + { + int parseInt = DefaultTypeConverter.INSTANCE.intValue(counterStr); + counter = parseInt + 1; + fileName = fileName.substring(0, + beforeCounter) + + fileName.substring(dotIndex); + dotIndex = fileName.lastIndexOf(dot); + + } + catch (NumberFormatException ex) + { + // "-" is not before counter + } + + } + } + tmpFilename = fileName.substring(0, + dotIndex) + + hyphen + counter + fileName.substring(dotIndex); + } + else + { + // Filename didn't contain a dot at all, create "filename-1" + tmpFilename = fileName + hyphen + counter; + } + existingFile = actualNodeService.getChildByName(parentNodeRef, + ContentModel.ASSOC_CONTAINS, + tmpFilename); + counter++; + } + fileName = tmpFilename; + } + + return fileName; + } + + @Override + public List getParentAssocs(NodeRef nodeRef) + { + if (Reference.isReference(nodeRef)) + { + return getParentAssocs(nodeRef, + RegexQNamePattern.MATCH_ALL, + RegexQNamePattern.MATCH_ALL); + } + else + { + return getTrait().getParentAssocs(nodeRef); + } + } + + @Override + public List getParentAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, + QNamePattern qnamePattern) + { + NodeServiceTrait theTrait = getTrait(); + if (Reference.isReference(nodeRef)) + { + Reference reference = Reference.fromNodeRef(nodeRef); + Reference parent = reference.execute(new GetParentReferenceMethod()); + if (parent == null) + { + return theTrait.getParentAssocs(reference.execute(new GetActualNodeRefMethod(environment)), + typeQNamePattern, + qnamePattern); + } + else + { + if (typeQNamePattern.isMatch(ContentModel.ASSOC_CONTAINS)) + { + Reference parentsParent = parent.execute(new GetParentReferenceMethod()); + + NodeRef parentNodeRef = parent.toNodeRef(); + if (parentsParent == null) + { + parentNodeRef = parent.execute(new GetActualNodeRefMethod(environment)); + + } + + NodeRef referenceNodeRef = reference.toNodeRef(); + Map properties = virtualStore.getProperties(reference); + Serializable name = properties.get(ContentModel.PROP_NAME); + QName assocChildName = QName + .createQNameWithValidLocalName(VirtualContentModel.VIRTUAL_CONTENT_MODEL_1_0_URI, + name.toString()); + if (qnamePattern.isMatch(assocChildName)) + { + ChildAssociationRef assoc = new ChildAssociationRef(ContentModel.ASSOC_CONTAINS, + parentNodeRef, + assocChildName, + referenceNodeRef); + return Arrays.asList(assoc); + } + else + { + return Collections.emptyList(); + } + } + else + { + return Collections.emptyList(); + } + } + } + else + { + return theTrait.getParentAssocs(nodeRef, + typeQNamePattern, + qnamePattern); + } + } + + @Override + public List getChildAssocs(NodeRef nodeRef) + { + NodeServiceTrait theTrait = getTrait(); + boolean canVirtualize = canVirtualizeAssocNodeRef(nodeRef); + if (canVirtualize) + { + Reference reference = virtualStore.virtualize(nodeRef); + List virtualAssociations = virtualStore.getChildAssocs(reference, + RegexQNamePattern.MATCH_ALL, + RegexQNamePattern.MATCH_ALL, + Integer.MAX_VALUE, + false); + List associations = new LinkedList<>(virtualAssociations); + if (virtualStore.canMaterialize(reference)) + { + NodeRef materialReference = virtualStore.materialize(reference); + List actualAssociations = theTrait.getChildAssocs(materialReference, + RegexQNamePattern.MATCH_ALL, + RegexQNamePattern.MATCH_ALL, + Integer.MAX_VALUE, + false); + associations.addAll(actualAssociations); + } + + return associations; + } + else + { + return theTrait.getChildAssocs(nodeRef); + } + } + + @Override + public List getChildAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, + QNamePattern qnamePattern) + { + NodeServiceTrait theTrait = getTrait(); + boolean canVirtualize = canVirtualizeAssocNodeRef(nodeRef); + if (canVirtualize) + { + Reference reference = virtualStore.virtualize(nodeRef); + List virtualAssociations = virtualStore.getChildAssocs(reference, + typeQNamePattern, + qnamePattern, + Integer.MAX_VALUE, + false); + List associations = new LinkedList<>(virtualAssociations); + + if (virtualStore.canMaterialize(reference)) + { + NodeRef materialReference = virtualStore.materialize(reference); + List actualAssociations = theTrait.getChildAssocs(materialReference, + typeQNamePattern, + qnamePattern); + associations.addAll(actualAssociations); + } + return associations; + } + else + { + return theTrait.getChildAssocs(nodeRef, + typeQNamePattern, + qnamePattern); + } + } + + @Override + public List getChildAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, + QNamePattern qnamePattern, int maxResults, boolean preload) + { + NodeServiceTrait theTrait = getTrait(); + boolean canVirtualize = canVirtualizeAssocNodeRef(nodeRef); + if (canVirtualize) + { + Reference reference = virtualStore.virtualize(nodeRef); + List virtualAssociations = virtualStore.getChildAssocs(reference, + typeQNamePattern, + qnamePattern, + maxResults, + preload); + List associations = new LinkedList<>(virtualAssociations); + + if (associations.size() < maxResults) + { + if (virtualStore.canMaterialize(reference)) + { + NodeRef materialReference = virtualStore.materialize(reference); + List actualAssociations = theTrait.getChildAssocs(materialReference, + typeQNamePattern, + qnamePattern, + maxResults - associations + .size(), + preload); + associations.addAll(actualAssociations); + } + } + return associations; + } + else + { + return theTrait.getChildAssocs(nodeRef, + typeQNamePattern, + qnamePattern, + maxResults, + preload); + } + } + + private boolean canVirtualizeAssocNodeRef(NodeRef nodeRef) + { + boolean canVirtualize = nodeRef.getId().contains("solr") ? false : virtualStore.canVirtualize(nodeRef); + return canVirtualize; + } + + @Override + public List getChildAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, + QNamePattern qnamePattern, boolean preload) + { + NodeServiceTrait theTrait = getTrait(); + boolean canVirtualize = canVirtualizeAssocNodeRef(nodeRef); + if (canVirtualize) + { + Reference reference = virtualStore.virtualize(nodeRef); + List virtualAssociations = virtualStore.getChildAssocs(reference, + typeQNamePattern, + qnamePattern, + Integer.MAX_VALUE, + preload); + List associations = new LinkedList<>(virtualAssociations); + + if (virtualStore.canMaterialize(reference)) + { + NodeRef materialReference = virtualStore.materialize(reference); + List actualAssociations = theTrait.getChildAssocs(materialReference, + typeQNamePattern, + qnamePattern, + preload); + associations.addAll(actualAssociations); + } + return associations; + } + else + { + return theTrait.getChildAssocs(nodeRef, + typeQNamePattern, + qnamePattern, + preload); + } + } + + @Override + public List getChildAssocs(NodeRef nodeRef, Set childNodeTypeQNames) + { + NodeServiceTrait theTrait = getTrait(); + boolean canVirtualize = canVirtualizeAssocNodeRef(nodeRef); + if (canVirtualize) + { + Reference reference = virtualStore.virtualize(nodeRef); + List virtualAssociations = virtualStore.getChildAssocs(reference, + childNodeTypeQNames); + List associations = new LinkedList<>(virtualAssociations); + if (virtualStore.canMaterialize(reference)) + { + NodeRef materialReference = virtualStore.materialize(reference); + List actualAssociations = theTrait.getChildAssocs(materialReference, + childNodeTypeQNames); + associations.addAll(actualAssociations); + } + + return associations; + } + else + { + return theTrait.getChildAssocs(nodeRef, + childNodeTypeQNames); + } + } + + @Override + public List getChildAssocsByPropertyValue(NodeRef nodeRef, QName propertyQName, + Serializable value) + { + NodeServiceTrait theTrait = getTrait(); + boolean canVirtualize = canVirtualizeAssocNodeRef(nodeRef); + if (canVirtualize) + { + Reference reference = virtualStore.virtualize(nodeRef); + List virtualAssociations = virtualStore.getChildAssocsByPropertyValue(reference, + propertyQName, + value); + List associations = new LinkedList<>(virtualAssociations); + if (virtualStore.canMaterialize(reference)) + { + NodeRef materialReference = virtualStore.materialize(reference); + List actualAssociations = theTrait.getChildAssocsByPropertyValue(materialReference, + propertyQName, + value); + associations.addAll(actualAssociations); + } + + return associations; + } + else + { + return theTrait.getChildAssocsByPropertyValue(nodeRef, + propertyQName, + value); + } + } + + @Override + public NodeRef getChildByName(NodeRef nodeRef, QName assocTypeQName, String childName) + { + // TODO: optimize + + if (virtualStore.canVirtualize(nodeRef)) + { + Reference virtualNode = virtualStore.virtualize(nodeRef); + + Reference theChild = virtualStore.getChildByName(virtualNode, + assocTypeQName, + childName); + + if (theChild != null) + { + NodeRef childNodeRef = theChild.toNodeRef(); + + return childNodeRef; + } + if (virtualStore.isVirtual(nodeRef)) + { + return null; + } + } + + // TODO: add virtualizable enabler + nodeRef = materializeIfPossible(nodeRef); + return getTrait().getChildByName(nodeRef, + assocTypeQName, + childName); + } + + @Override + public ChildAssociationRef getPrimaryParent(NodeRef nodeRef) + { + if (Reference.isReference(nodeRef)) + { + Reference reference = Reference.fromNodeRef(nodeRef); + Reference parent = reference.execute(new GetParentReferenceMethod()); + if (parent == null) + { + return getTrait().getPrimaryParent(reference.execute(new GetActualNodeRefMethod(environment))); + } + else + { + Reference parentsParent = parent.execute(new GetParentReferenceMethod()); + + NodeRef parentNodeRef = parent.toNodeRef(); + if (parentsParent == null) + { + parentNodeRef = parent.execute(new GetActualNodeRefMethod(environment)); + + } + + NodeRef referenceNodeRef = reference.toNodeRef(); + ChildAssociationRef assoc = new ChildAssociationRef(ContentModel.ASSOC_CONTAINS, + parentNodeRef, + NodeArchiveService.QNAME_ARCHIVED_ITEM, + referenceNodeRef, + true, + -1); + return assoc; + } + } + else + { + return getTrait().getPrimaryParent(nodeRef); + } + } + + public void setDownloadAssociationsFolder(NodeRefExpression downloadAssocaiationsFolder) + { + this.downloadAssociationsFolder = downloadAssocaiationsFolder; + } + + private void cleanUpDownloadTargetAssocs(NodeRef sourceNodeRef) + { + + NodeRef tempFolderNodeRef = downloadAssociationsFolder.resolve(); + if (tempFolderNodeRef == null) + { + return; + } + + String tempFileName = sourceNodeRef.getId(); + + NodeRef tempFileNodeRef = environment.getChildByName(tempFolderNodeRef, + ContentModel.ASSOC_CONTAINS, + tempFileName); + if (tempFileNodeRef == null) + { + return; + } + + environment.delete(tempFileNodeRef); + } + + private List getDownloadTargetAssocs(NodeRef sourceNodeRef) + { + try + { + List result = new ArrayList(); + + NodeRef tempFolderNodeRef = downloadAssociationsFolder.resolve(); + if (tempFolderNodeRef == null) + { + return result; + } + + String tempFileName = sourceNodeRef.getId(); + + NodeRef tempFileNodeRef = environment.getChildByName(tempFolderNodeRef, + ContentModel.ASSOC_CONTAINS, + tempFileName); + if (tempFileNodeRef == null) + { + return result; + } + List readLines = IOUtils.readLines(environment.openContentStream(tempFileNodeRef), + StandardCharsets.UTF_8); + for (String line : readLines) + { + NodeRef targetRef = new NodeRef(line); + AssociationRef assocRef = new AssociationRef(sourceNodeRef, + DownloadModel.ASSOC_REQUESTED_NODES, + targetRef); + result.add(assocRef); + } + + return result; + } + catch (IOException e) + { + throw new VirtualizationException(e); + } + } + + private void createDownloadAssociation(NodeRef sourceNodeRef, NodeRef targetRef) + { + + NodeRef tempFolderNodeRef = downloadAssociationsFolder.resolve(true); + + String tempFileName = sourceNodeRef.getId(); + NodeRef tempFileNodeRef = environment.getChildByName(tempFolderNodeRef, + ContentModel.ASSOC_CONTAINS, + tempFileName); + if (tempFileNodeRef == null) + { + FileInfo newTempFileInfo = environment.create(tempFolderNodeRef, + tempFileName, + ContentModel.TYPE_CONTENT); + tempFileNodeRef = newTempFileInfo.getNodeRef(); + ContentWriter writer = environment.getWriter(tempFileNodeRef, + ContentModel.PROP_CONTENT, + true); + writer.setMimetype("text/plain"); + writer.putContent(targetRef.toString()); + + } + else + { + ContentWriter writer = environment.getWriter(tempFileNodeRef, + ContentModel.PROP_CONTENT, + true); + try + { + List readLines = IOUtils.readLines(environment.openContentStream(tempFileNodeRef), + StandardCharsets.UTF_8); + + String targetRefString = targetRef.toString(); + if (!readLines.contains(targetRefString)) + { + readLines.add(targetRefString); + } + String text = ""; + for (String line : readLines) + { + if (text.isEmpty()) + { + text = line; + } + else + { + text = text + IOUtils.LINE_SEPARATOR + line; + } + + } + writer.putContent(text); + } + catch (IOException e) + { + throw new ActualEnvironmentException(e); + } + + } + + } + + @Override + public List getTargetAssocs(NodeRef sourceRef, QNamePattern qnamePattern) + { + NodeServiceTrait theTrait = getTrait(); + List targetAssocs = null; + + if (Reference.isReference(sourceRef)) + { + Reference reference = Reference.fromNodeRef(sourceRef); + if (virtualStore.canMaterialize(reference)) + { + NodeRef materializedReferece = virtualStore.materialize(reference); + targetAssocs = theTrait.getTargetAssocs(materializedReferece, + qnamePattern); + } + else + { + targetAssocs = new LinkedList<>(); + } + } + else + { + targetAssocs = theTrait.getTargetAssocs(sourceRef, + qnamePattern); + } + + List virtualizedIfNeededTargetAssocs = null; + + if (Reference.isReference(sourceRef)) + { + virtualizedIfNeededTargetAssocs = new LinkedList<>(); + + Reference sourceReference = Reference.fromNodeRef(sourceRef); + + for (AssociationRef associationRef : targetAssocs) + { + NodeRef targetNodeRef = associationRef.getTargetRef(); + Reference targetReference = NodeProtocol.newReference(targetNodeRef, + sourceReference + .execute(new GetParentReferenceMethod())); + AssociationRef virtualAssocRef = new AssociationRef(associationRef.getId(), + sourceRef, + associationRef.getTypeQName(), + targetReference.toNodeRef(targetNodeRef + .getStoreRef())); + virtualizedIfNeededTargetAssocs.add(virtualAssocRef); + } + + } + else + { + virtualizedIfNeededTargetAssocs = targetAssocs; + + if (DownloadModel.TYPE_DOWNLOAD.equals(environment.getType(sourceRef))) + { + if (qnamePattern.isMatch(DownloadModel.ASSOC_REQUESTED_NODES)) + { + List virtualTargetAssocs = getDownloadTargetAssocs(sourceRef); + virtualizedIfNeededTargetAssocs.addAll(virtualTargetAssocs); + } + } + } + + return virtualizedIfNeededTargetAssocs; + } + + @Override + public List getSourceAssocs(NodeRef targetRef, QNamePattern qnamePattern) + { + NodeServiceTrait theTrait = getTrait(); + + if (Reference.isReference(targetRef)) + { + Reference reference = Reference.fromNodeRef(targetRef); + + List materialAssocs = new ArrayList(); + + if (virtualStore.canMaterialize(reference)) + { + List sourceAssocs = theTrait.getSourceAssocs(virtualStore.materialize(reference), + qnamePattern); + for (AssociationRef associationRef : sourceAssocs) + { + NodeRef sourceRef = associationRef.getSourceRef(); + Reference sourceReference = NodeProtocol.newReference(sourceRef, + reference + .execute(new GetParentReferenceMethod())); + AssociationRef virtualAssocRef = new AssociationRef(associationRef.getId(), + sourceReference.toNodeRef(), + associationRef.getTypeQName(), + targetRef); + materialAssocs.add(virtualAssocRef); + } + } + + // Download sources are deliberately not virtualized due to + // performance and complexity issues. + // However they could be detected using + // if (qnamePattern.isMatch(DownloadModel.ASSOC_REQUESTED_NODES)) + + return materialAssocs; + } + else + { + return theTrait.getSourceAssocs(targetRef, + qnamePattern); + } + } + + @Override + public ChildAssociationRef moveNode(NodeRef nodeToMoveRef, NodeRef newParentRef, QName assocTypeQName, + QName assocQName) + { + if (Reference.isReference(nodeToMoveRef) || Reference.isReference(newParentRef)) + { + throw new UnsupportedOperationException("Unsuported operation for virtual source or destination"); + } + else + { + return getTrait().moveNode(nodeToMoveRef, + newParentRef, + assocTypeQName, + assocQName); + } + } + + @Override + public void addProperties(NodeRef nodeRef, Map properties) + { + if (Reference.isReference(nodeRef)) + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef actualNodeRef = reference.execute(new GetActualNodeRefMethod(null)); + + getTrait().addProperties(actualNodeRef, + properties); + } + else + { + getTrait().addProperties(nodeRef, + properties); + } + } + + @Override + public AssociationRef createAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) + { + if (Reference.isReference(targetRef) + && getTrait().getType(materializeIfPossible(sourceRef)).equals(DownloadModel.TYPE_DOWNLOAD)) + { + // NOTE : this is enables downloads of virtual structures + createDownloadAssociation(sourceRef, + targetRef); + + AssociationRef assocRef = new AssociationRef(sourceRef, + assocTypeQName, + targetRef); + return assocRef; + } + else + { + return getTrait().createAssociation(materializeIfPossible(sourceRef), + materializeIfPossible(targetRef), + assocTypeQName); + } + } + + private List materializeIfPossible(Collection nodeRefs) + { + List nodeRefList = new LinkedList<>(); + for (NodeRef nodeRef : nodeRefs) + { + nodeRefList.add(materializeIfPossible(nodeRef)); + } + + return nodeRefList; + } + + /** + * @deprecated use {@link VirtualStore#materializeIfPossible(NodeRef)} + * instead + */ + private NodeRef materializeIfPossible(NodeRef nodeRef) + { + if (Reference.isReference(nodeRef)) + { + Reference ref = Reference.fromNodeRef(nodeRef); + if (virtualStore.canMaterialize(ref)) + { + return virtualStore.materialize(ref); + } + + } + return nodeRef; + } + + @Override + public List getStores() + { + return getTrait().getStores(); + } + + @Override + public StoreRef createStore(String protocol, String identifier) throws StoreExistsException + { + return getTrait().createStore(protocol, + identifier); + } + + @Override + public void deleteStore(StoreRef storeRef) + { + getTrait().deleteStore(storeRef); + } + + @Override + public boolean exists(StoreRef storeRef) + { + return getTrait().exists(storeRef); + } + + @Override + public Status getNodeStatus(NodeRef nodeRef) + { + return getTrait().getNodeStatus(materializeIfPossible(nodeRef)); + } + + @Override + public NodeRef getNodeRef(Long nodeId) + { + return getTrait().getNodeRef(nodeId); + } + + @Override + public NodeRef getRootNode(StoreRef storeRef) throws InvalidStoreRefException + { + return getTrait().getRootNode(storeRef); + } + + @Override + public Set getAllRootNodes(StoreRef storeRef) + { + return getTrait().getAllRootNodes(storeRef); + } + + @Override + public void setChildAssociationIndex(ChildAssociationRef childAssocRef, int index) + throws InvalidChildAssociationRefException + { + getTrait().setChildAssociationIndex(childAssocRef, + index); + } + + @Override + public void setType(NodeRef nodeRef, QName typeQName) throws InvalidNodeRefException + { + getTrait().setType(materializeIfPossible(nodeRef), + typeQName); + } + + @Override + public void addAspect(NodeRef nodeRef, QName aspectTypeQName, Map aspectProperties) + throws InvalidNodeRefException, InvalidAspectException + { + getTrait().addAspect(materializeIfPossible(nodeRef), + aspectTypeQName, + aspectProperties); + } + + @Override + public void removeAspect(NodeRef nodeRef, QName aspectTypeQName) + throws InvalidNodeRefException, InvalidAspectException + { + getTrait().removeAspect(materializeIfPossible(nodeRef), + aspectTypeQName); + } + + @Override + public void deleteNode(NodeRef nodeRef) throws InvalidNodeRefException + { + NodeServiceTrait theTrait = getTrait(); + NodeRef materialNode = virtualStore.materializeIfPossible(nodeRef); + boolean isDownload = DownloadModel.TYPE_DOWNLOAD.equals(theTrait.getType(materialNode)); + theTrait.deleteNode(materialNode); + if (isDownload) + { + cleanUpDownloadTargetAssocs(nodeRef); + } + } + + @Override + public ChildAssociationRef addChild(NodeRef parentRef, NodeRef childRef, QName assocTypeQName, QName qname) + throws InvalidNodeRefException + { + return getTrait().addChild(materializeIfPossible(parentRef), + materializeIfPossible(childRef), + assocTypeQName, + qname); + } + + @Override + public List addChild(Collection parentRefs, NodeRef childRef, QName assocTypeQName, + QName qname) throws InvalidNodeRefException + { + return getTrait().addChild(materializeIfPossible(parentRefs), + materializeIfPossible(childRef), + assocTypeQName, + qname); + } + + @Override + public void removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException + { + getTrait().removeChild(materializeIfPossible(parentRef), + materializeIfPossible(childRef)); + } + + @Override + public boolean removeChildAssociation(ChildAssociationRef childAssocRef) + { + NodeServiceTrait theTrait = getTrait(); + + NodeRef childRef = childAssocRef.getChildRef(); + if (Reference.isReference(childRef)) + { + List assocsToRemove = revertVirtualAssociation(childAssocRef, + theTrait, + childRef); + boolean removed = false; + if (!assocsToRemove.isEmpty()) + { + for (ChildAssociationRef assoc : assocsToRemove) + { + removed = removed || theTrait.removeChildAssociation(assoc); + } + } + return removed; + } + else + { + return theTrait.removeChildAssociation(childAssocRef); + } + } + + private List revertVirtualAssociation(ChildAssociationRef childAssocRef, + NodeServiceTrait theTrait, NodeRef childRef) + { + childRef = virtualStore.materialize(Reference.fromNodeRef(childRef)); + ChildAssociationRef parent = theTrait.getPrimaryParent(childRef); + final QName assocName = childAssocRef.getQName(); + List assocsToRemove = theTrait.getChildAssocs(parent.getParentRef(), + childAssocRef.getTypeQName(), + new QNamePattern() + { + + @Override + public boolean isMatch(QName qname) + { + return assocName + .getLocalName() + .equals(qname + .getLocalName()); + } + }); + return assocsToRemove; + } + + @Override + public boolean removeSeconaryChildAssociation(ChildAssociationRef childAssocRef) + { + + NodeServiceTrait theTrait = getTrait(); + + NodeRef childRef = childAssocRef.getChildRef(); + if (Reference.isReference(childRef)) + { + List assocsToRemove = revertVirtualAssociation(childAssocRef, + theTrait, + childRef); + boolean removed = false; + if (!assocsToRemove.isEmpty()) + { + for (ChildAssociationRef assoc : assocsToRemove) + { + removed = removed || theTrait.removeSeconaryChildAssociation(assoc); + } + } + return removed; + } + else + { + return theTrait.removeSeconaryChildAssociation(childAssocRef); + } + } + + @Override + public boolean removeSecondaryChildAssociation(ChildAssociationRef childAssocRef) + { + NodeServiceTrait theTrait = getTrait(); + + NodeRef childRef = childAssocRef.getChildRef(); + if (Reference.isReference(childRef)) + { + List assocsToRemove = revertVirtualAssociation(childAssocRef, + theTrait, + childRef); + boolean removed = false; + if (!assocsToRemove.isEmpty()) + { + for (ChildAssociationRef assoc : assocsToRemove) + { + removed = removed || theTrait.removeSecondaryChildAssociation(assoc); + } + } + return removed; + } + else + { + return theTrait.removeSecondaryChildAssociation(childAssocRef); + } + } + + @Override + public Long getNodeAclId(NodeRef nodeRef) throws InvalidNodeRefException + { + return getTrait().getNodeAclId(materializeIfPossible(nodeRef)); + } + + @Override + public void setProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException + { + getTrait().setProperties(materializeIfPossible(nodeRef), + properties); + } + + @Override + public void setProperty(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException + { + getTrait().setProperty(materializeIfPossible(nodeRef), + qname, + value); + } + + @Override + public void removeProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException + { + getTrait().removeProperty(materializeIfPossible(nodeRef), + qname); + } + + @Override + public List getChildrenByName(NodeRef nodeRef, QName assocTypeQName, + Collection childNames) + { + return getTrait().getChildrenByName(materializeIfPossible(nodeRef), + assocTypeQName, + childNames); + } + + @Override + public Collection getChildAssocsWithoutParentAssocsOfType(NodeRef nodeRef, + QName assocTypeQName) + { + NodeServiceTrait theTrait = getTrait(); + boolean canVirtualize = canVirtualizeAssocNodeRef(nodeRef); + if (canVirtualize) + { + Reference reference = virtualStore.virtualize(nodeRef); + Collection virtualAssociations = virtualStore + .getChildAssocsWithoutParentAssocsOfType(reference, + assocTypeQName); + List associations = new LinkedList<>(virtualAssociations); + if (virtualStore.canMaterialize(reference)) + { + NodeRef materialReference = virtualStore.materialize(reference); + Collection actualAssociations = theTrait + .getChildAssocsWithoutParentAssocsOfType(materialReference, + assocTypeQName); + associations.addAll(actualAssociations); + } + + return associations; + } + else + { + return theTrait.getChildAssocsWithoutParentAssocsOfType(nodeRef, + assocTypeQName); + } + } + + @Override + public void removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) + throws InvalidNodeRefException + { + getTrait().removeAssociation(materializeIfPossible(sourceRef), + materializeIfPossible(targetRef), + assocTypeQName); + } + + @Override + public void setAssociations(NodeRef sourceRef, QName assocTypeQName, List targetRefs) + { + getTrait().setAssociations(materializeIfPossible(sourceRef), + assocTypeQName, + materializeIfPossible(targetRefs)); + } + + @Override + public AssociationRef getAssoc(Long id) + { + return getTrait().getAssoc(id); + } + + @Override + public NodeRef getStoreArchiveNode(StoreRef storeRef) + { + return getTrait().getStoreArchiveNode(storeRef); + } + + @Override + public NodeRef restoreNode(NodeRef archivedNodeRef, NodeRef destinationParentNodeRef, QName assocTypeQName, + QName assocQName) + { + return getTrait().restoreNode(materializeIfPossible(archivedNodeRef), + materializeIfPossible(destinationParentNodeRef), + assocTypeQName, + assocQName); + } + + @Override + public List findNodes(FindNodeParameters params) + { + return getTrait().findNodes(params); + } + + @Override + public int countChildAssocs(NodeRef nodeRef, boolean isPrimary) throws InvalidNodeRefException + { + return getTrait().countChildAssocs(nodeRef, + isPrimary); + } + + @Override + public List getTargetAssocsByPropertyValue(NodeRef sourceRef, QNamePattern qnamePattern, + QName propertyQName, Serializable propertyValue) + { + return getTrait().getTargetAssocsByPropertyValue(sourceRef, + qnamePattern, + propertyQName, + propertyValue); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/bundle/VirtualPermissionServiceExtension.java b/source/java/org/alfresco/repo/virtual/bundle/VirtualPermissionServiceExtension.java new file mode 100644 index 0000000000..1528c9d337 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/bundle/VirtualPermissionServiceExtension.java @@ -0,0 +1,563 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.bundle; + +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.alfresco.repo.security.permissions.NodePermissionEntry; +import org.alfresco.repo.security.permissions.PermissionEntry; +import org.alfresco.repo.security.permissions.PermissionReference; +import org.alfresco.repo.security.permissions.impl.SimpleNodePermissionEntry; +import org.alfresco.repo.security.permissions.impl.traitextender.PermissionServiceExtension; +import org.alfresco.repo.security.permissions.impl.traitextender.PermissionServiceTrait; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.store.VirtualStore; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.PermissionContext; +import org.alfresco.service.namespace.QName; +import org.alfresco.traitextender.SpringBeanExtension; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class VirtualPermissionServiceExtension extends + SpringBeanExtension implements + PermissionServiceExtension +{ + private static Log logger = LogFactory.getLog(VirtualPermissionServiceExtension.class); + + private VirtualStore virtualStore; + + public VirtualPermissionServiceExtension() + { + super(PermissionServiceTrait.class); + } + + public void setVirtualStore(VirtualStore virtualStore) + { + this.virtualStore = virtualStore; + } + + public AccessStatus hasPermission(NodeRef nodeRef, String perm) + { + PermissionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + return theTrait.hasPermission(nodeRef, + perm); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + AccessStatus virtualAccessStatus = virtualStore.hasPermission(reference, + perm); + if (!AccessStatus.UNDETERMINED.equals(virtualAccessStatus)) + { + return virtualAccessStatus; + } + else + { + NodeRef nodeToAdhereTo = establishPermisisonAdherence(reference); + if (nodeToAdhereTo == null) + { + return AccessStatus.UNDETERMINED; + } + else + { + return theTrait.hasPermission(nodeToAdhereTo, + perm); + } + } + } + } + + public AccessStatus hasPermission(NodeRef nodeRef, PermissionReference perm) + { + PermissionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + return theTrait.hasPermission(nodeRef, + perm); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + AccessStatus virtualAccessStatus = virtualStore.hasPermission(reference, + perm); + if (!AccessStatus.UNDETERMINED.equals(virtualAccessStatus)) + { + return virtualAccessStatus; + } + else + { + NodeRef nodeToAdhereTo = establishPermisisonAdherence(reference); + if (nodeToAdhereTo == null) + { + return AccessStatus.UNDETERMINED; + } + else + { + return theTrait.hasPermission(nodeToAdhereTo, + perm); + } + } + } + } + + @Override + public PermissionReference getAllPermissionReference() + { + return getTrait().getAllPermissionReference(); + } + + @Override + public Set getSettablePermissionReferences(QName type) + { + return getTrait().getSettablePermissionReferences(type); + } + + @Override + public Set getSettablePermissionReferences(NodeRef nodeRef) + { + if (!Reference.isReference(nodeRef)) + { + return getTrait().getSettablePermissionReferences(nodeRef); + } + else + { + return Collections.emptySet(); + } + } + + @Override + public NodePermissionEntry getSetPermissions(NodeRef nodeRef) + { + PermissionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + return theTrait.getSetPermissions(nodeRef); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodePermissionEntry virtualSetPermissions = virtualStore.getSetPermissions(reference); + + NodeRef nodeToAdhereTo = establishPermisisonAdherence(reference); + List actualPermissionEntries; + boolean inheritPermissions = false; + if (nodeToAdhereTo != null) + { + NodePermissionEntry actualSetPermissions = theTrait.getSetPermissions(nodeToAdhereTo); + actualPermissionEntries = actualSetPermissions.getPermissionEntries(); + inheritPermissions = actualSetPermissions.inheritPermissions(); + } + else + { + actualPermissionEntries = Collections.emptyList(); + inheritPermissions = false; + } + + List mergedEntries = new LinkedList<>(); + + List virtualPermissionEntries = virtualSetPermissions.getPermissionEntries(); + Set overridenPermissions = new HashSet<>(); + for (PermissionEntry permissionEntry : virtualPermissionEntries) + { + overridenPermissions.add(permissionEntry.getPermissionReference().getQName()); + mergedEntries.add(permissionEntry); + } + for (PermissionEntry permissionEntry : actualPermissionEntries) + { + if (!overridenPermissions.contains(permissionEntry.getPermissionReference().getQName())) + { + mergedEntries.add(permissionEntry); + } + } + return new SimpleNodePermissionEntry(nodeRef, + inheritPermissions, + mergedEntries); + } + } + + private NodeRef establishPermisisonAdherence(Reference reference) + { + NodeRef nodeToAdhereTo = virtualStore.adhere(reference, + VirtualStore.FILING_OR_MATERIAL_ADHERENCE); + if (logger.isDebugEnabled()) + { + logger.debug("Could not establish permission adherence for " + reference.toString()); + } + + return nodeToAdhereTo; + } + + @Override + public NodePermissionEntry explainPermission(NodeRef nodeRef, PermissionReference perm) + { + return getTrait().explainPermission(virtualStore.materializeIfPossible(nodeRef), + perm); + } + + @Override + public void deletePermissions(NodePermissionEntry nodePermissionEntry) + { + if (!Reference.isReference(nodePermissionEntry.getNodeRef())) + { + getTrait().deletePermissions(nodePermissionEntry); + } + else + { + // no action taken on virtual nodes + } + } + + @Override + public void deletePermission(PermissionEntry permissionEntry) + { + if (!Reference.isReference(permissionEntry.getNodeRef())) + { + getTrait().deletePermission(permissionEntry); + } + else + { + // no action taken on virtual nodes + } + } + + @Override + public void setPermission(PermissionEntry permissionEntry) + { + if (!Reference.isReference(permissionEntry.getNodeRef())) + { + getTrait().setPermission(permissionEntry); + } + else + { + // no action taken on virtual nodes + } + } + + @Override + public void setPermission(NodePermissionEntry nodePermissionEntry) + { + if (!Reference.isReference(nodePermissionEntry.getNodeRef())) + { + getTrait().setPermission(nodePermissionEntry); + } + else + { + // no action taken on virtual nodes + } + } + + @Override + public PermissionReference getPermissionReference(QName qname, String permissionName) + { + return getTrait().getPermissionReference(permissionName); + } + + @Override + public PermissionReference getPermissionReference(String permissionName) + { + return getTrait().getPermissionReference(permissionName); + } + + @Override + public String getPermission(PermissionReference permissionReference) + { + return getTrait().getPermission(permissionReference); + } + + @Override + public void deletePermissions(String recipient) + { + getTrait().deletePermissions(recipient); + } + + @Override + public NodePermissionEntry getSetPermissions(StoreRef storeRef) + { + return getTrait().getSetPermissions(storeRef); + } + + @Override + public String getOwnerAuthority() + { + return getTrait().getOwnerAuthority(); + } + + @Override + public String getAllAuthorities() + { + return getTrait().getAllAuthorities(); + } + + @Override + public String getAllPermission() + { + return getTrait().getAllPermission(); + } + + @Override + public Set getPermissions(NodeRef nodeRef) + { + PermissionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + return theTrait.getPermissions(nodeRef); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + Set virtualSetPermissions = virtualStore.getAllSetPermissions(reference); + NodeRef nodeToAdhereTo = establishPermisisonAdherence(reference); + Set mergedEntries = new HashSet<>(virtualSetPermissions); + if (nodeToAdhereTo != null) + { + Set actualSetPermissions = theTrait.getPermissions(nodeToAdhereTo); + mergedEntries.addAll(actualSetPermissions); + } + return mergedEntries; + } + } + + @Override + public Set getAllSetPermissions(NodeRef nodeRef) + { + PermissionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + return theTrait.getAllSetPermissions(nodeRef); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + Set virtualSetPermissions = virtualStore.getAllSetPermissions(reference); + NodeRef nodeToAdhereTo = establishPermisisonAdherence(reference); + Set actualSetPermissions; + if (nodeToAdhereTo != null) + { + actualSetPermissions = theTrait.getAllSetPermissions(nodeToAdhereTo); + } + else + { + actualSetPermissions = Collections.emptySet(); + } + + Set overridenPermissions = new HashSet<>(); + Set mergedEntries = new HashSet<>(); + for (AccessPermission permission : virtualSetPermissions) + { + overridenPermissions.add(permission.getPermission()); + mergedEntries.add(permission); + } + for (AccessPermission permission : actualSetPermissions) + { + if (!overridenPermissions.contains(permission.getPermission())) + { + mergedEntries.add(permission); + } + } + + return mergedEntries; + } + } + + @Override + public Set getSettablePermissions(NodeRef nodeRef) + { + if (!Reference.isReference(nodeRef)) + { + return getTrait().getSettablePermissions(nodeRef); + } + else + { + return Collections.emptySet(); + } + } + + @Override + public Set getSettablePermissions(QName type) + { + return getTrait().getSettablePermissions(type); + } + + @Override + public AccessStatus hasReadPermission(NodeRef nodeRef) + { + return getTrait().hasReadPermission(nodeRef); + } + + @Override + public Set getReaders(Long aclId) + { + return getTrait().getReaders(aclId); + } + + @Override + public Set getReadersDenied(Long aclId) + { + return getTrait().getReadersDenied(aclId); + } + + @Override + public AccessStatus hasPermission(Long aclID, PermissionContext context, String permission) + { + return getTrait().hasPermission(aclID, + context, + permission); + } + + @Override + public void deletePermissions(NodeRef nodeRef) + { + if (!Reference.isReference(nodeRef)) + { + getTrait().deletePermissions(nodeRef); + } + else + { + // no action taken on virtual nodes + } + } + + @Override + public void clearPermission(NodeRef nodeRef, String authority) + { + if (!Reference.isReference(nodeRef)) + { + getTrait().clearPermission(nodeRef, + authority); + } + else + { + // no action taken on virtual nodes + } + } + + @Override + public void deletePermission(NodeRef nodeRef, String authority, String permission) + { + if (!Reference.isReference(nodeRef)) + { + getTrait().deletePermission(nodeRef, + authority, + permission); + } + else + { + // no action taken on virtual nodes + } + } + + @Override + public void setPermission(NodeRef nodeRef, String authority, String permission, boolean allow) + { + if (!Reference.isReference(nodeRef)) + { + getTrait().setPermission(nodeRef, + authority, + permission, + allow); + } + else + { + // no action taken on virtual nodes + } + } + + @Override + public void setInheritParentPermissions(NodeRef nodeRef, boolean inheritParentPermissions) + { + if (!Reference.isReference(nodeRef)) + { + getTrait().setInheritParentPermissions(nodeRef, + inheritParentPermissions); + } + else + { + // no action taken on virtual nodes + } + + } + + @Override + public boolean getInheritParentPermissions(NodeRef nodeRef) + { + if (!Reference.isReference(nodeRef)) + { + return getTrait().getInheritParentPermissions(nodeRef); + } + else + { + return false; + } + } + + @Override + public void setPermission(StoreRef storeRef, String authority, String permission, boolean allow) + { + getTrait().setPermission(storeRef, + authority, + permission, + allow); + } + + @Override + public void deletePermission(StoreRef storeRef, String authority, String permission) + { + getTrait().deletePermission(storeRef, + authority, + permission); + } + + @Override + public void clearPermission(StoreRef storeRef, String authority) + { + getTrait().clearPermission(storeRef, + authority); + } + + @Override + public void deletePermissions(StoreRef storeRef) + { + getTrait().deletePermissions(storeRef); + } + + @Override + public Set getAllSetPermissions(StoreRef storeRef) + { + return getTrait().getAllSetPermissions(storeRef); + } + + @Override + public Set getAuthorisations() + { + return getTrait().getAuthorisations(); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/bundle/VirtualPreferenceServiceExtension.java b/source/java/org/alfresco/repo/virtual/bundle/VirtualPreferenceServiceExtension.java new file mode 100644 index 0000000000..f5e2526262 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/bundle/VirtualPreferenceServiceExtension.java @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.bundle; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.preference.traitextender.PreferenceServiceExtension; +import org.alfresco.repo.preference.traitextender.PreferenceServiceTrait; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ref.GetActualNodeRefMethod; +import org.alfresco.repo.virtual.ref.GetParentReferenceMethod; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.store.VirtualStore; +import org.alfresco.service.cmr.preference.PreferenceService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.traitextender.SpringBeanExtension; + +public class VirtualPreferenceServiceExtension extends + SpringBeanExtensionimplements PreferenceServiceExtension +{ + + private static final String DOCUMENTS_FAVOURITES_KEY = "org.alfresco.share.documents.favourites"; + + private static final String CREATED_AT = ".createdAt"; + + private static final String EXT_DOCUMENTS_FAVOURITES = "org.alfresco.ext.documents.favourites."; + + private VirtualStore virtualStore; + + private ActualEnvironment environment; + + private PreferenceService preferenceService; + + public VirtualPreferenceServiceExtension() + { + super(PreferenceServiceTrait.class); + } + + public ActualEnvironment getEnvironment() + { + return environment; + } + + public void setEnvironment(ActualEnvironment environment) + { + this.environment = environment; + } + + public PreferenceService getPreferenceService() + { + return preferenceService; + } + + public void setPreferenceService(PreferenceService preferenceService) + { + this.preferenceService = preferenceService; + } + + public void setVirtualStore(VirtualStore virtualStore) + { + this.virtualStore = virtualStore; + } + + private String getExtDocumentPreferenceKey(Map preferences) + { + String extKey = null; + if (!preferences.containsKey(DOCUMENTS_FAVOURITES_KEY)) + { + return null; + } + Set> entrySet = preferences.entrySet(); + if (entrySet == null) + { + return null; + } + + Iterator> iterator = entrySet.iterator(); + if (!iterator.hasNext()) + { + return null; + } + while (iterator.hasNext()) + { + Entry entry = iterator.next(); + String key = entry.getKey(); + if (key.startsWith(EXT_DOCUMENTS_FAVOURITES)) + { + extKey = key; + break; + } + } + + return extKey; + } + + @Override + public void setPreferences(String userName, Map preferences) throws Throwable + { + final String comma = ","; + + String extKey = getExtDocumentPreferenceKey(preferences); + if (extKey != null) + { + String pattern = "^" + EXT_DOCUMENTS_FAVOURITES + "(\\S+)" + CREATED_AT + "$"; + Pattern p = Pattern.compile(pattern); + Matcher m = p.matcher(extKey); + if (m.find()) + { + String documentNodeRefStr = m.group(1); + String favorites = (String) preferences.get(DOCUMENTS_FAVOURITES_KEY); + if (documentNodeRefStr != null && !documentNodeRefStr.isEmpty()) + { + NodeRef documentNodeRef = new NodeRef(documentNodeRefStr); + + if (Reference.isReference(documentNodeRef)) + { + Reference reference = Reference.fromNodeRef(documentNodeRef); + NodeRef actualNodeRef = reference.execute(new GetActualNodeRefMethod(null)); + String actualNodeRefStr = actualNodeRef.toString(); + Reference parentVF = reference.execute(new GetParentReferenceMethod()); + NodeRef actualFolder = parentVF.execute(new GetActualNodeRefMethod(environment)); + Reference virtualizedRoot = virtualStore.virtualize(actualFolder); + String documentName = (String) environment.getProperty(documentNodeRef, + ContentModel.PROP_NAME); + List results = virtualStore.search(virtualizedRoot, + documentName, + true, + false, + true); + if (favorites.contains(documentNodeRefStr)) + { + for (Reference ref : results) + { + NodeRef nodeRef = ref.toNodeRef(); + String nodeRefStr = nodeRef.toString(); + if (!favorites.contains(nodeRefStr)) + { + if (favorites.isEmpty()) + { + favorites = nodeRefStr; + } + else + { + favorites = favorites + comma + nodeRefStr; + } + + } + } + if (!favorites.contains(actualNodeRefStr)) + { + favorites = favorites + comma + actualNodeRefStr; + } + preferences.put(DOCUMENTS_FAVOURITES_KEY, + favorites); + } + else + { + List elements = new ArrayList(Arrays.asList(favorites.split(comma))); + for (Reference ref : results) + { + NodeRef nodeRef = ref.toNodeRef(); + String nodeRefStr = nodeRef.toString(); + if (elements.contains(nodeRefStr)) + { + elements.remove(nodeRefStr); + String preferenceToClear = EXT_DOCUMENTS_FAVOURITES + nodeRefStr + CREATED_AT; + preferenceService.clearPreferences(userName, + preferenceToClear); + } + } + if (elements.contains(actualNodeRefStr)) + { + elements.remove(actualNodeRefStr); + String preferenceToClear = EXT_DOCUMENTS_FAVOURITES + actualNodeRefStr + CREATED_AT; + preferenceService.clearPreferences(userName, + preferenceToClear); + } + favorites = ""; + for (String element : elements) + { + if (favorites.isEmpty()) + { + favorites = element; + } + else + { + favorites = favorites + comma + element; + } + } + preferences.put(DOCUMENTS_FAVOURITES_KEY, + favorites); + } + } + else + { + ChildAssociationRef parentAssociation = environment.getPrimaryParent(documentNodeRef); + NodeRef parentNodeRef = parentAssociation.getParentRef(); + if (virtualStore.canVirtualize(parentNodeRef)) + { + Reference virtualizedRoot = virtualStore.virtualize(parentNodeRef); + String documentName = (String) environment.getProperty(documentNodeRef, + ContentModel.PROP_NAME); + List results = virtualStore.search(virtualizedRoot, + documentName, + true, + false, + true); + if (preferences.get(extKey) == null) + { + List elements = new ArrayList(Arrays.asList(favorites.split(comma))); + for (Reference ref : results) + { + NodeRef nodeRef = ref.toNodeRef(); + String nodeRefStr = nodeRef.toString(); + if (elements.contains(nodeRefStr)) + { + elements.remove(nodeRefStr); + String preferenceToClear = EXT_DOCUMENTS_FAVOURITES + nodeRefStr + CREATED_AT; + preferenceService.clearPreferences(userName, + preferenceToClear); + + } + } + favorites = ""; + for (String element : elements) + { + if (favorites.isEmpty()) + { + favorites = element; + } + else + { + favorites = favorites + comma + element; + } + } + preferences.put(DOCUMENTS_FAVOURITES_KEY, + favorites); + } + else + { + for (Reference ref : results) + { + NodeRef nodeRef = ref.toNodeRef(); + String nodeRefStr = nodeRef.toString(); + if (!favorites.contains(nodeRefStr)) + { + if (favorites.isEmpty()) + { + favorites = nodeRefStr; + } + else + { + favorites = favorites + comma + nodeRefStr; + } + } + } + preferences.put(DOCUMENTS_FAVOURITES_KEY, + favorites); + } + } + } + } + } + } + getTrait().setPreferences(userName, + preferences); + + } +} diff --git a/source/java/org/alfresco/repo/virtual/bundle/VirtualRatingServiceExtension.java b/source/java/org/alfresco/repo/virtual/bundle/VirtualRatingServiceExtension.java new file mode 100644 index 0000000000..4a22566e2e --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/bundle/VirtualRatingServiceExtension.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.bundle; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.rating.traitextender.RatingServiceExtension; +import org.alfresco.repo.rating.traitextender.RatingServiceTrait; +import org.alfresco.repo.virtual.store.VirtualStore; +import org.alfresco.service.cmr.rating.Rating; +import org.alfresco.service.cmr.rating.RatingScheme; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.traitextender.SpringBeanExtension; + +public class VirtualRatingServiceExtension extends SpringBeanExtension + implements RatingServiceExtension +{ + + private VirtualStore virtualStore; + + public VirtualRatingServiceExtension() + { + super(RatingServiceTrait.class); + } + + public void setVirtualStore(VirtualStore virtualStore) + { + this.virtualStore = virtualStore; + } + + + + public void applyRating(NodeRef targetNode, float rating, String ratingSchemeName) + { + NodeRef materialNode = virtualStore.materializeIfPossible(targetNode); + getTrait().applyRating(materialNode, + rating, + ratingSchemeName); + } + + public int getRatingsCount(NodeRef targetNode, String ratingSchemeName) + { + NodeRef materialNode = virtualStore.materializeIfPossible(targetNode); + return getTrait().getRatingsCount(materialNode, + ratingSchemeName); + } + + public float getTotalRating(NodeRef targetNode, String ratingSchemeName) + { + NodeRef materialNode = virtualStore.materializeIfPossible(targetNode); + return getTrait().getTotalRating(materialNode, + ratingSchemeName); + } + + public float getAverageRating(NodeRef targetNode, String ratingSchemeName) + { + NodeRef materialNode = virtualStore.materializeIfPossible(targetNode); + return getTrait().getAverageRating(materialNode, + ratingSchemeName); + } + + public Rating getRatingByCurrentUser(NodeRef targetNode, String ratingSchemeName) + { + NodeRef materialNode = virtualStore.materializeIfPossible(targetNode); + return getTrait().getRatingByCurrentUser(materialNode, + ratingSchemeName); + } + + public List getRatingsByCurrentUser(NodeRef targetNode) + { + NodeRef materialNode = virtualStore.materializeIfPossible(targetNode); + return getTrait().getRatingsByCurrentUser(materialNode); + } + + public Rating removeRatingByCurrentUser(NodeRef targetNode, String ratingSchemeName) + { + NodeRef materialNode = virtualStore.materializeIfPossible(targetNode); + return getTrait().removeRatingByCurrentUser(materialNode, + ratingSchemeName); + } + + public Serializable getRatingRollup(NodeRef targetNode, String ratingSchemeName, String ratingRollupName) + { + NodeRef materialNode = virtualStore.materializeIfPossible(targetNode); + return getTrait().getRatingRollup(materialNode, + ratingSchemeName, + ratingRollupName); + } + + @Override + public Map getRatingSchemes() + { + return getTrait().getRatingSchemes(); + } + + @Override + public RatingScheme getRatingScheme(String ratingSchemeName) + { + return getTrait().getRatingScheme(ratingSchemeName); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/bundle/VirtualVersionServiceExtension.java b/source/java/org/alfresco/repo/virtual/bundle/VirtualVersionServiceExtension.java new file mode 100644 index 0000000000..d069047e1d --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/bundle/VirtualVersionServiceExtension.java @@ -0,0 +1,626 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.bundle; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +import org.alfresco.repo.version.Version2Model; +import org.alfresco.repo.version.VersionModel; +import org.alfresco.repo.version.VersionServicePolicies.CalculateVersionLabelPolicy; +import org.alfresco.repo.version.common.VersionImpl; +import org.alfresco.repo.version.traitextender.VersionServiceExtension; +import org.alfresco.repo.version.traitextender.VersionServiceTrait; +import org.alfresco.repo.virtual.ref.GetParentReferenceMethod; +import org.alfresco.repo.virtual.ref.NodeProtocol; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.store.VirtualStore; +import org.alfresco.service.cmr.repository.AspectMissingException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.version.ReservedVersionNameException; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionHistory; +import org.alfresco.service.namespace.QName; +import org.alfresco.traitextender.SpringBeanExtension; + +public class VirtualVersionServiceExtension extends SpringBeanExtension + implements VersionServiceExtension +{ + private VirtualStore virtualStore; + + public class VirtualVersionHistory implements VersionHistory + { + /** + * + */ + private static final long serialVersionUID = 2640439550254763191L; + + private Reference versionedReference; + + private VersionHistory actualHistory; + + public VirtualVersionHistory(Reference versionedReference, VersionHistory actualHistory) + { + super(); + this.versionedReference = versionedReference; + this.actualHistory = actualHistory; + } + + @Override + public Version getRootVersion() + { + Version actualRootVersion = actualHistory.getRootVersion(); + return VirtualVersionServiceExtension.this.virtualizeVersion(versionedReference, + actualRootVersion); + } + + @Override + public Version getHeadVersion() + { + Version actualHeadVersion = actualHistory.getRootVersion(); + return VirtualVersionServiceExtension.this.virtualizeVersion(versionedReference, + actualHeadVersion); + } + + @Override + public Collection getAllVersions() + { + Collection allActualVersions = actualHistory.getAllVersions(); + return VirtualVersionServiceExtension.this.virtualizeVersions(versionedReference, + allActualVersions); + } + + @Override + public Version getPredecessor(Version version) + { + Version actualVersion = VirtualVersionServiceExtension.this.materializeVersionIfReference(version); + Version actualPredecesor = actualHistory.getPredecessor(actualVersion); + + return VirtualVersionServiceExtension.this.virtualizeVersion(versionedReference, + actualPredecesor); + } + + @Override + public Collection getSuccessors(Version version) + { + Version actualVersion = VirtualVersionServiceExtension.this.materializeVersionIfReference(version); + Collection actualSuccessors = actualHistory.getSuccessors(actualVersion); + + return VirtualVersionServiceExtension.this.virtualizeVersions(versionedReference, + actualSuccessors); + } + + @Override + public Version getVersion(String versionLabel) + { + Version actualVersion = actualHistory.getVersion(versionLabel); + return VirtualVersionServiceExtension.this.virtualizeVersion(versionedReference, + actualVersion); + } + + } + + public VirtualVersionServiceExtension() + { + super(VersionServiceTrait.class); + } + + public void setVirtualStore(VirtualStore virtualStore) + { + this.virtualStore = virtualStore; + } + + @Override + public StoreRef getVersionStoreReference() + { + return getTrait().getVersionStoreReference(); + } + + @Override + public boolean isAVersion(NodeRef nodeRef) + { + VersionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + return theTrait.isAVersion(nodeRef); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef materialNode = virtualStore.materialize(reference); + return theTrait.isAVersion(materialNode); + } + } + + @Override + public boolean isVersioned(NodeRef nodeRef) + { + VersionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + return theTrait.isVersioned(nodeRef); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef materialNode = virtualStore.materialize(reference); + return theTrait.isVersioned(materialNode); + } + } + + private Version materializeVersionIfReference(Version virtualVersion) + { + NodeRef frozenStateNodeRef = virtualVersion.getFrozenStateNodeRef(); + StoreRef frozenStoreRef = frozenStateNodeRef.getStoreRef(); + + NodeRef materialFrozenNodeRef = frozenStateNodeRef; + + if (Reference.isReference(frozenStateNodeRef)) + { + Reference frozenReference = Reference.fromNodeRef(frozenStateNodeRef); + materialFrozenNodeRef = virtualStore.materialize(frozenReference); + } + + Map virtualProperties = virtualVersion.getVersionProperties(); + Map actualProperties = new HashMap<>(virtualProperties); + + if (frozenStoreRef.getIdentifier().equals(Version2Model.STORE_ID)) + { + // V2 version store (eg. workspace://version2Store) + NodeRef propFrozenNode = (NodeRef) virtualProperties.get(Version2Model.PROP_FROZEN_NODE_REF); + NodeRef propActualFrozenNode = propFrozenNode; + + if (Reference.isReference(propFrozenNode)) + { + Reference propFrozenReference = Reference.fromNodeRef(propFrozenNode); + propActualFrozenNode = virtualStore.materialize(propFrozenReference); + } + + actualProperties.put(Version2Model.PROP_FROZEN_NODE_REF, + propActualFrozenNode); + + } + else if (frozenStoreRef.getIdentifier().equals(VersionModel.STORE_ID)) + { + // Deprecated V1 version store (eg. + // workspace://lightWeightVersionStore) + String frozenNodeStoreProtocol = (String) virtualProperties + .get(VersionModel.PROP_FROZEN_NODE_STORE_PROTOCOL); + String frozenNodeStoreId = (String) virtualProperties.get(VersionModel.PROP_FROZEN_NODE_STORE_ID); + String frozenNodeId = (String) virtualProperties.get(VersionModel.PROP_FROZEN_NODE_ID); + + NodeRef propFrozenNode = new NodeRef(frozenNodeStoreProtocol, + frozenNodeStoreId, + frozenNodeId); + NodeRef propActualFrozenNode = propFrozenNode; + + if (Reference.isReference(propFrozenNode)) + { + Reference propFrozenReference = Reference.fromNodeRef(propFrozenNode); + propActualFrozenNode = virtualStore.materialize(propFrozenReference); + } + StoreRef propActualStoreRef = propFrozenNode.getStoreRef(); + actualProperties.put(VersionModel.PROP_FROZEN_NODE_STORE_PROTOCOL, + propActualStoreRef.getProtocol()); + actualProperties.put(VersionModel.PROP_FROZEN_NODE_STORE_ID, + propActualStoreRef.getIdentifier()); + actualProperties.put(VersionModel.PROP_FROZEN_NODE_ID, + propActualFrozenNode.getId()); + } + + Version actualVersion = new VersionImpl(actualProperties, + materialFrozenNodeRef); + + return actualVersion; + } + + private Version virtualizeVersion(Reference versionedReference, Version actualVersion) + { + if (actualVersion == null) + { + return null; + } + + NodeRef frozenStateNodeRef = actualVersion.getFrozenStateNodeRef(); + StoreRef frozenStoreRef = frozenStateNodeRef.getStoreRef(); + + Reference parentReference = versionedReference.execute(new GetParentReferenceMethod()); + Reference virtualFrozenReference = NodeProtocol.newReference(frozenStateNodeRef, + parentReference); + + Map properties = actualVersion.getVersionProperties(); + Map virtualProperties = new HashMap(properties); + + // Switch VersionStore depending on configured impl + if (frozenStoreRef.getIdentifier().equals(Version2Model.STORE_ID)) + { + // V2 version store (eg. workspace://version2Store) + NodeRef propFrozenNodeRef = (NodeRef) virtualProperties.get(Version2Model.PROP_FROZEN_NODE_REF); + Reference virtualPropFrozenReference = NodeProtocol.newReference(propFrozenNodeRef, + parentReference); + virtualProperties.put(Version2Model.PROP_FROZEN_NODE_REF, + virtualPropFrozenReference.toNodeRef(propFrozenNodeRef.getStoreRef())); + } + else if (frozenStoreRef.getIdentifier().equals(VersionModel.STORE_ID)) + { + // Deprecated V1 version store (eg. + // workspace://lightWeightVersionStore) + String frozenNodeStoreProtocol = (String) virtualProperties + .get(VersionModel.PROP_FROZEN_NODE_STORE_PROTOCOL); + String frozenNodeStoreId = (String) virtualProperties.get(VersionModel.PROP_FROZEN_NODE_STORE_ID); + String frozenNodeId = (String) virtualProperties.get(VersionModel.PROP_FROZEN_NODE_ID); + + StoreRef propFrozenStoreRef = new StoreRef(frozenNodeStoreProtocol, + frozenNodeStoreId); + + NodeRef propFrozenNode = new NodeRef(propFrozenStoreRef, + frozenNodeId); + + Reference virtualPropFrozenReference = NodeProtocol.newReference(propFrozenNode, + parentReference); + NodeRef virtualPropFrozenNodeRef = virtualPropFrozenReference.toNodeRef(propFrozenStoreRef); + + virtualProperties.put(VersionModel.PROP_FROZEN_NODE_STORE_PROTOCOL, + propFrozenStoreRef.getProtocol()); + virtualProperties.put(VersionModel.PROP_FROZEN_NODE_STORE_ID, + propFrozenStoreRef.getIdentifier()); + virtualProperties.put(VersionModel.PROP_FROZEN_NODE_ID, + virtualPropFrozenNodeRef.getId()); + } + return new VersionImpl(virtualProperties, + virtualFrozenReference.toNodeRef(frozenStateNodeRef.getStoreRef())); + } + + private Collection virtualizeVersions(Reference versionedReference, Collection actualVersions) + { + Collection virtualizedVersions = new LinkedList<>(); + + for (Version actualVersion : actualVersions) + { + Version virtualizedVersion = virtualizeVersion(versionedReference, + actualVersion); + virtualizedVersions.add(virtualizedVersion); + } + + return virtualizedVersions; + } + + @Override + public Version createVersion(NodeRef nodeRef, Map versionProperties) + throws ReservedVersionNameException, AspectMissingException + { + VersionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + return theTrait.createVersion(nodeRef, + versionProperties); + } + else + { + NodeRef materialNode = virtualStore.materializeIfPossible(nodeRef); + Version actualVersion = theTrait.createVersion(materialNode, + versionProperties); + Reference reference = Reference.fromNodeRef(nodeRef); + return virtualizeVersion(reference, + actualVersion); + } + } + + @Override + public Collection createVersion(NodeRef nodeRef, Map versionProperties, + boolean versionChildren) throws ReservedVersionNameException, AspectMissingException + { + VersionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + return theTrait.createVersion(nodeRef, + versionProperties, + versionChildren); + } + else + { + NodeRef materialNode = virtualStore.materializeIfPossible(nodeRef); + Collection actualVersions = theTrait.createVersion(materialNode, + versionProperties, + versionChildren); + + Reference reference = Reference.fromNodeRef(nodeRef); + return virtualizeVersions(reference, + actualVersions); + } + } + + @Override + public Collection createVersion(Collection nodeRefs, Map versionProperties) + throws ReservedVersionNameException, AspectMissingException + { + VersionServiceTrait theTrait = getTrait(); + Collection materialNodeRefs = new LinkedList<>(); + Map materializedNodeRefs = new HashMap<>(); + for (NodeRef nodeRef : nodeRefs) + { + if (!Reference.isReference(nodeRef)) + { + materialNodeRefs.add(nodeRef); + } + else + { + NodeRef materialNode = virtualStore.materializeIfPossible(nodeRef); + materialNodeRefs.add(materialNode); + materializedNodeRefs.put(materialNode, + Reference.fromNodeRef(nodeRef)); + } + } + + Collection versions = theTrait.createVersion(materialNodeRefs, + versionProperties); + Collection virtualizedVersions = new LinkedList<>(); + for (Version version : versions) + { + NodeRef versionedNodeRef = version.getVersionedNodeRef(); + Reference reference = materializedNodeRefs.get(versionedNodeRef); + + if (reference != null) + { + Version virtualizedVersion = virtualizeVersion(reference, + version); + virtualizedVersions.add(virtualizedVersion); + } + else + { + virtualizedVersions.add(version); + } + } + + return virtualizedVersions; + } + + @Override + public VersionHistory getVersionHistory(NodeRef nodeRef) throws AspectMissingException + { + VersionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + return theTrait.getVersionHistory(nodeRef); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef materialNode = virtualStore.materialize(reference); + VersionHistory actualVersionHistory = theTrait.getVersionHistory(materialNode); + if (actualVersionHistory == null) + { + return null; + } + else + { + Reference versionedReference = Reference.fromNodeRef(nodeRef); + + return new VirtualVersionHistory(versionedReference, + actualVersionHistory); + } + } + } + + @Override + public Version getCurrentVersion(NodeRef nodeRef) + { + VersionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + return theTrait.getCurrentVersion(nodeRef); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef materialNode = virtualStore.materialize(reference); + Reference versionedReference = Reference.fromNodeRef(nodeRef); + Version actualVersion = theTrait.getCurrentVersion(materialNode); + return virtualizeVersion(versionedReference, + actualVersion); + } + } + + @Override + public void revert(NodeRef nodeRef) + { + VersionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + theTrait.revert(nodeRef); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef materialNode = virtualStore.materialize(reference); + theTrait.revert(materialNode); + } + } + + @Override + public void revert(NodeRef nodeRef, boolean deep) + { + VersionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + theTrait.revert(nodeRef, + deep); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef materialNode = virtualStore.materialize(reference); + theTrait.revert(materialNode, + deep); + } + } + + @Override + public void revert(NodeRef nodeRef, Version version) + { + + VersionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + theTrait.revert(nodeRef, + version); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef materialNode = virtualStore.materialize(reference); + Version actualVersion = VirtualVersionServiceExtension.this.materializeVersionIfReference(version); + theTrait.revert(materialNode, + actualVersion); + } + } + + @Override + public void revert(NodeRef nodeRef, Version version, boolean deep) + { + VersionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + theTrait.revert(nodeRef, + version, + deep); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef materialNode = virtualStore.materialize(reference); + Version actualVersion = VirtualVersionServiceExtension.this.materializeVersionIfReference(version); + theTrait.revert(materialNode, + actualVersion, + deep); + } + } + + @Override + public NodeRef restore(NodeRef nodeRef, NodeRef parentNodeRef, QName assocTypeQName, QName assocQName) + { + VersionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + return theTrait.restore(nodeRef, + parentNodeRef, + assocTypeQName, + assocQName); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef materialNode = virtualStore.materialize(reference); + return theTrait.restore(materialNode, + parentNodeRef, + assocTypeQName, + assocQName); + } + } + + @Override + public NodeRef restore(NodeRef nodeRef, NodeRef parentNodeRef, QName assocTypeQName, QName assocQName, boolean deep) + { + VersionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + return theTrait.restore(nodeRef, + parentNodeRef, + assocTypeQName, + assocQName, + deep); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef materialNode = virtualStore.materialize(reference); + return theTrait.restore(materialNode, + parentNodeRef, + assocTypeQName, + assocQName, + deep); + } + } + + @Override + public void deleteVersionHistory(NodeRef nodeRef) throws AspectMissingException + { + VersionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + theTrait.deleteVersionHistory(nodeRef); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef materialNode = virtualStore.materialize(reference); + theTrait.deleteVersionHistory(materialNode); + } + } + + @Override + public void deleteVersion(NodeRef nodeRef, Version version) + { + VersionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + theTrait.deleteVersion(nodeRef, + version); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef materialNode = virtualStore.materialize(reference); + Version actualVersion = materializeVersionIfReference(version); + theTrait.deleteVersion(materialNode, + actualVersion); + } + } + + @Override + public void ensureVersioningEnabled(NodeRef nodeRef, Map versionProperties) + { + VersionServiceTrait theTrait = getTrait(); + if (!Reference.isReference(nodeRef)) + { + theTrait.ensureVersioningEnabled(nodeRef, + versionProperties); + } + else + { + Reference reference = Reference.fromNodeRef(nodeRef); + NodeRef materialNode = virtualStore.materialize(reference); + theTrait.ensureVersioningEnabled(materialNode, + versionProperties); + } + } + + @Override + public void registerVersionLabelPolicy(QName typeQName, CalculateVersionLabelPolicy policy) + { + getTrait().registerVersionLabelPolicy(typeQName, + policy); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/bundle/VirtualWorkflowPackageExtension.java b/source/java/org/alfresco/repo/virtual/bundle/VirtualWorkflowPackageExtension.java new file mode 100644 index 0000000000..72e61c102e --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/bundle/VirtualWorkflowPackageExtension.java @@ -0,0 +1,52 @@ + +package org.alfresco.repo.virtual.bundle; + +import java.util.List; + +import org.alfresco.repo.virtual.store.VirtualStore; +import org.alfresco.repo.workflow.traitextender.WorkflowPackageExtension; +import org.alfresco.repo.workflow.traitextender.WorkflowPackageTrait; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.workflow.WorkflowInstance; +import org.alfresco.traitextender.SpringBeanExtension; + +public class VirtualWorkflowPackageExtension extends + SpringBeanExtension implements WorkflowPackageExtension +{ + private VirtualStore virtualStore; + + public VirtualWorkflowPackageExtension() + { + super(WorkflowPackageTrait.class); + } + + public void setVirtualStore(VirtualStore virtualStore) + { + this.virtualStore = virtualStore; + } + + @Override + public NodeRef createPackage(NodeRef container) + { + return getTrait().createPackage(virtualStore.materializeIfPossible(container)); + } + + @Override + public void deletePackage(NodeRef container) + { + getTrait().deletePackage(virtualStore.materializeIfPossible(container)); + } + + @Override + public List getWorkflowIdsForContent(NodeRef packageItem) + { + return getTrait().getWorkflowIdsForContent(virtualStore.materializeIfPossible(packageItem)); + } + + @Override + public boolean setWorkflowForPackage(WorkflowInstance instance) + { + return getTrait().setWorkflowForPackage(instance); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/config/CompanyHomeContext.java b/source/java/org/alfresco/repo/virtual/config/CompanyHomeContext.java new file mode 100644 index 0000000000..0862070635 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/config/CompanyHomeContext.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.config; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; + +/** + * A {@link NodeRefContext} that solves a name path relative to the Alfresco + * company home node. + */ +public class CompanyHomeContext implements NodeRefContext +{ + + public static final String COMPANY_HOME_CONTEXT_NAME = "CompanyHome"; + + private static final String[] EMPTY_PATH = new String[0]; + + private String companyHomeQName; + + public void setCompanyHomeQName(String companyHomeQName) + { + this.companyHomeQName = companyHomeQName; + } + + @Override + public NodeRef resolveNamePath(String[] namePath, NodeRefResolver resolver) + { + String[] companyHomeRealtiveRef = createRelativeNamePath(namePath); + + return resolver.resolvePathReference(companyHomeRealtiveRef); + } + + private String[] createRelativeNamePath(String[] namePath) + { + if (namePath == null) + { + namePath = EMPTY_PATH; + } + String[] companyHomeRealtiveRef = new String[namePath.length + 3]; + companyHomeRealtiveRef[0] = StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getProtocol(); + companyHomeRealtiveRef[1] = StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.getIdentifier(); + companyHomeRealtiveRef[2] = companyHomeQName; + + if (namePath.length > 0) + { + System.arraycopy(namePath, + 0, + companyHomeRealtiveRef, + 3, + namePath.length); + } + return companyHomeRealtiveRef; + } + + @Override + public NodeRef resolveQNamePath(String[] qNamePath, NodeRefResolver resolver) + { + String[] companyHomeRealtiveRef = createRelativeQNamePath(qNamePath); + + return resolver.resolveQNameReference(companyHomeRealtiveRef); + } + + private String[] createRelativeQNamePath(String[] qNamePath) + { + if (qNamePath == null) + { + qNamePath = EMPTY_PATH; + } + String[] companyHomeRealtiveRef = new String[qNamePath.length + 1]; + companyHomeRealtiveRef[0] = companyHomeQName; + + if (qNamePath.length > 0) + { + System.arraycopy(qNamePath, + 0, + companyHomeRealtiveRef, + 1, + qNamePath.length); + } + return companyHomeRealtiveRef; + } + + @Override + public NodeRef createNamePath(String[] namePath, NodeRefResolver resolver) + { + String[] relativeNamePath = createRelativeNamePath(namePath); + return resolver.createNamePath(relativeNamePath); + } + + @Override + public NodeRef createQNamePath(String[] qNamePath, String[] names, NodeRefResolver resolver) + { + String[] relativeQNamePath = createRelativeQNamePath(qNamePath); + return resolver.createQNamePath(relativeQNamePath, + names); + } + + @Override + public String getContextName() + { + return COMPANY_HOME_CONTEXT_NAME; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/config/NodeRefContext.java b/source/java/org/alfresco/repo/virtual/config/NodeRefContext.java new file mode 100644 index 0000000000..7f03ad7ab9 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/config/NodeRefContext.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.config; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * A repository context in which a {@link NodeRefPathExpression} should resolve + * to a {@link NodeRef} using a relative name or qname path. + */ +public interface NodeRefContext +{ + /** + * @param namePath + * @param resolver + * @return the {@link NodeRef} the given name path resolves to using the + * supplied resolver. + */ + NodeRef resolveNamePath(String[] namePath, NodeRefResolver resolver); + + /** + * @param qNamePath + * @param resolver + * @return the {@link NodeRef} the given {@link QName} prefixed string path + * resolves to using the supplied resolver. + */ + NodeRef resolveQNamePath(String[] qNamePath, NodeRefResolver resolver); + + /** + * @return the name of this context + */ + String getContextName(); + + NodeRef createNamePath(String[] namePath, NodeRefResolver resolver); + + NodeRef createQNamePath(String[] qNamePath, String[] names, NodeRefResolver resolver); + +} diff --git a/source/java/org/alfresco/repo/virtual/config/NodeRefExpression.java b/source/java/org/alfresco/repo/virtual/config/NodeRefExpression.java new file mode 100644 index 0000000000..8f08acd873 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/config/NodeRefExpression.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ +package org.alfresco.repo.virtual.config; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * An expression that resolves to a {@link NodeRef}.
+ */ +public interface NodeRefExpression +{ + NodeRef resolve(); + + NodeRef resolve(boolean createIfNotFound); +} diff --git a/source/java/org/alfresco/repo/virtual/config/NodeRefPathExpression.java b/source/java/org/alfresco/repo/virtual/config/NodeRefPathExpression.java new file mode 100644 index 0000000000..6fbe70ebe6 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/config/NodeRefPathExpression.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.config; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * A String name or qname path expression that resolves to a {@link NodeRef}.
+ * The given name or qname path is relative to a {@link NodeRefContext} + * repository location. The default context is set to + * {@link CompanyHomeContext#COMPANY_HOME_CONTEXT_NAME}. Other contexts can be + * set using their name with {@link #setContext(String)}.
+ * The set path is automatically detected and checked for consistency. + */ +public class NodeRefPathExpression implements NodeRefExpression +{ + private static final String NAMESPACE_DELIMITER = ":"; + + private static final String PATH_DELIMITER = "/"; + + private Map contexts = new HashMap(); + + private NodeRefResolver resolver; + + private String context = CompanyHomeContext.COMPANY_HOME_CONTEXT_NAME; + + private String[] createNamePath; + + private String[] namePath; + + private String[] qNamePath; + + public NodeRefPathExpression(NodeRefResolver resolver, Map contexts) + { + this(resolver, + contexts, + CompanyHomeContext.COMPANY_HOME_CONTEXT_NAME, + null); + } + + public NodeRefPathExpression(NodeRefResolver resolver, Map contexts, String context, + String path) + { + super(); + this.resolver = resolver; + this.contexts = contexts; + this.context = context; + setPath(path); + } + + public void setContext(String context) + { + this.context = context; + } + + public void setCreatedPathName(String createNamePath) + { + this.createNamePath = createNamePath.split(","); + } + + /** + * Path setter.
+ * The type of path is automatically detected and checked for consistency. + * + * @param path the string path to be resolved later + * @throws AlfrescoRuntimeException if the given path is inconsistent (i.e. + * a combination of qnames and names) + */ + public void setPath(String path) throws AlfrescoRuntimeException + { + String[] pathElements = splitAndNormalizePath(path); + if (isQNamePath(pathElements)) + { + this.qNamePath = pathElements; + } + else if (isNamePath(pathElements)) + { + this.namePath = pathElements; + } + else + { + throw new AlfrescoRuntimeException("Invalid path format : " + path); + } + + } + + private boolean isQNamePath(String[] pathElements) + { + for (int i = 0; i < pathElements.length; i++) + { + if (!pathElements[i].contains(NAMESPACE_DELIMITER)) + { + return false; + } + } + + return true; + } + + private boolean isNamePath(String[] pathElements) + { + for (int i = 0; i < pathElements.length; i++) + { + if (pathElements[i].contains(NAMESPACE_DELIMITER)) + { + return false; + } + } + + return true; + } + + public static String[] splitAndNormalizePath(String path) + { + if (path == null || path.trim().length() == 0) + { + return new String[] {}; + } + + String[] splitPath = path.split(PATH_DELIMITER); + + // remove blank entries resulted from misplaced delimiters + int shift = 0; + for (int i = 0; i < splitPath.length; i++) + { + if (splitPath[i] == null || splitPath[i].trim().isEmpty()) + { + shift++; + } + else if (shift > 0) + { + splitPath[i - shift] = splitPath[i]; + } + } + + if (shift > 0) + { + String[] noBlanksSplitPath = new String[splitPath.length - shift]; + if (noBlanksSplitPath.length > 0) + { + System.arraycopy(splitPath, + 0, + noBlanksSplitPath, + 0, + noBlanksSplitPath.length); + } + + splitPath = noBlanksSplitPath; + } + + return splitPath; + } + + @Override + public NodeRef resolve() + { + NodeRefContext theContext = contexts.get(context); + if (this.namePath != null) + { + return theContext.resolveNamePath(this.namePath, + resolver); + } + else + { + return theContext.resolveQNamePath(this.qNamePath, + resolver); + } + } + + @Override + public NodeRef resolve(boolean createIfNotFound) + { + NodeRef nodeRef = resolve(); + if (nodeRef == null && createIfNotFound) + { + NodeRefContext theContext = contexts.get(context); + + if (this.namePath != null) + { + return theContext.createNamePath(this.namePath, + resolver); + } + else + { + return theContext.createQNamePath(this.qNamePath, + this.createNamePath, + resolver); + } + + } + return nodeRef; + } + + @Override + public String toString() + { + StringBuilder pathString = new StringBuilder(); + pathString.append("<"); + pathString.append(this.context); + pathString.append(">/"); + + if (this.namePath != null) + { + pathString.append(Arrays.toString(this.namePath)); + } + + if (this.qNamePath != null) + { + pathString.append(Arrays.toString(this.qNamePath)); + } + + return ""; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/config/NodeRefPathExpressionFactory.java b/source/java/org/alfresco/repo/virtual/config/NodeRefPathExpressionFactory.java new file mode 100644 index 0000000000..40deba581a --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/config/NodeRefPathExpressionFactory.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.config; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Spring context {@link NodeRefPathExpression} factory bean.
+ * It creates {@link NodeRefPathExpression} instances configured with the + * spring context defined {@link NodeRefResolver} and the given set of spring + * configured {@link NodeRefContext}s. + */ +public class NodeRefPathExpressionFactory +{ + private Map contextsMap = Collections.emptyMap(); + + private NodeRefResolver resolver; + + public void setResolver(NodeRefResolver resolver) + { + this.resolver = resolver; + } + + public void setNodeRefContexts(Set nodeRefContexts) + { + this.contextsMap = new HashMap(); + + for (NodeRefContext nodeRefContext : nodeRefContexts) + { + this.contextsMap.put(nodeRefContext.getContextName(), + nodeRefContext); + } + } + + public NodeRefPathExpression createInstance() + { + return new NodeRefPathExpression(resolver, + this.contextsMap); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/config/NodeRefResolver.java b/source/java/org/alfresco/repo/virtual/config/NodeRefResolver.java new file mode 100644 index 0000000000..8b101556e2 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/config/NodeRefResolver.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.config; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.model.Repository; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Dependency inversion of the Provision of Repository Context.
+ * + * @see Repository + */ +public interface NodeRefResolver +{ + + /** + * Path type reference create if absent. Fail substitute for + * {@link Repository#findNodeRef(String, String[])}. + * + * @param reference path element names array + * @return reference array of reference segments + * @throws AlfrescoRuntimeException if an unimplemented or invalid reference + * type is provided + * @see Repository#findNodeRef(String, String[]) + */ + NodeRef createNamePath(String[] reference); + + /** + * QName type reference create if absent.Fail safe substitute for + * {@link Repository#findNodeRef(String, String[])}. + * + * @param reference path element qnames array + * @param names names to be used when creating the given path. If less than + * reference elements they will be matched from the end of the + * reference path. + * @return reference array of reference segments + * @throws AlfrescoRuntimeException if an unimplemented or invalid reference + * type is provided + * @see Repository#findNodeRef(String, String[]) + */ + NodeRef createQNamePath(String[] reference, String[] names); + + /** + * Node type explicit inversion of + * {@link Repository#findNodeRef(String, String[])}. + * + * @return reference array of reference segments + * @throws AlfrescoRuntimeException if an unimplemented or invalid reference + * type is provided + * @see Repository#findNodeRef(String, String[]) + */ + NodeRef resolveNodeReference(String[] reference); + + /** + * Path type explicit inversion of + * {@link Repository#findNodeRef(String, String[])}. + * + * @return reference array of reference segments + * @throws AlfrescoRuntimeException if an unimplemented or invalid reference + * type is provided + * @see Repository#findNodeRef(String, String[]) + */ + NodeRef resolvePathReference(String[] reference); + + /** + * QName type explicit inversion of + * {@link Repository#findNodeRef(String, String[])}.
+ * Unlike {@link Repository} {@link NodeRefResolver} implementors must + * provide an adequate implementation. + * + * @return reference array of reference segments + * @throws AlfrescoRuntimeException if an unimplemented or invalid reference + * type is provided + * @see Repository#findNodeRef(String, String[]) + */ + NodeRef resolveQNameReference(String[] reference); + + /** + * Gets the Company Home. Note this is tenant-aware if the correct Cache is + * supplied. + * + * @return company home node ref + */ + NodeRef getCompanyHome(); + + /** + * Gets the root home of the company home store + * + * @return root node ref + */ + NodeRef getRootHome(); + + /** + * Gets the Shared Home. Note this is tenant-aware if the correct Cache is + * supplied. + * + * @return shared home node ref + */ + NodeRef getSharedHome(); + + /** + * Gets the user home of the currently authenticated person + * + * @param person person + * @return user home of person + */ + NodeRef getUserHome(NodeRef person); + +} diff --git a/source/java/org/alfresco/repo/virtual/config/RepositoryNodeRefResolver.java b/source/java/org/alfresco/repo/virtual/config/RepositoryNodeRefResolver.java new file mode 100644 index 0000000000..3d1a3c4349 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/config/RepositoryNodeRefResolver.java @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.config; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.model.Repository; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * {@link Repository} based {@link NodeRefResolver} implementation.
+ */ +public class RepositoryNodeRefResolver implements NodeRefResolver +{ + public static final String PATH_REF_EXPRESSION = "path"; + + public static final String NODE_REF_EXPRESSION = "node"; + + public static final String QNAME_REF_EXPRESSION = "qname"; + + private Repository repository; + + private NodeService nodeService; + + private NamespacePrefixResolver namespacePrefixResolver; + + public RepositoryNodeRefResolver() + { + + } + + public RepositoryNodeRefResolver(Repository repository) + { + super(); + this.repository = repository; + } + + @Override + public NodeRef resolveNodeReference(String[] reference) + { + return repository.findNodeRef(NODE_REF_EXPRESSION, + reference); + } + + @Override + public NodeRef resolvePathReference(String[] reference) + { + return repository.findNodeRef(PATH_REF_EXPRESSION, + reference); + } + + @Override + public NodeRef resolveQNameReference(String[] reference) + { + NodeRef theNodeRef = null; + + if (reference.length > 0) + { + + theNodeRef = repository.getRootHome(); + + List rootChildren = nodeService + .getChildAssocs(theNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(reference[0], + namespacePrefixResolver), + false); + if (rootChildren == null || rootChildren.isEmpty()) + { + // one more attempt : might be a contains assoc + + rootChildren = nodeService.getChildAssocs(theNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(reference[0], + namespacePrefixResolver), + false); + } + + if (rootChildren == null || rootChildren.isEmpty()) + { + // one more attempt : might be a contains assoc though + return null; + } + else + { + theNodeRef = rootChildren.get(0).getChildRef(); + } + + for (int i = 1; i < reference.length; i++) + { + + List children = nodeService + .getChildAssocs(theNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(reference[i], + namespacePrefixResolver), + false); + if (children == null || children.isEmpty()) + { + theNodeRef = null; + break; + } + else + { + theNodeRef = children.get(0).getChildRef(); + } + } + } + return theNodeRef; + } + + public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) + { + this.namespacePrefixResolver = namespacePrefixResolver; + } + + public void setRepository(Repository repository) + { + this.repository = repository; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + @Override + public NodeRef getCompanyHome() + { + return repository.getCompanyHome(); + } + + @Override + public NodeRef getRootHome() + { + return repository.getRootHome(); + } + + @Override + public NodeRef getSharedHome() + { + return repository.getSharedHome(); + } + + @Override + public NodeRef getUserHome(NodeRef person) + { + return repository.getUserHome(person); + } + + @Override + public NodeRef createNamePath(String[] reference) + { + Stack notFoundStack = new Stack<>(); + NodeRef found; + if (reference == null || reference.length == 0) + { + found = getRootHome(); + } + else + { + NodeRef parentNodeRef = null; + for (int i = reference.length; i > 0; i--) + { + String[] parent = new String[i]; + System.arraycopy(reference, + 0, + parent, + 0, + i); + parentNodeRef = resolvePathReference(parent); + if (parentNodeRef != null) + { + break; + } + else + { + notFoundStack.push(reference[i - 1]); + } + } + while (!notFoundStack.isEmpty()) + { + String toCreate = notFoundStack.pop(); + final HashMap newProperties = new HashMap(); + newProperties.put(ContentModel.PROP_NAME, + toCreate); + ChildAssociationRef newAssoc = nodeService + .createNode(parentNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + QName.createValidLocalName(toCreate)), + ContentModel.TYPE_FOLDER, + newProperties); + parentNodeRef = newAssoc.getChildRef(); + } + + found = parentNodeRef; + } + return found; + } + + @Override + public NodeRef createQNamePath(String[] reference, String[] names) + { + Stack notFoundStack = new Stack<>(); + Stack notFoundNameStack = new Stack<>(); + NodeRef found; + if (reference == null || reference.length == 0) + { + found = getRootHome(); + } + else + { + NodeRef parentNodeRef = null; + for (int i = reference.length; i > 0; i--) + { + String[] parent = new String[i]; + System.arraycopy(reference, + 0, + parent, + 0, + i); + parentNodeRef = resolveQNameReference(parent); + if (parentNodeRef != null) + { + break; + } + else + { + if (names != null) + { + int offset = reference.length - i; + if (offset < names.length) + { + notFoundNameStack.push(names[names.length - 1 - offset]); + } + } + notFoundStack.push(reference[i - 1]); + } + } + while (!notFoundStack.isEmpty()) + { + String stringQNameToCreate = notFoundStack.pop(); + QName qNameToCreate = QName.createQName(stringQNameToCreate, + namespacePrefixResolver); + String nameToCreate; + if (!notFoundNameStack.isEmpty()) + { + nameToCreate = notFoundNameStack.pop(); + } + else + { + nameToCreate = qNameToCreate.getLocalName(); + } + final HashMap newProperties = new HashMap(); + newProperties.put(ContentModel.PROP_NAME, + nameToCreate); + ChildAssociationRef newAssoc = nodeService.createNode(parentNodeRef, + ContentModel.ASSOC_CONTAINS, + qNameToCreate, + ContentModel.TYPE_FOLDER, + newProperties); + parentNodeRef = newAssoc.getChildRef(); + } + + found = parentNodeRef; + } + + return found; + + } + +} diff --git a/source/java/org/alfresco/repo/virtual/model/SystemTemplateLocationsConstraint.java b/source/java/org/alfresco/repo/virtual/model/SystemTemplateLocationsConstraint.java new file mode 100644 index 0000000000..8cff3c1977 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/model/SystemTemplateLocationsConstraint.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.model; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.dictionary.constraint.ListOfValuesConstraint; +import org.alfresco.repo.virtual.config.NodeRefExpression; +import org.alfresco.repo.virtual.store.SystemVirtualizationMethod; +import org.alfresco.service.cmr.dictionary.ConstraintException; +import org.alfresco.service.cmr.i18n.MessageLookup; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * A system paths string values constraint to be used in conjunction with + * {@link SystemVirtualizationMethod}s to provide available system paths by + * exploring a predefined location in the java classpath ad a predefined + * repository location. + * + * @author Silviu Dinuta + */ +public class SystemTemplateLocationsConstraint extends ListOfValuesConstraint +{ + public static final String NULL_SYSTEM_TEMPLATE = "nullSystemTemplate"; + + public static final String NULL_SYSTEM_TEMPLATE_MESSAGE = "vm_virtualization.nullSystemTemplate.message"; + + private NodeService nodeService; + + private String templatesParentClasspath; + + private NodeRefExpression templatesParentRepositoryPath; + + private NamespacePrefixResolver namespacePrefixResolver; + + private String repositoryTemplateTypeName; + + public SystemTemplateLocationsConstraint() + { + super(); + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setTemplatesParentClasspath(String templatesParentClasspath) + { + this.templatesParentClasspath = templatesParentClasspath; + } + + public String getTemplatesParentClasspath() + { + return this.templatesParentClasspath; + } + + public NodeRefExpression getTemplatesParentRepositoryPath() + { + return this.templatesParentRepositoryPath; + } + + public void setTemplatesParentRepositoryPath(NodeRefExpression templatesParentRepositoryPath) + { + this.templatesParentRepositoryPath = templatesParentRepositoryPath; + } + + public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) + { + this.namespacePrefixResolver = namespacePrefixResolver; + } + + public void setRepositoryTemplateTypeName(String repositoryTemplateTypeName) + { + this.repositoryTemplateTypeName = repositoryTemplateTypeName; + } + + @Override + public List getRawAllowedValues() + { + List result = null; + try + { + result = loadClasspathTemplates(templatesParentClasspath, + "json"); + } + catch (IOException e) + { + throw new ConstraintException("ListTemplateTypesConstraints", + e); + } + List repositoryTemplates = loadRepositoryTemplates(templatesParentRepositoryPath); + result.addAll(repositoryTemplates); + if (result.size() == 0) + { + result.add(NULL_SYSTEM_TEMPLATE); + } + super.setAllowedValues(result); + return result; + } + + @Override + public List getAllowedValues() + { + if (sorted == true) + { + List rawValues = getRawAllowedValues(); + List values = new ArrayList(rawValues); + Collections.sort(values, + new LabelComparator()); + return values; + } + else + { + return super.getAllowedValues(); + } + } + + @Override + public String getDisplayLabel(String constraintAllowableValue, MessageLookup messageLookup) + { + if (constraintAllowableValue.startsWith("N")) + { + Serializable nameProperty = nodeService.getProperty(new NodeRef(constraintAllowableValue.substring(1)), + ContentModel.PROP_NAME); + return nameProperty.toString(); + } + else if (constraintAllowableValue.equals(SystemTemplateLocationsConstraint.NULL_SYSTEM_TEMPLATE)) + { + String message = messageLookup.getMessage(SystemTemplateLocationsConstraint.NULL_SYSTEM_TEMPLATE_MESSAGE, + I18NUtil.getLocale()); + return message == null ? constraintAllowableValue : message; + } + else + { + return constraintAllowableValue.substring(constraintAllowableValue.lastIndexOf("/") + 1); + } + } + + @Override + protected void evaluateSingleValue(Object value) + { + super.setAllowedValues(getAllowedValues()); + super.evaluateSingleValue(value); + } + + private List loadClasspathTemplates(String templatesParentClasspath, String... extensions) + throws IOException + { + List result = new ArrayList(5); + List files = new ArrayList(5); + + ClassLoader cl = this.getClass().getClassLoader(); + ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl); + Resource[] resources = resolver.getResources("classpath*:" + templatesParentClasspath + "/*"); + for (Resource resource : resources) + { + files.add(resource.getFilename()); + } + + if (extensions != null && extensions.length > 0) + { + + String extensionStr = ""; + for (int i = 0; i < extensions.length; i++) + { + if (i == extensions.length - 1) + { + extensionStr += extensions[i]; + } + else + { + extensionStr += extensions[i] + "|"; + } + } + String fileExtensions = "(?i).*\\.(" + extensionStr + ")$"; + Pattern pattern = Pattern.compile(fileExtensions); + for (String file : files) + { + if (pattern.matcher(file).matches()) + { + result.add("C" + templatesParentClasspath + "/" + file); + } + } + } + return result; + } + + private List loadRepositoryTemplates(NodeRefExpression templatesParentRepositoryPath) + { + List result = new ArrayList(); + NodeRef findNodeRef = templatesParentRepositoryPath.resolve(); + + if (findNodeRef != null) + { + final QName repositoryTemplateTypeQName = QName.createQName(repositoryTemplateTypeName, + namespacePrefixResolver); + Set searchTypeQNames = new HashSet(); + searchTypeQNames.add(repositoryTemplateTypeQName); + + List children = nodeService.getChildAssocs(findNodeRef, + searchTypeQNames); + for (ChildAssociationRef childAssociationRef : children) + { + NodeRef childNodeRef = childAssociationRef.getChildRef(); + QName childType = nodeService.getType(childNodeRef); + if (searchTypeQNames.contains(childType)) + { + result.add("N" + childNodeRef.toString()); + } + } + } + return result; + } + + private class LabelComparator implements Comparator + { + + @Override + public int compare(String o1, String o2) + { + return getDisplayLabel(o1, + null).compareTo(getDisplayLabel(o2, + null)); + } + } +} diff --git a/source/java/org/alfresco/repo/virtual/page/InvalidPageBounds.java b/source/java/org/alfresco/repo/virtual/page/InvalidPageBounds.java new file mode 100644 index 0000000000..b739b06134 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/page/InvalidPageBounds.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.page; + +public class InvalidPageBounds extends PageCollationException +{ + + /** + * + */ + private static final long serialVersionUID = -1197373266957787084L; + + public InvalidPageBounds() + { + super(); + } + + public InvalidPageBounds(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) + { + super(message, + cause, + enableSuppression, + writableStackTrace); + } + + public InvalidPageBounds(String message, Throwable cause) + { + super(message, + cause); + } + + public InvalidPageBounds(String message) + { + super(message); + } + + public InvalidPageBounds(Throwable cause) + { + super(cause); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/page/PageCollationException.java b/source/java/org/alfresco/repo/virtual/page/PageCollationException.java new file mode 100644 index 0000000000..f424704b52 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/page/PageCollationException.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.page; + +public class PageCollationException extends Exception +{ + + /** + * + */ + private static final long serialVersionUID = -6956517169771939562L; + + public PageCollationException() + { + super(); + } + + public PageCollationException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) + { + super(message, + cause, + enableSuppression, + writableStackTrace); + } + + public PageCollationException(String message, Throwable cause) + { + super(message, + cause); + } + + public PageCollationException(String message) + { + super(message); + } + + public PageCollationException(Throwable cause) + { + super(cause); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/page/PageCollator.java b/source/java/org/alfresco/repo/virtual/page/PageCollator.java new file mode 100644 index 0000000000..1ac9daf750 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/page/PageCollator.java @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.page; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; + +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A generic utility for arranging a {@link List} of objects of type + * R into a R objects page obtained from a + * {@link PagingResultsSource} considering a given {@link PagingRequest} for the + * resulted {@link PagingResults}. + * + * @author Bogdan Horje + * @param + */ +public class PageCollator +{ + private static Log logger = LogFactory.getLog(PageCollator.class); + + /** + * Implementors are paging request capable objects - i.e. can provide + * data-paged results of type R considering a given + * {@link PagingRequest}. + * + * @param + */ + public interface PagingResultsSource + { + PagingResults retrieve(PagingRequest pr) throws PageCollationException; + } + + /** + * @param objects + * @param objectPageSurce + * @param pagingRequest + * @param comparator + * @return a {@link PagingResults} R objects obtained from merging a + * collection of R objects with a paged result obtained from a + * {@link PagingResultsSource} considering the a merged result + * {@link PagingRequest} + * @throws PageCollationException + */ + public PagingResults collate(List objects, PagingResultsSource objectPageSurce, + PagingRequest pagingRequest, Comparator comparator) throws PageCollationException + { + final int skip = pagingRequest.getSkipCount(); + final int pageSize = pagingRequest.getMaxItems(); + + if (skip < 0 || pageSize < 0) + { + throw new InvalidPageBounds("Negative page skip index and/or bounds."); + } + + int preemptiveSkip = Math.max(0, + skip - objects.size()); + int pageSkip = skip - preemptiveSkip; + int preemptiveSize = pageSize + pageSkip; + PagingResults pageResults = null; + try + { + PagingRequest preemptiveRequest = new PagingRequest(preemptiveSkip, + preemptiveSize, + pagingRequest.getQueryExecutionId()); + preemptiveRequest.setRequestTotalCountMax(pagingRequest.getRequestTotalCountMax()); + pageResults = objectPageSurce.retrieve(preemptiveRequest); + } + catch (InvalidPageBounds e) + { + if (logger.isDebugEnabled()) + { + logger.debug(e); + } + pageResults = new PagingResults() + { + + @Override + public List getPage() + { + return Collections.emptyList(); + } + + @Override + public boolean hasMoreItems() + { + return false; + } + + @Override + public Pair getTotalResultCount() + { + return new Pair(null, + null); + } + + @Override + public String getQueryExecutionId() + { + return null; + } + }; + } + + return collate(objects, + pageResults, + pageSkip, + pagingRequest, + comparator); + } + + private PagingResults collate(List objects, final PagingResults objectPageSurce, int pageSkip, + final PagingRequest pagingRequest, Comparator comparator) + { + final int pageSize = pagingRequest.getMaxItems(); + final List inPageList = objectPageSurce.getPage(); + final List collatedPageList = new LinkedList<>(); + final boolean endOfCollation = collate(objects, + inPageList, + pageSkip, + pageSize, + comparator, + collatedPageList); + final int resultsSize = objects.size(); + + final Pair pageTotal = objectPageSurce.getTotalResultCount(); + Integer pageTotalFirst = null; + Integer pageTotalSecond = null; + + if (pageTotal != null) + { + pageTotalFirst = pageTotal.getFirst(); + pageTotalSecond = pageTotal.getSecond(); + } + + final Pair total = new Pair<>(pageTotalFirst == null ? null : pageTotalFirst + resultsSize, + pageTotalSecond == null ? null : pageTotalSecond + resultsSize); + + final boolean hasMoreItems = objectPageSurce.hasMoreItems() || !endOfCollation; + + return new PagingResults() + { + + @Override + public List getPage() + { + return collatedPageList; + } + + @Override + public boolean hasMoreItems() + { + return hasMoreItems; + } + + @Override + public Pair getTotalResultCount() + { + return total; + } + + @Override + public String getQueryExecutionId() + { + return pagingRequest.getQueryExecutionId(); + } + + }; + } + + private boolean collate(List objects, List pageObjects, int pageSkip, int pageSize, Comparator comparator, + List collatedResult) + { + + final int resultsSize = objects.size(); + final int inPageSize = pageObjects.size(); + if (pageSkip >= resultsSize + inPageSize) + { + return true; + } + + List collation = new ArrayList<>(objects.size() + pageObjects.size()); + collation.addAll(pageObjects); + + for (int i = 0; i < resultsSize; i++) + { + final int collationSize = collation.size(); + final R result = objects.get(i); + int j = 0; + + if (comparator != null) + { + for (; j < collationSize; j++) + { + final R collated = collation.get(j); + if (comparator.compare(result, + collated) <= 0) + { + break; + } + } + } + + collation.add(j, + result); + } + + final R[] collationArray = (R[]) collation.toArray(); + final int zeroPageSize = (pageSize == 0 ? collationArray.length - pageSkip : pageSize); + final int to = Math.min(pageSkip + zeroPageSize, + collationArray.length); + + collatedResult.addAll(Arrays.asList(Arrays.copyOfRange(collationArray, + pageSkip, + to))); + + return to == collationArray.length; + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/AbstractProtocolMethod.java b/source/java/org/alfresco/repo/virtual/ref/AbstractProtocolMethod.java new file mode 100644 index 0000000000..570e60e6db --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/AbstractProtocolMethod.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +/** + * Handles protocol method dispatch. Not supported: base protocol + * dispatches. The vanilla protocol can be handled by default + * as the virtual protocol. + * + * @param + */ +public abstract class AbstractProtocolMethod implements ProtocolMethod +{ + public static final String PATH_SEPARATOR = "/"; + + @Override + public R execute(VanillaProtocol vanillaProtocol, Reference reference) throws ProtocolMethodException + { + // cast to force virtual protocol execution + + return execute((VirtualProtocol) vanillaProtocol, + reference); + } + + @Override + public R execute(VirtualProtocol virtualProtocol, Reference reference) throws ProtocolMethodException + { + return throwUnsupportedProtocolException(virtualProtocol); + } + + @Override + public R execute(NodeProtocol protocol, Reference reference) throws ProtocolMethodException + { + return throwUnsupportedProtocolException(protocol); + } + + private R throwUnsupportedProtocolException(Protocol protocol) throws ProtocolMethodException + { + throw new ProtocolMethodException("Unsupported protocol " + protocol); + } + + @Override + public R execute(Protocol protocol, Reference reference) throws ProtocolMethodException + { + return throwUnsupportedProtocolException(protocol); + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/ClasspathResource.java b/source/java/org/alfresco/repo/virtual/ref/ClasspathResource.java new file mode 100644 index 0000000000..db5ceecf2f --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/ClasspathResource.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.io.InputStream; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ActualEnvironmentException; + +/** + * Identifies content from a classpath + */ +public class ClasspathResource implements Resource +{ + + private String classpath; + + public ClasspathResource(String classpath) + { + this.classpath = classpath; + } + + public String getClasspath() + { + return classpath; + } + + @Override + public String stringify(Stringifier stringifier) throws ReferenceEncodingException + { + return stringifier.stringifyResource(this); + } + + @Override + public int hashCode() + { + return classpath != null ? classpath.hashCode() : 0; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj == null) + { + return false; + } + else if (!(obj instanceof ClasspathResource)) + { + return false; + } + + ClasspathResource other = (ClasspathResource) obj; + + if (classpath == null) + { + return other.classpath == null; + } + else + { + return this.classpath.equals(other.classpath); + } + } + + @Override + public R processWith(ResourceProcessor processor) throws ResourceProcessingError + { + return processor.process(this); + } + + @Override + public InputStream asStream(ActualEnvironment environment) throws ActualEnvironmentException + { + return environment.openContentStream(classpath); + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/Encoding.java b/source/java/org/alfresco/repo/virtual/ref/Encoding.java new file mode 100644 index 0000000000..ffb188b241 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/Encoding.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.io.Serializable; + +/** + * A {@link Reference} {@link String} encoding definition.
+ * + * @see ReferenceParser + * @see Stringifier + * @author Bogdan Horje + */ +public class Encoding implements Serializable +{ + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * String encoding token - signals the beginning of a reference encoded with + * this encoding in given String. + */ + public final Character token; + + /** + * true if {@link Reference}s encoded using this encoding + * can be part of URLs. + */ + public final boolean urlNative; + + public final ReferenceParser parser; + + public final Stringifier stringifier; + + public Encoding(Character token, ReferenceParser parser, Stringifier stringifier, boolean urlNative) + { + super(); + this.token = token; + this.parser = parser; + this.stringifier = stringifier; + this.urlNative = urlNative; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/Encodings.java b/source/java/org/alfresco/repo/virtual/ref/Encodings.java new file mode 100644 index 0000000000..1f7befe3fd --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/Encodings.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.HashMap; +import java.util.Map; + +/** + * Common {@link Reference} encodings. + * + * @author Bogdan Horje + */ +public enum Encodings +{ + /** + * {@link Reference} string encoding that parses and produces reference + * string representations that conform to the following grammar : + * + *
+     *  reference            := protocol':'resource[':'parameters]
+     *  protocol             := 'virtual'|'node'|'template'
+     *  resource             := repositoryResource | classpathResource
+     *  repositoryResource   := 'repository'':'repositoryLocation
+     *  repositoryLocation   := ('path'':'path)|('node'':'nodeRef)
+     *  classpathResource    := 'classpath'':'path
+     *  path                 := string
+     *  parameters           := parameter[':'parameters]
+     *  parameter            := referenceParameter | integerParameter | stringParameter
+     *  referenceParameter   := 'ref'':'reference 
+     *  resourceParameter    := 'r'':'resource
+     *  integerParameter     := 'i'':'integer
+     *  stringParameter      := 's'':'string
+     * 
+     * Examples (syntax is correct, but not the semantic):  
+     *  
+     * virtual:classpath:/org/alfresco/
+     * virtual:classpath:/org/alfresco/:r:repository:node:workspace:SpacesStore:0029-222-333-444
+     * virtual:classpath:/org/alfresco/:r:repository:node:workspace:SpacesStore:0029-222-333-444:r:repository:path:/Data Dictionary/Virtual Folders/claim.json
+     * node:repository:node:workspace:SpacesStore:0029-222-333-444:r:virtual:repository:node:workspace:SpacesStore:0029-122-333-0023
+     * 
+ */ + PLAIN(new Encoding('p', + new PlainReferenceParser(), + new PlainStringifier(), + false)), + + /** + * A condensed {@link Reference} string representation. + */ + ZERO(new Encoding('0', + new ZeroReferenceParser(), + new ZeroStringifier(), + false)), + + /** + * A hash based condensed {@link Reference} string representation. + */ + HASH(new Encoding('H', + new HashReferenceParser(), + new HashStringifier(), + true)); + + private static volatile Map tokenMap; + + public static synchronized Encoding fromToken(Character token) + { + return tokenMap.get(token); + } + + private static synchronized void register(Encoding encoding) + { + if (tokenMap == null) + { + tokenMap = new HashMap<>(); + } + tokenMap.put(encoding.token, + encoding); + } + + public final Encoding encoding; + + Encodings(Encoding encoding) + { + this.encoding = encoding; + register(encoding); + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/GetActualNodeRefMethod.java b/source/java/org/alfresco/repo/virtual/ref/GetActualNodeRefMethod.java new file mode 100644 index 0000000000..655125848d --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/GetActualNodeRefMethod.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ActualEnvironmentException; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Returns the actual {@link NodeRef} of a given reference.
+ * Actual {@link NodeRef} are node references of content elements found in the + * Alfresco repository that are subjected to a virtualization process. + * + * @author Bogdan Horje + */ +public class GetActualNodeRefMethod extends AbstractProtocolMethod +{ + private ActualEnvironment environment; + + public GetActualNodeRefMethod(ActualEnvironment environment) + { + super(); + this.environment = environment; + } + + @Override + public NodeRef execute(VirtualProtocol virtualProtocol, Reference reference) throws ProtocolMethodException + { + try + { + RepositoryLocation nodeRefLocation = virtualProtocol.getActualNodeLocation(reference); + return nodeRefLocation.asNodeRef(environment); + } + catch (ActualEnvironmentException e) + { + throw new ProtocolMethodException(e); + } + } + + @Override + public NodeRef execute(NodeProtocol protocol, Reference reference) throws ProtocolMethodException + { + return protocol.getNodeRef(reference); + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/GetAspectsMethod.java b/source/java/org/alfresco/repo/virtual/ref/GetAspectsMethod.java new file mode 100644 index 0000000000..5c7bbd60c8 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/GetAspectsMethod.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.HashSet; +import java.util.Set; + +import org.alfresco.repo.node.db.traitextender.NodeServiceTrait; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ActualEnvironmentException; +import org.alfresco.repo.virtual.VirtualContentModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +public class GetAspectsMethod extends AbstractProtocolMethod> +{ + private NodeServiceTrait nodeServiceTrait; + + private ActualEnvironment environment; + + public GetAspectsMethod(NodeServiceTrait nodeServiceTrait, ActualEnvironment environment) + { + super(); + this.nodeServiceTrait = nodeServiceTrait; + this.environment = environment; + } + + private Set createVirtualAspects() + { + Set aspects = new HashSet(); + aspects.add(VirtualContentModel.ASPECT_VIRTUAL); + return aspects; + } + + private Set createVirtualDocumentAspects() + { + Set aspects = new HashSet(); + aspects.add(VirtualContentModel.ASPECT_VIRTUAL_DOCUMENT); + return aspects; + } + + @Override + public Set execute(NodeProtocol protocol, Reference reference) throws ProtocolMethodException + { + + NodeRef nodeRef = protocol.getNodeRef(reference); + Set nodeAspects = nodeServiceTrait.getAspects(nodeRef); + Set aspects = createVirtualDocumentAspects(); + aspects.addAll(nodeAspects); + return aspects; + } + + @Override + public Set execute(VirtualProtocol virtualProtocol, Reference reference) throws ProtocolMethodException + { + try + { + Set aspects = createVirtualAspects(); + String path = virtualProtocol.getTemplatePath(reference); + if (PATH_SEPARATOR.equals(path.trim())) + { + NodeRef nodeRef; + + nodeRef = virtualProtocol.getActualNodeLocation(reference).asNodeRef(environment); + + Set nodeAspects = nodeServiceTrait.getAspects(nodeRef); + aspects.addAll(nodeAspects); + } + return aspects; + } + catch (ActualEnvironmentException e) + { + throw new ProtocolMethodException(e); + } + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/GetChildByIdMethod.java b/source/java/org/alfresco/repo/virtual/ref/GetChildByIdMethod.java new file mode 100644 index 0000000000..315c2f672a --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/GetChildByIdMethod.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +public class GetChildByIdMethod extends AbstractProtocolMethod +{ + private String childId; + + public GetChildByIdMethod(String childId) + { + super(); + this.childId = childId; + } + + /** + * Provides a child {@link Reference} obtained from the parent + * {@link Reference} and the childId. The inner template path is obtained + * from the parent {@link Reference} and then the childId String is + * concatenated to it. The child {@link Reference} is created by calling + * Protocol#replaceTemplatePathMethod with the new id String as a parameter. + * + * @param virtualProtocol + * @param reference the parent {@link Reference} + * @return the child {@link Reference} + * @throws ProtocolMethodException + */ + @Override + public Reference execute(VirtualProtocol virtualProtocol, Reference reference) throws ProtocolMethodException + { + String path = reference.execute(new GetTemplatePathMethod()).trim(); + StringBuilder pathBuilder = new StringBuilder(path); + if (!path.endsWith(PATH_SEPARATOR)) + { + pathBuilder.append(PATH_SEPARATOR); + } + pathBuilder.append(childId); + + return virtualProtocol.replaceTemplatePath(reference, + pathBuilder.toString()); + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/GetParentReferenceMethod.java b/source/java/org/alfresco/repo/virtual/ref/GetParentReferenceMethod.java new file mode 100644 index 0000000000..a578de8ead --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/GetParentReferenceMethod.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +/** + * Returns a virtual parent reference upon execution by subtracting the last + * template path element from of the given reference.
+ * For root template path references a null will be returned upon + * execution. + * + * @see VirtualProtocol#replaceTemplatePath(Reference, String) + * @author Bogdan Horje + */ +public class GetParentReferenceMethod extends AbstractProtocolMethod +{ + @Override + public Reference execute(VirtualProtocol virtualProtocol, Reference reference) throws ProtocolMethodException + { + String path = virtualProtocol.getTemplatePath(reference); + if (path.trim().endsWith(PATH_SEPARATOR)) + { + + int trailingPathIndex = path.lastIndexOf(PATH_SEPARATOR); + if (trailingPathIndex == 0) + { + return null; + } + else + { + path = path.substring(0, + trailingPathIndex); + } + } + + int index = path.lastIndexOf(PATH_SEPARATOR); + if (index < 0) + { + return null; + } + else + { + String parentPath = path.substring(0, + index); + + if (parentPath.isEmpty()) + { + if (path.length() > 1) + { + parentPath = PATH_SEPARATOR; + } + else + { + return null; + } + } + + return virtualProtocol.replaceTemplatePath(reference, + parentPath); + } + } + + @Override + public Reference execute(NodeProtocol protocol, Reference reference) throws ProtocolMethodException + { + return ((ReferenceParameter) reference.getParameters().get(0)).getValue(); + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/GetReferenceType.java b/source/java/org/alfresco/repo/virtual/ref/GetReferenceType.java new file mode 100644 index 0000000000..df9dbada6d --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/GetReferenceType.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Returns actual {@link QName} Type of a given reference
+ * indicated by the given protocol reference. + */ +public class GetReferenceType extends AbstractProtocolMethod +{ + private ActualEnvironment environment; + + public GetReferenceType(ActualEnvironment environment) + { + super(); + this.environment = environment; + } + + @Override + public QName execute(NodeProtocol protocol, Reference reference) throws ProtocolMethodException + { + NodeRef nodeRef = protocol.getNodeRef(reference); + + return environment.getType(nodeRef); + } + + @Override + public QName execute(VirtualProtocol virtualProtocol, Reference reference) throws ProtocolMethodException + { + return ContentModel.TYPE_FOLDER; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/GetTemplatePathMethod.java b/source/java/org/alfresco/repo/virtual/ref/GetTemplatePathMethod.java new file mode 100644 index 0000000000..76173194ac --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/GetTemplatePathMethod.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +/** + * Returns the virtual folder template inner path for a virtualized entity + * reference. + * + * @author Bogdan Horje + */ +public class GetTemplatePathMethod extends AbstractProtocolMethod +{ + @Override + public String execute(VirtualProtocol virtualProtocol, Reference reference) throws ProtocolMethodException + { + String path = virtualProtocol.getTemplatePath(reference); + return path; + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/GetVanillaScriptInputStreamMethod.java b/source/java/org/alfresco/repo/virtual/ref/GetVanillaScriptInputStreamMethod.java new file mode 100644 index 0000000000..c26f7efbab --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/GetVanillaScriptInputStreamMethod.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.io.InputStream; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ActualEnvironmentException; + +/** + * It returns an {@link InputStream} for the vanilla-virtual folder template resource + * indicated by the given vanilla protocol reference. + * + * @author Bogdan Horje + */ +public class GetVanillaScriptInputStreamMethod extends AbstractProtocolMethod +{ + private ActualEnvironment environment; + + public GetVanillaScriptInputStreamMethod(ActualEnvironment environment) + { + super(); + this.environment = environment; + } + + @Override + public InputStream execute(VanillaProtocol vanillaProtocol, Reference reference) throws ProtocolMethodException + { + Resource resource = vanillaProtocol.getVanillaTemplateResource(reference); + try + { + return resource.asStream(environment); + } + catch (ActualEnvironmentException e) + { + throw new ProtocolMethodException(e); + } + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/HashEncodingArtefact.java b/source/java/org/alfresco/repo/virtual/ref/HashEncodingArtefact.java new file mode 100644 index 0000000000..b72e608f76 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/HashEncodingArtefact.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +/** + * Hash encoding constants interface. + */ +public interface HashEncodingArtefact +{ + + static final String VANILLA_PROTOCOL_CODE = "1"; + + static final String VIRTUAL_PROTOCOL_CODE = "2"; + + static final String NODE_PROTOCOL_CODE = "3"; + + static final String REPOSITORY_NODEREF_RESOURCE_CODE = "1"; + + static final String REPOSITORY_PATH_CODE = "2"; + + static final String HASHED_REPOSITORY_PATH_CODE = "3"; + + static final String MIXED_REPOSITORY_PATH_CODE = "4"; + + static final String CLASSPATH_RESOUCE_CODE = "5"; + + static final String HASHED_CLASSPATH_RESOUCE_CODE = "6"; + + static final String MIXED_CLASSPATH_RESOUCE_CODE = "7"; + + static final String NUMERIC_ROOT_PATH_CODE = "1"; + + static final String NUMERIC_PATH_CODE = "2"; + + static final String HASHED_NUMERIC_PATH_CODE = "3"; + + static final String MIXED_NUMERIC_PATH_CODE = "4"; + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/HashReferenceParser.java b/source/java/org/alfresco/repo/virtual/ref/HashReferenceParser.java new file mode 100644 index 0000000000..c69281aafc --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/HashReferenceParser.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +/** + * Hash encoded reference string parser.
+ * Delegates to {@link VirtualHashReferenceParser}, + * {@link VanillaHashReferenceParser} or {@link NodeHashReferenceParser} for + * custom protocol parsing. + */ +public class HashReferenceParser implements ReferenceParser, HashEncodingArtefact +{ + /** + * + */ + private static final long serialVersionUID = -2569625423953183530L; + + private NodeHashReferenceParser nodeReferenceParser; + + private VirtualHashReferenceParser virtualReferenceParser; + + private VanillaHashReferenceParser vanillaReferenceParser; + + public HashReferenceParser() + { + HashStore cpStore = HashStoreConfiguration.getInstance().getClasspathHashStore(); + + nodeReferenceParser = new NodeHashReferenceParser(cpStore, + this); + + virtualReferenceParser = new VirtualHashReferenceParser(cpStore); + + vanillaReferenceParser = new VanillaHashReferenceParser(cpStore); + } + + @Override + public Reference parse(String referenceString) throws ReferenceParseException + { + String[] tokens = referenceString.split("-"); + Cursor c = new Cursor(tokens, + 0); + + return parse(c); + } + + public Reference parse(Cursor c) + { + if (NODE_PROTOCOL_CODE.equals(c.currentToken())) + { + return nodeReferenceParser.parse(c); + } + else if (VANILLA_PROTOCOL_CODE.equals(c.currentToken())) + { + return vanillaReferenceParser.parse(c); + } + else if (VIRTUAL_PROTOCOL_CODE.equals(c.currentToken())) + { + return virtualReferenceParser.parse(c); + } + else + { + throw new ReferenceEncodingException("Unknown reference code " + c.currentToken()); + } + + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/HashStore.java b/source/java/org/alfresco/repo/virtual/ref/HashStore.java new file mode 100644 index 0000000000..729f935dda --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/HashStore.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.HashMap; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * A synchronized string hash code mapping store.
+ * Associates a string with a given string-hash. + */ +public class HashStore +{ + + private HashMap hashStore = new HashMap<>(); + + private HashMap lookupStore = new HashMap<>(); + + private ReentrantReadWriteLock configurationLock = new ReentrantReadWriteLock(); + + public void put(String string, String hash) + { + configurationLock.writeLock().lock(); + try + { + hashStore.put(string, + hash); + lookupStore.put(hash, + string); + } + finally + { + configurationLock.writeLock().unlock(); + } + } + + public String hash(String string) + { + configurationLock.readLock().lock(); + try + { + return hashStore.get(string); + } + finally + { + configurationLock.readLock().unlock(); + } + + } + + public String lookup(String hash) + { + configurationLock.readLock().lock(); + try + { + return lookupStore.get(hash); + } + finally + { + configurationLock.readLock().unlock(); + } + + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/HashStoreConfiguration.java b/source/java/org/alfresco/repo/virtual/ref/HashStoreConfiguration.java new file mode 100644 index 0000000000..c53785ccc3 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/HashStoreConfiguration.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import org.alfresco.repo.version.Version2Model; +import org.alfresco.repo.version.VersionModel; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.version.VersionService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Global hash store configuration.
+ * Used for custom global {@link HashStore}s required for {@link Reference} + * string encoding. + */ +public class HashStoreConfiguration +{ + private static Log logger = LogFactory.getLog(HashStoreConfiguration.class); + + public static class HashStoreConfigurationBean + { + public void setClasspathsHashes(String classpathsHashes) + { + String[] hashes = classpathsHashes.split(","); + for (int i = 0; i < hashes.length; i++) + { + String[] pathHash = hashes[i].split("->"); + if (pathHash.length != 2) + { + logger.error("Invalid classpath hash configuration " + hashes[i]); + } + else + { + HashStoreConfiguration.getInstance().getClasspathHashStore().put(pathHash[0], + pathHash[1]); + if (logger.isDebugEnabled()) + { + logger.debug("Configured classpath hash " + pathHash[0] + " -> " + pathHash[1]); + } + } + } + } + } + + private static HashStoreConfiguration instance = null; + + public static synchronized HashStoreConfiguration getInstance() + { + if (instance == null) + { + instance = new HashStoreConfiguration(); + } + + return instance; + } + + private final HashStore classpathStore; + + private HashStore storeProtocolStore; + + private HashStore storeIdStore; + + private HashStoreConfiguration() + { + classpathStore = new HashStore(); + + storeProtocolStore = new HashStore(); + + storeIdStore = new HashStore(); + + storeProtocolStore.put(StoreRef.PROTOCOL_WORKSPACE, + "1"); + storeProtocolStore.put(StoreRef.PROTOCOL_ARCHIVE, + "2"); + storeProtocolStore.put(StoreRef.PROTOCOL_AVM, + "3"); + storeProtocolStore.put(StoreRef.PROTOCOL_DELETED, + "4"); + storeProtocolStore.put(StoreRef.PROTOCOL_TEST, + "5"); + storeProtocolStore.put(VersionService.VERSION_STORE_PROTOCOL, + "6"); + storeIdStore.put("SpacesStore", + "1"); + storeIdStore.put(VersionModel.STORE_ID, + "2"); + storeIdStore.put(Version2Model.STORE_ID, + "3"); + } + + public HashStore getClasspathHashStore() + { + return classpathStore; + } + + public HashStore getStoreIdStore() + { + return this.storeIdStore; + } + + public HashStore getStoreProtocolStore() + { + return this.storeProtocolStore; + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/HashStringifier.java b/source/java/org/alfresco/repo/virtual/ref/HashStringifier.java new file mode 100644 index 0000000000..9f4beea2df --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/HashStringifier.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.List; + +/** + * Hash encoded reference stringifier.
+ * Delegates to {@link VanillaHashStringifier}, + * {@link VirtualHashStringifier} or {@link NodeHashStringifier} for + * custom protocol parsing. + */ +public class HashStringifier implements Stringifier +{ + + /** + * + */ + private static final long serialVersionUID = 2213824445193662644L; + + private NodeHashStringifier nodeStringifier; + + private VirtualHashStringifier virtualStringifier; + + private VanillaHashStringifier vanillaStringifier; + + public HashStringifier() + { + HashStore cpStore = HashStoreConfiguration.getInstance().getClasspathHashStore(); + nodeStringifier = new NodeHashStringifier(cpStore, + this); + virtualStringifier = new VirtualHashStringifier(cpStore, + this); + + vanillaStringifier = new VanillaHashStringifier(cpStore, + this); + } + + @Override + public String stringify(Reference reference) throws ReferenceEncodingException + { + final Protocol protocol = reference.getProtocol(); + + if (Protocols.NODE.protocol.equals(protocol)) + { + return nodeStringifier.stringify(reference); + } + else if (Protocols.VIRTUAL.protocol.equals(protocol)) + { + return virtualStringifier.stringify(reference); + } + else if (Protocols.VANILLA.protocol.equals(protocol)) + { + return vanillaStringifier.stringify(reference); + } + else + { + return stringifyUnknown(reference); + } + } + + private String stringifyUnknown(Reference reference) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Could not stringify unknown protocol reference s" + + reference.encode(Encodings.PLAIN.encoding)); + } + + @Override + public String stringify(Resource resource) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Unknown hash protocol."); + } + + @Override + public String stringifyResource(Resource resource) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Unknown hash protocol."); + } + + @Override + public String stringifyResource(RepositoryResource resource) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Unknown hash protocol."); + } + + @Override + public String stringifyResource(ClasspathResource resource) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Unknown hash protocol."); + } + + @Override + public String stringify(RepositoryLocation repositoryLocation) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Unknown hash protocol."); + } + + @Override + public String stringifyRepositoryLocation(RepositoryLocation repositoryLocation) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Unknown hash protocol."); + } + + @Override + public String stringifyRepositoryLocation(RepositoryNodeRef repositoryNodeRef) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Unknown hash protocol."); + } + + @Override + public String stringifyRepositoryLocation(RepositoryPath repositoryPath) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Unknown hash protocol."); + } + + @Override + public String stringify(List parameters) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Unknown hash protocol."); + } + + @Override + public String stringify(Parameter parameter) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Unknown hash protocol."); + } + + @Override + public String stringifyParameter(Parameter parameter) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Unknown hash protocol."); + } + + @Override + public String stringifyParameter(ResourceParameter resourceParameter) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Unknown hash protocol."); + } + + @Override + public String stringifyParameter(StringParameter stringParameter) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Unknown hash protocol."); + } + + @Override + public String stringifyParameter(ReferenceParameter referenceParameter) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Unknown hash protocol."); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/HierarchicalPathHasher.java b/source/java/org/alfresco/repo/virtual/ref/HierarchicalPathHasher.java new file mode 100644 index 0000000000..80fa44fbc7 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/HierarchicalPathHasher.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import org.alfresco.util.Pair; +import org.alfresco.util.ParameterCheck; +import org.apache.commons.codec.binary.Base64; + +/** + * Creates and looks up hashes of '/' paths defining strings.
+ * Paths are hashed using {@link HashStore} defined hashes.
+ * Store defined hashes are matched for the longest possible sub-path of a given + * path. The remaining path is encoded using a Base64 encoder. The two resulted + * strings. + */ +public abstract class HierarchicalPathHasher implements PathHasher +{ + private static String normalizePath(String classpath) + { + String normalizedClasspath = classpath.trim(); + if (!normalizedClasspath.startsWith("/")) + { + normalizedClasspath = "/" + normalizedClasspath; + } + if (normalizedClasspath.endsWith("/")) + { + normalizedClasspath = normalizedClasspath.substring(0, + normalizedClasspath.length() - 1); + } + return normalizedClasspath; + } + + protected abstract String hashSubpath(String subpath); + + protected abstract String lookupSubpathHash(String hash); + + @Override + public Pair hash(String path) + { + ParameterCheck.mandatoryString("path", + path); + + String normalClasspath = normalizePath(path); + String searchedClasspath = normalClasspath; + String notFoundPath = null; + String hash = hashSubpath(searchedClasspath); + + while (hash == null) + { + int lastSeparator = searchedClasspath.lastIndexOf('/'); + if (lastSeparator < 0) + { + String code = new String(Base64.encodeBase64(normalClasspath.getBytes(), + false)); + return new Pair(null, + code); + } + + if (notFoundPath != null) + { + notFoundPath = searchedClasspath.substring(lastSeparator + 1) + "/" + notFoundPath; + + } + else + { + notFoundPath = searchedClasspath.substring(lastSeparator + 1); + + } + + searchedClasspath = searchedClasspath.substring(0, + lastSeparator); + hash = hashSubpath(searchedClasspath); + + if (hash != null) + { + String notFoundClasspathBase64 = new String(Base64.encodeBase64(notFoundPath.getBytes(), + false)); + + return new Pair(hash, + notFoundClasspathBase64); + } + } + + return new Pair(hash, + null); + + } + + @Override + public String lookup(Pair hash) + { + if (hash.getSecond() == null) + { + return lookupSubpathHash(hash.getFirst()); + } + else if (hash.getFirst() == null) + { + return lookupSubpathCode(hash.getSecond()); + } + else + { + String lHash = lookupSubpathHash(hash.getFirst()); + String lCode = lookupSubpathCode(hash.getSecond()); + return lHash + "/" + lCode; + } + } + + private String lookupSubpathCode(String code) + { + if (code.isEmpty()) + { + return "/"; + } + byte[] decodedBytes = Base64.decodeBase64(code.getBytes()); + return new String(decodedBytes); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/NewVirtualReferenceMethod.java b/source/java/org/alfresco/repo/virtual/ref/NewVirtualReferenceMethod.java new file mode 100644 index 0000000000..5377f6e59f --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/NewVirtualReferenceMethod.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import org.alfresco.service.cmr.repository.NodeRef; + +public class NewVirtualReferenceMethod extends AbstractProtocolMethod +{ + private String templateSysPath = null; + + private NodeRef templateRef = null; + + private String templatePath = null; + + private NodeRef actualNodeRef = null; + + /** + * classpath to the javascript processor of vanilla JSON templates used to construct virtual folders + */ + private String vanillaProcessorClasspath = null; + + /** + * @param templateSysPath + * @param templatePath + * @param actualNodeRef + * @param vanillaProcessorClasspath + * @deprecated In future system paths will be replaced with actual resources + * or string encoded references + */ + public NewVirtualReferenceMethod(String templateSysPath, String templatePath, NodeRef actualNodeRef, + String vanillaProcessorClasspath) + { + super(); + this.templateSysPath = templateSysPath; + this.templatePath = templatePath; + this.actualNodeRef = actualNodeRef; + this.vanillaProcessorClasspath = vanillaProcessorClasspath; + } + + public NewVirtualReferenceMethod(NodeRef templateRef, String templatePath, NodeRef actualNodeRef, + String vanillaProcessorClasspath) + { + super(); + this.templateRef = templateRef; + this.templatePath = templatePath; + this.actualNodeRef = actualNodeRef; + this.vanillaProcessorClasspath = vanillaProcessorClasspath; + } + + @Override + public Reference execute(VirtualProtocol virtualProtocol, Reference reference) throws ProtocolMethodException + { + if (templateRef != null) + { + return virtualProtocol.newReference(templateRef, + templatePath, + actualNodeRef); + } + else + { + return virtualProtocol.newReference(templateSysPath, + templatePath, + actualNodeRef); + } + } + + @Override + public Reference execute(VanillaProtocol vanillaProtocol, Reference reference) throws ProtocolMethodException + { + if (templateRef != null) + { + return vanillaProtocol.newReference(vanillaProcessorClasspath, + templatePath, + actualNodeRef, + templateRef); + + } + else + { + return vanillaProtocol.newReference(vanillaProcessorClasspath, + templatePath, + actualNodeRef, + templateSysPath); + } + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/NodeHashReferenceParser.java b/source/java/org/alfresco/repo/virtual/ref/NodeHashReferenceParser.java new file mode 100644 index 0000000000..e37d717efe --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/NodeHashReferenceParser.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import org.alfresco.repo.virtual.ref.ReferenceParser.Cursor; + +/** + * Custom parser for hash encoded strings of {@link Reference}s having the + * protocol set to {@link NodeProtocol}. + */ +public class NodeHashReferenceParser extends ProtocolHashParser +{ + private HashReferenceParser referenceParser; + + public NodeHashReferenceParser(HashStore classpathHashStore, HashReferenceParser referenceParser) + { + super(classpathHashStore); + this.referenceParser = referenceParser; + } + + @Override + public Reference parse(Cursor cursor) throws ReferenceParseException + { + if (!NODE_PROTOCOL_CODE.equals(cursor.currentToken())) + { + throw new ReferenceParseException("Node token \"" + NODE_PROTOCOL_CODE + "\" expected instead of \"" + + cursor.currentToken() + "\""); + } + cursor.i++; + + Resource resource = parseResource(cursor); + + Reference parentReference = referenceParser.parse(cursor); + + return NodeProtocol.newReference(Encodings.HASH.encoding, + resource, + parentReference); + + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/NodeHashStringifier.java b/source/java/org/alfresco/repo/virtual/ref/NodeHashStringifier.java new file mode 100644 index 0000000000..04f2530a27 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/NodeHashStringifier.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.List; +/** + * Custom stringifier for hash encoded strings of {@link Reference}s having the + * protocol set to {@link NodeProtocol}. + */ +public class NodeHashStringifier extends ProtocolHashStringifier +{ + + /** + * + */ + private static final long serialVersionUID = 1L; + + + public NodeHashStringifier(HashStore classpathHashStore, Stringifier referenceDispatcher) + { + super(classpathHashStore, + referenceDispatcher); + } + + @Override + public String stringify(Reference reference) throws ReferenceEncodingException + { + final Protocol protocol = reference.getProtocol(); + if (!Protocols.NODE.protocol.equals(protocol)) + { + throw new ReferenceEncodingException("Unsupported protocol " + protocol); + } + + Resource resource = reference.getResource(); + + String resourceString = stringify(resource); + + List parameters = reference.getParameters(); + + ReferenceParameter referenceParameter = (ReferenceParameter) parameters.get(0); + + Reference parentReference = referenceParameter.getValue(); + + String parametersString = dispatchStringifyReference(parentReference); + + return NODE_PROTOCOL_CODE + "-" + resourceString + "-" + parametersString; + + } + + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/NodeProtocol.java b/source/java/org/alfresco/repo/virtual/ref/NodeProtocol.java new file mode 100644 index 0000000000..7e94b40a7d --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/NodeProtocol.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.Arrays; + +import org.alfresco.repo.version.Version2Model; +import org.alfresco.repo.version.VersionBaseModel; +import org.alfresco.repo.version.VersionModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; + +public class NodeProtocol extends Protocol +{ + /** + * + */ + private static final long serialVersionUID = -6120481299842983600L; + + public static Reference newReference(NodeRef nodeRef, Reference parentReference) + { + return new Reference(DEFAULT_ENCODING, + Protocols.NODE.protocol, + new RepositoryResource(new RepositoryNodeRef(nodeRef)), + Arrays.asList(new ReferenceParameter(parentReference))); + } + + public static Reference newReference(Encoding encoding, Resource actualNodeResource, Reference parentReference) + { + return new Reference(DEFAULT_ENCODING, + Protocols.NODE.protocol, + actualNodeResource, + Arrays.asList(new ReferenceParameter(parentReference))); + } + + public NodeProtocol() + { + super("node"); + } + + public NodeRef getNodeRef(Reference reference) + { + // TODO: use a resource processor for node ref extraction + RepositoryResource repositoryResource = (RepositoryResource) reference.getResource(); + RepositoryNodeRef reposioryNodeRef = (RepositoryNodeRef) repositoryResource.getLocation(); + return reposioryNodeRef.getNodeRef(); + } + + public Reference getVirtualParentReference(Reference reference) + { + return ((ReferenceParameter) reference.getParameters().get(0)).getValue(); + } + + @Override + public R dispatch(ProtocolMethod method, Reference reference) throws ProtocolMethodException + { + return method.execute(this, + reference); + } + + @Override + public Reference propagateNodeRefMutations(NodeRef mutatedNodeRef, Reference reference) + { + StoreRef storeRef = mutatedNodeRef.getStoreRef(); + String storeId = storeRef.getIdentifier(); + String protocol = storeRef.getProtocol(); + + if (Version2Model.STORE_ID.equals(storeId) || VersionModel.STORE_ID.equals(storeId) + || VersionBaseModel.STORE_PROTOCOL.equals(protocol)) + { + Resource resource = reference.getResource(); + if (resource instanceof RepositoryResource) + { + RepositoryResource repositoryResource = (RepositoryResource) resource; + RepositoryLocation location = repositoryResource.getLocation(); + if (location instanceof RepositoryNodeRef) + { + RepositoryNodeRef repositoryNodeRef = (RepositoryNodeRef) location; + NodeRef nodeRef = repositoryNodeRef.getNodeRef(); + NodeRef nodeRefPropagation = new NodeRef(mutatedNodeRef.getStoreRef(), + nodeRef.getId()); + Resource resourcePropagation = new RepositoryResource(new RepositoryNodeRef(nodeRefPropagation)); + + return new Reference(reference.getEncoding(), + reference.getProtocol(), + resourcePropagation, + reference.getParameters()); + } + } + } + + // default branch + + return reference; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/NodeRefHasher.java b/source/java/org/alfresco/repo/virtual/ref/NodeRefHasher.java new file mode 100644 index 0000000000..f8fa5c5029 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/NodeRefHasher.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.util.Pair; + +/** + * Creates and looks up string-pair hash codes of {@link NodeRef}s.
+ */ +public interface NodeRefHasher +{ + + NodeRef lookup(Pair hash); + + Pair hash(NodeRef nodeRef); +} diff --git a/source/java/org/alfresco/repo/virtual/ref/NodeRefRadixHasher.java b/source/java/org/alfresco/repo/virtual/ref/NodeRefRadixHasher.java new file mode 100644 index 0000000000..ec43bcde08 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/NodeRefRadixHasher.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.math.BigInteger; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.util.Pair; +import org.apache.commons.lang.StringUtils; + +import com.sun.star.uno.RuntimeException; + +/** + * Creates string-pair hashes of {@link NodeRef}s where the first string is a + * stored hash combination for {@link NodeRef} store elements (protocol and id) + * and the second is a radix 36 encoded {@link NodeRef} id. + */ +public class NodeRefRadixHasher implements NodeRefHasher +{ + public static final NodeRefRadixHasher RADIX_36_HASHER = new NodeRefRadixHasher(36); + + private HashStore storeProtocolStore; + + private HashStore storeIdStore; + + private int radix; + + public NodeRefRadixHasher() + { + this(16); + } + + public NodeRefRadixHasher(int radix) + { + super(); + this.radix = radix; + this.storeProtocolStore = HashStoreConfiguration.getInstance().getStoreProtocolStore(); + this.storeIdStore = HashStoreConfiguration.getInstance().getStoreIdStore(); + } + + @Override + public Pair hash(NodeRef nodeRef) + { + String uuid = nodeRef.getId(); + + if (uuid.length() != 36) + { + throw new RuntimeException("Invalid noderf id length " + uuid); + } + + String bigInt16String = uuid.replaceAll("-", + ""); + if (bigInt16String.length() != 32) + { + throw new RuntimeException("Invalid noderf id format " + uuid); + } + + BigInteger bigIntId = new BigInteger(bigInt16String, + 16); + StoreRef storeRef = nodeRef.getStoreRef(); + String storeProtocolHash = storeProtocolStore.hash(storeRef.getProtocol()); + String storeIdHash = storeIdStore.hash(storeRef.getIdentifier()); + if (storeProtocolHash == null || storeIdHash == null) + { + throw new RuntimeException("Missing hash for " + storeRef); + } + String storeHash = storeProtocolHash + storeIdHash; + return new Pair(storeHash, + bigIntId.toString(radix)); + + } + + @Override + public NodeRef lookup(Pair hash) + { + String storeHash = hash.getFirst(); + String storeProtocolHash = storeHash.substring(0, + 1); + String storeIdHash = storeHash.substring(1, + 2); + + String storeProtocol = storeProtocolStore.lookup(storeProtocolHash); + String storeId = storeIdStore.lookup(storeIdHash); + if (storeProtocol == null || storeId == null) + { + throw new RuntimeException("Lookup found no protocol or id for " + storeHash); + } + BigInteger nodeId = new BigInteger(hash.getSecond(), + radix); + String nodeIdHexa = nodeId.toString(16); + nodeIdHexa = StringUtils.leftPad(nodeIdHexa, + 32, + "0"); + int leadZeros = 32 - nodeIdHexa.length(); + if (leadZeros > 0) + { + } + String groups[] = new String[5]; + groups[0] = nodeIdHexa.substring(0, + 8); + groups[1] = nodeIdHexa.substring(8, + 12); + groups[2] = nodeIdHexa.substring(12, + 16); + groups[3] = nodeIdHexa.substring(16, + 20); + groups[4] = nodeIdHexa.substring(20, + 32); + StringBuilder idBuilder = new StringBuilder(groups[0]); + for (int i = 1; i < groups.length; i++) + { + idBuilder.append("-"); + idBuilder.append(groups[i]); + } + return new NodeRef(storeProtocol, + storeId, + idBuilder.toString()); + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/NumericPathHasher.java b/source/java/org/alfresco/repo/virtual/ref/NumericPathHasher.java new file mode 100644 index 0000000000..279491f9c9 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/NumericPathHasher.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import org.apache.commons.lang.StringUtils; + +/** + * Creates string-pair hashes of single digit element numeric paths.
+ * @see HierarchicalPathHasher + */ +public class NumericPathHasher extends HierarchicalPathHasher +{ + + @Override + protected String hashSubpath(String subpath) + { + try + { + if (subpath.trim().isEmpty()) + { + return null; + } + + String[] numericPathElements = subpath.split("/"); + + if (numericPathElements == null || numericPathElements.length == 0) + { + return "0"; + } + + long lHash = 0; + + for (int i = numericPathElements[0].isEmpty() ? 1 : 0; i < numericPathElements.length; i++) + { + if (numericPathElements[i].length() == 1) + { + long intPathElement = Long.parseLong(numericPathElements[i]); + if (intPathElement > 0) + { + lHash = lHash * 10 + intPathElement; + if (lHash < 0) + { + return null; + } + else + { + continue; + } + } + else + { + return null; + } + } + else + { + return null; + } + } + + return "" + lHash; + } + catch (NumberFormatException e) + { + return null; + } + } + + @Override + protected String lookupSubpathHash(String hash) + { + String[] digits = hash.split("(?<=.)"); + return "/" + StringUtils.join(digits, + '/'); + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/Parameter.java b/source/java/org/alfresco/repo/virtual/ref/Parameter.java new file mode 100644 index 0000000000..ddd4b16d9b --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/Parameter.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +public interface Parameter +{ + /** + * Converts the value attribute into a string representation according to + * {@link Encodings} definition, using provided {@link Stringifier} + * parameter. + * + * @param stringifier + * @return the string representation of this parameter as provided by the + * given {@link Stringifier} + * @throws ReferenceEncodingException + */ + String stringify(Stringifier stringifier) throws ReferenceEncodingException; +} diff --git a/source/java/org/alfresco/repo/virtual/ref/PathHasher.java b/source/java/org/alfresco/repo/virtual/ref/PathHasher.java new file mode 100644 index 0000000000..f9246c2e3d --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/PathHasher.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import org.alfresco.util.Pair; + +public interface PathHasher +{ + + Pair hash(String path); + + String lookup(Pair hash); +} diff --git a/source/java/org/alfresco/repo/virtual/ref/PlainEncoding.java b/source/java/org/alfresco/repo/virtual/ref/PlainEncoding.java new file mode 100644 index 0000000000..443ddeae60 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/PlainEncoding.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +public interface PlainEncoding +{ + static final String DELIMITER = ":"; + + static final String REFERENCE_DELIMITER = "*"; + + static final String REPOSITORY = "repository"; + + static final String CLASSPATH = "classpath"; + + static final String PATH = "path"; + + static final String NODE = "node"; + + static final String INT_PARAMETER = "i"; + + static final String STRING_PARAMETER = "s"; + + static final String RESOURCE_PARAMETER = "r"; + + static final String REFERENCE_PARAMETER = "ref"; +} diff --git a/source/java/org/alfresco/repo/virtual/ref/PlainReferenceParser.java b/source/java/org/alfresco/repo/virtual/ref/PlainReferenceParser.java new file mode 100644 index 0000000000..f54b47491a --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/PlainReferenceParser.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Parses string references into {@link Reference} objects based on Plain + * encodings grammar. + *

+ * For grammar examples see {@link Encodings#PLAIN} encoding definition. + */ +public class PlainReferenceParser implements ReferenceParser, PlainEncoding +{ + /** + * + */ + private static final long serialVersionUID = -7053644289373735564L; + + + + @Override + public Reference parse(String referenceString) throws ReferenceParseException + { + String[] referenceTokens = referenceString.split(DELIMITER); + final Cursor cursor = new Cursor(referenceTokens, + 0); + if (referenceTokens.length < 2) + { + throw new ReferenceParseException("Invalid reference " + referenceString); + } + return this.parseReference(cursor); + } + + private Reference parseReference(Cursor cursor) throws ReferenceParseException + { + try + { + + final String protocolString = cursor.tokens[cursor.i]; + final Protocol protocol = Protocols.fromName(protocolString); + if (protocol == null) + { + throw new ReferenceParseException("Unknown protocol " + protocolString); + } + cursor.i++; + + final Resource resource = parseResource(cursor); + final List parameters = parseParameters(cursor); + + return new Reference(Encodings.PLAIN.encoding, + protocol, + resource, + parameters); + } + catch (ArrayIndexOutOfBoundsException e) + { + throw new ReferenceParseException("Invalid reference", + e); + } + } + + /** + * Obtains a list of {@link Parameter} from the cursor parameter + * + * @param cursor + * @return a list of {@link Parameter} from the cursor parameter + * @throws ReferenceParseException + */ + private List parseParameters(Cursor cursor) throws ReferenceParseException + { + List parameters = new ArrayList<>(); + while (cursor.i < cursor.tokens.length) + { + if (RESOURCE_PARAMETER.equals(cursor.tokens[cursor.i])) + { + cursor.i++; + parameters.add(parseResourceParameter(cursor)); + } + else if (STRING_PARAMETER.equals(cursor.tokens[cursor.i])) + { + cursor.i++; + parameters.add(parseStringParameter(cursor)); + } + else if (REFERENCE_PARAMETER.equals(cursor.tokens[cursor.i])) + { + cursor.i++; + parameters.add(parseReferenceParameter(cursor)); + } + else if (REFERENCE_DELIMITER.equals(cursor.tokens[cursor.i])) + { + cursor.i++; + break; + } + else + { + throw new ReferenceParseException("Invalid parameter " + cursor.tokens[cursor.i]); + } + } + return parameters; + } + + /** + * Obtains a {@link Parameter} reference that is instance of + * {@link StringParameter} from the cursor parameter + * + * @param cursor + * @return a {@link Parameter} reference that is instance of + * {@link StringParameter} from the cursor parameter + */ + private Parameter parseStringParameter(Cursor cursor) + { + StringParameter paramenter = new StringParameter(cursor.tokens[cursor.i]); + cursor.i++; + return paramenter; + } + + /** + * Obtains a {@link Parameter} reference that is instance of + * {@link ResourceParameter} from the cursor parameter + * + * @param cursor + * @return a {@link Parameter} reference that is instance of + * {@link ResourceParameter} from the cursor parameter + * @throws ReferenceParseException + */ + private Parameter parseResourceParameter(Cursor cursor) throws ReferenceParseException + { + Resource resource = parseResource(cursor); + return new ResourceParameter(resource); + } + + /** + * Obtains {@link Resource} reference witch is an instance of + * {@link RepositoryResource} if current token is + * {@link PlainEncoding#REPOSITORY} or an instance of + * {@link ClasspathResource} if current token is + * {@link PlainEncoding#CLASSPATH} + * + * @param cursor + * @return {@link Resource} reference witch an instance of + * {@link RepositoryResource} if current token is + * {@link PlainEncoding#REPOSITORY} or an instance of + * {@link ClasspathResource} if current token is + * {@link PlainEncoding#CLASSPATH} + * @throws ReferenceParseException + */ + private Resource parseResource(Cursor cursor) throws ReferenceParseException + { + if (REPOSITORY.equals(cursor.tokens[cursor.i])) + { + cursor.i++; + return parseRepositoryResource(cursor); + } + else if (CLASSPATH.equals(cursor.tokens[cursor.i])) + { + cursor.i++; + return parseClasspathResource(cursor); + } + else + { + throw new ReferenceParseException("Invalid resource " + cursor.tokens[cursor.i]); + } + } + + /** + * Obtains {@link RepositoryResource} reference from the cursor parameter + * + * @param cursor + * @return A {@link RepositoryResource} reference from the cursor parameter + * @throws ReferenceParseException + */ + private RepositoryResource parseRepositoryResource(Cursor cursor) throws ReferenceParseException + { + return new RepositoryResource(parseRepositoryLocation(cursor)); + } + + /** + * Obtains {@link RepositoryLocation} reference witch is an instance of + * {@link RepositoryPath} if current token is {@link PlainEncoding#PATH} or + * an instance of {@link RepositoryNodeRef} if current token is + * {@link PlainEncoding#NODE} + * + * @param cursor + * @return A {@link RepositoryLocation} reference witch an instance of + * {@link RepositoryPath} if current token is + * {@link PlainEncoding#PATH} or an instance of + * {@link RepositoryNodeRef} if current token is + * {@link PlainEncoding#NODE} + * @throws ReferenceParseException + */ + private RepositoryLocation parseRepositoryLocation(Cursor cursor) throws ReferenceParseException + { + if (PATH.equals(cursor.tokens[cursor.i])) + { + cursor.i++; + return parseRepositoryPath(cursor); + } + else if (NODE.equals(cursor.tokens[cursor.i])) + { + cursor.i++; + return parseRepositoryNode(cursor); + } + else + { + throw new ReferenceParseException("Invalid location " + cursor.tokens[cursor.i]); + } + } + + /** + * Obtains {@link RepositoryPath} reference from current token of the cursor + * parameter + * + * @param cursor + * @return A {@link RepositoryPath} reference from current token of the + * cursor parameter + */ + private RepositoryPath parseRepositoryPath(Cursor cursor) + { + String path = cursor.tokens[cursor.i]; + cursor.i++; + return new RepositoryPath(path); + } + + /** + * Obtains {@link RepositoryNodeRef} reference from store protocol, store + * identifier and node id obtained from cursor's tokens starting with + * current token of the cursor. + * + * @param cursor + * @return A {@link RepositoryNodeRef} reference from store protocol, store + * identifier and node id obtained from cursor's tokens starting + * with current token of the cursor. + */ + private RepositoryNodeRef parseRepositoryNode(Cursor cursor) + { + String storeProtocol = cursor.tokens[cursor.i]; + String storeIdentifier = cursor.tokens[cursor.i + 1]; + String id = cursor.tokens[cursor.i + 2]; + cursor.i += 3; + + return new RepositoryNodeRef(new NodeRef(storeProtocol, + storeIdentifier, + id)); + } + + /** + * Obtains {@link ClasspathResource} reference from current token of the + * cursor parameter + * + * @param cursor + * @return A {@link ClasspathResource} reference from current token of the + * cursor parameter + */ + private ClasspathResource parseClasspathResource(Cursor cursor) + { + String classpath = cursor.tokens[cursor.i]; + cursor.i++; + return new ClasspathResource(classpath); + } + + private ReferenceParameter parseReferenceParameter(Cursor cursor) throws ReferenceParseException + { + Reference reference = parseReference(cursor); + ReferenceParameter parameter = new ReferenceParameter(reference); + return parameter; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/PlainStringifier.java b/source/java/org/alfresco/repo/virtual/ref/PlainStringifier.java new file mode 100644 index 0000000000..1177b06d2b --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/PlainStringifier.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.List; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; + +/** + * Converts the object to a string representation according to + * {@link Encodings#PLAIN} encoding definition. + */ +public class PlainStringifier implements Stringifier, PlainEncoding +{ + /** + * + */ + private static final long serialVersionUID = -8169416257716384803L; + + @Override + public String stringify(Reference reference) throws ReferenceEncodingException + { + return reference.getProtocol() + DELIMITER + stringify(reference.getResource()) + + stringify(reference.getParameters()); + } + + @Override + public String stringify(Resource resource) throws ReferenceEncodingException + { + return resource.stringify(this); + } + + @Override + public String stringifyResource(Resource resource) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Invalid reference " + resource.getClass()); + } + + @Override + public String stringifyResource(RepositoryResource resource) throws ReferenceEncodingException + { + return REPOSITORY + DELIMITER + stringify(resource.getLocation()); + } + + @Override + public String stringifyResource(ClasspathResource resource) + { + return CLASSPATH + DELIMITER + resource.getClasspath(); + } + + @Override + public String stringify(RepositoryLocation repositoryLocation) throws ReferenceEncodingException + { + return repositoryLocation.stringify(this); + } + + @Override + public String stringifyRepositoryLocation(RepositoryLocation repositoryLocation) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Invalid location " + repositoryLocation.getClass()); + } + + @Override + public String stringifyRepositoryLocation(RepositoryNodeRef repositoryNodeRef) throws ReferenceEncodingException + { + NodeRef nodeRef = repositoryNodeRef.getNodeRef(); + StoreRef storeRef = nodeRef.getStoreRef(); + + return NODE + DELIMITER + storeRef.getProtocol() + DELIMITER + storeRef.getIdentifier() + DELIMITER + + nodeRef.getId(); + } + + @Override + public String stringifyRepositoryLocation(RepositoryPath repositoryPath) throws ReferenceEncodingException + { + return PATH + DELIMITER + repositoryPath.getPath(); + } + + @Override + public String stringify(List parameters) throws ReferenceEncodingException + { + StringBuilder parametersBuilder = new StringBuilder(); + for (Parameter parameter : parameters) + { + parametersBuilder.append(DELIMITER); + parametersBuilder.append(stringify(parameter)); + } + return parametersBuilder.toString(); + } + + @Override + public String stringify(Parameter parameter) throws ReferenceEncodingException + { + return parameter.stringify(this); + } + + @Override + public String stringifyParameter(ResourceParameter resourceParameter) throws ReferenceEncodingException + { + return RESOURCE_PARAMETER + DELIMITER + stringify(resourceParameter.getValue()); + } + + @Override + public String stringifyParameter(StringParameter stringParameter) throws ReferenceEncodingException + { + return STRING_PARAMETER + DELIMITER + stringParameter.getValue(); + } + + @Override + public String stringifyParameter(Parameter parameter) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Invalid parameter " + parameter.getClass()); + } + + @Override + public String stringifyParameter(ReferenceParameter parameter) throws ReferenceEncodingException + { + return REFERENCE_PARAMETER + DELIMITER + stringify(parameter.getValue()) + DELIMITER + REFERENCE_DELIMITER; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/Protocol.java b/source/java/org/alfresco/repo/virtual/ref/Protocol.java new file mode 100644 index 0000000000..6a1c0af611 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/Protocol.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.io.Serializable; +import java.util.ArrayList; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * A {@link Reference} model element that defines the type of the referred + * artefact and the virtualization process that was applied when creating a + * reference.
+ * Protocols define the semantics of all elements of a reference. Protocol + * implementations add syntactic processing methods.
+ * The high level semantic functionality of a protocol is implemented with + * {@link ProtocolMethod}s. + * + * @author Bogdan Horje + */ +public class Protocol implements Serializable +{ + /** + * + */ + private static final long serialVersionUID = -6969613804420028905L; + + public final String name; + + protected static final Encoding DEFAULT_ENCODING = Encodings.HASH.encoding; + + public Protocol(String name) + { + super(); + this.name = name; + } + + public Parameter getParameter(Reference reference, int index) + { + return reference.getParameters().get(index); + } + + public Reference replaceParameter(Reference reference, int index, String stringParameter) + { + return replaceParameter(reference, + index, + new StringParameter(stringParameter)); + } + + public Reference replaceParameter(Reference reference, int index, Parameter parameter) + { + ArrayList parameters = new ArrayList(reference.getParameters()); + parameters.set(index, + parameter); + return new Reference(reference.getEncoding(), + reference.getProtocol(), + reference.getResource(), + parameters); + } + + public Reference addParameter(Reference reference, Resource resource) + { + return addParameter(reference, + new ResourceParameter(resource)); + } + + public Reference addParameter(Reference reference, Parameter parameter) + { + ArrayList parameters = new ArrayList(reference.getParameters()); + parameters.add(parameter); + return new Reference(reference.getEncoding(), + reference.getProtocol(), + reference.getResource(), + parameters); + } + + /** + * {@link ProtocolMethod} double-dispatch/visitor protocol type selector. + * Subclasses should override this method to select the appropriate type + * bound method.
+ * This particular implementation calls the default + * {@link ProtocolMethod#execute(Protocol, Reference)} default method + * implementation. + * + * @param method + * @param reference + * @return the value returned by the type bond method + * @throws ProtocolMethodException + */ + public R dispatch(ProtocolMethod method, Reference reference) throws ProtocolMethodException + { + return method.execute(this, + reference); + } + + @Override + public String toString() + { + return name; + } + + /** + * {@link Protocol} delegate of + * {@link Reference#propagateNodeRefMutations(NodeRef)} + * + * @param mutatedNodeRef + * @param reference + * @return a mutated version of the given {@link Reference} corresponding to + * the given mutated node or the given {@link Reference} if no + * mutations are detected + */ + public Reference propagateNodeRefMutations(NodeRef mutatedNodeRef, Reference reference) + { + // nothing to propagate by default + return reference; + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/ProtocolHashParser.java b/source/java/org/alfresco/repo/virtual/ref/ProtocolHashParser.java new file mode 100644 index 0000000000..0b16f5e148 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/ProtocolHashParser.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import org.alfresco.repo.virtual.ref.ReferenceParser.Cursor; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.util.Pair; + +/** + * Base class for custom protocol hash encoded {@link ReferenceParser}s. + */ +public abstract class ProtocolHashParser implements HashEncodingArtefact +{ + protected abstract Reference parse(Cursor cursor) throws ReferenceParseException; + + private NodeRefHasher nodeRefHasher = NodeRefRadixHasher.RADIX_36_HASHER; + + private PathHasher classpathHasher; + + private PathHasher repositoryPathHasher; + + public ProtocolHashParser(HashStore classpathHashStore) + { + this.classpathHasher = new StoredPathHasher(classpathHashStore); + this.repositoryPathHasher = new StoredPathHasher(classpathHashStore); + } + + protected Resource parseResource(Cursor cursor) throws ReferenceParseException + { + String token = cursor.nextToken(); + if (REPOSITORY_NODEREF_RESOURCE_CODE.equals(token)) + { + + String nodeRefHash = cursor.nextToken(); + String storeHash = nodeRefHash.substring(0, + 2); + String nodeIdHash = nodeRefHash.substring(2); + NodeRef nodeRef = nodeRefHasher.lookup(new Pair(storeHash, + nodeIdHash)); + return new RepositoryResource(new RepositoryNodeRef(nodeRef)); + } + else if (HASHED_CLASSPATH_RESOUCE_CODE.equals(token)) + { + String cp = classpathHasher.lookup(new Pair(cursor.nextToken(), + null)); + return new ClasspathResource(cp); + } + else if (MIXED_CLASSPATH_RESOUCE_CODE.equals(token)) + { + String cp = classpathHasher.lookup(new Pair(cursor.nextToken(), + cursor.nextToken())); + return new ClasspathResource(cp); + } + else if (CLASSPATH_RESOUCE_CODE.equals(token)) + { + String cp = classpathHasher.lookup(new Pair(null, + cursor.nextToken())); + return new ClasspathResource(cp); + } + else if (HASHED_REPOSITORY_PATH_CODE.equals(token)) + { + String path = repositoryPathHasher.lookup(new Pair(cursor.nextToken(), + null)); + + return new RepositoryResource(new RepositoryPath(path)); + } + else if (MIXED_REPOSITORY_PATH_CODE.equals(token)) + { + String path = repositoryPathHasher.lookup(new Pair(cursor.nextToken(), + cursor.nextToken())); + + return new RepositoryResource(new RepositoryPath(path)); + } + else if (REPOSITORY_PATH_CODE.equals(token)) + { + String path = repositoryPathHasher.lookup(new Pair(null, + cursor.nextToken())); + + return new RepositoryResource(new RepositoryPath(path)); + } + else + { + throw new ReferenceParseException("Unknown resource token " + token + " at " + (cursor.i--)); + } + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/ProtocolHashStringifier.java b/source/java/org/alfresco/repo/virtual/ref/ProtocolHashStringifier.java new file mode 100644 index 0000000000..c1d341a6b1 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/ProtocolHashStringifier.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.List; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.util.Pair; + +/** + * Base class for custom protocol hash encoded {@link Stringifier}s. + */ +public abstract class ProtocolHashStringifier implements HashEncodingArtefact, Stringifier +{ + /** + * + */ + private static final long serialVersionUID = 6471653470842760043L; + + private NodeRefHasher nodeRefHasher = NodeRefRadixHasher.RADIX_36_HASHER; + + private PathHasher classpathHasher; + + private PathHasher repositoryPathHasher; + + private Stringifier referenceDispatcher; + + public ProtocolHashStringifier(HashStore classpathHashStore, Stringifier referenceDispatcher) + { + this.classpathHasher = new StoredPathHasher(classpathHashStore); + this.repositoryPathHasher=new StoredPathHasher(classpathHashStore); + this.referenceDispatcher = referenceDispatcher; + } + + protected String dispatchStringifyReference(Reference reference) + { + return referenceDispatcher.stringify(reference); + } + + @Override + public String stringify(Resource resource) throws ReferenceEncodingException + { + return resource.stringify(this); + } + + @Override + public String stringifyResource(Resource resource) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Unknown resource type " + resource); + } + + @Override + public String stringifyResource(RepositoryResource resource) throws ReferenceEncodingException + { + RepositoryLocation location = resource.getLocation(); + return location.stringify(this); + + } + + @Override + public String stringifyResource(ClasspathResource resource) throws ReferenceEncodingException + { + String cp = resource.getClasspath(); + Pair hash = classpathHasher.hash(cp); + final String hashed = hash.getFirst(); + final String nonHashed = hash.getSecond(); + if (nonHashed == null) + { + return HASHED_CLASSPATH_RESOUCE_CODE + "-" + hashed; + } + else if (hashed == null) + { + return CLASSPATH_RESOUCE_CODE + "-" + nonHashed; + } + else + { + return MIXED_CLASSPATH_RESOUCE_CODE + "-" + hashed + "-" + nonHashed; + } + + } + + @Override + public String stringify(RepositoryLocation repositoryLocation) throws ReferenceEncodingException + { + return repositoryLocation.stringify(this); + } + + @Override + public String stringifyRepositoryLocation(RepositoryLocation repositoryLocation) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Unknown repository location " + repositoryLocation); + } + + @Override + public String stringifyRepositoryLocation(RepositoryNodeRef repositoryNodeRef) throws ReferenceEncodingException + { + NodeRef nodeRef = repositoryNodeRef.getNodeRef(); + Pair hash = nodeRefHasher.hash(nodeRef); + return REPOSITORY_NODEREF_RESOURCE_CODE + "-" + hash.getFirst() + hash.getSecond(); + } + + @Override + public String stringifyRepositoryLocation(RepositoryPath repositoryPath) throws ReferenceEncodingException + { + String cp = repositoryPath.getPath(); + Pair hash = repositoryPathHasher.hash(cp); + final String hashed = hash.getFirst(); + final String nonHashed = hash.getSecond(); + if (nonHashed == null) + { + return HASHED_REPOSITORY_PATH_CODE + "-" + hashed; + } + else if (hashed == null) + { + return REPOSITORY_PATH_CODE + "-" + nonHashed; + } + else + { + return MIXED_REPOSITORY_PATH_CODE + "-" + hashed + "-" + nonHashed; + } + } + + // parameters + + @Override + public String stringify(List parameters) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Invalid stringifier cotext " + parameters); + } + + @Override + public String stringify(Parameter parameter) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Invalid stringifier cotext " + parameter); + } + + @Override + public String stringifyParameter(Parameter parameter) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Invalid stringifier cotext " + parameter); + } + + @Override + public String stringifyParameter(ResourceParameter resourceParameter) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Invalid stringifier cotext " + resourceParameter); + } + + @Override + public String stringifyParameter(StringParameter stringParameter) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Invalid stringifier cotext " + stringParameter); + } + + @Override + public String stringifyParameter(ReferenceParameter referenceParameter) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Invalid stringifier cotext " + referenceParameter); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/ProtocolMethod.java b/source/java/org/alfresco/repo/virtual/ref/ProtocolMethod.java new file mode 100644 index 0000000000..e1b4102a7e --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/ProtocolMethod.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +/** + * A double-dispatched method abstraction.
+ * The method execution is dispatched based on concrete protocol type.
+ * Implementor encapsulate high-level reference protocol based semantic + * functionality. + * + * @param method return type + * @see Reference#execute(ProtocolMethod) + * @author Bogdan Horje + */ +public interface ProtocolMethod +{ + R execute(VanillaProtocol vanillaProtocol, Reference reference) throws ProtocolMethodException; + + R execute(VirtualProtocol virtualProtocol, Reference reference) throws ProtocolMethodException; + + R execute(NodeProtocol protocol, Reference reference) throws ProtocolMethodException; + + R execute(Protocol protocol, Reference reference) throws ProtocolMethodException; +} diff --git a/source/java/org/alfresco/repo/virtual/ref/ProtocolMethodException.java b/source/java/org/alfresco/repo/virtual/ref/ProtocolMethodException.java new file mode 100644 index 0000000000..f85a6d3acd --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/ProtocolMethodException.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import org.alfresco.repo.virtual.VirtualizationException; + +public class ProtocolMethodException extends VirtualizationException +{ + private static final long serialVersionUID = -7476127763746116107L; + + public ProtocolMethodException() + { + super(); + } + + public ProtocolMethodException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) + { + super(message, + cause, + enableSuppression, + writableStackTrace); + } + + public ProtocolMethodException(String message, Throwable cause) + { + super(message, + cause); + } + + public ProtocolMethodException(String message) + { + super(message); + } + + public ProtocolMethodException(Throwable cause) + { + super(cause); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/Protocols.java b/source/java/org/alfresco/repo/virtual/ref/Protocols.java new file mode 100644 index 0000000000..72d8729243 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/Protocols.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.HashMap; +import java.util.Map; + +/** + * Common {@link Reference} protocols. + */ +public enum Protocols +{ + NODE(new NodeProtocol()), VIRTUAL(new VirtualProtocol()), VANILLA(new VanillaProtocol()); + + private static volatile Map protocolsMap; + + public static synchronized Protocol fromName(String name) + { + return protocolsMap.get(name); + } + + private synchronized static void register(Protocol protocol) + { + if (protocolsMap == null) + { + protocolsMap = new HashMap<>(); + } + protocolsMap.put(protocol.name, + protocol); + } + + public final Protocol protocol; + + Protocols(Protocol protocol) + { + this.protocol = protocol; + register(protocol); + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/Reference.java b/source/java/org/alfresco/repo/virtual/ref/Reference.java new file mode 100644 index 0000000000..bc10d31cd0 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/Reference.java @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.alfresco.repo.version.common.VersionUtil; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A generic, immutable virtualized artefact reference.
+ * Refers virtualized artefacts through :
+ *

    + *
  • a {@link Protocol} - defines what kind of virtualzied artefact is + * referred by this reference as well as what virtualization process was applied + *
  • + *
  • a {@link Resource} - identifies the main resource used in the + * virtualization (egg. a classpath location or repository node reference)
  • + *
  • a list of {@link ResourceParameter}s - used in customizing the + * virtualization process (egg. a resource pointing to the actual node of a + * semi-virtual folder reference)
  • + *
+ * + * @author Bogdan Horje + */ +public class Reference +{ + private static Log logger = LogFactory.getLog(Reference.class); + + private static final Character VIRTUAL_TOKEN = 'v'; + + /** + * Quick Reference compliance check of a {@link NodeRef}.
+ * NodeRef Reference representations validated by this method should produce + * valid Reference objects based on the given {@link NodeRef} when passed to + * the {@link #fromNodeRef(NodeRef)} method. + * + * @param nodeRef + * @return true if the given {@link NodeRef} is a valid + * Reference representation
+ * false otherwise + */ + public static final boolean isReference(NodeRef nodeRef) + { + if (nodeRef != null) + { + String id = nodeRef.getId(); + if (id != null) + { + char zeroChar = id.charAt(0); + return VIRTUAL_TOKEN.equals(zeroChar); + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug("NodeRef with null ID."); + } + } + } + + return false; + } + + /** + * {@link NodeRef} {@link Reference} representation decoder/converter + * method.
+ * Creates a {@link Reference} representation based on the ID of the given + * {@link NodeRef}.
+ * It expects a {@link #VIRTUAL_TOKEN} prefixed encoded string. The encoded + * string must start with a valid {@link Encoding} token. The Reference + * representation structure is (no delimiters between the 3 elements): + * VIRTUAL_TOKEN ENCODING_TOKEN referenceString Given that a valid encoding + * was detected {@link Encoding#urlNative} information is used to obtain a + * reference string. The reference string is parsed using the encoding + * configured parser. + * + * @param nodeRef + * @return the {@link Reference} object corresponding to the given + * {@link NodeRef} + * @throws ReferenceParseException if an error occurs during the reference + * string parsing + * @throws ReferenceEncodingException if the {@link NodeRef} ID has an + * invalid virtual token prefix or it uses an invalid encoding + * token + */ + public static final Reference fromNodeRef(NodeRef nodeRef) throws ReferenceParseException, + ReferenceEncodingException + { + String id = nodeRef.getId(); + if (id.startsWith("" + VIRTUAL_TOKEN)) + { + char token = id.charAt(1); + Encoding encoding = Encodings.fromToken(token); + if (encoding == null) + { + throw new ReferenceEncodingException("Invalid encoding token " + token + " in " + id); + } + else + { + String referenceString = id.substring(2); + if (!encoding.urlNative) + { + referenceString = new String(org.apache.commons.codec.binary.Base64.decodeBase64(referenceString)); + } + + Reference reference = encoding.parser.parse(referenceString); + return reference.propagateNodeRefMutations(nodeRef); + } + } + else + { + throw new ReferenceEncodingException("Invalid node ID format " + id); + } + } + + // Average reference length DEBUG trace + + private static long _debug_refLength = 0; + + private static long _debug_refCount = 0; + + private static final long _debug_refBatchSize = 256; + + private static synchronized void debug_avg_ref_length(long refLength) + { + _debug_refLength += refLength; + _debug_refCount++; + if (_debug_refBatchSize > 0 && _debug_refCount % _debug_refBatchSize == 0) + { + logger.debug("Average reference encoding size : " + (_debug_refLength / _debug_refCount)); + _debug_refCount = 0; + _debug_refLength = 0; + } + } + + private Encoding encoding; + + private Protocol protocol; + + private Resource resource; + + private List parameters; + + /** + * Constructor + * + * @param encoding the default {@link Encoding} of the new resource - to be + * used where an encoding is required and none is specified + * @param protocol + * @param resource + * @param parameters resource parameters - a copy of the provided list will + * be stored by this reference + */ + public Reference(Encoding encoding, Protocol protocol, Resource resource, List parameters) + { + this.encoding = encoding; + this.protocol = protocol; + this.resource = resource; + this.parameters = new LinkedList<>(parameters); + } + + public Reference(Encoding encoding, Protocol protocol, Resource resource) + { + this(encoding, + protocol, + resource, + Collections. emptyList()); + } + + /** + * @return a {@link String} representation of this reference using its + * default {@link Encoding} + * @throws ReferenceEncodingException + */ + public String encode() throws ReferenceEncodingException + { + return encode(this.encoding); + } + + /** + * @param anEncoding + * @return a {@link String} representation of this reference using the given + * {@link Encoding} + * @throws ReferenceEncodingException + */ + public String encode(Encoding anEncoding) throws ReferenceEncodingException + { + return anEncoding.stringifier.stringify(this); + } + + /** + * @return the default {@link Encoding} of this reference + */ + public Encoding getEncoding() + { + return this.encoding; + } + + public Protocol getProtocol() + { + return this.protocol; + } + + public Resource getResource() + { + return this.resource; + } + + public List getParameters() + { + return parameters; + } + + /** + * @return a {@link NodeRef} representation of this resource using the + * {@link StoreRef#STORE_REF_WORKSPACE_SPACESSTORE} and the default + * encoding of this resource + * @throws ReferenceEncodingException + */ + public NodeRef toNodeRef() throws ReferenceEncodingException + { + return toNodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + } + + /** + * @param storeRef + * @return a {@link NodeRef} representation of this resource using the given + * {@link StoreRef} and the default encoding of this resource + * @throws ReferenceEncodingException + */ + public NodeRef toNodeRef(StoreRef storeRef) throws ReferenceEncodingException + { + return toNodeRef(storeRef, + this.encoding); + } + + /** + * @param storeRef + * @param encoding + * @return a {@link NodeRef} representation of this resource using the given + * {@link StoreRef} and {@link Encoding} + * @throws ReferenceEncodingException + */ + public NodeRef toNodeRef(StoreRef storeRef, Encoding encoding) throws ReferenceEncodingException + { + String id = encode(encoding); + // TODO: move non-native encoding to encoding object itself + if (!encoding.urlNative) + { + id = new String(org.apache.commons.codec.binary.Base64.encodeBase64(id.getBytes(), + false)); + } + StringBuilder idBuilder = new StringBuilder(); + idBuilder.append(VIRTUAL_TOKEN); + idBuilder.append(encoding.token); + idBuilder.append(id); + + NodeRef theNode = new NodeRef(storeRef, + idBuilder.toString()); + + if (logger.isDebugEnabled()) + { + debug_avg_ref_length(theNode.toString().length()); + } + + return theNode; + } + + /** + * Double-dispatches {@link ProtocolMethod}s.
+ * Uses {@link Protocol#dispatch(ProtocolMethod, Reference)} to trigger + * concrete protocol based double dispatch + * ProtocolMethod::execute invocation on the given method + * object. + * + * @param method + * @return the dispatched method execution result + * @throws ProtocolMethodException + */ + public R execute(ProtocolMethod method) throws ProtocolMethodException + { + return this.protocol.dispatch(method, + this); + } + + /** + * Despite claimed {@link NodeRef} opacity Alfresco sometimes alters + * NodeRefs representation to achieve functionality. For example see + * {@link VersionUtil#convertNodeRef(NodeRef)}.
+ * We say that altered {@link NodeRef}s have suffered mutations and we try + * to detect those mutations and create a correspondent reference. + * + * @param mutatedNodeRef + * @return a mutated version of this {@link Reference} corresponding to the + * given mutated node or + * this Reference if no mutations are detected + */ + public Reference propagateNodeRefMutations(NodeRef mutatedNodeRef) + { + return protocol.propagateNodeRefMutations(mutatedNodeRef, + this); + } + + @Override + public String toString() + { + try + { + return encode(); + } + catch (ReferenceEncodingException e) + { + logger.error("Invalid reference", + e); + return super.toString(); + } + } + + @Override + public int hashCode() + { + return resource != null ? resource.hashCode() : 0; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj == null) + { + return false; + } + else if (!(obj instanceof Reference)) + { + return false; + } + + Reference other = (Reference) obj; + + if (resource == null) + { + return other.resource == null; + } + else + { + if (!this.resource.equals(other.resource)) + { + return false; + } + else + { + if (parameters == null) + { + return other.parameters == null; + } + else + { + return parameters.equals(other.parameters); + } + } + } + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/ReferenceEncodingException.java b/source/java/org/alfresco/repo/virtual/ref/ReferenceEncodingException.java new file mode 100644 index 0000000000..93d8fd9957 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/ReferenceEncodingException.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import org.alfresco.repo.virtual.VirtualizationException; + +public class ReferenceEncodingException extends VirtualizationException +{ + private static final long serialVersionUID = 8952014414253439106L; + + public ReferenceEncodingException() + { + super(); + } + + public ReferenceEncodingException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) + { + super(message, + cause, + enableSuppression, + writableStackTrace); + } + + public ReferenceEncodingException(String message, Throwable cause) + { + super(message, + cause); + } + + public ReferenceEncodingException(String message) + { + super(message); + } + + public ReferenceEncodingException(Throwable cause) + { + super(cause); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/ReferenceParameter.java b/source/java/org/alfresco/repo/virtual/ref/ReferenceParameter.java new file mode 100644 index 0000000000..4917e676c9 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/ReferenceParameter.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +public class ReferenceParameter extends ValueParameter +{ + + public ReferenceParameter(Reference reference) + { + super(reference); + } + + @Override + public String stringify(Stringifier stringifier) throws ReferenceEncodingException + { + return stringifier.stringifyParameter(this); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/ReferenceParseException.java b/source/java/org/alfresco/repo/virtual/ref/ReferenceParseException.java new file mode 100644 index 0000000000..d913728f00 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/ReferenceParseException.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import org.alfresco.repo.virtual.VirtualizationException; + +public class ReferenceParseException extends VirtualizationException +{ + private static final long serialVersionUID = 7234372861104307635L; + + public ReferenceParseException() + { + super(); + } + + public ReferenceParseException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) + { + super(message, + cause, + enableSuppression, + writableStackTrace); + } + + public ReferenceParseException(String message, Throwable cause) + { + super(message, + cause); + } + + public ReferenceParseException(String message) + { + super(message); + } + + public ReferenceParseException(Throwable cause) + { + super(cause); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/ReferenceParser.java b/source/java/org/alfresco/repo/virtual/ref/ReferenceParser.java new file mode 100644 index 0000000000..c9cd0237e0 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/ReferenceParser.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.io.Serializable; + +public interface ReferenceParser extends Serializable +{ + /** + * Helper class used in parsing string reference. + */ + class Cursor + { + + /** + * Tokens obtained by splitting the reference string using + * {@link PlainEncoding#DELIMITER} + */ + String[] tokens; + + /** + * Current processed Token + */ + int i; + + Cursor(String[] tokens, int i) + { + super(); + this.tokens = tokens; + this.i = i; + } + + String currentToken() + { + return tokens[i]; + } + + String nextToken() + { + String c = tokens[i]; + i++; + return c; + } + + boolean hasNext() + { + return i < tokens.length; + } + + } + + /** + * Parses a string reference into a {@link Reference} object + * + * @param referenceString + * @return A reference of {@link Reference} + * @throws ReferenceParseException + */ + Reference parse(String referenceString) throws ReferenceParseException; +} diff --git a/source/java/org/alfresco/repo/virtual/ref/RepositoryLocation.java b/source/java/org/alfresco/repo/virtual/ref/RepositoryLocation.java new file mode 100644 index 0000000000..373f345ef5 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/RepositoryLocation.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.io.InputStream; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ActualEnvironmentException; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * An Alfresco repository content abstraction. + * + * @author Bogdan Horje + */ +public interface RepositoryLocation +{ + String stringify(Stringifier stringifier) throws ReferenceEncodingException; + + InputStream openContentStream(ActualEnvironment environment) throws ActualEnvironmentException; + + NodeRef asNodeRef(ActualEnvironment environment) throws ActualEnvironmentException; +} diff --git a/source/java/org/alfresco/repo/virtual/ref/RepositoryNodeRef.java b/source/java/org/alfresco/repo/virtual/ref/RepositoryNodeRef.java new file mode 100644 index 0000000000..65084e62ae --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/RepositoryNodeRef.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.io.InputStream; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ActualEnvironmentException; +import org.alfresco.service.cmr.repository.NodeRef; + +public class RepositoryNodeRef implements RepositoryLocation +{ + + private NodeRef nodeRef; + + public RepositoryNodeRef(NodeRef aNodeRef) + { + this.nodeRef = aNodeRef; + } + + public NodeRef getNodeRef() + { + return nodeRef; + } + + @Override + public String stringify(Stringifier stringifier) throws ReferenceEncodingException + { + return stringifier.stringifyRepositoryLocation(this); + } + + @Override + public int hashCode() + { + return nodeRef != null ? nodeRef.hashCode() : 0; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj == null) + { + return false; + } + else if (!(obj instanceof RepositoryNodeRef)) + { + return false; + } + + RepositoryNodeRef other = (RepositoryNodeRef) obj; + + if (nodeRef == null) + { + return other.nodeRef == null; + } + else + { + return this.nodeRef.equals(other.nodeRef); + } + } + + @Override + public InputStream openContentStream(ActualEnvironment environment) throws ActualEnvironmentException + { + return environment.openContentStream(nodeRef); + } + + @Override + public NodeRef asNodeRef(ActualEnvironment environment) throws ActualEnvironmentException + { + return getNodeRef(); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/RepositoryPath.java b/source/java/org/alfresco/repo/virtual/ref/RepositoryPath.java new file mode 100644 index 0000000000..f9c14439bb --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/RepositoryPath.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.io.InputStream; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ActualEnvironmentException; +import org.alfresco.service.cmr.repository.NodeRef; + +public class RepositoryPath implements RepositoryLocation +{ + private String path; + + public RepositoryPath(String aPath) + { + this.path = aPath; + } + + public String getPath() + { + return path; + } + + @Override + public String stringify(Stringifier stringifier) throws ReferenceEncodingException + { + return stringifier.stringifyRepositoryLocation(this); + } + + @Override + public int hashCode() + { + return this.path != null ? this.path.hashCode() : 0; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj == null) + { + return false; + } + else if (!(obj instanceof RepositoryPath)) + { + return false; + } + + RepositoryPath other = (RepositoryPath) obj; + + if (path == null) + { + return other.path == null; + } + else + { + return this.path.equals(other.path); + } + } + + @Override + public InputStream openContentStream(ActualEnvironment environment) throws ActualEnvironmentException + { + throw new ActualEnvironmentException("Not implemented!"); + } + + @Override + public NodeRef asNodeRef(ActualEnvironment environment) throws ActualEnvironmentException + { + throw new RuntimeException("Not implemented!"); + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/RepositoryResource.java b/source/java/org/alfresco/repo/virtual/ref/RepositoryResource.java new file mode 100644 index 0000000000..53a6add078 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/RepositoryResource.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.io.InputStream; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ActualEnvironmentException; + +/** + * Identifies content from {@link RepositoryLocation} + */ +public class RepositoryResource implements Resource +{ + private RepositoryLocation location; + + public RepositoryResource(RepositoryLocation aLocation) + { + this.location = aLocation; + } + + @Override + public String stringify(Stringifier stringifier) throws ReferenceEncodingException + { + return stringifier.stringifyResource(this); + } + + public RepositoryLocation getLocation() + { + return location; + } + + @Override + public int hashCode() + { + return location != null ? location.hashCode() : 0; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj == null) + { + return false; + } + else if (!(obj instanceof RepositoryResource)) + { + return false; + } + + RepositoryResource other = (RepositoryResource) obj; + + if (location == null) + { + return other.location == null; + } + else + { + return this.location.equals(other.location); + } + } + + @Override + public R processWith(ResourceProcessor processor) throws ResourceProcessingError + { + return processor.process(this); + } + + @Override + public InputStream asStream(ActualEnvironment environment) throws ActualEnvironmentException + { + return location.openContentStream(environment); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/Resource.java b/source/java/org/alfresco/repo/virtual/ref/Resource.java new file mode 100644 index 0000000000..565b229cb8 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/Resource.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.io.InputStream; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ActualEnvironmentException; + +/** + * A {@link Reference} element that identifies the main or a parameter content + * location.
+ * The semantics of the resource is given by {@link Reference} protocol. + * + * @author Bogdan Horje + */ +public interface Resource +{ + /** + * Returns the String representation of the resource. + * + * @param stringifier + * @return + * @throws ReferenceEncodingException + */ + String stringify(Stringifier stringifier) throws ReferenceEncodingException; + + /** + * Processes the Resource with a {@link ResourceProcessor}. This method has + * the role of the accept method in the Visitor pattern, in this case the + * Visitor being the {@link ResourceProcessor} and the Element - the + * Resource. + * + * @param processor + * @return + * @throws ResourceProcessingError + */ + R processWith(ResourceProcessor processor) throws ResourceProcessingError; + + InputStream asStream(ActualEnvironment environment) throws ActualEnvironmentException; +} diff --git a/source/java/org/alfresco/repo/virtual/ref/ResourceParameter.java b/source/java/org/alfresco/repo/virtual/ref/ResourceParameter.java new file mode 100644 index 0000000000..9f32811a47 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/ResourceParameter.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +/** + * Helper class that has one {@link Resource} as a value attribute, value that + * can be retrieved and used in virtualization process. + *

+ * It also provides the possibility of converting the value attribute into a + * string representation according to {@link Encodings} definition, using + * provided {@link Stringifier} objects. + */ +public class ResourceParameter extends ValueParameter +{ + public ResourceParameter(Resource resource) + { + super(resource); + } + + @Override + public String stringify(Stringifier stringifier) throws ReferenceEncodingException + { + return stringifier.stringifyParameter(this); + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/ResourceProcessingError.java b/source/java/org/alfresco/repo/virtual/ref/ResourceProcessingError.java new file mode 100644 index 0000000000..0fafaea6e5 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/ResourceProcessingError.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +public class ResourceProcessingError extends Exception +{ + private static final long serialVersionUID = 191847639145802931L; + + public ResourceProcessingError() + { + super(); + } + + public ResourceProcessingError(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) + { + super(message, + cause, + enableSuppression, + writableStackTrace); + } + + public ResourceProcessingError(String message, Throwable cause) + { + super(message, + cause); + } + + public ResourceProcessingError(String message) + { + super(message); + } + + public ResourceProcessingError(Throwable cause) + { + super(cause); + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/ResourceProcessor.java b/source/java/org/alfresco/repo/virtual/ref/ResourceProcessor.java new file mode 100644 index 0000000000..79bfac42e5 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/ResourceProcessor.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +/** + * Generic {@link Resource} visitor. It ensures the processing of different + * types of resources.
+ * + * @author Bogdan Horje + * @param + */ +public interface ResourceProcessor +{ + /** + * Processes a resource of type {@link Resource}. + * + * @param resource a {@link Resource} to be processed. + * @return generic parameter R that implementors are parameterised with. + * @throws ResourceProcessingError + */ + R process(Resource resource) throws ResourceProcessingError; + + /** + * Processes a resource of type {@link ClasspathResource}. + * + * @param classpath the {@link ClasspathResource} to be processed. + * @return generic parameter R that implementors are parameterised with. + * @throws ResourceProcessingError + */ + R process(ClasspathResource classpath) throws ResourceProcessingError; + + /** + * Processes a resource of type {@link RepositoryResource}. + * + * @param repository a {@link RepositoryResource} to be processed. + * @return generic parameter R that implementors are parameterised with. + * @throws ResourceProcessingError + */ + R process(RepositoryResource repository) throws ResourceProcessingError; +} diff --git a/source/java/org/alfresco/repo/virtual/ref/StoredPathHasher.java b/source/java/org/alfresco/repo/virtual/ref/StoredPathHasher.java new file mode 100644 index 0000000000..0eaaba1e11 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/StoredPathHasher.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +/** + * Creates string-pair hashes based on stored path hashes.
+ * @see HierarchicalPathHasher + */ +public class StoredPathHasher extends HierarchicalPathHasher +{ + + private HashStore pathHashStore; + + public StoredPathHasher(HashStore pathHashStore) + { + super(); + this.pathHashStore = pathHashStore; + } + + @Override + protected String hashSubpath(String subpath) + { + return pathHashStore.hash(subpath); + } + + @Override + protected String lookupSubpathHash(String hash) + { + return pathHashStore.lookup(hash); + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/StringParameter.java b/source/java/org/alfresco/repo/virtual/ref/StringParameter.java new file mode 100644 index 0000000000..82a85d9de8 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/StringParameter.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +/** + * Helper class that has one {@link String} as a value attribute, value that can + * be retrieved and used in virtualization process. + *

+ * It also provides the possibility of converting the value attribute into a + * string representation according to {@link Encodings} definition, using + * provided {@link Stringifier} objects. + */ +public class StringParameter extends ValueParameter +{ + public StringParameter(String value) + { + super(value); + } + + @Override + public String stringify(Stringifier stringifier) throws ReferenceEncodingException + { + return stringifier.stringifyParameter(this); + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/Stringifier.java b/source/java/org/alfresco/repo/virtual/ref/Stringifier.java new file mode 100644 index 0000000000..2f16fcef6b --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/Stringifier.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.io.Serializable; +import java.util.List; + +/** + * A {@link Reference} abstract tree visitor designed to produce custom string + * representations. + * + * @author Bogdan Horje + */ +public interface Stringifier extends Serializable +{ + String stringify(Reference reference) throws ReferenceEncodingException; + + String stringify(Resource resource) throws ReferenceEncodingException; + + String stringifyResource(Resource resource) throws ReferenceEncodingException; + + String stringifyResource(RepositoryResource resource) throws ReferenceEncodingException; + + String stringifyResource(ClasspathResource resource) throws ReferenceEncodingException; + + String stringify(RepositoryLocation repositoryLocation) throws ReferenceEncodingException; + + String stringifyRepositoryLocation(RepositoryLocation repositoryLocation) throws ReferenceEncodingException; + + String stringifyRepositoryLocation(RepositoryNodeRef repositoryNodeRef) throws ReferenceEncodingException; + + String stringifyRepositoryLocation(RepositoryPath repositoryPath) throws ReferenceEncodingException; + + String stringify(List parameters) throws ReferenceEncodingException; + + String stringify(Parameter parameter) throws ReferenceEncodingException; + + String stringifyParameter(Parameter parameter) throws ReferenceEncodingException; + + String stringifyParameter(ResourceParameter resourceParameter) throws ReferenceEncodingException; + + String stringifyParameter(StringParameter stringParameter) throws ReferenceEncodingException; + + String stringifyParameter(ReferenceParameter referenceParameter) throws ReferenceEncodingException; +} diff --git a/source/java/org/alfresco/repo/virtual/ref/ValueParameter.java b/source/java/org/alfresco/repo/virtual/ref/ValueParameter.java new file mode 100644 index 0000000000..bee4981bd7 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/ValueParameter.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +/** + * Generic value of type V holder parameter. + * + * @param + */ +public abstract class ValueParameter implements Parameter +{ + private V value; + + public ValueParameter(V value) + { + super(); + this.value = value; + } + + public V getValue() + { + return value; + } + + @Override + public int hashCode() + { + return this.value != null ? this.value.hashCode() : 0; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj == null) + { + return false; + } + else if (!(getClass().equals(obj.getClass()))) + { + return false; + } + + if (obj instanceof ValueParameter) + { + ValueParameter other = (ValueParameter) obj; + + if (value == null) + { + return other.value == null; + } + else + { + return this.value.equals(other.value); + } + } + else + { + return false; + } + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/VanillaHashReferenceParser.java b/source/java/org/alfresco/repo/virtual/ref/VanillaHashReferenceParser.java new file mode 100644 index 0000000000..547976a0ad --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/VanillaHashReferenceParser.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.Collections; +import java.util.List; + +import org.alfresco.repo.virtual.ref.ReferenceParser.Cursor; +/** + * Custom parser for hash encoded strings of {@link Reference}s having the + * protocol set to {@link VanillaProtocol}. + */ +public class VanillaHashReferenceParser extends VirtualHashReferenceParser +{ + + public VanillaHashReferenceParser(HashStore classpathHashStore) + { + super(classpathHashStore, + VANILLA_PROTOCOL_CODE); + } + + @Override + protected Reference parseVirtualExtension(Cursor c, Resource templateResource, String templatePath, + Resource actualNodeResource) + { + Resource vanillaTemplateResource = parseResource(c); + // TODO :parse vanilla template + List extraParameters = Collections. emptyList(); + return ((VanillaProtocol) Protocols.VANILLA.protocol).newReference(Encodings.HASH.encoding, + templateResource, + templatePath, + actualNodeResource, + vanillaTemplateResource, + extraParameters); + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/VanillaHashStringifier.java b/source/java/org/alfresco/repo/virtual/ref/VanillaHashStringifier.java new file mode 100644 index 0000000000..5d184ff3cf --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/VanillaHashStringifier.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; +/** + * Custom stringifier for hash encoded strings of {@link Reference}s having the + * protocol set to {@link VanillaProtocol}. + */ +public class VanillaHashStringifier extends VirtualHashStringifier +{ + private static final long serialVersionUID = -2087786593789426927L; + + public VanillaHashStringifier(HashStore classpathHashStore, Stringifier referenceDispatcher) + { + super(classpathHashStore, + referenceDispatcher); + } + + @Override + public String stringify(Reference reference) throws ReferenceEncodingException + { + final Protocol protocol = reference.getProtocol(); + if (!Protocols.VANILLA.protocol.equals(protocol)) + { + throw new ReferenceEncodingException("Unsupported protocol " + protocol + "." + + Protocols.VIRTUAL.protocol.name + " exoected "); + } + + String virtualString = stringifyVirtualReference(reference); + + ResourceParameter vanillaTemplateParam = (ResourceParameter) reference + .getParameters() + .get(VanillaProtocol.VANILLA_TEMPLATE_PARAM_INDEX); + + String vanillaString = stringify(vanillaTemplateParam.getValue()); + + return VANILLA_PROTOCOL_CODE + "-" + virtualString + "-" + vanillaString; + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/VanillaProtocol.java b/source/java/org/alfresco/repo/virtual/ref/VanillaProtocol.java new file mode 100644 index 0000000000..7069be6330 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/VanillaProtocol.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * A {@link VirtualProtocol} extension that uses a scripted processor virtual + * template in order to process a so-called vanilla JSON static template + * definition on template execution.
+ * Vanilla references store have an extra {@link ResourceParameter} for the + * vanilla-JSON template. + * + * @author Bogdan Horje + */ +public class VanillaProtocol extends VirtualProtocol +{ + /** + * + */ + private static final long serialVersionUID = -7192024582935232081L; + + public static final int VANILLA_TEMPLATE_PARAM_INDEX = 2; + + public VanillaProtocol() + { + super("vanilla"); + } + + @Override + public R dispatch(ProtocolMethod method, Reference reference) throws ProtocolMethodException + { + return method.execute(this, + reference); + } + + public Reference newReference(String vanillaProcessorClasspath, String templatePath, NodeRef actualNodeRef, + NodeRef templateRef) + { + return this + .newReference(new ClasspathResource(vanillaProcessorClasspath), + templatePath, + actualNodeRef, + Arrays + . asList(new ResourceParameter(new RepositoryResource(new RepositoryNodeRef(templateRef))))); + } + + public Reference newReference(Encoding encoding, Resource virtualTemplateResource, String templatePath, + Resource actualResource, Resource vanillTemplateResource, List extraParameters) + { + List parameters = new ArrayList<>(2); + parameters.add(new ResourceParameter(vanillTemplateResource)); + parameters.addAll(extraParameters); + return this.newReference(encoding, + virtualTemplateResource, + templatePath, + actualResource, + parameters); + } + + public Resource getVanillaTemplateResource(Reference reference) + { + ResourceParameter vanillaTemplateParamter = (ResourceParameter) reference + .getParameters() + .get(VANILLA_TEMPLATE_PARAM_INDEX); + Resource resource = vanillaTemplateParamter.getValue(); + + return resource; + } + + public Reference newReference(String vanillaProcessorClasspath, String templatePath, NodeRef actualNodeRef, + String templateSysPath) throws ProtocolMethodException + { + Resource templateResource = createSystemPathResource(templateSysPath); + + if (templateResource != null) + { + return this.newReference(new ClasspathResource(vanillaProcessorClasspath), + templatePath, + actualNodeRef, + Arrays. asList(new ResourceParameter(templateResource))); + } + else + { + throw new ProtocolMethodException("Invalid template system path : " + templatePath); + } + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/VirtualHashReferenceParser.java b/source/java/org/alfresco/repo/virtual/ref/VirtualHashReferenceParser.java new file mode 100644 index 0000000000..cbdd0b196a --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/VirtualHashReferenceParser.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.Collections; +import java.util.List; +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + + +import org.alfresco.repo.virtual.ref.ReferenceParser.Cursor; +import org.alfresco.util.Pair; +/** + * Custom parser for hash encoded strings of {@link Reference}s having the + * protocol set to {@link VirtualProtocol}. + */ +public class VirtualHashReferenceParser extends ProtocolHashParser +{ + private String protocolCode; + + private NumericPathHasher numericPathHasher = new NumericPathHasher(); + + public VirtualHashReferenceParser(HashStore classpathHashStore) + { + this(classpathHashStore, + VIRTUAL_PROTOCOL_CODE); + } + + public VirtualHashReferenceParser(HashStore classpathHashStore, String protocolCode) + { + super(classpathHashStore); + this.protocolCode = protocolCode; + } + + @Override + public Reference parse(Cursor cursor) throws ReferenceParseException + { + if (!protocolCode.equals(cursor.currentToken())) + { + throw new ReferenceParseException("Node token \"" + protocolCode + "\" expected instead of \"" + + cursor.currentToken() + "\""); + } + cursor.i++; + + Resource templateResource = parseResource(cursor); + + Resource actualNodeResource = parseResource(cursor); + + String pathToken = cursor.nextToken(); + String pathCode = pathToken.substring(0, + 1); + String templatePath; + + if (HASHED_NUMERIC_PATH_CODE.equals(pathCode)) + { + String pathHash = pathToken.substring(1); + templatePath = numericPathHasher.lookup(new Pair(pathHash, + null)); + } + else if (NUMERIC_ROOT_PATH_CODE.equals(pathCode)) + { + templatePath = "/"; + } + else if (NUMERIC_PATH_CODE.equals(pathCode)) + { + String pathNonHashed = pathToken.substring(1); + templatePath = numericPathHasher.lookup(new Pair(null, + pathNonHashed)); + } + else if (MIXED_NUMERIC_PATH_CODE.equals(pathCode)) + { + String pathHash = pathToken.substring(1); + String pathNonHashed = cursor.nextToken(); + templatePath = numericPathHasher.lookup(new Pair(pathHash, + pathNonHashed)); + } + else + { + throw new ReferenceParseException("Pnvalid path token code " + pathCode); + } + + return parseVirtualExtension(cursor, + templateResource, + templatePath, + actualNodeResource); + + } + + protected Reference parseVirtualExtension(Cursor c, Resource templateResource, String templatePath, + Resource actualNodeResource) + { + List extraParameters = Collections. emptyList(); + return ((VirtualProtocol) Protocols.VIRTUAL.protocol).newReference(Encodings.HASH.encoding, + templateResource, + templatePath, + actualNodeResource, + extraParameters); + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/VirtualHashStringifier.java b/source/java/org/alfresco/repo/virtual/ref/VirtualHashStringifier.java new file mode 100644 index 0000000000..c7d9de4126 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/VirtualHashStringifier.java @@ -0,0 +1,103 @@ + +package org.alfresco.repo.virtual.ref; + +import java.util.List; + +import org.alfresco.util.Pair; +/** + * Custom stringifier for hash encoded strings of {@link Reference}s having the + * protocol set to {@link VirtualProtocol}. + */ +public class VirtualHashStringifier extends ProtocolHashStringifier +{ + private static final long serialVersionUID = -252596166306653635L; + + private NumericPathHasher numericPathHasher = new NumericPathHasher(); + + public VirtualHashStringifier(HashStore classpathHashStore, Stringifier referenceDispatcher) + { + super(classpathHashStore, + referenceDispatcher); + } + + /** + * + */ + + @Override + public String stringify(Reference reference) throws ReferenceEncodingException + { + final Protocol protocol = reference.getProtocol(); + if (!Protocols.VIRTUAL.protocol.equals(protocol)) + { + throw new ReferenceEncodingException("Unsupported protocol " + protocol + "." + + Protocols.VIRTUAL.protocol.name + " exoected "); + } + + return VIRTUAL_PROTOCOL_CODE + "-" + stringifyVirtualReference(reference); + } + + protected String stringifyVirtualReference(Reference reference) + { + Resource resource = reference.getResource(); + String resourceString = resource.stringify(this); + + List parameters = reference.getParameters(); + + ResourceParameter actualNodeParameter = (ResourceParameter) parameters + .get(VirtualProtocol.ACTUAL_NODE_LOCATION_PARAM_INDEX); + Resource actualNodeResource = actualNodeParameter.getValue(); + String actualNodeResourceString = actualNodeResource.stringify(this); + + StringParameter templatePathParameter; + templatePathParameter = (StringParameter) parameters.get(VirtualProtocol.TEMPLATE_PATH_PARAM_INDEX); + String pathString = templatePathParameter.getValue(); + + Pair pathHash = numericPathHasher.hash(pathString); + + StringBuilder stringifiedPath = new StringBuilder(); + String hashed = pathHash.getFirst(); + String nonHashed = pathHash.getSecond(); + + if (nonHashed == null) + { + stringifiedPath.append(HASHED_NUMERIC_PATH_CODE); + stringifiedPath.append(hashed); + } + else if (hashed == null) + { + if (nonHashed.isEmpty()) + { + stringifiedPath.append(NUMERIC_ROOT_PATH_CODE); + } + else + { + stringifiedPath.append(NUMERIC_PATH_CODE); + stringifiedPath.append(nonHashed); + } + } + else + { + stringifiedPath.append(MIXED_NUMERIC_PATH_CODE); + stringifiedPath.append(hashed); + stringifiedPath.append("-"); + stringifiedPath.append(nonHashed); + } + +// String delimitedPathString; +// if ("/".equals(pathString.trim())) +// { +// delimitedPathString = ""; +// } +// else +// { +// delimitedPathString = pathString.replace('/', +// '-'); +// } + + String parametersString = actualNodeResourceString +"-"+ stringifiedPath.toString(); + + return resourceString + "-" + parametersString; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/ref/VirtualProtocol.java b/source/java/org/alfresco/repo/virtual/ref/VirtualProtocol.java new file mode 100644 index 0000000000..513cc2ec11 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/VirtualProtocol.java @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.util.ParameterCheck; +import org.springframework.core.io.ClassPathResource; + +/** + * A protocol for encoding virtual artefacts.
+ * Virtual artefacts are generated using a virtual folder + * templateindicated by the main {@link Reference} resource.
+ * The virtual folder template defines a hierarchical structure of virtual + * nodes.
+ * The template path (see {@link #getTemplatePath(Reference)}) indicates + * a path in this structure.
+ * Virtual folders templates can be applied on actual repository nodes + * (see {@link #getActualNodeLocation(Reference)}) which should be passed as + * parameters to the virtual folder template.
+ * The protocol implementation also handles virtual protocol {@link Reference} + * creation and template path navigation. + */ +public class VirtualProtocol extends Protocol +{ + /** + * + */ + private static final long serialVersionUID = -520071882362365522L; + + /** + * Actual node {@link Parameter} index. + */ + public static final int ACTUAL_NODE_LOCATION_PARAM_INDEX = 1; + + /** + * Template path {@link Parameter} index. + */ + public static final int TEMPLATE_PATH_PARAM_INDEX = 0; + + /** + * Repository node path system path token. + */ + public static final Character NODE_TEMPLATE_PATH_TOKEN = 'N'; + + /** + * Classpath system path token. + */ + public static final Character CLASS_TEMPLATE_PATH_TOKEN = 'C'; + + public VirtualProtocol() + { + this("virtual"); + } + + public VirtualProtocol(String name) + { + super(name); + } + + @Override + public R dispatch(ProtocolMethod method, Reference reference) throws ProtocolMethodException + { + return method.execute(this, + reference); + } + + /** + * @param reference + * @return the inner template path referenced by the given {@link Reference} + * @see VirtualProtocol#TEMPLATE_PATH_PARAM_INDEX + */ + public String getTemplatePath(Reference reference) + { + StringParameter parameter = (StringParameter) getParameter(reference, + TEMPLATE_PATH_PARAM_INDEX); + return parameter.getValue(); + } + + /** + * @param reference + * @param path + * @return a {@link Reference} copy of the given reference parameter with + * the template path set to the given path parameter value + * @see VirtualProtocol#TEMPLATE_PATH_PARAM_INDEX + */ + public Reference replaceTemplatePath(Reference reference, String path) + { + return replaceParameter(reference, + TEMPLATE_PATH_PARAM_INDEX, + path); + } + + /** + * @param reference + * @return the repository location of the actual node that the virtual + * template should be applied on + * @see VirtualProtocol#ACTUAL_NODE_LOCATION_PARAM_INDEX + */ + public RepositoryLocation getActualNodeLocation(Reference reference) + { + ResourceParameter parameter = (ResourceParameter) getParameter(reference, + ACTUAL_NODE_LOCATION_PARAM_INDEX); + RepositoryResource repoResource = (RepositoryResource) parameter.getValue(); + return repoResource.getLocation(); + } + + /** + * @param templateNodeRef {@link NodeRef} of the template content holding + * repository node + * @param templatePath + * @param actualNodeRef + * @return a new virtual protocol {@link Reference} with the given virtual + * protocol reference elements + */ + public Reference newReference(NodeRef templateNodeRef, String templatePath, NodeRef actualNodeRef) + { + ParameterCheck.mandatoryString("templatePath", templatePath); + return this.newReference(new RepositoryResource(new RepositoryNodeRef(templateNodeRef)), + templatePath, + actualNodeRef, + Collections. emptyList()); + } + + /** + * @param templateResource template content holding resource + * @param templatePath + * @param actualNodeRef + * @return a new virtual protocol {@link Reference} with the given virtual + * protocol reference elements + */ + public Reference newReference(Resource templateResource, String templatePath, NodeRef actualNodeRef, + List extraParameters) + { + ParameterCheck.mandatoryString("templatePath", templatePath); + ArrayList parameters = new ArrayList(); + parameters.add(new StringParameter(templatePath)); + parameters.add(new ResourceParameter(new RepositoryResource(new RepositoryNodeRef(actualNodeRef)))); + parameters.addAll(extraParameters); + return new Reference(DEFAULT_ENCODING, + this, + templateResource, + parameters); + } + + public Reference newReference(Encoding encoding, Resource templateResource, String templatePath, + Resource actualNodeResource, List extraParameters) + { + ParameterCheck.mandatoryString("templatePath", templatePath); + ArrayList parameters = new ArrayList(3); + parameters.add(new StringParameter(templatePath)); + parameters.add(new ResourceParameter(actualNodeResource)); + parameters.addAll(extraParameters); + return new Reference(encoding, + this, + templateResource, + parameters); + } + + /** + * Creates a resource based on the given template system-path that is used + * in creating a new virtual protocol reference. + * + * @param templateSysPath a template-system-path for the template holding + * content
+ * Template-system-paths are classpaths paths or repository paths + * prefixed with + * {@link VirtualProtocol#CLASS_TEMPLATE_PATH_TOKEN} or + * {@link VirtualProtocol#NODE_TEMPLATE_PATH_TOKEN} respectively. + * @param templatePath + * @param actualNodeRef + * @return a new virtual protocol {@link Reference} with the given virtual + * protocol reference elements + * @throws ProtocolMethodException + * @deprecated In future system paths will be replaced with actual resources + * or string encoded references + */ + public Reference newReference(String templateSysPath, String templatePath, NodeRef actualNodeRef) + throws ProtocolMethodException + { + Resource templateResource = createSystemPathResource(templateSysPath); + + if (templateResource != null) + { + return this.newReference(templateResource, + templatePath, + actualNodeRef, + Collections. emptyList()); + } + else + { + throw new ProtocolMethodException("Invalid template system path : " + templatePath); + } + } + + /** + * System path resource factory method. + * + * @param templateSysPath a classpath or a repository path prefixed with + * {@link VirtualProtocol#CLASS_TEMPLATE_PATH_TOKEN} or + * {@link VirtualProtocol#NODE_TEMPLATE_PATH_TOKEN} respectively. + * @return a {@link ClassPathResource} or a {@link RepositoryResource} for + * the given system path + * @deprecated In future system paths will be replaced with actual resources + * or string encoded references + */ + protected Resource createSystemPathResource(String templateSysPath) + { + final char systemToken = templateSysPath.charAt(0); + + Resource templateResource = null; + + if (systemToken == NODE_TEMPLATE_PATH_TOKEN) + { + templateResource = new RepositoryResource(new RepositoryPath(templateSysPath.substring(1))); + } + else if (systemToken == CLASS_TEMPLATE_PATH_TOKEN) + { + templateResource = new ClasspathResource(templateSysPath.substring(1)); + } + + return templateResource; + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/ZeroEncoding.java b/source/java/org/alfresco/repo/virtual/ref/ZeroEncoding.java new file mode 100644 index 0000000000..c8d2dfa024 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/ZeroEncoding.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +public interface ZeroEncoding +{ + + static final String DELIMITER = ":"; + + static final String REFERENCE_DELIMITER = "*"; + + static final String STRING_PARAMETER = "s"; + + /** depending on the resource type */ + static final String RESOURCE_PARAMETER[] = { "0", "1", "2" }; + + static final String REFERENCE_PARAMETER = "r"; + + public static final int VANILLA_PROTOCOL_CODE = 0; + + public static final int VIRTUAL_PROTOCOL_CODE = 3; + + public static final int NODE_PROTOCOL_CODE = 6; + + public static final int REPOSITORY_RESOURCE_CODE = 0; + + public static final int PATH_CODE = 0; + + public static final int NODE_CODE = 1; + + public static final int CLASSPATH_RESOURCE_CODE = 2; +} diff --git a/source/java/org/alfresco/repo/virtual/ref/ZeroReferenceParser.java b/source/java/org/alfresco/repo/virtual/ref/ZeroReferenceParser.java new file mode 100644 index 0000000000..787a38b7d4 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/ZeroReferenceParser.java @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; + +public class ZeroReferenceParser implements ReferenceParser, ZeroEncoding +{ + + /** + * + */ + private static final long serialVersionUID = 795566334039858555L; + + /** + * Parses a String representation of a {@link Reference} and returns the + * corresponding {@link Reference}. Parsing is done with the help of the + * {@link Cursor} class, which keeps track of the String elements that make + * up the {@link Reference} String representation. This method instantiates + * and initiates the cursor and checks the validity of the referenceString + * and relies upon the {@link ZeroReferenceParser#parseReference} for the + * actual parsing. + * + * @param referenceString the String representation of a {@link Reference}. + * @return the newly created {@link Reference} + * @throws ReferenceParseException + */ + @Override + public Reference parse(String referenceString) throws ReferenceParseException + { + String[] referenceTokens = referenceString.split(DELIMITER); + final Cursor cursor = new Cursor(referenceTokens, + 0); + + if (referenceTokens.length < 2) + { + throw new ReferenceParseException("Invalid reference " + referenceString); + } + return this.parseReference(cursor); + } + + /** + * Parses a String representation of a {@link Reference} and returns the + * corresponding {@link Reference}. Parsing is done with the help of the + * {@link Cursor} class, which keeps track of the String elements that make + * up the {@link Reference} String representation. + * + * @param cursor + * @return A {@link Reference} instance. + * @throws ReferenceParseException + */ + private Reference parseReference(Cursor cursor) throws ReferenceParseException + { + try + { + Protocol protocol = null; + int protocolResourceEncoding = Integer.parseInt(cursor.tokens[cursor.i]); + if (protocolResourceEncoding < VIRTUAL_PROTOCOL_CODE) + { + protocol = Protocols.fromName("vanilla"); + } + else if (protocolResourceEncoding < NODE_PROTOCOL_CODE) + { + protocol = Protocols.fromName("virtual"); + protocolResourceEncoding -= VIRTUAL_PROTOCOL_CODE; + } + else if ((protocolResourceEncoding - NODE_PROTOCOL_CODE) < VIRTUAL_PROTOCOL_CODE) + { + protocol = Protocols.fromName("node"); + protocolResourceEncoding -= NODE_PROTOCOL_CODE; + } + if (protocol == null) + { + throw new ReferenceParseException("Unknown protocol " + protocol); + } + cursor.i++; + final Resource resource = parseResource(protocolResourceEncoding, + cursor); + final List parameters = parseParameters(cursor); + + Reference reference = new Reference(Encodings.ZERO.encoding, + protocol, + resource, + parameters); + return reference; + } + catch (ArrayIndexOutOfBoundsException e) + { + throw new ReferenceParseException("Invalid reference", + e); + } + } + + /** + * Parses a {@link Resource} which is an instance of + * {@link RepositoryResource} or {@link ClasspathResource} if the current + * token is contained in the array {@link ZeroEncoding#RESOURCE_PARAMETER}. + * + * @param resourceEncoding + * @param cursor + * @return an instance of {@link RepositoryResource} or + * {@link ClasspathResource} + * @throws ReferenceParseException + */ + private Resource parseResource(int resourceEncoding, Cursor cursor) throws ReferenceParseException + { + Resource resource = null; + if (resourceEncoding == PATH_CODE) + { + resource = new RepositoryResource(parseRepositoryPath(cursor)); + } + else if (resourceEncoding == NODE_CODE) + { + resource = new RepositoryResource(parseRepositoryNode(cursor)); + } + else if (resourceEncoding == CLASSPATH_RESOURCE_CODE) + { + resource = parseClasspathResource(cursor); + } + if (resource == null) + { + throw new ReferenceParseException("Unknown resource encoding " + resourceEncoding); + } + return resource; + } + + /** + * Parses a {@link Resource} reference which is an instance of + * {@link RepositoryResource} or {@link ClasspathResource}, depending on the + * {@link ZeroEncoding#RESOURCE_PARAMETER} + * + * @param cursor + * @return A {@link Resource} reference. + * @throws ReferenceParseException + */ + private Resource parseResource(Cursor cursor) throws ReferenceParseException + { + return this.parseResource(Byte.parseByte(cursor.tokens[cursor.i - 1]), + cursor); + } + + /** + * Creates a list of {@link Parameter}s by parsing the individual parameters + * given by the cursor's tokens. Stops parsing parameters upon encountering + * {@link ZeroEncoding#REFERENCE_DELIMITER}, which signals the end of a + * {@link ReferenceParameter} + * + * @param cursor + * @return a list of {@link Parameter}s + * @throws ReferenceParseException + */ + private List parseParameters(Cursor cursor) throws ReferenceParseException + { + List parameters = new ArrayList<>(); + while (cursor.i < cursor.tokens.length) + { + if (Arrays.asList(RESOURCE_PARAMETER).contains(cursor.tokens[cursor.i])) + { + cursor.i++; + parameters.add(parseResourceParameter(cursor)); + } + else if (STRING_PARAMETER.equals(cursor.tokens[cursor.i])) + { + cursor.i++; + parameters.add(parseStringParameter(cursor)); + } + else if (REFERENCE_PARAMETER.equals(cursor.tokens[cursor.i])) + { + cursor.i++; + parameters.add(parseReferenceParameter(cursor)); + } + else if (REFERENCE_DELIMITER.equals(cursor.tokens[cursor.i])) + { + cursor.i++; + break; + } + else + { + throw new ReferenceParseException("Invalid parameter " + cursor.tokens[cursor.i]); + } + } + return parameters; + } + + /** + * Creates a {@link Parameter} reference that is an instance of + * {@link StringParameter} by parsing the resource given by the cursor's + * current token. + * + * @param cursor + * @return a {@link Parameter} reference that is instance of + * {@link StringParameter} from the cursor parameter + */ + private Parameter parseStringParameter(Cursor cursor) + { + StringParameter paramenter = new StringParameter(cursor.tokens[cursor.i]); + cursor.i++; + return paramenter; + } + + /** + * Creates a {@link Parameter} reference that is an instance of + * {@link ResourceParameter} by parsing the resource given by the cursor's + * current token. + * + * @param cursor + * @return a {@link Parameter} reference that is instance of + * {@link ResourceParameter} from the cursor parameter + * @throws ReferenceParseException + */ + private Parameter parseResourceParameter(Cursor cursor) throws ReferenceParseException + { + Resource resource = parseResource(cursor); + return new ResourceParameter(resource); + } + + /** + * Creates a {@link RepositoryPath} reference from current token of the cursor. + * + * @param cursor + * @return A {@link RepositoryPath} reference. + */ + private RepositoryPath parseRepositoryPath(Cursor cursor) + { + String path = cursor.tokens[cursor.i]; + cursor.i++; + return new RepositoryPath(path); + } + + /** + * Creates a {@link RepositoryNodeRef} reference from the default + * {@link StoreRef}, SpacesStore and from the node id given by the cursor's + * current token. + * + * @param cursor + * @return A {@link RepositoryNodeRef} reference. + */ + private RepositoryNodeRef parseRepositoryNode(Cursor cursor) + { + String id = cursor.tokens[cursor.i]; + cursor.i++; + + return new RepositoryNodeRef(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, + id)); + } + + /** + * Creates a {@link ClasspathResource} reference from the current token of the + * cursor parameter. + * + * @param cursor + * @return A {@link ClasspathResource} reference. + */ + private ClasspathResource parseClasspathResource(Cursor cursor) + { + String classpath = cursor.tokens[cursor.i]; + cursor.i++; + return new ClasspathResource(classpath); + } + + /** + * Creates a {@link ReferenceParameter} reference from the cursor's tokens up + * to the * token which signifies the end of the current + * {@link ReferenceParameter}. + * + * @param cursor + * @return A {@link ReferenceParameter} reference. + */ + private ReferenceParameter parseReferenceParameter(Cursor cursor) throws ReferenceParseException + { + Reference reference = parseReference(cursor); + ReferenceParameter parameter = new ReferenceParameter(reference); + return parameter; + } +} diff --git a/source/java/org/alfresco/repo/virtual/ref/ZeroStringifier.java b/source/java/org/alfresco/repo/virtual/ref/ZeroStringifier.java new file mode 100644 index 0000000000..19045c565d --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/ref/ZeroStringifier.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.ref; + +import java.util.List; + +import org.alfresco.service.cmr.repository.NodeRef; + +public class ZeroStringifier implements Stringifier, ZeroEncoding +{ + + private static final long serialVersionUID = 6777894566062875199L; + + private int getProtocolEncoding(Protocol protocol) throws ReferenceEncodingException + { + String protocolStr = protocol.toString(); + if (protocolStr.equals("vanilla")) + { + return VANILLA_PROTOCOL_CODE; + } + else if (protocolStr.equals("virtual")) + { + return VIRTUAL_PROTOCOL_CODE; + } + else if (protocolStr.equals("node")) + { + return NODE_PROTOCOL_CODE; + } + else + { + throw new ReferenceEncodingException("Invalid protocol: " + protocolStr); + } + } + + @Override + public String stringify(Reference reference) throws ReferenceEncodingException + { + String resource = stringify(reference.getResource()); + int protocolResourceEncoding = this.getProtocolEncoding(reference.getProtocol()); + protocolResourceEncoding += Integer.parseInt(resource.substring(0, + 1)); + return protocolResourceEncoding + DELIMITER + resource.substring(2) + stringify(reference.getParameters()); + } + + @Override + public String stringify(Resource resource) throws ReferenceEncodingException + { + return resource.stringify(this); + } + + @Override + public String stringifyResource(Resource resource) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Invalid reference " + resource.getClass()); + } + + @Override + public String stringifyResource(RepositoryResource resource) throws ReferenceEncodingException + { + String resourceLocation = stringify(resource.getLocation()); + int locationCode = Integer.parseInt(resourceLocation.substring(0, + 1)); + locationCode += REPOSITORY_RESOURCE_CODE; + return locationCode + DELIMITER + resourceLocation.substring(2); + } + + @Override + public String stringifyResource(ClasspathResource resource) + { + return CLASSPATH_RESOURCE_CODE + DELIMITER + resource.getClasspath(); + } + + @Override + public String stringify(RepositoryLocation repositoryLocation) throws ReferenceEncodingException + { + return repositoryLocation.stringify(this); + } + + @Override + public String stringifyRepositoryLocation(RepositoryLocation repositoryLocation) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Invalid location: " + repositoryLocation.getClass()); + } + + @Override + public String stringifyRepositoryLocation(RepositoryNodeRef repositoryNodeRef) throws ReferenceEncodingException + { + NodeRef nodeRef = repositoryNodeRef.getNodeRef(); + return NODE_CODE + DELIMITER + nodeRef.getId(); + } + + @Override + public String stringifyRepositoryLocation(RepositoryPath repositoryPath) throws ReferenceEncodingException + { + return PATH_CODE + DELIMITER + repositoryPath.getPath(); + } + + @Override + public String stringify(List parameters) throws ReferenceEncodingException + { + StringBuilder parametersBuilder = new StringBuilder(); + for (Parameter parameter : parameters) + { + parametersBuilder.append(DELIMITER); + parametersBuilder.append(stringify(parameter)); + } + return parametersBuilder.toString(); + } + + @Override + public String stringify(Parameter parameter) throws ReferenceEncodingException + { + return parameter.stringify(this); + } + + @Override + public String stringifyParameter(ResourceParameter resourceParameter) throws ReferenceEncodingException + { + return stringify(resourceParameter.getValue()); + } + + @Override + public String stringifyParameter(StringParameter stringParameter) throws ReferenceEncodingException + { + return STRING_PARAMETER + DELIMITER + stringParameter.getValue(); + } + + @Override + public String stringifyParameter(Parameter parameter) throws ReferenceEncodingException + { + throw new ReferenceEncodingException("Invalid parameter: " + parameter.getClass()); + } + + @Override + public String stringifyParameter(ReferenceParameter parameter) throws ReferenceEncodingException + { + return REFERENCE_PARAMETER + DELIMITER + stringify(parameter.getValue()) + DELIMITER + REFERENCE_DELIMITER; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/store/AspectVirtualizationMethod.java b/source/java/org/alfresco/repo/virtual/store/AspectVirtualizationMethod.java new file mode 100644 index 0000000000..8269594579 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/store/AspectVirtualizationMethod.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.store; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ActualEnvironmentException; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; + +/** + * A template virtualization rule implementation that uses Alfresco-aspect + * defined meta-data for locating templates.
+ * Stores the aspect {@link QName} to be used in resolving templates by querying + * aspect defined meta data from the virtualized {@link NodeRef}. + * + * @author Bogdan Horje + */ +public abstract class AspectVirtualizationMethod extends TemplateVirtualizationMethod +{ + private QName aspectQName; + + private String aspectName; + + protected NamespacePrefixResolver namespacePrefixResolver; + + public AspectVirtualizationMethod() + { + + } + + public AspectVirtualizationMethod(QName aspectName) + { + super(); + this.aspectQName = aspectName; + } + + public void init() + { + if (aspectName != null) + { + aspectQName = QName.createQName(aspectName, + namespacePrefixResolver); + } + } + + /** + * Determines if a given {@link NodeRef} can be virtualized by this + * virtualization method by checking the presence of the of the configured + * aspect (i.e. {@link #aspectQName}) on the given {@link NodeRef}. + * + * @param env the environment in which the virtualization should take place + * @param nodeRef the {@link NodeRef} that should be virtualized + * @return true if the given {@link NodeRef} can be virtualized + * by this virtualization method (i.e. the configurend aspect is set + * on the given {@link NodeRef}) false otherwise + * @throws VirtualizationException + */ + @Override + public boolean canVirtualize(ActualEnvironment env, NodeRef nodeRef) throws ActualEnvironmentException + { + return env.hasAspect(nodeRef, + getAspectQName()); + } + + protected QName getAspectQName() + { + return aspectQName; + } + + public void setAspectName(String aspectName) + { + this.aspectName = aspectName; + } + + public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) + { + this.namespacePrefixResolver = namespacePrefixResolver; + } +} diff --git a/source/java/org/alfresco/repo/virtual/store/CustomVirtualizationMethod.java b/source/java/org/alfresco/repo/virtual/store/CustomVirtualizationMethod.java new file mode 100644 index 0000000000..908c59c1dc --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/store/CustomVirtualizationMethod.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.store; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ActualEnvironmentException; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * An {@link AspectVirtualizationMethod} that uses an aspect defined repository + * association to a node that holds the template contents. + * + * @author Bogdan Horje + */ +public class CustomVirtualizationMethod extends AspectVirtualizationMethod +{ + /** Template association {@link QName} */ + private QName associationQName; + + /** + * String representation of the template association. Will be converted into + * a {@link QName} during {@link #init()} + */ + private String associationName; + + public CustomVirtualizationMethod() + { + + } + + /** + * Bean initialization. + */ + @Override + public void init() + { + super.init(); + if (associationName != null) + { + associationQName = QName.createQName(associationName, + namespacePrefixResolver); + } + } + + public void setAssociationName(String associationName) + { + this.associationName = associationName; + } + + @Override + public Reference virtualize(ActualEnvironment env, NodeRef nodeRef) throws VirtualizationException + { + NodeRef templateNode = env.getTargetAssocs(nodeRef, + associationQName); + + if (templateNode != null) + { + return newVirtualReference(env, + nodeRef, + templateNode); + } + else + { + // default branch - invalid virtual node + throw new VirtualizationException("Invalid virtualization : missing template association."); + } + } + + @Override + public boolean canVirtualize(ActualEnvironment env, NodeRef nodeRef) throws ActualEnvironmentException + { + boolean canVirtualize = super.canVirtualize(env, + nodeRef); + if (canVirtualize) + { + // TODO: optimize - should not need another repository meta data access !!! + + NodeRef templateNode = env.getTargetAssocs(nodeRef, + associationQName); + canVirtualize = templateNode != null; + } + + return canVirtualize; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/store/GetAllSetPermissionsMethod.java b/source/java/org/alfresco/repo/virtual/store/GetAllSetPermissionsMethod.java new file mode 100644 index 0000000000..558457c067 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/store/GetAllSetPermissionsMethod.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.store; + +import java.util.HashSet; +import java.util.Set; + +import org.alfresco.repo.security.permissions.impl.AccessPermissionImpl; +import org.alfresco.repo.virtual.ref.AbstractProtocolMethod; +import org.alfresco.repo.virtual.ref.NodeProtocol; +import org.alfresco.repo.virtual.ref.ProtocolMethodException; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.ref.VirtualProtocol; +import org.alfresco.repo.virtual.template.FilingParameters; +import org.alfresco.repo.virtual.template.FilingRule; +import org.alfresco.repo.virtual.template.VirtualFolderDefinition; +import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.security.AccessStatus; + +public class GetAllSetPermissionsMethod extends AbstractProtocolMethod> +{ + private VirtualUserPermissions userPermissions; + + private String authority; + + private VirtualFolderDefinitionResolver resolver; + + public GetAllSetPermissionsMethod(VirtualFolderDefinitionResolver resolver, VirtualUserPermissions userPermissions, + String authority) + { + super(); + this.userPermissions = userPermissions; + this.authority = authority; + this.resolver = resolver; + } + + @Override + public Set execute(VirtualProtocol virtualProtocol, Reference reference) + throws ProtocolMethodException + { + Set toAllow = userPermissions.getAllowVirtualNodes(); + Set toDeny = userPermissions.getDenyVirtualNodes(); + + VirtualFolderDefinition definition = resolver.resolveVirtualFolderDefinition(reference); + FilingRule filingRule = definition.getFilingRule(); + boolean readonly = filingRule.isNullFilingRule() + || filingRule.filingNodeRefFor(new FilingParameters(reference)) == null; + if (readonly) + { + Set deniedPermissions = userPermissions.getDenyReadonlyVirtualNodes(); + toDeny = new HashSet<>(toDeny); + toDeny.addAll(deniedPermissions); + } + else + { + + } + + return execute(reference, + toAllow, + toDeny); + } + + private Set execute(Reference reference, Set toAllow, Set toDeny) + { + Set permissions = new HashSet<>(); + + for (String permission : toAllow) + { + permissions.add(new AccessPermissionImpl(permission, + AccessStatus.ALLOWED, + authority, + 0)); + } + + for (String permission : toDeny) + { + + permissions.add(new AccessPermissionImpl(permission, + AccessStatus.DENIED, + authority, + 0)); + } + + return permissions; + } + + @Override + public Set execute(NodeProtocol protocol, Reference reference) throws ProtocolMethodException + { + Set toAllow = userPermissions.getAllowQueryNodes(); + Set toDeny = userPermissions.getDenyQueryNodes(); + + return execute(reference, + toAllow, + toDeny); + } +} diff --git a/source/java/org/alfresco/repo/virtual/store/GetChildAssocsMethod.java b/source/java/org/alfresco/repo/virtual/store/GetChildAssocsMethod.java new file mode 100644 index 0000000000..5e009303a4 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/store/GetChildAssocsMethod.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.store; + +import java.io.Serializable; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.VirtualContentModel; +import org.alfresco.repo.virtual.ref.AbstractProtocolMethod; +import org.alfresco.repo.virtual.ref.GetActualNodeRefMethod; +import org.alfresco.repo.virtual.ref.NodeProtocol; +import org.alfresco.repo.virtual.ref.ProtocolMethodException; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.ref.VirtualProtocol; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.QNamePattern; + +public class GetChildAssocsMethod extends AbstractProtocolMethod> +{ + private VirtualStore virtualStore; + + private ActualEnvironment environment; + + private boolean preload; + + private int maxResults; + + private QNamePattern qnamePattern; + + private QNamePattern typeQNamePattern; + + public GetChildAssocsMethod(VirtualStore virtualStore, ActualEnvironment environment, boolean preload, + int maxResults, QNamePattern qnamePattern, QNamePattern typeQNamePattern) + { + super(); + this.virtualStore = virtualStore; + this.environment = environment; + this.preload = preload; + this.maxResults = maxResults; + this.qnamePattern = qnamePattern; + this.typeQNamePattern = typeQNamePattern; + } + + @Override + public List execute(VirtualProtocol virtualProtocol, Reference reference) + throws ProtocolMethodException + { + if (typeQNamePattern.isMatch(ContentModel.ASSOC_CONTAINS)) + { + List childAssocs = new LinkedList<>(); + List children = virtualStore.list(reference); + NodeRef nodeRefReference = reference.toNodeRef(); + int count = 0; + for (Reference child : children) + { + if (count >= maxResults) + { + break; + } + + NodeRef childNodeRef = child.toNodeRef(); + Serializable childName = environment.getProperty(childNodeRef, + ContentModel.PROP_NAME); + QName childAssocQName = QName + .createQNameWithValidLocalName(VirtualContentModel.VIRTUAL_CONTENT_MODEL_1_0_URI, + childName.toString()); + if (qnamePattern.isMatch(childAssocQName)) + { + + ChildAssociationRef childAssoc = new ChildAssociationRef(ContentModel.ASSOC_CONTAINS, + nodeRefReference, + childAssocQName, + childNodeRef, + true, + -1); + childAssocs.add(childAssoc); + count++; + } + } + + return childAssocs; + } + else + { + return Collections.emptyList(); + } + } + + @Override + public List execute(NodeProtocol protocol, Reference reference) throws ProtocolMethodException + { + NodeRef actualNodeRef = reference.execute(new GetActualNodeRefMethod(null)); + NodeRef nodeRefReference = reference.toNodeRef(); + List referenceAssociations = new LinkedList<>(); + if (!environment.isSubClass(environment.getType(nodeRefReference), ContentModel.TYPE_FOLDER)) + { + List actualAssociations = environment.getChildAssocs(actualNodeRef, + typeQNamePattern, + qnamePattern, + maxResults, + preload); + + for (ChildAssociationRef actualAssoc : actualAssociations) + { + ChildAssociationRef referenceChildAssocRef = new ChildAssociationRef(actualAssoc.getTypeQName(), + nodeRefReference, + actualAssoc.getQName(), + actualAssoc.getChildRef(), + actualAssoc.isPrimary(), + actualAssoc.getNthSibling()); + + referenceAssociations.add(referenceChildAssocRef); + } + } + return referenceAssociations; + } +} diff --git a/source/java/org/alfresco/repo/virtual/store/GetSetPermissionsMethod.java b/source/java/org/alfresco/repo/virtual/store/GetSetPermissionsMethod.java new file mode 100644 index 0000000000..9a411a4247 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/store/GetSetPermissionsMethod.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.store; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.alfresco.repo.security.permissions.NodePermissionEntry; +import org.alfresco.repo.security.permissions.PermissionEntry; +import org.alfresco.repo.security.permissions.PermissionReference; +import org.alfresco.repo.security.permissions.impl.PermissionReferenceImpl; +import org.alfresco.repo.security.permissions.impl.SimpleNodePermissionEntry; +import org.alfresco.repo.security.permissions.impl.SimplePermissionEntry; +import org.alfresco.repo.virtual.ref.AbstractProtocolMethod; +import org.alfresco.repo.virtual.ref.NodeProtocol; +import org.alfresco.repo.virtual.ref.ProtocolMethodException; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.ref.VirtualProtocol; +import org.alfresco.repo.virtual.template.FilingParameters; +import org.alfresco.repo.virtual.template.FilingRule; +import org.alfresco.repo.virtual.template.VirtualFolderDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AccessStatus; + +public class GetSetPermissionsMethod extends AbstractProtocolMethod +{ + private VirtualUserPermissions userPermissions; + + private String authority; + + private VirtualFolderDefinitionResolver resolver; + + public GetSetPermissionsMethod(VirtualFolderDefinitionResolver resolver, VirtualUserPermissions userPermissions, + String authority) + { + super(); + this.userPermissions = userPermissions; + this.authority = authority; + this.resolver = resolver; + } + + @Override + public NodePermissionEntry execute(VirtualProtocol virtualProtocol, Reference reference) + throws ProtocolMethodException + { + Set toAllow = userPermissions.getAllowVirtualNodes(); + Set toDeny = userPermissions.getDenyVirtualNodes(); + VirtualFolderDefinition definition = resolver.resolveVirtualFolderDefinition(reference); + FilingRule filingRule = definition.getFilingRule(); + boolean readonly = filingRule.isNullFilingRule() + || filingRule.filingNodeRefFor(new FilingParameters(reference)) == null; + if (readonly) + { + Set deniedPermissions = userPermissions.getDenyReadonlyVirtualNodes(); + toDeny = new HashSet<>(toDeny); + toDeny.addAll(deniedPermissions); + } + + return execute(reference, + toAllow, + toDeny); + } + + private NodePermissionEntry execute(Reference reference, Set toAllow, Set toDeny) + { + NodeRef rNodeRef = reference.toNodeRef(); + List permissions = new LinkedList<>(); + + for (String permission : toAllow) + { + PermissionReference permissionReference = PermissionReferenceImpl + .getPermissionReference(userPermissions.getPermissionTypeQName(), + permission); + permissions.add(new SimplePermissionEntry(rNodeRef, + permissionReference, + authority, + AccessStatus.ALLOWED)); + } + + for (String permission : toDeny) + { + PermissionReference permissionReference = PermissionReferenceImpl + .getPermissionReference(userPermissions.getPermissionTypeQName(), + permission); + permissions.add(new SimplePermissionEntry(rNodeRef, + permissionReference, + authority, + AccessStatus.DENIED)); + } + + return new SimpleNodePermissionEntry(rNodeRef, + false, + permissions); + } + + @Override + public NodePermissionEntry execute(NodeProtocol protocol, Reference reference) throws ProtocolMethodException + { + Set toAllow = userPermissions.getAllowQueryNodes(); + Set toDeny = userPermissions.getDenyQueryNodes(); + + return execute(reference, + toAllow, + toDeny); + } +} diff --git a/source/java/org/alfresco/repo/virtual/store/HasPermissionMethod.java b/source/java/org/alfresco/repo/virtual/store/HasPermissionMethod.java new file mode 100644 index 0000000000..00d73d5072 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/store/HasPermissionMethod.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.store; + +import java.util.Set; + +import org.alfresco.repo.virtual.ref.AbstractProtocolMethod; +import org.alfresco.repo.virtual.ref.NodeProtocol; +import org.alfresco.repo.virtual.ref.ProtocolMethodException; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.ref.VirtualProtocol; +import org.alfresco.repo.virtual.template.FilingParameters; +import org.alfresco.repo.virtual.template.FilingRule; +import org.alfresco.repo.virtual.template.VirtualFolderDefinition; +import org.alfresco.service.cmr.security.AccessStatus; + +public class HasPermissionMethod extends AbstractProtocolMethod +{ + private VirtualUserPermissions userPermissions; + + private String permissionToCheck; + + private VirtualFolderDefinitionResolver resolver; + + public HasPermissionMethod(VirtualFolderDefinitionResolver resolver, VirtualUserPermissions userPermissions, + String permissionToCheck) + { + super(); + this.userPermissions = userPermissions; + this.permissionToCheck = permissionToCheck; + this.resolver = resolver; + } + + @Override + public AccessStatus execute(VirtualProtocol virtualProtocol, Reference reference) throws ProtocolMethodException + { + VirtualFolderDefinition definition = resolver.resolveVirtualFolderDefinition(reference); + FilingRule filingRule = definition.getFilingRule(); + + boolean readonly = filingRule.isNullFilingRule() + || filingRule.filingNodeRefFor(new FilingParameters(reference)) == null; + if (readonly) + { + Set deniedPermissions = userPermissions.getDenyReadonlyVirtualNodes(); + if (deniedPermissions.contains(permissionToCheck)) + { + return AccessStatus.DENIED; + } + } + + return userPermissions.hasVirtualNodePermission(permissionToCheck, + readonly); + } + + @Override + public AccessStatus execute(NodeProtocol protocol, Reference reference) throws ProtocolMethodException + { + return userPermissions.hasQueryNodePermission(permissionToCheck); + } +} diff --git a/source/java/org/alfresco/repo/virtual/store/ReferenceComparator.java b/source/java/org/alfresco/repo/virtual/store/ReferenceComparator.java new file mode 100644 index 0000000000..73a2d8cebe --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/store/ReferenceComparator.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.store; + +import java.io.Serializable; +import java.text.Collator; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.AlfrescoCollator; +import org.alfresco.util.Pair; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * Property value based virtual nodes {@link Comparator}. Compares two + * virtualized nodes based on an ordered collection of property names. The + * property with the lower index in the collection of properties is the more + * significant (with the highest-order) one in the comparison process. + * + * @author Silviu Dinuta + */ +public class ReferenceComparator implements Comparator +{ + private VirtualStore virtualStore; + + private List> sortProps; + + private Collator collator; + + public ReferenceComparator(VirtualStore virtualStore, List> sortProps) + { + this.sortProps = sortProps; + this.collator = AlfrescoCollator.getInstance(I18NUtil.getContentLocale()); + this.virtualStore = virtualStore; + } + + @Override + public int compare(Reference r1, Reference r2) + { + try + { + return compareImpl(r1, + r2, + sortProps); + } + catch (VirtualizationException e) + { + throw new RuntimeException(e); + } + } + + private int compareImpl(Reference ref1In, Reference ref2In, List> sortProps) + throws VirtualizationException + { + Object pv1 = null; + Object pv2 = null; + + QName sortPropQName = (QName) sortProps.get(0).getFirst(); + boolean sortAscending = sortProps.get(0).getSecond(); + + Reference ref1 = ref1In; + Reference ref2 = ref2In; + + if (sortAscending == false) + { + ref1 = ref2In; + ref2 = ref1In; + } + + int result = 0; + + Map properties1 = virtualStore.getProperties(ref1); + pv1 = properties1.get(sortPropQName); + + Map properties2 = virtualStore.getProperties(ref2); + pv2 = properties2.get(sortPropQName); + + if (pv1 == null) + { + if (pv2 == null && sortProps.size() > 1) + { + return compareImpl(ref1In, + ref2In, + sortProps.subList(1, + sortProps.size())); + } + else + { + return (pv2 == null ? 0 : -1); + } + } + else if (pv2 == null) + { + return 1; + } + + if (pv1 instanceof String) + { + // TODO: use collation keys (re: performance) + result = collator.compare((String) pv1, + (String) pv2); + } + else if (pv1 instanceof Date) + { + result = (((Date) pv1).compareTo((Date) pv2)); + } + else if (pv1 instanceof Long) + { + result = (((Long) pv1).compareTo((Long) pv2)); + } + else if (pv1 instanceof Integer) + { + result = (((Integer) pv1).compareTo((Integer) pv2)); + } + else if (pv1 instanceof QName) + { + result = (((QName) pv1).compareTo((QName) pv2)); + } + else if (pv1 instanceof Boolean) + { + result = (((Boolean) pv1).compareTo((Boolean) pv2)); + } + else + { + throw new RuntimeException("Unsupported sort type: " + pv1.getClass().getName()); + } + + if ((result == 0) && (sortProps.size() > 1)) + { + return compareImpl(ref1In, + ref2In, + sortProps.subList(1, + sortProps.size())); + } + + return result; + } +} diff --git a/source/java/org/alfresco/repo/virtual/store/SystemVirtualizationMethod.java b/source/java/org/alfresco/repo/virtual/store/SystemVirtualizationMethod.java new file mode 100644 index 0000000000..558e74f097 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/store/SystemVirtualizationMethod.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.store; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ActualEnvironmentException; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.repo.virtual.model.SystemTemplateLocationsConstraint; +import org.alfresco.repo.virtual.ref.Encodings; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.ref.VirtualProtocol; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * An {@link AspectVirtualizationMethod} that uses an aspect defined String + * property that holds the system path of a template resource.
+ * System paths are custom string reference of a resource that can be located + * either in the repository or in the java classpath - system paths are + * deprecated and they will be replaced by {@link Encodings#PLAIN} encoded + * {@link Reference} strings.
+ * + * @author Bogdan Horje + */ +public class SystemVirtualizationMethod extends AspectVirtualizationMethod +{ + /** {@link QName} of template system path holding property */ + private QName systemPathPropertyQName; + + /** + * String name of template system path holding property. Will be converted + * into a {@link QName} during {@link #init()} + */ + private String systemPathPropertyName; + + public SystemVirtualizationMethod() + { + + } + + /** + * Bean initialization. + */ + @Override + public void init() + { + super.init(); + if (systemPathPropertyName != null) + { + systemPathPropertyQName = QName.createQName(systemPathPropertyName, + namespacePrefixResolver); + } + } + + public void setSystemPathPropertyName(String systemPathPropertyName) + { + this.systemPathPropertyName = systemPathPropertyName; + } + + @Override + public Reference virtualize(ActualEnvironment env, NodeRef nodeRef) throws VirtualizationException + { + String templateSystemPath = (String) env.getProperty(nodeRef, + systemPathPropertyQName); + if (templateSystemPath != null) + { + return newVirtualReference(env, + nodeRef, + templateSystemPath); + } + else + { + // default branch - invalid virtual node + throw new VirtualizationException("Invalid virtualization : missing template system-path."); + } + } + + @Override + public boolean canVirtualize(ActualEnvironment env, NodeRef nodeRef) throws ActualEnvironmentException + { + boolean canVirtualize = super.canVirtualize(env, + nodeRef); + if (canVirtualize) + { + // TODO: optimize - should not need another repository meta data access !!! + // Optimization requires a default value specified. + + String templateSystemPath = (String) env.getProperty(nodeRef, + systemPathPropertyQName); + if(templateSystemPath==null){ + return false; + } + final char systemToken = templateSystemPath.charAt(0); + if (systemToken == VirtualProtocol.NODE_TEMPLATE_PATH_TOKEN) + { + return env.exists(new NodeRef(templateSystemPath.substring(1))); + } + + if (systemToken == VirtualProtocol.CLASS_TEMPLATE_PATH_TOKEN) + { + return env.exists(templateSystemPath.substring(1)); + } + canVirtualize = !templateSystemPath.equals(SystemTemplateLocationsConstraint.NULL_SYSTEM_TEMPLATE); + } + + return canVirtualize; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/store/TemplateVirtualizationMethod.java b/source/java/org/alfresco/repo/virtual/store/TemplateVirtualizationMethod.java new file mode 100644 index 0000000000..c280b16eff --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/store/TemplateVirtualizationMethod.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.store; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ActualEnvironmentException; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.repo.virtual.ref.Encodings; +import org.alfresco.repo.virtual.ref.NewVirtualReferenceMethod; +import org.alfresco.repo.virtual.ref.Protocol; +import org.alfresco.repo.virtual.ref.ProtocolMethodException; +import org.alfresco.repo.virtual.ref.Protocols; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.ref.VirtualProtocol; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Base implementation for virtualization rules defined using template located + * in the content repository or in a system path.
+ * System paths are custom string references of a resource that can be located + * either in the repository or in the java classpath - system paths are + * deprecated and they will be replaced by {@link Encodings#PLAIN} encoded + * {@link Reference} strings.
+ * Templates are programmatic or declarative definitions of the rules + * implemented by this virtualization method.
+ * Supported template formats include JavaScript defined templates and + * JSON defined templates (JSON templates are also referred as vanilla + * templates). The extension present in the name of the template is used to + * determine the nature of the template (*.js for JavaScript and *.json for + * JSON).
+ * JSON templates processing is done using a configurable JavaScriot processor + * script (actually a JavaScript template that gets an extra parameter + * containing the JSON template) that resides in the Java class path.
+ * Templates are processed in order to virtualize {@link NodeRef}s using + * {@link NewVirtualReferenceMethod} protocol reference constructor visitor. + *
+ * + * @author Bogdan Horje + */ +public abstract class TemplateVirtualizationMethod implements VirtualizationMethod +{ + protected static final String PATH_SEPARATOR = "/"; + + private String vanillaProcessorClasspath; + + public TemplateVirtualizationMethod() + { + super(); + } + + public void setVanillaProcessor(String vanillaProcessorClasspath) + { + this.vanillaProcessorClasspath = vanillaProcessorClasspath; + } + + /** + * @param env the environment in which the virtualization takes place + * @param actualNodeRef the node that is virtualized using the given + * template + * @param templateSystemPath system path string of the template used in + * virtualizing the given NodeRef + * @return a {@link Reference} correspondent of the given {@link NodeRef} + * according to the rules defined by the given template + * @throws VirtualizationException + * @deprecated all template system path functionality should be replaced by + * plain encoded references + */ + protected Reference newVirtualReference(ActualEnvironment env, NodeRef actualNodeRef, String templateSystemPath) + throws VirtualizationException + { + + final char systemToken = templateSystemPath.charAt(0); + if (systemToken == VirtualProtocol.NODE_TEMPLATE_PATH_TOKEN) + { + // create node based reference + return newVirtualReference(env, + actualNodeRef, + new NodeRef(templateSystemPath.substring(1))); + } + + String templateName = retrieveTemplateContentName(env, + templateSystemPath); + if (!templateName.isEmpty()) + { + Protocol protocol = protocolFormName(templateName); + + return protocol.dispatch(new NewVirtualReferenceMethod(templateSystemPath, + PATH_SEPARATOR, + actualNodeRef, + vanillaProcessorClasspath), + null); + } + else + { + // default branch - invalid virtual node + throw new VirtualizationException("Invalid virtualization : missing template name for " + + templateSystemPath); + } + } + + /** + * @param env the environment in which the virtualization takes place + * @param actualNodeRef the node that is virtualized using the given + * template + * @param templateRef {@link NodeRef} of the template used in virtualizing + * the given NodeRef + * @return a {@link Reference} correspondent of the given {@link NodeRef} + * according to the rules defined by the given template + * @throws VirtualizationException + */ + protected Reference newVirtualReference(ActualEnvironment env, NodeRef actualNodeRef, NodeRef templateRef) + throws VirtualizationException + { + String templateName = retrieveTemplateContentName(env, + templateRef); + if (templateName != null) + { + Protocol protocol = protocolFormName(templateName); + + return newVirtualReference(protocol, + templateRef, + actualNodeRef); + } + else + { + // default branch - invalid virtual node + throw new VirtualizationException("Invalid virtualization : missing template name for " + templateRef); + } + } + + /** + * @param protocol {@link Protocol} to be used in virtualizing the given + * actulalNodeRef + * @param templateRef {@link NodeRef} of the template used in virtualizing + * the given NodeRef + * @param actualNodeRef the node that is virtualized using the given + * template + * @return a {@link Reference} correspondent of the given {@link NodeRef} + * according to the rules defined by the given template + * @throws ProtocolMethodException + */ + protected Reference newVirtualReference(Protocol protocol, NodeRef templateRef, NodeRef actualNodeRef) + throws ProtocolMethodException + { + return protocol.dispatch(new NewVirtualReferenceMethod(templateRef, + PATH_SEPARATOR, + actualNodeRef, + vanillaProcessorClasspath), + null); + } + + /** + * @param env + * @param sysPath + * @return template name for the given system path + * @throws ActualEnvironmentException + * @deprecated all template system path functionality should be replaced by + * plain encoded references + */ + private String retrieveTemplateContentName(ActualEnvironment env, String sysPath) throws ActualEnvironmentException + { + int index = sysPath.lastIndexOf(PATH_SEPARATOR); + if (index < 0) + { + index = 1; + } + return sysPath.substring(index); + } + + private String retrieveTemplateContentName(ActualEnvironment env, NodeRef templateRef) + throws ActualEnvironmentException + { + String templateName = (String) env.getProperty(templateRef, + ContentModel.PROP_NAME); + return templateName; + } + + private final Protocol protocolFormName(String name) throws VirtualizationException + { + + if (name.toUpperCase().endsWith(".JS")) + { + return Protocols.VIRTUAL.protocol; + } + else if (name.toUpperCase().endsWith(".JSON")) + { + return Protocols.VANILLA.protocol; + } + else + { + throw new VirtualizationException("Invalid template script file .extension for " + name); + } + } + +} diff --git a/source/java/org/alfresco/repo/virtual/store/TypeVirtualizationMethod.java b/source/java/org/alfresco/repo/virtual/store/TypeVirtualizationMethod.java new file mode 100644 index 0000000000..0092aa0fa7 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/store/TypeVirtualizationMethod.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.store; + +import java.util.Set; +import java.util.regex.PatternSyntaxException; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ActualEnvironmentException; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.repo.virtual.config.NodeRefExpression; +import org.alfresco.repo.virtual.ref.Protocols; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.QNamePattern; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Content type and aspect based virtualization strategy.
+ * Virtualizes nodes by associating their types or aspects with a template.
+ * A type or an aspect {@link QName} prefixed string form is associated with a + * vanilla template that has the same name as the type or aspect prefixed name + * with ':' replaced by '_' and it is .json postfixed. The template + * is located at predefined templates repository path.
+ * Example:
+ * If the templates repository path is + * Data Dictionary/Virtual Folders the cm:author + * aspect will be associated with a vanilla template found by the + * Data Dictionary/Virtual Folders/cm_author.json path.
+ * Considering all aspects for vitualization can degrade performance so the set + * of aspects considered for virtualization can be limited to a predefined + * accepted {@link QName} prefix set by setting a comma separated list of + * accepted prefixes through {@link #setAspectPrefixFilter(String)}. + * + * @author Bogdan Horje + */ +public class TypeVirtualizationMethod extends TemplateVirtualizationMethod +{ + private static Log logger = LogFactory.getLog(TypeVirtualizationMethod.class); + + private NamespacePrefixResolver namespacePrefixResolver; + + private NodeRefExpression templatesPath; + + private QNamePattern qnamePattern = RegexQNamePattern.MATCH_ALL; + + /** + * Thread local solving template in process indicator.
+ * Used to prevent infinite recursion when solving templates within type + * virtualized folders. + */ + private ThreadLocal solvingTemplate = new ThreadLocal() + { + protected Boolean initialValue() + { + return false; + }; + }; + + public void init() + { + //void + } + + public void setQnameFilterRegexp(String regexp) + { + this.qnamePattern = new RegexQNamePattern(regexp); + } + + public void setTemplatesPath(NodeRefExpression templatesPath) + { + this.templatesPath = templatesPath; + } + + public void setNamespacePrefixResolver(NamespacePrefixResolver resolver) + { + this.namespacePrefixResolver = resolver; + } + + @Override + public boolean canVirtualize(ActualEnvironment env, NodeRef nodeRef) throws ActualEnvironmentException + { + if (solvingTemplate.get()) + { + return false; + } + + solvingTemplate.set(true); + try + { + return templateNodeFor(env, + nodeRef) != null; + } + finally + { + solvingTemplate.set(false); + } + + } + + private String templateNodeNameForType(QName type) + { + String extension = ".json"; + String typePrefixString = type.toPrefixString(namespacePrefixResolver); + String typeTemplateContentName = typePrefixString.replaceAll(":", + "_"); + return typeTemplateContentName + extension; + } + + private NodeRef templateNodeFor(ActualEnvironment env, NodeRef nodeRef) + { + try + { + NodeRef templatesContainerNode = templatesPath.resolve(); + if (templatesContainerNode == null) + { + return null; + } + NodeRef templateNode = null; + QName nodeType = env.getType(nodeRef); + if (qnamePattern.isMatch(nodeType)) + { + String typeTemplateNodeName = templateNodeNameForType(nodeType); + + templateNode = env.getChildByName(templatesContainerNode, + ContentModel.ASSOC_CONTAINS, + typeTemplateNodeName); + } + + if (templateNode == null) + { + Set aspects = env.getAspects(nodeRef); + for (QName aspect : aspects) + { + + if (qnamePattern.isMatch(aspect)) + { + String aspectTemplateNodeName = templateNodeNameForType(aspect); + templateNode = env.getChildByName(templatesContainerNode, + ContentModel.ASSOC_CONTAINS, + aspectTemplateNodeName); + + if (templateNode != null) + { + break; + } + } + + } + } + + return templateNode; + } + catch (PatternSyntaxException e) + { + logger.error("Invalid type methof type and aspect qName regexp pattern " + qnamePattern.toString()); + return null; + } + catch (Exception e) + { + logger.error("Type virtualization template search failed.", + e); + return null; + } + } + + @Override + public Reference virtualize(ActualEnvironment env, NodeRef nodeRef) throws VirtualizationException + { + if (solvingTemplate.get()) + { + throw new VirtualizationException("Concurrent virtualization!"); + } + + try + { + solvingTemplate.set(true); + + NodeRef templateNode = templateNodeFor(env, + nodeRef); + return newVirtualReference(Protocols.VANILLA.protocol, + templateNode, + nodeRef); + } + catch (VirtualizationException e) + { + throw e; + } + catch (Exception e) + { + throw new VirtualizationException(e); + } + finally + { + solvingTemplate.set(false); + } + } +} diff --git a/source/java/org/alfresco/repo/virtual/store/VirtualFolderDefinitionResolver.java b/source/java/org/alfresco/repo/virtual/store/VirtualFolderDefinitionResolver.java new file mode 100644 index 0000000000..3ee7a159f4 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/store/VirtualFolderDefinitionResolver.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.store; + +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.template.VirtualFolderDefinition; + +/** + * A {@link Reference} based {@link VirtualFolderDefinition} resolver.
+ * + * @author Bogdan Horje + */ +public interface VirtualFolderDefinitionResolver +{ + /** + * @param reference + * @return the {@link VirtualFolderDefinition} of the given + * {@link Reference} considering inner paths + * @throws VirtualizationException + */ + VirtualFolderDefinition resolveVirtualFolderDefinition(Reference reference) throws VirtualizationException; +} diff --git a/source/java/org/alfresco/repo/virtual/store/VirtualStore.java b/source/java/org/alfresco/repo/virtual/store/VirtualStore.java new file mode 100644 index 0000000000..fba8abbc00 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/store/VirtualStore.java @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.store; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.security.permissions.NodePermissionEntry; +import org.alfresco.repo.security.permissions.PermissionReference; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.template.FilingData; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.QNamePattern; +import org.alfresco.util.Pair; + +/** + * Interface for public and internal reference operations. + *

+ * Handles most virtualized {@link Reference} meta-data interactions analogous + * to actual Alfresco public Repository Services. + * + * @see ServiceRegistry The Public Repository Services + * @author Bogdan Horje + */ +public interface VirtualStore +{ + static final int MATERIAL_ADHERENCE=1; + + static final int FILING_OR_MATERIAL_ADHERENCE=2; + + Collection materializeIfPossible(Collection nodeRefs) throws VirtualizationException; + + NodeRef materializeIfPossible(NodeRef nodeRef) throws VirtualizationException; + + boolean isVirtual(NodeRef nodeRef) throws VirtualizationException; + + boolean canVirtualize(NodeRef nodeRef) throws VirtualizationException; + + Reference virtualize(NodeRef nodeRef) throws VirtualizationException; + + NodeRef materialize(Reference reference) throws VirtualizationException; + + NodeRef adhere(Reference reference,int mode) throws VirtualizationException; + + boolean canMaterialize(Reference reference) throws VirtualizationException; + + Path getPath(Reference reference) throws VirtualizationException; + + /** + * @param reference + * @return all virtual properties of the referred virtualized artefact keyed + * by their qualified name + * @throws VirtualizationException + */ + Map getProperties(Reference reference) throws VirtualizationException; + + /** + * Get the reference of the virtualized artefact with the given name within + * the virtual context (only) of the parent reference.
+ * The name is case-insensitive as Alfresco has to support case-insensitive + * clients as standard.
+ * + * @param parentReference parent {@link Reference} + * @param assocTypeQName + * @param childName + * @return the virtual child reference for the given name in the context of + * the given parent reference + * @throws VirtualizationException + * @see NodeService#getChildByName(org.alfresco.service.cmr.repository.NodeRef, + * QName, String) + */ + Reference getChildByName(Reference parentReference, QName assocTypeQName, String childName) + throws VirtualizationException; + + /** + * Retrieve immediate children references of a given reference where the + * child nodes are in the given inclusive list. + * + * @param parentReference the parent node - usually a container + * @param childNodeTypeQNames the types that the children may be. Subtypes + * are not automatically calculated and the list must therefore + * be exhaustive. + * @return Returns a list of ChildAssociationRef instances. + */ + List getChildAssocs(Reference parentReference, Set childNodeTypeQNames); + + /** + * Gets all child references associations where the pattern of the + * association qualified name is an exact match. + * + * @param parentReference the parent node - usually a container + * @param typeQNamePattern the qualified name of the association ( + * null to ignore) + * @param qnamePattern the path qualified name (null to ignore) + * @param maxResults the number of results to get + * @param preload true if the nodes must be preloaded into the + * cache + * @return Returns a list of ChildAssociationRef instances + * @throws InvalidNodeRefException if the node could not be found + * @see QName + */ + List getChildAssocs(Reference parentReference, final QNamePattern typeQNamePattern, + final QNamePattern qnamePattern, final int maxResults, final boolean preload) + throws InvalidNodeRefException; + + /** + * Retrieve the immediate children of a given node based on the value of a + * property of those children. + *

+ * If the property to be searched is multi-valued then will match on any one + * values. + *

+ * Please note, the following system maintained properties that cannot be + * used with this method. + *

    + *
  • cm:name - use getChildByName instead
  • + *
  • cm:created
  • + *
  • cm:creator
  • + *
  • cm:modified
  • + *
  • cm:modifier
  • + *
  • sys:node-uuid
  • + *
  • sys:node-dbid
  • + *
  • sys:store-identifier
  • + *
  • sys:store-protocol
  • + *
      + * + * @param parentReference the parent reference - usually a container + * @param propertyQName the fully qualified name of the property + * @param value the value to search for. Must be a simple type such as + * String, Number, Date or Boolean, it cannot be a collection, a + * content property, MLText or a float. + * @return Returns a list of ChildAssociationRef instances. + */ + List getChildAssocsByPropertyValue(Reference parentReference, QName propertyQName, + Serializable value); + + /** + * Gets the set of child associations of a certain parent node without + * parent associations of a certain type to other nodes with the same + * parent! In effect the 'orphans' with respect to a certain association + * type. + * + * @param parent the parent reference + * @param assocTypeQName the association type QName + * @return a {@link Collection} of child associations + */ + Collection getChildAssocsWithoutParentAssocsOfType(Reference parentReference, + QName assocTypeQName); + + /** + * Lists page of immediate children of the referred virtualized artefact + * with optional filtering (exclusion of certain child file/folder subtypes, + * actual-virtual filtering) and sorting.
      + * Pattern uses '*' as a wildcard + * + * @param ref + * @param actual + * @param virtual + * @param files + * @param folders + * @param pattern + * @param ignoreQNames + * @param searchTypeQNames + * @param ignoreAspectQNames + * @param sortProps + * @param pagingRequest + * @throws VirtualizationException + */ + PagingResults list(Reference ref, boolean actual, boolean virtual, final boolean files, + final boolean folders, final String pattern, final Set searchTypeQNames, + final Set ignoreTypeQNames, final Set ignoreAspectQNames, + final List> sortProps, final PagingRequest pagingRequest) + throws VirtualizationException; + + /** + * Lists page of immediate children of the referred virtualized artefact + * with optional filtering (exclusion of certain child file/folder subtypes, + * actual-virtual filtering) and sorting.
      + * + * @param ref + * @param actual + * @param virtual + * @param files + * @param folders + * @param pattern + * @param ignoreQNames + * @param sortProps + * @param pagingRequest + * @throws VirtualizationException + */ + PagingResults list(Reference ref, boolean actual, boolean virtual, boolean files, boolean folders, + String pattern, Set ignoreTypeQNames, Set ignoreAspectQNames, + List> sortProps, PagingRequest pagingRequest) throws VirtualizationException; + + /** + * Lists page of immediate children of the referred virtualized artefact + * with optional filtering (exclusion of certain child file/folder subtypes, + * actual-virtual filtering) and sorting.
      + * + * @throws VirtualizationException + */ + PagingResults list(Reference ref, boolean actual, boolean virtual, Set searchTypeQNames, + Set ignoreTypeQNames, Set ignoreAspectQNames, List> sortProps, + PagingRequest pagingRequest) throws VirtualizationException; + + /** + * Lists all immediate children of the referred virtualized artefact.
      + * Note: this could be a long list (and will be trimmed at a pre-configured + * maximum). You should consider using a paging request. + * + * @param reference + * @throws VirtualizationException + */ + List list(Reference reference) throws VirtualizationException; + + /** + * Perform a search against the name of the files or folders within a + * virtualized artefact {@link Reference} hierarchy.
      + * Wildcard characters are * and ?.
      + * Warning: Please avoid using this method with any "namePattern" other than + * "*". Although it works, its performance is poor which is why this method + * is deprecated. + * + * @param reference + * @param namePattern + * @param fileSearch + * @param folderSearch + * @param includeSubFolders + * @throws VirtualizationException + * @deprecated {@link FileFolderService#search(NodeRef, String, boolean, boolean, boolean)} + * alignment : for shallow search use list, listFolders, + * listFiles, searchSimple. For deep listing use + * listDeepFolders. Avoid calling this method with any name + * pattern except for "*". + */ + List search(Reference reference, String namePattern, boolean fileSearch, boolean folderSearch, + boolean includeSubFolders) throws VirtualizationException; + + /** + * @param reference + * @return qualified type of the referred virtualized artefact + * @throws VirtualizationException + */ + QName getType(Reference reference) throws VirtualizationException; + + /** + * @param reference + * @param assocTypeQName + * @param assocQName + * @param nodeTypeQName + * @param properties + * @return {@link FilingData} of the given parent location + * @throws VirtualizationException + */ + FilingData createFilingData(Reference reference, QName assocTypeQName, QName assocQName, QName nodeTypeQName, + Map properties) throws VirtualizationException; + + /** + * Check that the given authentication has a particular permission for the + * given virtualized artefact. (The default behaviour is to inherit + * permissions) + * + * @param reference + * @param perm + * @return an {@link AccessStatus} + * @throws VirtualizationException + */ + AccessStatus hasPermission(Reference reference, String perm) throws VirtualizationException; + + /** + * Check that the given authentication has a particular permission for the + * given virtualized artefact. (The default behaviour is to inherit + * permissions) + * + * @param reference + * @param perm + * @return an {@link AccessStatus} + * @throws VirtualizationException + */ + AccessStatus hasPermission(Reference reference, PermissionReference perm) throws VirtualizationException; + + NodePermissionEntry getSetPermissions(Reference reference) throws VirtualizationException; + + Set getAllSetPermissions(Reference reference); +} diff --git a/source/java/org/alfresco/repo/virtual/store/VirtualStoreImpl.java b/source/java/org/alfresco/repo/virtual/store/VirtualStoreImpl.java new file mode 100644 index 0000000000..91834c6208 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/store/VirtualStoreImpl.java @@ -0,0 +1,806 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.store; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.query.ListBackedPagingResults; +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.NodePermissionEntry; +import org.alfresco.repo.security.permissions.PermissionReference; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.VirtualContentModel; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.repo.virtual.page.PageCollationException; +import org.alfresco.repo.virtual.page.PageCollator; +import org.alfresco.repo.virtual.page.PageCollator.PagingResultsSource; +import org.alfresco.repo.virtual.ref.GetActualNodeRefMethod; +import org.alfresco.repo.virtual.ref.GetChildByIdMethod; +import org.alfresco.repo.virtual.ref.GetParentReferenceMethod; +import org.alfresco.repo.virtual.ref.GetReferenceType; +import org.alfresco.repo.virtual.ref.GetTemplatePathMethod; +import org.alfresco.repo.virtual.ref.Protocol; +import org.alfresco.repo.virtual.ref.ProtocolMethodException; +import org.alfresco.repo.virtual.ref.Protocols; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.ref.ReferenceEncodingException; +import org.alfresco.repo.virtual.ref.ReferenceParseException; +import org.alfresco.repo.virtual.template.ApplyTemplateMethod; +import org.alfresco.repo.virtual.template.BasicConstraint; +import org.alfresco.repo.virtual.template.FilesFoldersConstraint; +import org.alfresco.repo.virtual.template.FilingData; +import org.alfresco.repo.virtual.template.FilingParameters; +import org.alfresco.repo.virtual.template.FilingRule; +import org.alfresco.repo.virtual.template.PropertyValueConstraint; +import org.alfresco.repo.virtual.template.NullFilingRule; +import org.alfresco.repo.virtual.template.VirtualFolderDefinition; +import org.alfresco.repo.virtual.template.VirtualQuery; +import org.alfresco.repo.virtual.template.VirtualQueryConstraint; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.repository.Path.ChildAssocElement; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.QNamePattern; +import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.util.Pair; + +public class VirtualStoreImpl implements VirtualStore, VirtualFolderDefinitionResolver +{ + + private List virtualizationMethods = null; + + private ActualEnvironment environment; + + /** User permissions */ + private VirtualUserPermissions userPermissions; + + public void setVirtualizationMethods(List methdods) + { + this.virtualizationMethods = methdods; + } + + public void setEnvironment(ActualEnvironment environment) + { + this.environment = environment; + } + + @Override + public boolean isVirtual(NodeRef nodeRef) throws VirtualizationException + { + if (Reference.isReference(nodeRef)) + { + Protocol protocol = Reference.fromNodeRef(nodeRef).getProtocol(); + return Protocols.VIRTUAL.protocol.equals(protocol) || Protocols.VANILLA.protocol.equals(protocol); + } + else + { + return false; + } + } + + @Override + public boolean canVirtualize(NodeRef nodeRef) throws VirtualizationException + { + if (Reference.isReference(nodeRef)) + { + return true; + } + else + { + for (VirtualizationMethod vMethod : virtualizationMethods) + { + if (vMethod.canVirtualize(environment, + nodeRef)) + { + return true; + } + } + + return false; + } + } + + @Override + public boolean canMaterialize(Reference reference) throws VirtualizationException + { + // TODO: extract a protocol method, push into virtualization methods + + if (Protocols.NODE.protocol.equals(reference.getProtocol())) + { + return true; + } + else + { + String templatePath = reference.execute(new GetTemplatePathMethod()); + return templatePath.trim().equals("/"); + } + } + + private NodeRef nodeProtocolNodeRef(NodeRef nodeRef) throws ProtocolMethodException, ReferenceParseException, + ReferenceEncodingException + { + NodeRef theNodeRef = nodeRef; + if (Reference.isReference(nodeRef)) + { + Reference ref = Reference.fromNodeRef(theNodeRef); + if (Protocols.NODE.protocol.equals(ref.getProtocol())) + { + theNodeRef = ref.execute(new GetActualNodeRefMethod(environment)); + } + } + return theNodeRef; + } + + @Override + public Reference virtualize(NodeRef nodeRef) throws VirtualizationException + { + Reference reference = null; + + if (isVirtual(nodeRef)) + { + reference = Reference.fromNodeRef(nodeRef); + } + else + { + NodeRef theNodeRef = nodeProtocolNodeRef(nodeRef); + + for (VirtualizationMethod vMethod : virtualizationMethods) + { + if (vMethod.canVirtualize(environment, + theNodeRef)) + { + reference = vMethod.virtualize(environment, + theNodeRef); + } + } + + if (reference == null) + { + if (Reference.isReference(nodeRef)) + { + reference = Reference.fromNodeRef(nodeRef); + } + else + { + throw new VirtualizationException("No virtualization method for " + nodeRef); + } + } + + } + + return reference; + } + + @Override + public NodeRef materialize(Reference reference) throws VirtualizationException + { + return reference.execute(new GetActualNodeRefMethod(environment)); + } + + @Override + public Collection materializeIfPossible(Collection nodeRefs) throws VirtualizationException + { + List nodeRefList = new LinkedList<>(); + for (NodeRef nodeRef : nodeRefs) + { + nodeRefList.add(materializeIfPossible(nodeRef)); + } + + return nodeRefList; + } + + @Override + public NodeRef materializeIfPossible(NodeRef nodeRef) throws VirtualizationException + { + if (Reference.isReference(nodeRef)) + { + Reference ref = Reference.fromNodeRef(nodeRef); + if (canMaterialize(ref)) + { + return materialize(ref); + } + + } + return nodeRef; + } + + @Override + public List getChildAssocs(Reference parentReference, QNamePattern typeQNamePattern, + QNamePattern qnamePattern, int maxResults, boolean preload) throws InvalidNodeRefException + { + if (typeQNamePattern.isMatch(ContentModel.ASSOC_CONTAINS)) + { + return parentReference.execute(new GetChildAssocsMethod(this, + environment, + preload, + maxResults, + qnamePattern, + typeQNamePattern)); + } + else + { + return Collections.emptyList(); + } + } + + @Override + public List getChildAssocs(Reference parentReference, Set childNodeTypeQNames) + { + List allAssociations = getChildAssocs(parentReference, + RegexQNamePattern.MATCH_ALL, + RegexQNamePattern.MATCH_ALL, + Integer.MAX_VALUE, + false); + + List associations = new LinkedList<>(); + + for (ChildAssociationRef childAssociationRef : allAssociations) + { + QName childType = environment.getType(childAssociationRef.getChildRef()); + if (childNodeTypeQNames.contains(childType)) + { + associations.add(childAssociationRef); + } + } + + return associations; + } + + @Override + public Collection getChildAssocsWithoutParentAssocsOfType(Reference parentReference, + QName assocTypeQName) + { + return Collections.emptyList(); + } + + @Override + public List getChildAssocsByPropertyValue(Reference parentReference, QName propertyQName, + Serializable value) + { + List allAssociations = getChildAssocs(parentReference, + RegexQNamePattern.MATCH_ALL, + RegexQNamePattern.MATCH_ALL, + Integer.MAX_VALUE, + false); + + List associations = new LinkedList<>(); + + for (ChildAssociationRef childAssociationRef : allAssociations) + { + Serializable propertyValue = environment.getProperty(childAssociationRef.getChildRef(), + propertyQName); + + if ((value == null && propertyValue == null) || (value != null && value.equals(propertyValue))) + { + associations.add(childAssociationRef); + } + } + + return associations; + } + + @Override + public Reference getChildByName(Reference reference, QName assocTypeQName, String childName) + throws VirtualizationException + { + VirtualFolderDefinition structure = resolveVirtualFolderDefinition(reference); + VirtualFolderDefinition theChild = structure.findChildByName(childName); + + if (theChild != null) + { + return reference.execute(new GetChildByIdMethod(theChild.getId())); + } + else + { + final VirtualQuery query = structure.getQuery(); + + if (query != null) + { + PropertyValueConstraint constraint = new PropertyValueConstraint(new FilesFoldersConstraint(BasicConstraint.INSTANCE, + true, + true), + ContentModel.PROP_NAME, + childName, + environment + .getNamespacePrefixResolver()); + PagingResults result = query.perform(environment, + constraint, + null, + reference); + List page = result.getPage(); + + return page == null || page.isEmpty() ? null : page.get(0); + } + else + { + return null; + } + } + } + + private List createChildReferences(Reference parent, VirtualFolderDefinition structure) + throws ProtocolMethodException + { + + List structureChildren = structure.getChildren(); + List childReferences = new LinkedList(); + + for (VirtualFolderDefinition child : structureChildren) + { + childReferences.add(parent.execute(new GetChildByIdMethod(child.getId()))); + } + + return childReferences; + } + + public VirtualFolderDefinition resolveVirtualFolderDefinition(Reference reference) throws ProtocolMethodException + { + return reference.execute(new ApplyTemplateMethod(environment)); + } + + @Override + public PagingResults list(final Reference ref, boolean actual, boolean virtual, final boolean files, + final boolean folders, final String pattern, final Set searchTypeQNames, + final Set ignoreTypeQNames, final Set ignoreAspectQNames, + final List> sortProps, final PagingRequest pagingRequest) + throws VirtualizationException + { + + VirtualFolderDefinition structure = resolveVirtualFolderDefinition(ref); + + List virtualRefs = null; + // TODO: this blocks file exclusive virtual listing - complex query + // constraints should handle folder & file filtering + if (virtual && folders) + { + virtualRefs = createChildReferences(ref, + structure); + } + else + { + virtualRefs = Collections.emptyList(); + } + + if (actual) + { + final VirtualQuery query = structure.getQuery(); + if (query != null) + { + // we have a query we must collate results + + PagingResultsSource querySourc = new PagingResultsSource() + { + + @Override + public PagingResults retrieve(PagingRequest pr) throws PageCollationException + { + try + { + return query.perform(environment, + files, + folders, + pattern, + searchTypeQNames, + ignoreTypeQNames, + ignoreAspectQNames, + sortProps, + pr, + ref); + } + catch (VirtualizationException e) + { + throw new PageCollationException(e); + } + } + + }; + + PageCollator collator = new PageCollator<>(); + + try + { + return collator.collate(virtualRefs, + querySourc, + pagingRequest, + new ReferenceComparator(this, + sortProps)); + } + catch (PageCollationException e) + { + throw new VirtualizationException(e); + } + } + + } + + return new ListBackedPagingResults<>(virtualRefs); + } + + @Override + public PagingResults list(Reference ref, boolean actual, boolean virtual, boolean files, + boolean folders, String pattern, Set ignoreTypeQNames, Set ignoreAspectQNames, + List> sortProps, PagingRequest pagingRequest) throws VirtualizationException + { + return list(ref, + actual, + virtual, + files, + folders, + pattern, + Collections. emptySet(), + ignoreTypeQNames, + ignoreAspectQNames, + sortProps, + pagingRequest); + } + + @Override + public PagingResults list(Reference ref, boolean actual, boolean virtual, Set searchTypeQNames, + Set ignoreTypeQNames, Set ignoreAspectQNames, List> sortProps, + PagingRequest pagingRequest) throws VirtualizationException + { + // TODO: find null string value for pattern + return list(ref, + actual, + virtual, + true, + true, + null, + Collections. emptySet(), + searchTypeQNames, + ignoreAspectQNames, + sortProps, + pagingRequest); + } + + @Override + public List list(Reference reference) throws VirtualizationException + { + VirtualFolderDefinition structure = resolveVirtualFolderDefinition(reference); + List result = createChildReferences(reference, + structure); + final VirtualQuery query = structure.getQuery(); + if (query != null) + { + PagingResults queryNodes = query.perform(environment, + new FilesFoldersConstraint(BasicConstraint.INSTANCE, + true, + true), + null, + reference); + result.addAll(queryNodes.getPage()); + + } + + return result; + } + + @Override + public List search(Reference reference, String namePattern, boolean fileSearch, boolean folderSearch, + boolean includeSubFolders) throws VirtualizationException + { + VirtualFolderDefinition structure = resolveVirtualFolderDefinition(reference); + List result = new LinkedList(); + List childReferences = createChildReferences(reference, + structure); + if (folderSearch) + { + result.addAll(childReferences); + } + if (includeSubFolders) + { + for (Reference childRef : childReferences) + { + List childResults = search(childRef, + namePattern, + fileSearch, + folderSearch, + includeSubFolders); + result.addAll(childResults); + } + } + if (fileSearch) + { + final VirtualQuery query = structure.getQuery(); + if (query != null) + { + VirtualQueryConstraint vqConstraint = null; + if (namePattern == null) + { + vqConstraint = BasicConstraint.INSTANCE; + } + else + { + vqConstraint = new PropertyValueConstraint(new FilesFoldersConstraint(BasicConstraint.INSTANCE, + true, + true), + ContentModel.PROP_NAME, + namePattern, + environment.getNamespacePrefixResolver()); + } + PagingResults queryNodes = query.perform(environment, + vqConstraint, + null, + reference); + result.addAll(queryNodes.getPage()); + } + } + return result; + } + + @Override + public Map getProperties(Reference reference) throws VirtualizationException + { + final Protocol protocol = reference.getProtocol(); + if (Protocols.VIRTUAL.protocol.equals(protocol) || Protocols.VANILLA.protocol.equals(protocol)) + { + + VirtualFolderDefinition folderDefinition = resolveVirtualFolderDefinition(reference); + + Map properties = new HashMap(); + + // We first set default property values. They might be overridden by + // folder definition properties. + + properties.put(ContentModel.PROP_NAME, + folderDefinition.getName()); + + StoreRef storeRef = StoreRef.STORE_REF_WORKSPACE_SPACESSTORE; + + properties.put(ContentModel.PROP_STORE_IDENTIFIER, + storeRef.getIdentifier()); + + properties.put(ContentModel.PROP_STORE_PROTOCOL, + storeRef.getProtocol()); + + properties.put(ContentModel.PROP_LOCALE, + Locale.UK.toString()); + + properties.put(ContentModel.PROP_TITLE, + folderDefinition.getName()); + + properties.put(ContentModel.PROP_MODIFIED, + new Date()); + properties.put(ContentModel.PROP_MODIFIER, + AuthenticationUtil.SYSTEM_USER_NAME); + properties.put(ContentModel.PROP_CREATED, + new Date()); + properties.put(ContentModel.PROP_CREATOR, + AuthenticationUtil.SYSTEM_USER_NAME); + + properties.put(ContentModel.PROP_NODE_DBID, + 0); + + properties.put(ContentModel.PROP_DESCRIPTION, + folderDefinition.getDescription()); + + // We add virtual folder definition structure properties. They might + // override the above defaults. + + Map nodeProperties = folderDefinition.getProperties(); + + if (nodeProperties != null) + { + Set> propertyEntries = nodeProperties.entrySet(); + NamespacePrefixResolver nsPrefixResolver = environment.getNamespacePrefixResolver(); + + for (Entry propertyValueEntry : propertyEntries) + { + QName propertyQName = QName.createQName(propertyValueEntry.getKey(), + nsPrefixResolver); + properties.put(propertyQName, + propertyValueEntry.getValue().toString()); + } + } + + return properties; + } + else + { + NodeRef actual = reference.execute(new GetActualNodeRefMethod(environment)); + return environment.getProperties(actual); + + } + } + + @Override + public QName getType(Reference ref) throws VirtualizationException + { + return ref.execute(new GetReferenceType(environment)); + } + + @Override + public FilingData createFilingData(Reference parentReference, QName assocTypeQName, QName assocQName, + QName nodeTypeQName, Map properties) throws VirtualizationException + { + VirtualFolderDefinition structure = resolveVirtualFolderDefinition(parentReference); + FilingRule filingRule = structure.getFilingRule(); + if (filingRule == null) + { + filingRule = new NullFilingRule(environment); + } + + FilingParameters filingParameters = new FilingParameters(parentReference, + assocTypeQName, + assocQName, + nodeTypeQName, + properties); + FilingData filingData = filingRule.createFilingData(filingParameters); + + return filingData; + } + + @Override + public AccessStatus hasPermission(Reference reference, String perm) throws VirtualizationException + { + return reference.execute(new HasPermissionMethod(this, + userPermissions, + perm)); + } + + @Override + public AccessStatus hasPermission(Reference reference, PermissionReference perm) throws VirtualizationException + { + return hasPermission(reference, + perm.getName()); + } + + /** + * @param userPermissions user permissions + */ + public void setUserPermissions(VirtualUserPermissions userPermissions) + { + this.userPermissions = userPermissions; + } + + /** + * @return a {@link VirtualUserPermissions} clone + */ + public VirtualUserPermissions getUserPermissions() + { + return new VirtualUserPermissions(this.userPermissions); + } + + @Override + public NodePermissionEntry getSetPermissions(Reference reference) throws VirtualizationException + { + return reference.execute(new GetSetPermissionsMethod(this, + userPermissions, + PermissionService.ALL_AUTHORITIES)); + } + + @Override + public Set getAllSetPermissions(Reference reference) + { + return reference.execute(new GetAllSetPermissionsMethod(this, + userPermissions, + PermissionService.ALL_AUTHORITIES)); + } + + @Override + public Path getPath(Reference reference) throws VirtualizationException + { + Reference virtualPathElement = reference; + Reference virtualPathParent = reference.execute(new GetParentReferenceMethod()); + + Path virtualPath = new Path(); + + while (virtualPathElement != null && virtualPathParent != null) + { + NodeRef parentNodeRef; + + parentNodeRef = virtualPathParent.toNodeRef(); + + NodeRef parent = parentNodeRef; + + NodeRef virtualPathNodeRef = virtualPathElement.toNodeRef(); + + // TODO: extract node reference name into protocol method in order + // to enforce path processing code reuse and consistency + + String templatePath = virtualPathElement.execute(new GetTemplatePathMethod()).trim(); + final String pathSeparator = "/"; + if (pathSeparator.equals(templatePath)) + { + // found root + break; + } + else if (templatePath.endsWith(pathSeparator)) + { + templatePath = templatePath.substring(0, + templatePath.length() - 1); + } + int lastSeparator = templatePath.lastIndexOf(pathSeparator); + String childId = templatePath.substring(lastSeparator + 1); + VirtualFolderDefinition structure = resolveVirtualFolderDefinition(virtualPathParent); + VirtualFolderDefinition child = structure.findChildById(childId); + if (child == null) + { + throw new VirtualizationException("Invalid reference: " + reference.encode()); + } + String childName = child.getName(); + QName childQName = QName.createQName(VirtualContentModel.VIRTUAL_CONTENT_MODEL_1_0_URI, + childName); + + ChildAssociationRef assocRef = new ChildAssociationRef(ContentModel.ASSOC_CHILDREN, + parent, + childQName, + virtualPathNodeRef, + true, + -1); + ChildAssocElement assocRefElement = new ChildAssocElement(assocRef); + virtualPath.prepend(assocRefElement); + + virtualPathElement = virtualPathParent; + virtualPathParent = virtualPathParent.execute(new GetParentReferenceMethod()); + } + + return virtualPath; + } + + @Override + public NodeRef adhere(Reference reference, int mode) throws VirtualizationException + { + switch (mode) + { + case MATERIAL_ADHERENCE: + { + return materialize(reference); + } + case FILING_OR_MATERIAL_ADHERENCE: + { + VirtualFolderDefinition vfDefinition = resolveVirtualFolderDefinition(reference); + FilingRule filingRule = vfDefinition.getFilingRule(); + if (filingRule.isNullFilingRule()) + { + return materialize(reference); + } + else + { + return filingRule.filingNodeRefFor(new FilingParameters(reference)); + } + } + + default: + throw new VirtualizationException("Invalid adherence mode " + mode); + } + } + +} diff --git a/source/java/org/alfresco/repo/virtual/store/VirtualUserPermissions.java b/source/java/org/alfresco/repo/virtual/store/VirtualUserPermissions.java new file mode 100644 index 0000000000..c7eaa0184a --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/store/VirtualUserPermissions.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.store; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.namespace.QName; + +/** + * Configuration bean for virtual references overridden permissions. + * + * @author Dinuta Silviu + */ +public class VirtualUserPermissions +{ + private Set allowVirtualNodes = Collections.emptySet(); + + private Set allowVirtualNodesFull = Collections.emptySet(); + + private Set denyVirtualNodes = Collections.emptySet(); + + private Set denyVirtualNodesFull = Collections.emptySet(); + + private Set denyReadonlyVirtualNodes = Collections.emptySet(); + + private Set denyReadonlyVirtualNodesFull = Collections.emptySet(); + + private Set allowQueryNodes = Collections.emptySet(); + + private Set allowQueryNodesFull = Collections.emptySet(); + + private Set denyQueryNodes = Collections.emptySet(); + + private Set denyQueryNodesFull = Collections.emptySet(); + + private QName permissionTypeQName = ContentModel.TYPE_BASE; + + public VirtualUserPermissions() + { + + } + + public VirtualUserPermissions(VirtualUserPermissions userPermissions) + { + this.allowVirtualNodes = new HashSet<>(userPermissions.allowVirtualNodes); + this.denyVirtualNodes = new HashSet<>(userPermissions.denyVirtualNodes); + this.allowQueryNodes = new HashSet<>(userPermissions.allowQueryNodes); + this.denyQueryNodes = new HashSet<>(userPermissions.denyQueryNodes); + this.denyReadonlyVirtualNodes = new HashSet<>(userPermissions.denyReadonlyVirtualNodes); + init(); + } + + private Set asFullNamePermissions(Set permissions) + { + Set fullPermissions = new HashSet(); + for (String s : permissions) + { + fullPermissions.add(permissionTypeQName + "." + s); + } + + return fullPermissions; + } + + public void init() + { + this.allowQueryNodesFull = asFullNamePermissions(allowQueryNodes); + this.allowVirtualNodesFull = asFullNamePermissions(allowVirtualNodes); + this.denyQueryNodesFull = asFullNamePermissions(denyQueryNodes); + this.denyVirtualNodesFull = asFullNamePermissions(denyVirtualNodes); + this.denyReadonlyVirtualNodesFull = asFullNamePermissions(denyReadonlyVirtualNodes); + } + + public QName getPermissionTypeQName() + { + return this.permissionTypeQName; + } + + public AccessStatus hasVirtualNodePermission(String permission, boolean readonly) + { + if (readonly) + { + if (denyReadonlyVirtualNodesFull.contains(permission) || denyReadonlyVirtualNodes.contains(permission)) + { + return AccessStatus.DENIED; + } + } + + if (denyVirtualNodesFull.contains(permission) || denyVirtualNodes.contains(permission)) + { + return AccessStatus.DENIED; + } + else if (allowVirtualNodesFull.contains(permission) || allowVirtualNodes.contains(permission)) + { + return AccessStatus.ALLOWED; + } + else + { + return AccessStatus.UNDETERMINED; + } + } + + public AccessStatus hasQueryNodePermission(String permission) + { + if (denyQueryNodesFull.contains(permission) || denyQueryNodes.contains(permission)) + { + return AccessStatus.DENIED; + } + else if (allowQueryNodesFull.contains(permission) || allowQueryNodes.contains(permission)) + { + return AccessStatus.ALLOWED; + } + else + { + return AccessStatus.UNDETERMINED; + } + } + + public Set getAllowVirtualNodes() + { + return this.allowVirtualNodes; + } + + public void setAllowVirtualNodes(Set allowFolders) + { + this.allowVirtualNodes = allowFolders; + } + + public Set getDenyVirtualNodes() + { + return this.denyVirtualNodes; + } + + public void setDenyVirtualNodes(Set denyFolders) + { + this.denyVirtualNodes = denyFolders; + } + + public void setDenyReadonlyVirtualNodes(Set denyReadonlyVirtualNodes) + { + this.denyReadonlyVirtualNodes = denyReadonlyVirtualNodes; + } + + public Set getDenyReadonlyVirtualNodes() + { + return this.denyReadonlyVirtualNodes; + } + + public Set getAllowQueryNodes() + { + return this.allowQueryNodes; + } + + public void setAllowQueryNodes(Set allowDocuments) + { + this.allowQueryNodes = allowDocuments; + } + + public Set getDenyQueryNodes() + { + return this.denyQueryNodes; + } + + public void setDenyQueryNodes(Set denyDocuments) + { + this.denyQueryNodes = denyDocuments; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/virtual/store/VirtualizationMethod.java b/source/java/org/alfresco/repo/virtual/store/VirtualizationMethod.java new file mode 100644 index 0000000000..83f07e3dbd --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/store/VirtualizationMethod.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.store; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Implementors define virtualization rules.
      + * Virtualization is the process of converting {@link NodeRef}s into + * {@link Reference}s in the context of given {@link ActualEnvironment} . + * + * @author Bogdan Horje + */ +public interface VirtualizationMethod +{ + /** + * Determines if a given {@link NodeRef} can be virtualized by this + * virtualization method. + * + * @param env the environment in which the virtualization should take place + * @param nodeRef the {@link NodeRef} that should be virtualized + * @return true if the given {@link NodeRef} can be virtualized + * by this virtualization method
      + * false otherwise + * @throws VirtualizationException + */ + boolean canVirtualize(ActualEnvironment env, NodeRef nodeRef) throws VirtualizationException; + + /** + * Applies this virtualizatio rule on a given {@link NodeRef}. + * + * @param env the environment in which the virtualization takes place + * @param nodeRef nodeRef the {@link NodeRef} that will be virtualized + * @return a {@link Reference} correspondent of the given {@link NodeRef} + * @throws VirtualizationException + */ + Reference virtualize(ActualEnvironment env, NodeRef nodeRef) throws VirtualizationException; +} diff --git a/source/java/org/alfresco/repo/virtual/template/ApplyTemplateMethod.java b/source/java/org/alfresco/repo/virtual/template/ApplyTemplateMethod.java new file mode 100644 index 0000000000..c554a53f08 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/ApplyTemplateMethod.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.VirtualContext; +import org.alfresco.repo.virtual.ref.AbstractProtocolMethod; +import org.alfresco.repo.virtual.ref.GetActualNodeRefMethod; +import org.alfresco.repo.virtual.ref.GetTemplatePathMethod; +import org.alfresco.repo.virtual.ref.GetVanillaScriptInputStreamMethod; +import org.alfresco.repo.virtual.ref.NodeProtocol; +import org.alfresco.repo.virtual.ref.ProtocolMethodException; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.repo.virtual.ref.Resource; +import org.alfresco.repo.virtual.ref.ResourceProcessingError; +import org.alfresco.repo.virtual.ref.VanillaProtocol; +import org.alfresco.repo.virtual.ref.VirtualProtocol; +import org.apache.commons.io.IOUtils; + +/** + * Creates a {@link VirtualFolderDefinition} by executing the template indicated + * by a virtualized entity reference. + * + * @author Bogdan Horje + */ +public class ApplyTemplateMethod extends AbstractProtocolMethod +{ + public static final String VANILLA_JSON_PARAM_NAME = "vanillaJSON"; + + private ActualEnvironment environment; + + public ApplyTemplateMethod(ActualEnvironment environment) + { + super(); + this.environment = environment; + } + + public VirtualFolderDefinition execute(VirtualProtocol virtualProtocol, Reference reference) + throws ProtocolMethodException + { + VirtualContext context = createVirtualContext(reference); + return execute(virtualProtocol, + reference, + context); + } + + private VirtualContext createVirtualContext(Reference reference) throws ProtocolMethodException + { + return new VirtualContext(environment, + reference.execute(new GetActualNodeRefMethod(environment))); + } + + public VirtualFolderDefinition execute(VirtualProtocol virtualProtocol, Reference reference, VirtualContext context) + throws ProtocolMethodException + { + Resource resource = reference.getResource(); + + try + { + VirtualFolderDefinition theStructure = resource.processWith(new TemplateResourceProcessor(context)); + String path = reference.execute(new GetTemplatePathMethod()); + + if (!path.isEmpty()) + { + String[] pathElements = path.split(PATH_SEPARATOR); + int startIndex = path.startsWith(PATH_SEPARATOR) ? 1 : 0; + for (int i = startIndex; i < pathElements.length; i++) + { + theStructure = theStructure.findChildById(pathElements[i]); + if (theStructure == null) + { + + throw new ProtocolMethodException("Invalid template path in " + reference.toString()); + + } + } + } + + return theStructure; + } + catch (ResourceProcessingError e) + { + throw new ProtocolMethodException(e); + } + } + + @Override + public VirtualFolderDefinition execute(VanillaProtocol vanillaProtocol, Reference reference) + throws ProtocolMethodException + { + InputStream vanillaIS = reference.execute(new GetVanillaScriptInputStreamMethod(environment)); + try + { + String vanillaJSON = IOUtils.toString(vanillaIS, + StandardCharsets.UTF_8); + VirtualContext context = createVirtualContext(reference); + context.setParameter(VANILLA_JSON_PARAM_NAME, + vanillaJSON); + return execute(vanillaProtocol, + reference, + context); + + } + catch (IOException e) + { + throw new ProtocolMethodException(e); + } + } + + /** + * Creates an empty {@link VirtualFolderDefinition} parameterized with a + * {@link NullFilingRule} as this method is called for non-virtual nodes. + * + * @param protocol + * @param reference + * @return The empty {@link VirtualFolderDefinition}. + */ + @Override + public VirtualFolderDefinition execute(NodeProtocol protocol, Reference reference) throws ProtocolMethodException + { + VirtualFolderDefinition virtualStructure = new VirtualFolderDefinition(); + virtualStructure.setFilingRule(new NodeFilingRule(environment)); + return virtualStructure; + } +} diff --git a/source/java/org/alfresco/repo/virtual/template/BasicConstraint.java b/source/java/org/alfresco/repo/virtual/template/BasicConstraint.java new file mode 100644 index 0000000000..80c7769ba8 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/BasicConstraint.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.QueryConsistency; +import org.alfresco.service.cmr.search.SearchParameters; + +public class BasicConstraint implements VirtualQueryConstraint +{ + public static final BasicConstraint INSTANCE = new BasicConstraint(); + + private BasicConstraint() + { + + } + + @Override + public SearchParameters apply(ActualEnvironment environment, VirtualQuery query) throws VirtualizationException + { + SearchParameters searchParameters = new SearchParameters(); + + String storeRefString = query.getStoreRef(); + if (storeRefString != null) + { + searchParameters.addStore(new StoreRef(storeRefString)); + } + else + { + searchParameters.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + } + + searchParameters.setLanguage(query.getLanguage()); + searchParameters.setQuery(query.getQueryString()); + searchParameters.setQueryConsistency(QueryConsistency.TRANSACTIONAL_IF_POSSIBLE); + + return searchParameters; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/template/FilesFoldersConstraint.java b/source/java/org/alfresco/repo/virtual/template/FilesFoldersConstraint.java new file mode 100644 index 0000000000..d3c915c21f --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/FilesFoldersConstraint.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchService; + +/** + * Handles generic files and folders search query disjunction parameters decorations. + * + * @author Bogdan Horje + */ +public class FilesFoldersConstraint extends VirtualQueryConstraintDecorator +{ + + private boolean files; + + private boolean folders; + + public FilesFoldersConstraint(VirtualQueryConstraint decoratedConstraint, boolean files, boolean folders) + { + super(decoratedConstraint); + + this.files = files; + this.folders = folders; + } + + @Override + protected SearchParameters applyDecorations(ActualEnvironment environment, SearchParameters searchParameters, + VirtualQuery query) + { + String queryString = searchParameters.getQuery(); + String language = searchParameters.getLanguage(); + String filteredQuery = filter(language, + queryString, + files, + folders); + SearchParameters searchParametersCopy = searchParameters.copy(); + searchParametersCopy.setQuery(filteredQuery); + return searchParametersCopy; + } + + private String filter(String language, String query, boolean files, boolean folders) throws VirtualizationException + { + String filteredQuery = query; + + if (files ^ folders) + { + if (SearchService.LANGUAGE_FTS_ALFRESCO.equals(language)) + { + if (!files) + { + filteredQuery = "(" + filteredQuery + ") and TYPE:\"cm:folder\""; + } + else + { + filteredQuery = "(" + filteredQuery + ") and TYPE:\"cm:content\""; + } + } + else + { + throw new VirtualizationException("Disjunctive file-folder filters are only supported on " + + SearchService.LANGUAGE_FTS_ALFRESCO + " virtual query language."); + } + + } + + return filteredQuery; + } +} diff --git a/source/java/org/alfresco/repo/virtual/template/FilingData.java b/source/java/org/alfresco/repo/virtual/template/FilingData.java new file mode 100644 index 0000000000..08391ed9c8 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/FilingData.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import java.io.Serializable; +import java.util.Map; +import java.util.Set; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +public class FilingData +{ + private NodeRef filingNodeRef; + + private QName assocTypeQName; + + private QName assocQName; + + private QName nodeTypeQName; + + private Set aspects; + + private Map properties; + + public FilingData(NodeRef filingNodeRef, QName assocTypeQName, QName assocQName, QName nodeTypeQName, + Set aspects, Map properties) + { + super(); + this.filingNodeRef = filingNodeRef; + this.assocTypeQName = assocTypeQName; + this.assocQName = assocQName; + this.nodeTypeQName = nodeTypeQName; + this.aspects = aspects; + this.properties = properties; + } + + public Set getAspects() + { + return aspects; + } + + public NodeRef getFilingNodeRef() + { + return filingNodeRef; + } + + public QName getAssocTypeQName() + { + return assocTypeQName; + } + + public QName getAssocQName() + { + return assocQName; + } + + public QName getNodeTypeQName() + { + return nodeTypeQName; + } + + public Map getProperties() + { + return properties; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/template/FilingParameters.java b/source/java/org/alfresco/repo/virtual/template/FilingParameters.java new file mode 100644 index 0000000000..bb06b82395 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/FilingParameters.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.service.namespace.QName; + +/** + * Encapsulated node creation parameters needed to produce {@link FilingData} + * used for node creation in virtual contexts using {@link FilingRule}s. + */ +public class FilingParameters +{ + private Reference parentRef; + + private QName assocTypeQName; + + private QName assocQName; + + private QName nodeTypeQName; + + private Map properties; + + public FilingParameters(Reference parentReference) + { + this(parentReference, + null, + null, + null, + null); + } + + public FilingParameters(Reference parentReference, QName assocTypeQName, QName assocQName, QName nodeTypeQName, + Map properties) + { + super(); + this.parentRef = parentReference; + this.assocTypeQName = assocTypeQName; + this.assocQName = assocQName; + this.nodeTypeQName = nodeTypeQName; + this.properties = properties; + } + + public Reference getParentRef() + { + return this.parentRef; + } + + public QName getAssocTypeQName() + { + return this.assocTypeQName; + } + + public QName getAssocQName() + { + return this.assocQName; + } + + public QName getNodeTypeQName() + { + return this.nodeTypeQName; + } + + public Map getProperties() + { + return this.properties; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/template/FilingRule.java b/source/java/org/alfresco/repo/virtual/template/FilingRule.java new file mode 100644 index 0000000000..36ed8e7929 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/FilingRule.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * A rule for filing (storing in a physical location) content that is created in + * a virtual folder. + * + * @author Bogdan Horje + */ +public interface FilingRule +{ + FilingData createFilingData(FilingParameters parameters) throws VirtualizationException; + + NodeRef filingNodeRefFor(FilingParameters parameters) throws VirtualizationException; + + boolean isNullFilingRule(); +} diff --git a/source/java/org/alfresco/repo/virtual/template/IgnoreConstraint.java b/source/java/org/alfresco/repo/virtual/template/IgnoreConstraint.java new file mode 100644 index 0000000000..8358a60710 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/IgnoreConstraint.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import java.util.Set; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; + +/** + * Handles generic ignore type and/or aspects decorations of the search query parameters. + * + * @author Bogdan Horje + */ +public class IgnoreConstraint extends VirtualQueryConstraintDecorator +{ + + private Set ignoreAspectQNames; + + private Set ignoreTypeNames; + + public IgnoreConstraint(VirtualQueryConstraint decoratedConstraint, Set ignoreTypeQNames, + Set ignoreAspectQNames) + { + super(decoratedConstraint); + this.ignoreAspectQNames = ignoreAspectQNames; + this.ignoreTypeNames = ignoreTypeQNames; + } + + @Override + protected SearchParameters applyDecorations(ActualEnvironment environment, SearchParameters searchParameters, + VirtualQuery query) + { + if ((ignoreAspectQNames != null && !ignoreAspectQNames.isEmpty()) + || (ignoreTypeNames != null && !ignoreTypeNames.isEmpty())) + { + + if (SearchService.LANGUAGE_FTS_ALFRESCO.equals(searchParameters.getLanguage())) + { + SearchParameters searchParametersCopy = searchParameters.copy(); + return applyFTSDecorations(searchParametersCopy, + environment.getNamespacePrefixResolver()); + } + else + { + throw new VirtualizationException("Unsupported constrating language " + searchParameters.getLanguage()); + } + } + else + { + return searchParameters; + } + } + + private SearchParameters applyFTSDecorations(SearchParameters searchParameters, NamespacePrefixResolver nspResolver) + { + SearchParameters constrainedParameters = searchParameters.copy(); + String theQuery = constrainedParameters.getQuery(); + theQuery = "(" + theQuery + ")"; + + if (ignoreAspectQNames != null) + { + for (QName ignoredAspect : ignoreAspectQNames) + { + theQuery = theQuery + " and " + "!ASPECT:'" + ignoredAspect.toPrefixString(nspResolver) + "'"; + } + } + + if (ignoreTypeNames != null) + { + for (QName ignoredType : ignoreTypeNames) + { + theQuery = theQuery + " and " + "!TYPE:'" + ignoredType.toPrefixString(nspResolver) + "'"; + } + } + + constrainedParameters.setQuery(theQuery); + + return constrainedParameters; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/template/NodeFilingRule.java b/source/java/org/alfresco/repo/virtual/template/NodeFilingRule.java new file mode 100644 index 0000000000..69b236e22c --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/NodeFilingRule.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import java.util.Collections; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.repo.virtual.ref.GetActualNodeRefMethod; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +public class NodeFilingRule implements FilingRule +{ + + private ActualEnvironment environment; + + public NodeFilingRule(ActualEnvironment environment) + { + super(); + this.environment = environment; + } + + @Override + public FilingData createFilingData(FilingParameters parameters) throws VirtualizationException + { + NodeRef filingNodeRef = filingNodeRefFor(parameters); + return new FilingData(filingNodeRef, + parameters.getAssocTypeQName(), + parameters.getAssocQName(), + parameters.getNodeTypeQName(), + Collections. emptySet(), + parameters.getProperties()); + } + + @Override + public NodeRef filingNodeRefFor(FilingParameters parameters) throws VirtualizationException + { + return parameters.getParentRef().execute(new GetActualNodeRefMethod(environment)); + } + + @Override + public boolean isNullFilingRule() + { + return false; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/template/NullFilingRule.java b/source/java/org/alfresco/repo/virtual/template/NullFilingRule.java new file mode 100644 index 0000000000..0275a605d4 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/NullFilingRule.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * A {@link FilingRule} created in the absence of filing criteria in the virtual + * folder template. + * + * @author Bogdan Horje + */ +public class NullFilingRule implements FilingRule +{ + + public NullFilingRule(ActualEnvironment environment) + { + super(); + } + + @Override + public FilingData createFilingData(FilingParameters parameters) throws VirtualizationException + { + throw new VirtualizationException("Can not create filing data for readonly filing rule."); + } + + @Override + public NodeRef filingNodeRefFor(FilingParameters parameters) throws VirtualizationException + { + throw new VirtualizationException("Can not create parent readonly filing rule."); + } + + @Override + public boolean isNullFilingRule() + { + return true; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/template/PagingRequestConstraint.java b/source/java/org/alfresco/repo/virtual/template/PagingRequestConstraint.java new file mode 100644 index 0000000000..fa0db6b6a2 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/PagingRequestConstraint.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import org.alfresco.query.PagingRequest; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.service.cmr.search.SearchParameters; + +/** + * Constraints decorator that adds {@link PagingRequest} related information + * (things like skip-count and max-items) to query parameters. + * + * @author Bogdan Horje + */ +public class PagingRequestConstraint extends VirtualQueryConstraintDecorator +{ + private PagingRequest pagingRequest; + + public PagingRequestConstraint(VirtualQueryConstraint decoratedConstraint, PagingRequest pagingRequest) + { + super(decoratedConstraint); + this.pagingRequest = pagingRequest; + } + + @Override + protected SearchParameters applyDecorations(ActualEnvironment environment, SearchParameters searchParameters, + VirtualQuery query) + { + SearchParameters searchParametersCopy = searchParameters.copy(); + + if (pagingRequest != null) + { + searchParametersCopy.setSkipCount(pagingRequest.getSkipCount()); + searchParametersCopy.setMaxItems(pagingRequest.getMaxItems()); + } + + return searchParametersCopy; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/template/PropertyValueConstraint.java b/source/java/org/alfresco/repo/virtual/template/PropertyValueConstraint.java new file mode 100644 index 0000000000..c060555d61 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/PropertyValueConstraint.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import java.io.Serializable; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; + +/** + * Specifies a constraint on a property value, as for e.g. + * ContentModel.PROP_NAME, to be applied to queries given in the virtual folder + * template. + * + * @author Bogdan Horje + */ +public class PropertyValueConstraint extends VirtualQueryConstraintDecorator +{ + // TODO: introduce operator + + private QName property; + + private Serializable value; + + private NamespacePrefixResolver nspResolver; + + public PropertyValueConstraint(VirtualQueryConstraint decoratedConstraint, QName property, Serializable value, + NamespacePrefixResolver nspResolver) + { + super(decoratedConstraint); + this.property = property; + this.value = value; + this.nspResolver = nspResolver; + } + + @Override + public SearchParameters applyDecorations(ActualEnvironment environment, SearchParameters searchParameters, + VirtualQuery query) throws VirtualizationException + { + // TODO: allow custom language and not only constraint appliers + + if (SearchService.LANGUAGE_FTS_ALFRESCO.equals(searchParameters.getLanguage())) + { + SearchParameters searchParametersCopy = searchParameters.copy(); + return applyFTS(searchParametersCopy); + } + else + { + throw new VirtualizationException("Unsupported constrating language " + searchParameters.getLanguage()); + } + + } + + private SearchParameters applyFTS(SearchParameters searchParameters) + { + SearchParameters constrainedParameters = searchParameters.copy(); + String theQuery = constrainedParameters.getQuery(); + + // TODO: introduce and use operator + + theQuery = "(" + theQuery + ")" + " and " + "( " + "=" + property.toPrefixString(this.nspResolver) + ":" + + org.alfresco.util.ISO9075.encode(value.toString()) + " )"; + + constrainedParameters.setQuery(theQuery); + + return constrainedParameters; + } +} diff --git a/source/java/org/alfresco/repo/virtual/template/SortConstraint.java b/source/java/org/alfresco/repo/virtual/template/SortConstraint.java new file mode 100644 index 0000000000..288fa711f1 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/SortConstraint.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.alfresco.repo.model.filefolder.GetChildrenCannedQuery; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchParameters.SortDefinition; +import org.alfresco.service.cmr.search.SearchParameters.SortDefinition.SortType; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; + + +/** + * Handles generic sort information decorations of the search query parameters. + * + * @author Bogdan Horje + */ +public class SortConstraint extends VirtualQueryConstraintDecorator +{ + private static final Set IGNORED_SORT_PROPERTIES = new HashSet<>(Arrays.asList(GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER)); + + private List> sortProps; + + public SortConstraint(VirtualQueryConstraint decoratedConstraint, List> sortProps) + { + super(decoratedConstraint); + this.sortProps = sortProps; + } + + @Override + protected SearchParameters applyDecorations(ActualEnvironment environment, SearchParameters searchParameters, + VirtualQuery query) + { + SearchParameters searchParametersCopy = searchParameters.copy(); + for (Pair sort : sortProps) + { + if (!IGNORED_SORT_PROPERTIES.contains(sort.getFirst())) + { + SortDefinition sortDefinition = new SortDefinition(SortType.FIELD, + sort.getFirst().getLocalName(), + sort.getSecond()); + searchParametersCopy.addSort(sortDefinition); + } + } + return searchParametersCopy; + } + +} diff --git a/source/java/org/alfresco/repo/virtual/template/TemplateFilingRule.java b/source/java/org/alfresco/repo/virtual/template/TemplateFilingRule.java new file mode 100644 index 0000000000..67f5cbc899 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/TemplateFilingRule.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.repo.virtual.config.NodeRefPathExpression; +import org.alfresco.repo.virtual.ref.GetActualNodeRefMethod; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO9075; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A {@link FilingRule} created with the criteria given in the applied virtual + * folder template. + * + * @author Bogdan Horje + */ +public class TemplateFilingRule implements FilingRule +{ + private static Log logger = LogFactory.getLog(TemplateFilingRule.class); + + private ActualEnvironment env; + + private String path; + + private String type; + + private Set aspects; + + private Map stringProperties; + + + public TemplateFilingRule(ActualEnvironment environment, String path, String type, Set aspects, + Map properties) + { + this.env = environment; + this.path = path; + this.type = type; + this.aspects = aspects; + this.stringProperties = properties; + } + + @Override + public FilingData createFilingData(FilingParameters parameters) throws VirtualizationException + { + return createFilingData(parameters.getParentRef(), + parameters.getAssocTypeQName(), + parameters.getAssocQName(), + parameters.getNodeTypeQName(), + parameters.getProperties()); + } + + private FilingData createFilingData(Reference parentRef, QName assocTypeQName, QName assocQName, + QName nodeTypeQName, Map properties) throws VirtualizationException + { + + NodeRef fParentRef = null; + QName fType = null; + Set fAspects = null; + Map fProperties = null; + + NamespacePrefixResolver nsPrefixResolver = env.getNamespacePrefixResolver(); + + if (type == null || type.length() == 0) + { + fType = nodeTypeQName; + } + else + { + fType = QName.createQName(type, + nsPrefixResolver); + + // CM-528 acceptance criteria 3 : + // Given that the current user can upload new content into a + // specific virtual folder (filing rule) + // when a filing rule specifies a type or sub type of cm:folder + // (which is a non supported configuration) + // uploading content will create a document, not a folder + + if (env.isSubClass(fType, + ContentModel.TYPE_FOLDER)) + { + if (logger.isDebugEnabled()) + { + logger.debug("CM-528 acceptance criteria 3 : we deny the creation of folders subtype " + fType + + " and force cm:content instead."); + } + fType = ContentModel.TYPE_CONTENT; + } + + // Explicit type matching follows. + // It might cause non-transactional behavior. + // To avoid it we rely on folder creation exclusion in + // VirtualNodeServiceExtension#createNode + // See CM-533 Suppress options to create folders in a virtual folder + + if (env.isSubClass(nodeTypeQName, + fType)) + { + fType = nodeTypeQName; + } + } + + fParentRef = parentNodeRefFor(parentRef, + true); + + fProperties = new HashMap(properties); + + Set> propertyEntries = stringProperties.entrySet(); + + for (Entry propertyEntry : propertyEntries) + { + String name = propertyEntry.getKey(); + QName qName = QName.createQName(name, + nsPrefixResolver); + if (!fProperties.containsKey(qName)) + { + fProperties.put(qName, + stringProperties.get(name)); + } + } + + fAspects = new HashSet<>(); + + for (String aspect : aspects) + { + fAspects.add(QName.createQName(aspect, + env.getNamespacePrefixResolver())); + } + + return new FilingData(fParentRef, + assocTypeQName, + assocQName, + fType, + fAspects, + fProperties); + + } + + private NodeRef parentNodeRefFor(Reference parentReference, boolean failIfNotFound) + { + NodeRef fParentRef; + if (path == null || path.length() == 0) + { + fParentRef = parentReference.execute(new GetActualNodeRefMethod(env)); + } + else + { + + String[] pathElements = NodeRefPathExpression.splitAndNormalizePath(path); + for (int i = 0; i < pathElements.length; i++) + { + pathElements[i]=ISO9075.decode(pathElements[i]); + } + fParentRef = env.findQNamePath(pathElements); + } + + if (failIfNotFound && fParentRef == null) + { + throw new VirtualizationException("The filing path " + path + " could not be resolved."); + } + + return fParentRef; + } + + @Override + public boolean isNullFilingRule() + { + return false; + } + + @Override + public NodeRef filingNodeRefFor(FilingParameters parameters) throws VirtualizationException + { + return parentNodeRefFor(parameters.getParentRef(), + false); + } + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/virtual/template/TemplateResourceProcessor.java b/source/java/org/alfresco/repo/virtual/template/TemplateResourceProcessor.java new file mode 100644 index 0000000000..33f26b4b01 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/TemplateResourceProcessor.java @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.virtual.ActualEnvironmentException; +import org.alfresco.repo.virtual.VirtualContext; +import org.alfresco.repo.virtual.ref.ClasspathResource; +import org.alfresco.repo.virtual.ref.RepositoryResource; +import org.alfresco.repo.virtual.ref.Resource; +import org.alfresco.repo.virtual.ref.ResourceProcessingError; +import org.alfresco.repo.virtual.ref.ResourceProcessor; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.util.ParameterCheck; + +/** + * Executes JavaScript virtual folders templates ensuring their required context and the + * {@link VirtualFolderDefinition} post execution JSON map result unmarshalling. + * + * @author Bogdan Horje + */ +public class TemplateResourceProcessor implements ResourceProcessor +{ + private static final String ID_KEY = "id"; + + private static final String NAME_KEY = "name"; + + private static final String DESCRIPTION_KEY = "description"; + + private static final String NODES_KEY = "nodes"; + + private static final String SEARCH_KEY = "search"; + + private static final String LANGUAGE_KEY = "language"; + + private static final String QUERY_KEY = "query"; + + private static final String STORE_KEY = "store"; + + private static final String FILING_KEY = "filing"; + + private static final String CLASSIFICATION_KEY = "classification"; + + private static final String TYPE_KEY = "type"; + + private static final String ASPECTS_KEY = "aspects"; + + private static final String PROPERTIES_KEY = "properties"; + + private static final String PATH_KEY = "path"; + + private VirtualContext context; + + public TemplateResourceProcessor(VirtualContext context) + { + super(); + this.context = context; + } + + @Override + public VirtualFolderDefinition process(Resource resource) throws ResourceProcessingError + { + ParameterCheck.mandatory("resource", + resource); + + if (resource instanceof ClasspathResource) + { + return process((ClasspathResource) resource); + } + else if (resource instanceof RepositoryResource) + { + return process((RepositoryResource) resource); + } + else + { + throw new ResourceProcessingError("Unsupported resource class " + resource.getClass()); + } + } + + /** + * Processes the JavaScript template given by the {@link ClasspathResource} + * and creates a {@link VirtualFolderDefinition} (composite) structure that + * represents the virtual folder. + * + * @param A {@link ClasspathResource} reference. + * @return A {@link VirtualFolderDefinition} reference that represents the + * structure (tree of nodes) of the virtual folder. + */ + @SuppressWarnings("unchecked") + @Override + public VirtualFolderDefinition process(ClasspathResource classpath) throws ResourceProcessingError + { + Object result; + try + { + result = context.getActualEnviroment().executeScript(classpath.getClasspath(), + createScriptParameters(context)); + return asVirtualStructure(context, + (Map) result); + } + catch (ActualEnvironmentException e) + { + throw new ResourceProcessingError(e); + } + } + + /** + * Processes the JavaScript template given by the {@link RepositoryResource} + * and creates a {@link VirtualFolderDefinition} (composite) structure that + * represents the virtual folder. + * + * @param A {@link RepositoryResource} reference. + * @return A {@link VirtualFolderDefinition} reference that represents the + * structure (tree of nodes) of the virtual folder. + */ + @SuppressWarnings("unchecked") + @Override + public VirtualFolderDefinition process(RepositoryResource repositoryResource) throws ResourceProcessingError + { + Object result; + try + { + NodeRef templateNodeRef = repositoryResource.getLocation().asNodeRef(context.getActualEnviroment()); + result = context.getActualEnviroment().executeScript(templateNodeRef, + createScriptParameters(context)); + return asVirtualStructure(context, + (Map) result); + } + catch (ActualEnvironmentException e) + { + throw new ResourceProcessingError(e); + } + } + + /** + * Adds the script virtual context to the parameters of the context. + * + * @param context A {@link VirtualContext} reference. + * @return A map of context parameters. + * @throws ActualEnvironmentException + */ + protected Map createScriptParameters(VirtualContext context) throws ActualEnvironmentException + { + Map parameters = context.getParameters(); + Object scriptContext = context.getActualEnviroment().createScriptVirtualContext(context); + parameters.put(VirtualContext.CONTEXT_PARAM, + scriptContext); + return parameters; + } + + /** + * Creates a filing rule based on the criteria (path, classification: type, + * aspects) given in the template. + * + * @param context The context in which the virtualization process takes + * place. + * @param filing A map containing the filing criteria used to create the + * rule. + * @return A {@link FilingRule} reference. + * @throws ResourceProcessingError + */ + @SuppressWarnings("unchecked") + protected FilingRule asFilingRule(VirtualContext context, Object filing) throws ResourceProcessingError + { + if (filing == null) + { + return null; + } + + Map filingMap = (Map) filing; + + if (filingMap.isEmpty()) + { + return null; + } + + String path = (String) filingMap.get(PATH_KEY); + + Map classificationMap = (Map) filingMap.get(CLASSIFICATION_KEY); + + String type = null; + + List aspects = null; + + if (classificationMap != null) + { + type = (String) classificationMap.get(TYPE_KEY); + + aspects = (List) classificationMap.get(ASPECTS_KEY); + } + + if (aspects == null) + { + aspects = Collections.emptyList(); + } + + Map properties = (Map) filingMap.get(PROPERTIES_KEY); + if (properties == null) + { + properties = Collections.emptyMap(); + } + + if (path == null && type == null && aspects.isEmpty() && properties.isEmpty()) + { + return new NullFilingRule(context.getActualEnviroment()); + } + else + { + return new TemplateFilingRule(context.getActualEnviroment(), + path, + type, + new HashSet(aspects), + properties); + + } + + } + + /** + * Creates a query based on the details: language, store, query statement + * given in the template. + * + * @param context The context in which the virtualization process takes + * place. + * @param search A map containing the details that define the query: + * language, store, query statement. + * @return an {@link AlfrescoVirtualQuery} reference. + * @throws ResourceProcessingError + */ + protected VirtualQuery asVirtualQuery(VirtualContext context, Map search) + throws ResourceProcessingError + { + if (search == null) + { + return null; + } + + Map mapSearch = search; + + String language = (String) mapSearch.get(LANGUAGE_KEY); + String store = (String) mapSearch.get(STORE_KEY); + String query = (String) mapSearch.get(QUERY_KEY); + + return new VirtualQueryImpl(store, + language, + query); + } + + /** + * Creates a {@link VirtualFolderDefinition} that represents the structure + * of the virtual folder. + * + * @param context The context in which the virtualization process takes + * place. + * @param result A map containing the details that define the virtual + * entries. + * @return a {@link VirtualFolderDefinition} reference. + * @throws ResourceProcessingError + */ + protected VirtualFolderDefinition asVirtualStructure(VirtualContext context, Map result) + throws ResourceProcessingError + { + try + { + Map mapResult = result; + + VirtualFolderDefinition virtualStructure = new VirtualFolderDefinition(); + + String id = (String) mapResult.get(ID_KEY); + virtualStructure.setId(id); + + String name = (String) mapResult.get(NAME_KEY); + virtualStructure.setName(name); + + String description = (String) mapResult.get(DESCRIPTION_KEY); + virtualStructure.setDescription(description); + + @SuppressWarnings("unchecked") + VirtualQuery virtualQuery = asVirtualQuery(context, + (Map) mapResult.get(SEARCH_KEY)); + virtualStructure.setQuery(virtualQuery); + + FilingRule filingRule = asFilingRule(context, + mapResult.get(FILING_KEY)); + if (filingRule == null) + { + filingRule = new NullFilingRule(context.getActualEnviroment()); + } + virtualStructure.setFilingRule(filingRule); + + @SuppressWarnings("unchecked") + Map properties = (Map) mapResult.get(PROPERTIES_KEY); + if (properties == null) + { + properties = Collections.emptyMap(); + } + + virtualStructure.setProperties(properties); + + @SuppressWarnings("unchecked") + List> nodes = (List>) mapResult.get(NODES_KEY); + + if (nodes != null) + { + for (Map node : nodes) + { + VirtualFolderDefinition child = asVirtualStructure(context, + node); + virtualStructure.addChild(child); + } + } + + return virtualStructure; + } + catch (ClassCastException e) + { + throw new ResourceProcessingError(e); + } + } + +} diff --git a/source/java/org/alfresco/repo/virtual/template/VirtualFolderDefinition.java b/source/java/org/alfresco/repo/virtual/template/VirtualFolderDefinition.java new file mode 100644 index 0000000000..c41704ef66 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/VirtualFolderDefinition.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * A virtual folder definition. This class holds the (virtual entry) + * structure of a virtual folder, created according to the template that is + * applied.
      + * + * @author Bogdan Horje + */ +public class VirtualFolderDefinition +{ + + private String name; + + private String description; + + private FilingRule filingRule; + + private VirtualQuery query; + + private List children = new LinkedList(); + + private Map childrenByName = new HashMap(); + + private Map childrenById = new HashMap(); + + private String id; + + private Map properties = new HashMap<>(); + + public VirtualFolderDefinition() + { + this(""); + } + + public VirtualFolderDefinition(String name) + { + super(); + this.name = name; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + if (this.id == null) + { + this.id = name; + } + } + + public void setId(String id) + { + this.id = id; + if (this.name == null) + { + this.name = id; + } + } + + public String getId() + { + return this.id; + } + + public String getDescription() + { + return description; + } + + public void setDescription(String description) + { + this.description = description; + } + + public FilingRule getFilingRule() + { + return filingRule; + } + + public void setFilingRule(FilingRule filingRule) + { + this.filingRule = filingRule; + } + + public VirtualQuery getQuery() + { + return query; + } + + public void setQuery(VirtualQuery query) + { + this.query = query; + } + + public VirtualFolderDefinition findChildByName(String name) + { + return childrenByName.get(name); + } + + public List getChildren() + { + return children; + } + + public void addChild(VirtualFolderDefinition child) + { + this.children.add(child); + this.childrenByName.put(child.getName(), + child); + this.childrenById.put(child.getId(), + child); + } + + public VirtualFolderDefinition findChildById(String childId) + { + return childrenById.get(childId); + } + + public void setProperties(Map properties) + { + this.properties = properties; + } + + public Map getProperties() + { + return this.properties; + } +} diff --git a/source/java/org/alfresco/repo/virtual/template/VirtualQuery.java b/source/java/org/alfresco/repo/virtual/template/VirtualQuery.java new file mode 100644 index 0000000000..d5be8b375f --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/VirtualQuery.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import java.util.List; +import java.util.Set; + +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; + +/** + * A virtual nodes query used in virtualization processes. + * + * @author Bogdan Horje + */ +public interface VirtualQuery +{ + /** + * @param actualEnvironment + * @param files + * @param folders + * @param pattern + * @param ignoreTypeQNames + * @param searchTypeQNames + * @param ignoreAspectQNames + * @param sortProps + * @param pagingRequest + * @return - + * @throws VirtualizationException + * @deprecated will be replaced by + * {@link #perform(ActualEnvironment, VirtualQueryConstraint,Reference)} + * once complex constrains are implemented + */ + PagingResults perform(ActualEnvironment actualEnvironment, boolean files, boolean folders, + String pattern, Set searchTypeQNames,Set ignoreTypeQNames, Set ignoreAspectQNames, + List> sortProps, PagingRequest pagingRequest, Reference parentReference) + throws VirtualizationException; + + PagingResults perform(ActualEnvironment actualEnvironment, VirtualQueryConstraint constraint, + PagingRequest pagingRequest, Reference parentReference) throws VirtualizationException; + + String getQueryString(); + + String getLanguage(); + + String getStoreRef(); +} diff --git a/source/java/org/alfresco/repo/virtual/template/VirtualQueryConstraint.java b/source/java/org/alfresco/repo/virtual/template/VirtualQueryConstraint.java new file mode 100644 index 0000000000..2caf164027 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/VirtualQueryConstraint.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.service.cmr.search.SearchParameters; + +/** + * Specifies the constraint to be applied to queries given in the + * virtual folder template. + * + * @author Bogdan Horje + */ +public interface VirtualQueryConstraint +{ + /** + * + * @param environment + * @param query + * @return the {@link SearchParameters} representation of the given query with this constraint applied + * @throws VirtualizationException + */ + SearchParameters apply(ActualEnvironment environment, VirtualQuery query) + throws VirtualizationException; +} diff --git a/source/java/org/alfresco/repo/virtual/template/VirtualQueryConstraintDecorator.java b/source/java/org/alfresco/repo/virtual/template/VirtualQueryConstraintDecorator.java new file mode 100644 index 0000000000..adf93e607f --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/VirtualQueryConstraintDecorator.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.service.cmr.search.SearchParameters; + +/** + * {@link SearchParameters} decorator delegate implementation if a query + * constraint. + * + * @author Bogdan Horje + */ +public abstract class VirtualQueryConstraintDecorator implements VirtualQueryConstraint +{ + private VirtualQueryConstraint decoratedConstraint; + + public VirtualQueryConstraintDecorator(VirtualQueryConstraint decoratedConstraint) + { + super(); + this.decoratedConstraint = decoratedConstraint; + } + + @Override + public final SearchParameters apply(ActualEnvironment environment, VirtualQuery query) + throws VirtualizationException + { + SearchParameters searchParametersToDecorate = decoratedConstraint.apply(environment, + query); + + return applyDecorations(environment, + searchParametersToDecorate, + query); + } + + /** + * @param environment + * @param searchParameters + * @param query + * @return a new {@link SearchParameters} instance containing the given + * parameters values with additional decorations/changes enforced by + * this decorator constraint + */ + protected abstract SearchParameters applyDecorations(ActualEnvironment environment, + SearchParameters searchParameters, VirtualQuery query); +} diff --git a/source/java/org/alfresco/repo/virtual/template/VirtualQueryImpl.java b/source/java/org/alfresco/repo/virtual/template/VirtualQueryImpl.java new file mode 100644 index 0000000000..8a7852f571 --- /dev/null +++ b/source/java/org/alfresco/repo/virtual/template/VirtualQueryImpl.java @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2005-2015 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * 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/. + */ + +package org.alfresco.repo.virtual.template; + +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.search.EmptyResultSet; +import org.alfresco.repo.virtual.ActualEnvironment; +import org.alfresco.repo.virtual.ActualEnvironmentException; +import org.alfresco.repo.virtual.VirtualizationException; +import org.alfresco.repo.virtual.ref.NodeProtocol; +import org.alfresco.repo.virtual.ref.Reference; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.QueryConsistency; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.ResultSetRow; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class VirtualQueryImpl implements VirtualQuery +{ + private static Log logger = LogFactory.getLog(VirtualQueryImpl.class); + + private String language; + + private String store; + + private String query; + + public VirtualQueryImpl(String store, String language, String query) + { + super(); + this.language = language; + this.store = store; + this.query = query; + } + + @Override + public String getLanguage() + { + return language; + } + + @Override + public String getStoreRef() + { + return store; + } + + @Override + public String getQueryString() + { + return query; + } + + /** + * @deprecated will be replaced by + * {@link #perform(ActualEnvironment, VirtualQueryConstraint,Reference)} + * once complex constrains are implemented + */ + @Override + public PagingResults perform(ActualEnvironment environment, boolean files, boolean folders, + String pattern, Set ignoreTypeQNames, Set searchTypeQNames, Set ignoreAspectQNames, + List> sortProps, PagingRequest pagingRequest, Reference parentReference) + throws VirtualizationException + { + + if (!files && !folders) + { + if (logger.isDebugEnabled()) + { + logger.debug("Deprecated query will be skipped due do incompatible types request."); + } + + return asPagingResults(environment, + pagingRequest, + new EmptyResultSet(), + parentReference); + + } + else + { + VirtualQueryConstraint constraint = BasicConstraint.INSTANCE; + constraint = new FilesFoldersConstraint(constraint, + files, + folders); + constraint = new IgnoreConstraint(constraint, + ignoreTypeQNames, + ignoreAspectQNames); + constraint = new PagingRequestConstraint(constraint, + pagingRequest); + constraint = new SortConstraint(constraint, + sortProps); + + return perform(environment, + constraint, + null, + parentReference); + } + + } + + /** + * @deprecated will be replaced by {@link VirtualQueryConstraint}s once + * complex constrains are implemented + */ + private String filter(String language, String query, boolean files, boolean folders) throws VirtualizationException + { + String filteredQuery = query; + + if (files ^ folders) + { + if (SearchService.LANGUAGE_FTS_ALFRESCO.equals(language)) + { + if (!files) + { + filteredQuery = "(" + filteredQuery + ") and TYPE:\"cm:folder\""; + } + else + { + filteredQuery = "(" + filteredQuery + ") and TYPE:\"cm:content\""; + } + } + else + { + throw new VirtualizationException("Disjunctive file-folder filters are only supported on " + + SearchService.LANGUAGE_FTS_ALFRESCO + " virtual query language."); + } + + } + + return filteredQuery; + } + + private PagingResults asPagingResults(ActualEnvironment environment, PagingRequest pagingRequest, + ResultSet result, Reference parentReference) throws ActualEnvironmentException + { + final List page = new LinkedList(); + + for (ResultSetRow row : result) + { + page.add(NodeProtocol.newReference(row.getNodeRef(), + parentReference)); + } + + final boolean hasMore = result.hasMore(); + final int totalFirst = (int) result.getNumberFound(); + int start; + try + { + start = result.getStart(); + } + catch (UnsupportedOperationException e) + { + if (logger.isDebugEnabled()) + { + logger.debug("Unsupported ResultSet.getStart() when trying to create query paging result"); + } + if (pagingRequest != null) + { + start = pagingRequest.getSkipCount(); + } + else + { + start = 0; + } + } + final int totlaSecond = !hasMore ? (int) result.getNumberFound() : (int) (start + result.getNumberFound() + 1); + final Pair total = new Pair(totalFirst, + totlaSecond); + return new PagingResults() + { + + @Override + public List getPage() + { + return page; + } + + @Override + public boolean hasMoreItems() + { + return hasMore; + } + + @Override + public Pair getTotalResultCount() + { + + return total; + } + + @Override + public String getQueryExecutionId() + { + return null; + } + + }; + } + + private SearchParameters createSearchParameters(boolean files, boolean folders, PagingRequest pagingRequest) + throws VirtualizationException + { + SearchParameters searchParameters = new SearchParameters(); + + if (store != null) + { + searchParameters.addStore(new StoreRef(store)); + } + else + { + searchParameters.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + } + searchParameters.setLanguage(language); + searchParameters.setQuery(filter(language, + query, + files, + folders)); + searchParameters.setQueryConsistency(QueryConsistency.TRANSACTIONAL_IF_POSSIBLE); + + if (pagingRequest != null) + { + searchParameters.setSkipCount(pagingRequest.getSkipCount()); + searchParameters.setMaxItems(pagingRequest.getMaxItems()); + } + + return searchParameters; + } + + @Override + public PagingResults perform(ActualEnvironment environment, VirtualQueryConstraint constraint, + PagingRequest pagingRequest, Reference parentReference) throws VirtualizationException + { + VirtualQueryConstraint theConstraint = constraint; + + if (pagingRequest != null) + { + theConstraint = new PagingRequestConstraint(theConstraint, + pagingRequest); + } + + SearchParameters searchParameters = theConstraint.apply(environment, + this); + + ResultSet result = environment.query(searchParameters); + + if (logger.isDebugEnabled()) + { + logger.debug("Constrained query " + searchParameters + " resulted in " + result.length() + " rows."); + } + + return asPagingResults(environment, + pagingRequest, + result, + parentReference); + } + +}