From 0b840643f90c8e92659a01f311c6aec5d8c4349c Mon Sep 17 00:00:00 2001 From: Lucian Tuca Date: Thu, 20 May 2021 15:06:25 +0300 Subject: [PATCH] ACS-1582 : Storage classes - java api (#467) * ACS-1582 : Storage classes - java api - added ContentContext updates - added StorageClasses related exception --- .../alfresco/repo/content/ContentContext.java | 21 +++- .../alfresco/repo/content/ContentStore.java | 77 +++++++++++- .../UnsupportedStorageClassException.java | 58 +++++++++ .../content/AbstractRoutingContentStore.java | 111 ++++++++++++------ .../content/caching/CachingContentStore.java | 13 ++ .../content/filestore/FileContentStore.java | 108 ++++++++--------- .../replication/AggregatingContentStore.java | 33 ++++++ .../AbstractTenantRoutingContentStore.java | 107 +++++++++-------- .../cmr/repository/ContentService.java | 65 +++++++++- .../repo/content/RoutingContentStoreTest.java | 100 ++++++++-------- .../caching/CachingContentStoreTest.java | 23 +++- .../filestore/FileContentStoreTest.java | 77 +++++++----- .../AggregatingContentStoreTest.java | 74 ++++++++++-- 13 files changed, 630 insertions(+), 237 deletions(-) create mode 100644 data-model/src/main/java/org/alfresco/repo/content/UnsupportedStorageClassException.java diff --git a/data-model/src/main/java/org/alfresco/repo/content/ContentContext.java b/data-model/src/main/java/org/alfresco/repo/content/ContentContext.java index 7ff9bf3ab3..95ca0a8f96 100644 --- a/data-model/src/main/java/org/alfresco/repo/content/ContentContext.java +++ b/data-model/src/main/java/org/alfresco/repo/content/ContentContext.java @@ -26,6 +26,7 @@ package org.alfresco.repo.content; import java.io.Serializable; +import java.util.Set; import org.alfresco.service.cmr.repository.ContentReader; @@ -49,6 +50,7 @@ public class ContentContext implements Serializable private ContentReader existingContentReader; private String contentUrl; + private Set storageClasses; /** * Construct the instance with the content URL. @@ -88,5 +90,22 @@ public class ContentContext implements Serializable { return contentUrl; } - + + /** + * @return Returns the storage classes for the content- may be null + */ + public Set getStorageClasses() + { + return storageClasses; + } + + /** + * Sets the storage classes for the content- may be null + * + * @param storageClasses + */ + public void setStorageClasses(Set storageClasses) + { + this.storageClasses = storageClasses; + } } diff --git a/data-model/src/main/java/org/alfresco/repo/content/ContentStore.java b/data-model/src/main/java/org/alfresco/repo/content/ContentStore.java index ccded57adb..8e00c4fe1c 100644 --- a/data-model/src/main/java/org/alfresco/repo/content/ContentStore.java +++ b/data-model/src/main/java/org/alfresco/repo/content/ContentStore.java @@ -25,6 +25,11 @@ */ package org.alfresco.repo.content; +import java.util.Collections; +import java.util.Date; +import java.util.Map; +import java.util.Set; + import org.alfresco.api.AlfrescoPublicApi; import org.alfresco.service.cmr.repository.ContentAccessor; import org.alfresco.service.cmr.repository.ContentIOException; @@ -33,8 +38,6 @@ import org.alfresco.service.cmr.repository.ContentStreamListener; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.DirectAccessUrl; -import java.util.Date; - /** * Provides low-level retrieval of content * {@link org.alfresco.service.cmr.repository.ContentReader readers} and @@ -88,6 +91,16 @@ public interface ContentStore * The delimiter that must be found in all URLS, i.e :// */ public static final String PROTOCOL_DELIMITER = "://"; + + /** + * The 'default' storage class + * + * A content is considered to have a default storage class if: + * the value is a Set.of("default") + * the value is an empty set + * the value is null + */ + public static final String DEFAULT_SC = "default"; /** * Check if the content URL format is supported by the store. @@ -263,4 +276,64 @@ public interface ContentStore { return false; } + + /** + * Checks whether or not the current {@link ContentStore} supports the provided {@link Set} storage classes + * + * @param storageClasses The storage classes that will be checked whether or not are supported + * @return true if the storage classes are supported, false otherwise. + */ + default boolean isStorageClassesSupported(Set storageClasses) + { + return storageClasses == null || + storageClasses.isEmpty() || + (1 == storageClasses.size() && storageClasses.contains(DEFAULT_SC)); + } + + /** + * @return Returns the complete {@link Set} of supported storage classes by this {@link ContentStore} + */ + default Set getSupportedStorageClasses() + { + return Set.of(DEFAULT_SC); + } + + /** + * Updates the storage class for content + * + * @param contentUrl The URL of the content that will have its storage classes updated + * @param storageClasses The new storage classes + * @param parameters extra parameters + */ + default void updateStorageClasses(String contentUrl, Set storageClasses, Map parameters) + { + + } + + /** + * @param contentUrl the URL of the content for which the storage classes are to be requested + * @return Returns the current storage classes for the content found at the contentUrl + */ + default Set findStorageClasses(String contentUrl) + { + return Collections.emptySet(); + } + + /** + * @return Returns the complete collection of allowed storage classes transitions. + * The key represents the source storage classes while the value (as a {@link Set}) represents all the possible target storage classes. + */ + default Map, Set>> getStorageClassesTransitions() + { + return Collections.emptyMap(); + } + + /** + * @param contentUrl the URL of the content for which the storage classes transitions are to be requested + * @return Returns the complete collection of allowed storage classes transitions for the content found at content URL + */ + default Map, Set>> findStorageClassesTransitions(String contentUrl) + { + return Collections.emptyMap(); + } } diff --git a/data-model/src/main/java/org/alfresco/repo/content/UnsupportedStorageClassException.java b/data-model/src/main/java/org/alfresco/repo/content/UnsupportedStorageClassException.java new file mode 100644 index 0000000000..91ac187646 --- /dev/null +++ b/data-model/src/main/java/org/alfresco/repo/content/UnsupportedStorageClassException.java @@ -0,0 +1,58 @@ +/* + * #%L + * Alfresco Data model classes + * %% + * Copyright (C) 2005 - 2021 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.content; + +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * Thrown when an operation regarding the storage classes of the content failed to execute. + * + * @author Lucian Tuca + */ +public class UnsupportedStorageClassException extends AlfrescoRuntimeException +{ + private final ContentStore contentStore; + private final Set storageClasses; + + public UnsupportedStorageClassException(ContentStore contentStore, Set storageClasses, String msg) + { + super(msg); + this.contentStore = contentStore; + this.storageClasses = storageClasses; + } + + public ContentStore getContentStore() + { + return contentStore; + } + + public Set getStorageClasses() + { + return storageClasses; + } +} diff --git a/repository/src/main/java/org/alfresco/repo/content/AbstractRoutingContentStore.java b/repository/src/main/java/org/alfresco/repo/content/AbstractRoutingContentStore.java index 2bb42921c7..a363918e20 100644 --- a/repository/src/main/java/org/alfresco/repo/content/AbstractRoutingContentStore.java +++ b/repository/src/main/java/org/alfresco/repo/content/AbstractRoutingContentStore.java @@ -1,44 +1,46 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.repo.content; -import java.util.List; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.cache.SimpleCache; -import org.alfresco.service.cmr.repository.ContentIOException; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.util.GUID; -import org.alfresco.util.Pair; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.service.cmr.repository.ContentIOException; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.util.GUID; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * A store providing support for content store implementations that provide @@ -416,4 +418,35 @@ public abstract class AbstractRoutingContentStore implements ContentStore } return deleted; } + + @Override + public boolean isStorageClassesSupported(Set storageClasses) + { + boolean supported = false; + for (ContentStore store : getAllStores()) + { + if (store.isStorageClassesSupported(storageClasses)) + { + supported = true; + break; + } + } + // Done + if (logger.isDebugEnabled()) + { + logger.debug("The storage classes " + storageClasses + (supported ? "are" : "are not") + " supported by at least one store."); + } + return supported; + } + + @Override + public Set getSupportedStorageClasses() + { + Set supportedStorageClasses = new HashSet<>(); + for (ContentStore store : getAllStores()) + { + supportedStorageClasses.addAll(store.getSupportedStorageClasses()); + } + return supportedStorageClasses; + } } diff --git a/repository/src/main/java/org/alfresco/repo/content/caching/CachingContentStore.java b/repository/src/main/java/org/alfresco/repo/content/caching/CachingContentStore.java index 5cad001984..42e1e64c1d 100644 --- a/repository/src/main/java/org/alfresco/repo/content/caching/CachingContentStore.java +++ b/repository/src/main/java/org/alfresco/repo/content/caching/CachingContentStore.java @@ -26,6 +26,7 @@ package org.alfresco.repo.content.caching; import java.util.Date; +import java.util.Set; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; @@ -487,4 +488,16 @@ public class CachingContentStore implements ContentStore, ApplicationEventPublis { return backingStore.getDirectAccessUrl(contentUrl, expiresAt); } + + @Override + public boolean isStorageClassesSupported(Set storageClasses) + { + return backingStore.isStorageClassesSupported(storageClasses); + } + + @Override + public Set getSupportedStorageClasses() + { + return backingStore.getSupportedStorageClasses(); + } } diff --git a/repository/src/main/java/org/alfresco/repo/content/filestore/FileContentStore.java b/repository/src/main/java/org/alfresco/repo/content/filestore/FileContentStore.java index 45fdfe9af7..16afa45bfe 100644 --- a/repository/src/main/java/org/alfresco/repo/content/filestore/FileContentStore.java +++ b/repository/src/main/java/org/alfresco/repo/content/filestore/FileContentStore.java @@ -1,58 +1,58 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.repo.content.filestore; -import java.io.File; -import java.io.IOException; -import java.io.Serializable; -import java.util.Collections; -import java.util.Map; - -import org.alfresco.api.AlfrescoPublicApi; -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.content.AbstractContentStore; -import org.alfresco.repo.content.ContentContext; -import org.alfresco.repo.content.ContentStore; -import org.alfresco.repo.content.ContentStoreCreatedEvent; -import org.alfresco.repo.content.EmptyContentReader; -import org.alfresco.repo.content.UnsupportedContentUrlException; -import org.alfresco.service.cmr.repository.ContentIOException; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.util.Deleter; -import org.alfresco.util.Pair; -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.context.event.ContextRefreshedEvent; +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.util.Collections; +import java.util.Map; + +import org.alfresco.api.AlfrescoPublicApi; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.content.AbstractContentStore; +import org.alfresco.repo.content.ContentContext; +import org.alfresco.repo.content.ContentStore; +import org.alfresco.repo.content.ContentStoreCreatedEvent; +import org.alfresco.repo.content.EmptyContentReader; +import org.alfresco.repo.content.UnsupportedContentUrlException; +import org.alfresco.service.cmr.repository.ContentIOException; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.util.Deleter; +import org.alfresco.util.Pair; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; /** * Provides a store of node content directly to the file system. The writers @@ -206,7 +206,7 @@ public class FileContentStore this.readOnly = readOnly; } - public void setFileContentUrlProvider(FileContentUrlProvider fileContentUrlProvider) + public void setFileContentUrlProvider(FileContentUrlProvider fileContentUrlProvider) { this.fileContentUrlProvider = fileContentUrlProvider; } diff --git a/repository/src/main/java/org/alfresco/repo/content/replication/AggregatingContentStore.java b/repository/src/main/java/org/alfresco/repo/content/replication/AggregatingContentStore.java index 15cc9c5246..e66829eaa5 100644 --- a/repository/src/main/java/org/alfresco/repo/content/replication/AggregatingContentStore.java +++ b/repository/src/main/java/org/alfresco/repo/content/replication/AggregatingContentStore.java @@ -27,6 +27,7 @@ package org.alfresco.repo.content.replication; import java.util.Date; import java.util.List; +import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -375,4 +376,36 @@ public class AggregatingContentStore extends AbstractContentStore readLock.unlock(); } } + + @Override + public boolean isStorageClassesSupported(Set storageClasses) + { + // Check the primary store + boolean isStorageClassesSupported = primaryStore.isStorageClassesSupported(storageClasses); + + if (!isStorageClassesSupported) + { + // Storage class is not supported by the primary store so we have to check the + // other stores + for (ContentStore store : secondaryStores) + { + isStorageClassesSupported = store.isDirectAccessSupported(); + + if (isStorageClassesSupported) + { + break; + } + } + } + + return isStorageClassesSupported; + } + + @Override + public Set getSupportedStorageClasses() + { + // We only need to provide info about the primary store, + // because the aggregating CS only allows to be written in the primary + return primaryStore.getSupportedStorageClasses(); + } } diff --git a/repository/src/main/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java b/repository/src/main/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java index 02c61a1563..4eb19339d8 100644 --- a/repository/src/main/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java +++ b/repository/src/main/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java @@ -1,51 +1,52 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.repo.tenant; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; - -import org.alfresco.repo.content.AbstractRoutingContentStore; -import org.alfresco.repo.content.ContentContext; -import org.alfresco.repo.content.ContentStore; -import org.alfresco.repo.domain.tenant.TenantAdminDAO; -import org.alfresco.repo.domain.tenant.TenantEntity; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.transaction.TransactionService; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +import org.alfresco.repo.content.AbstractRoutingContentStore; +import org.alfresco.repo.content.ContentContext; +import org.alfresco.repo.content.ContentStore; +import org.alfresco.repo.domain.tenant.TenantAdminDAO; +import org.alfresco.repo.domain.tenant.TenantEntity; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.transaction.TransactionService; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; /** * Content Store that supports tenant routing, if multi-tenancy is enabled. @@ -270,6 +271,18 @@ public abstract class AbstractTenantRoutingContentStore extends AbstractRoutingC return -1; } } - + + @Override + public boolean isStorageClassesSupported(Set storageClasses) + { + return getTenantContentStore().isStorageClassesSupported(storageClasses); + } + + @Override + public Set getSupportedStorageClasses() + { + return getTenantContentStore().getSupportedStorageClasses(); + } + protected abstract ContentStore initContentStore(ApplicationContext ctx, String contentRoot); } diff --git a/repository/src/main/java/org/alfresco/service/cmr/repository/ContentService.java b/repository/src/main/java/org/alfresco/service/cmr/repository/ContentService.java index 81622ff41a..8cc191ec31 100644 --- a/repository/src/main/java/org/alfresco/service/cmr/repository/ContentService.java +++ b/repository/src/main/java/org/alfresco/service/cmr/repository/ContentService.java @@ -25,13 +25,16 @@ */ package org.alfresco.service.cmr.repository; +import java.util.Collections; +import java.util.Date; +import java.util.Map; +import java.util.Set; + import org.alfresco.api.AlfrescoPublicApi; import org.alfresco.service.Auditable; import org.alfresco.service.cmr.dictionary.InvalidTypeException; import org.alfresco.service.namespace.QName; -import java.util.Date; - /** * Provides methods for accessing and transforming content. *

@@ -170,4 +173,62 @@ public interface ContentService */ @Auditable(parameters = {"nodeRef", "expiresAt"}) public DirectAccessUrl getDirectAccessUrl(NodeRef nodeRef, Date expiresAt); + + /** + * Checks whether or not the current {@link ContentService} supports the provided {@link Set} storage classes + * + * @param storageClasses The storage classes that will be checked whether or not are supported + * @return true if the storage classes are supported, false otherwise. + */ + default boolean isStorageClassesSupported(Set storageClasses) + { + return false; + } + + /** + * @return Returns the complete {@link Set} of supported storage classes by this {@link ContentService} + */ + default Set getSupportedStorageClasses() + { + return Collections.emptySet(); + } + + /** + * Updates the storage class for a {@link NodeRef} + * + * @param nodeRef The ref of the node that will have its storage classes updated + * @param storageClasses The new storage classes + * @param parameters extra parameters + */ + default void updateStorageClasses(NodeRef nodeRef, Set storageClasses, Map parameters) + { + + } + + /** + * @param nodeRef the {@link NodeRef} for which the storage classes are to be requested + * @return Returns the current storage classes for the given {@link NodeRef} + */ + default Set findStorageClasses(NodeRef nodeRef) + { + return Collections.emptySet(); + } + + /** + * @return Returns the complete collection of allowed storage classes transitions. + * The key represents the source storage classes while the value (as a {@link Set}) represents all the possible target storage classes. + */ + default Map, Set>> getStorageClassesTransitions() + { + return Collections.emptyMap(); + } + + /** + * @param nodeRef the {@link NodeRef} for which the storage classes transitions are to be requested + * @return Returns the complete collection of allowed storage classes transitions for the content found at content URL + */ + default Map, Set>> findStorageClassesTransitions(NodeRef nodeRef) + { + return Collections.emptyMap(); + } } diff --git a/repository/src/test/java/org/alfresco/repo/content/RoutingContentStoreTest.java b/repository/src/test/java/org/alfresco/repo/content/RoutingContentStoreTest.java index 4b534eb37a..fdfdc22039 100644 --- a/repository/src/test/java/org/alfresco/repo/content/RoutingContentStoreTest.java +++ b/repository/src/test/java/org/alfresco/repo/content/RoutingContentStoreTest.java @@ -1,52 +1,52 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.repo.content; -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.alfresco.repo.cache.DefaultSimpleCache; -import org.alfresco.repo.cache.SimpleCache; -import org.alfresco.repo.content.filestore.FileContentStore; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.test_category.OwnJVMTestsCategory; -import org.alfresco.util.Pair; -import org.alfresco.util.TempFileProvider; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.alfresco.repo.cache.DefaultSimpleCache; +import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.content.filestore.FileContentStore; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.test_category.OwnJVMTestsCategory; +import org.alfresco.util.Pair; +import org.alfresco.util.TempFileProvider; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; /** * Ensures that the routing of URLs based on context is working. A combination @@ -156,6 +156,12 @@ public class RoutingContentStoreTest extends AbstractWritableContentStoreTest assertTrue("Reader should be onto live content", reader.exists()); } } + + @Test + public void testIsStorageClassesSupported() + { + assertTrue(routingStore.isStorageClassesSupported(null)); + } /** * A test routing store that directs content writes to a randomly-chosen store. diff --git a/repository/src/test/java/org/alfresco/repo/content/caching/CachingContentStoreTest.java b/repository/src/test/java/org/alfresco/repo/content/caching/CachingContentStoreTest.java index ffac9eea8b..dbf9274cde 100644 --- a/repository/src/test/java/org/alfresco/repo/content/caching/CachingContentStoreTest.java +++ b/repository/src/test/java/org/alfresco/repo/content/caching/CachingContentStoreTest.java @@ -30,21 +30,24 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; - import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anySet; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.only; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.IOException; +import java.util.Collections; import java.util.Locale; +import java.util.Set; import org.alfresco.repo.content.ContentContext; import org.alfresco.repo.content.ContentStore; @@ -520,4 +523,22 @@ public class CachingContentStoreTest when(backingStore.getDirectAccessUrl(anyString(), any())).thenReturn(new DirectAccessUrl()); cachingStore.getDirectAccessUrl("url", null); } + + @Test + public void testBackingStoreIsCalledForSupportedStorageClasses() + { + when(backingStore.isStorageClassesSupported(anySet())).thenReturn(true); + + final Set storageClasses = Set.of("a-certain-storage-class"); + assertTrue(cachingStore.isStorageClassesSupported(storageClasses)); + verify(backingStore, times(1)).isStorageClassesSupported(storageClasses); + } + + @Test + public void testBackingStoreIsCalledForGetSupportedStorageClasses() + { + when(backingStore.getSupportedStorageClasses()).thenReturn(Collections.emptySet()); + assertTrue(cachingStore.getSupportedStorageClasses().isEmpty()); + verify(backingStore, times(1)).getSupportedStorageClasses(); + } } diff --git a/repository/src/test/java/org/alfresco/repo/content/filestore/FileContentStoreTest.java b/repository/src/test/java/org/alfresco/repo/content/filestore/FileContentStoreTest.java index 2e63f557f3..809185869f 100644 --- a/repository/src/test/java/org/alfresco/repo/content/filestore/FileContentStoreTest.java +++ b/repository/src/test/java/org/alfresco/repo/content/filestore/FileContentStoreTest.java @@ -1,34 +1,41 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.repo.content.filestore; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.File; import java.nio.ByteBuffer; import java.util.Locale; - +import java.util.Set; + import org.alfresco.repo.content.AbstractWritableContentStoreTest; import org.alfresco.repo.content.ContentContext; import org.alfresco.repo.content.ContentExistsException; @@ -45,12 +52,6 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; /** * Tests read and write functionality for the store. @@ -347,6 +348,18 @@ public class FileContentStoreTest extends AbstractWritableContentStoreTest assertTrue(reader instanceof SpoofedTextContentReader); assertEquals(1024L, reader.getContentString().getBytes("UTF-8").length); } + + @Test + public void testSupportsDefaultStorageClass() + { + assertTrue(store.isStorageClassesSupported(Set.of(ContentStore.DEFAULT_SC))); + } + + @Test + public void testDoesNotSupportUnknownStorageClass() + { + assertFalse(store.isStorageClassesSupported(Set.of("unknown"))); + } private void assertDirExists(File root, String dir) { diff --git a/repository/src/test/java/org/alfresco/repo/content/replication/AggregatingContentStoreTest.java b/repository/src/test/java/org/alfresco/repo/content/replication/AggregatingContentStoreTest.java index ecfaf17823..44390acc5a 100644 --- a/repository/src/test/java/org/alfresco/repo/content/replication/AggregatingContentStoreTest.java +++ b/repository/src/test/java/org/alfresco/repo/content/replication/AggregatingContentStoreTest.java @@ -25,9 +25,24 @@ */ package org.alfresco.repo.content.replication; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import java.io.File; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Set; import org.alfresco.repo.content.AbstractWritableContentStoreTest; import org.alfresco.repo.content.ContentContext; @@ -49,18 +64,6 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - /** * Tests read and write functionality for the aggregating store. *

@@ -309,4 +312,51 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes directAccessUrl = aggStore.getDirectAccessUrl("urlSecSupported", null); assertNotNull(directAccessUrl); } + + @Test + public void testIsStorageClassesSupported() + { + Set sc = Set.of("a-certain-storage-class"); + // Create the aggregating store + AggregatingContentStore aggStore = new AggregatingContentStore(); + aggStore.setPrimaryStore(primaryStoreMock); + aggStore.setSecondaryStores(List.of(secondaryStoreMock)); + + // SC supported by the primary store + when(primaryStoreMock.isStorageClassesSupported(sc)).thenReturn(true); + assertTrue(aggStore.isStorageClassesSupported(sc)); + verify(primaryStoreMock, times(1)).isStorageClassesSupported(sc); + verify(secondaryStoreMock, never()).isStorageClassesSupported(sc); + + // SC supported by the secondary store + when(primaryStoreMock.isStorageClassesSupported(sc)).thenReturn(false); + when(secondaryStoreMock.isStorageClassesSupported(sc)).thenReturn(true); + assertTrue(aggStore.isStorageClassesSupported(sc)); + verify(primaryStoreMock, times(1)).isStorageClassesSupported(sc); + verify(secondaryStoreMock, times(1)).isStorageClassesSupported(sc); + + // SC not supported by the stores + when(primaryStoreMock.isStorageClassesSupported(sc)).thenReturn(false); + when(secondaryStoreMock.isStorageClassesSupported(sc)).thenReturn(false); + assertFalse(aggStore.isStorageClassesSupported(sc)); + verify(primaryStoreMock, times(1)).isStorageClassesSupported(sc); + verify(secondaryStoreMock, times(1)).isStorageClassesSupported(sc); + } + + @Test + public void testGetSupportedStorageClasses() + { + Set sc = Collections.emptySet(); + // Create the aggregating store + AggregatingContentStore aggStore = new AggregatingContentStore(); + aggStore.setPrimaryStore(primaryStoreMock); + aggStore.setSecondaryStores(List.of(secondaryStoreMock)); + + when(primaryStoreMock.getSupportedStorageClasses()).thenReturn(sc); + + final Set supportedStorageClasses = aggStore.getSupportedStorageClasses(); + assertTrue(supportedStorageClasses.isEmpty()); + verify(primaryStoreMock, times(1)).getSupportedStorageClasses(); + verify(secondaryStoreMock, times(0)).getSupportedStorageClasses(); + } }