diff --git a/docs/modules/misc/pages/integrations/spring-boot.adoc b/docs/modules/misc/pages/integrations/spring-boot.adoc index 7d8dcb4b2..75761e4bf 100644 --- a/docs/modules/misc/pages/integrations/spring-boot.adoc +++ b/docs/modules/misc/pages/integrations/spring-boot.adoc @@ -66,6 +66,31 @@ You have the opportunity to perform actions on the _StorageManager_ or root obje - The _StorageManager_ is already started unless you specified the configuration value `one.microstream.auto-start=false`. - If you have used the `@Storage` annotation on a class, the _StorageManager_ is already associated with an instance of that class as the Root object. +It is also possible to obtain the entire configuration within the `StorageManagerConfiguration` Bean, enabling you to directly create a foundation and storage manager. This can be helpful if you need to stop storage at runtime and then restart it. + +[source] +---- +@Component +public class ConfigurationBeanInject +{ + @Autowired + StorageManagerConfiguration configuration; + + @Autowired + StorageManagerProvider provider; + + Root root = new Root(); + + void startStorage() + { + + EmbeddedStorageFoundation embeddedStorageFoundation = provider.embeddedStorageFoundation(configuration.getValues()); + EmbeddedStorageManager storage = embeddedStorageFoundation.start(root)); + ... + + } +---- + == Root object The root object can be indicated by using the `@Storage` annotation on the class. This annotation converts the POJO into a Spring bean (The annotation is a Spring Qualifier that makes the class also a _Component_). diff --git a/integrations/spring-boot/src/it/core-config/src/main/resources/application.properties b/integrations/spring-boot/src/it/core-config/src/main/resources/application.properties index 49527d01b..ba9aba371 100644 --- a/integrations/spring-boot/src/it/core-config/src/main/resources/application.properties +++ b/integrations/spring-boot/src/it/core-config/src/main/resources/application.properties @@ -9,4 +9,4 @@ one.microstream.data-file-minimum-use-ratio=0.9 one.microstream.data-file-cleanup-head-file=true one.microstream.entity-cache-timeout=1d -one.microstream.auto-start=false \ No newline at end of file +one.microstream.auto-start=false diff --git a/integrations/spring-boot/src/it/core-config/src/test/java/test/microstream/integrations/spring/boot/ConfigurationBeanTest.java b/integrations/spring-boot/src/it/core-config/src/test/java/test/microstream/integrations/spring/boot/ConfigurationBeanTest.java new file mode 100644 index 000000000..c1084e99e --- /dev/null +++ b/integrations/spring-boot/src/it/core-config/src/test/java/test/microstream/integrations/spring/boot/ConfigurationBeanTest.java @@ -0,0 +1,85 @@ +package test.microstream.integrations.spring.boot; + +/*- + * #%L + * microstream-integrations-spring-boot + * %% + * Copyright (C) 2019 - 2023 MicroStream Software + * %% + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License, v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is + * available at https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + * #L% + */ + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import one.microstream.integrations.spring.boot.types.config.StorageManagerConfiguration; +import one.microstream.integrations.spring.boot.types.config.StorageManagerProvider; +import one.microstream.storage.embedded.types.EmbeddedStorageFoundation; +import one.microstream.storage.embedded.types.EmbeddedStorageManager; + +@SpringBootTest +public class ConfigurationBeanTest +{ + @Autowired + StorageManagerConfiguration configuration; + + @Autowired + StorageManagerProvider provider; + + private final String TEST_SENTENCE = "I love MicroStream Spring Extension"; + + @Test + void testReloadData() + { + Assertions.assertNotNull(configuration); + Assertions.assertNotNull(provider); + + EmbeddedStorageFoundation embeddedStorageFoundation = provider.embeddedStorageFoundation(configuration.getValues()); + TestData data = new TestData(TEST_SENTENCE); + try (EmbeddedStorageManager storage = embeddedStorageFoundation.start(data)) { + } + + EmbeddedStorageFoundation embeddedStorageFoundation2 = provider.embeddedStorageFoundation(configuration.getValues()); + TestData data2 = new TestData(); + try (EmbeddedStorageManager storage2 = embeddedStorageFoundation2.start(data2)) { + Assertions.assertEquals(TEST_SENTENCE, data2.getValue()); + } + } + + @Test + void testReloadDataWithoutFoundation() + { + Assertions.assertNotNull(configuration); + Assertions.assertNotNull(provider); + + try (EmbeddedStorageManager storageManager = provider.create(configuration.getValues())) { + TestData data = new TestData(TEST_SENTENCE); + storageManager.start(); + storageManager.setRoot(data); + storageManager.storeRoot(); + } + + try (EmbeddedStorageManager storageManager2 = provider.create(configuration.getValues())) { + + TestData data2 = new TestData(); + storageManager2.start(); + TestData readData = (TestData) storageManager2.root(); + Assertions.assertEquals(TEST_SENTENCE, readData.getValue()); + + } + } + +} diff --git a/integrations/spring-boot/src/it/core-config/src/test/java/test/microstream/integrations/spring/boot/TestData.java b/integrations/spring-boot/src/it/core-config/src/test/java/test/microstream/integrations/spring/boot/TestData.java new file mode 100644 index 000000000..393edddc5 --- /dev/null +++ b/integrations/spring-boot/src/it/core-config/src/test/java/test/microstream/integrations/spring/boot/TestData.java @@ -0,0 +1,40 @@ +package test.microstream.integrations.spring.boot; + +/*- + * #%L + * microstream-integrations-spring-boot + * %% + * Copyright (C) 2019 - 2023 MicroStream Software + * %% + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License, v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is + * available at https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + * #L% + */ + +public class TestData +{ + private String value = ""; + + public TestData() + { + } + + public TestData(String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } +} diff --git a/integrations/spring-boot/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerConfiguration.java b/integrations/spring-boot/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerConfiguration.java new file mode 100644 index 000000000..0e051eaa2 --- /dev/null +++ b/integrations/spring-boot/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerConfiguration.java @@ -0,0 +1,43 @@ +package one.microstream.integrations.spring.boot.types.config; + +/*- + * #%L + * microstream-integrations-spring-boot + * %% + * Copyright (C) 2019 - 2023 MicroStream Software + * %% + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License, v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is + * available at https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + * #L% + */ + +import java.util.HashMap; +import java.util.Map; + +public class StorageManagerConfiguration +{ + private Map values; + + public StorageManagerConfiguration() + { + } + + public StorageManagerConfiguration(Map values) + { + this.values = values; + } + + public Map getValues() + { + return values; + } +} diff --git a/integrations/spring-boot/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerFactory.java b/integrations/spring-boot/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerFactory.java index fc05c21ae..5f331841f 100644 --- a/integrations/spring-boot/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerFactory.java +++ b/integrations/spring-boot/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerFactory.java @@ -40,6 +40,13 @@ public StorageManagerFactory(final StorageManagerProvider storageManagerProvider this.storageManagerProvider = storageManagerProvider; } + @Bean + @Lazy + @Primary + public StorageManagerConfiguration storageManagerConfiguration () + { + return this.storageManagerProvider.prepareConfiguration(); + } @Bean @Lazy diff --git a/integrations/spring-boot/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerProvider.java b/integrations/spring-boot/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerProvider.java index bd73d5db9..42b90cca3 100644 --- a/integrations/spring-boot/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerProvider.java +++ b/integrations/spring-boot/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerProvider.java @@ -87,7 +87,7 @@ public StorageManagerProvider( this.applicationContext = applicationContext; } - private Map readProperties(final String qualifier) + protected Map readProperties(final String qualifier) { final MutablePropertySources sources = ((AbstractEnvironment) this.env).getPropertySources(); @@ -118,11 +118,28 @@ public EmbeddedStorageManager get(final String qualifier) return this.storageManagers.computeIfAbsent(qualifier, this::create); } - private EmbeddedStorageManager create(final String qualifier) + public EmbeddedStorageManager get(final String qualifier, Map values) + { + return this.storageManagers.computeIfAbsent(qualifier, this::create); + } + + protected StorageManagerConfiguration prepareConfiguration(final String qualifier) + { + return new StorageManagerConfiguration(this.normalizeProperties(this.readProperties(qualifier))); + } + + protected StorageManagerConfiguration prepareConfiguration() { + return new StorageManagerConfiguration(this.normalizeProperties(this.readProperties(PRIMARY_QUALIFIER))); + } - final Map values = this.normalizeProperties(this.readProperties(qualifier)); + public EmbeddedStorageManager create(Map values) + { + return create(PRIMARY_QUALIFIER, values); + } + public EmbeddedStorageManager create(final String qualifier, Map values) + { final EmbeddedStorageFoundation embeddedStorageFoundation = this.embeddedStorageFoundation(qualifier, values); final MicrostreamConfigurationProperties configuration = new MicrostreamConfigurationProperties(); @@ -131,8 +148,6 @@ private EmbeddedStorageManager create(final String qualifier) Binder.get(new EnvironmentFromMap(values)) .bind("", Bindable.ofInstance(configuration)); - embeddedStorageFoundation.getConnectionFoundation() - .setClassLoaderProvider(typeName -> this.applicationContext.getClassLoader()); ByQualifier.filter(this.customizers, qualifier) .forEach(c -> c.customize(embeddedStorageFoundation)); @@ -148,12 +163,20 @@ private EmbeddedStorageManager create(final String qualifier) if (!this.hasRootDefined(qualifier)) { // No @Storage,so we need to execute initializers now. - // Otherwise the StorageBeanFactory.createRootObject is responsible for calling the + // Otherwise, the StorageBeanFactory.createRootObject is responsible for calling the ByQualifier.filter(this.initializers, qualifier) .forEach(i -> i.initialize(storageManager)); } return storageManager; + + } + + private EmbeddedStorageManager create(final String qualifier) + { + final Map values = prepareConfiguration(qualifier).getValues(); + + return create(qualifier, values); } private boolean hasRootDefined(final String qualifier) @@ -175,33 +198,32 @@ private boolean hasRootDefined(final String qualifier) public EmbeddedStorageFoundation embeddedStorageFoundation() { - final Map values = this.normalizeProperties(this.readProperties(PRIMARY_QUALIFIER)); + final Map values = prepareConfiguration(PRIMARY_QUALIFIER).getValues(); + return this.embeddedStorageFoundation(PRIMARY_QUALIFIER, values); + } + + public EmbeddedStorageFoundation embeddedStorageFoundation(final Map values) + { return this.embeddedStorageFoundation(PRIMARY_QUALIFIER, values); } - private EmbeddedStorageFoundation embeddedStorageFoundation(final String qualifier, final Map values) + public EmbeddedStorageFoundation embeddedStorageFoundation(final String qualifier, final Map values) { final EmbeddedStorageConfigurationBuilder builder = EmbeddedStorageConfigurationBuilder.New(); this.logger.debug("MicroStream configuration items: "); - values.forEach((key, value) -> - { - if (value != null) - { - if (key.contains("password")) - { - this.logger.debug(key + " : xxxxxx"); - } - else - { - this.logger.debug(key + " : " + value); - } - builder.set(key, value); - } - }); + values.forEach((key, value) -> { + if (value != null) { + String logValue = key.contains("password") ? "xxxxxx" : value; + this.logger.debug(key + " : " + logValue); + builder.set(key, value); + } + }); final EmbeddedStorageFoundation storageFoundation = builder.createEmbeddedStorageFoundation(); + storageFoundation.getConnectionFoundation() + .setClassLoaderProvider(typeName -> this.applicationContext.getClassLoader()); storageFoundation.setDataBaseName(qualifier); return storageFoundation; diff --git a/integrations/spring-boot3/src/it/core-config/src/test/java/test/microstream/integrations/spring/boot/ConfigurationBeanTest.java b/integrations/spring-boot3/src/it/core-config/src/test/java/test/microstream/integrations/spring/boot/ConfigurationBeanTest.java new file mode 100644 index 000000000..92f38561e --- /dev/null +++ b/integrations/spring-boot3/src/it/core-config/src/test/java/test/microstream/integrations/spring/boot/ConfigurationBeanTest.java @@ -0,0 +1,65 @@ +package test.microstream.integrations.spring.boot; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import one.microstream.integrations.spring.boot.types.config.StorageManagerConfiguration; +import one.microstream.integrations.spring.boot.types.config.StorageManagerProvider; +import one.microstream.storage.embedded.types.EmbeddedStorageFoundation; +import one.microstream.storage.embedded.types.EmbeddedStorageManager; + +@SpringBootTest +public class ConfigurationBeanTest +{ + @Autowired + StorageManagerConfiguration configuration; + + @Autowired + StorageManagerProvider provider; + + private final String TEST_SENTENCE = "I love MicroStream Spring Extension"; + + @Test + void testReloadData() + { + Assertions.assertNotNull(configuration); + Assertions.assertNotNull(provider); + + EmbeddedStorageFoundation embeddedStorageFoundation = provider.embeddedStorageFoundation(configuration.getValues()); + TestData data = new TestData(TEST_SENTENCE); + try (EmbeddedStorageManager storage = embeddedStorageFoundation.start(data)) { + } + + EmbeddedStorageFoundation embeddedStorageFoundation2 = provider.embeddedStorageFoundation(configuration.getValues()); + TestData data2 = new TestData(); + try (EmbeddedStorageManager storage2 = embeddedStorageFoundation2.start(data2)) { + Assertions.assertEquals(TEST_SENTENCE, data2.getValue()); + } + } + + @Test + void testReloadDataWithoutFoundation() + { + Assertions.assertNotNull(configuration); + Assertions.assertNotNull(provider); + + try (EmbeddedStorageManager storageManager = provider.create(configuration.getValues())) { + TestData data = new TestData(TEST_SENTENCE); + storageManager.start(); + storageManager.setRoot(data); + storageManager.storeRoot(); + } + + try (EmbeddedStorageManager storageManager2 = provider.create(configuration.getValues())) { + + TestData data2 = new TestData(); + storageManager2.start(); + TestData readData = (TestData) storageManager2.root(); + Assertions.assertEquals(TEST_SENTENCE, readData.getValue()); + + } + } + +} diff --git a/integrations/spring-boot3/src/it/core-config/src/test/java/test/microstream/integrations/spring/boot/TestData.java b/integrations/spring-boot3/src/it/core-config/src/test/java/test/microstream/integrations/spring/boot/TestData.java new file mode 100644 index 000000000..ddb0f0fc4 --- /dev/null +++ b/integrations/spring-boot3/src/it/core-config/src/test/java/test/microstream/integrations/spring/boot/TestData.java @@ -0,0 +1,20 @@ +package test.microstream.integrations.spring.boot; + +public class TestData +{ + private String value = ""; + + public TestData() + { + } + + public TestData(String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } +} diff --git a/integrations/spring-boot3/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerConfiguration.java b/integrations/spring-boot3/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerConfiguration.java new file mode 100644 index 000000000..c3fc87503 --- /dev/null +++ b/integrations/spring-boot3/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerConfiguration.java @@ -0,0 +1,42 @@ +package one.microstream.integrations.spring.boot.types.config; + +/*- + * #%L + * microstream-integrations-spring-boot + * %% + * Copyright (C) 2019 - 2023 MicroStream Software + * %% + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License, v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is + * available at https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + * #L% + */ + +import java.util.Map; + +public class StorageManagerConfiguration +{ + private Map values; + + public StorageManagerConfiguration() + { + } + + public StorageManagerConfiguration(Map values) + { + this.values = values; + } + + public Map getValues() + { + return values; + } +} diff --git a/integrations/spring-boot3/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerFactory.java b/integrations/spring-boot3/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerFactory.java index ca5579d8a..068bb7038 100644 --- a/integrations/spring-boot3/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerFactory.java +++ b/integrations/spring-boot3/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerFactory.java @@ -40,6 +40,13 @@ public StorageManagerFactory(final StorageManagerProvider storageManagerProvider this.storageManagerProvider = storageManagerProvider; } + @Bean + @Lazy + @Primary + public StorageManagerConfiguration storageManagerConfiguration () + { + return this.storageManagerProvider.prepareConfiguration(); + } @Bean @Lazy diff --git a/integrations/spring-boot3/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerProvider.java b/integrations/spring-boot3/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerProvider.java index e1425a6db..94dc1ab8c 100644 --- a/integrations/spring-boot3/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerProvider.java +++ b/integrations/spring-boot3/src/main/java/one/microstream/integrations/spring/boot/types/config/StorageManagerProvider.java @@ -87,7 +87,7 @@ public StorageManagerProvider( this.applicationContext = applicationContext; } - private Map readProperties(final String qualifier) + protected Map readProperties(final String qualifier) { final MutablePropertySources sources = ((AbstractEnvironment) this.env).getPropertySources(); @@ -118,11 +118,28 @@ public EmbeddedStorageManager get(final String qualifier) return this.storageManagers.computeIfAbsent(qualifier, this::create); } - private EmbeddedStorageManager create(final String qualifier) + public EmbeddedStorageManager get(final String qualifier, Map values) { + return this.storageManagers.computeIfAbsent(qualifier, this::create); + } - final Map values = this.normalizeProperties(this.readProperties(qualifier)); + protected StorageManagerConfiguration prepareConfiguration(final String qualifier) + { + return new StorageManagerConfiguration(this.normalizeProperties(this.readProperties(qualifier))); + } + protected StorageManagerConfiguration prepareConfiguration() + { + return new StorageManagerConfiguration(this.normalizeProperties(this.readProperties(PRIMARY_QUALIFIER))); + } + + public EmbeddedStorageManager create(Map values) + { + return create(PRIMARY_QUALIFIER, values); + } + + public EmbeddedStorageManager create(final String qualifier, Map values) + { final EmbeddedStorageFoundation embeddedStorageFoundation = this.embeddedStorageFoundation(qualifier, values); final MicrostreamConfigurationProperties configuration = new MicrostreamConfigurationProperties(); @@ -131,8 +148,6 @@ private EmbeddedStorageManager create(final String qualifier) Binder.get(new EnvironmentFromMap(values)) .bind("", Bindable.ofInstance(configuration)); - embeddedStorageFoundation.getConnectionFoundation() - .setClassLoaderProvider(typeName -> this.applicationContext.getClassLoader()); ByQualifier.filter(this.customizers, qualifier) .forEach(c -> c.customize(embeddedStorageFoundation)); @@ -148,12 +163,20 @@ private EmbeddedStorageManager create(final String qualifier) if (!this.hasRootDefined(qualifier)) { // No @Storage,so we need to execute initializers now. - // Otherwise the StorageBeanFactory.createRootObject is responsible for calling the + // Otherwise, the StorageBeanFactory.createRootObject is responsible for calling the ByQualifier.filter(this.initializers, qualifier) .forEach(i -> i.initialize(storageManager)); } return storageManager; + + } + + private EmbeddedStorageManager create(final String qualifier) + { + final Map values = prepareConfiguration(qualifier).getValues(); + + return create(qualifier, values); } private boolean hasRootDefined(final String qualifier) @@ -175,33 +198,33 @@ private boolean hasRootDefined(final String qualifier) public EmbeddedStorageFoundation embeddedStorageFoundation() { - final Map values = this.normalizeProperties(this.readProperties(PRIMARY_QUALIFIER)); + final Map values = prepareConfiguration(PRIMARY_QUALIFIER).getValues(); return this.embeddedStorageFoundation(PRIMARY_QUALIFIER, values); } - private EmbeddedStorageFoundation embeddedStorageFoundation(final String qualifier, final Map values) + public EmbeddedStorageFoundation embeddedStorageFoundation(final Map values) + { + return this.embeddedStorageFoundation(PRIMARY_QUALIFIER, values); + } + + public EmbeddedStorageFoundation embeddedStorageFoundation(final String qualifier, final Map values) { final EmbeddedStorageConfigurationBuilder builder = EmbeddedStorageConfigurationBuilder.New(); this.logger.debug("MicroStream configuration items: "); values.forEach((key, value) -> - { - if (value != null) - { - if (key.contains("password")) - { - this.logger.debug(key + " : xxxxxx"); - } - else - { - this.logger.debug(key + " : " + value); - } - builder.set(key, value); - } - }); + { + if (value != null) { + String logValue = key.contains("password") ? "xxxxxx" : value; + this.logger.debug(key + " : " + logValue); + builder.set(key, value); + } + }); final EmbeddedStorageFoundation storageFoundation = builder.createEmbeddedStorageFoundation(); + storageFoundation.getConnectionFoundation() + .setClassLoaderProvider(typeName -> this.applicationContext.getClassLoader()); storageFoundation.setDataBaseName(qualifier); return storageFoundation;