Repository: james-project Updated Branches: refs/heads/master e00ebe12c -> 956ee5a32
JAMES-2582 declare module to bind blobstore impl by properties file configuration Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/956ee5a3 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/956ee5a3 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/956ee5a3 Branch: refs/heads/master Commit: 956ee5a32c9054a5e2b5c097ffec06b52b37a39a Parents: e6691f5 Author: duc <dt...@linagora.com> Authored: Sun Nov 4 21:16:19 2018 +0700 Committer: Antoine Duprat <adup...@linagora.com> Committed: Mon Nov 5 17:35:58 2018 +0100 ---------------------------------------------------------------------- .../objectstorage/FakePropertiesProvider.java | 2 +- .../guice/cassandra-rabbitmq-guice/pom.xml | 10 ++ .../BlobStoreChoosingConfiguration.java | 2 +- .../objectstore/BlobStoreChoosingModule.java | 133 +++++++++++++++++++ .../BlobStoreChoosingConfigurationTest.java | 10 +- .../BlobStoreChoosingModuleTest.java | 114 ++++++++++++++++ 6 files changed, 264 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/956ee5a3/server/container/guice/blob-objectstorage-guice/src/test/java/org/apache/james/modules/objectstorage/FakePropertiesProvider.java ---------------------------------------------------------------------- diff --git a/server/container/guice/blob-objectstorage-guice/src/test/java/org/apache/james/modules/objectstorage/FakePropertiesProvider.java b/server/container/guice/blob-objectstorage-guice/src/test/java/org/apache/james/modules/objectstorage/FakePropertiesProvider.java index cff47ee..b4ca08b 100644 --- a/server/container/guice/blob-objectstorage-guice/src/test/java/org/apache/james/modules/objectstorage/FakePropertiesProvider.java +++ b/server/container/guice/blob-objectstorage-guice/src/test/java/org/apache/james/modules/objectstorage/FakePropertiesProvider.java @@ -55,7 +55,7 @@ public class FakePropertiesProvider extends PropertiesProvider { return new FakePropertiesProviderBuilder(); } - static class FakePropertiesProviderBuilder { + public static class FakePropertiesProviderBuilder { private final ImmutableMap.Builder<String, Configuration> configurations; public FakePropertiesProviderBuilder() { http://git-wip-us.apache.org/repos/asf/james-project/blob/956ee5a3/server/container/guice/cassandra-rabbitmq-guice/pom.xml ---------------------------------------------------------------------- diff --git a/server/container/guice/cassandra-rabbitmq-guice/pom.xml b/server/container/guice/cassandra-rabbitmq-guice/pom.xml index 47d1112..79ded79 100644 --- a/server/container/guice/cassandra-rabbitmq-guice/pom.xml +++ b/server/container/guice/cassandra-rabbitmq-guice/pom.xml @@ -83,6 +83,16 @@ </dependency> <dependency> <groupId>${james.groupId}</groupId> + <artifactId>blob-objectstorage-guice</artifactId> + </dependency> + <dependency> + <groupId>${james.groupId}</groupId> + <artifactId>blob-objectstorage-guice</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>${james.groupId}</groupId> <artifactId>james-server-cassandra-guice</artifactId> </dependency> <dependency> http://git-wip-us.apache.org/repos/asf/james-project/blob/956ee5a3/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/objectstore/BlobStoreChoosingConfiguration.java ---------------------------------------------------------------------- diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/objectstore/BlobStoreChoosingConfiguration.java b/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/objectstore/BlobStoreChoosingConfiguration.java index 2d9fc5c..f980530 100644 --- a/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/objectstore/BlobStoreChoosingConfiguration.java +++ b/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/objectstore/BlobStoreChoosingConfiguration.java @@ -37,7 +37,7 @@ public class BlobStoreChoosingConfiguration { static String supportedImplNames() { return Stream.of(BlobStoreImplName.values()) .map(BlobStoreImplName::getName) - .collect(Collectors.joining(",")); + .collect(Collectors.joining(", ")); } static BlobStoreImplName from(String name) { http://git-wip-us.apache.org/repos/asf/james-project/blob/956ee5a3/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/objectstore/BlobStoreChoosingModule.java ---------------------------------------------------------------------- diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/objectstore/BlobStoreChoosingModule.java b/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/objectstore/BlobStoreChoosingModule.java new file mode 100644 index 0000000..a6467e0 --- /dev/null +++ b/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/objectstore/BlobStoreChoosingModule.java @@ -0,0 +1,133 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.modules.objectstore; + +import java.io.FileNotFoundException; +import java.util.function.Supplier; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.james.backends.cassandra.components.CassandraModule; +import org.apache.james.backends.cassandra.init.configuration.CassandraConfiguration; +import org.apache.james.blob.api.BlobStore; +import org.apache.james.blob.api.HashBlobId; +import org.apache.james.blob.cassandra.CassandraBlobModule; +import org.apache.james.blob.cassandra.CassandraBlobsDAO; +import org.apache.james.blob.objectstorage.PayloadCodec; +import org.apache.james.modules.objectstorage.ObjectStorageBlobsDAOProvider; +import org.apache.james.modules.objectstorage.PayloadCodecProvider; +import org.apache.james.utils.PropertiesProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.datastax.driver.core.Session; +import com.google.common.annotations.VisibleForTesting; +import com.google.inject.AbstractModule; +import com.google.inject.Provides; +import com.google.inject.Scopes; +import com.google.inject.multibindings.Multibinder; + +public class BlobStoreChoosingModule extends AbstractModule { + + interface BlobStoreFactory extends Supplier<BlobStore> {} + + static class CassandraBlobStoreFactory implements BlobStoreFactory { + private final Session session; + private final CassandraConfiguration configuration; + private final HashBlobId.Factory blobIdFactory; + + @Inject + CassandraBlobStoreFactory(Session session, CassandraConfiguration configuration, HashBlobId.Factory blobIdFactory) { + this.session = session; + this.configuration = configuration; + this.blobIdFactory = blobIdFactory; + } + + @Override + public BlobStore get() { + return new CassandraBlobsDAO( + session, + configuration, + blobIdFactory); + } + } + + static class SwiftBlobStoreFactory implements BlobStoreFactory { + private final ObjectStorageBlobsDAOProvider blobsDAOProvider; + + @Inject + SwiftBlobStoreFactory(ObjectStorageBlobsDAOProvider blobsDAOProvider) { + this.blobsDAOProvider = blobsDAOProvider; + } + + @Override + public BlobStore get() { + return blobsDAOProvider.get(); + } + } + + private static final Logger LOGGER = LoggerFactory.getLogger(BlobStoreChoosingModule.class); + + static final String BLOBSTORE_CONFIGURATION_NAME = "objectstore"; + + @Override + protected void configure() { + bind(SwiftBlobStoreFactory.class).in(Scopes.SINGLETON); + bind(PayloadCodec.class).toProvider(PayloadCodecProvider.class).in(Scopes.SINGLETON); + + bind(CassandraBlobStoreFactory.class).in(Scopes.SINGLETON); + Multibinder<CassandraModule> cassandraDataDefinitions = Multibinder.newSetBinder(binder(), CassandraModule.class); + cassandraDataDefinitions.addBinding().toInstance(CassandraBlobModule.MODULE); + } + + @VisibleForTesting + @Provides + @Singleton + BlobStoreFactory provideBlobStoreFactory(PropertiesProvider propertiesProvider, + CassandraBlobStoreFactory cassandraBlobStoreFactory, + SwiftBlobStoreFactory swiftBlobStoreFactory) throws ConfigurationException { + try { + Configuration configuration = propertiesProvider.getConfiguration(BLOBSTORE_CONFIGURATION_NAME); + BlobStoreChoosingConfiguration choosingConfiguration = BlobStoreChoosingConfiguration.from(configuration); + switch (choosingConfiguration.getImplementation()) { + case SWIFT: + return swiftBlobStoreFactory; + case CASSANDRA: + return cassandraBlobStoreFactory; + default: + throw new RuntimeException(String.format("can not get the right blobstore provider with configuration %s", + choosingConfiguration.toString())); + } + } catch (FileNotFoundException e) { + LOGGER.warn("Could not find " + BLOBSTORE_CONFIGURATION_NAME + " configuration file, using cassandra blobstore as the default"); + return cassandraBlobStoreFactory; + } + } + + @Provides + @Singleton + private BlobStore provideBlobStore(BlobStoreFactory blobStoreFactory) { + return blobStoreFactory.get(); + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/956ee5a3/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/objectstore/BlobStoreChoosingConfigurationTest.java ---------------------------------------------------------------------- diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/objectstore/BlobStoreChoosingConfigurationTest.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/objectstore/BlobStoreChoosingConfigurationTest.java index 34b4b7a..a32fba8 100644 --- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/objectstore/BlobStoreChoosingConfigurationTest.java +++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/objectstore/BlobStoreChoosingConfigurationTest.java @@ -36,17 +36,17 @@ class BlobStoreChoosingConfigurationTest { assertThatThrownBy(() -> BlobStoreChoosingConfiguration.from(configuration)) .isInstanceOf(IllegalStateException.class) - .hasMessage("objectstore.implementation property is missing please use one of supported values in: cassandra,swift"); + .hasMessage("objectstore.implementation property is missing please use one of supported values in: cassandra, swift"); } @Test - void fromShouldReturnSwiftWhenBlobStoreImplIsNull() { + void fromShouldThrowWhenBlobStoreImplIsNull() { PropertiesConfiguration configuration = new PropertiesConfiguration(); configuration.addProperty("objectstore.implementation", null); assertThatThrownBy(() -> BlobStoreChoosingConfiguration.from(configuration)) .isInstanceOf(IllegalStateException.class) - .hasMessage("objectstore.implementation property is missing please use one of supported values in: cassandra,swift"); + .hasMessage("objectstore.implementation property is missing please use one of supported values in: cassandra, swift"); } @Test @@ -56,7 +56,7 @@ class BlobStoreChoosingConfigurationTest { assertThatThrownBy(() -> BlobStoreChoosingConfiguration.from(configuration)) .isInstanceOf(IllegalStateException.class) - .hasMessage("objectstore.implementation property is missing please use one of supported values in: cassandra,swift"); + .hasMessage("objectstore.implementation property is missing please use one of supported values in: cassandra, swift"); } @Test @@ -66,7 +66,7 @@ class BlobStoreChoosingConfigurationTest { assertThatThrownBy(() -> BlobStoreChoosingConfiguration.from(configuration)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("un_supported is not a valid name of BlobStores, please use one of supported values in: cassandra,swift"); + .hasMessage("un_supported is not a valid name of BlobStores, please use one of supported values in: cassandra, swift"); } @Test http://git-wip-us.apache.org/repos/asf/james-project/blob/956ee5a3/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/objectstore/BlobStoreChoosingModuleTest.java ---------------------------------------------------------------------- diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/objectstore/BlobStoreChoosingModuleTest.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/objectstore/BlobStoreChoosingModuleTest.java new file mode 100644 index 0000000..d6a575d --- /dev/null +++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/objectstore/BlobStoreChoosingModuleTest.java @@ -0,0 +1,114 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.modules.objectstore; + +import static org.apache.james.modules.objectstore.BlobStoreChoosingModule.BLOBSTORE_CONFIGURATION_NAME; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; + +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.james.modules.objectstorage.FakePropertiesProvider; +import org.apache.james.modules.objectstore.BlobStoreChoosingConfiguration.BlobStoreImplName; +import org.apache.james.modules.objectstore.BlobStoreChoosingModule.CassandraBlobStoreFactory; +import org.apache.james.modules.objectstore.BlobStoreChoosingModule.SwiftBlobStoreFactory; +import org.junit.jupiter.api.Test; + +class BlobStoreChoosingModuleTest { + + private static CassandraBlobStoreFactory CASSANDRA_BLOBSTORE_FACTORY = mock(CassandraBlobStoreFactory.class); + private static SwiftBlobStoreFactory SWIFT_BLOBSTORE_FACTORY = mock(SwiftBlobStoreFactory.class); + + @Test + void provideBlobStoreFactoryShouldThrowWhenMissingPropertyField() throws Exception { + BlobStoreChoosingModule module = new BlobStoreChoosingModule(); + PropertiesConfiguration configuration = new PropertiesConfiguration(); + configuration.addProperty("objectstore.implementation", ""); + FakePropertiesProvider propertyProvider = FakePropertiesProvider.builder() + .register(BLOBSTORE_CONFIGURATION_NAME, configuration) + .build(); + + assertThatThrownBy(() -> module.provideBlobStoreFactory(propertyProvider, CASSANDRA_BLOBSTORE_FACTORY, SWIFT_BLOBSTORE_FACTORY)) + .isInstanceOf(IllegalStateException.class); + } + + @Test + void provideBlobStoreFactoryShouldThrowWhenEmptyPropertyField() throws Exception { + BlobStoreChoosingModule module = new BlobStoreChoosingModule(); + PropertiesConfiguration configuration = new PropertiesConfiguration(); + configuration.addProperty("objectstore.implementation", ""); + FakePropertiesProvider propertyProvider = FakePropertiesProvider.builder() + .register(BLOBSTORE_CONFIGURATION_NAME, configuration) + .build(); + + assertThatThrownBy(() -> module.provideBlobStoreFactory(propertyProvider, CASSANDRA_BLOBSTORE_FACTORY, SWIFT_BLOBSTORE_FACTORY)) + .isInstanceOf(IllegalStateException.class); + } + + @Test + void provideBlobStoreFactoryShouldThrowWhenPropertyFieldIsNotInSupportedList() throws Exception { + BlobStoreChoosingModule module = new BlobStoreChoosingModule(); + PropertiesConfiguration configuration = new PropertiesConfiguration(); + configuration.addProperty("objectstore.implementation", "gabouzomeuh"); + FakePropertiesProvider propertyProvider = FakePropertiesProvider.builder() + .register(BLOBSTORE_CONFIGURATION_NAME, configuration) + .build(); + + assertThatThrownBy(() -> module.provideBlobStoreFactory(propertyProvider, CASSANDRA_BLOBSTORE_FACTORY, SWIFT_BLOBSTORE_FACTORY)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void provideBlobStoreFactoryShouldReturnCassandraWhenNoFile() throws Exception { + BlobStoreChoosingModule module = new BlobStoreChoosingModule(); + FakePropertiesProvider propertyProvider = FakePropertiesProvider.builder() + .register("other_configuration_file", new PropertiesConfiguration()) + .build(); + + assertThat(module.provideBlobStoreFactory(propertyProvider, CASSANDRA_BLOBSTORE_FACTORY, SWIFT_BLOBSTORE_FACTORY)) + .isEqualTo(CASSANDRA_BLOBSTORE_FACTORY); + } + + @Test + void provideBlobStoreFactoryShouldReturnSwiftFactoryWhenConfigurationImplIsSwift() throws Exception { + BlobStoreChoosingModule module = new BlobStoreChoosingModule(); + PropertiesConfiguration configuration = new PropertiesConfiguration(); + configuration.addProperty("objectstore.implementation", BlobStoreImplName.SWIFT.getName()); + FakePropertiesProvider propertyProvider = FakePropertiesProvider.builder() + .register(BLOBSTORE_CONFIGURATION_NAME, configuration) + .build(); + + assertThat(module.provideBlobStoreFactory(propertyProvider, CASSANDRA_BLOBSTORE_FACTORY, SWIFT_BLOBSTORE_FACTORY)) + .isEqualTo(SWIFT_BLOBSTORE_FACTORY); + } + + @Test + void provideBlobStoreFactoryShouldReturnCassandraFactoryWhenConfigurationImplIsCassandra() throws Exception { + BlobStoreChoosingModule module = new BlobStoreChoosingModule(); + PropertiesConfiguration configuration = new PropertiesConfiguration(); + configuration.addProperty("objectstore.implementation", BlobStoreImplName.CASSANDRA.getName()); + FakePropertiesProvider propertyProvider = FakePropertiesProvider.builder() + .register(BLOBSTORE_CONFIGURATION_NAME, configuration) + .build(); + + assertThat(module.provideBlobStoreFactory(propertyProvider, CASSANDRA_BLOBSTORE_FACTORY, SWIFT_BLOBSTORE_FACTORY)) + .isEqualTo(CASSANDRA_BLOBSTORE_FACTORY); + } +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org