diff --git a/crane4j-core/src/main/java/cn/crane4j/core/container/ImmutableMapContainer.java b/crane4j-core/src/main/java/cn/crane4j/core/container/ImmutableMapContainer.java index 4c2679a4..ba2c0a18 100644 --- a/crane4j-core/src/main/java/cn/crane4j/core/container/ImmutableMapContainer.java +++ b/crane4j-core/src/main/java/cn/crane4j/core/container/ImmutableMapContainer.java @@ -1,9 +1,9 @@ package cn.crane4j.core.container; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; -import lombok.RequiredArgsConstructor; import org.checkerframework.checker.nullness.qual.NonNull; import java.util.Collection; @@ -30,7 +30,7 @@ * @since 2.0.0 */ @EqualsAndHashCode -@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) public class ImmutableMapContainer implements LimitedContainer, Container.Lifecycle { /** @@ -42,7 +42,7 @@ public class ImmutableMapContainer implements LimitedContainer, Container. /** * data source objects grouped by key value */ - private final Map data; + private volatile Map data; /** *

Create a key-value pair container based on the specified {@link Map} instance. @@ -79,6 +79,17 @@ public static ImmutableMapContainer forMap(String namespace, @NonNull Map return data; } + /** + * Refresh the container with new data. + * + * @param data data + * @since 2.9.0 + */ + @Override + public void refresh(@NonNull Map data) { + this.data = data; + } + /** * Destroy the container */ diff --git a/crane4j-core/src/main/java/cn/crane4j/core/container/LimitedContainer.java b/crane4j-core/src/main/java/cn/crane4j/core/container/LimitedContainer.java index 0f1dfb9d..c5eb6ae2 100644 --- a/crane4j-core/src/main/java/cn/crane4j/core/container/LimitedContainer.java +++ b/crane4j-core/src/main/java/cn/crane4j/core/container/LimitedContainer.java @@ -1,5 +1,7 @@ package cn.crane4j.core.container; +import org.checkerframework.checker.nullness.qual.NonNull; + import java.util.Map; /** @@ -17,4 +19,14 @@ public interface LimitedContainer extends Container { * @return all elements */ Map getAll(); + + /** + * Refresh the container with new data. + * + * @param data data + * @since 2.9.0 + */ + default void refresh(@NonNull Map data) { + // do nothing + } } diff --git a/crane4j-core/src/main/java/cn/crane4j/core/support/Crane4jTemplate.java b/crane4j-core/src/main/java/cn/crane4j/core/support/Crane4jTemplate.java index 054c8500..19a4c2e3 100644 --- a/crane4j-core/src/main/java/cn/crane4j/core/support/Crane4jTemplate.java +++ b/crane4j-core/src/main/java/cn/crane4j/core/support/Crane4jTemplate.java @@ -9,7 +9,9 @@ import cn.crane4j.core.container.Container; import cn.crane4j.core.container.ContainerProvider; import cn.crane4j.core.container.Containers; +import cn.crane4j.core.container.LimitedContainer; import cn.crane4j.core.container.lifecycle.ContainerLifecycleProcessor; +import cn.crane4j.core.exception.Crane4jException; import cn.crane4j.core.executor.AsyncBeanOperationExecutor; import cn.crane4j.core.executor.BeanOperationExecutor; import cn.crane4j.core.executor.OrderedBeanOperationExecutor; @@ -27,6 +29,7 @@ import cn.crane4j.core.support.operator.OperatorProxyFactory; import cn.crane4j.core.support.operator.OperatorProxyMethodFactory; import cn.crane4j.core.support.reflect.PropertyOperator; +import cn.crane4j.core.util.Asserts; import cn.crane4j.core.util.ConfigurationUtil; import lombok.Builder; import lombok.Getter; @@ -37,6 +40,7 @@ import java.lang.reflect.AnnotatedElement; import java.util.Collection; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; @@ -491,6 +495,38 @@ public

OpsForContainer configureContainerProvider( public ContainerProvider getContainerProvider(String providerName) { return configuration.getContainerProvider(providerName); } + + /** + * Refresh data cache for specified namespace if possible. + *

    + *
  • if container not exists, register a new container with the given data;
  • + *
  • if container exists, refresh the data cache if possible;
  • + *
+ * + * @param namespace namespace + * @param data date + * @param key type + * @return this + * @throws Crane4jException if container already exists and it is not a {@link LimitedContainer}. + * @see LimitedContainer#refresh + * @since 2.9.0 + */ + @SuppressWarnings("unchecked") + public Crane4jTemplate.OpsForContainer refreshContainerData( + String namespace, @NonNull Map data) { + Container container = configuration.getContainer(namespace); + if (Objects.isNull(container)) { + registerMapContainer(namespace, data); + return this; + } + Asserts.isTrue( + container instanceof LimitedContainer, + "Container [{}] does not support refreshing data, is it a [{}]?", + namespace, LimitedContainer.class + ); + ((LimitedContainer) container).refresh(data); + return this; + } } /** diff --git a/crane4j-core/src/test/java/cn/crane4j/core/container/EmptyContainerTest.java b/crane4j-core/src/test/java/cn/crane4j/core/container/EmptyContainerTest.java index c9af31e7..f6182f04 100644 --- a/crane4j-core/src/test/java/cn/crane4j/core/container/EmptyContainerTest.java +++ b/crane4j-core/src/test/java/cn/crane4j/core/container/EmptyContainerTest.java @@ -3,6 +3,8 @@ import org.junit.Assert; import org.junit.Test; +import java.util.Collections; + /** * test for {@link EmptyContainer} * @@ -16,7 +18,11 @@ public void get() { Assert.assertSame(container, Container.empty()); Assert.assertEquals(Container.EMPTY_CONTAINER_NAMESPACE, container.getNamespace()); Assert.assertTrue(container.get(null).isEmpty()); - Assert.assertTrue(((LimitedContainer)container).getAll().isEmpty()); + + Assert.assertTrue(container instanceof LimitedContainer); + LimitedContainer limitedContainer = (LimitedContainer)container; + Assert.assertTrue(limitedContainer.getAll().isEmpty()); + limitedContainer.refresh(Collections.emptyMap()); } } diff --git a/crane4j-core/src/test/java/cn/crane4j/core/container/ImmutableMapContainerTest.java b/crane4j-core/src/test/java/cn/crane4j/core/container/ImmutableMapContainerTest.java index 0c98b171..3a735c8a 100644 --- a/crane4j-core/src/test/java/cn/crane4j/core/container/ImmutableMapContainerTest.java +++ b/crane4j-core/src/test/java/cn/crane4j/core/container/ImmutableMapContainerTest.java @@ -27,6 +27,10 @@ public void forMap() { assertEquals(map, container.getAll()); //always return all data assertEquals(map, container.get(Collections.singletonList("1"))); + + container.refresh(Collections.emptyMap()); + Assert.assertNotSame(map, container.getAll()); + Assert.assertTrue(container.getAll().isEmpty()); } @Test diff --git a/crane4j-core/src/test/java/cn/crane4j/core/support/Crane4jTemplateTest.java b/crane4j-core/src/test/java/cn/crane4j/core/support/Crane4jTemplateTest.java index 9710fffd..0138faa7 100644 --- a/crane4j-core/src/test/java/cn/crane4j/core/support/Crane4jTemplateTest.java +++ b/crane4j-core/src/test/java/cn/crane4j/core/support/Crane4jTemplateTest.java @@ -14,6 +14,7 @@ import cn.crane4j.core.container.Container; import cn.crane4j.core.container.ContainerProvider; import cn.crane4j.core.container.Containers; +import cn.crane4j.core.container.LimitedContainer; import cn.crane4j.core.container.PartitionContainerProvider; import cn.crane4j.core.container.lifecycle.ContainerLifecycleProcessor; import cn.crane4j.core.executor.BeanOperationExecutor; @@ -176,6 +177,26 @@ public void testChainConfigure() { Assert.assertSame(ops.opsForProxy(), crane4jTemplate.opsForProxy()); } + @Test + public void testRefreshCaches() { + String containerNamespace = "testRefreshCaches"; + Assert.assertFalse(configuration.containsContainer(containerNamespace)); + + Crane4jTemplate.OpsForContainer ops = crane4jTemplate.opsForContainer(); + Map data = Collections.singletonMap("1", "1"); + ops.refreshContainerData(containerNamespace, data); + + Assert.assertTrue(configuration.containsContainer(containerNamespace)); + Container container = configuration.getContainer(containerNamespace); + Assert.assertTrue(container instanceof LimitedContainer); + LimitedContainer limitedContainer = (LimitedContainer) container; + Assert.assertEquals(data, limitedContainer.getAll()); + + ops.refreshContainerData(containerNamespace, Collections.emptyMap()); + Assert.assertNotEquals(data, limitedContainer.getAll()); + Assert.assertEquals(Collections.emptyMap(), limitedContainer.getAll()); + } + public static class TestMethodContainers { @ContainerMethod(namespace = TEST_METHOD_CONTAINERS, resultType = Map.class) public List> listByIds(Collection ids) {