From cb909ea9e391ba66f6fd6134ddcf1ad8a604666d Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Sat, 28 Oct 2023 15:54:06 +0100 Subject: [PATCH 01/32] Allow callbacks for tuning contexts and cleanup Also: * Rename `DeliveredMessages` -> `DeliveredMessagesCache`. * `Blackbox` no longer copies `BoundedContextBuilder`. * Buses are now `Closable` instead of `AutoClosable`. * `Closable` interface got `closeIfOpen()` method. * `BoundedContext` uses `closeIfOpen()` for closing its parts. --- .../java/io/spine/server/BoundedContext.java | 33 ++++-- .../spine/server/BoundedContextBuilder.java | 100 +++++++----------- .../main/java/io/spine/server/Closeable.java | 18 ++++ .../io/spine/server/delivery/Conveyor.java | 16 +-- ...sages.java => DeliveredMessagesCache.java} | 4 +- .../io/spine/server/delivery/Delivery.java | 8 +- .../spine/server/entity/EntityLifecycle.java | 2 +- .../server/integration/IntegrationBroker.java | 8 +- .../server/stand/InMemoryTypeRegistry.java | 10 +- .../java/io/spine/server/stand/Stand.java | 9 +- .../io/spine/server/stand/TypeRegistry.java | 3 +- .../io/spine/server/transport/ChannelHub.java | 8 +- .../server/delivery/AbstractStationTest.java | 2 +- .../server/delivery/CatchUpStationTest.java | 10 +- .../server/delivery/CleanupStationTest.java | 4 +- .../delivery/LiveDeliveryStationTest.java | 8 +- .../testing/server/blackbox/BlackBox.java | 74 ++++++++----- .../testing/server/blackbox/MtBlackBox.java | 4 +- .../testing/server/blackbox/StBlackBox.java | 5 +- 19 files changed, 192 insertions(+), 134 deletions(-) rename server/src/main/java/io/spine/server/delivery/{DeliveredMessages.java => DeliveredMessagesCache.java} (94%) diff --git a/server/src/main/java/io/spine/server/BoundedContext.java b/server/src/main/java/io/spine/server/BoundedContext.java index 18a0ed9683f..6c3e4225c25 100644 --- a/server/src/main/java/io/spine/server/BoundedContext.java +++ b/server/src/main/java/io/spine/server/BoundedContext.java @@ -59,6 +59,7 @@ import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; @@ -86,7 +87,7 @@ * @see * Martin Fowler on Bounded Contexts */ -@SuppressWarnings({"OverlyCoupledClass", "ClassWithTooManyMethods"}) +@SuppressWarnings("ClassWithTooManyMethods") public abstract class BoundedContext implements Comparable, Closeable, @@ -122,6 +123,8 @@ public abstract class BoundedContext /** Provides access to internally-used features of the context. */ private final InternalAccess internalAccess; + private final @Nullable Consumer onBeforeClose; + /** * Creates new instance. * @@ -145,6 +148,8 @@ protected BoundedContext(BoundedContextBuilder builder) { this.importBus = buildImportBus(tenantIndex); this.aggregateRootDirectory = builder.aggregateRootDirectory(); this.internalAccess = new InternalAccess(); + this.onBeforeClose = builder.getOnBeforeClose(); + } /** @@ -421,19 +426,29 @@ public boolean isMultitenant() { */ @Override public void close() throws Exception { - commandBus.close(); - eventBus.close(); - broker.close(); - stand.close(); - importBus.close(); - shutDownRepositories(); + var isOpen = isOpen(); + if (isOpen && onBeforeClose != null) { + onBeforeClose.accept(this); + } + + commandBus.closeIfOpen(); + eventBus.closeIfOpen(); + broker.closeIfOpen(); + stand.closeIfOpen(); + importBus.closeIfOpen(); - logger().atDebug().log(() -> format("%s", closed(nameForLogging()))); + if (isOpen) { + shutDownRepositories(); + } + + if (isOpen) { + logger().atDebug().log(() -> format("%s", closed(nameForLogging()))); + } } @Override public boolean isOpen() { - return !guard.isClosed(); + return commandBus.isOpen(); } final String nameForLogging() { diff --git a/server/src/main/java/io/spine/server/BoundedContextBuilder.java b/server/src/main/java/io/spine/server/BoundedContextBuilder.java index 3e2f09785d0..d3c03d4cd2d 100644 --- a/server/src/main/java/io/spine/server/BoundedContextBuilder.java +++ b/server/src/main/java/io/spine/server/BoundedContextBuilder.java @@ -73,8 +73,7 @@ /** * A builder for producing {@code BoundedContext} instances. */ -@SuppressWarnings({"ClassWithTooManyMethods", "OverlyCoupledClass"}) -// OK for this central piece. +@SuppressWarnings("ClassWithTooManyMethods") // OK for this central piece. public final class BoundedContextBuilder implements WithLogging { private final ContextSpec spec; @@ -82,8 +81,8 @@ public final class BoundedContextBuilder implements WithLogging { private final CommandBus.Builder commandBus = CommandBus.newBuilder(); /** - * Command dispatchers to be registered with the context {@link CommandBus} after the Bounded - * Context creation. + * Command dispatchers to be registered with the context {@link CommandBus} + * after the Bounded Context creation. */ private final Collection commandDispatchers = new ArrayList<>(); @@ -105,6 +104,10 @@ public final class BoundedContextBuilder implements WithLogging { /** Repositories to be registered with the Bounded Context being built after its creation. */ private final Collection> repositories = new ArrayList<>(); + private @Nullable Consumer onBeforeBuild = null; + private @Nullable Consumer onBuild = null; + private @Nullable Consumer onBeforeClose = null; + /** * Creates a new builder with the given spec. * @@ -222,14 +225,6 @@ public BoundedContextBuilder enrichEventsUsing(EventEnricher enricher) { return this; } - /** - * Obtains {@code EventEnricher} assigned to the context to be built, or - * empty {@code Optional} if no enricher was assigned prior to this call. - */ - public Optional eventEnricher() { - return eventBus.enricher(); - } - @CanIgnoreReturnValue public BoundedContextBuilder setTenantIndex(TenantIndex tenantIndex) { if (isMultitenant()) { @@ -278,7 +273,7 @@ BoundedContextBuilder ifRepository(D dispatcher, * Adds the given assignee to the Bounded Context. * * @param assignee - * the assignee to add + * the assignee to add */ @CanIgnoreReturnValue public BoundedContextBuilder addAssignee(AbstractAssignee assignee) { @@ -289,7 +284,8 @@ public BoundedContextBuilder addAssignee(AbstractAssignee assignee) { * Adds the passed command dispatcher to the dispatcher registration list which will be * processed after the Bounded Context is created. * - * @apiNote This method is also capable of registering {@linkplain Repository repositories} + * @apiNote This method is also capable of registering + * {@linkplain Repository repositories} * that implement {@code CommandDispatcher}, but the {@link #add(Repository)} method * should be preferred for this purpose. */ @@ -326,7 +322,8 @@ public BoundedContextBuilder addCommandListener(Listener listen * Adds the passed event dispatcher to the dispatcher registration list which will be processed * after the Bounded Context is created. * - * @apiNote This method is also capable of registering {@linkplain Repository repositories} + * @apiNote This method is also capable of registering + * {@linkplain Repository repositories} * that implement {@code EventDispatcher}, but the {@link #add(Repository)} method * should be preferred for this purpose. */ @@ -459,8 +456,8 @@ boolean hasEventDispatcher(EventDispatcher eventDispatcher) { checkNotNull(entityClass); var result = repositories.stream() - .anyMatch(repository -> repository.entityClass() - .equals(entityClass)); + .anyMatch(repository -> repository.entityClass() + .equals(entityClass)); return result; } @@ -485,26 +482,6 @@ boolean hasRepository(Repository repository) { return ImmutableList.copyOf(repositories); } - /** - * Obtains the list of command dispatchers added to the builder by the time of the call. - * - *

Adding dispatchers to the builder after this method returns will not update the - * returned list. - */ - public ImmutableList commandDispatchers() { - return ImmutableList.copyOf(commandDispatchers); - } - - /** - * Obtains the list of event dispatchers added to the builder by the time of the call. - * - *

Adding dispatchers to the builder after this method returns will not update the - * returned list. - */ - public ImmutableList eventDispatchers() { - return ImmutableList.copyOf(eventDispatchers); - } - /** * Obtains the {@link AggregateRootDirectory} to be used in the built context. * @@ -564,15 +541,22 @@ public SystemSettings systemSettings() { * @return new {@code BoundedContext} */ public BoundedContext build() { + if (onBeforeBuild != null) { + onBeforeBuild.accept(this); + } var system = buildSystem(); var result = buildDomain(system); - logger().atDebug().log(() -> format("%s created.", result.nameForLogging())); + logger().atDebug() + .log(() -> format("%s created.", result.nameForLogging())); registerRepositories(result); registerDispatchers(result); ServerEnvironment.instance() .delivery() .registerDispatchersIn(result); + if (onBuild != null) { + onBuild.accept(result); + } return result; } @@ -583,6 +567,22 @@ EventBus buildEventBus(BoundedContext context) { return eventBus.build(); } + public void setOnBeforeBuild(Consumer consumer) { + this.onBeforeBuild = checkNotNull(consumer); + } + + public void setOnBuild(Consumer consumer) { + this.onBuild = checkNotNull(consumer); + } + + public void setOnBeforeClose(Consumer consumer) { + this.onBeforeClose = checkNotNull(consumer); + } + + @Nullable Consumer getOnBeforeClose() { + return onBeforeClose; + } + CommandBus buildCommandBus() { return commandBus.build(); } @@ -590,7 +590,8 @@ CommandBus buildCommandBus() { private void registerRepositories(BoundedContext result) { for (var repository : repositories) { result.register(repository); - logger().atDebug().log(() -> format("`%s` registered.", repository)); + logger().atDebug() + .log(() -> format("`%s` registered.", repository)); } } @@ -661,25 +662,4 @@ private Stand createStand(@Nullable Stand systemStand) { } return result.build(); } - - /** - * Creates a copy of this context builder for the purpose of testing. - */ - @Internal - @VisibleForTesting - public BoundedContextBuilder testingCopy() { - var copy = new BoundedContextBuilder(this.spec, this.systemSettings); - var enricher = eventEnricher() - .orElseGet(() -> EventEnricher.newBuilder().build()); - copy.enrichEventsUsing(enricher); - tenantIndex().ifPresent(copy::setTenantIndex); - repositories().forEach(copy::add); - commandDispatchers().forEach(copy::addCommandDispatcher); - commandBus.filters().forEach(copy::addCommandFilter); - commandBus.listeners().forEach(copy::addCommandListener); - eventDispatchers().forEach(copy::addEventDispatcher); - eventBus.filters().forEach(copy::addEventFilter); - eventBus.listeners().forEach(copy::addEventListener); - return copy; - } } diff --git a/server/src/main/java/io/spine/server/Closeable.java b/server/src/main/java/io/spine/server/Closeable.java index 9e24860fff3..f47c8e868ac 100644 --- a/server/src/main/java/io/spine/server/Closeable.java +++ b/server/src/main/java/io/spine/server/Closeable.java @@ -27,6 +27,7 @@ package io.spine.server; import static com.google.common.base.Preconditions.checkState; +import static io.spine.util.Exceptions.illegalStateWithCauseOf; /** * Base interface for server-side objects that may hold resources that need to be released @@ -56,4 +57,21 @@ public interface Closeable extends AutoCloseable { default void checkOpen() throws IllegalStateException { checkState(isOpen(), "`%s` is already closed.", this); } + + /** + * Performs the release of the resources held by this object only if it is still open. + * Otherwise, does nothing. + * + * @throws IllegalStateException + * if an exception occurs on {@link #close()} + */ + default void closeIfOpen() { + if (isOpen()) { + try { + close(); + } catch (Exception e) { + throw illegalStateWithCauseOf(e); + } + } + } } diff --git a/server/src/main/java/io/spine/server/delivery/Conveyor.java b/server/src/main/java/io/spine/server/delivery/Conveyor.java index ab88e8a43a8..918e10468db 100644 --- a/server/src/main/java/io/spine/server/delivery/Conveyor.java +++ b/server/src/main/java/io/spine/server/delivery/Conveyor.java @@ -54,13 +54,13 @@ *

Collects the state updates of the messages and allows to flush the pending changes to the * respective {@link InboxStorage} in a bulk. * - *

By accessing the {@linkplain DeliveredMessages cache}, knows which messages were marked + *

By accessing the {@linkplain DeliveredMessagesCache cache}, knows which messages were marked * delivered by the instances of {@code Conveyor} in the previous {@code DeliveryStage}s. */ final class Conveyor implements Iterable { private final Map messages = new LinkedHashMap<>(); - private final DeliveredMessages deliveredMessages; + private final DeliveredMessagesCache deliveredMessagesCache; private final Set dirtyMessages = new HashSet<>(); private final Set removals = new HashSet<>(); private final Set duplicates = new HashSet<>(); @@ -71,11 +71,11 @@ final class Conveyor implements Iterable { * * @param messages * messages to process - * @param deliveredMessages + * @param deliveredMessagesCache * cache of the previously delivered messages */ - Conveyor(Collection messages, DeliveredMessages deliveredMessages) { - this.deliveredMessages = deliveredMessages; + Conveyor(Collection messages, DeliveredMessagesCache deliveredMessagesCache) { + this.deliveredMessagesCache = deliveredMessagesCache; for (var message : messages) { this.messages.put(message.getId(), message); } @@ -97,7 +97,7 @@ public Iterator iterator() { */ void markDelivered(InboxMessage message) { changeStatus(message, DELIVERED); - deliveredMessages.recordDelivered(message); + deliveredMessagesCache.recordDelivered(message); } /** @@ -191,13 +191,13 @@ private InboxMessage.Builder mutableMessage(InboxMessageId id) { * *

This includes both the IDs of messages delivered within the lifetime of this conveyor * instance and of the messages delivered - * {@linkplain Conveyor#Conveyor(Collection, DeliveredMessages) before it}. + * {@linkplain Conveyor#Conveyor(Collection, DeliveredMessagesCache) before it}. */ Set allDelivered() { var recentlyDelivered = recentlyDelivered() .map(DispatchingId::new) .collect(Collectors.toSet()); - var result = Sets.union(recentlyDelivered, deliveredMessages.allDelivered()); + var result = Sets.union(recentlyDelivered, deliveredMessagesCache.allDelivered()); return result; } diff --git a/server/src/main/java/io/spine/server/delivery/DeliveredMessages.java b/server/src/main/java/io/spine/server/delivery/DeliveredMessagesCache.java similarity index 94% rename from server/src/main/java/io/spine/server/delivery/DeliveredMessages.java rename to server/src/main/java/io/spine/server/delivery/DeliveredMessagesCache.java index 70dfeb3c03a..4303bd0f1e5 100644 --- a/server/src/main/java/io/spine/server/delivery/DeliveredMessages.java +++ b/server/src/main/java/io/spine/server/delivery/DeliveredMessagesCache.java @@ -36,12 +36,12 @@ /** * A cache of the messages locally delivered within the instance of {@link Delivery}. * - *

The cache is limited in size, aiming to hunt down the only the duplicates of the recently + *

The cache is limited in size, aiming to hunt down only the duplicates of the recently * delivered messages. The idea behind it is that the messages were read locally anyway, * so as well their identifiers may be reused for deduplication instead of just wasting * the effort and feeding the garbage collector. */ -final class DeliveredMessages { +final class DeliveredMessagesCache { private final Cache cache = CacheBuilder.newBuilder() diff --git a/server/src/main/java/io/spine/server/delivery/Delivery.java b/server/src/main/java/io/spine/server/delivery/Delivery.java index 1e808d3584a..5b3bbdd6f68 100644 --- a/server/src/main/java/io/spine/server/delivery/Delivery.java +++ b/server/src/main/java/io/spine/server/delivery/Delivery.java @@ -197,7 +197,7 @@ * performing the actual message dispatching rely onto this knowledge and deduplicate * the messages prior to calling the target's endpoint. * - *

Additionally, the {@code Delivery} provides a {@linkplain DeliveredMessages cache of recently + *

Additionally, the {@code Delivery} provides a {@linkplain DeliveredMessagesCache cache of recently * delivered messages}. Each instance of the {@code Conveyor} has an access to it and uses it * in deduplication procedures. * @@ -298,7 +298,7 @@ public final class Delivery implements WithLogging { /** * The cache of the locally delivered messages. */ - private final DeliveredMessages deliveredMessages; + private final DeliveredMessagesCache deliveredMessagesCache; /** * The maximum amount of messages to deliver within a {@link DeliveryStage}. @@ -325,7 +325,7 @@ public final class Delivery implements WithLogging { this.pageSize = builder.getPageSize(); this.deliveries = new InboxDeliveries(); this.shardObservers = synchronizedList(new ArrayList<>()); - this.deliveredMessages = new DeliveredMessages(); + this.deliveredMessagesCache = new DeliveredMessagesCache(); } /** @@ -571,7 +571,7 @@ private ImmutableList refreshCatchUpJobs() { private DeliveryStage deliverMessages(ImmutableList messages, ShardIndex index, Iterable catchUpJobs) { - var conveyor = new Conveyor(messages, deliveredMessages); + var conveyor = new Conveyor(messages, deliveredMessagesCache); DeliveryAction action = new GroupByTargetAndDeliver(deliveries, monitor, conveyor); List stations = conveyorStationsFor(catchUpJobs, action); var stage = launch(conveyor, stations, index); diff --git a/server/src/main/java/io/spine/server/entity/EntityLifecycle.java b/server/src/main/java/io/spine/server/entity/EntityLifecycle.java index 2289bcb1cf5..9f39bdc28cf 100644 --- a/server/src/main/java/io/spine/server/entity/EntityLifecycle.java +++ b/server/src/main/java/io/spine/server/entity/EntityLifecycle.java @@ -114,7 +114,7 @@ public class EntityLifecycle { private final EventFilter eventFilter; /** - * The message ID of of the associated {@link Entity} state. + * The message ID of the associated {@link Entity} state. */ private final MessageId entityId; diff --git a/server/src/main/java/io/spine/server/integration/IntegrationBroker.java b/server/src/main/java/io/spine/server/integration/IntegrationBroker.java index 7c275ad5372..634cacd49dc 100644 --- a/server/src/main/java/io/spine/server/integration/IntegrationBroker.java +++ b/server/src/main/java/io/spine/server/integration/IntegrationBroker.java @@ -29,6 +29,7 @@ import io.spine.annotation.Internal; import io.spine.core.BoundedContextName; import io.spine.server.BoundedContext; +import io.spine.server.Closeable; import io.spine.server.ContextAware; import io.spine.server.ServerEnvironment; import io.spine.server.event.EventDispatcher; @@ -115,7 +116,7 @@ * between the Contexts. */ @Internal -public final class IntegrationBroker implements ContextAware, AutoCloseable { +public final class IntegrationBroker implements ContextAware, Closeable { private final SubscriberHub subscriberHub; private final PublisherHub publisherHub; @@ -203,6 +204,11 @@ public void close() throws Exception { publisherHub.close(); } + @Override + public boolean isOpen() { + return subscriberHub.isOpen(); + } + @Override public String toString() { return "`IntegrationBroker` of <" + contextName.getValue() + '>'; diff --git a/server/src/main/java/io/spine/server/stand/InMemoryTypeRegistry.java b/server/src/main/java/io/spine/server/stand/InMemoryTypeRegistry.java index d10ca679b3d..d5c78e2e74f 100644 --- a/server/src/main/java/io/spine/server/stand/InMemoryTypeRegistry.java +++ b/server/src/main/java/io/spine/server/stand/InMemoryTypeRegistry.java @@ -64,17 +64,18 @@ static TypeRegistry newInstance() { return new InMemoryTypeRegistry(); } - @SuppressWarnings("ChainOfInstanceofChecks") @Override + @SuppressWarnings("ChainOfInstanceofChecks") public > void register(Repository repository) { var entityType = repository.entityStateType(); if (repository instanceof QueryableRepository) { @SuppressWarnings("unchecked") /* Guaranteed by the `QueryableRepository` contract. */ - var recordRepo = (QueryableRepository) repository; + var recordRepo = (QueryableRepository) repository; repositories.put(entityType, recordRepo); } if (repository instanceof AggregateRepository) { + @SuppressWarnings("unchecked") AggregateRepository aggRepository = (AggregateRepository) repository; aggregateTypes.add(aggRepository.entityStateType()); } @@ -98,6 +99,11 @@ public ImmutableSet allTypes() { return ImmutableSet.copyOf(repositories.keySet()); } + @Override + public boolean isOpen() { + return !repositories.isEmpty(); + } + @Override public void close() { repositories.clear(); diff --git a/server/src/main/java/io/spine/server/stand/Stand.java b/server/src/main/java/io/spine/server/stand/Stand.java index f4763e382b0..68573b4751c 100644 --- a/server/src/main/java/io/spine/server/stand/Stand.java +++ b/server/src/main/java/io/spine/server/stand/Stand.java @@ -41,6 +41,7 @@ import io.spine.core.Response; import io.spine.core.Responses; import io.spine.protobuf.AnyPacker; +import io.spine.server.Closeable; import io.spine.server.EventProducer; import io.spine.server.Identity; import io.spine.server.bus.Listener; @@ -83,8 +84,7 @@ * @see * Spine Architecture Diagram */ -@SuppressWarnings("OverlyCoupledClass") -public class Stand implements AutoCloseable { +public class Stand implements Closeable { /** * Used to return an empty result collection for {@link Query}. @@ -371,6 +371,11 @@ public void registerTypeSupplier(EventProducer producer) { eventRegistry.register(producer); } + @Override + public boolean isOpen() { + return typeRegistry.isOpen(); + } + /** * Closes the {@code Stand} performing necessary cleanups. */ diff --git a/server/src/main/java/io/spine/server/stand/TypeRegistry.java b/server/src/main/java/io/spine/server/stand/TypeRegistry.java index c9578c81ac2..e3eaace4ba9 100644 --- a/server/src/main/java/io/spine/server/stand/TypeRegistry.java +++ b/server/src/main/java/io/spine/server/stand/TypeRegistry.java @@ -26,6 +26,7 @@ package io.spine.server.stand; import com.google.common.collect.ImmutableSet; +import io.spine.server.Closeable; import io.spine.server.entity.Entity; import io.spine.server.entity.QueryableRepository; import io.spine.server.entity.RecordBasedRepository; @@ -40,7 +41,7 @@ *

In addition to types, manages the information about the {@linkplain Repository repositories} * for the objects of known types. */ -interface TypeRegistry extends AutoCloseable { +interface TypeRegistry extends Closeable { /** * Registers a {@linkplain Repository repository} of objects and diff --git a/server/src/main/java/io/spine/server/transport/ChannelHub.java b/server/src/main/java/io/spine/server/transport/ChannelHub.java index 4e002e485a0..c3fea4de5df 100644 --- a/server/src/main/java/io/spine/server/transport/ChannelHub.java +++ b/server/src/main/java/io/spine/server/transport/ChannelHub.java @@ -26,6 +26,7 @@ package io.spine.server.transport; import io.spine.annotation.SPI; +import io.spine.server.Closeable; import java.util.Map; import java.util.Set; @@ -45,7 +46,7 @@ * the type of grouped channels */ @SPI -public abstract class ChannelHub implements AutoCloseable { +public abstract class ChannelHub implements Closeable { private final TransportFactory transportFactory; private final Map channels = new ConcurrentHashMap<>(); @@ -112,6 +113,11 @@ private Set detectStale() { return toRemove; } + @Override + public boolean isOpen() { + return !channels.isEmpty(); + } + @Override public void close() throws Exception { for (var channel : channels.values()) { diff --git a/server/src/test/java/io/spine/server/delivery/AbstractStationTest.java b/server/src/test/java/io/spine/server/delivery/AbstractStationTest.java index 18da6374862..206e6fb7982 100644 --- a/server/src/test/java/io/spine/server/delivery/AbstractStationTest.java +++ b/server/src/test/java/io/spine/server/delivery/AbstractStationTest.java @@ -65,7 +65,7 @@ abstract class AbstractStationTest { void doNothingOnEmptyConveyor() { var action = new MemoizingAction(); var station = newStation(action); - var emptyConveyor = new Conveyor(new ArrayList<>(), new DeliveredMessages()); + var emptyConveyor = new Conveyor(new ArrayList<>(), new DeliveredMessagesCache()); var result = station.process(emptyConveyor); assertDeliveredCount(result, 0); diff --git a/server/src/test/java/io/spine/server/delivery/CatchUpStationTest.java b/server/src/test/java/io/spine/server/delivery/CatchUpStationTest.java index 0ab9d958def..ebb619eb6d2 100644 --- a/server/src/test/java/io/spine/server/delivery/CatchUpStationTest.java +++ b/server/src/test/java/io/spine/server/delivery/CatchUpStationTest.java @@ -73,7 +73,7 @@ void removeLiveMessagesIfCatchUpStarted() { var differentTarget = delivered(targetTwo, type); var conveyor = new Conveyor( ImmutableList.of(toDeliver, anotherToDeliver, delivered, differentTarget), - new DeliveredMessages() + new DeliveredMessagesCache() ); var job = catchUpJob(type, IN_PROGRESS, currentTime(), ImmutableList.of(targetOne)); @@ -104,7 +104,7 @@ void matchAndRunDeliveryAction() { var initialContents = ImmutableList.of(toCatchUp, anotherToCatchUp, duplicateCopy, alreadyDelivered, differentTarget); - var conveyor = new Conveyor(initialContents, new DeliveredMessages()); + var conveyor = new Conveyor(initialContents, new DeliveredMessagesCache()); var job = catchUpJob(type, IN_PROGRESS, currentTime(), ImmutableList.of(targetOne)); var station = new CatchUpStation(MemoizingAction.empty(), ImmutableList.of(job)); @@ -138,7 +138,7 @@ void markLiveMessagesCatchUpIfJobFinalizing() { var differentTarget = delivered(targetTwo, type); var conveyor = new Conveyor( ImmutableList.of(toDeliver, anotherToDeliver, delivered, differentTarget), - new DeliveredMessages() + new DeliveredMessagesCache() ); var job = catchUpJob(type, FINALIZING, currentTime(), ImmutableList.of(targetOne)); @@ -167,7 +167,7 @@ void deduplicateAndDeliverWhenJobCompleted() { var conveyor = new Conveyor( ImmutableList.of(toCatchUp, moreToCatchUp, toDeliver, duplicateToCatchUp, duplicateToDeliver), - new DeliveredMessages() + new DeliveredMessagesCache() ); var job = catchUpJob(type, COMPLETED, currentTime(), ImmutableList.of(targetOne)); @@ -207,7 +207,7 @@ void sortCatchUpMessagesBeforeCallingToAction() { var toCatchUp4 = catchingUp(targetOne, type, now); var conveyor = new Conveyor( ImmutableList.of(toCatchUp3, toCatchUp2, toCatchUp4, toCatchUp1), - new DeliveredMessages() + new DeliveredMessagesCache() ); var job = catchUpJob(type, IN_PROGRESS, currentTime(), ImmutableList.of(targetOne)); diff --git a/server/src/test/java/io/spine/server/delivery/CleanupStationTest.java b/server/src/test/java/io/spine/server/delivery/CleanupStationTest.java index 44c7affda85..526cead96e7 100644 --- a/server/src/test/java/io/spine/server/delivery/CleanupStationTest.java +++ b/server/src/test/java/io/spine/server/delivery/CleanupStationTest.java @@ -56,7 +56,7 @@ void removeDeliveredMessages() { var toDeliver = toDeliver(targetOne, type); var conveyor = new Conveyor( ImmutableList.of(delivered, deliveredToAnotherTarget, catchingUp, toDeliver), - new DeliveredMessages() + new DeliveredMessagesCache() ); Station station = new CleanupStation(); @@ -85,7 +85,7 @@ void considerKeepUntilWhenRemoving() { ); var conveyor = new Conveyor( ImmutableList.of(deliveredKeepTillFuture, deliveredKeepUntilPastTime), - new DeliveredMessages() + new DeliveredMessagesCache() ); Station station = new CleanupStation(); diff --git a/server/src/test/java/io/spine/server/delivery/LiveDeliveryStationTest.java b/server/src/test/java/io/spine/server/delivery/LiveDeliveryStationTest.java index 0a593e35eea..8e1b9d0e85c 100644 --- a/server/src/test/java/io/spine/server/delivery/LiveDeliveryStationTest.java +++ b/server/src/test/java/io/spine/server/delivery/LiveDeliveryStationTest.java @@ -66,7 +66,7 @@ void runDeliveryActionForToDeliver() { var initialContents = ImmutableList.of(toDeliver, anotherToDeliver, differentTarget, alreadyDelivered, toCatchUp); - var conveyor = new Conveyor(initialContents, new DeliveredMessages()); + var conveyor = new Conveyor(initialContents, new DeliveredMessagesCache()); var action = MemoizingAction.empty(); Station station = new LiveDeliveryStation(action, noWindow()); @@ -104,7 +104,7 @@ void removeDuplicates() { var initialContents = ImmutableList.of(toDeliver, duplicate, anotherDuplicate, alreadyDelivered, duplicateOfDelivered, toCatchUp); - var conveyor = new Conveyor(initialContents, new DeliveredMessages()); + var conveyor = new Conveyor(initialContents, new DeliveredMessagesCache()); var action = MemoizingAction.empty(); Station station = new LiveDeliveryStation(action, noWindow()); @@ -143,7 +143,7 @@ void sort() { var toDeliver4 = toDeliver(targetTwo, type, now); var conveyor = new Conveyor( ImmutableList.of(toDeliver2, toDeliver3, toDeliver4, toDeliver1), - new DeliveredMessages() + new DeliveredMessagesCache() ); var action = MemoizingAction.empty(); @@ -167,7 +167,7 @@ void keepMessagesForLongerIfDeduplicationWindowSet() { var initialContents = ImmutableList.of(toDeliver, differentTarget, alreadyDelivered, toCatchUp); - var conveyor = new Conveyor(initialContents, new DeliveredMessages()); + var conveyor = new Conveyor(initialContents, new DeliveredMessagesCache()); Station station = new LiveDeliveryStation(MemoizingAction.empty(), fromSeconds(100)); var result = station.process(conveyor); diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java index 80816f2f825..56c7ee5adb0 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java @@ -58,12 +58,14 @@ import io.spine.testing.server.entity.EntitySubject; import io.spine.testing.server.query.QueryResultSubject; import io.spine.time.ZoneId; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Predicate; import java.util.function.Supplier; @@ -73,7 +75,6 @@ import static io.spine.grpc.StreamObservers.memoizingObserver; import static io.spine.server.entity.model.EntityClass.stateClassOf; import static io.spine.testing.server.blackbox.Actor.defaultActor; -import static io.spine.util.Exceptions.illegalStateWithCauseOf; import static java.util.Collections.singletonList; import static java.util.Collections.synchronizedSet; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -91,12 +92,12 @@ public abstract class BlackBox implements WithLogging, Closeable { /** * The context under the test. */ - private final BoundedContext context; + private @MonotonicNonNull BoundedContext context; /** * A factory of {@link Client}s which send requests to this context. */ - private final ClientFactory clientFactory; + private @MonotonicNonNull ClientFactory clientFactory; /** * Collects all commands, including posted to the context during its setup or @@ -140,35 +141,61 @@ public abstract class BlackBox implements WithLogging, Closeable { * Creates new instance obtaining configuration parameters from the passed builder. */ public static BlackBox from(BoundedContextBuilder builder) { + var supplier = initLazilyFrom(builder, null); + var ignored = builder.build(); + return supplier.get(); + } + + /** + * Creates new instance obtaining configuration parameters from the passed builder. + * + * @param builder + * the builder of the context under the test + * @param customCleanup + * if not `null`, the procedure to be executed in {@link #close()} + * before closing other resources of the blackbox + */ + public static BlackBox from(BoundedContextBuilder builder, @Nullable Runnable customCleanup) { var result = builder.isMultitenant() - ? new MtBlackBox(builder) - : new StBlackBox(builder); + ? new MtBlackBox(builder, customCleanup) + : new StBlackBox(builder, customCleanup); return result; } - BlackBox(BoundedContextBuilder builder) { + public static Supplier initLazilyFrom(BoundedContextBuilder builder, + @Nullable Runnable customCleanup) { + var result = new AtomicReference(); + builder.setOnBeforeBuild(configuredBuilder -> { + var box = builder.isMultitenant() + ? new MtBlackBox(configuredBuilder, customCleanup) + : new StBlackBox(configuredBuilder, customCleanup); + result.set(box); + }); + return result::get; + } + + BlackBox(BoundedContextBuilder builder, @Nullable Runnable customCleanup) { super(); this.commands = new CommandCollector(); this.postedCommands = synchronizedSet(new HashSet<>()); this.events = new EventCollector(); this.postedEvents = synchronizedSet(new HashSet<>()); this.failedHandlerGuard = new FailedHandlerGuard(); - var wiredCopy = wiredCopyOf(builder); - this.context = wiredCopy.build(); this.actor = defaultActor(); - this.clientFactory = new ClientFactory(context); - } - - private BoundedContextBuilder wiredCopyOf(BoundedContextBuilder builder) { - var result = builder.testingCopy(); - result.addCommandListener(commands) + builder.addCommandListener(commands) .addEventListener(events) .addEventDispatcher(failedHandlerGuard) - .addEventDispatcher(new UnsupportedCommandGuard(result.name() - .getValue())) + .addEventDispatcher(new UnsupportedCommandGuard(builder.name().getValue())) .addEventDispatcher(DiagnosticLog.instance()); - return result; + if(customCleanup != null) { + builder.setOnBeforeClose((ignored) -> customCleanup.run()); + } + var created = this; + builder.setOnBuild(context -> { + created.clientFactory = new ClientFactory(context); + created.context = context; + }); } /** @@ -478,20 +505,13 @@ private BlackBoxSetup setup() { */ @Override public final void close() { - if (!isOpen()) { - return; - } - try { - context.close(); - clientFactory.close(); - } catch (Exception e) { - throw illegalStateWithCauseOf(e); - } + context.closeIfOpen(); + clientFactory.closeIfOpen(); } @Override public boolean isOpen() { - return context().isOpen(); + return context.isOpen() || clientFactory.isOpen(); } /** diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/MtBlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/MtBlackBox.java index 2298fc3f114..eeef920c73a 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/MtBlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/MtBlackBox.java @@ -52,8 +52,8 @@ final class MtBlackBox extends BlackBox { /** * Creates a new multi-tenant instance. */ - MtBlackBox(BoundedContextBuilder b) { - super(b); + MtBlackBox(BoundedContextBuilder b, @Nullable Runnable customCleanup) { + super(b, customCleanup); } /** diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/StBlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/StBlackBox.java index 9381d39edcd..2d8c2ff45fb 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/StBlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/StBlackBox.java @@ -33,6 +33,7 @@ import io.spine.core.TenantId; import io.spine.server.BoundedContextBuilder; import io.spine.testing.client.TestActorRequestFactory; +import org.checkerframework.checker.nullness.qual.Nullable; import static com.google.protobuf.TextFormat.shortDebugString; import static io.spine.util.Exceptions.newIllegalStateException; @@ -42,8 +43,8 @@ */ final class StBlackBox extends BlackBox { - StBlackBox(BoundedContextBuilder b) { - super(b); + StBlackBox(BoundedContextBuilder b, @Nullable Runnable customCleanup) { + super(b, customCleanup); } @Override From 3c809c4e92ccb41665d4a443259eb2f698e5260d Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Sat, 28 Oct 2023 16:30:04 +0100 Subject: [PATCH 02/32] Remove redundant parameter --- .../testing/server/blackbox/BlackBox.java | 33 +++++-------------- .../testing/server/blackbox/MtBlackBox.java | 4 +-- .../testing/server/blackbox/StBlackBox.java | 4 +-- 3 files changed, 12 insertions(+), 29 deletions(-) diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java index 56c7ee5adb0..9a1699cf1e0 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java @@ -141,41 +141,27 @@ public abstract class BlackBox implements WithLogging, Closeable { * Creates new instance obtaining configuration parameters from the passed builder. */ public static BlackBox from(BoundedContextBuilder builder) { - var supplier = initLazilyFrom(builder, null); + var supplier = initLazilyFrom(builder); var ignored = builder.build(); return supplier.get(); } /** - * Creates new instance obtaining configuration parameters from the passed builder. - * - * @param builder - * the builder of the context under the test - * @param customCleanup - * if not `null`, the procedure to be executed in {@link #close()} - * before closing other resources of the blackbox - */ - public static BlackBox from(BoundedContextBuilder builder, @Nullable Runnable customCleanup) { - var result = - builder.isMultitenant() - ? new MtBlackBox(builder, customCleanup) - : new StBlackBox(builder, customCleanup); - return result; - } - - public static Supplier initLazilyFrom(BoundedContextBuilder builder, - @Nullable Runnable customCleanup) { + * Arranged a delayed creation of new {@code Blackbox} obtaining configuration parameters + * from the passed builder. + */ + public static Supplier initLazilyFrom(BoundedContextBuilder builder) { var result = new AtomicReference(); builder.setOnBeforeBuild(configuredBuilder -> { var box = builder.isMultitenant() - ? new MtBlackBox(configuredBuilder, customCleanup) - : new StBlackBox(configuredBuilder, customCleanup); + ? new MtBlackBox(configuredBuilder) + : new StBlackBox(configuredBuilder); result.set(box); }); return result::get; } - BlackBox(BoundedContextBuilder builder, @Nullable Runnable customCleanup) { + BlackBox(BoundedContextBuilder builder) { super(); this.commands = new CommandCollector(); this.postedCommands = synchronizedSet(new HashSet<>()); @@ -188,9 +174,6 @@ public static Supplier initLazilyFrom(BoundedContextBuilder builder, .addEventDispatcher(failedHandlerGuard) .addEventDispatcher(new UnsupportedCommandGuard(builder.name().getValue())) .addEventDispatcher(DiagnosticLog.instance()); - if(customCleanup != null) { - builder.setOnBeforeClose((ignored) -> customCleanup.run()); - } var created = this; builder.setOnBuild(context -> { created.clientFactory = new ClientFactory(context); diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/MtBlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/MtBlackBox.java index eeef920c73a..2298fc3f114 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/MtBlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/MtBlackBox.java @@ -52,8 +52,8 @@ final class MtBlackBox extends BlackBox { /** * Creates a new multi-tenant instance. */ - MtBlackBox(BoundedContextBuilder b, @Nullable Runnable customCleanup) { - super(b, customCleanup); + MtBlackBox(BoundedContextBuilder b) { + super(b); } /** diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/StBlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/StBlackBox.java index 2d8c2ff45fb..6ed954ed7c3 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/StBlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/StBlackBox.java @@ -43,8 +43,8 @@ */ final class StBlackBox extends BlackBox { - StBlackBox(BoundedContextBuilder b, @Nullable Runnable customCleanup) { - super(b, customCleanup); + StBlackBox(BoundedContextBuilder b) { + super(b); } @Override From 5f060470392d6aebf6453e62f8be96339fb8e7ab Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Sat, 28 Oct 2023 16:51:30 +0100 Subject: [PATCH 03/32] Fix code layout --- .../java/io/spine/testing/server/blackbox/BlackBox.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java index 9a1699cf1e0..eedbd2ee1b2 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java @@ -170,10 +170,10 @@ public static Supplier initLazilyFrom(BoundedContextBuilder builder) { this.failedHandlerGuard = new FailedHandlerGuard(); this.actor = defaultActor(); builder.addCommandListener(commands) - .addEventListener(events) - .addEventDispatcher(failedHandlerGuard) - .addEventDispatcher(new UnsupportedCommandGuard(builder.name().getValue())) - .addEventDispatcher(DiagnosticLog.instance()); + .addEventListener(events) + .addEventDispatcher(failedHandlerGuard) + .addEventDispatcher(new UnsupportedCommandGuard(builder.name().getValue())) + .addEventDispatcher(DiagnosticLog.instance()); var created = this; builder.setOnBuild(context -> { created.clientFactory = new ClientFactory(context); From 3d61b40c98119cffe0d72db6f85ff49e791eb546 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Sat, 28 Oct 2023 16:51:44 +0100 Subject: [PATCH 04/32] Bump version -> `2.0.0-SNAPSHOT.161` --- license-report.md | 24 ++++++++++++------------ pom.xml | 2 +- version.gradle.kts | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/license-report.md b/license-report.md index 6330185004a..69df68ab262 100644 --- a/license-report.md +++ b/license-report.md @@ -1,6 +1,6 @@ -# Dependencies of `io.spine:spine-client:2.0.0-SNAPSHOT.160` +# Dependencies of `io.spine:spine-client:2.0.0-SNAPSHOT.161` ## Runtime 1. **Group** : com.google.android. **Name** : annotations. **Version** : 4.1.1.4. @@ -800,12 +800,12 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 13 00:27:07 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Sat Oct 28 16:50:16 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine:spine-core:2.0.0-SNAPSHOT.160` +# Dependencies of `io.spine:spine-core:2.0.0-SNAPSHOT.161` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -1565,12 +1565,12 @@ This report was generated on **Fri Oct 13 00:27:07 WEST 2023** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 13 00:27:08 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Sat Oct 28 16:50:17 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine:spine-server:2.0.0-SNAPSHOT.160` +# Dependencies of `io.spine:spine-server:2.0.0-SNAPSHOT.161` ## Runtime 1. **Group** : com.google.android. **Name** : annotations. **Version** : 4.1.1.4. @@ -2378,12 +2378,12 @@ This report was generated on **Fri Oct 13 00:27:08 WEST 2023** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 13 00:27:08 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Sat Oct 28 16:50:17 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:spine-testutil-client:2.0.0-SNAPSHOT.160` +# Dependencies of `io.spine.tools:spine-testutil-client:2.0.0-SNAPSHOT.161` ## Runtime 1. **Group** : com.google.android. **Name** : annotations. **Version** : 4.1.1.4. @@ -3311,12 +3311,12 @@ This report was generated on **Fri Oct 13 00:27:08 WEST 2023** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 13 00:27:09 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Sat Oct 28 16:50:18 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:spine-testutil-core:2.0.0-SNAPSHOT.160` +# Dependencies of `io.spine.tools:spine-testutil-core:2.0.0-SNAPSHOT.161` ## Runtime 1. **Group** : com.google.android. **Name** : annotations. **Version** : 4.1.1.4. @@ -4244,12 +4244,12 @@ This report was generated on **Fri Oct 13 00:27:09 WEST 2023** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 13 00:27:10 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Sat Oct 28 16:50:18 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:spine-testutil-server:2.0.0-SNAPSHOT.160` +# Dependencies of `io.spine.tools:spine-testutil-server:2.0.0-SNAPSHOT.161` ## Runtime 1. **Group** : com.google.android. **Name** : annotations. **Version** : 4.1.1.4. @@ -5225,4 +5225,4 @@ This report was generated on **Fri Oct 13 00:27:10 WEST 2023** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Fri Oct 13 00:27:10 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file +This report was generated on **Sat Oct 28 16:50:19 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file diff --git a/pom.xml b/pom.xml index ef23dc312fb..c044183239c 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ all modules and does not describe the project structure per-subproject. --> io.spine spine-core-java -2.0.0-SNAPSHOT.160 +2.0.0-SNAPSHOT.161 2015 diff --git a/version.gradle.kts b/version.gradle.kts index 9ede62134fb..6549ecbd082 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -29,4 +29,4 @@ * * For versions of Spine-based dependencies, please see [io.spine.internal.dependency.Spine]. */ -val versionToPublish: String by extra("2.0.0-SNAPSHOT.160") +val versionToPublish: String by extra("2.0.0-SNAPSHOT.161") From e0fc1fc897a9d9ee784cb1258d1997e5eca112ad Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Sun, 29 Oct 2023 19:22:01 +0000 Subject: [PATCH 05/32] Introduce `Probe` --- .../testing/server/blackbox/BlackBox.java | 48 ++++-------- .../server/blackbox/BlackBoxSetup.java | 2 +- .../server/blackbox/CommandCollector.java | 2 +- .../server/blackbox/EventCollector.java | 2 +- .../server/blackbox/FailedHandlerGuard.java | 2 +- .../server/blackbox/MessageCollector.java | 2 +- .../spine/testing/server/blackbox/Probe.java | 76 +++++++++++++++++++ 7 files changed, 96 insertions(+), 38 deletions(-) create mode 100644 testutil-server/src/main/java/io/spine/testing/server/blackbox/Probe.java diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java index eedbd2ee1b2..0a6ad44f652 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java @@ -94,16 +94,20 @@ public abstract class BlackBox implements WithLogging, Closeable { */ private @MonotonicNonNull BoundedContext context; + /** + * The probe inserted into the context under the test. + */ + private final Probe probe; + /** * A factory of {@link Client}s which send requests to this context. */ private @MonotonicNonNull ClientFactory clientFactory; /** - * Collects all commands, including posted to the context during its setup or - * generated by the entities in response to posted or generated events or commands. + * Information about the current user and the time-zone. */ - private final CommandCollector commands; + private Actor actor; /** * Commands received by this instance and posted to Command Bus during the test setup. @@ -113,12 +117,6 @@ public abstract class BlackBox implements WithLogging, Closeable { */ private final Set postedCommands; - /** - * Collects all events, including posted to the context during its setup or - * generated by the entities in response to posted or generated events or commands. - */ - private final EventCollector events; - /** * Events received by this instance and posted to the Event Bus during the test setup. * @@ -127,16 +125,6 @@ public abstract class BlackBox implements WithLogging, Closeable { */ private final Set postedEvents; - /** - * Handles runtime exceptions thrown from signal handlers. - */ - private final FailedHandlerGuard failedHandlerGuard; - - /** - * Information about the current user and the time-zone. - */ - private Actor actor; - /** * Creates new instance obtaining configuration parameters from the passed builder. */ @@ -163,22 +151,16 @@ public static Supplier initLazilyFrom(BoundedContextBuilder builder) { BlackBox(BoundedContextBuilder builder) { super(); - this.commands = new CommandCollector(); - this.postedCommands = synchronizedSet(new HashSet<>()); - this.events = new EventCollector(); - this.postedEvents = synchronizedSet(new HashSet<>()); - this.failedHandlerGuard = new FailedHandlerGuard(); - this.actor = defaultActor(); - builder.addCommandListener(commands) - .addEventListener(events) - .addEventDispatcher(failedHandlerGuard) - .addEventDispatcher(new UnsupportedCommandGuard(builder.name().getValue())) - .addEventDispatcher(DiagnosticLog.instance()); + this.probe = new Probe(); + probe.installIn(builder); var created = this; builder.setOnBuild(context -> { created.clientFactory = new ClientFactory(context); created.context = context; }); + this.actor = defaultActor(); + this.postedCommands = synchronizedSet(new HashSet<>()); + this.postedEvents = synchronizedSet(new HashSet<>()); } /** @@ -221,7 +203,7 @@ public final BlackBox in(ZoneId zoneId) { */ @CanIgnoreReturnValue public final BlackBox tolerateFailures() { - failedHandlerGuard.tolerateFailures(); + probe.failedHandlerGuard().tolerateFailures(); return this; } @@ -513,7 +495,7 @@ public boolean isOpen() { private ImmutableList commands() { var wasNotReceived = ((Predicate) postedCommands::contains).negate(); - return select(this.commands) + return select(probe.commands()) .stream() .filter(wasNotReceived) .collect(toImmutableList()); @@ -548,7 +530,7 @@ private ImmutableList events() { */ @VisibleForTesting final ImmutableList allEvents() { - return select(this.events); + return select(probe.events()); } /** diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBoxSetup.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBoxSetup.java index 437875c97f1..62195d18727 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBoxSetup.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBoxSetup.java @@ -54,7 +54,7 @@ import static java.util.stream.Collectors.toList; /** - * A class which sets up a {@link BlackBox}. + * A class which configures a test environment in which {@link BlackBox} is used. * *

The setup may involve: *

    diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/CommandCollector.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/CommandCollector.java index 015dd770425..3a91478e4e7 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/CommandCollector.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/CommandCollector.java @@ -33,5 +33,5 @@ /** * Remembers commands posted to a Command Bus. */ -final class CommandCollector extends MessageCollector { +public final class CommandCollector extends MessageCollector { } diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/EventCollector.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/EventCollector.java index 1812d9faf1f..92872356b6e 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/EventCollector.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/EventCollector.java @@ -33,5 +33,5 @@ /** * Remembers events posted to an Event Bus. */ -final class EventCollector extends MessageCollector { +public final class EventCollector extends MessageCollector { } diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/FailedHandlerGuard.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/FailedHandlerGuard.java index 634662b5924..d62bba67018 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/FailedHandlerGuard.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/FailedHandlerGuard.java @@ -44,7 +44,7 @@ * Performs logging of failed signals handlers or fails the test * depending upon the current context exception tolerance. */ -final class FailedHandlerGuard extends AbstractEventSubscriber implements DiagnosticLogging { +public final class FailedHandlerGuard extends AbstractEventSubscriber implements DiagnosticLogging { private HandlerFailureTolerance tolerance = HandlerFailureTolerance.RAISE_AND_FAIL; diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/MessageCollector.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/MessageCollector.java index 3e36df09dc7..d5df7c42463 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/MessageCollector.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/MessageCollector.java @@ -57,7 +57,7 @@ * @param * the type of the message envelopes */ -abstract class MessageCollector, E extends MessageEnvelope> implements Listener { diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/Probe.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/Probe.java new file mode 100644 index 00000000000..b2750bda458 --- /dev/null +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/Probe.java @@ -0,0 +1,76 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed 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 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.testing.server.blackbox; + +import io.spine.server.BoundedContextBuilder; + +public class Probe { + + /** + * Collects all commands, including posted to the context during its setup or + * generated by the entities in response to posted or generated events or commands. + */ + private final CommandCollector commands; + + /** + * Collects all events, including posted to the context during its setup or + * generated by the entities in response to posted or generated events or commands. + */ + private final EventCollector events; + + /** + * Handles runtime exceptions thrown from signal handlers. + */ + private final FailedHandlerGuard failedHandlerGuard; + + Probe() { + this.commands = new CommandCollector(); + this.events = new EventCollector(); + this.failedHandlerGuard = new FailedHandlerGuard(); + } + + public CommandCollector commands() { + return commands; + } + + public EventCollector events() { + return events; + } + + public FailedHandlerGuard failedHandlerGuard() { + return failedHandlerGuard; + } + + public void installIn(BoundedContextBuilder builder) { + builder.addCommandListener(commands()) + .addEventListener(events()) + .addEventDispatcher(failedHandlerGuard()) + .addEventDispatcher(new UnsupportedCommandGuard(builder.name() + .getValue())) + .addEventDispatcher(DiagnosticLog.instance()); + } +} From e05f1627d2262541165bb7d3973da505852bf049 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 30 Oct 2023 14:31:33 +0000 Subject: [PATCH 06/32] Teach `BoundedContext` swallowing `Probe` --- .../java/io/spine/server/BoundedContext.java | 102 ++++++++++++++++-- .../java/io/spine/server/ContextAware.java | 16 ++- .../io/spine/server/ServerEnvironment.java | 2 +- .../main/java/io/spine/server/bus/Bus.java | 32 ++++-- .../server/bus/DelegatingDispatcher.java | 42 ++++++++ .../spine/server/bus/DispatcherDelegate.java | 35 ++++++ .../spine/server/bus/DispatcherRegistry.java | 12 +++ .../java/io/spine/server/bus/Listeners.java | 18 +++- .../commandbus/CommandDispatcherDelegate.java | 6 +- .../DelegatingCommandDispatcher.java | 10 +- .../event/DelegatingEventDispatcher.java | 10 +- .../server/event/EventDispatcherDelegate.java | 5 +- .../io/spine/system/server/SystemContext.java | 4 +- .../AggregateHistoryTruncationTest.java | 86 +++++++-------- .../testing/server/blackbox/BlackBox.java | 5 +- .../testing/server/blackbox/MtBlackBox.java | 2 + .../testing/server/blackbox/StBlackBox.java | 3 +- .../testing/server/blackbox/package-info.java | 2 +- .../{ => probe}/CommandCollector.java | 4 +- .../blackbox/{ => probe}/DiagnosticLog.java | 4 +- .../{ => probe}/DiagnosticLogging.java | 2 +- .../blackbox/{ => probe}/EventCollector.java | 4 +- .../{ => probe}/FailedHandlerGuard.java | 6 +- .../{ => probe}/HandlerFailureTolerance.java | 6 +- .../{ => probe}/MessageCollector.java | 4 +- .../server/blackbox/{ => probe}/Probe.java | 57 ++++++++-- .../{ => probe}/UnsupportedCommandGuard.java | 5 +- .../server/blackbox/probe/package-info.java | 37 +++++++ .../blackbox/UnsupportedCommandGuardTest.java | 75 ------------- .../given/BbProjectFailerProcess.java | 3 +- .../{ => probe}/DiagnosticLogTest.java | 4 +- .../{ => probe}/DiagnosticLoggingTest.java | 5 +- .../{ => probe}/FailedHandlerGuardTest.java | 4 +- .../probe/UnsupportedCommandGuardSpec.kt | 70 ++++++++++++ 34 files changed, 500 insertions(+), 182 deletions(-) create mode 100644 server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java create mode 100644 server/src/main/java/io/spine/server/bus/DispatcherDelegate.java rename testutil-server/src/main/java/io/spine/testing/server/blackbox/{ => probe}/CommandCollector.java (93%) rename testutil-server/src/main/java/io/spine/testing/server/blackbox/{ => probe}/DiagnosticLog.java (97%) rename testutil-server/src/main/java/io/spine/testing/server/blackbox/{ => probe}/DiagnosticLogging.java (98%) rename testutil-server/src/main/java/io/spine/testing/server/blackbox/{ => probe}/EventCollector.java (93%) rename testutil-server/src/main/java/io/spine/testing/server/blackbox/{ => probe}/FailedHandlerGuard.java (98%) rename testutil-server/src/main/java/io/spine/testing/server/blackbox/{ => probe}/HandlerFailureTolerance.java (91%) rename testutil-server/src/main/java/io/spine/testing/server/blackbox/{ => probe}/MessageCollector.java (97%) rename testutil-server/src/main/java/io/spine/testing/server/blackbox/{ => probe}/Probe.java (62%) rename testutil-server/src/main/java/io/spine/testing/server/blackbox/{ => probe}/UnsupportedCommandGuard.java (97%) create mode 100644 testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/package-info.java delete mode 100644 testutil-server/src/test/java/io/spine/testing/server/blackbox/UnsupportedCommandGuardTest.java rename testutil-server/src/test/java/io/spine/testing/server/blackbox/{ => probe}/DiagnosticLogTest.java (98%) rename testutil-server/src/test/java/io/spine/testing/server/blackbox/{ => probe}/DiagnosticLoggingTest.java (95%) rename testutil-server/src/test/java/io/spine/testing/server/blackbox/{ => probe}/FailedHandlerGuardTest.java (96%) create mode 100644 testutil-server/src/test/kotlin/io/spine/testing/server/blackbox/probe/UnsupportedCommandGuardSpec.kt diff --git a/server/src/main/java/io/spine/server/BoundedContext.java b/server/src/main/java/io/spine/server/BoundedContext.java index 6c3e4225c25..f2bc047ff60 100644 --- a/server/src/main/java/io/spine/server/BoundedContext.java +++ b/server/src/main/java/io/spine/server/BoundedContext.java @@ -34,6 +34,7 @@ import io.spine.option.EntityOption.Visibility; import io.spine.server.aggregate.AggregateRootDirectory; import io.spine.server.aggregate.ImportBus; +import io.spine.server.bus.Listener; import io.spine.server.commandbus.CommandBus; import io.spine.server.commandbus.CommandDispatcher; import io.spine.server.commandbus.CommandDispatcherDelegate; @@ -52,6 +53,8 @@ import io.spine.server.storage.StorageFactory; import io.spine.server.tenant.TenantIndex; import io.spine.server.trace.TracerFactory; +import io.spine.server.type.CommandEnvelope; +import io.spine.server.type.EventEnvelope; import io.spine.system.server.SystemClient; import io.spine.system.server.SystemContext; import io.spine.type.TypeName; @@ -69,7 +72,7 @@ /** * A logical and structural boundary of a model. * - *

    Logically, a Bounded Context represents a sub-system built to be described with the same + *

    Logically, a Bounded Context represents a subsystem built to be described with the same * Ubiquitous Language. Any term within a single bounded context has a single meaning and may or * may not map to another term in the language of another Bounded Context. * @@ -120,11 +123,16 @@ public abstract class BoundedContext /** The index of tenants having data in this context. */ private final TenantIndex tenantIndex; - /** Provides access to internally-used features of the context. */ + /** Provides access to internally used features of the context. */ private final InternalAccess internalAccess; private final @Nullable Consumer onBeforeClose; + /** + * The currently installed probe. + */ + private @Nullable Probe probe; + /** * Creates new instance. * @@ -149,7 +157,6 @@ protected BoundedContext(BoundedContextBuilder builder) { this.aggregateRootDirectory = builder.aggregateRootDirectory(); this.internalAccess = new InternalAccess(); this.onBeforeClose = builder.getOnBeforeClose(); - } /** @@ -238,6 +245,7 @@ protected final void register(Repository repository) { * * @see #registerCommandDispatcher(CommandDispatcherDelegate) */ + @SuppressWarnings("WeakerAccess") // This class is effectively `sealed`, so this is stricter. protected void registerCommandDispatcher(CommandDispatcher dispatcher) { checkNotNull(dispatcher); registerIfAware(dispatcher); @@ -262,6 +270,13 @@ private void registerCommandDispatcher(CommandDispatcherDelegate dispatcher) { } } + private void unregisterCommandDispatcher(CommandDispatcherDelegate dispatcher) { + checkNotNull(dispatcher); + if (dispatcher.dispatchesCommands()) { + commandBus().unregister(dispatcher); + } + } + /** * Registering the passed event dispatcher with the buses of this context. * @@ -290,6 +305,25 @@ protected void registerEventDispatcher(EventDispatcher dispatcher) { } } + protected void unregisterEventDispatcher(EventDispatcher dispatcher) { + checkNotNull(dispatcher); + Security.allowOnlyFrameworkServer(); + unregisterIfAware(dispatcher); + if (dispatcher.dispatchesEvents()) { + var eventBus = eventBus(); + eventBus.unregister(dispatcher); + var systemReadSide = systemClient().readSide(); + systemReadSide.unregister(dispatcher); + } + if (dispatcher.dispatchesExternalEvents()) { + broker.unregister(dispatcher); + } + if (dispatcher instanceof CommandDispatcherDelegate) { + var commandDispatcher = (CommandDispatcherDelegate) dispatcher; + unregisterCommandDispatcher(commandDispatcher); + } + } + /** * Registers the passed delegate of an {@link EventDispatcher} with the buses of this context. * @@ -314,6 +348,19 @@ private void registerIfAware(Object contextPart) { } } + /** + * If the given {@code contextPart} is {@link ContextAware}, + * {@linkplain ContextAware#unregister() unregisters} it from this context. + */ + private static void unregisterIfAware(Object contextPart) { + if (contextPart instanceof ContextAware) { + var contextAware = (ContextAware) contextPart; + if (contextAware.isRegistered()) { + contextAware.unregister(); + } + } + } + /** * Obtains a set of entity type names by their visibility. */ @@ -440,7 +487,9 @@ public void close() throws Exception { if (isOpen) { shutDownRepositories(); } - + if (hasProbe()) { + removeProbe(); + } if (isOpen) { logger().atDebug().log(() -> format("%s", closed(nameForLogging()))); } @@ -503,6 +552,39 @@ public int compareTo(BoundedContext another) { return name().value().compareTo(another.name().value()); } + public final void install(Probe probe) { + checkNotNull(probe); + checkState(this.probe == null, + "Probe is already installed (`%s`). Please remove previous probe first.", probe); + probe.registerWith(this); + commandBus.add(probe.commandListener()); + eventBus.add(probe.eventListener()); + probe.eventDispatchers() + .forEach(this::registerEventDispatcher); + + this.probe = probe; + } + + /** + * Removes the currently installed probe, unregistering it with this context. + * + * @throws IllegalStateException + * if no probe was installed before + */ + public final void removeProbe() { + checkState(probe != null, "Probe is not installed."); + commandBus.remove(probe.commandListener()); + eventBus.remove(probe.eventListener()); + probe.eventDispatchers() + .forEach(this::unregisterEventDispatcher); + probe.unregister(); + probe = null; + } + + public final boolean hasProbe() { + return probe != null; + } + /** * Returns {@code true} if another bounded context has the same name as this one, * {@code false} otherwise. @@ -524,13 +606,19 @@ public int hashCode() { return name().hashCode(); } + public interface Probe extends ContextAware { + Listener commandListener(); + Listener eventListener(); + Set eventDispatchers(); + } + /** * Provides access to features of {@link BoundedContext} used internally by the framework. */ @Internal public final class InternalAccess { - /** Prevents instantiation from outside. */ + /** Prevents instantiation from the outside. */ private InternalAccess() { } @@ -574,7 +662,7 @@ public InternalAccess registerEventDispatcher(EventDispatcher dispatcher) { * Obtains repositories of the context. * * @throws IllegalStateException - * if there is not repository entities of which have the passed state + * if there are no repository entities of which have the passed state */ public Repository getRepository(Class> stateClass) { return guard.get(stateClass); @@ -591,7 +679,7 @@ public InternalAccess registerEventDispatcher(EventDispatcher dispatcher) { * the requested entity is {@linkplain Visibility#NONE not visible}. * * @param stateCls - * the class of the state of the entity managed by the resulting repository + * the class of the entity state managed by the resulting repository * @return the requested repository or {@link Optional#empty()} if the repository manages * a {@linkplain Visibility#NONE non-visible} entity * @throws IllegalStateException diff --git a/server/src/main/java/io/spine/server/ContextAware.java b/server/src/main/java/io/spine/server/ContextAware.java index fb915b46f71..bb20a77a1eb 100644 --- a/server/src/main/java/io/spine/server/ContextAware.java +++ b/server/src/main/java/io/spine/server/ContextAware.java @@ -28,6 +28,8 @@ import io.spine.annotation.Internal; +import javax.annotation.OverridingMethodsMustInvokeSuper; + import static com.google.common.base.Preconditions.checkState; /** @@ -53,6 +55,18 @@ public interface ContextAware { */ boolean isRegistered(); + /** + * Unregisters this instance from the Bounded Context with which this instance + * was {@linkplain #registerWith(BoundedContext) registered before}. + * + *

    The default implementation checks if this instance is registered and does nothing else. + * Override this method to perform additional actions, making sure to call the super method. + */ + @OverridingMethodsMustInvokeSuper + default void unregister() { + checkRegistered(); + } + /** * Verifies that this instance is already registered. * @@ -68,6 +82,6 @@ default void checkRegistered() { *

    Throws an {@code IllegalStateException} if already registered. */ default void checkNotRegistered() { - checkState(!isRegistered(), "%s is already registered.", this); + checkState(!isRegistered(), "`%s` is already registered.", this); } } diff --git a/server/src/main/java/io/spine/server/ServerEnvironment.java b/server/src/main/java/io/spine/server/ServerEnvironment.java index d8c47f9498a..3897bff68d3 100644 --- a/server/src/main/java/io/spine/server/ServerEnvironment.java +++ b/server/src/main/java/io/spine/server/ServerEnvironment.java @@ -343,7 +343,7 @@ public static TypeConfigurator when(Class> type) { } /** - * Allows to configure values used by the {@code ServerEnvironment} for the given type. + * Allows configuring values used by the {@code ServerEnvironment} for the given type. */ public static class TypeConfigurator { diff --git a/server/src/main/java/io/spine/server/bus/Bus.java b/server/src/main/java/io/spine/server/bus/Bus.java index 736b8c04134..7b21b549c4a 100644 --- a/server/src/main/java/io/spine/server/bus/Bus.java +++ b/server/src/main/java/io/spine/server/bus/Bus.java @@ -93,7 +93,8 @@ protected Bus(BusBuilder builder) { * exposed} by the dispatcher is empty */ public void register(D dispatcher) { - registry().register(checkNotNull(dispatcher)); + checkNotNull(dispatcher); + registry().register(dispatcher); } /** @@ -102,14 +103,29 @@ public void register(D dispatcher) { * @param dispatcher the dispatcher to unregister */ public void unregister(D dispatcher) { - registry().unregister(checkNotNull(dispatcher)); + checkNotNull(dispatcher); + registry().unregister(dispatcher); + } + + public void unregister(DispatcherDelegate delegate) { + checkNotNull(delegate); + } + + public void add(Listener listener) { + checkNotNull(listener); + listeners.add(listener); + } + + public void remove(Listener listener) { + checkNotNull(listener); + listeners.remove(listener); } /** * Posts the message to the bus. * * @param message the message to post - * @param observer the observer to receive outcome of the operation + * @param observer the observer to receive an outcome of the operation * @see #post(Iterable, StreamObserver) for posing multiple messages at once */ public final void post(T message, StreamObserver observer) { @@ -132,18 +148,18 @@ public final void post(T message, StreamObserver observer) { * {@link io.spine.base.Error Error} status is passed in {@code Ack} instance. * *

    Depending on the underlying {@link MessageDispatcher}, a message which causes a business - * {@linkplain io.spine.base.RejectionThrowable rejection} may result either a rejection status or - * an {@link io.spine.core.Status.StatusCase#OK OK} status {@link Ack} instance. + * {@linkplain io.spine.base.RejectionThrowable rejection} may result either a rejection + * status or an {@link io.spine.core.Status.StatusCase#OK OK} status {@link Ack} instance. * The rejection status may only pop up if the {@link MessageDispatcher} processes the message * sequentially and throws the rejection (wrapped in a - * the {@linkplain io.spine.base.RejectionThrowable RejectionThrowables}) instead of handling them. - * Otherwise, the {@code OK} status should be expected. + * the {@linkplain io.spine.base.RejectionThrowable RejectionThrowables}) instead of + * handling them. Otherwise, the {@code OK} status should be expected. * *

    Note that {@linkplain StreamObserver#onError StreamObserver.onError()} is never called * for the passed observer, since errors are propagated as statuses of {@code Ack} response. * * @param messages the messages to post - * @param observer the observer to receive outcome of the operation + * @param observer the observer to receive the outcome of the operation */ public final void post(Iterable messages, StreamObserver observer) { checkNotNull(messages); diff --git a/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java b/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java new file mode 100644 index 00000000000..ed21cdd714b --- /dev/null +++ b/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java @@ -0,0 +1,42 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed 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 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.server.bus; + +import io.spine.annotation.Internal; +import io.spine.server.type.MessageEnvelope; +import io.spine.type.MessageClass; + +@Internal +public interface DelegatingDispatcher, + E extends MessageEnvelope> + extends MessageDispatcher { + + /** + * Obtains the dispatcher to which the messages should be delegated. + */ + DispatcherDelegate delegate(); +} diff --git a/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java b/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java new file mode 100644 index 00000000000..17fd40fe0f2 --- /dev/null +++ b/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java @@ -0,0 +1,35 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed 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 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.server.bus; + +import io.spine.server.type.MessageEnvelope; +import io.spine.type.MessageClass; + +public interface DispatcherDelegate, + E extends MessageEnvelope> { + +} diff --git a/server/src/main/java/io/spine/server/bus/DispatcherRegistry.java b/server/src/main/java/io/spine/server/bus/DispatcherRegistry.java index a3024e6f0d3..42a7f2fa3dd 100644 --- a/server/src/main/java/io/spine/server/bus/DispatcherRegistry.java +++ b/server/src/main/java/io/spine/server/bus/DispatcherRegistry.java @@ -86,6 +86,18 @@ public void unregister(D dispatcher) { } } + public void unregister(DispatcherDelegate delegate) { + checkNotNull(delegate); + @SuppressWarnings("unchecked") // Checked with `instanceof`. + var delegatingDispatcher = dispatchers.values().stream() + .filter(d -> d instanceof DelegatingDispatcher) + .map(d -> (DelegatingDispatcher) d) + .filter(d -> d.delegate().equals(delegate)) + .map(d -> (D) d) + .findFirst(); + delegatingDispatcher.ifPresent(this::unregister); + } + /** * Unregisters all dispatchers. */ diff --git a/server/src/main/java/io/spine/server/bus/Listeners.java b/server/src/main/java/io/spine/server/bus/Listeners.java index 3183112763e..e7325a34b87 100644 --- a/server/src/main/java/io/spine/server/bus/Listeners.java +++ b/server/src/main/java/io/spine/server/bus/Listeners.java @@ -27,12 +27,14 @@ package io.spine.server.bus; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableSet; import io.spine.server.type.SignalEnvelope; +import java.util.HashSet; +import java.util.Set; import java.util.function.Consumer; import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Collections.synchronizedSet; /** * Manages consumption of a message posted to the bus by its listeners. @@ -41,11 +43,21 @@ */ final class Listeners> implements Consumer { - private final ImmutableSet> listeners; + private final Set> listeners = synchronizedSet(new HashSet<>()); Listeners(BusBuilder builder) { checkNotNull(builder); - this.listeners = ImmutableSet.copyOf(builder.listeners()); + listeners.addAll(builder.listeners()); + } + + void add(Listener listener) { + checkNotNull(listener); + listeners.add(listener); + } + + void remove(Listener listener) { + checkNotNull(listener); + listeners.remove(listener); } @Override diff --git a/server/src/main/java/io/spine/server/commandbus/CommandDispatcherDelegate.java b/server/src/main/java/io/spine/server/commandbus/CommandDispatcherDelegate.java index f256d888363..2fb22dd56c7 100644 --- a/server/src/main/java/io/spine/server/commandbus/CommandDispatcherDelegate.java +++ b/server/src/main/java/io/spine/server/commandbus/CommandDispatcherDelegate.java @@ -27,6 +27,7 @@ import com.google.common.collect.ImmutableSet; import io.spine.annotation.Internal; +import io.spine.server.bus.DispatcherDelegate; import io.spine.server.dispatch.DispatchOutcome; import io.spine.server.type.CommandClass; import io.spine.server.type.CommandEnvelope; @@ -61,7 +62,8 @@ * @see DelegatingCommandDispatcher */ @Internal -public interface CommandDispatcherDelegate { +public interface CommandDispatcherDelegate + extends DispatcherDelegate { /** * Obtains the classes of dispatched commands. @@ -69,7 +71,7 @@ public interface CommandDispatcherDelegate { ImmutableSet commandClasses(); /** - * Dispatches the command and returns the outcome of the dispatching. + * Dispatches the command and returns the outcome of the dispatching. */ DispatchOutcome dispatchCommand(CommandEnvelope envelope); diff --git a/server/src/main/java/io/spine/server/commandbus/DelegatingCommandDispatcher.java b/server/src/main/java/io/spine/server/commandbus/DelegatingCommandDispatcher.java index e7187312dcb..50e1829ffe5 100644 --- a/server/src/main/java/io/spine/server/commandbus/DelegatingCommandDispatcher.java +++ b/server/src/main/java/io/spine/server/commandbus/DelegatingCommandDispatcher.java @@ -28,6 +28,7 @@ import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableSet; import io.spine.annotation.Internal; +import io.spine.server.bus.DelegatingDispatcher; import io.spine.server.dispatch.DispatchOutcome; import io.spine.server.type.CommandClass; import io.spine.server.type.CommandEnvelope; @@ -39,7 +40,9 @@ * @see CommandDispatcherDelegate */ @Internal -public class DelegatingCommandDispatcher implements CommandDispatcher { +public class DelegatingCommandDispatcher + implements CommandDispatcher, + DelegatingDispatcher { /** * A target delegate. @@ -70,6 +73,11 @@ public final DispatchOutcome dispatch(CommandEnvelope envelope) { return delegate.dispatchCommand(envelope); } + @Override + public CommandDispatcherDelegate delegate() { + return delegate; + } + /** * Returns the string representation of this dispatcher. * diff --git a/server/src/main/java/io/spine/server/event/DelegatingEventDispatcher.java b/server/src/main/java/io/spine/server/event/DelegatingEventDispatcher.java index 8a8be712b7c..cd3729869bc 100644 --- a/server/src/main/java/io/spine/server/event/DelegatingEventDispatcher.java +++ b/server/src/main/java/io/spine/server/event/DelegatingEventDispatcher.java @@ -29,6 +29,7 @@ import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableSet; import io.spine.annotation.Internal; +import io.spine.server.bus.DelegatingDispatcher; import io.spine.server.dispatch.DispatchOutcome; import io.spine.server.type.EventClass; import io.spine.server.type.EventEnvelope; @@ -42,7 +43,9 @@ * @see EventDispatcherDelegate */ @Internal -public final class DelegatingEventDispatcher implements EventDispatcher { +public final class DelegatingEventDispatcher + implements EventDispatcher, + DelegatingDispatcher { /** * A target delegate. @@ -90,6 +93,11 @@ public boolean canDispatch(EventEnvelope envelope) { return delegate.canDispatchEvent(envelope); } + @Override + public EventDispatcherDelegate delegate() { + return delegate; + } + /** * Returns the string representation of this dispatcher. * diff --git a/server/src/main/java/io/spine/server/event/EventDispatcherDelegate.java b/server/src/main/java/io/spine/server/event/EventDispatcherDelegate.java index 59ea5b1419c..ad6b8fcacae 100644 --- a/server/src/main/java/io/spine/server/event/EventDispatcherDelegate.java +++ b/server/src/main/java/io/spine/server/event/EventDispatcherDelegate.java @@ -28,6 +28,7 @@ import com.google.common.collect.ImmutableSet; import io.spine.annotation.Internal; +import io.spine.server.bus.DispatcherDelegate; import io.spine.server.dispatch.DispatchOutcome; import io.spine.server.type.EventClass; import io.spine.server.type.EventEnvelope; @@ -44,7 +45,7 @@ * @see DelegatingEventDispatcher */ @Internal -public interface EventDispatcherDelegate { +public interface EventDispatcherDelegate extends DispatcherDelegate { /** * Obtains all event classes dispatched by this delegate. @@ -96,7 +97,7 @@ default boolean dispatchesExternalEvents() { /** * Checks if this dispatcher can dispatch the given event. * - *

    By default, all events are permitted. Implementations may change this behaviour to reject + *

    By default, all events are permitted. Implementations may change this behavior to reject * certain events as early as possible. * * @param envelope diff --git a/server/src/main/java/io/spine/system/server/SystemContext.java b/server/src/main/java/io/spine/system/server/SystemContext.java index 712177d1f29..bb444bf3b14 100644 --- a/server/src/main/java/io/spine/system/server/SystemContext.java +++ b/server/src/main/java/io/spine/system/server/SystemContext.java @@ -108,8 +108,8 @@ public SystemClient createClient() { /** * {@inheritDoc} * - *

    Since a system bounded context does not have an associated system bounded context, returns - * a {@link NoOpSystemWriteSide} instance. + *

    Since a system-bounded context does not have an associated system bounded context, + * the method returns a {@link NoOpSystemWriteSide} instance. */ @Override public NoOpSystemClient systemClient() { diff --git a/server/src/test/java/io/spine/server/aggregate/AggregateHistoryTruncationTest.java b/server/src/test/java/io/spine/server/aggregate/AggregateHistoryTruncationTest.java index 59041a4efbe..ba0187ceca5 100644 --- a/server/src/test/java/io/spine/server/aggregate/AggregateHistoryTruncationTest.java +++ b/server/src/test/java/io/spine/server/aggregate/AggregateHistoryTruncationTest.java @@ -84,8 +84,7 @@ @SuppressWarnings("AbstractClassWithoutAbstractMethods") // designed for the various storage impls. public abstract class AggregateHistoryTruncationTest { - private static final SequenceId ID = SequenceId - .newBuilder() + private static final SequenceId ID = SequenceId.newBuilder() .setValue(newUuid()) .build(); @@ -101,49 +100,50 @@ void restoreAggregateState() { BoundedContextBuilder.assumingTests() .add(repo) ); - - // Set the starting numbers. - var setStartingNumbers = SetStartingNumbers.newBuilder() - .setId(ID) - .setNumberOne(0) - .setNumberTwo(1) - .build(); - context.receivesCommand(setStartingNumbers) - .assertEvents() - .withType(StartingNumbersSet.class) - .hasSize(1); - // Send a lot of `MoveSequence` events, so several snapshots are created. - var moveSequence = MoveSequence.newBuilder() - .setId(ID) - .build(); - var snapshotTrigger = repo.snapshotTrigger(); - for (var i = 0; i < snapshotTrigger * 5 + 1; i++) { + try (context) { + // Set the starting numbers. + var setStartingNumbers = SetStartingNumbers.newBuilder() + .setId(ID) + .setNumberOne(0) + .setNumberTwo(1) + .build(); + context.receivesCommand(setStartingNumbers) + .assertEvents() + .withType(StartingNumbersSet.class) + .hasSize(1); + // Send a lot of `MoveSequence` events, so several snapshots are created. + var moveSequence = MoveSequence.newBuilder() + .setId(ID) + .build(); + var snapshotTrigger = repo.snapshotTrigger(); + for (var i = 0; i < snapshotTrigger * 5 + 1; i++) { + context.receivesCommand(moveSequence); + } + + // Compare against the numbers calculated by hand. + var expectedNumberOne = 121393; + var expectedNumberTwo = 196418; + assertThat(lastNumberOne()) + .isEqualTo(expectedNumberOne); + assertThat(lastNumberTwo()) + .isEqualTo(expectedNumberTwo); + + // Truncate the storage. + var storage = repo.aggregateStorage(); + var countBeforeTruncate = recordCount(storage); + assertThat(countBeforeTruncate) + .isGreaterThan(snapshotTrigger); + storage.truncateOlderThan(0); + var countAfterTruncate = recordCount(storage); + assertThat(countAfterTruncate) + .isAtMost(snapshotTrigger); + + // Run one more command and check the result. + var expectedNext = lastNumberOne() + lastNumberTwo(); context.receivesCommand(moveSequence); + assertThat(lastNumberTwo()) + .isEqualTo(expectedNext); } - - // Compare against the numbers calculated by hand. - var expectedNumberOne = 121393; - var expectedNumberTwo = 196418; - assertThat(lastNumberOne()) - .isEqualTo(expectedNumberOne); - assertThat(lastNumberTwo()) - .isEqualTo(expectedNumberTwo); - - // Truncate the storage. - var storage = repo.aggregateStorage(); - var countBeforeTruncate = recordCount(storage); - assertThat(countBeforeTruncate) - .isGreaterThan(snapshotTrigger); - storage.truncateOlderThan(0); - var countAfterTruncate = recordCount(storage); - assertThat(countAfterTruncate) - .isAtMost(snapshotTrigger); - - // Run one more command and check the result. - var expectedNext = lastNumberOne() + lastNumberTwo(); - context.receivesCommand(moveSequence); - assertThat(lastNumberTwo()) - .isEqualTo(expectedNext); } private int recordCount(AggregateStorage storage) { diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java index 0a6ad44f652..0da9b3ca994 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java @@ -56,6 +56,9 @@ import io.spine.testing.server.CommandSubject; import io.spine.testing.server.EventSubject; import io.spine.testing.server.entity.EntitySubject; +import io.spine.testing.server.blackbox.probe.CommandCollector; +import io.spine.testing.server.blackbox.probe.EventCollector; +import io.spine.testing.server.blackbox.probe.Probe; import io.spine.testing.server.query.QueryResultSubject; import io.spine.time.ZoneId; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -152,10 +155,10 @@ public static Supplier initLazilyFrom(BoundedContextBuilder builder) { BlackBox(BoundedContextBuilder builder) { super(); this.probe = new Probe(); - probe.installIn(builder); var created = this; builder.setOnBuild(context -> { created.clientFactory = new ClientFactory(context); + context.install(probe); created.context = context; }); this.actor = defaultActor(); diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/MtBlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/MtBlackBox.java index 2298fc3f114..1cb02238ac4 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/MtBlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/MtBlackBox.java @@ -34,6 +34,8 @@ import io.spine.server.BoundedContextBuilder; import io.spine.server.tenant.TenantAwareRunner; import io.spine.testing.client.TestActorRequestFactory; +import io.spine.testing.server.blackbox.probe.CommandCollector; +import io.spine.testing.server.blackbox.probe.EventCollector; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/StBlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/StBlackBox.java index 6ed954ed7c3..73112183c3a 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/StBlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/StBlackBox.java @@ -33,7 +33,8 @@ import io.spine.core.TenantId; import io.spine.server.BoundedContextBuilder; import io.spine.testing.client.TestActorRequestFactory; -import org.checkerframework.checker.nullness.qual.Nullable; +import io.spine.testing.server.blackbox.probe.CommandCollector; +import io.spine.testing.server.blackbox.probe.EventCollector; import static com.google.protobuf.TextFormat.shortDebugString; import static io.spine.util.Exceptions.newIllegalStateException; diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/package-info.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/package-info.java index 362c89297e1..aca90f6206d 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/package-info.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/package-info.java @@ -32,7 +32,7 @@ *

    One such black box example is for {@link io.spine.testing.server.blackbox.BlackBox * Bounded Context testing}. It allows sending Commands and Events to the * {@link io.spine.server.BoundedContext Bounded Context} and then verifying their effect - * inside of the Bounded Context. + * inside the Bounded Context. * * @see io.spine.testing.server.blackbox.BlackBox */ diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/CommandCollector.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/CommandCollector.java similarity index 93% rename from testutil-server/src/main/java/io/spine/testing/server/blackbox/CommandCollector.java rename to testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/CommandCollector.java index 3a91478e4e7..0f515289034 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/CommandCollector.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/CommandCollector.java @@ -1,5 +1,5 @@ /* - * Copyright 2022, TeamDev. All rights reserved. + * Copyright 2023, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.testing.server.blackbox; +package io.spine.testing.server.blackbox.probe; import io.spine.core.Command; import io.spine.core.CommandId; diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/DiagnosticLog.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/DiagnosticLog.java similarity index 97% rename from testutil-server/src/main/java/io/spine/testing/server/blackbox/DiagnosticLog.java rename to testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/DiagnosticLog.java index 43a2f57db88..999e666346e 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/DiagnosticLog.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/DiagnosticLog.java @@ -1,5 +1,5 @@ /* - * Copyright 2022, TeamDev. All rights reserved. + * Copyright 2023, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.testing.server.blackbox; +package io.spine.testing.server.blackbox.probe; import io.spine.base.Identifier; import io.spine.core.Subscribe; diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/DiagnosticLogging.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/DiagnosticLogging.java similarity index 98% rename from testutil-server/src/main/java/io/spine/testing/server/blackbox/DiagnosticLogging.java rename to testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/DiagnosticLogging.java index bbe7c6f328a..9e6795694b6 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/DiagnosticLogging.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/DiagnosticLogging.java @@ -24,7 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.testing.server.blackbox; +package io.spine.testing.server.blackbox.probe; import com.google.errorprone.annotations.FormatMethod; import com.google.errorprone.annotations.FormatString; diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/EventCollector.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/EventCollector.java similarity index 93% rename from testutil-server/src/main/java/io/spine/testing/server/blackbox/EventCollector.java rename to testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/EventCollector.java index 92872356b6e..26fd3f3a526 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/EventCollector.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/EventCollector.java @@ -1,5 +1,5 @@ /* - * Copyright 2022, TeamDev. All rights reserved. + * Copyright 2023, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.testing.server.blackbox; +package io.spine.testing.server.blackbox.probe; import io.spine.core.Event; import io.spine.core.EventId; diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/FailedHandlerGuard.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/FailedHandlerGuard.java similarity index 98% rename from testutil-server/src/main/java/io/spine/testing/server/blackbox/FailedHandlerGuard.java rename to testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/FailedHandlerGuard.java index d62bba67018..3fcd836feb6 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/FailedHandlerGuard.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/FailedHandlerGuard.java @@ -24,7 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.testing.server.blackbox; +package io.spine.testing.server.blackbox.probe; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableSet; @@ -34,8 +34,8 @@ import io.spine.server.type.EventEnvelope; import io.spine.system.server.HandlerFailedUnexpectedly; -import static io.spine.type.Json.toJson; import static io.spine.server.dispatch.DispatchOutcomes.successfulOutcome; +import static io.spine.type.Json.toJson; import static java.lang.String.format; import static java.lang.System.lineSeparator; import static org.junit.jupiter.api.Assertions.fail; @@ -97,7 +97,7 @@ void on(HandlerFailedUnexpectedly event) { /** * Asks the guard to tolerate exceptions. */ - void tolerateFailures() { + public void tolerateFailures() { tolerance = HandlerFailureTolerance.LOG; } diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/HandlerFailureTolerance.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/HandlerFailureTolerance.java similarity index 91% rename from testutil-server/src/main/java/io/spine/testing/server/blackbox/HandlerFailureTolerance.java rename to testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/HandlerFailureTolerance.java index af6b3b6fa7d..c2dd85d59ad 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/HandlerFailureTolerance.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/HandlerFailureTolerance.java @@ -1,5 +1,5 @@ /* - * Copyright 2022, TeamDev. All rights reserved. + * Copyright 2023, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.testing.server.blackbox; +package io.spine.testing.server.blackbox.probe; + +import io.spine.testing.server.blackbox.BlackBox; /** * Configures the behavior of the {@link BlackBox} on handling runtime exceptions diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/MessageCollector.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/MessageCollector.java similarity index 97% rename from testutil-server/src/main/java/io/spine/testing/server/blackbox/MessageCollector.java rename to testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/MessageCollector.java index d5df7c42463..fac7a68a197 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/MessageCollector.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/MessageCollector.java @@ -1,5 +1,5 @@ /* - * Copyright 2022, TeamDev. All rights reserved. + * Copyright 2023, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.testing.server.blackbox; +package io.spine.testing.server.blackbox.probe; import com.google.common.collect.ImmutableList; import com.google.common.truth.extensions.proto.ProtoTruth; diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/Probe.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/Probe.java similarity index 62% rename from testutil-server/src/main/java/io/spine/testing/server/blackbox/Probe.java rename to testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/Probe.java index b2750bda458..89c02625bd7 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/Probe.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/Probe.java @@ -24,11 +24,24 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.testing.server.blackbox; +package io.spine.testing.server.blackbox.probe; +import com.google.common.collect.ImmutableSet; +import io.spine.server.BoundedContext; import io.spine.server.BoundedContextBuilder; +import io.spine.server.bus.Listener; +import io.spine.server.event.EventDispatcher; +import io.spine.server.type.CommandEnvelope; +import io.spine.server.type.EventEnvelope; +import org.checkerframework.checker.nullness.qual.Nullable; -public class Probe { +import java.util.Set; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class Probe implements BoundedContext.Probe { + + private @Nullable BoundedContext context; /** * Collects all commands, including posted to the context during its setup or @@ -47,12 +60,23 @@ public class Probe { */ private final FailedHandlerGuard failedHandlerGuard; - Probe() { + public Probe() { this.commands = new CommandCollector(); this.events = new EventCollector(); this.failedHandlerGuard = new FailedHandlerGuard(); } + @Override + public void registerWith(BoundedContext context) { + checkNotRegistered(); + this.context = checkNotNull(context); + } + + @Override + public boolean isRegistered() { + return context != null; + } + public CommandCollector commands() { return commands; } @@ -65,12 +89,25 @@ public FailedHandlerGuard failedHandlerGuard() { return failedHandlerGuard; } - public void installIn(BoundedContextBuilder builder) { - builder.addCommandListener(commands()) - .addEventListener(events()) - .addEventDispatcher(failedHandlerGuard()) - .addEventDispatcher(new UnsupportedCommandGuard(builder.name() - .getValue())) - .addEventDispatcher(DiagnosticLog.instance()); + @Override + public Listener commandListener() { + return commands(); + } + + @Override + public Listener eventListener() { + return events(); + } + + @Override + public Set eventDispatchers() { + checkRegistered(); + assert(context != null); + var commandGuard = new UnsupportedCommandGuard(context.name().value()); + return ImmutableSet.of( + failedHandlerGuard(), + commandGuard, + DiagnosticLog.instance() + ); } } diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/UnsupportedCommandGuard.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/UnsupportedCommandGuard.java similarity index 97% rename from testutil-server/src/main/java/io/spine/testing/server/blackbox/UnsupportedCommandGuard.java rename to testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/UnsupportedCommandGuard.java index 99bc8efbd0f..8f2a917b942 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/UnsupportedCommandGuard.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/UnsupportedCommandGuard.java @@ -1,5 +1,5 @@ /* - * Copyright 2022, TeamDev. All rights reserved. + * Copyright 2023, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.testing.server.blackbox; +package io.spine.testing.server.blackbox.probe; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableSet; @@ -35,6 +35,7 @@ import io.spine.server.type.EventClass; import io.spine.server.type.EventEnvelope; import io.spine.system.server.event.CommandErrored; +import io.spine.testing.server.blackbox.BlackBox; import org.checkerframework.checker.nullness.qual.Nullable; import static io.spine.core.CommandValidationError.UNSUPPORTED_COMMAND_VALUE; diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/package-info.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/package-info.java new file mode 100644 index 00000000000..0bf3d522413 --- /dev/null +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/package-info.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed 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 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * This package contains diagnostic utilities for + * {@link io.spine.server.BoundedContext io.spine.server.BoundedContext}. + */ +@CheckReturnValue +@ParametersAreNonnullByDefault +package io.spine.testing.server.blackbox.probe; + +import com.google.errorprone.annotations.CheckReturnValue; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/testutil-server/src/test/java/io/spine/testing/server/blackbox/UnsupportedCommandGuardTest.java b/testutil-server/src/test/java/io/spine/testing/server/blackbox/UnsupportedCommandGuardTest.java deleted file mode 100644 index 89a340b733a..00000000000 --- a/testutil-server/src/test/java/io/spine/testing/server/blackbox/UnsupportedCommandGuardTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2022, TeamDev. All rights reserved. - * - * Licensed 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 - * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package io.spine.testing.server.blackbox; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static com.google.common.truth.Truth.assertThat; -import static io.spine.testing.server.blackbox.given.GivenCommandError.duplicationError; -import static io.spine.testing.server.blackbox.given.GivenCommandError.nonValidationError; -import static io.spine.testing.server.blackbox.given.GivenCommandError.unsupportedError; - -@DisplayName("`UnsupportedCommandGuard` should") -class UnsupportedCommandGuardTest { - - private UnsupportedCommandGuard guard; - - @BeforeEach - void initGuard() { - guard = new UnsupportedCommandGuard(getClass().getName()); - } - - @Test - @DisplayName("check and remember the unsupported command error") - void checkAndRememberUnsupported() { - var commandType = "SomeCommand"; - var result = guard.checkAndRemember(unsupportedError(commandType)); - - assertThat(result).isTrue(); - assertThat(guard.commandType()).isEqualTo(commandType); - } - - @Test - @DisplayName("return `false` and ignore the command validation error with the different code") - void ignoreGenericValidationError() { - var result = guard.checkAndRemember(duplicationError()); - - assertThat(result).isFalse(); - assertThat(guard.commandType()).isNull(); - } - - @Test - @DisplayName("return `false` and ignore the command error of the different type") - void ignoreGenericCommandError() { - var result = guard.checkAndRemember(nonValidationError()); - - assertThat(result).isFalse(); - assertThat(guard.commandType()).isNull(); - } -} diff --git a/testutil-server/src/test/java/io/spine/testing/server/blackbox/given/BbProjectFailerProcess.java b/testutil-server/src/test/java/io/spine/testing/server/blackbox/given/BbProjectFailerProcess.java index fa1f5d47328..2d085e6325c 100644 --- a/testutil-server/src/test/java/io/spine/testing/server/blackbox/given/BbProjectFailerProcess.java +++ b/testutil-server/src/test/java/io/spine/testing/server/blackbox/given/BbProjectFailerProcess.java @@ -34,10 +34,11 @@ import io.spine.testing.server.blackbox.BbProjectId; import io.spine.testing.server.blackbox.command.BbFailProject; import io.spine.testing.server.blackbox.event.BbProjectFailed; +import io.spine.testing.server.blackbox.probe.FailedHandlerGuard; /** * Test environment for testing - * {@link io.spine.testing.server.blackbox.FailedHandlerGuard FailedHandlerGuard} integration. + * {@link FailedHandlerGuard FailedHandlerGuard} integration. */ public final class BbProjectFailerProcess extends ProcessManager { diff --git a/testutil-server/src/test/java/io/spine/testing/server/blackbox/DiagnosticLogTest.java b/testutil-server/src/test/java/io/spine/testing/server/blackbox/probe/DiagnosticLogTest.java similarity index 98% rename from testutil-server/src/test/java/io/spine/testing/server/blackbox/DiagnosticLogTest.java rename to testutil-server/src/test/java/io/spine/testing/server/blackbox/probe/DiagnosticLogTest.java index aafb01f9a2f..f4dcb3f3924 100644 --- a/testutil-server/src/test/java/io/spine/testing/server/blackbox/DiagnosticLogTest.java +++ b/testutil-server/src/test/java/io/spine/testing/server/blackbox/probe/DiagnosticLogTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022, TeamDev. All rights reserved. + * Copyright 2023, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.testing.server.blackbox; +package io.spine.testing.server.blackbox.probe; import io.spine.base.Identifier; import io.spine.core.CommandId; diff --git a/testutil-server/src/test/java/io/spine/testing/server/blackbox/DiagnosticLoggingTest.java b/testutil-server/src/test/java/io/spine/testing/server/blackbox/probe/DiagnosticLoggingTest.java similarity index 95% rename from testutil-server/src/test/java/io/spine/testing/server/blackbox/DiagnosticLoggingTest.java rename to testutil-server/src/test/java/io/spine/testing/server/blackbox/probe/DiagnosticLoggingTest.java index 2611f1c0460..75b95e8c0e8 100644 --- a/testutil-server/src/test/java/io/spine/testing/server/blackbox/DiagnosticLoggingTest.java +++ b/testutil-server/src/test/java/io/spine/testing/server/blackbox/probe/DiagnosticLoggingTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022, TeamDev. All rights reserved. + * Copyright 2023, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,10 +24,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.testing.server.blackbox; +package io.spine.testing.server.blackbox.probe; import com.google.protobuf.Empty; import io.spine.core.MessageId; +import io.spine.testing.server.blackbox.probe.DiagnosticLogging; import io.spine.type.TypeUrl; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/testutil-server/src/test/java/io/spine/testing/server/blackbox/FailedHandlerGuardTest.java b/testutil-server/src/test/java/io/spine/testing/server/blackbox/probe/FailedHandlerGuardTest.java similarity index 96% rename from testutil-server/src/test/java/io/spine/testing/server/blackbox/FailedHandlerGuardTest.java rename to testutil-server/src/test/java/io/spine/testing/server/blackbox/probe/FailedHandlerGuardTest.java index 2c669f8ceaf..8a9480160fa 100644 --- a/testutil-server/src/test/java/io/spine/testing/server/blackbox/FailedHandlerGuardTest.java +++ b/testutil-server/src/test/java/io/spine/testing/server/blackbox/probe/FailedHandlerGuardTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022, TeamDev. All rights reserved. + * Copyright 2023, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.testing.server.blackbox; +package io.spine.testing.server.blackbox.probe; import io.spine.system.server.HandlerFailedUnexpectedly; import org.junit.jupiter.api.BeforeEach; diff --git a/testutil-server/src/test/kotlin/io/spine/testing/server/blackbox/probe/UnsupportedCommandGuardSpec.kt b/testutil-server/src/test/kotlin/io/spine/testing/server/blackbox/probe/UnsupportedCommandGuardSpec.kt new file mode 100644 index 00000000000..cbf8a55f22e --- /dev/null +++ b/testutil-server/src/test/kotlin/io/spine/testing/server/blackbox/probe/UnsupportedCommandGuardSpec.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed 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 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package io.spine.testing.server.blackbox.probe + +import io.kotest.matchers.shouldBe +import io.spine.testing.server.blackbox.given.GivenCommandError.duplicationError +import io.spine.testing.server.blackbox.given.GivenCommandError.nonValidationError +import io.spine.testing.server.blackbox.given.GivenCommandError.unsupportedError +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +@DisplayName("`UnsupportedCommandGuard` should") +internal class UnsupportedCommandGuardSpec { + + private lateinit var guard: UnsupportedCommandGuard + + @BeforeEach + fun initGuard() { + guard = UnsupportedCommandGuard(javaClass.name) + } + + @Test + fun `check and remember the unsupported command error`() { + val commandType = "SomeCommand" + val result = guard.checkAndRemember(unsupportedError(commandType)) + + result shouldBe true + guard.commandType() shouldBe commandType + } + + @Test + fun `return 'false' and ignore the command validation error with the different code`() { + val result = guard.checkAndRemember(duplicationError()) + + result shouldBe false + guard.commandType() shouldBe null + } + + @Test + fun `return 'false' and ignore the command error of the different type`() { + val result = guard.checkAndRemember(nonValidationError()) + + result shouldBe false + guard.commandType() shouldBe null + } +} From 0cca1d4687fc475bb2cef40ac45e1f329a1fab49 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 30 Oct 2023 18:46:44 +0000 Subject: [PATCH 07/32] Pass `BoundedContext` to `BlackBox` ... instead of `BoundedContextBuilder`. --- .../java/io/spine/server/BoundedContext.java | 10 ++--- .../spine/server/BoundedContextBuilder.java | 25 ++++------- .../testing/server/blackbox/BlackBox.java | 42 ++++++------------- .../testing/server/blackbox/MtBlackBox.java | 6 +-- .../testing/server/blackbox/StBlackBox.java | 6 +-- 5 files changed, 30 insertions(+), 59 deletions(-) diff --git a/server/src/main/java/io/spine/server/BoundedContext.java b/server/src/main/java/io/spine/server/BoundedContext.java index f2bc047ff60..f598b9c89a1 100644 --- a/server/src/main/java/io/spine/server/BoundedContext.java +++ b/server/src/main/java/io/spine/server/BoundedContext.java @@ -46,13 +46,10 @@ import io.spine.server.event.EventBus; import io.spine.server.event.EventDispatcher; import io.spine.server.event.EventDispatcherDelegate; -import io.spine.server.event.store.DefaultEventStore; import io.spine.server.integration.IntegrationBroker; import io.spine.server.security.Security; import io.spine.server.stand.Stand; -import io.spine.server.storage.StorageFactory; import io.spine.server.tenant.TenantIndex; -import io.spine.server.trace.TracerFactory; import io.spine.server.type.CommandEnvelope; import io.spine.server.type.EventEnvelope; import io.spine.system.server.SystemClient; @@ -457,15 +454,15 @@ public boolean isMultitenant() { * *

    This method performs the following: *

      - *
    1. Closes associated {@link StorageFactory}. + *
    2. Invokes {@link BoundedContextBuilder#getOnBeforeClose onBeforeClose} if it was + * configured when the context was {@linkplain BoundedContextBuilder built}. *
    3. Closes {@link CommandBus}. *
    4. Closes {@link EventBus}. *
    5. Closes {@link IntegrationBroker}. - *
    6. Closes {@link DefaultEventStore EventStore}. *
    7. Closes {@link Stand}. *
    8. Closes {@link ImportBus}. - *
    9. Closes {@link TracerFactory} if it is present. *
    10. Closes all registered {@linkplain Repository repositories}. + *
    11. Removes a {@link Probe}, if it was {@linkplain #install(Probe) installed}. *
    * * @throws Exception @@ -561,7 +558,6 @@ public final void install(Probe probe) { eventBus.add(probe.eventListener()); probe.eventDispatchers() .forEach(this::registerEventDispatcher); - this.probe = probe; } diff --git a/server/src/main/java/io/spine/server/BoundedContextBuilder.java b/server/src/main/java/io/spine/server/BoundedContextBuilder.java index d3c03d4cd2d..c4b0c76e669 100644 --- a/server/src/main/java/io/spine/server/BoundedContextBuilder.java +++ b/server/src/main/java/io/spine/server/BoundedContextBuilder.java @@ -104,8 +104,6 @@ public final class BoundedContextBuilder implements WithLogging { /** Repositories to be registered with the Bounded Context being built after its creation. */ private final Collection> repositories = new ArrayList<>(); - private @Nullable Consumer onBeforeBuild = null; - private @Nullable Consumer onBuild = null; private @Nullable Consumer onBeforeClose = null; /** @@ -541,9 +539,6 @@ public SystemSettings systemSettings() { * @return new {@code BoundedContext} */ public BoundedContext build() { - if (onBeforeBuild != null) { - onBeforeBuild.accept(this); - } var system = buildSystem(); var result = buildDomain(system); logger().atDebug() @@ -554,9 +549,6 @@ public BoundedContext build() { ServerEnvironment.instance() .delivery() .registerDispatchersIn(result); - if (onBuild != null) { - onBuild.accept(result); - } return result; } @@ -567,14 +559,15 @@ EventBus buildEventBus(BoundedContext context) { return eventBus.build(); } - public void setOnBeforeBuild(Consumer consumer) { - this.onBeforeBuild = checkNotNull(consumer); - } - - public void setOnBuild(Consumer consumer) { - this.onBuild = checkNotNull(consumer); - } - + /** + * Sets the action to be performed before the context is closed. + * + *

    The action is performed after the context is closed, but before the + * {@linkplain BoundedContext#onClose() context's own action} is performed. + * + * @param consumer + * the action to perform + */ public void setOnBeforeClose(Consumer consumer) { this.onBeforeClose = checkNotNull(consumer); } diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java index 0da9b3ca994..356510dec56 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java @@ -55,20 +55,18 @@ import io.spine.testing.client.TestActorRequestFactory; import io.spine.testing.server.CommandSubject; import io.spine.testing.server.EventSubject; -import io.spine.testing.server.entity.EntitySubject; import io.spine.testing.server.blackbox.probe.CommandCollector; import io.spine.testing.server.blackbox.probe.EventCollector; import io.spine.testing.server.blackbox.probe.Probe; +import io.spine.testing.server.entity.EntitySubject; import io.spine.testing.server.query.QueryResultSubject; import io.spine.time.ZoneId; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Predicate; import java.util.function.Supplier; @@ -95,7 +93,7 @@ public abstract class BlackBox implements WithLogging, Closeable { /** * The context under the test. */ - private @MonotonicNonNull BoundedContext context; + private final BoundedContext context; /** * The probe inserted into the context under the test. @@ -105,7 +103,7 @@ public abstract class BlackBox implements WithLogging, Closeable { /** * A factory of {@link Client}s which send requests to this context. */ - private @MonotonicNonNull ClientFactory clientFactory; + private final ClientFactory clientFactory; /** * Information about the current user and the time-zone. @@ -132,35 +130,19 @@ public abstract class BlackBox implements WithLogging, Closeable { * Creates new instance obtaining configuration parameters from the passed builder. */ public static BlackBox from(BoundedContextBuilder builder) { - var supplier = initLazilyFrom(builder); - var ignored = builder.build(); - return supplier.get(); - } - - /** - * Arranged a delayed creation of new {@code Blackbox} obtaining configuration parameters - * from the passed builder. - */ - public static Supplier initLazilyFrom(BoundedContextBuilder builder) { - var result = new AtomicReference(); - builder.setOnBeforeBuild(configuredBuilder -> { - var box = builder.isMultitenant() - ? new MtBlackBox(configuredBuilder) - : new StBlackBox(configuredBuilder); - result.set(box); - }); - return result::get; + var context = builder.build(); + var box = builder.isMultitenant() + ? new MtBlackBox(context) + : new StBlackBox(context); + return box; } - BlackBox(BoundedContextBuilder builder) { + BlackBox(BoundedContext context) { super(); + this.context = context; this.probe = new Probe(); - var created = this; - builder.setOnBuild(context -> { - created.clientFactory = new ClientFactory(context); - context.install(probe); - created.context = context; - }); + context.install(probe); + this.clientFactory = new ClientFactory(context); this.actor = defaultActor(); this.postedCommands = synchronizedSet(new HashSet<>()); this.postedEvents = synchronizedSet(new HashSet<>()); diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/MtBlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/MtBlackBox.java index 1cb02238ac4..3dd4426d7fd 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/MtBlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/MtBlackBox.java @@ -31,7 +31,7 @@ import io.spine.core.Command; import io.spine.core.Event; import io.spine.core.TenantId; -import io.spine.server.BoundedContextBuilder; +import io.spine.server.BoundedContext; import io.spine.server.tenant.TenantAwareRunner; import io.spine.testing.client.TestActorRequestFactory; import io.spine.testing.server.blackbox.probe.CommandCollector; @@ -54,8 +54,8 @@ final class MtBlackBox extends BlackBox { /** * Creates a new multi-tenant instance. */ - MtBlackBox(BoundedContextBuilder b) { - super(b); + MtBlackBox(BoundedContext c) { + super(c); } /** diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/StBlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/StBlackBox.java index 73112183c3a..219093f2714 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/StBlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/StBlackBox.java @@ -31,7 +31,7 @@ import io.spine.core.Command; import io.spine.core.Event; import io.spine.core.TenantId; -import io.spine.server.BoundedContextBuilder; +import io.spine.server.BoundedContext; import io.spine.testing.client.TestActorRequestFactory; import io.spine.testing.server.blackbox.probe.CommandCollector; import io.spine.testing.server.blackbox.probe.EventCollector; @@ -44,8 +44,8 @@ */ final class StBlackBox extends BlackBox { - StBlackBox(BoundedContextBuilder b) { - super(b); + StBlackBox(BoundedContext c) { + super(c); } @Override From 723eb67e339605b45ed4054cfe0dd07aadc39421 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Mon, 30 Oct 2023 19:25:55 +0000 Subject: [PATCH 08/32] Add documentation --- .../java/io/spine/server/BoundedContext.java | 17 ++++++++++ .../testing/server/blackbox/BlackBox.java | 10 +++--- .../blackbox/probe/DiagnosticLogging.java | 6 ++-- .../blackbox/probe/FailedHandlerGuard.java | 4 ++- .../blackbox/probe/MessageCollector.java | 10 +++--- .../testing/server/blackbox/probe/Probe.java | 31 +++++++++---------- .../server/blackbox/probe/package-info.java | 2 ++ 7 files changed, 51 insertions(+), 29 deletions(-) diff --git a/server/src/main/java/io/spine/server/BoundedContext.java b/server/src/main/java/io/spine/server/BoundedContext.java index f598b9c89a1..10825c1934d 100644 --- a/server/src/main/java/io/spine/server/BoundedContext.java +++ b/server/src/main/java/io/spine/server/BoundedContext.java @@ -602,9 +602,26 @@ public int hashCode() { return name().hashCode(); } + /** + * A diagnostic probe which can be {@linkplain #install(Probe) installed} into + * a Bounded Context to collect information about the commands and events processed by it. + */ public interface Probe extends ContextAware { + + /** + * The listener of commands posted processed by the context. + */ Listener commandListener(); + + /** + * The listener of events posted processed by the context. + */ Listener eventListener(); + + /** + * Obtains the event dispatchers exposed by the probe for gathering + * additional information about the events. + */ Set eventDispatchers(); } diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java index 356510dec56..88813aa3487 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java @@ -471,8 +471,8 @@ public boolean isOpen() { abstract TestActorRequestFactory requestFactory(); /** - * Obtains immutable list of commands generated in this Bounded Context in response to posted - * messages. + * Obtains an immutable list of commands generated in this Bounded Context in response + * to posted messages. * *

    The returned list does NOT contain commands posted to this Bounded Context * during test setup. @@ -480,7 +480,7 @@ public boolean isOpen() { private ImmutableList commands() { var wasNotReceived = ((Predicate) postedCommands::contains).negate(); - return select(probe.commands()) + return select(probe.commandListener()) .stream() .filter(wasNotReceived) .collect(toImmutableList()); @@ -511,11 +511,11 @@ private ImmutableList events() { abstract ImmutableList select(EventCollector collector); /** - * Obtains immutable list of all the events in this Bounded Context. + * Obtains an immutable list of all the events in this Bounded Context. */ @VisibleForTesting final ImmutableList allEvents() { - return select(probe.events()); + return select(probe.eventListener()); } /** diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/DiagnosticLogging.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/DiagnosticLogging.java index 9e6795694b6..0b94fe7d3bf 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/DiagnosticLogging.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/DiagnosticLogging.java @@ -49,7 +49,9 @@ interface DiagnosticLogging extends WithLogging { * the arguments, if any, for the error message */ @FormatMethod - default void log(DiagnosticEvent event, @FormatString String errorMessage, Object... formatArgs) { + default void log(DiagnosticEvent event, + @FormatString String errorMessage, + Object... formatArgs) { var msg = format(errorMessage, formatArgs); log(msg, event); } @@ -70,7 +72,7 @@ default void log(String msg, DiagnosticEvent event) { errorLogger.log(() -> eventJson); } else { @SuppressWarnings("UseOfSystemOutOrSystemErr") - // Edge case for disabled/misconfigured logging . + // Edge case for disabled/misconfigured logging. var stderr = System.err; stderr.println(msg); stderr.println(eventJson); diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/FailedHandlerGuard.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/FailedHandlerGuard.java index 3fcd836feb6..7630f176b4d 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/FailedHandlerGuard.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/FailedHandlerGuard.java @@ -44,7 +44,9 @@ * Performs logging of failed signals handlers or fails the test * depending upon the current context exception tolerance. */ -public final class FailedHandlerGuard extends AbstractEventSubscriber implements DiagnosticLogging { +public final class FailedHandlerGuard + extends AbstractEventSubscriber + implements DiagnosticLogging { private HandlerFailureTolerance tolerance = HandlerFailureTolerance.RAISE_AND_FAIL; diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/MessageCollector.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/MessageCollector.java index fac7a68a197..46da496691a 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/MessageCollector.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/MessageCollector.java @@ -51,13 +51,13 @@ * Abstract base for message listeners that collect messages posted to a bus. * * @param - * the type of the message identifiers + * the type of the signal identifiers * @param - * the type of the outer objects of messages + * the type of the collected signals * @param * the type of the message envelopes */ -public abstract class MessageCollector, E extends MessageEnvelope> implements Listener { @@ -87,14 +87,14 @@ public final void accept(E envelope) { } /** - * Obtains immutable list with outer objects of messages collected so far. + * Obtains an immutable list with outer objects of messages collected so far. */ public final ImmutableList all() { return ImmutableList.copyOf(outerObjects); } /** - * Obtains immutable list with outer objects of messages belonging to the passed tenant. + * Obtains an immutable list with outer objects of messages belonging to the passed tenant. */ public final ImmutableList ofTenant(TenantId tenantId) { checkNotNull(tenantId); diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/Probe.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/Probe.java index 89c02625bd7..d52d69939ff 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/Probe.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/Probe.java @@ -28,17 +28,18 @@ import com.google.common.collect.ImmutableSet; import io.spine.server.BoundedContext; -import io.spine.server.BoundedContextBuilder; -import io.spine.server.bus.Listener; import io.spine.server.event.EventDispatcher; -import io.spine.server.type.CommandEnvelope; -import io.spine.server.type.EventEnvelope; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Set; import static com.google.common.base.Preconditions.checkNotNull; +/** + * An implementation of {@link BoundedContext.Probe} which is used by + * {@link io.spine.testing.server.blackbox.BlackBox BlackBox} to collect + * commands and events produced by a {@link BoundedContext} being tested. + */ public class Probe implements BoundedContext.Probe { private @Nullable BoundedContext context; @@ -60,6 +61,9 @@ public class Probe implements BoundedContext.Probe { */ private final FailedHandlerGuard failedHandlerGuard; + /** + * Creates a new instance. + */ public Probe() { this.commands = new CommandCollector(); this.events = new EventCollector(); @@ -77,26 +81,21 @@ public boolean isRegistered() { return context != null; } - public CommandCollector commands() { - return commands; - } - - public EventCollector events() { - return events; - } - + /** + * Obtains a {@link FailedHandlerGuard} instance used by this probe. + */ public FailedHandlerGuard failedHandlerGuard() { return failedHandlerGuard; } @Override - public Listener commandListener() { - return commands(); + public CommandCollector commandListener() { + return commands; } @Override - public Listener eventListener() { - return events(); + public EventCollector eventListener() { + return events; } @Override diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/package-info.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/package-info.java index 0bf3d522413..afd6a40519d 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/package-info.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/package-info.java @@ -28,10 +28,12 @@ * This package contains diagnostic utilities for * {@link io.spine.server.BoundedContext io.spine.server.BoundedContext}. */ +@Internal @CheckReturnValue @ParametersAreNonnullByDefault package io.spine.testing.server.blackbox.probe; import com.google.errorprone.annotations.CheckReturnValue; +import io.spine.annotation.Internal; import javax.annotation.ParametersAreNonnullByDefault; From 4e69262cf35da2a55af3e398e36f5f7fc9d34813 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 31 Oct 2023 00:17:13 +0000 Subject: [PATCH 09/32] Convert tests of `BoundedContext` to Kotlin Also: * Add ext. fun for `BoundedContext`. --- .../java/io/spine/server/BoundedContext.java | 11 + .../io/spine/server/BoundedContextExts.kt | 36 ++ .../com/example/ForeignContextConfig.java | 2 +- .../io/spine/server/BoundedContextSpec.kt | 489 +++++++++++++++++ .../io/spine/server/BoundedContextTest.java | 518 ------------------ 5 files changed, 537 insertions(+), 519 deletions(-) create mode 100644 server/src/main/kotlin/io/spine/server/BoundedContextExts.kt create mode 100644 server/src/test/java/io/spine/server/BoundedContextSpec.kt delete mode 100644 server/src/test/java/io/spine/server/BoundedContextTest.java diff --git a/server/src/main/java/io/spine/server/BoundedContext.java b/server/src/main/java/io/spine/server/BoundedContext.java index 10825c1934d..ca45516c2ba 100644 --- a/server/src/main/java/io/spine/server/BoundedContext.java +++ b/server/src/main/java/io/spine/server/BoundedContext.java @@ -577,6 +577,17 @@ public final void removeProbe() { probe = null; } + /** + * Obtains the currently installed {@link Probe} or {@link Optional#empty()} if no probe + * is installed. + */ + public final Optional probe() { + return Optional.ofNullable(probe); + } + + /** + * Returns {@code true} if a {@link Probe} is installed, {@code false} otherwise. + */ public final boolean hasProbe() { return probe != null; } diff --git a/server/src/main/kotlin/io/spine/server/BoundedContextExts.kt b/server/src/main/kotlin/io/spine/server/BoundedContextExts.kt new file mode 100644 index 00000000000..a412fc12bad --- /dev/null +++ b/server/src/main/kotlin/io/spine/server/BoundedContextExts.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2023, TeamDev. All rights reserved. + * + * Licensed 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 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.server + +import io.spine.base.EntityState +import io.spine.server.entity.Entity + +public inline fun > BoundedContext.hasEntitiesOfType(): Boolean = + hasEntitiesOfType(E::class.java) + +public inline fun > BoundedContext.hasEntitiesWithState(): Boolean = + hasEntitiesWithState(S::class.java) diff --git a/server/src/test/java/com/example/ForeignContextConfig.java b/server/src/test/java/com/example/ForeignContextConfig.java index 10256118481..d96f2fa2030 100644 --- a/server/src/test/java/com/example/ForeignContextConfig.java +++ b/server/src/test/java/com/example/ForeignContextConfig.java @@ -46,7 +46,7 @@ * Test environment class for testing {@code BoundedContext} configuration from * outside the framework. * - * @see io.spine.server.BoundedContextTest.RestrictRegistrationCalls + * @see io.spine.server.BoundedContextSpec.RestrictRegistrationCalls */ public final class ForeignContextConfig { diff --git a/server/src/test/java/io/spine/server/BoundedContextSpec.kt b/server/src/test/java/io/spine/server/BoundedContextSpec.kt new file mode 100644 index 00000000000..b975026c98c --- /dev/null +++ b/server/src/test/java/io/spine/server/BoundedContextSpec.kt @@ -0,0 +1,489 @@ +/* + * Copyright 2022, TeamDev. All rights reserved. + * + * Licensed 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 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package io.spine.server + +import com.example.ForeignContextConfig +import com.google.common.collect.ImmutableSet +import com.google.common.collect.Sets +import com.google.common.testing.EqualsTester +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth8 +import io.kotest.matchers.collections.shouldBeEmpty +import io.kotest.matchers.collections.shouldContain +import io.kotest.matchers.collections.shouldNotBeEmpty +import io.kotest.matchers.collections.shouldNotContain +import io.kotest.matchers.optional.shouldBeEmpty +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.spine.annotation.Internal +import io.spine.core.BoundedContextName +import io.spine.core.BoundedContextNames +import io.spine.logging.Level.Companion.DEBUG +import io.spine.logging.toJavaLogging +import io.spine.option.EntityOption +import io.spine.server.bc.given.AnotherProjectAggregate +import io.spine.server.bc.given.FinishedProjectProjection +import io.spine.server.bc.given.ProjectAggregate +import io.spine.server.bc.given.ProjectCreationRepository +import io.spine.server.bc.given.ProjectProcessManager +import io.spine.server.bc.given.ProjectProjection +import io.spine.server.bc.given.ProjectRemovalProcman +import io.spine.server.bc.given.ProjectReport +import io.spine.server.bc.given.SecretProjectRepository +import io.spine.server.bc.given.TestEventSubscriber +import io.spine.server.entity.Entity +import io.spine.server.entity.Repository +import io.spine.system.server.SystemClient +import io.spine.system.server.SystemContext +import io.spine.test.bc.Project +import io.spine.test.bc.SecretProject +import io.spine.testing.TestValues +import io.spine.testing.logging.Interceptor +import io.spine.testing.server.model.ModelTests +import java.util.function.BooleanSupplier +import java.util.logging.Level +import java.util.stream.Stream +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.function.Executable +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + +/** + * Tests of [BoundedContext]. + * + * Messages used in this test suite are defined in: + * + * * `spine/test/bc/project.proto` - data types + * * `spine/test/bc/commands.proto` — commands + * * `spine/test/bc/events.proto` — events. + */ +@DisplayName("`BoundedContext` should") +internal class BoundedContextSpec { + private val subscriber = TestEventSubscriber() + + private lateinit var context: BoundedContext + + private var handlersRegistered = false + + @BeforeEach + fun setUp() { + ModelTests.dropAllModels() + context = BoundedContextBuilder.assumingTests(true).build() + } + + @AfterEach + fun tearDown() { + if (handlersRegistered) { + context.eventBus() + .unregister(subscriber) + } + context.close() + } + + /** Registers all test repositories, handlers, etc. */ + private fun registerAll() { + context.register(DefaultRepository.of(ProjectAggregate::class.java)) + context.eventBus().register(subscriber) + handlersRegistered = true + } + + @Nested + @DisplayName("provide access to") + @Suppress("TestFunctionName") // For readability of methods named after class names. + internal inner class Return { + + @Test + fun EventBus() { + context.eventBus() shouldNotBe null + } + + @Test + fun IntegrationBroker() { + context.internalAccess().broker() shouldNotBe null + } + + @Test + fun CommandDispatcher() { + context.commandBus() shouldNotBe null + } + + @Test + fun `multitenancy state`() { + BoundedContextBuilder.assumingTests(true).build().use { + it.isMultitenant shouldBe true + } + } + } + + @Nested + @DisplayName("register") + @Suppress("TestFunctionName") // For readability of methods named after class names. + internal inner class Register { + + @Test + fun AggregateRepository() = + registerAndAssertRepository(ProjectAggregate::class.java) + + @Test + fun ProcessManagerRepository() = + registerAndAssertRepository(ProjectProcessManager::class.java) + + @Test + fun ProjectionRepository() = + registerAndAssertRepository(ProjectReport::class.java) + + /** + * Registers a [DefaultRepository] for the given entity class and asserts that + * the context has entities of the given type. + */ + private fun > registerAndAssertRepository(cls: Class) { + context.register(DefaultRepository.of(cls)) + context.hasEntitiesOfType(cls) shouldBe true + } + + @Test + fun `a 'DefaultRepository' via passed entity class`() { + context.register(ProjectAggregate::class.java) + context.hasEntitiesOfType() shouldBe true + } + } + + @Nested + @DisplayName("test presence of entities by") + internal inner class EntityTypePresence { + + @Nested + @DisplayName("entity state class for") + internal inner class ByEntityStateClass { + + @Test + fun `visible entities`() { + context.register(ProjectAggregate::class.java) + context.hasEntitiesWithState() shouldBe true + } + + @Test + @DisplayName("invisible entities") + fun invisible() { + context.register(SecretProjectRepository()) + context.hasEntitiesWithState() shouldBe true + } + } + + @Nested + @DisplayName("entity class for") + internal inner class ByEntityClass { + + @Test + fun `visible entities`() { + context.register(ProjectAggregate::class.java) + context.hasEntitiesOfType() shouldBe true + } + + @Test + fun `invisible entities`() { + // Process Managers are invisible by default. + context.register(ProjectProcessManager::class.java) + + context.hasEntitiesOfType() shouldBe true + } + } + } + + @Test + fun `propagate registered repositories to 'Stand'`() { + val context = BoundedContextBuilder.assumingTests().build() + val stand = context.stand() + + val repo = DefaultRepository.of(ProjectAggregate::class.java) + val stateType = repo.entityStateType() + + // No type until registration at the context. + stand.exposedTypes() shouldNotContain stateType + + context.register(repo) + + // After registration, the type is exposed. + stand.exposedTypes() shouldContain stateType + } + + @ParameterizedTest + @MethodSource("sameStateRepositories") + fun `not allow two entity repositories with entities of same state`( + firstRepo: Repository<*, *>, secondRepo: Repository<*, *> + ) { + context.register(firstRepo) + assertThrows { + context.register(secondRepo) + } + } + + @Test + fun `assign storage during registration if repository does not have one`() { + val repository = DefaultRepository.of(ProjectAggregate::class.java) + context.register(repository) + repository.storageAssigned() shouldBe true + } + + @Nested + @DisplayName("assign own multitenancy state to") + @Suppress("TestFunctionName") // for readability of methods named after class names. + internal inner class AssignMultitenancyState { + + private lateinit var context: BoundedContext + + @Test + fun CommandBus() { + context = multiTenant() + assertMultitenancyEqual( + { context.isMultitenant }, + { context.commandBus().isMultitenant }) + + context = singleTenant() + assertMultitenancyEqual( + { context.isMultitenant }, + { context.commandBus().isMultitenant }) + } + + @Test + fun Stand() { + context = multiTenant() + + assertMultitenancyEqual( + { context.isMultitenant }, + { context.stand().isMultitenant }) + + context = singleTenant() + + assertMultitenancyEqual( + { context.isMultitenant }, + { context.stand().isMultitenant }) + } + + private fun assertMultitenancyEqual(s1: BooleanSupplier, s2: BooleanSupplier) { + s1.asBoolean shouldBe s2.asBoolean + } + + private fun multiTenant(): BoundedContext = + BoundedContextBuilder.assumingTests(true).build() + + private fun singleTenant(): BoundedContext = + BoundedContextBuilder.assumingTests(false).build() + } + + /** + * Simply checks that the result isn't empty to cover the integration with + * [VisibilityGuard]. + * + * See [tests of VisibilityGuard][io.spine.server.entity.VisibilityGuardTest] + * for how visibility filtering works. + */ + @Test + fun `obtain entity types by visibility`() { + context.stateTypes(EntityOption.Visibility.FULL).shouldBeEmpty() + registerAll() + context.stateTypes(EntityOption.Visibility.FULL).shouldNotBeEmpty() + } + + @Test + fun `throw 'ISE' when obtaining a repository for non-registered entity state class`() { + // Attempt to get a repository without registering. + assertThrows { + context.internalAccess().findRepository(Project::class.java) + } + } + + @Test + fun `not expose invisible aggregates`() { + ModelTests.dropAllModels() + context.register(SecretProjectRepository()) + context.internalAccess().findRepository(SecretProject::class.java).shouldBeEmpty() + } + + @Test + @DisplayName("prohibit 3rd-party descendants") + fun `prohibit 3rd-party descendants`() { + assertThrows { + object : BoundedContext(BoundedContextBuilder.assumingTests()) { + @Internal + override fun systemClient(): SystemClient = TestValues.nullRef() + } + } + } + + @Nested + @DisplayName("when closing") + internal inner class ClosingContext { + private val contextName: BoundedContextName = BoundedContextNames.newName("TestDomain") + private val systemContextName: BoundedContextName = contextName.toSystem() + private val debugLevel: Level = DEBUG.toJavaLogging() + + private lateinit var domainInterceptor: Interceptor + private lateinit var systemInterceptor: Interceptor + + @BeforeEach + fun closeContext() { + val context = BoundedContext.singleTenant(contextName.value).build() + domainInterceptor = Interceptor(DomainContext::class.java, debugLevel) + domainInterceptor.intercept() + systemInterceptor = Interceptor(SystemContext::class.java, debugLevel) + systemInterceptor.intercept() + + context.close() + } + + @AfterEach + fun releaseInterceptors() { + domainInterceptor.release() + systemInterceptor.release() + } + + @Test + fun `log its closing`() { + domainInterceptor.assertLog().record().run { + hasLevelThat().isEqualTo(debugLevel) + hasMessageThat().contains(contextName.value) + } + } + + @Test + fun `close its System context`() { + systemInterceptor.assertLog().record().run { + hasLevelThat().isEqualTo(debugLevel) + hasMessageThat().contains(systemContextName.value) + } + } + } + + @Test + fun `return its name in 'toString()'`() { + val name = TestValues.randomString() + BoundedContext.singleTenant(name).build().use { + it.toString() shouldBe name + } + } + + @Nested + @DisplayName("do not allow registration calls from outside the `io.spine.server` package for") + @Suppress("TestFunctionName") // For readability of methods named after class names. + internal inner class RestrictRegistrationCalls { + + @Test + fun Repository() = assertThrowsOn { + ForeignContextConfig.repositoryRegistration() + } + + @Test + fun CommandDispatcher() = assertThrowsOn { + ForeignContextConfig.commandDispatcherRegistration() + } + + @Test + fun EventDispatcher() = assertThrowsOn { + ForeignContextConfig.eventDispatcherRegistration() + } + + private fun assertThrowsOn(executable: Executable) { + Assertions.assertThrows(SecurityException::class.java, executable) + } + } + + @Test + fun `be equal to another context by its name`() { + val c1 = BoundedContext.singleTenant("One").build() + val c2 = BoundedContext.singleTenant("Two").build() + val c1m = BoundedContext.multitenant("One").build() + val c2m = BoundedContext.multitenant("Two").build() + + EqualsTester() + .addEqualityGroup(c1, c1m) + .addEqualityGroup(c2, c2m) + .testEquals() + } + + @Test + fun `be comparable by its name`() { + val c1 = BoundedContext.singleTenant("1").build() + val c2 = BoundedContext.singleTenant("2").build() + + assertThat(c1).isLessThan(c2) + assertThat(c2).isGreaterThan(c1) + assertThat(c1).isEqualTo(BoundedContext.multitenant("1").build()) + } + + companion object { + + /** + * Returns all combinations of repositories that manage entities of the same state. + * + * + * To check whether a [io.spine.server.BoundedContext] really throws + * an `IllegalStateException` upon an attempt to register a repository that manages an + * entity of a state that a registered entity repository is already managing, + * all combinations of entities that take state as a type parameter need to be checked. + * + * This method returns a stream of pairs of all such combinations, which is a Cartesian + * product of: + * + * * [Process Manager][io.spine.server.procman.ProcessManagerRepository]; + * * [Aggregate][io.spine.server.aggregate.AggregateRepository]; + * * [Projection][io.spine.server.projection.ProjectionRepository]. + * + * All the returned repositories manage entities of the same state type. + */ + @Suppress("unused") /* A method source. */ + @JvmStatic + fun sameStateRepositories(): Stream { + val repositories: Set> = + ImmutableSet.of>( + DefaultRepository.of(ProjectAggregate::class.java), + DefaultRepository.of(ProjectProjection::class.java), + ProjectCreationRepository() + ) + + val sameStateRepositories: Set> = + ImmutableSet.of>( + DefaultRepository.of(AnotherProjectAggregate::class.java), + DefaultRepository.of(FinishedProjectProjection::class.java), + DefaultRepository.of(ProjectRemovalProcman::class.java) + ) + + val cartesianProduct = Sets.cartesianProduct(repositories, sameStateRepositories) + val result = cartesianProduct.stream() + .map { repos: List> -> + Arguments.of(repos[0], repos[1]) + } + return result + } + } +} diff --git a/server/src/test/java/io/spine/server/BoundedContextTest.java b/server/src/test/java/io/spine/server/BoundedContextTest.java deleted file mode 100644 index b5df2aefa63..00000000000 --- a/server/src/test/java/io/spine/server/BoundedContextTest.java +++ /dev/null @@ -1,518 +0,0 @@ -/* - * Copyright 2022, TeamDev. All rights reserved. - * - * Licensed 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 - * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package io.spine.server; - -import com.example.ForeignContextConfig; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; -import com.google.common.testing.EqualsTester; -import io.spine.annotation.Internal; -import io.spine.core.BoundedContextName; -import io.spine.option.EntityOption; -import io.spine.server.bc.given.AnotherProjectAggregate; -import io.spine.server.bc.given.FinishedProjectProjection; -import io.spine.server.bc.given.ProjectAggregate; -import io.spine.server.bc.given.ProjectCreationRepository; -import io.spine.server.bc.given.ProjectProcessManager; -import io.spine.server.bc.given.ProjectProjection; -import io.spine.server.bc.given.ProjectRemovalProcman; -import io.spine.server.bc.given.ProjectReport; -import io.spine.server.bc.given.SecretProjectRepository; -import io.spine.server.bc.given.TestEventSubscriber; -import io.spine.server.entity.Entity; -import io.spine.server.entity.Repository; -import io.spine.system.server.SystemClient; -import io.spine.system.server.SystemContext; -import io.spine.test.bc.Project; -import io.spine.test.bc.SecretProject; -import io.spine.testing.logging.Interceptor; -import io.spine.testing.server.model.ModelTests; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.function.Executable; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.Set; -import java.util.function.BooleanSupplier; -import java.util.logging.Level; -import java.util.stream.Stream; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth8.assertThat; -import static io.spine.core.BoundedContextNames.newName; -import static io.spine.logging.JvmLoggerKt.toJavaLogging; -import static io.spine.testing.TestValues.nullRef; -import static io.spine.testing.TestValues.randomString; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -/** - * Tests of {@link BoundedContext}. - * - *

    Messages used in this test suite are defined in: - *

      - *
    • {@code spine/test/bc/project.proto} - data types - *
    • {@code spine/test/bc/commands.proto} — commands - *
    • {@code spine/test/bc/events.proto} — events. - *
    - */ -@DisplayName("`BoundedContext` should") -class BoundedContextTest { - - private final TestEventSubscriber subscriber = new TestEventSubscriber(); - - private BoundedContext context; - - private boolean handlersRegistered = false; - - @BeforeEach - void setUp() { - ModelTests.dropAllModels(); - context = BoundedContextBuilder.assumingTests(true).build(); - } - - @AfterEach - void tearDown() throws Exception { - if (handlersRegistered) { - context.eventBus() - .unregister(subscriber); - } - context.close(); - } - - /** Registers all test repositories, handlers etc. */ - private void registerAll() { - context.register(DefaultRepository.of(ProjectAggregate.class)); - context.eventBus() - .register(subscriber); - handlersRegistered = true; - } - - @Nested - @DisplayName("return") - class Return { - - @Test - @DisplayName("`EventBus`") - void eventBus() { - assertNotNull(context.eventBus()); - } - - @Test - @DisplayName("`IntegrationBroker`") - void integrationBroker() { - assertNotNull(context.internalAccess() - .broker()); - } - - @Test - @DisplayName("`CommandDispatcher`") - void commandDispatcher() { - assertNotNull(context.commandBus()); - } - - @Test - @DisplayName("multitenancy state") - void ifSetMultitenant() { - var bc = BoundedContextBuilder.assumingTests(true).build(); - assertTrue(bc.isMultitenant()); - } - } - - @Nested - @DisplayName("register") - class Register { - - @Test - @DisplayName("`AggregateRepository`") - void aggregateRepository() { - registerAndAssertRepository(ProjectAggregate.class); - } - - @Test - @DisplayName("`ProcessManagerRepository`") - void processManagerRepository() { - registerAndAssertRepository(ProjectProcessManager.class); - } - - @Test - @DisplayName("`ProjectionRepository`") - void projectionRepository() { - registerAndAssertRepository(ProjectReport.class); - } - - > void registerAndAssertRepository(Class cls) { - context.register(DefaultRepository.of(cls)); - assertTrue(context.hasEntitiesOfType(cls)); - } - - @Test - @DisplayName("`DefaultRepository` via passed entity class") - void entityClass() { - context.register(ProjectAggregate.class); - assertTrue(context.hasEntitiesOfType(ProjectAggregate.class)); - } - } - - @Nested - @DisplayName("test presence of entities by") - class EntityTypePresence { - - @Nested - @DisplayName("entity state class for") - class ByEntityStateClass { - - @Test - @DisplayName("visible entities") - void visible() { - context.register(ProjectAggregate.class); - assertTrue(context.hasEntitiesWithState(Project.class)); - } - - @Test - @DisplayName("invisible entities") - void invisible() { - context.register(new SecretProjectRepository()); - assertTrue(context.hasEntitiesWithState(SecretProject.class)); - } - } - - @Nested - @DisplayName("entity class for") - class ByEntityClass { - - @Test - @DisplayName("visible entities") - void visible() { - context.register(ProjectAggregate.class); - assertTrue(context.hasEntitiesOfType(ProjectAggregate.class)); - } - - @Test - @DisplayName("invisible entities") - void invisible() { - // Process Managers are invisible by default. - context.register(ProjectProcessManager.class); - assertTrue(context.hasEntitiesOfType(ProjectProcessManager.class)); - } - } - } - - @Test - @DisplayName("propagate registered repositories to `Stand`") - void propagateRepositoriesToStand() { - var context = BoundedContextBuilder.assumingTests().build(); - var stand = context.stand(); - - var repo = - DefaultRepository.of(ProjectAggregate.class); - var stateType = repo.entityStateType(); - - assertThat(stand.exposedTypes()) - .doesNotContain(stateType); - - context.register(repo); - - assertThat(stand.exposedTypes()) - .contains(stateType); - } - - @ParameterizedTest - @MethodSource("sameStateRepositories") - @DisplayName("not allow two entity repositories with entities of same state") - void throwOnSameEntityState(Repository firstRepo, - Repository secondRepo) { - context.register(firstRepo); - assertThrows(IllegalStateException.class, () -> context.register(secondRepo)); - } - - /** - * Returns all combinations of repositories that manage entities of the same state. - * - *

    To check whether a {@link io.spine.server.BoundedContext} really throws - * an {@code IllegalStateException} upon an attempt to register a repository that manages an - * entity of a state that a registered entity repository is already managing, all combinations - * of entities that take state as a type parameter need to be checked. - * - *

    This method returns a stream of pairs of all such combinations, which is a Cartesian - * product of: - *

      - *
    • {@linkplain io.spine.server.procman.ProcessManagerRepository process manager}; - *
    • {@linkplain io.spine.server.aggregate.AggregateRepository aggregate}; - *
    • {@linkplain io.spine.server.projection.ProjectionRepository projection}. - *
    - * All of the returned repositories manage entities of the same state type. - */ - @SuppressWarnings("unused") /* A method source. */ - private static Stream sameStateRepositories() { - Set> repositories = - ImmutableSet.of(DefaultRepository.of(ProjectAggregate.class), - DefaultRepository.of(ProjectProjection.class), - new ProjectCreationRepository()); - - Set> sameStateRepositories = - ImmutableSet.of(DefaultRepository.of(AnotherProjectAggregate.class), - DefaultRepository.of(FinishedProjectProjection.class), - DefaultRepository.of(ProjectRemovalProcman.class)); - - var cartesianProduct = - Sets.cartesianProduct(repositories, sameStateRepositories); - var result = - cartesianProduct.stream() - .map(repos -> Arguments.of(repos.get(0), repos.get(1))); - return result; - } - - @Test - @DisplayName("assign storage during registration if repository does not have one") - void setStorageOnRegister() { - var repository = DefaultRepository.of(ProjectAggregate.class); - context.register(repository); - assertTrue(repository.storageAssigned()); - } - - @Nested - @DisplayName("assign own multitenancy state to") - class AssignMultitenancyState { - - private BoundedContext context; - - @Test - @DisplayName("`CommandBus`") - void toCommandBus() { - context = multiTenant(); - assertMultitenancyEqual(context::isMultitenant, context.commandBus()::isMultitenant); - - context = singleTenant(); - assertMultitenancyEqual(context::isMultitenant, context.commandBus()::isMultitenant); - } - - @Test - @DisplayName("`Stand`") - void toStand() { - context = multiTenant(); - - assertMultitenancyEqual(context::isMultitenant, context.stand()::isMultitenant); - - context = singleTenant(); - - assertMultitenancyEqual(context::isMultitenant, context.stand()::isMultitenant); - } - - private void assertMultitenancyEqual(BooleanSupplier s1, BooleanSupplier s2) { - assertThat(s1.getAsBoolean()) - .isEqualTo(s2.getAsBoolean()); - } - - private BoundedContext multiTenant() { - return BoundedContextBuilder - .assumingTests(true) - .build(); - } - - private BoundedContext singleTenant() { - return BoundedContextBuilder - .assumingTests(false) - .build(); - } - } - - /** - * Simply checks that the result isn't empty to cover the integration with - * {@link VisibilityGuard VisibilityGuard}. - * - *

    See {@linkplain io.spine.server.entity.VisibilityGuardTest tests of VisibilityGuard} - * for how visibility filtering works. - */ - @Test - @DisplayName("obtain entity types by visibility") - void getEntityTypesByVisibility() { - assertThat(context.stateTypes(EntityOption.Visibility.FULL)) - .isEmpty(); - - registerAll(); - - assertThat(context.stateTypes(EntityOption.Visibility.FULL)) - .isNotEmpty(); - } - - @Test - @DisplayName("throw `ISE` when obtaining a repository for non-registered entity state class") - void throwOnNoRepoRegistered() { - // Attempt to get a repository without registering. - assertThrows(IllegalStateException.class, - () -> context.internalAccess() - .findRepository(Project.class)); - } - - @Test - @DisplayName("not expose invisible aggregates") - void notExposeInvisibleAggregates() { - ModelTests.dropAllModels(); - - context.register(new SecretProjectRepository()); - - assertThat(context.internalAccess() - .findRepository(SecretProject.class)) - .isEmpty(); - } - - @Test - @DisplayName("prohibit 3rd-party descendants") - void noExternalDescendants() { - assertThrows( - IllegalStateException.class, - () -> - new BoundedContext(BoundedContextBuilder.assumingTests()) { - - @Internal - @Override - public SystemClient systemClient() { - return nullRef(); - } - } - ); - } - - @Nested - @DisplayName("when closing") - class ClosingContext { - - private final BoundedContextName contextName = newName("TestDomain"); - private final BoundedContextName systemContextName = contextName.toSystem(); - private final Level debugLevel = toJavaLogging(io.spine.logging.Level.Companion.getDEBUG()); - - private Interceptor domainInterceptor; - private Interceptor systemInterceptor; - - @BeforeEach - void closeContext() throws Exception { - var context = BoundedContext.singleTenant(contextName.getValue()).build(); - domainInterceptor = new Interceptor(DomainContext.class, debugLevel); - domainInterceptor.intercept(); - systemInterceptor = new Interceptor(SystemContext.class, debugLevel); - systemInterceptor.intercept(); - - context.close(); - } - - @AfterEach - void releaseInterceptors() { - domainInterceptor.release(); - systemInterceptor.release(); - } - - @Test - @DisplayName("log its closing") - void logClosing() { - var assertDomainLog = domainInterceptor.assertLog().record(); - assertDomainLog.hasLevelThat() - .isEqualTo(debugLevel); - assertDomainLog.hasMessageThat() - .contains(contextName.getValue()); - } - - @Test - @DisplayName("close its System context") - void closeSystem() { - var assertSystemLog = systemInterceptor.assertLog().record(); - assertSystemLog.hasLevelThat() - .isEqualTo(debugLevel); - assertSystemLog.hasMessageThat() - .contains(systemContextName.getValue()); - } - } - - @Test - @DisplayName("return its name in `toString()`") - void stringForm() { - var name = randomString(); - - assertThat(BoundedContext.singleTenant(name) - .build() - .toString()) - .isEqualTo(name); - } - - @Nested - @DisplayName("do not allow registration calls from outside the `io.spine.server` package for") - class RestrictRegistrationCalls { - - @Test - @DisplayName("`Repository`") - void forRepository() { - assertThrowsOn(ForeignContextConfig::repositoryRegistration); - } - - @Test - @DisplayName("`CommandDispatcher`") - void forCommandDispatcher() { - assertThrowsOn(ForeignContextConfig::commandDispatcherRegistration); - } - - @Test - @DisplayName("`EventDispatcher`") - void forEventDispatcher() { - assertThrowsOn(ForeignContextConfig::eventDispatcherRegistration); - } - - private void assertThrowsOn(Executable executable) { - assertThrows(SecurityException.class, executable); - } - } - - @Test - @DisplayName("be equal to another context by its name") - void equality() { - var c1 = BoundedContext.singleTenant("One").build(); - var c2 = BoundedContext.singleTenant("Two").build(); - var c1m = BoundedContext.multitenant("One").build(); - var c2m = BoundedContext.multitenant("Two").build(); - - new EqualsTester() - .addEqualityGroup(c1, c1m) - .addEqualityGroup(c2, c2m) - .testEquals(); - } - - @Test - @DisplayName("be comparable by its name") - void comparability() { - var c1 = BoundedContext.singleTenant("1").build(); - var c2 = BoundedContext.singleTenant("2").build(); - - assertThat(c1).isLessThan(c2); - assertThat(c2).isGreaterThan(c1); - assertThat(c1).isEqualTo(BoundedContext.multitenant("1").build()); - } -} From b952d8361df1de1fba474522b9b13c22036c97e7 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 31 Oct 2023 04:57:04 +0000 Subject: [PATCH 10/32] Test internal access and probe --- .../java/io/spine/server/BoundedContext.java | 16 +- .../spine/server/BoundedContextBuilder.java | 5 +- .../main/java/io/spine/server/bus/Bus.java | 36 +++- .../spine/server/bus/DispatcherRegistry.java | 8 +- .../commandbus/CommandDispatcherRegistry.java | 2 +- .../io/spine/server/BoundedContextSpec.kt | 191 ++++++++++++++---- 6 files changed, 203 insertions(+), 55 deletions(-) diff --git a/server/src/main/java/io/spine/server/BoundedContext.java b/server/src/main/java/io/spine/server/BoundedContext.java index ca45516c2ba..4f900bde252 100644 --- a/server/src/main/java/io/spine/server/BoundedContext.java +++ b/server/src/main/java/io/spine/server/BoundedContext.java @@ -537,7 +537,7 @@ public String toString() { @Internal public final InternalAccess internalAccess() { Security.allowOnlyFrameworkServer(); - return this.internalAccess; + return internalAccess; } /** @@ -549,10 +549,22 @@ public int compareTo(BoundedContext another) { return name().value().compareTo(another.name().value()); } + /** + * Installs the passed probe into this context. + * + * @see #hasProbe() + * @see #probe() + * @see #removeProbe() + * @throws IllegalStateException + * if another probe is already installed + */ public final void install(Probe probe) { checkNotNull(probe); - checkState(this.probe == null, + checkState(this.probe == null || probe.equals(this.probe), "Probe is already installed (`%s`). Please remove previous probe first.", probe); + if (probe.equals(this.probe)) { + return; + } probe.registerWith(this); commandBus.add(probe.commandListener()); eventBus.add(probe.eventListener()); diff --git a/server/src/main/java/io/spine/server/BoundedContextBuilder.java b/server/src/main/java/io/spine/server/BoundedContextBuilder.java index c4b0c76e669..e27a62a726f 100644 --- a/server/src/main/java/io/spine/server/BoundedContextBuilder.java +++ b/server/src/main/java/io/spine/server/BoundedContextBuilder.java @@ -563,13 +563,14 @@ EventBus buildEventBus(BoundedContext context) { * Sets the action to be performed before the context is closed. * *

    The action is performed after the context is closed, but before the - * {@linkplain BoundedContext#onClose() context's own action} is performed. + * {@linkplain BoundedContext#close() context's own action} is performed. * * @param consumer * the action to perform */ - public void setOnBeforeClose(Consumer consumer) { + public BoundedContextBuilder setOnBeforeClose(Consumer consumer) { this.onBeforeClose = checkNotNull(consumer); + return this; } @Nullable Consumer getOnBeforeClose() { diff --git a/server/src/main/java/io/spine/server/bus/Bus.java b/server/src/main/java/io/spine/server/bus/Bus.java index 7b21b549c4a..39179cd1171 100644 --- a/server/src/main/java/io/spine/server/bus/Bus.java +++ b/server/src/main/java/io/spine/server/bus/Bus.java @@ -54,15 +54,15 @@ import static java.util.Collections.singleton; /** - * Abstract base for buses. + * Abstract base for message buses. * * @param the type of outer objects (containing messages of interest) that are posted to the bus * @param the type of envelopes for outer objects used by this bus * @param the type of message class * @param the type of dispatches used by this bus */ -@SuppressWarnings("ClassWithTooManyMethods") // It's OK. @Internal +@SuppressWarnings("ClassWithTooManyMethods") // OK the abstract bus. public abstract class Bus, E extends SignalEnvelope, C extends MessageClass, @@ -75,6 +75,11 @@ public abstract class Bus, /** Listeners of the messages posted to the bus. */ private final Listeners listeners; + /** + * A supplier of the filter chain, which can be configured by the derived classes. + * + * @see #setupFilters(Iterable) + */ private final Supplier> filterChain; protected Bus(BusBuilder builder) { @@ -94,28 +99,38 @@ protected Bus(BusBuilder builder) { */ public void register(D dispatcher) { checkNotNull(dispatcher); - registry().register(dispatcher); + registry.register(dispatcher); } /** - * Unregisters dispatching for message classes of the passed dispatcher. + * Stops dispatching messages the given dispatcher. * * @param dispatcher the dispatcher to unregister */ public void unregister(D dispatcher) { checkNotNull(dispatcher); - registry().unregister(dispatcher); + registry.unregister(dispatcher); } + /** + * Stops dispatching messages the given dispatcher delegate. + */ public void unregister(DispatcherDelegate delegate) { checkNotNull(delegate); + registry.unregister(delegate); } + /** + * Adds the passed listener to the bus. + */ public void add(Listener listener) { checkNotNull(listener); listeners.add(listener); } + /** + * Removes the passed listener from the bus. + */ public void remove(Listener listener) { checkNotNull(listener); listeners.remove(listener); @@ -227,7 +242,7 @@ public final boolean isOpen() { @Override public void close() throws Exception { filterChain().close(); - registry().unregisterAll(); + registry.unregisterAll(); } /** @@ -343,8 +358,10 @@ private Optional filter(E message) { /** * Posts each of the given envelopes into the bus and notifies the given observer. * - * @param envelopes the envelopes to post - * @param observer the observer to be notified of the operation result + * @param envelopes + * the envelopes to post + * @param observer + * the observer to be notified of the operation result * @see #dispatch(SignalEnvelope) */ @SuppressWarnings("ProhibitedExceptionThrown") // Rethrow a caught exception. @@ -418,8 +435,7 @@ protected void onDispatched(SignalId signal) { */ @OverridingMethodsMustInvokeSuper protected Iterable> setupFilters(Iterable> filters) { - return ImmutableList - .>builder() + return ImmutableList.>builder() .add(new ValidatingFilter<>(validator())) .add(new DeadMessageFilter<>(deadMessageHandler(), registry())) .addAll(filters) diff --git a/server/src/main/java/io/spine/server/bus/DispatcherRegistry.java b/server/src/main/java/io/spine/server/bus/DispatcherRegistry.java index 42a7f2fa3dd..e6c8c86df64 100644 --- a/server/src/main/java/io/spine/server/bus/DispatcherRegistry.java +++ b/server/src/main/java/io/spine/server/bus/DispatcherRegistry.java @@ -54,10 +54,9 @@ * @param * the type of the message dispatchers */ -public abstract class -DispatcherRegistry, - E extends MessageEnvelope, - D extends MessageDispatcher> { +public abstract class DispatcherRegistry, + E extends MessageEnvelope, + D extends MessageDispatcher> { /** * The map from a message class to one or more dispatchers of * messages of this class. @@ -79,7 +78,6 @@ public void register(D dispatcher) { public void unregister(D dispatcher) { checkNotNull(dispatcher); checkNotEmpty(dispatcher); - Set messageClasses = dispatcher.messageClasses(); for (var messageClass : messageClasses) { dispatchers.remove(messageClass, dispatcher); diff --git a/server/src/main/java/io/spine/server/commandbus/CommandDispatcherRegistry.java b/server/src/main/java/io/spine/server/commandbus/CommandDispatcherRegistry.java index 3317783068a..04a518cfd94 100644 --- a/server/src/main/java/io/spine/server/commandbus/CommandDispatcherRegistry.java +++ b/server/src/main/java/io/spine/server/commandbus/CommandDispatcherRegistry.java @@ -41,7 +41,7 @@ * *

    There can be only one dispatcher per command class. */ -class CommandDispatcherRegistry +public final class CommandDispatcherRegistry extends DispatcherRegistry { /** diff --git a/server/src/test/java/io/spine/server/BoundedContextSpec.kt b/server/src/test/java/io/spine/server/BoundedContextSpec.kt index b975026c98c..17520d4cea6 100644 --- a/server/src/test/java/io/spine/server/BoundedContextSpec.kt +++ b/server/src/test/java/io/spine/server/BoundedContextSpec.kt @@ -30,7 +30,6 @@ import com.google.common.collect.ImmutableSet import com.google.common.collect.Sets import com.google.common.testing.EqualsTester import com.google.common.truth.Truth.assertThat -import com.google.common.truth.Truth8 import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.collections.shouldNotBeEmpty @@ -43,7 +42,8 @@ import io.spine.core.BoundedContextName import io.spine.core.BoundedContextNames import io.spine.logging.Level.Companion.DEBUG import io.spine.logging.toJavaLogging -import io.spine.option.EntityOption +import io.spine.option.EntityOption.Visibility.FULL +import io.spine.server.BoundedContextBuilder.assumingTests import io.spine.server.bc.given.AnotherProjectAggregate import io.spine.server.bc.given.FinishedProjectProjection import io.spine.server.bc.given.ProjectAggregate @@ -54,8 +54,12 @@ import io.spine.server.bc.given.ProjectRemovalProcman import io.spine.server.bc.given.ProjectReport import io.spine.server.bc.given.SecretProjectRepository import io.spine.server.bc.given.TestEventSubscriber +import io.spine.server.bus.Listener import io.spine.server.entity.Entity import io.spine.server.entity.Repository +import io.spine.server.event.EventDispatcher +import io.spine.server.type.CommandEnvelope +import io.spine.server.type.EventEnvelope import io.spine.system.server.SystemClient import io.spine.system.server.SystemContext import io.spine.test.bc.Project @@ -64,6 +68,7 @@ import io.spine.testing.TestValues import io.spine.testing.logging.Interceptor import io.spine.testing.server.model.ModelTests import java.util.function.BooleanSupplier +import java.util.function.Consumer import java.util.logging.Level import java.util.stream.Stream import org.junit.jupiter.api.AfterEach @@ -72,6 +77,7 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.function.Executable import org.junit.jupiter.params.ParameterizedTest @@ -88,6 +94,7 @@ import org.junit.jupiter.params.provider.MethodSource * * `spine/test/bc/events.proto` — events. */ @DisplayName("`BoundedContext` should") +@Suppress("TestFunctionName") // For readability of methods named after class names. internal class BoundedContextSpec { private val subscriber = TestEventSubscriber() @@ -98,19 +105,20 @@ internal class BoundedContextSpec { @BeforeEach fun setUp() { ModelTests.dropAllModels() - context = BoundedContextBuilder.assumingTests(true).build() + context = assumingTests(true).build() } @AfterEach fun tearDown() { if (handlersRegistered) { - context.eventBus() - .unregister(subscriber) + context.eventBus().unregister(subscriber) + } + if (context.isOpen) { + context.close() } - context.close() } - /** Registers all test repositories, handlers, etc. */ + /** Registers all test repositories, handlers, etc. */ private fun registerAll() { context.register(DefaultRepository.of(ProjectAggregate::class.java)) context.eventBus().register(subscriber) @@ -119,35 +127,68 @@ internal class BoundedContextSpec { @Nested @DisplayName("provide access to") - @Suppress("TestFunctionName") // For readability of methods named after class names. internal inner class Return { + @Test + fun CommandBus() { + context.commandBus() shouldNotBe null + } + @Test fun EventBus() { context.eventBus() shouldNotBe null } @Test - fun IntegrationBroker() { - context.internalAccess().broker() shouldNotBe null + fun ImportBus() { + context.importBus() shouldNotBe null } @Test - fun CommandDispatcher() { - context.commandBus() shouldNotBe null + fun Stand() { + context.stand() shouldNotBe null } @Test fun `multitenancy state`() { - BoundedContextBuilder.assumingTests(true).build().use { + assumingTests(true).build().use { it.isMultitenant shouldBe true } } + + @Test + fun SystemClient() { + context.systemClient() shouldNotBe null + } + } + + + @Nested + @DisplayName("provide internal secured access to") + inner class InternalAccess { + + private val internalAccess = context.internalAccess() + + @Test + fun IntegrationBroker() { + internalAccess.broker() shouldNotBe null + } + + @Test + fun TenantIndex() { + internalAccess.tenantIndex() shouldNotBe null + } + + @Test + fun `prohibiting access from outside of the 'server' package`() { + assertThrows { + Consumer { context.internalAccess() } + } + } } @Nested @DisplayName("register") - @Suppress("TestFunctionName") // For readability of methods named after class names. internal inner class Register { @Test @@ -222,7 +263,7 @@ internal class BoundedContextSpec { @Test fun `propagate registered repositories to 'Stand'`() { - val context = BoundedContextBuilder.assumingTests().build() + val context = assumingTests().build() val stand = context.stand() val repo = DefaultRepository.of(ProjectAggregate::class.java) @@ -257,7 +298,6 @@ internal class BoundedContextSpec { @Nested @DisplayName("assign own multitenancy state to") - @Suppress("TestFunctionName") // for readability of methods named after class names. internal inner class AssignMultitenancyState { private lateinit var context: BoundedContext @@ -267,12 +307,14 @@ internal class BoundedContextSpec { context = multiTenant() assertMultitenancyEqual( { context.isMultitenant }, - { context.commandBus().isMultitenant }) + { context.commandBus().isMultitenant } + ) context = singleTenant() assertMultitenancyEqual( { context.isMultitenant }, - { context.commandBus().isMultitenant }) + { context.commandBus().isMultitenant } + ) } @Test @@ -281,13 +323,15 @@ internal class BoundedContextSpec { assertMultitenancyEqual( { context.isMultitenant }, - { context.stand().isMultitenant }) + { context.stand().isMultitenant } + ) context = singleTenant() assertMultitenancyEqual( { context.isMultitenant }, - { context.stand().isMultitenant }) + { context.stand().isMultitenant } + ) } private fun assertMultitenancyEqual(s1: BooleanSupplier, s2: BooleanSupplier) { @@ -295,24 +339,24 @@ internal class BoundedContextSpec { } private fun multiTenant(): BoundedContext = - BoundedContextBuilder.assumingTests(true).build() + assumingTests(true).build() private fun singleTenant(): BoundedContext = - BoundedContextBuilder.assumingTests(false).build() + assumingTests(false).build() } /** * Simply checks that the result isn't empty to cover the integration with * [VisibilityGuard]. * - * See [tests of VisibilityGuard][io.spine.server.entity.VisibilityGuardTest] + * See [tests of VisibilityGuard][io.spine.server.VisibilityGuard] * for how visibility filtering works. */ @Test fun `obtain entity types by visibility`() { - context.stateTypes(EntityOption.Visibility.FULL).shouldBeEmpty() + context.stateTypes(FULL).shouldBeEmpty() registerAll() - context.stateTypes(EntityOption.Visibility.FULL).shouldNotBeEmpty() + context.stateTypes(FULL).shouldNotBeEmpty() } @Test @@ -334,7 +378,7 @@ internal class BoundedContextSpec { @DisplayName("prohibit 3rd-party descendants") fun `prohibit 3rd-party descendants`() { assertThrows { - object : BoundedContext(BoundedContextBuilder.assumingTests()) { + object : BoundedContext(assumingTests()) { @Internal override fun systemClient(): SystemClient = TestValues.nullRef() } @@ -395,7 +439,6 @@ internal class BoundedContextSpec { @Nested @DisplayName("do not allow registration calls from outside the `io.spine.server` package for") - @Suppress("TestFunctionName") // For readability of methods named after class names. internal inner class RestrictRegistrationCalls { @Test @@ -424,11 +467,16 @@ internal class BoundedContextSpec { val c2 = BoundedContext.singleTenant("Two").build() val c1m = BoundedContext.multitenant("One").build() val c2m = BoundedContext.multitenant("Two").build() - - EqualsTester() - .addEqualityGroup(c1, c1m) - .addEqualityGroup(c2, c2m) - .testEquals() + try { + EqualsTester() + .addEqualityGroup(c1, c1m) + .addEqualityGroup(c2, c2m) + .testEquals() + } finally { + listOf(c1, c2, c1m, c2m).forEach { + it.close() + } + } } @Test @@ -436,9 +484,71 @@ internal class BoundedContextSpec { val c1 = BoundedContext.singleTenant("1").build() val c2 = BoundedContext.singleTenant("2").build() - assertThat(c1).isLessThan(c2) - assertThat(c2).isGreaterThan(c1) - assertThat(c1).isEqualTo(BoundedContext.multitenant("1").build()) + try { + assertThat(c1).isLessThan(c2) + assertThat(c2).isGreaterThan(c1) + assertThat(c1).isEqualTo(BoundedContext.multitenant("1").build()) + } finally { + listOf(c1, c2).forEach { + it.close() + } + } + } + + @Nested + @DisplayName("support `Probe`") + inner class SupportProbe { + + private val probe: BoundedContext.Probe = EmptyProbe() + + @BeforeEach + fun installProbe() { + context.install(probe) + } + + @AfterEach + fun removeProbe() { + if (context.hasProbe()) { + context.removeProbe() + } + } + + @Test + fun installation() { + context.hasProbe() shouldBe true + } + + @Test + fun removal() { + context.hasProbe() shouldBe true + context.removeProbe() + context.hasProbe() shouldBe false + } + + @Test + fun `allowing passing the same instance`() { + assertDoesNotThrow { + context.install(probe) + } + } + + @Test + fun `removal when the context closes`() { + context.close() + context.hasProbe() shouldBe false + } + } + + @Test + fun `call 'onBeforeClose' when configured`() { + var called = false + val context = assumingTests() + .setOnBeforeClose { called = true } + .build() + context.use { + it.close() + } + called shouldBe true } companion object { @@ -487,3 +597,14 @@ internal class BoundedContextSpec { } } } + +private class EmptyProbe : BoundedContext.Probe { + private lateinit var context: BoundedContext + override fun registerWith(context: BoundedContext) { + this.context = context + } + override fun isRegistered(): Boolean = this::context.isInitialized + override fun commandListener(): Listener = Listener { _ -> } + override fun eventListener(): Listener = Listener { _ -> } + override fun eventDispatchers(): Set = mutableSetOf() +} From e97f29f42be366247a210b1f638c78d97b011930 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 31 Oct 2023 22:04:19 +0000 Subject: [PATCH 11/32] Add `BoundedContext` tests Also: * Fix an error message in `Policy`. --- .../java/io/spine/server/BoundedContext.java | 3 +- .../kotlin/io/spine/server/event/Policy.kt | 7 +- .../test/java/com/example/ForeignClass.java | 5 ++ .../io/spine/server/BoundedContextSpec.kt | 85 ++++++++++++++----- .../test/proto/spine/test/shared_events.proto | 51 +++++++++++ 5 files changed, 124 insertions(+), 27 deletions(-) create mode 100644 server/src/test/proto/spine/test/shared_events.proto diff --git a/server/src/main/java/io/spine/server/BoundedContext.java b/server/src/main/java/io/spine/server/BoundedContext.java index 4f900bde252..42b3e7efb06 100644 --- a/server/src/main/java/io/spine/server/BoundedContext.java +++ b/server/src/main/java/io/spine/server/BoundedContext.java @@ -524,8 +524,7 @@ private void shutDownRepositories() { */ @Override public String toString() { - return spec.name() - .getValue(); + return spec.name().value(); } /** diff --git a/server/src/main/kotlin/io/spine/server/event/Policy.kt b/server/src/main/kotlin/io/spine/server/event/Policy.kt index 61128bd4bc7..ff406c6b77d 100644 --- a/server/src/main/kotlin/io/spine/server/event/Policy.kt +++ b/server/src/main/kotlin/io/spine/server/event/Policy.kt @@ -33,6 +33,7 @@ import io.spine.core.ContractFor import io.spine.logging.WithLogging import io.spine.server.BoundedContext import io.spine.server.type.EventClass +import io.spine.string.Diags /** * A policy converts one event into zero to many other events. @@ -63,7 +64,7 @@ public abstract class Policy : AbstractEventReactor(), WithLog * Handles an event and produces some number of events in response. */ @ContractFor(handler = React::class) - protected abstract fun whenever(event: E): Iterable + protected abstract fun whenever(event: E): Iterable final override fun registerWith(context: BoundedContext) { super.registerWith(context) @@ -85,7 +86,9 @@ public abstract class Policy : AbstractEventReactor(), WithLog private fun checkReceptors(events: Iterable) { val classes = events.toList() check(classes.size == 1) { - "Policy `${javaClass.name}` handles too many events: [${classes.joinToString()}]." + "A policy should handle only one event." + + " `${javaClass.name}` attempts to handle ${classes.size}:" + + " [${classes.stream().collect(Diags.toEnumerationBackticked())}]." } } } diff --git a/server/src/test/java/com/example/ForeignClass.java b/server/src/test/java/com/example/ForeignClass.java index 3d0ab22957b..e2b428a23b2 100644 --- a/server/src/test/java/com/example/ForeignClass.java +++ b/server/src/test/java/com/example/ForeignClass.java @@ -26,6 +26,7 @@ package com.example; +import io.spine.server.BoundedContext; import io.spine.server.security.GivenRestrictedApi; import io.spine.server.security.Security; @@ -42,4 +43,8 @@ private ForeignClass() {} public static void attemptToCallRestrictedApi() { GivenRestrictedApi.guardedMethod(); } + + public static void callInternalOf(BoundedContext context) { + context.internalAccess(); + } } diff --git a/server/src/test/java/io/spine/server/BoundedContextSpec.kt b/server/src/test/java/io/spine/server/BoundedContextSpec.kt index 17520d4cea6..14db370a5f7 100644 --- a/server/src/test/java/io/spine/server/BoundedContextSpec.kt +++ b/server/src/test/java/io/spine/server/BoundedContextSpec.kt @@ -25,24 +25,32 @@ */ package io.spine.server +import com.example.ForeignClass import com.example.ForeignContextConfig import com.google.common.collect.ImmutableSet import com.google.common.collect.Sets import com.google.common.testing.EqualsTester -import com.google.common.truth.Truth.assertThat +import com.google.protobuf.Message import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.collections.shouldNotBeEmpty import io.kotest.matchers.collections.shouldNotContain +import io.kotest.matchers.comparables.shouldBeGreaterThan +import io.kotest.matchers.comparables.shouldBeLessThan import io.kotest.matchers.optional.shouldBeEmpty +import io.kotest.matchers.optional.shouldBePresent +import io.kotest.matchers.optional.shouldNotBeEmpty import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.spine.annotation.Internal +import io.spine.base.EventMessage import io.spine.core.BoundedContextName import io.spine.core.BoundedContextNames import io.spine.logging.Level.Companion.DEBUG import io.spine.logging.toJavaLogging import io.spine.option.EntityOption.Visibility.FULL +import io.spine.server.BoundedContext.multitenant +import io.spine.server.BoundedContext.singleTenant import io.spine.server.BoundedContextBuilder.assumingTests import io.spine.server.bc.given.AnotherProjectAggregate import io.spine.server.bc.given.FinishedProjectProjection @@ -58,17 +66,20 @@ import io.spine.server.bus.Listener import io.spine.server.entity.Entity import io.spine.server.entity.Repository import io.spine.server.event.EventDispatcher +import io.spine.server.event.Policy +import io.spine.server.event.React import io.spine.server.type.CommandEnvelope import io.spine.server.type.EventEnvelope import io.spine.system.server.SystemClient import io.spine.system.server.SystemContext import io.spine.test.bc.Project import io.spine.test.bc.SecretProject +import io.spine.test.shared.event.SomethingElseHappened +import io.spine.test.shared.event.SomethingHappened import io.spine.testing.TestValues import io.spine.testing.logging.Interceptor import io.spine.testing.server.model.ModelTests import java.util.function.BooleanSupplier -import java.util.function.Consumer import java.util.logging.Level import java.util.stream.Stream import org.junit.jupiter.api.AfterEach @@ -96,10 +107,11 @@ import org.junit.jupiter.params.provider.MethodSource @DisplayName("`BoundedContext` should") @Suppress("TestFunctionName") // For readability of methods named after class names. internal class BoundedContextSpec { - private val subscriber = TestEventSubscriber() private lateinit var context: BoundedContext + private val subscriber = TestEventSubscriber() + private var handlersRegistered = false @BeforeEach @@ -162,27 +174,31 @@ internal class BoundedContextSpec { } } - @Nested @DisplayName("provide internal secured access to") inner class InternalAccess { - private val internalAccess = context.internalAccess() + private lateinit var access: BoundedContext.InternalAccess + + @BeforeEach + fun getAccess() { + access = context.internalAccess() + } @Test fun IntegrationBroker() { - internalAccess.broker() shouldNotBe null + access.broker() shouldNotBe null } @Test fun TenantIndex() { - internalAccess.tenantIndex() shouldNotBe null + access.tenantIndex() shouldNotBe null } @Test fun `prohibiting access from outside of the 'server' package`() { assertThrows { - Consumer { context.internalAccess() } + ForeignClass.callInternalOf(context) } } } @@ -397,7 +413,7 @@ internal class BoundedContextSpec { @BeforeEach fun closeContext() { - val context = BoundedContext.singleTenant(contextName.value).build() + val context = singleTenant(contextName.value).build() domainInterceptor = Interceptor(DomainContext::class.java, debugLevel) domainInterceptor.intercept() systemInterceptor = Interceptor(SystemContext::class.java, debugLevel) @@ -432,7 +448,7 @@ internal class BoundedContextSpec { @Test fun `return its name in 'toString()'`() { val name = TestValues.randomString() - BoundedContext.singleTenant(name).build().use { + singleTenant(name).build().use { it.toString() shouldBe name } } @@ -463,10 +479,10 @@ internal class BoundedContextSpec { @Test fun `be equal to another context by its name`() { - val c1 = BoundedContext.singleTenant("One").build() - val c2 = BoundedContext.singleTenant("Two").build() - val c1m = BoundedContext.multitenant("One").build() - val c2m = BoundedContext.multitenant("Two").build() + val c1 = singleTenant("One").build() + val c2 = singleTenant("Two").build() + val c1m = multitenant("One").build() + val c2m = multitenant("Two").build() try { EqualsTester() .addEqualityGroup(c1, c1m) @@ -481,13 +497,14 @@ internal class BoundedContextSpec { @Test fun `be comparable by its name`() { - val c1 = BoundedContext.singleTenant("1").build() - val c2 = BoundedContext.singleTenant("2").build() - + val c1 = singleTenant("1").build() + val c2 = singleTenant("2").build() try { - assertThat(c1).isLessThan(c2) - assertThat(c2).isGreaterThan(c1) - assertThat(c1).isEqualTo(BoundedContext.multitenant("1").build()) + c1 shouldBeLessThan c2 + c2 shouldBeGreaterThan c1 + multitenant("1").build().use { + c1 shouldBe it + } } finally { listOf(c1, c2).forEach { it.close() @@ -496,7 +513,7 @@ internal class BoundedContextSpec { } @Nested - @DisplayName("support `Probe`") + @DisplayName("support diagnostics via `Probe`") inner class SupportProbe { private val probe: BoundedContext.Probe = EmptyProbe() @@ -516,6 +533,9 @@ internal class BoundedContextSpec { @Test fun installation() { context.hasProbe() shouldBe true + context.probe().shouldBePresent { + it shouldBe probe + } } @Test @@ -526,12 +546,19 @@ internal class BoundedContextSpec { } @Test - fun `allowing passing the same instance`() { + fun `allowing passing the same probe`() { assertDoesNotThrow { context.install(probe) } } + @Test + fun `prohibiting setting another probe`() { + assertThrows { + context.install(EmptyProbe()) + } + } + @Test fun `removal when the context closes`() { context.close() @@ -606,5 +633,17 @@ private class EmptyProbe : BoundedContext.Probe { override fun isRegistered(): Boolean = this::context.isInitialized override fun commandListener(): Listener = Listener { _ -> } override fun eventListener(): Listener = Listener { _ -> } - override fun eventDispatchers(): Set = mutableSetOf() + override fun eventDispatchers(): Set = mutableSetOf( + StubPolicy1(), StubPolicy2() + ) +} + +private class StubPolicy1: Policy() { + @React + override fun whenever(event: SomethingHappened): Iterable = setOf() +} + +private class StubPolicy2: Policy() { + @React + override fun whenever(event: SomethingElseHappened): Iterable = setOf() } diff --git a/server/src/test/proto/spine/test/shared_events.proto b/server/src/test/proto/spine/test/shared_events.proto new file mode 100644 index 00000000000..7bf02f3e5eb --- /dev/null +++ b/server/src/test/proto/spine/test/shared_events.proto @@ -0,0 +1,51 @@ +/* + * Copyright 2022, TeamDev. All rights reserved. + * + * Licensed 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 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +syntax = "proto3"; + +package spine.test.event; + +import "spine/options.proto"; + +option (type_url_prefix) = "type.spine.io"; +option java_package = "io.spine.test.shared.event"; +option java_outer_classname = "SharedEventsProto"; +option java_multiple_files = true; + +/** + * This is a stub event message for the cases when we need just a valid event message and + * are not interested in its contents. + */ +message SomethingHappened { + string description = 1; +} + +/** + * Similar to `SomethingHappened` but with a different type for cases when more than one + * stub event is required. + */ +message SomethingElseHappened { + string description = 1; +} From 458d589fbbdac1d10bcfad508e47dcf872756398 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 31 Oct 2023 22:56:42 +0000 Subject: [PATCH 12/32] Test with a policy handling external event --- .../java/io/spine/server/BoundedContextSpec.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/server/src/test/java/io/spine/server/BoundedContextSpec.kt b/server/src/test/java/io/spine/server/BoundedContextSpec.kt index 14db370a5f7..e18f52bf225 100644 --- a/server/src/test/java/io/spine/server/BoundedContextSpec.kt +++ b/server/src/test/java/io/spine/server/BoundedContextSpec.kt @@ -30,7 +30,6 @@ import com.example.ForeignContextConfig import com.google.common.collect.ImmutableSet import com.google.common.collect.Sets import com.google.common.testing.EqualsTester -import com.google.protobuf.Message import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.collections.shouldNotBeEmpty @@ -39,13 +38,13 @@ import io.kotest.matchers.comparables.shouldBeGreaterThan import io.kotest.matchers.comparables.shouldBeLessThan import io.kotest.matchers.optional.shouldBeEmpty import io.kotest.matchers.optional.shouldBePresent -import io.kotest.matchers.optional.shouldNotBeEmpty import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.spine.annotation.Internal import io.spine.base.EventMessage import io.spine.core.BoundedContextName import io.spine.core.BoundedContextNames +import io.spine.core.External import io.spine.logging.Level.Companion.DEBUG import io.spine.logging.toJavaLogging import io.spine.option.EntityOption.Visibility.FULL @@ -70,6 +69,7 @@ import io.spine.server.event.Policy import io.spine.server.event.React import io.spine.server.type.CommandEnvelope import io.spine.server.type.EventEnvelope +import io.spine.system.server.ConstraintViolated import io.spine.system.server.SystemClient import io.spine.system.server.SystemContext import io.spine.test.bc.Project @@ -634,7 +634,7 @@ private class EmptyProbe : BoundedContext.Probe { override fun commandListener(): Listener = Listener { _ -> } override fun eventListener(): Listener = Listener { _ -> } override fun eventDispatchers(): Set = mutableSetOf( - StubPolicy1(), StubPolicy2() + StubPolicy1(), StubPolicy2(), StubPolicy3() ) } @@ -647,3 +647,12 @@ private class StubPolicy2: Policy() { @React override fun whenever(event: SomethingElseHappened): Iterable = setOf() } + +/** + * A policy which reacts to an external event. + */ +private class StubPolicy3: Policy() { + @React + override fun whenever(@External event: ConstraintViolated): Iterable = setOf() + +} From 26be6bf9d2bb0b2c78f98cd6eab09f91eca81c88 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 31 Oct 2023 22:56:55 +0000 Subject: [PATCH 13/32] Improve documentation layout --- .../java/io/spine/server/BoundedContext.java | 21 ++++++++++++---- .../aggregate/AggregateRootDirectory.java | 15 ++++++------ .../commandbus/CommandDispatcherRegistry.java | 24 +++++++++++-------- .../io/spine/server/BoundedContextExts.kt | 6 +++++ .../kotlin/io/spine/server/event/Policy.kt | 1 - 5 files changed, 45 insertions(+), 22 deletions(-) diff --git a/server/src/main/java/io/spine/server/BoundedContext.java b/server/src/main/java/io/spine/server/BoundedContext.java index 42b3e7efb06..ce7021cb3e7 100644 --- a/server/src/main/java/io/spine/server/BoundedContext.java +++ b/server/src/main/java/io/spine/server/BoundedContext.java @@ -134,7 +134,7 @@ public abstract class BoundedContext * Creates new instance. * * @throws IllegalStateException - * if called from a derived class, which is not a part of the framework + * if called from a derived class, which is not a part of the framework * @apiNote * This constructor is for internal use of the framework. Application developers should not * create classes derived from {@code BoundedContext}. @@ -277,11 +277,12 @@ private void unregisterCommandDispatcher(CommandDispatcherDelegate dispatcher) { /** * Registering the passed event dispatcher with the buses of this context. * - *

    If the passed instance dispatches domestic events, registers it with the {@code EventBus}. - * If the passed instance dispatches external events, registers it with + *

    If the given instance dispatches domestic events, registers it with the {@code EventBus}. + * If the given instance dispatches external events, registers it with * the {@code IntegrationBroker}. * * @see #registerEventDispatcher(EventDispatcherDelegate) + * @see #unregisterEventDispatcher(EventDispatcher) */ protected void registerEventDispatcher(EventDispatcher dispatcher) { checkNotNull(dispatcher); @@ -302,7 +303,16 @@ protected void registerEventDispatcher(EventDispatcher dispatcher) { } } - protected void unregisterEventDispatcher(EventDispatcher dispatcher) { + /** + * Unregisters the passed event dispatcher from the buses of this context. + * + *

    If the given instance dispatches domestic events, unregisters it from + * the {@code EventBus}. If the given instance dispatches external events, unregisters it from + * the {@code IntegrationBroker}. + * + * @see #registerEventDispatcher(EventDispatcher) + */ + private void unregisterEventDispatcher(EventDispatcher dispatcher) { checkNotNull(dispatcher); Security.allowOnlyFrameworkServer(); unregisterIfAware(dispatcher); @@ -748,6 +758,9 @@ public TenantIndex tenantIndex() { return tenantIndex; } + /** + * Obtains an {@link AggregateRootDirectory} of this {@code BoundedContext}. + */ public AggregateRootDirectory aggregateRootDirectory() { return aggregateRootDirectory; } diff --git a/server/src/main/java/io/spine/server/aggregate/AggregateRootDirectory.java b/server/src/main/java/io/spine/server/aggregate/AggregateRootDirectory.java index ac130afd411..20ff7ac4bb4 100644 --- a/server/src/main/java/io/spine/server/aggregate/AggregateRootDirectory.java +++ b/server/src/main/java/io/spine/server/aggregate/AggregateRootDirectory.java @@ -35,8 +35,8 @@ /** * A mapping of aggregate roots to the associated {@linkplain AggregatePart parts}. * - *

    In the directory, the aggregate root is represented by its type and the parts - by their - * repositories. + *

    In the directory, the aggregate root is represented by its type and + * the parts - by their repositories. */ @Experimental @SPI @@ -48,17 +48,18 @@ public interface AggregateRootDirectory { void register(AggregatePartRepository repository); /** - * Looks up an aggregate part repository by the type of the root and the type of the part state. + * Looks up an aggregate part repository by the type of the root and + * the type of the part state. * - *

    If a matching repository if registered, it is obtained by this method with no regard to - * the visibility of the aggregate. + *

    If a matching repository if registered, it is obtained by this method with + * no regard to the visibility of the aggregate. * * @param rootClass * the type of the aggregate root * @param partStateClass * the type of the part state - * @return the {@link AggregatePartRepository} or {@code Optional.empty()} if such a repository - * is not registered + * @return the {@link AggregatePartRepository} or {@code Optional.empty()} if + * such a repository is not registered */ Optional> findPart(Class> rootClass, diff --git a/server/src/main/java/io/spine/server/commandbus/CommandDispatcherRegistry.java b/server/src/main/java/io/spine/server/commandbus/CommandDispatcherRegistry.java index 04a518cfd94..928f1868015 100644 --- a/server/src/main/java/io/spine/server/commandbus/CommandDispatcherRegistry.java +++ b/server/src/main/java/io/spine/server/commandbus/CommandDispatcherRegistry.java @@ -72,9 +72,11 @@ public void register(CommandDispatcher dispatcher) { *

    Ensures that there are not dispatchers already registered for * the commands of this dispatcher. * - * @param dispatcher the dispatcher to check - * @throws IllegalStateException if there is at least one command of the passed dispatcher - * that already has a registered dispatcher + * @param dispatcher + * the dispatcher to check + * @throws IllegalStateException + * if there is at least one command of the passed dispatcher that already + * has a registered dispatcher */ @Override protected void checkDispatcher(CommandDispatcher dispatcher) @@ -87,8 +89,8 @@ protected void checkDispatcher(CommandDispatcher dispatcher) * Ensures that all of the commands of the passed dispatcher are not * already registered for dispatched in this command bus. * - * @throws IllegalArgumentException if at least one command class already has - * a registered dispatcher + * @throws IllegalArgumentException + * if at least one command class already has a registered dispatcher */ private void checkNotAlreadyRegistered(CommandDispatcher dispatcher) { Set commandClasses = dispatcher.messageClasses(); @@ -107,9 +109,12 @@ private void checkNotAlreadyRegistered(CommandDispatcher dispatcher) { * *

    This is a convenience method for checking registration of handling dispatching. * - * @param alreadyRegistered the map of already registered classes or an empty set - * @param registeringObject the object which tries to register dispatching or handling - * @throws IllegalArgumentException if the set is not empty + * @param alreadyRegistered + * the map of already registered classes or an empty set + * @param registeringObject + * the object which tries to register dispatching or handling + * @throws IllegalArgumentException + * if the set is not empty */ private static void doCheck(Map alreadyRegistered, Object registeringObject) { @@ -136,8 +141,7 @@ protected Set registeredMessageClasses() { /** * {@inheritDoc} * - *

    Overrides to expose the method to - * {@link CommandBus#close() CommandBus}. + *

    Overrides to expose the method to {@link CommandBus#close() CommandBus}. */ @Override protected void unregisterAll() { diff --git a/server/src/main/kotlin/io/spine/server/BoundedContextExts.kt b/server/src/main/kotlin/io/spine/server/BoundedContextExts.kt index a412fc12bad..417132e8b4d 100644 --- a/server/src/main/kotlin/io/spine/server/BoundedContextExts.kt +++ b/server/src/main/kotlin/io/spine/server/BoundedContextExts.kt @@ -29,8 +29,14 @@ package io.spine.server import io.spine.base.EntityState import io.spine.server.entity.Entity +/** + * Tells if the bounded context has entities of the given type. + */ public inline fun > BoundedContext.hasEntitiesOfType(): Boolean = hasEntitiesOfType(E::class.java) +/** + * Tells if the bounded context has entities with the given state type. + */ public inline fun > BoundedContext.hasEntitiesWithState(): Boolean = hasEntitiesWithState(S::class.java) diff --git a/server/src/main/kotlin/io/spine/server/event/Policy.kt b/server/src/main/kotlin/io/spine/server/event/Policy.kt index ff406c6b77d..e2fdc9b270f 100644 --- a/server/src/main/kotlin/io/spine/server/event/Policy.kt +++ b/server/src/main/kotlin/io/spine/server/event/Policy.kt @@ -27,7 +27,6 @@ package io.spine.server.event import com.google.common.collect.ImmutableSet -import com.google.protobuf.Message import io.spine.base.EventMessage import io.spine.core.ContractFor import io.spine.logging.WithLogging From c54a9e569e217976e6443a4ddeb7633f1a66b0bf Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 31 Oct 2023 23:42:12 +0000 Subject: [PATCH 14/32] Use Kotlin ext. fun --- server/src/main/kotlin/io/spine/server/event/Policy.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/kotlin/io/spine/server/event/Policy.kt b/server/src/main/kotlin/io/spine/server/event/Policy.kt index e2fdc9b270f..e5e61ee0b17 100644 --- a/server/src/main/kotlin/io/spine/server/event/Policy.kt +++ b/server/src/main/kotlin/io/spine/server/event/Policy.kt @@ -87,7 +87,7 @@ public abstract class Policy : AbstractEventReactor(), WithLog check(classes.size == 1) { "A policy should handle only one event." + " `${javaClass.name}` attempts to handle ${classes.size}:" + - " [${classes.stream().collect(Diags.toEnumerationBackticked())}]." + " [${classes.joinToString(separator = "`, `", prefix = "`", postfix = "`")}]." } } } From 15c48214598d8271f8fa8129a4116f58014a5bdc Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 31 Oct 2023 23:42:33 +0000 Subject: [PATCH 15/32] Bump external dependencies --- .../io/spine/internal/dependency/Guava.kt | 2 +- .../io/spine/internal/dependency/Jackson.kt | 4 +- .../io/spine/internal/dependency/Protobuf.kt | 2 +- license-report.md | 210 +++++++++--------- 4 files changed, 103 insertions(+), 115 deletions(-) diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Guava.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Guava.kt index c99f17fa707..810a8041558 100644 --- a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Guava.kt +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Guava.kt @@ -37,7 +37,7 @@ package io.spine.internal.dependency */ @Suppress("unused", "ConstPropertyName") object Guava { - private const val version = "32.1.2-jre" + private const val version = "32.1.3-jre" const val lib = "com.google.guava:guava:${version}" const val testLib = "com.google.guava:guava-testlib:${version}" } diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Jackson.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Jackson.kt index 3b2821668bc..8bfaa5c1b26 100644 --- a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Jackson.kt +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Jackson.kt @@ -29,8 +29,8 @@ package io.spine.internal.dependency // https://github.com/FasterXML/jackson/wiki/Jackson-Releases @Suppress("unused", "ConstPropertyName") object Jackson { - const val version = "2.15.2" - private const val databindVersion = "2.15.2" + const val version = "2.15.3" + private const val databindVersion = "2.15.3" private const val coreGroup = "com.fasterxml.jackson.core" private const val dataformatGroup = "com.fasterxml.jackson.dataformat" diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Protobuf.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Protobuf.kt index c115f9b4224..8f02b9b6a3d 100644 --- a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Protobuf.kt +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Protobuf.kt @@ -33,7 +33,7 @@ package io.spine.internal.dependency ) object Protobuf { private const val group = "com.google.protobuf" - const val version = "3.24.1" + const val version = "3.24.4" val libs = listOf( "${group}:protobuf-java:${version}", "${group}:protobuf-java-util:${version}", diff --git a/license-report.md b/license-report.md index 69df68ab262..1bf9e5116ad 100644 --- a/license-report.md +++ b/license-report.md @@ -27,11 +27,10 @@ * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-jre. * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava-parent. **Version** : 32.1.2-jre.**No license information found** 1. **Group** : com.google.guava. **Name** : listenablefuture. **Version** : 9999.0-empty-to-avoid-conflict-with-guava. * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -39,15 +38,15 @@ * **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.4. * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.57.2. @@ -129,27 +128,27 @@ * **Project URL:** [https://jcommander.org](https://jcommander.org) * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.15.2.**No license information found** -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.15.3.**No license information found** +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-xml. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-xml. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformat-xml](https://github.com/FasterXML/jackson-dataformat-xml) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-kotlin. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-kotlin. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-module-kotlin](https://github.com/FasterXML/jackson-module-kotlin) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -231,12 +230,11 @@ * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-jre. * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava-parent. **Version** : 32.1.2-jre.**No license information found** -1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.3-jre. * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : com.google.guava. **Name** : listenablefuture. **Version** : 9999.0-empty-to-avoid-conflict-with-guava. @@ -250,15 +248,15 @@ * **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.4. * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 1. **Group** : com.google.protobuf. **Name** : protoc. **Version** : 3.24.1. @@ -800,7 +798,7 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Sat Oct 28 16:50:16 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Tue Oct 31 23:41:13 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -824,11 +822,10 @@ This report was generated on **Sat Oct 28 16:50:16 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-jre. * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava-parent. **Version** : 32.1.2-jre.**No license information found** 1. **Group** : com.google.guava. **Name** : listenablefuture. **Version** : 9999.0-empty-to-avoid-conflict-with-guava. * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -836,15 +833,15 @@ This report was generated on **Sat Oct 28 16:50:16 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.4. * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 1. **Group** : io.spine.validation. **Name** : spine-validation-java-runtime. **Version** : 2.0.0-SNAPSHOT.109.**No license information found** @@ -894,27 +891,27 @@ This report was generated on **Sat Oct 28 16:50:16 WEST 2023** using [Gradle-Lic * **Project URL:** [https://jcommander.org](https://jcommander.org) * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.15.2.**No license information found** -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.15.3.**No license information found** +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-xml. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-xml. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformat-xml](https://github.com/FasterXML/jackson-dataformat-xml) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-kotlin. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-kotlin. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-module-kotlin](https://github.com/FasterXML/jackson-module-kotlin) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -996,12 +993,11 @@ This report was generated on **Sat Oct 28 16:50:16 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-jre. * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava-parent. **Version** : 32.1.2-jre.**No license information found** -1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.3-jre. * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : com.google.guava. **Name** : listenablefuture. **Version** : 9999.0-empty-to-avoid-conflict-with-guava. @@ -1015,15 +1011,15 @@ This report was generated on **Sat Oct 28 16:50:16 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.4. * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 1. **Group** : com.google.protobuf. **Name** : protoc. **Version** : 3.24.1. @@ -1565,7 +1561,7 @@ This report was generated on **Sat Oct 28 16:50:16 WEST 2023** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Sat Oct 28 16:50:17 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Tue Oct 31 23:41:14 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -1597,11 +1593,10 @@ This report was generated on **Sat Oct 28 16:50:17 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-jre. * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava-parent. **Version** : 32.1.2-jre.**No license information found** 1. **Group** : com.google.guava. **Name** : listenablefuture. **Version** : 9999.0-empty-to-avoid-conflict-with-guava. * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -1609,15 +1604,15 @@ This report was generated on **Sat Oct 28 16:50:17 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.4. * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.57.2. @@ -1699,27 +1694,27 @@ This report was generated on **Sat Oct 28 16:50:17 WEST 2023** using [Gradle-Lic * **Project URL:** [https://jcommander.org](https://jcommander.org) * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.15.2.**No license information found** -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.15.3.**No license information found** +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-xml. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-xml. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformat-xml](https://github.com/FasterXML/jackson-dataformat-xml) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-kotlin. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-kotlin. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-module-kotlin](https://github.com/FasterXML/jackson-module-kotlin) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -1805,12 +1800,11 @@ This report was generated on **Sat Oct 28 16:50:17 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-jre. * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava-parent. **Version** : 32.1.2-jre.**No license information found** -1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.3-jre. * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : com.google.guava. **Name** : listenablefuture. **Version** : 9999.0-empty-to-avoid-conflict-with-guava. @@ -1824,15 +1818,15 @@ This report was generated on **Sat Oct 28 16:50:17 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.4. * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 1. **Group** : com.google.protobuf. **Name** : protoc. **Version** : 3.24.1. @@ -2378,7 +2372,7 @@ This report was generated on **Sat Oct 28 16:50:17 WEST 2023** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Sat Oct 28 16:50:17 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Tue Oct 31 23:41:14 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -2422,12 +2416,11 @@ This report was generated on **Sat Oct 28 16:50:17 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-jre. * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava-parent. **Version** : 32.1.2-jre.**No license information found** -1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.3-jre. * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : com.google.guava. **Name** : listenablefuture. **Version** : 9999.0-empty-to-avoid-conflict-with-guava. @@ -2437,15 +2430,15 @@ This report was generated on **Sat Oct 28 16:50:17 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.4. * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 1. **Group** : com.google.truth. **Name** : truth. **Version** : 1.1.5. @@ -2625,27 +2618,27 @@ This report was generated on **Sat Oct 28 16:50:17 WEST 2023** using [Gradle-Lic * **Project URL:** [https://jcommander.org](https://jcommander.org) * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.15.2.**No license information found** -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.15.3.**No license information found** +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-xml. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-xml. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformat-xml](https://github.com/FasterXML/jackson-dataformat-xml) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-kotlin. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-kotlin. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-module-kotlin](https://github.com/FasterXML/jackson-module-kotlin) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -2727,12 +2720,11 @@ This report was generated on **Sat Oct 28 16:50:17 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-jre. * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava-parent. **Version** : 32.1.2-jre.**No license information found** -1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.3-jre. * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : com.google.guava. **Name** : listenablefuture. **Version** : 9999.0-empty-to-avoid-conflict-with-guava. @@ -2746,15 +2738,15 @@ This report was generated on **Sat Oct 28 16:50:17 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.4. * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 1. **Group** : com.google.protobuf. **Name** : protoc. **Version** : 3.24.1. @@ -3311,7 +3303,7 @@ This report was generated on **Sat Oct 28 16:50:17 WEST 2023** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Sat Oct 28 16:50:18 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Tue Oct 31 23:41:14 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -3355,12 +3347,11 @@ This report was generated on **Sat Oct 28 16:50:18 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-jre. * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava-parent. **Version** : 32.1.2-jre.**No license information found** -1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.3-jre. * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : com.google.guava. **Name** : listenablefuture. **Version** : 9999.0-empty-to-avoid-conflict-with-guava. @@ -3370,15 +3361,15 @@ This report was generated on **Sat Oct 28 16:50:18 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.4. * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 1. **Group** : com.google.truth. **Name** : truth. **Version** : 1.1.5. @@ -3558,27 +3549,27 @@ This report was generated on **Sat Oct 28 16:50:18 WEST 2023** using [Gradle-Lic * **Project URL:** [https://jcommander.org](https://jcommander.org) * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.15.2.**No license information found** -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.15.3.**No license information found** +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-xml. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-xml. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformat-xml](https://github.com/FasterXML/jackson-dataformat-xml) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-kotlin. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-kotlin. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-module-kotlin](https://github.com/FasterXML/jackson-module-kotlin) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -3660,12 +3651,11 @@ This report was generated on **Sat Oct 28 16:50:18 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-jre. * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava-parent. **Version** : 32.1.2-jre.**No license information found** -1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.3-jre. * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : com.google.guava. **Name** : listenablefuture. **Version** : 9999.0-empty-to-avoid-conflict-with-guava. @@ -3679,15 +3669,15 @@ This report was generated on **Sat Oct 28 16:50:18 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.4. * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 1. **Group** : com.google.protobuf. **Name** : protoc. **Version** : 3.24.1. @@ -4244,7 +4234,7 @@ This report was generated on **Sat Oct 28 16:50:18 WEST 2023** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Sat Oct 28 16:50:18 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Tue Oct 31 23:41:15 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). @@ -4288,12 +4278,11 @@ This report was generated on **Sat Oct 28 16:50:18 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-jre. * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava-parent. **Version** : 32.1.2-jre.**No license information found** -1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.3-jre. * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : com.google.guava. **Name** : listenablefuture. **Version** : 9999.0-empty-to-avoid-conflict-with-guava. @@ -4303,15 +4292,15 @@ This report was generated on **Sat Oct 28 16:50:18 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.4. * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 1. **Group** : com.google.truth. **Name** : truth. **Version** : 1.1.5. @@ -4491,27 +4480,27 @@ This report was generated on **Sat Oct 28 16:50:18 WEST 2023** using [Gradle-Lic * **Project URL:** [https://jcommander.org](https://jcommander.org) * **License:** [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.15.2.**No license information found** -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.15.3.**No license information found** +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-xml. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-xml. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformat-xml](https://github.com/FasterXML/jackson-dataformat-xml) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-kotlin. **Version** : 2.15.2. +1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-kotlin. **Version** : 2.15.3. * **Project URL:** [https://github.com/FasterXML/jackson-module-kotlin](https://github.com/FasterXML/jackson-module-kotlin) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -4593,12 +4582,11 @@ This report was generated on **Sat Oct 28 16:50:18 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava. **Version** : 32.1.3-jre. * **Project URL:** [https://github.com/google/guava](https://github.com/google/guava) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.guava. **Name** : guava-parent. **Version** : 32.1.2-jre.**No license information found** -1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.2-jre. +1. **Group** : com.google.guava. **Name** : guava-testlib. **Version** : 32.1.3-jre. * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) 1. **Group** : com.google.guava. **Name** : listenablefuture. **Version** : 9999.0-empty-to-avoid-conflict-with-guava. @@ -4612,15 +4600,15 @@ This report was generated on **Sat Oct 28 16:50:18 WEST 2023** using [Gradle-Lic * **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 3.24.4. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.1. +1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 3.24.4. * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) 1. **Group** : com.google.protobuf. **Name** : protoc. **Version** : 3.24.1. @@ -5225,4 +5213,4 @@ This report was generated on **Sat Oct 28 16:50:18 WEST 2023** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Sat Oct 28 16:50:19 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file +This report was generated on **Tue Oct 31 23:41:15 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file From db38004136b52332ee7924b6bc3e2a45ea9ad0d5 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 31 Oct 2023 23:43:05 +0000 Subject: [PATCH 16/32] Optimise imports --- server/src/main/kotlin/io/spine/server/event/Policy.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/main/kotlin/io/spine/server/event/Policy.kt b/server/src/main/kotlin/io/spine/server/event/Policy.kt index e5e61ee0b17..23c28b29318 100644 --- a/server/src/main/kotlin/io/spine/server/event/Policy.kt +++ b/server/src/main/kotlin/io/spine/server/event/Policy.kt @@ -32,7 +32,6 @@ import io.spine.core.ContractFor import io.spine.logging.WithLogging import io.spine.server.BoundedContext import io.spine.server.type.EventClass -import io.spine.string.Diags /** * A policy converts one event into zero to many other events. From 596a8177780c6be0d01549a04942b6a869cc8bb0 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 Nov 2023 01:22:04 +0000 Subject: [PATCH 17/32] Simplify creating `BlackBox` instances Also: * Avoid checked exception on `close()`. --- .../java/io/spine/server/BoundedContext.java | 5 +- .../spine/server/BoundedContextBuilder.java | 5 +- .../main/java/io/spine/server/Closeable.java | 8 ++ .../java/io/spine/server/DomainContext.java | 13 +- .../io/spine/server/aggregate/ImportBus.java | 3 +- .../main/java/io/spine/server/bus/Bus.java | 5 +- .../java/io/spine/server/bus/BusFilter.java | 2 +- .../java/io/spine/server/bus/FilterChain.java | 4 +- .../java/io/spine/server/event/EventBus.java | 2 +- .../server/integration/ConfigExchange.java | 2 +- .../server/integration/IntegrationBroker.java | 2 +- .../server/integration/ThirdPartyContext.java | 5 +- .../io/spine/server/stand/EventRegistry.java | 3 + .../java/io/spine/server/stand/Stand.java | 2 +- .../io/spine/server/transport/ChannelHub.java | 2 +- .../server/transport/MessageChannel.java | 5 +- .../system/server/DefaultSystemClient.java | 2 +- .../io/spine/system/server/SystemClient.java | 5 +- .../server/ServerEnvironmentConfigTest.java | 2 +- .../AggregateHistoryTruncationTest.java | 6 +- .../aggregate/AggregateRepositoryTest.java | 21 ++-- .../aggregate/ApplyAllowImportTest.java | 6 +- .../server/aggregate/EventImportTest.java | 6 +- .../DispatchingQueueSynchronisationTest.java | 57 +++++---- .../io/spine/server/delivery/CatchUpTest.java | 113 +++++++++--------- .../spine/server/delivery/DeliveryTest.java | 49 ++++---- .../io/spine/server/delivery/NastyClient.java | 59 +++++---- .../server/delivery/given/CounterCatchUp.java | 21 ++-- .../given/ReceptionFailureTestEnv.java | 6 +- .../server/log/AbstractEntityLoggingTest.java | 5 +- .../mirror/given/MirrorMigrationTestEnv.java | 6 +- .../handler/MessageInterfaceResultTest.java | 6 +- .../procman/ProcessManagerRepositoryTest.java | 5 +- .../server/projection/StateRoutingTest.java | 13 +- .../e2e/ProjectionEndToEndTest.java | 53 ++++---- .../stand/EntityQueryProcessorTest.java | 5 +- .../system/server/ConstraintViolatedTest.java | 77 ++++++------ .../testing/server/blackbox/BlackBox.java | 99 ++++++++++++++- .../server/blackbox/ClientFactory.java | 2 +- 39 files changed, 364 insertions(+), 328 deletions(-) diff --git a/server/src/main/java/io/spine/server/BoundedContext.java b/server/src/main/java/io/spine/server/BoundedContext.java index ce7021cb3e7..5b0b53f9d1b 100644 --- a/server/src/main/java/io/spine/server/BoundedContext.java +++ b/server/src/main/java/io/spine/server/BoundedContext.java @@ -474,12 +474,9 @@ public boolean isMultitenant() { *

  • Closes all registered {@linkplain Repository repositories}. *
  • Removes a {@link Probe}, if it was {@linkplain #install(Probe) installed}. * - * - * @throws Exception - * caused by closing one of the components */ @Override - public void close() throws Exception { + public void close() { var isOpen = isOpen(); if (isOpen && onBeforeClose != null) { onBeforeClose.accept(this); diff --git a/server/src/main/java/io/spine/server/BoundedContextBuilder.java b/server/src/main/java/io/spine/server/BoundedContextBuilder.java index e27a62a726f..8206b5fa7cc 100644 --- a/server/src/main/java/io/spine/server/BoundedContextBuilder.java +++ b/server/src/main/java/io/spine/server/BoundedContextBuilder.java @@ -139,9 +139,10 @@ private BoundedContextBuilder(ContextSpec spec, SystemSettings systemSettings) { @Internal @VisibleForTesting public static BoundedContextBuilder assumingTests(boolean multitenant) { + var name = assumingTestsValue(); var spec = multitenant - ? multitenant(assumingTestsValue()) - : singleTenant(assumingTestsValue()); + ? multitenant(name) + : singleTenant(name); return new BoundedContextBuilder(spec); } diff --git a/server/src/main/java/io/spine/server/Closeable.java b/server/src/main/java/io/spine/server/Closeable.java index f47c8e868ac..0dfb73f1017 100644 --- a/server/src/main/java/io/spine/server/Closeable.java +++ b/server/src/main/java/io/spine/server/Closeable.java @@ -49,6 +49,14 @@ public interface Closeable extends AutoCloseable { */ boolean isOpen(); + /** + * {@inheritDoc} + * + *

    Overrides to remove the checked exception from the signature. + */ + @Override + void close(); + /** * Ensures that the object is {@linkplain #isOpen() open}. * diff --git a/server/src/main/java/io/spine/server/DomainContext.java b/server/src/main/java/io/spine/server/DomainContext.java index 5531813f4cb..e36de181096 100644 --- a/server/src/main/java/io/spine/server/DomainContext.java +++ b/server/src/main/java/io/spine/server/DomainContext.java @@ -40,11 +40,11 @@ *

    All the user interactions with the system (such as * {@linkplain BoundedContext#register(Repository) repository registration}, * {@linkplain BoundedContext#commandBus() command posting}, - * {@linkplain BoundedContext#findRepository(Class) query processing}, etc.) happen through - * an instance of this class. + * {@linkplain BoundedContext.InternalAccess#findRepository(Class) query processing}, etc.) happen + * through an instance of this class. * *

    Each {@code DomainContext} has an associated - * {@link io.spine.system.server.SystemContext SystemContext}, which manages the meta information + * {@link io.spine.system.server.SystemContext SystemContext}, which manages the meta-information * about entities of this Bounded Context. * * @see io.spine.system.server.SystemContext SystemContext @@ -53,8 +53,7 @@ final class DomainContext extends BoundedContext { private final SystemClient system; - private DomainContext(BoundedContextBuilder builder, - SystemClient system) { + private DomainContext(BoundedContextBuilder builder, SystemClient system) { super(builder); this.system = checkNotNull(system); } @@ -77,11 +76,9 @@ public SystemClient systemClient() { * {@inheritDoc} * *

    Closes the system context as well. - * - * @throws Exception if one of the context components throws an error when closing */ @Override - public void close() throws Exception { + public void close() { super.close(); system.closeSystemContext(); } diff --git a/server/src/main/java/io/spine/server/aggregate/ImportBus.java b/server/src/main/java/io/spine/server/aggregate/ImportBus.java index 06b3703a869..07fb490080b 100644 --- a/server/src/main/java/io/spine/server/aggregate/ImportBus.java +++ b/server/src/main/java/io/spine/server/aggregate/ImportBus.java @@ -82,7 +82,7 @@ public final class ImportBus extends UnicastBus> { - private final ImportValidator validator = new ImportValidator(); + private static final ImportValidator validator = new ImportValidator(); private final DeadImportEventHandler deadImportEventHandler = new DeadImportEventHandler(); private final TenantIndex tenantIndex; @@ -164,7 +164,6 @@ public MessageUnhandled handle(EventEnvelope event) { private static final class Registry extends DispatcherRegistry> { - @SuppressWarnings("RedundantMethodOverride") // Overrides to open access to the method. @Override protected Optional> dispatcherOf(EventEnvelope event) { return super.dispatcherOf(event); diff --git a/server/src/main/java/io/spine/server/bus/Bus.java b/server/src/main/java/io/spine/server/bus/Bus.java index 39179cd1171..389d56437ae 100644 --- a/server/src/main/java/io/spine/server/bus/Bus.java +++ b/server/src/main/java/io/spine/server/bus/Bus.java @@ -235,12 +235,9 @@ public final boolean isOpen() { /** * Closes the {@linkplain BusFilter filters} of this bus and unregisters all the dispatchers. - * - * @throws Exception if either filters or the {@linkplain DispatcherRegistry} throws - * an exception */ @Override - public void close() throws Exception { + public void close() { filterChain().close(); registry.unregisterAll(); } diff --git a/server/src/main/java/io/spine/server/bus/BusFilter.java b/server/src/main/java/io/spine/server/bus/BusFilter.java index 00b45338f75..21a84b6a64f 100644 --- a/server/src/main/java/io/spine/server/bus/BusFilter.java +++ b/server/src/main/java/io/spine/server/bus/BusFilter.java @@ -123,7 +123,7 @@ default Optional reject(E envelope, Error cause) { *

    By default, performs no action. */ @Override - default void close() throws Exception { + default void close() { // NoOp by default. } } diff --git a/server/src/main/java/io/spine/server/bus/FilterChain.java b/server/src/main/java/io/spine/server/bus/FilterChain.java index b6f199129bf..97797a892ce 100644 --- a/server/src/main/java/io/spine/server/bus/FilterChain.java +++ b/server/src/main/java/io/spine/server/bus/FilterChain.java @@ -82,12 +82,10 @@ boolean contains(BusFilter filter) { * * @throws IllegalStateException * on a repetitive call - * @throws Exception - * if a filter throws an {@link Exception} * @see #isOpen() */ @Override - public void close() throws Exception { + public void close() { checkOpen(); closed = true; for (BusFilter filter : chain.reverse()) { diff --git a/server/src/main/java/io/spine/server/event/EventBus.java b/server/src/main/java/io/spine/server/event/EventBus.java index 39e0a868890..233d482a7b6 100644 --- a/server/src/main/java/io/spine/server/event/EventBus.java +++ b/server/src/main/java/io/spine/server/event/EventBus.java @@ -258,7 +258,7 @@ protected void store(Iterable events) { } @Override - public void close() throws Exception { + public void close() { super.close(); eventStore().close(); } diff --git a/server/src/main/java/io/spine/server/integration/ConfigExchange.java b/server/src/main/java/io/spine/server/integration/ConfigExchange.java index 10c7e8dad36..872d4a60237 100644 --- a/server/src/main/java/io/spine/server/integration/ConfigExchange.java +++ b/server/src/main/java/io/spine/server/integration/ConfigExchange.java @@ -120,7 +120,7 @@ private static ExternalEventType typeOfTransmittedEvents(ChannelId channel) { * Bounded Context. */ @Override - public void close() throws Exception { + public void close() { observers.forEach(ObserveWantedEvents::close); notifyTypesChanged(); } diff --git a/server/src/main/java/io/spine/server/integration/IntegrationBroker.java b/server/src/main/java/io/spine/server/integration/IntegrationBroker.java index 634cacd49dc..b4a1d1ac3d2 100644 --- a/server/src/main/java/io/spine/server/integration/IntegrationBroker.java +++ b/server/src/main/java/io/spine/server/integration/IntegrationBroker.java @@ -196,7 +196,7 @@ public void unregister(EventDispatcher dispatcher) { * Removes all subscriptions and closes all the underlying transport channels. */ @Override - public void close() throws Exception { + public void close() { if (config != null) { config.close(); } diff --git a/server/src/main/java/io/spine/server/integration/ThirdPartyContext.java b/server/src/main/java/io/spine/server/integration/ThirdPartyContext.java index cbc50a5534b..5db5c167daf 100644 --- a/server/src/main/java/io/spine/server/integration/ThirdPartyContext.java +++ b/server/src/main/java/io/spine/server/integration/ThirdPartyContext.java @@ -174,12 +174,9 @@ private void checkTenant(ActorContext actorContext, EventMessage event) { * Closes this Context and clean up underlying resources. * *

    Attempts of emitting an event from a closed Context result in an exception. - * - * @throws Exception - * if the underlying {@link BoundedContext} fails to close */ @Override - public void close() throws Exception { + public void close() { context.close(); } diff --git a/server/src/main/java/io/spine/server/stand/EventRegistry.java b/server/src/main/java/io/spine/server/stand/EventRegistry.java index d24a5c8f797..d391197326a 100644 --- a/server/src/main/java/io/spine/server/stand/EventRegistry.java +++ b/server/src/main/java/io/spine/server/stand/EventRegistry.java @@ -61,4 +61,7 @@ interface EventRegistry extends AutoCloseable { * Retrieves all stored event types as {@link EventClass} instances. */ ImmutableSet eventClasses(); + + @Override + void close(); } diff --git a/server/src/main/java/io/spine/server/stand/Stand.java b/server/src/main/java/io/spine/server/stand/Stand.java index 68573b4751c..293e2a89bdb 100644 --- a/server/src/main/java/io/spine/server/stand/Stand.java +++ b/server/src/main/java/io/spine/server/stand/Stand.java @@ -380,7 +380,7 @@ public boolean isOpen() { * Closes the {@code Stand} performing necessary cleanups. */ @Override - public void close() throws Exception { + public void close() { typeRegistry.close(); eventRegistry.close(); } diff --git a/server/src/main/java/io/spine/server/transport/ChannelHub.java b/server/src/main/java/io/spine/server/transport/ChannelHub.java index c3fea4de5df..da12b2dbace 100644 --- a/server/src/main/java/io/spine/server/transport/ChannelHub.java +++ b/server/src/main/java/io/spine/server/transport/ChannelHub.java @@ -119,7 +119,7 @@ public boolean isOpen() { } @Override - public void close() throws Exception { + public void close() { for (var channel : channels.values()) { channel.close(); } diff --git a/server/src/main/java/io/spine/server/transport/MessageChannel.java b/server/src/main/java/io/spine/server/transport/MessageChannel.java index 3b18002e9e4..ef80af072f5 100644 --- a/server/src/main/java/io/spine/server/transport/MessageChannel.java +++ b/server/src/main/java/io/spine/server/transport/MessageChannel.java @@ -40,7 +40,7 @@ public interface MessageChannel extends AutoCloseable { ChannelId id(); /** - * Allows to understand whether this channel is stale and can be closed. + * Allows understanding whether this channel is stale and can be closed. * * @return {@code true} if the channel is stale, {@code false} otherwise */ @@ -68,4 +68,7 @@ static ChannelId channelIdFor(TypeUrl messageType) { .build(); return channelId; } + + @Override + void close(); } diff --git a/server/src/main/java/io/spine/system/server/DefaultSystemClient.java b/server/src/main/java/io/spine/system/server/DefaultSystemClient.java index 730838530ad..a30adedfd92 100644 --- a/server/src/main/java/io/spine/system/server/DefaultSystemClient.java +++ b/server/src/main/java/io/spine/system/server/DefaultSystemClient.java @@ -63,7 +63,7 @@ public SystemReadSide readSide() { } @Override - public void closeSystemContext() throws Exception { + public void closeSystemContext() { context.close(); } diff --git a/server/src/main/java/io/spine/system/server/SystemClient.java b/server/src/main/java/io/spine/system/server/SystemClient.java index 068b5cdd8a9..abadd59588e 100644 --- a/server/src/main/java/io/spine/system/server/SystemClient.java +++ b/server/src/main/java/io/spine/system/server/SystemClient.java @@ -46,9 +46,6 @@ public interface SystemClient { /** * Closes the underlying system context. - * - * @throws Exception - * if the context thrown an exception when closing */ - void closeSystemContext() throws Exception; + void closeSystemContext(); } diff --git a/server/src/test/java/io/spine/server/ServerEnvironmentConfigTest.java b/server/src/test/java/io/spine/server/ServerEnvironmentConfigTest.java index 7a70b9dcd93..0231e53fdc7 100644 --- a/server/src/test/java/io/spine/server/ServerEnvironmentConfigTest.java +++ b/server/src/test/java/io/spine/server/ServerEnvironmentConfigTest.java @@ -453,7 +453,7 @@ public boolean isOpen() { } @Override - public void close() throws Exception { + public void close() { delegate.close(); } } diff --git a/server/src/test/java/io/spine/server/aggregate/AggregateHistoryTruncationTest.java b/server/src/test/java/io/spine/server/aggregate/AggregateHistoryTruncationTest.java index ba0187ceca5..30308f4d6bd 100644 --- a/server/src/test/java/io/spine/server/aggregate/AggregateHistoryTruncationTest.java +++ b/server/src/test/java/io/spine/server/aggregate/AggregateHistoryTruncationTest.java @@ -33,7 +33,6 @@ import com.google.protobuf.util.Durations; import io.spine.core.Event; import io.spine.core.Version; -import io.spine.server.BoundedContextBuilder; import io.spine.server.ContextSpec; import io.spine.server.ServerEnvironment; import io.spine.server.aggregate.AggregateStorageTest.TestAggregate; @@ -96,10 +95,7 @@ class VerifyIntegrity { @DisplayName("restore the `Aggregate` state properly") void restoreAggregateState() { var repo = new FibonacciRepository(); - var context = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(repo) - ); + var context = BlackBox.singleTenantWith(repo); try (context) { // Set the starting numbers. var setStartingNumbers = SetStartingNumbers.newBuilder() diff --git a/server/src/test/java/io/spine/server/aggregate/AggregateRepositoryTest.java b/server/src/test/java/io/spine/server/aggregate/AggregateRepositoryTest.java index 552213be4fb..91a821ae363 100644 --- a/server/src/test/java/io/spine/server/aggregate/AggregateRepositoryTest.java +++ b/server/src/test/java/io/spine/server/aggregate/AggregateRepositoryTest.java @@ -33,7 +33,6 @@ import io.spine.core.Ack; import io.spine.core.Event; import io.spine.core.Events; -import io.spine.server.BoundedContextBuilder; import io.spine.server.aggregate.given.repo.AnemicAggregateRepository; import io.spine.server.aggregate.given.repo.EventDiscardingAggregateRepository; import io.spine.server.aggregate.given.repo.FailingAggregateRepository; @@ -504,10 +503,7 @@ class PostEventsToBus { @BeforeEach void createAnotherRepository() { resetRepository(); - context = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(repository()) - ); + context = BlackBox.singleTenantWith(repository()); } @Test @@ -573,15 +569,12 @@ void throughEventFilter() { .setProjectId(parent) .addChildProjectId(id) .build(); - var context = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(new EventDiscardingAggregateRepository()) - ); - context.receivesCommands(create, start) - .receivesEvent(archived); - context.assertEvents() - .isEmpty(); - context.close(); + try (var context = BlackBox.singleTenantWith(new EventDiscardingAggregateRepository())) { + context.receivesCommands(create, start) + .receivesEvent(archived); + context.assertEvents() + .isEmpty(); + } } private void assertEventVersions(int... expectedVersions) { diff --git a/server/src/test/java/io/spine/server/aggregate/ApplyAllowImportTest.java b/server/src/test/java/io/spine/server/aggregate/ApplyAllowImportTest.java index f6951043bc4..cbbe796576e 100644 --- a/server/src/test/java/io/spine/server/aggregate/ApplyAllowImportTest.java +++ b/server/src/test/java/io/spine/server/aggregate/ApplyAllowImportTest.java @@ -26,7 +26,6 @@ package io.spine.server.aggregate; -import io.spine.server.BoundedContextBuilder; import io.spine.server.aggregate.given.importado.DotSpace; import io.spine.server.aggregate.given.importado.ObjectId; import io.spine.server.aggregate.given.importado.event.Moved; @@ -54,10 +53,7 @@ class ApplyAllowImportTest { @BeforeEach void setUp() { - context = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(new DotSpace()) - ); + context = BlackBox.singleTenantWith(new DotSpace()); } @AfterEach diff --git a/server/src/test/java/io/spine/server/aggregate/EventImportTest.java b/server/src/test/java/io/spine/server/aggregate/EventImportTest.java index e42d5ee3345..54efc0cbf25 100644 --- a/server/src/test/java/io/spine/server/aggregate/EventImportTest.java +++ b/server/src/test/java/io/spine/server/aggregate/EventImportTest.java @@ -27,7 +27,6 @@ package io.spine.server.aggregate; import io.spine.base.EventMessage; -import io.spine.server.BoundedContextBuilder; import io.spine.server.aggregate.given.klasse.EngineAggregate; import io.spine.server.aggregate.given.klasse.EngineId; import io.spine.server.aggregate.given.klasse.EngineRepository; @@ -59,10 +58,7 @@ class EventImportTest { void createRepository(boolean routeByFirstMessageField) { repository = new EngineRepository(routeByFirstMessageField); - context = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(repository) - ); + context = BlackBox.singleTenantWith(repository); } @AfterEach diff --git a/server/src/test/java/io/spine/server/bus/DispatchingQueueSynchronisationTest.java b/server/src/test/java/io/spine/server/bus/DispatchingQueueSynchronisationTest.java index 856afd4baec..3b21ec60ce2 100644 --- a/server/src/test/java/io/spine/server/bus/DispatchingQueueSynchronisationTest.java +++ b/server/src/test/java/io/spine/server/bus/DispatchingQueueSynchronisationTest.java @@ -26,7 +26,6 @@ package io.spine.server.bus; -import io.spine.server.BoundedContextBuilder; import io.spine.server.bus.given.stock.JowDonsIndex; import io.spine.server.bus.given.stock.ShareAggregate; import io.spine.test.bus.Buy; @@ -56,33 +55,33 @@ class DispatchingQueueSynchronisationTest { @DisplayName("`Bus` should not lock with its system counterpart") void deadlock() throws InterruptedException { var executor = (ThreadPoolExecutor) newFixedThreadPool(10); - var context = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(ShareAggregate.class) - .add(new JowDonsIndex.Repository()) - ); - var taskCount = 10; - var shares = - Stream.generate(() -> ShareId.newBuilder() - .setValue(newUuid()) - .build()) - .limit(taskCount) - .collect(toImmutableList()); - shares.forEach(share -> executor.execute(() -> { - var buy = Buy.newBuilder() - .setShare(share) - .setAmount(42) - .build(); - context.receivesCommand(buy); - sleepUninterruptibly(Duration.ofSeconds(1)); - var sell = Sell.newBuilder() - .setShare(share) - .setAmount(12) - .build(); - context.receivesCommand(sell); - })); - executor.awaitTermination(5, SECONDS); - assertEquals(shares.size(), executor.getCompletedTaskCount(), - "Not all tasks have been executed. Most likely, a dead lock is reached."); + try (var context = BlackBox.singleTenantWith( + ShareAggregate.class, + new JowDonsIndex.Repository()) + ) { + var taskCount = 10; + var shares = + Stream.generate(() -> ShareId.newBuilder() + .setValue(newUuid()) + .build()) + .limit(taskCount) + .collect(toImmutableList()); + shares.forEach(share -> executor.execute(() -> { + var buy = Buy.newBuilder() + .setShare(share) + .setAmount(42) + .build(); + context.receivesCommand(buy); + sleepUninterruptibly(Duration.ofSeconds(1)); + var sell = Sell.newBuilder() + .setShare(share) + .setAmount(12) + .build(); + context.receivesCommand(sell); + })); + executor.awaitTermination(5, SECONDS); + assertEquals(shares.size(), executor.getCompletedTaskCount(), + "Not all tasks have been executed. Most likely, a dead lock is reached."); + } } } diff --git a/server/src/test/java/io/spine/server/delivery/CatchUpTest.java b/server/src/test/java/io/spine/server/delivery/CatchUpTest.java index e6616e4a563..21609ae652b 100644 --- a/server/src/test/java/io/spine/server/delivery/CatchUpTest.java +++ b/server/src/test/java/io/spine/server/delivery/CatchUpTest.java @@ -31,8 +31,6 @@ import com.google.protobuf.Timestamp; import com.google.protobuf.util.Durations; import io.spine.base.Time; -import io.spine.server.BoundedContextBuilder; -import io.spine.server.DefaultRepository; import io.spine.server.ServerEnvironment; import io.spine.server.delivery.given.ConsecutiveNumberProcess; import io.spine.server.delivery.given.ConsecutiveProjection; @@ -168,16 +166,18 @@ class AllowCatchUp { @Test @DisplayName("if the event store is empty") void onEmptyEventStore() { - var counterCatchUp = catchUpForCounter(); - counterCatchUp.catchUp(WhatToCatchUp.catchUpAll(aMinuteAgo())); + try (var counterCatchUp = catchUpForCounter()) { + counterCatchUp.catchUp(WhatToCatchUp.catchUpAll(aMinuteAgo())); + } } @Test @DisplayName("of the same instance, if the previous catch-up is already completed") void ifPreviousCatchUpCompleted() { CounterCatchUp.addOngoingCatchUpRecord(catchUpAll(aMinuteAgo()), COMPLETED); - var counterCatchUp = catchUpForCounter(); - counterCatchUp.catchUp(WhatToCatchUp.catchUpAll(aMinuteAgo())); + try (var counterCatchUp = catchUpForCounter()) { + counterCatchUp.catchUp(WhatToCatchUp.catchUpAll(aMinuteAgo())); + } } } @@ -191,9 +191,10 @@ class NotAllowSimultaneousCatchUp { @DisplayName("if catching up of all repository instances has started previously") void ifCatchUpAllStartedPreviously() { CounterCatchUp.addOngoingCatchUpRecord(catchUpAll(aMinuteAgo())); - var counterCatchUp = catchUpForCounter(); - for (var target : counterCatchUp.targets()) { - assertCatchUpAlreadyStarted(counterCatchUp, target); + try (var counterCatchUp = catchUpForCounter()) { + for (var target : counterCatchUp.targets()) { + assertCatchUpAlreadyStarted(counterCatchUp, target); + } } } @@ -201,17 +202,16 @@ void ifCatchUpAllStartedPreviously() { @DisplayName("of the same repository instances") void ofSameInstances() { CounterCatchUp.addOngoingCatchUpRecord(catchUpOf(TARGET_ID, aMinuteAgo())); - var counterCatchUp = new CounterCatchUp(TARGET_ID); - - assertCatchUpAlreadyStarted(counterCatchUp, TARGET_ID); + try (var counterCatchUp = new CounterCatchUp(TARGET_ID)) { + assertCatchUpAlreadyStarted(counterCatchUp, TARGET_ID); + } } @Test @DisplayName("of all instances if at least one catch-up of an instance is in progress") void ofAllIfOneAlreadyStarted() { CounterCatchUp.addOngoingCatchUpRecord(catchUpOf(TARGET_ID, aMinuteAgo())); - var counterCatchUp = new CounterCatchUp(TARGET_ID); - try { + try(var counterCatchUp = new CounterCatchUp(TARGET_ID)) { counterCatchUp.catchUp(catchUpAll(aMinuteAgo())); fail("It must not be possible to start catching up all the instances," + " while some instance is already catching up."); @@ -237,59 +237,60 @@ private static CounterCatchUp catchUpForCounter() { private static void testCatchUpEmpty() { changeShardCountTo(17); + try (var counterCatchUp = catchUpForCounter()) { + var aWhileAgo = subtract(currentTime(), Durations.fromHours(1)); + var someTarget = "some-target"; + counterCatchUp.catchUp(WhatToCatchUp.catchUpOf(someTarget, aWhileAgo)); - var counterCatchUp = catchUpForCounter(); - var aWhileAgo = subtract(currentTime(), Durations.fromHours(1)); - var someTarget = "some-target"; - counterCatchUp.catchUp(WhatToCatchUp.catchUpOf(someTarget, aWhileAgo)); - - var actual = counterCatchUp.find(someTarget); - assertThat(actual).isEmpty(); + var actual = counterCatchUp.find(someTarget); + assertThat(actual).isEmpty(); + } } private static void testCatchUpByIds() throws InterruptedException { changeShardCountTo(2); + try(var counterCatchUp = catchUpForCounter()) { - var counterCatchUp = catchUpForCounter(); - var events = counterCatchUp.generateEvents(200); + var events = counterCatchUp.generateEvents(200); - var aWhileAgo = subtract(currentTime(), Durations.fromHours(1)); - counterCatchUp.addHistory(aWhileAgo, events); + var aWhileAgo = subtract(currentTime(), Durations.fromHours(1)); + counterCatchUp.addHistory(aWhileAgo, events); - // Round 1. Fight! + // Round 1. Fight! - var initialWeight = 1; - CounterView.changeWeightTo(initialWeight); + var initialWeight = 1; + CounterView.changeWeightTo(initialWeight); - counterCatchUp.dispatch(events, 20); + counterCatchUp.dispatch(events, 20); - var targets = counterCatchUp.targets(); - var totalTargets = targets.length; - var initialTotals = counterCatchUp.counterValues(); - var sumInRound = events.size() / totalTargets * initialWeight; - var sums = IntStream.iterate(sumInRound, i -> i) - .limit(totalTargets); - assertThat(initialTotals).isEqualTo(sums.boxed() - .collect(toList())); + var targets = counterCatchUp.targets(); + var totalTargets = targets.length; + var initialTotals = counterCatchUp.counterValues(); + var sumInRound = events.size() / totalTargets * initialWeight; + var sums = IntStream.iterate(sumInRound, i -> i) + .limit(totalTargets); + assertThat(initialTotals).isEqualTo(sums.boxed() + .collect(toList())); - // Round 2. Catch up the first and the second and fight! + // Round 2. Catch up the first and the second and fight! - var newWeight = 100; - CounterView.changeWeightTo(newWeight); - counterCatchUp - .dispatchWithCatchUp(events, 20, - catchUpOf(targets[0], aWhileAgo), - catchUpOf(targets[1], aMinuteAgo())); + var newWeight = 100; + CounterView.changeWeightTo(newWeight); + counterCatchUp + .dispatchWithCatchUp(events, 20, + catchUpOf(targets[0], aWhileAgo), + catchUpOf(targets[1], aMinuteAgo())); - var totalsAfterCatchUp = counterCatchUp.counterValues(); + var totalsAfterCatchUp = counterCatchUp.counterValues(); - var firstSumExpected = sumInRound * newWeight / initialWeight * 3; - var secondSumExpected = sumInRound * newWeight / initialWeight * 2; - var untouchedSum = sumInRound + sumInRound * newWeight / initialWeight; - List expectedTotals = - ImmutableList.of(firstSumExpected, secondSumExpected, untouchedSum, untouchedSum); + var firstSumExpected = sumInRound * newWeight / initialWeight * 3; + var secondSumExpected = sumInRound * newWeight / initialWeight * 2; + var untouchedSum = sumInRound + sumInRound * newWeight / initialWeight; + List expectedTotals = + ImmutableList.of(firstSumExpected, secondSumExpected, untouchedSum, untouchedSum); - assertThat(totalsAfterCatchUp).isEqualTo(expectedTotals); + assertThat(totalsAfterCatchUp).isEqualTo(expectedTotals); + } } private static void testCatchUpAll() throws InterruptedException { @@ -301,12 +302,9 @@ private static void testCatchUpAll() throws InterruptedException { changeShardCountTo(3); var projectionRepo = new ConsecutiveProjection.Repo(); - var pmRepo = - DefaultRepository.of(ConsecutiveNumberProcess.class); - var ctx = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(projectionRepo) - .add(pmRepo) + var ctx = BlackBox.singleTenantWith( + projectionRepo, + ConsecutiveNumberProcess.class ); var jobs = asPostCommandJobs(ctx, commands); post(jobs, 1); @@ -347,6 +345,7 @@ private static void testCatchUpAll() throws InterruptedException { .state(); assertThat(state.getLastValue()).isEqualTo(negativeExpected); } + ctx.close(); } private static Timestamp aMinuteAgo() { diff --git a/server/src/test/java/io/spine/server/delivery/DeliveryTest.java b/server/src/test/java/io/spine/server/delivery/DeliveryTest.java index f8472f50955..5f0f62c4996 100644 --- a/server/src/test/java/io/spine/server/delivery/DeliveryTest.java +++ b/server/src/test/java/io/spine/server/delivery/DeliveryTest.java @@ -31,7 +31,6 @@ import io.spine.base.Identifier; import io.spine.environment.Tests; import io.spine.protobuf.Messages; -import io.spine.server.BoundedContextBuilder; import io.spine.server.ServerEnvironment; import io.spine.server.delivery.given.DeliveryTestEnv.RawMessageMemoizer; import io.spine.server.delivery.given.DeliveryTestEnv.ShardIndexMemoizer; @@ -347,11 +346,10 @@ public void deliverMessagesInOrderOfEmission() throws InterruptedException { changeShardCountTo(20); TaskView.enableStrictMode(); - var context = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(TaskAggregate.class) - .add(new TaskAssignment.Repository()) - .add(new TaskView.Repository()) + var context = BlackBox.singleTenantWith( + TaskAggregate.class, + new TaskAssignment.Repository(), + new TaskView.Repository() ); var commands = generateCommands(200); var service = newFixedThreadPool(20); @@ -386,26 +384,25 @@ public void directDelivery() { var directDelivery = Delivery.direct(); ServerEnvironment.when(Tests.class) .use(directDelivery); - - var context = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(TaskAggregate.class) - .add(new TaskAssignment.Repository()) - .add(new TaskView.Repository()) - ); - var commands = generateCommands(200); - for (var command : commands) { - context.receivesCommand(command); - var taskId = command.getId(); - var subject = context.assertEntity(taskId, TaskView.class); - subject.exists(); - - var actualView = (TaskView) subject.actual(); - var state = actualView.state(); - var actualAssignee = state.getAssignee(); - - assertThat(state.getId()).isEqualTo(taskId); - assertThat(Messages.isDefault(actualAssignee)).isFalse(); + try (var context = BlackBox.singleTenantWith( + TaskAggregate.class, + new TaskAssignment.Repository(), + new TaskView.Repository()) + ) { + var commands = generateCommands(200); + for (var command : commands) { + context.receivesCommand(command); + var taskId = command.getId(); + var subject = context.assertEntity(taskId, TaskView.class); + subject.exists(); + + var actualView = (TaskView) subject.actual(); + var state = actualView.state(); + var actualAssignee = state.getAssignee(); + + assertThat(state.getId()).isEqualTo(taskId); + assertThat(Messages.isDefault(actualAssignee)).isFalse(); + } } } diff --git a/server/src/test/java/io/spine/server/delivery/NastyClient.java b/server/src/test/java/io/spine/server/delivery/NastyClient.java index bc3070b4c8e..2591103e63e 100644 --- a/server/src/test/java/io/spine/server/delivery/NastyClient.java +++ b/server/src/test/java/io/spine/server/delivery/NastyClient.java @@ -29,7 +29,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterators; -import io.spine.server.BoundedContextBuilder; import io.spine.server.ServerEnvironment; import io.spine.server.delivery.given.CalcAggregate; import io.spine.server.delivery.given.CalculatorSignal; @@ -102,46 +101,44 @@ class NastyClient { * the identifiers of target entities */ void runWith(Set targets) { - var context = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(repository) - ); - var memoizer = subscribeToDelivered(); + try (var context = BlackBox.singleTenantWith(repository)) { + var memoizer = subscribeToDelivered(); - var streamSize = targets.size() * 30; + var streamSize = targets.size() * 30; - var targetsIterator = Iterators.cycle(targets); - var commands = commands(streamSize, targetsIterator); - var importEvents = eventsToImport(streamSize, targetsIterator); - var reactEvents = eventsToReact(streamSize, targetsIterator); + var targetsIterator = Iterators.cycle(targets); + var commands = commands(streamSize, targetsIterator); + var importEvents = eventsToImport(streamSize, targetsIterator); + var reactEvents = eventsToReact(streamSize, targetsIterator); - postAsync(context, commands, importEvents, reactEvents); + postAsync(context, commands, importEvents, reactEvents); - Stream signals = - concat(commands.stream(), importEvents.stream(), reactEvents.stream()); + Stream signals = + concat(commands.stream(), importEvents.stream(), reactEvents.stream()); - signalsPerTarget = signals.collect(groupingBy(CalculatorSignal::getCalculatorId)); + signalsPerTarget = signals.collect(groupingBy(CalculatorSignal::getCalculatorId)); - for (var calcId : signalsPerTarget.keySet()) { + for (var calcId : signalsPerTarget.keySet()) { - var receivedMessages = memoizer.messagesBy(calcId); - Set targetSignals = - ImmutableSet.copyOf(signalsPerTarget.get(calcId)); - assertEquals(targetSignals, receivedMessages); + var receivedMessages = memoizer.messagesBy(calcId); + Set targetSignals = + ImmutableSet.copyOf(signalsPerTarget.get(calcId)); + assertEquals(targetSignals, receivedMessages); - var sumForTarget = - targetSignals.stream() - .map(CalculatorSignal::getValue) - .reduce(0, Integer::sum); - var expectedState = Calc.newBuilder() - .setId(calcId) - .setSum(sumForTarget) - .build(); - context.assertState(calcId, Calc.class) - .isEqualTo(expectedState); + var sumForTarget = + targetSignals.stream() + .map(CalculatorSignal::getValue) + .reduce(0, Integer::sum); + var expectedState = Calc.newBuilder() + .setId(calcId) + .setSum(sumForTarget) + .build(); + context.assertState(calcId, Calc.class) + .isEqualTo(expectedState); + } + ensureInboxesEmpty(); } - ensureInboxesEmpty(); } /** diff --git a/server/src/test/java/io/spine/server/delivery/given/CounterCatchUp.java b/server/src/test/java/io/spine/server/delivery/given/CounterCatchUp.java index 3349de9a177..7e2ed2c1831 100644 --- a/server/src/test/java/io/spine/server/delivery/given/CounterCatchUp.java +++ b/server/src/test/java/io/spine/server/delivery/given/CounterCatchUp.java @@ -1,5 +1,5 @@ /* - * Copyright 2022, TeamDev. All rights reserved. + * Copyright 2023, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ import com.google.common.collect.Iterators; import com.google.protobuf.Timestamp; import io.spine.environment.Tests; -import io.spine.server.BoundedContextBuilder; +import io.spine.server.Closeable; import io.spine.server.ServerEnvironment; import io.spine.server.delivery.CatchUpStatus; import io.spine.server.delivery.CatchUpStorage; @@ -58,7 +58,7 @@ * A convenience wrapper over the {@link CounterView} repository and the {@link BlackBox} * Bounded Context to be used in the catch-up tests. */ -public class CounterCatchUp { +public class CounterCatchUp implements Closeable { private final CounterView.Repository repo; private final BlackBox ctx; @@ -67,10 +67,7 @@ public class CounterCatchUp { public CounterCatchUp(String... ids) { this.ids = ids.clone(); this.repo = new CounterView.Repository(); - this.ctx = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(repo) - ); + this.ctx = BlackBox.singleTenantWith(repo); } public void addHistory(Timestamp when, List events) { @@ -179,4 +176,14 @@ var record = TestCatchUpJobs.catchUpJob( ServerEnvironment.when(Tests.class) .use(delivery); } + + @Override + public void close() { + ctx.close(); + } + + @Override + public boolean isOpen() { + return ctx.isOpen(); + } } diff --git a/server/src/test/java/io/spine/server/delivery/given/ReceptionFailureTestEnv.java b/server/src/test/java/io/spine/server/delivery/given/ReceptionFailureTestEnv.java index 6bfc643a6ee..a80923e80fe 100644 --- a/server/src/test/java/io/spine/server/delivery/given/ReceptionFailureTestEnv.java +++ b/server/src/test/java/io/spine/server/delivery/given/ReceptionFailureTestEnv.java @@ -28,7 +28,6 @@ import com.google.common.collect.ImmutableList; import io.spine.environment.Tests; -import io.spine.server.BoundedContextBuilder; import io.spine.server.DefaultRepository; import io.spine.server.ServerEnvironment; import io.spine.server.delivery.Delivery; @@ -60,10 +59,7 @@ private ReceptionFailureTestEnv() { public static BlackBox blackBox() { var repository = DefaultRepository.of(ReceptionistAggregate.class); - var context = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(repository) - ); + var context = BlackBox.singleTenantWith(repository); return context; } diff --git a/server/src/test/java/io/spine/server/log/AbstractEntityLoggingTest.java b/server/src/test/java/io/spine/server/log/AbstractEntityLoggingTest.java index ab61b9a6182..aaedc006721 100644 --- a/server/src/test/java/io/spine/server/log/AbstractEntityLoggingTest.java +++ b/server/src/test/java/io/spine/server/log/AbstractEntityLoggingTest.java @@ -108,10 +108,7 @@ void withCause() { } private static BlackBox context() { - return BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(CardAggregate.class) - ); + return BlackBox.singleTenantWith(CardAggregate.class); } private static BorrowBooks borrowBooks(UserId reader) { diff --git a/server/src/test/java/io/spine/server/migration/mirror/given/MirrorMigrationTestEnv.java b/server/src/test/java/io/spine/server/migration/mirror/given/MirrorMigrationTestEnv.java index 20c526ff6a3..7ddb1e740cc 100644 --- a/server/src/test/java/io/spine/server/migration/mirror/given/MirrorMigrationTestEnv.java +++ b/server/src/test/java/io/spine/server/migration/mirror/given/MirrorMigrationTestEnv.java @@ -27,7 +27,6 @@ package io.spine.server.migration.mirror.given; import io.spine.environment.Tests; -import io.spine.server.BoundedContextBuilder; import io.spine.server.ServerEnvironment; import io.spine.server.entity.storage.EntityRecordStorage; import io.spine.server.migration.mirror.MirrorStorage; @@ -54,10 +53,7 @@ public static void assertWithinBc(EntityRecordStorage entityRe ServerEnvironment.when(Tests.class) .use(PreparedStorageFactory.with(entityRecordStorage)); - var context = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(ParcelAgg.class) - ); + var context = BlackBox.singleTenantWith(ParcelAgg.class); var client = context.clients() .withMatchingTenant() .asGuest(); diff --git a/server/src/test/java/io/spine/server/model/handler/MessageInterfaceResultTest.java b/server/src/test/java/io/spine/server/model/handler/MessageInterfaceResultTest.java index db96e862c96..e14016a2af2 100644 --- a/server/src/test/java/io/spine/server/model/handler/MessageInterfaceResultTest.java +++ b/server/src/test/java/io/spine/server/model/handler/MessageInterfaceResultTest.java @@ -26,7 +26,6 @@ package io.spine.server.model.handler; -import io.spine.server.BoundedContextBuilder; import io.spine.server.aggregate.model.AggregateClass; import io.spine.server.model.handler.given.RoverBot; import io.spine.server.model.handler.given.command.Start; @@ -52,10 +51,7 @@ class MessageInterfaceResultTest { @BeforeEach void createContext() { - context = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(RoverBot.class) - ); + context = BlackBox.singleTenantWith(RoverBot.class); } @AfterEach diff --git a/server/src/test/java/io/spine/server/procman/ProcessManagerRepositoryTest.java b/server/src/test/java/io/spine/server/procman/ProcessManagerRepositoryTest.java index 9d3bd806d40..9983c3497f9 100644 --- a/server/src/test/java/io/spine/server/procman/ProcessManagerRepositoryTest.java +++ b/server/src/test/java/io/spine/server/procman/ProcessManagerRepositoryTest.java @@ -596,10 +596,7 @@ void postEventsThroughFilter() { .newBuilder() .setProjectId(projectId) .build(); - var context = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(new EventDiscardingProcManRepository()) - ); + var context = BlackBox.singleTenantWith(new EventDiscardingProcManRepository()); context.receivesCommand(command) .assertEvents() diff --git a/server/src/test/java/io/spine/server/projection/StateRoutingTest.java b/server/src/test/java/io/spine/server/projection/StateRoutingTest.java index 7f0f40e9ce7..267150abf3f 100644 --- a/server/src/test/java/io/spine/server/projection/StateRoutingTest.java +++ b/server/src/test/java/io/spine/server/projection/StateRoutingTest.java @@ -29,7 +29,6 @@ import com.google.protobuf.StringValue; import io.spine.base.Identifier; import io.spine.protobuf.AnyPacker; -import io.spine.server.BoundedContextBuilder; import io.spine.server.route.given.sur.ArtistMood; import io.spine.server.route.given.sur.ArtistMoodRepo; import io.spine.server.route.given.sur.Gallery; @@ -55,13 +54,11 @@ class StateRoutingTest { @BeforeEach void setupContext() { - context = BlackBox.from( - BoundedContextBuilder - .assumingTests() - .add(MagazineAggregate.class) - .add(new ArtistMoodRepo()) - .add(new Gallery()) - .add(WorksProjection.class) + context = BlackBox.singleTenantWith( + MagazineAggregate.class, + new ArtistMoodRepo(), + new Gallery(), + WorksProjection.class ); } diff --git a/server/src/test/java/io/spine/server/projection/e2e/ProjectionEndToEndTest.java b/server/src/test/java/io/spine/server/projection/e2e/ProjectionEndToEndTest.java index d019c75f037..9a473f50db8 100644 --- a/server/src/test/java/io/spine/server/projection/e2e/ProjectionEndToEndTest.java +++ b/server/src/test/java/io/spine/server/projection/e2e/ProjectionEndToEndTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2022, TeamDev. All rights reserved. + * Copyright 2023, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,6 @@ import io.spine.core.MessageId; import io.spine.core.UserId; import io.spine.protobuf.TypeConverter; -import io.spine.server.BoundedContext; import io.spine.server.BoundedContextBuilder; import io.spine.server.ServerEnvironment; import io.spine.server.given.groups.GroupId; @@ -82,26 +81,26 @@ void receiveUpdates() { var firstTaskAdded = GivenEventMessage.taskAdded(); var secondTaskAdded = GivenEventMessage.taskAdded(); var producerId = created.getProjectId(); - var context = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(new EntitySubscriberProjection.Repository()) - .add(new TestProjection.Repository()) - ); - - context.receivesEventsProducedBy(producerId, - created, - firstTaskAdded, - secondTaskAdded); - - context.assertState( - producerId, - ProjectTaskNames.newBuilder() - .setProjectId(producerId) - .setProjectName(created.getName()) - .addTaskName(firstTaskAdded.getTask().getTitle()) - .addTaskName(secondTaskAdded.getTask().getTitle()) - .build() - ); + try (var context = BlackBox.singleTenantWith( + new EntitySubscriberProjection.Repository(), + new TestProjection.Repository()) + ) { + context.receivesEventsProducedBy(producerId, + created, + firstTaskAdded, + secondTaskAdded); + context.assertState( + producerId, + ProjectTaskNames.newBuilder() + .setProjectId(producerId) + .setProjectName(created.getName()) + .addTaskName(firstTaskAdded.getTask() + .getTitle()) + .addTaskName(secondTaskAdded.getTask() + .getTitle()) + .build() + ); + } } @Test @@ -109,14 +108,8 @@ void receiveUpdates() { void receiveExternal() { var established = GivenEventMessage.organizationEstablished(); - var sender = BlackBox.from( - BoundedContext.singleTenant("Organizations") - .add(new OrganizationProjection.Repository()) - ); - var receiver = BlackBox.from( - BoundedContext.singleTenant("Groups") - .add(new GroupNameProjection.Repository()) - ); + var sender = BlackBox.singleTenant("Organizations", new OrganizationProjection.Repository()); + var receiver = BlackBox.singleTenant("Groups", new GroupNameProjection.Repository()); var producerId = established.getId(); sender.receivesEventsProducedBy(producerId, established); diff --git a/server/src/test/java/io/spine/server/stand/EntityQueryProcessorTest.java b/server/src/test/java/io/spine/server/stand/EntityQueryProcessorTest.java index 1c060c482ec..409ece8357e 100644 --- a/server/src/test/java/io/spine/server/stand/EntityQueryProcessorTest.java +++ b/server/src/test/java/io/spine/server/stand/EntityQueryProcessorTest.java @@ -33,7 +33,6 @@ import io.spine.client.QueryFactory; import io.spine.client.QueryId; import io.spine.client.Target; -import io.spine.server.BoundedContext; import io.spine.server.projection.ProjectionRepository; import io.spine.server.stand.given.MenuRepository; import io.spine.system.server.Mirror; @@ -73,9 +72,7 @@ class EntityQueryProcessorTest { @BeforeEach void setUp() { ProjectionRepository repository = new MenuRepository(); - context = BlackBox.from( - BoundedContext.singleTenant("Cafeteria") - .add(repository)); + context = BlackBox.singleTenant("Cafeteria", repository); processor = new EntityQueryProcessor(repository); fill(); } diff --git a/server/src/test/java/io/spine/system/server/ConstraintViolatedTest.java b/server/src/test/java/io/spine/system/server/ConstraintViolatedTest.java index 6e2fababf7b..765ecf77816 100644 --- a/server/src/test/java/io/spine/system/server/ConstraintViolatedTest.java +++ b/server/src/test/java/io/spine/system/server/ConstraintViolatedTest.java @@ -43,6 +43,7 @@ import static io.spine.base.Identifier.newUuid; import static io.spine.system.server.given.diagnostics.ViolationsWatch.DEFAULT; +import static io.spine.testing.server.blackbox.BlackBox.singleTenantWith; @DisplayName("`ConstraintViolated` should be emitted when") class ConstraintViolatedTest { @@ -51,50 +52,48 @@ class ConstraintViolatedTest { @MuteLogging @DisplayName("an entity state is set to an invalid value as a result of an event") void afterEvent() { - var invalidText = "123-non numerical"; - var context = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(ValidatedAggregate.class) - .add(new ViolationsWatch.Repository()) - ).tolerateFailures(); - context.receivesCommand( - ValidateAndSet.newBuilder() - .setId(ValidatedId.generate()) - .setTextToValidate(invalidText) - .build() - ); - context.assertEntity(DEFAULT, ViolationsWatch.class) - .hasStateThat() - .isEqualTo(InvalidText.newBuilder() - .setId(DEFAULT) - .setInvalidText(invalidText) - .buildPartial() - ); + try (var context = singleTenantWith( + ValidatedAggregate.class, + new ViolationsWatch.Repository() + ).tolerateFailures() + ) { + var invalidText = "123-non numerical"; + context.receivesCommand( + ValidateAndSet.newBuilder() + .setId(ValidatedId.generate()) + .setTextToValidate(invalidText) + .build() + ); + context.assertEntity(DEFAULT, ViolationsWatch.class) + .hasStateThat() + .isEqualTo(InvalidText.newBuilder() + .setId(DEFAULT) + .setInvalidText(invalidText) + .buildPartial() + ); + } } @Test @MuteLogging @DisplayName("an entity state is set to an invalid value as a result of a command") void afterCommand() { - var context = BlackBox.from( - BoundedContextBuilder.assumingTests() - .add(VerificationProcman.class) - .add(new ViolationsWatch.Repository()) - ).tolerateFailures(); - context.receivesCommand( - StartVerification - .newBuilder() - .setUserId(UserId.newBuilder() - .setValue(newUuid())) - .setAddress(EmailAddress.newBuilder() - .setValue("a@b.c")) - .build() - ); - context.assertEntity(DEFAULT, ViolationsWatch.class) - .hasStateThat() - .isEqualTo(InvalidText.newBuilder() - .setId(DEFAULT) - .setErrorMessage("A value must be set.") - .buildPartial()); + try (var context = singleTenantWith(VerificationProcman.class, + new ViolationsWatch.Repository()) + .tolerateFailures() + ) { + context.receivesCommand( + StartVerification.newBuilder() + .setUserId(UserId.newBuilder().setValue(newUuid())) + .setAddress(EmailAddress.newBuilder().setValue("a@b.c")) + .build() + ); + context.assertEntity(DEFAULT, ViolationsWatch.class) + .hasStateThat() + .isEqualTo(InvalidText.newBuilder() + .setId(DEFAULT) + .setErrorMessage("A value must be set.") + .buildPartial()); + } } } diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java index 88813aa3487..edc40fa1c51 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java @@ -61,6 +61,7 @@ import io.spine.testing.server.entity.EntitySubject; import io.spine.testing.server.query.QueryResultSubject; import io.spine.time.ZoneId; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Collection; @@ -76,6 +77,7 @@ import static io.spine.grpc.StreamObservers.memoizingObserver; import static io.spine.server.entity.model.EntityClass.stateClassOf; import static io.spine.testing.server.blackbox.Actor.defaultActor; +import static io.spine.util.Exceptions.newIllegalArgumentException; import static java.util.Collections.singletonList; import static java.util.Collections.synchronizedSet; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -127,16 +129,105 @@ public abstract class BlackBox implements WithLogging, Closeable { private final Set postedEvents; /** - * Creates new instance obtaining configuration parameters from the passed builder. + * Creates new instance obtaining configuration parameters from the given context instance. */ - public static BlackBox from(BoundedContextBuilder builder) { - var context = builder.build(); - var box = builder.isMultitenant() + public static BlackBox from(BoundedContext context) { + var box = context.isMultitenant() ? new MtBlackBox(context) : new StBlackBox(context); return box; } + /** + * Creates new instance obtaining configuration parameters from the passed builder. + */ + public static BlackBox from(BoundedContextBuilder builder) { + var context = builder.build(); + return from(context); + } + + /** + * Creates a {@code BlackBox} over a single-tenant context with the given components. + * + * @see #with(boolean, Object...) + */ + public static BlackBox singleTenantWith(Object... components) { + return with(false, components); + } + + /** + * Creates a {@code BlackBox} over a single-tenant context with the given name and components. + * + * @see #with(boolean, Object...) + */ + public static BlackBox singleTenant(String name, Object... components) { + return with(BoundedContext.singleTenant(name), components); + } + + /** + * Creates a {@code BlackBox} over a multi-tenant context with the given components. + * + * @see #with(boolean, Object...) + */ + public static BlackBox multiTenantWith(Object... components) { + return with(false, components); + } + + /** + * Creates a {@code BlackBox} over a context with the given components. + * + *

    The components can be either {@link Repository} or {@link Entity} classes. + * + *

    For creating a test environment with other components, please use + * {@link BoundedContextBuilder} and then {@link #from(BoundedContext)}. + * + * @param multitenant + * whether the context under the test is multitenant + * @param components + * repositories or entity classes to be added to the context under the test + */ + public static BlackBox with(boolean multitenant, Object... components) { + var builder = BoundedContextBuilder.assumingTests(multitenant); + return with(builder, components); + } + + /** + * Creates a {@code BlackBox} over a context with the given components. + * + *

    The components can be either {@link Repository} or {@link Entity} classes. + * + *

    For creating a test environment with other components, please use + * {@link BoundedContextBuilder} and then {@link #from(BoundedContext)}. + * + * @param builder + * the context builder to use for creating the context under the test + * @param components + * repositories or entity classes to be added to the context under the test + */ + public static BlackBox with(BoundedContextBuilder builder, Object... components) { + for (var c : components) { + if (c instanceof Repository) { + builder.add((Repository) c); + } else if (c instanceof Class) { + if (Entity.class.isAssignableFrom((Class) c)) { + var entityClass = cast((Class)c); + builder.add(entityClass); + } + } else { + throw newIllegalArgumentException( + "Unsupported component type: `%s`.", c.getClass().getName() + ); + } + } + return from(builder); + } + + private static > Class cast(Class cls) { + @SuppressWarnings("unchecked") // Safe due to the `isAssignableFrom` check. + var result = (Class) cls; + return result; + } + BlackBox(BoundedContext context) { super(); this.context = context; diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/ClientFactory.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/ClientFactory.java index 02394440679..8c4286bbdcc 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/ClientFactory.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/ClientFactory.java @@ -156,7 +156,7 @@ private void initServer() { */ @Override @SuppressWarnings("TestOnlyProblems" /* Calling the production-level method. */) - public void close() throws Exception { + public void close() { if (!isOpen() || isNull(grpcContainer)) { return; } From 514c4ed14bd2ebf08d420eddbb4fec3f0365549d Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 Nov 2023 01:23:28 +0000 Subject: [PATCH 18/32] Bump version -> `2.0.0-SNAPSHOT.170` --- pom.xml | 2 +- version.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c044183239c..6b4a78f8508 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ all modules and does not describe the project structure per-subproject. --> io.spine spine-core-java -2.0.0-SNAPSHOT.161 +2.0.0-SNAPSHOT.170 2015 diff --git a/version.gradle.kts b/version.gradle.kts index 6549ecbd082..3d1fb0002eb 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -29,4 +29,4 @@ * * For versions of Spine-based dependencies, please see [io.spine.internal.dependency.Spine]. */ -val versionToPublish: String by extra("2.0.0-SNAPSHOT.161") +val versionToPublish: String by extra("2.0.0-SNAPSHOT.170") From 78c5073c01b2f5d4f0680fff48b4e74a8787efd8 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 Nov 2023 01:32:41 +0000 Subject: [PATCH 19/32] Do not catch exception not thrown --- server/src/main/java/io/spine/server/Closeable.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/server/src/main/java/io/spine/server/Closeable.java b/server/src/main/java/io/spine/server/Closeable.java index 0dfb73f1017..091743bad64 100644 --- a/server/src/main/java/io/spine/server/Closeable.java +++ b/server/src/main/java/io/spine/server/Closeable.java @@ -75,11 +75,7 @@ default void checkOpen() throws IllegalStateException { */ default void closeIfOpen() { if (isOpen()) { - try { - close(); - } catch (Exception e) { - throw illegalStateWithCauseOf(e); - } + close(); } } } From 6035903d5244f7ec58d0f51d98ecd40a6698a354 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 Nov 2023 01:35:55 +0000 Subject: [PATCH 20/32] Document the interface --- .../main/java/io/spine/server/bus/DelegatingDispatcher.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java b/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java index ed21cdd714b..0f896e91d06 100644 --- a/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java +++ b/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java @@ -30,6 +30,12 @@ import io.spine.server.type.MessageEnvelope; import io.spine.type.MessageClass; +/** + * A dispatcher which delegates the responsibilities to an aggregated {@link DispatcherDelegate}. + * + * @param the type of the message class + * @param the type of the message envelope + */ @Internal public interface DelegatingDispatcher, E extends MessageEnvelope> From 369b3a689210702c5d4e635343208924f3a6c7e0 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 Nov 2023 02:17:41 +0000 Subject: [PATCH 21/32] Do not require wrapping exceptions on `close()` --- .../testing/server/blackbox/BlackBox.java | 8 ++--- .../testing/server/blackbox/BlackBoxTest.java | 31 ++++++++++++------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java index edc40fa1c51..f7730b5b3aa 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java @@ -61,7 +61,6 @@ import io.spine.testing.server.entity.EntitySubject; import io.spine.testing.server.query.QueryResultSubject; import io.spine.time.ZoneId; -import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.Collection; @@ -170,7 +169,7 @@ public static BlackBox singleTenant(String name, Object... components) { * @see #with(boolean, Object...) */ public static BlackBox multiTenantWith(Object... components) { - return with(false, components); + return with(true, components); } /** @@ -204,6 +203,7 @@ public static BlackBox with(boolean multitenant, Object... components) { * @param components * repositories or entity classes to be added to the context under the test */ + @SuppressWarnings("ChainOfInstanceofChecks") // We allow passing `Object`s to simplify setup. public static BlackBox with(BoundedContextBuilder builder, Object... components) { for (var c : components) { if (c instanceof Repository) { @@ -583,8 +583,8 @@ private ImmutableList commands() { abstract ImmutableList select(CommandCollector collector); /** - * Obtains immutable list of events generated in this Bounded Context in response to posted - * messages. + * Obtains an immutable list of events generated in this Bounded Context in response + * to posted messages. * *

    The returned list does NOT contain events posted to this Bounded Context * during test setup. diff --git a/testutil-server/src/test/java/io/spine/testing/server/blackbox/BlackBoxTest.java b/testutil-server/src/test/java/io/spine/testing/server/blackbox/BlackBoxTest.java index 0fba5f4e419..4c0ef026b1d 100644 --- a/testutil-server/src/test/java/io/spine/testing/server/blackbox/BlackBoxTest.java +++ b/testutil-server/src/test/java/io/spine/testing/server/blackbox/BlackBoxTest.java @@ -450,7 +450,7 @@ void multiple() { } @Test - @DisplayName("throw `IllegalStateException` on Bounded Context close error") + @DisplayName("pass exception thrown when 'Bounded Context' is closed") void throwIllegalStateExceptionOnClose() { var throwingRepo = new RepositoryThrowingExceptionOnClose() { @Override @@ -459,11 +459,9 @@ protected void throwException() { } }; - var ctx = BlackBox.from( - newBuilder().add(throwingRepo) - ); - - assertThrows(IllegalStateException.class, ctx::close); + try (var ctx = BlackBox.singleTenantWith(throwingRepo)) { + assertThrows(RuntimeException.class, ctx::close); + } } @Nested @@ -496,16 +494,14 @@ void setUp() { @Test void singleTenant() { - var builder = BoundedContextBuilder - .assumingTests(false) + var builder = BoundedContextBuilder.assumingTests(false) .enrichEventsUsing(enricher); assertBlackBox(builder, StBlackBox.class); } @Test void multiTenant() { - var builder = BoundedContextBuilder - .assumingTests(true) + var builder = BoundedContextBuilder.assumingTests(true) .setTenantIndex(tenantIndex) .enrichEventsUsing(enricher); assertBlackBox(builder, MtBlackBox.class); @@ -580,7 +576,7 @@ private void assertEnricher() { } private void assertTenantIndex() { - if(context().isMultitenant()) { + if (context().isMultitenant()) { assertThat(tenantIndex()) .isSameInstanceAs(tenantIndex); } @@ -609,6 +605,19 @@ private Set toTypes(Iterable> repos) { } } + @Nested + @DisplayName("clreate an instance using passed components") + class CreateWith { + + @Test + @DisplayName("multitenant") + void multitenant() { + try(var ctx = BlackBox.multiTenantWith(new BbProjectRepository())) { + assertThat(ctx.context().isMultitenant()).isTrue(); + } + } + } + @Nested @DisplayName("obtain `EntitySubject`") class ObtainEntitySubject { From 6576993d7dab402472408d5c542e2ec364e5489b Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 Nov 2023 02:28:43 +0000 Subject: [PATCH 22/32] Improve documentation --- server/src/main/java/io/spine/server/bus/Bus.java | 2 +- .../java/io/spine/server/bus/DispatcherDelegate.java | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/server/src/main/java/io/spine/server/bus/Bus.java b/server/src/main/java/io/spine/server/bus/Bus.java index 389d56437ae..344f65de062 100644 --- a/server/src/main/java/io/spine/server/bus/Bus.java +++ b/server/src/main/java/io/spine/server/bus/Bus.java @@ -1,5 +1,5 @@ /* - * Copyright 2022, TeamDev. All rights reserved. + * Copyright 2023, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java b/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java index 17fd40fe0f2..1fc322ab1ce 100644 --- a/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java +++ b/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java @@ -29,7 +29,11 @@ import io.spine.server.type.MessageEnvelope; import io.spine.type.MessageClass; -public interface DispatcherDelegate, - E extends MessageEnvelope> { - +/** + * A delegate of a {@link DelegatingDispatcher}. + * + * @param the type of the dispatched message class + * @param the type of the message envelope + */ +public interface DispatcherDelegate, E extends MessageEnvelope> { } From 5fabf274919e5b163972b7fed3f602b0dce44c38 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 Nov 2023 02:38:22 +0000 Subject: [PATCH 23/32] Optimise imports --- server/src/main/java/io/spine/server/Closeable.java | 1 - server/src/main/java/io/spine/server/transport/ChannelHub.java | 2 +- .../io/spine/server/aggregate/AggregateRepositoryTest.java | 2 +- .../java/io/spine/server/log/AbstractEntityLoggingTest.java | 1 - .../io/spine/server/projection/e2e/ProjectionEndToEndTest.java | 3 +-- .../java/io/spine/system/server/ConstraintViolatedTest.java | 2 -- .../java/io/spine/testing/server/blackbox/BlackBoxTest.java | 1 - 7 files changed, 3 insertions(+), 9 deletions(-) diff --git a/server/src/main/java/io/spine/server/Closeable.java b/server/src/main/java/io/spine/server/Closeable.java index 091743bad64..dadb82675d9 100644 --- a/server/src/main/java/io/spine/server/Closeable.java +++ b/server/src/main/java/io/spine/server/Closeable.java @@ -27,7 +27,6 @@ package io.spine.server; import static com.google.common.base.Preconditions.checkState; -import static io.spine.util.Exceptions.illegalStateWithCauseOf; /** * Base interface for server-side objects that may hold resources that need to be released diff --git a/server/src/main/java/io/spine/server/transport/ChannelHub.java b/server/src/main/java/io/spine/server/transport/ChannelHub.java index da12b2dbace..f09c181f321 100644 --- a/server/src/main/java/io/spine/server/transport/ChannelHub.java +++ b/server/src/main/java/io/spine/server/transport/ChannelHub.java @@ -103,7 +103,7 @@ private Set detectStale() { if (channel.isStale()) { try { channel.close(); - } catch (Exception e) { + } catch (RuntimeException e) { throw illegalStateWithCauseOf(e); } finally { toRemove.add(channelId); diff --git a/server/src/test/java/io/spine/server/aggregate/AggregateRepositoryTest.java b/server/src/test/java/io/spine/server/aggregate/AggregateRepositoryTest.java index 91a821ae363..e437f248f17 100644 --- a/server/src/test/java/io/spine/server/aggregate/AggregateRepositoryTest.java +++ b/server/src/test/java/io/spine/server/aggregate/AggregateRepositoryTest.java @@ -114,7 +114,7 @@ void setUp() { } @AfterEach - void tearDown() throws Exception { + void tearDown() { context().close(); } diff --git a/server/src/test/java/io/spine/server/log/AbstractEntityLoggingTest.java b/server/src/test/java/io/spine/server/log/AbstractEntityLoggingTest.java index aaedc006721..f664a3fdb1b 100644 --- a/server/src/test/java/io/spine/server/log/AbstractEntityLoggingTest.java +++ b/server/src/test/java/io/spine/server/log/AbstractEntityLoggingTest.java @@ -27,7 +27,6 @@ package io.spine.server.log; import io.spine.core.UserId; -import io.spine.server.BoundedContextBuilder; import io.spine.server.log.given.Books; import io.spine.server.log.given.CardAggregate; import io.spine.testing.core.given.GivenUserId; diff --git a/server/src/test/java/io/spine/server/projection/e2e/ProjectionEndToEndTest.java b/server/src/test/java/io/spine/server/projection/e2e/ProjectionEndToEndTest.java index 9a473f50db8..ca1224c5ebb 100644 --- a/server/src/test/java/io/spine/server/projection/e2e/ProjectionEndToEndTest.java +++ b/server/src/test/java/io/spine/server/projection/e2e/ProjectionEndToEndTest.java @@ -126,8 +126,7 @@ void receiveExternal() { @Test @DisplayName("receive entity state updates along with system event context") - @SuppressWarnings("OverlyCoupledMethod") - void receiveEntityStateUpdatesAndEventContext() throws Exception { + void receiveEntityStateUpdatesAndEventContext() { var repository = new GroupProjection.Repository(); var groups = BoundedContextBuilder.assumingTests().build(); groups.internalAccess() diff --git a/server/src/test/java/io/spine/system/server/ConstraintViolatedTest.java b/server/src/test/java/io/spine/system/server/ConstraintViolatedTest.java index 765ecf77816..df48e0ae8d9 100644 --- a/server/src/test/java/io/spine/system/server/ConstraintViolatedTest.java +++ b/server/src/test/java/io/spine/system/server/ConstraintViolatedTest.java @@ -28,7 +28,6 @@ import io.spine.core.UserId; import io.spine.net.EmailAddress; -import io.spine.server.BoundedContextBuilder; import io.spine.system.server.given.diagnostics.ValidatedAggregate; import io.spine.system.server.given.diagnostics.VerificationProcman; import io.spine.system.server.given.diagnostics.ViolationsWatch; @@ -37,7 +36,6 @@ import io.spine.system.server.test.ValidateAndSet; import io.spine.system.server.test.ValidatedId; import io.spine.testing.logging.mute.MuteLogging; -import io.spine.testing.server.blackbox.BlackBox; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/testutil-server/src/test/java/io/spine/testing/server/blackbox/BlackBoxTest.java b/testutil-server/src/test/java/io/spine/testing/server/blackbox/BlackBoxTest.java index 4c0ef026b1d..b1651a44472 100644 --- a/testutil-server/src/test/java/io/spine/testing/server/blackbox/BlackBoxTest.java +++ b/testutil-server/src/test/java/io/spine/testing/server/blackbox/BlackBoxTest.java @@ -875,7 +875,6 @@ void linkedToTheContextUnderTest() { @Test @DisplayName("closed as `BlackBoxContext` is closed") - @SuppressWarnings("ResultOfMethodCallIgnored") /* Expecting an exception. */ void closedAsBlackBoxContextClosed() { var factory = context().clients(); var client = factory.withMatchingTenant(); From 23e745d1472582092c182fba8c9e04af02cd77c0 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 Nov 2023 02:42:40 +0000 Subject: [PATCH 24/32] Revert formatting --- .../main/java/io/spine/server/stand/InMemoryTypeRegistry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/io/spine/server/stand/InMemoryTypeRegistry.java b/server/src/main/java/io/spine/server/stand/InMemoryTypeRegistry.java index d5c78e2e74f..ee3d22492a7 100644 --- a/server/src/main/java/io/spine/server/stand/InMemoryTypeRegistry.java +++ b/server/src/main/java/io/spine/server/stand/InMemoryTypeRegistry.java @@ -71,7 +71,7 @@ static TypeRegistry newInstance() { if (repository instanceof QueryableRepository) { @SuppressWarnings("unchecked") /* Guaranteed by the `QueryableRepository` contract. */ - var recordRepo = (QueryableRepository) repository; + var recordRepo = (QueryableRepository) repository; repositories.put(entityType, recordRepo); } if (repository instanceof AggregateRepository) { From df8a53e42b98fd871c5d994c0fd30cc420cd78d0 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 Nov 2023 15:10:47 +0000 Subject: [PATCH 25/32] Document generic parameters --- .../java/io/spine/server/bus/DelegatingDispatcher.java | 6 ++++-- .../java/io/spine/server/bus/DispatcherDelegate.java | 10 ++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java b/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java index 0f896e91d06..3cc997c6681 100644 --- a/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java +++ b/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java @@ -33,8 +33,10 @@ /** * A dispatcher which delegates the responsibilities to an aggregated {@link DispatcherDelegate}. * - * @param the type of the message class - * @param the type of the message envelope + * @param + * the type of the message class + * @param + * the type of the message envelope */ @Internal public interface DelegatingDispatcher, diff --git a/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java b/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java index 1fc322ab1ce..4c550b4ae52 100644 --- a/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java +++ b/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java @@ -32,8 +32,14 @@ /** * A delegate of a {@link DelegatingDispatcher}. * - * @param the type of the dispatched message class - * @param the type of the message envelope + * @param + * the type of the dispatched message class + * @param + * the type of the message envelope + * @apiNote The generic parameters are the same as the ones of the {@link DelegatingDispatcher} + * to which this delegate belongs. Even though they are not used in this interface directly, + * they bring semantic meaning to the interface and code clarity at the usage sites. */ +@SuppressWarnings("unused") public interface DispatcherDelegate, E extends MessageEnvelope> { } From b7d4545c55c9bee5a33ca95beb18653b377c0612 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 Nov 2023 15:11:36 +0000 Subject: [PATCH 26/32] Bump `base` -> `2.0.0-SNAPSHOT.191` --- buildSrc/src/main/kotlin/io/spine/internal/dependency/Spine.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Spine.kt b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Spine.kt index 490fbf4f66c..08a521241b2 100644 --- a/buildSrc/src/main/kotlin/io/spine/internal/dependency/Spine.kt +++ b/buildSrc/src/main/kotlin/io/spine/internal/dependency/Spine.kt @@ -45,7 +45,7 @@ object Spine { * * @see spine-base */ - const val base = "2.0.0-SNAPSHOT.190" + const val base = "2.0.0-SNAPSHOT.191" /** * The version of [Spine.reflect]. From edf6a01b7579034e5a8fc2c45a92f328c24fe73c Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 Nov 2023 15:41:51 +0000 Subject: [PATCH 27/32] Remove `Probe` to `BlackBoxProbe` --- .../io/spine/testing/server/blackbox/BlackBox.java | 11 ++++++++--- .../blackbox/probe/{Probe.java => BlackboxProbe.java} | 4 ++-- .../spine/testing/server/blackbox/BlackBoxTest.java | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) rename testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/{Probe.java => BlackboxProbe.java} (97%) diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java index f7730b5b3aa..125cf54dcb6 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/BlackBox.java @@ -57,7 +57,7 @@ import io.spine.testing.server.EventSubject; import io.spine.testing.server.blackbox.probe.CommandCollector; import io.spine.testing.server.blackbox.probe.EventCollector; -import io.spine.testing.server.blackbox.probe.Probe; +import io.spine.testing.server.blackbox.probe.BlackboxProbe; import io.spine.testing.server.entity.EntitySubject; import io.spine.testing.server.query.QueryResultSubject; import io.spine.time.ZoneId; @@ -70,6 +70,7 @@ import java.util.function.Predicate; import java.util.function.Supplier; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.Lists.asList; @@ -99,7 +100,7 @@ public abstract class BlackBox implements WithLogging, Closeable { /** * The probe inserted into the context under the test. */ - private final Probe probe; + private final BlackboxProbe probe; /** * A factory of {@link Client}s which send requests to this context. @@ -169,6 +170,7 @@ public static BlackBox singleTenant(String name, Object... components) { * @see #with(boolean, Object...) */ public static BlackBox multiTenantWith(Object... components) { + checkNotNull(components); return with(true, components); } @@ -186,6 +188,7 @@ public static BlackBox multiTenantWith(Object... components) { * repositories or entity classes to be added to the context under the test */ public static BlackBox with(boolean multitenant, Object... components) { + checkNotNull(components); var builder = BoundedContextBuilder.assumingTests(multitenant); return with(builder, components); } @@ -205,7 +208,9 @@ public static BlackBox with(boolean multitenant, Object... components) { */ @SuppressWarnings("ChainOfInstanceofChecks") // We allow passing `Object`s to simplify setup. public static BlackBox with(BoundedContextBuilder builder, Object... components) { + checkNotNull(components); for (var c : components) { + checkArgument(c != null, "Null component is not allowed."); if (c instanceof Repository) { builder.add((Repository) c); } else if (c instanceof Class) { @@ -231,7 +236,7 @@ public static BlackBox with(BoundedContextBuilder builder, Object... components) BlackBox(BoundedContext context) { super(); this.context = context; - this.probe = new Probe(); + this.probe = new BlackboxProbe(); context.install(probe); this.clientFactory = new ClientFactory(context); this.actor = defaultActor(); diff --git a/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/Probe.java b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/BlackboxProbe.java similarity index 97% rename from testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/Probe.java rename to testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/BlackboxProbe.java index d52d69939ff..4b67f55a1ff 100644 --- a/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/Probe.java +++ b/testutil-server/src/main/java/io/spine/testing/server/blackbox/probe/BlackboxProbe.java @@ -40,7 +40,7 @@ * {@link io.spine.testing.server.blackbox.BlackBox BlackBox} to collect * commands and events produced by a {@link BoundedContext} being tested. */ -public class Probe implements BoundedContext.Probe { +public final class BlackboxProbe implements BoundedContext.Probe { private @Nullable BoundedContext context; @@ -64,7 +64,7 @@ public class Probe implements BoundedContext.Probe { /** * Creates a new instance. */ - public Probe() { + public BlackboxProbe() { this.commands = new CommandCollector(); this.events = new EventCollector(); this.failedHandlerGuard = new FailedHandlerGuard(); diff --git a/testutil-server/src/test/java/io/spine/testing/server/blackbox/BlackBoxTest.java b/testutil-server/src/test/java/io/spine/testing/server/blackbox/BlackBoxTest.java index b1651a44472..518c40bd4af 100644 --- a/testutil-server/src/test/java/io/spine/testing/server/blackbox/BlackBoxTest.java +++ b/testutil-server/src/test/java/io/spine/testing/server/blackbox/BlackBoxTest.java @@ -606,7 +606,7 @@ private Set toTypes(Iterable> repos) { } @Nested - @DisplayName("clreate an instance using passed components") + @DisplayName("create an instance using passed components") class CreateWith { @Test @@ -863,7 +863,7 @@ void linkedToTheContextUnderTest() { assertThat(clientRequest.run(BbProjectView.query().build())) .hasSize(0); - // Let's send a command with each of APIs. + // Let's send a command with each of the APIs. clientRequest.command(Given.createProject()).postAndForget(); context().receivesCommand(Given.createProject()); From 4e92e172544f6ea7320ef345d1476ecacb163257 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 Nov 2023 15:42:12 +0000 Subject: [PATCH 28/32] Address review comments in the docs --- .../src/main/java/io/spine/server/BoundedContext.java | 4 ++-- server/src/main/java/io/spine/server/bus/Bus.java | 4 ++-- .../io/spine/server/bus/DelegatingDispatcher.java | 2 +- .../java/io/spine/server/bus/DispatcherDelegate.java | 11 ++++++++--- .../java/io/spine/server/bus/MessageDispatcher.java | 2 +- .../java/io/spine/server/stand/EventRegistry.java | 5 +++++ .../io/spine/server/transport/MessageChannel.java | 5 +++++ .../java/io/spine/system/server/SystemContext.java | 2 +- .../test/java/io/spine/server/BoundedContextSpec.kt | 4 ++-- 9 files changed, 27 insertions(+), 12 deletions(-) diff --git a/server/src/main/java/io/spine/server/BoundedContext.java b/server/src/main/java/io/spine/server/BoundedContext.java index 5b0b53f9d1b..34d982ee981 100644 --- a/server/src/main/java/io/spine/server/BoundedContext.java +++ b/server/src/main/java/io/spine/server/BoundedContext.java @@ -638,12 +638,12 @@ public int hashCode() { public interface Probe extends ContextAware { /** - * The listener of commands posted processed by the context. + * The listener of commands processed by the context. */ Listener commandListener(); /** - * The listener of events posted processed by the context. + * The listener of events processed by the context. */ Listener eventListener(); diff --git a/server/src/main/java/io/spine/server/bus/Bus.java b/server/src/main/java/io/spine/server/bus/Bus.java index 344f65de062..19af0625301 100644 --- a/server/src/main/java/io/spine/server/bus/Bus.java +++ b/server/src/main/java/io/spine/server/bus/Bus.java @@ -62,7 +62,7 @@ * @param the type of dispatches used by this bus */ @Internal -@SuppressWarnings("ClassWithTooManyMethods") // OK the abstract bus. +@SuppressWarnings("ClassWithTooManyMethods") // OK for the abstract bus. public abstract class Bus, E extends SignalEnvelope, C extends MessageClass, @@ -103,7 +103,7 @@ public void register(D dispatcher) { } /** - * Stops dispatching messages the given dispatcher. + * Stops dispatching messages using the given dispatcher. * * @param dispatcher the dispatcher to unregister */ diff --git a/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java b/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java index 3cc997c6681..5c524851ff0 100644 --- a/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java +++ b/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java @@ -31,7 +31,7 @@ import io.spine.type.MessageClass; /** - * A dispatcher which delegates the responsibilities to an aggregated {@link DispatcherDelegate}. + * A dispatcher which delegates the responsibilities to a {@link DispatcherDelegate}. * * @param * the type of the message class diff --git a/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java b/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java index 4c550b4ae52..fefadc4f679 100644 --- a/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java +++ b/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java @@ -36,10 +36,15 @@ * the type of the dispatched message class * @param * the type of the message envelope - * @apiNote The generic parameters are the same as the ones of the {@link DelegatingDispatcher} - * to which this delegate belongs. Even though they are not used in this interface directly, + * @apiNote + * This interface does not extend {@link MessageDispatcher} in order to allow implementing + * "native" dispatching interface and this delegation interface in one class. + * + *

    The generic parameters are the same as the ones of {@link MessageDispatcher} and + * a {@link DelegatingDispatcher} to which this delegate belongs. + * Even though they are not used in this interface directly, * they bring semantic meaning to the interface and code clarity at the usage sites. */ -@SuppressWarnings("unused") +@SuppressWarnings("unused") // see the API note above. public interface DispatcherDelegate, E extends MessageEnvelope> { } diff --git a/server/src/main/java/io/spine/server/bus/MessageDispatcher.java b/server/src/main/java/io/spine/server/bus/MessageDispatcher.java index 67a05059f7d..ddbeb05612c 100644 --- a/server/src/main/java/io/spine/server/bus/MessageDispatcher.java +++ b/server/src/main/java/io/spine/server/bus/MessageDispatcher.java @@ -35,7 +35,7 @@ * A dispatcher of a message. * * @param - * the type of class of the dispatched messages + * the type of message class of dispatched messages * @param * the type of the message envelopes */ diff --git a/server/src/main/java/io/spine/server/stand/EventRegistry.java b/server/src/main/java/io/spine/server/stand/EventRegistry.java index d391197326a..21e1bc14746 100644 --- a/server/src/main/java/io/spine/server/stand/EventRegistry.java +++ b/server/src/main/java/io/spine/server/stand/EventRegistry.java @@ -62,6 +62,11 @@ interface EventRegistry extends AutoCloseable { */ ImmutableSet eventClasses(); + /** + * {@inheritDoc} + * + *

    Overrides to remove the checked exception from the signature. + */ @Override void close(); } diff --git a/server/src/main/java/io/spine/server/transport/MessageChannel.java b/server/src/main/java/io/spine/server/transport/MessageChannel.java index ef80af072f5..cae94588aff 100644 --- a/server/src/main/java/io/spine/server/transport/MessageChannel.java +++ b/server/src/main/java/io/spine/server/transport/MessageChannel.java @@ -69,6 +69,11 @@ static ChannelId channelIdFor(TypeUrl messageType) { return channelId; } + /** + * {@inheritDoc} + * + *

    Overrides to remove the checked exception from the signature. + */ @Override void close(); } diff --git a/server/src/main/java/io/spine/system/server/SystemContext.java b/server/src/main/java/io/spine/system/server/SystemContext.java index bb444bf3b14..03ba77aca47 100644 --- a/server/src/main/java/io/spine/system/server/SystemContext.java +++ b/server/src/main/java/io/spine/system/server/SystemContext.java @@ -108,7 +108,7 @@ public SystemClient createClient() { /** * {@inheritDoc} * - *

    Since a system-bounded context does not have an associated system bounded context, + *

    Since the System context does not have an associated System context of its own, * the method returns a {@link NoOpSystemWriteSide} instance. */ @Override diff --git a/server/src/test/java/io/spine/server/BoundedContextSpec.kt b/server/src/test/java/io/spine/server/BoundedContextSpec.kt index e18f52bf225..092ccb87bbc 100644 --- a/server/src/test/java/io/spine/server/BoundedContextSpec.kt +++ b/server/src/test/java/io/spine/server/BoundedContextSpec.kt @@ -175,7 +175,7 @@ internal class BoundedContextSpec { } @Nested - @DisplayName("provide internal secured access to") + @DisplayName("provide guarded internal access to") inner class InternalAccess { private lateinit var access: BoundedContext.InternalAccess @@ -514,7 +514,7 @@ internal class BoundedContextSpec { @Nested @DisplayName("support diagnostics via `Probe`") - inner class SupportProbe { + inner class SupportBlackboxProbe { private val probe: BoundedContext.Probe = EmptyProbe() From 01b815f5bc17d310f28879655268e37ab66325ae Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 Nov 2023 15:46:41 +0000 Subject: [PATCH 29/32] Update dependency reports --- license-report.md | 24 ++++++++++++------------ pom.xml | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/license-report.md b/license-report.md index 1bf9e5116ad..da6e689c6df 100644 --- a/license-report.md +++ b/license-report.md @@ -1,6 +1,6 @@ -# Dependencies of `io.spine:spine-client:2.0.0-SNAPSHOT.161` +# Dependencies of `io.spine:spine-client:2.0.0-SNAPSHOT.170` ## Runtime 1. **Group** : com.google.android. **Name** : annotations. **Version** : 4.1.1.4. @@ -798,12 +798,12 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Oct 31 23:41:13 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Wed Nov 01 15:46:08 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine:spine-core:2.0.0-SNAPSHOT.161` +# Dependencies of `io.spine:spine-core:2.0.0-SNAPSHOT.170` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -1561,12 +1561,12 @@ This report was generated on **Tue Oct 31 23:41:13 WET 2023** using [Gradle-Lice The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Oct 31 23:41:14 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Wed Nov 01 15:46:08 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine:spine-server:2.0.0-SNAPSHOT.161` +# Dependencies of `io.spine:spine-server:2.0.0-SNAPSHOT.170` ## Runtime 1. **Group** : com.google.android. **Name** : annotations. **Version** : 4.1.1.4. @@ -2372,12 +2372,12 @@ This report was generated on **Tue Oct 31 23:41:14 WET 2023** using [Gradle-Lice The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Oct 31 23:41:14 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Wed Nov 01 15:46:09 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:spine-testutil-client:2.0.0-SNAPSHOT.161` +# Dependencies of `io.spine.tools:spine-testutil-client:2.0.0-SNAPSHOT.170` ## Runtime 1. **Group** : com.google.android. **Name** : annotations. **Version** : 4.1.1.4. @@ -3303,12 +3303,12 @@ This report was generated on **Tue Oct 31 23:41:14 WET 2023** using [Gradle-Lice The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Oct 31 23:41:14 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Wed Nov 01 15:46:09 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:spine-testutil-core:2.0.0-SNAPSHOT.161` +# Dependencies of `io.spine.tools:spine-testutil-core:2.0.0-SNAPSHOT.170` ## Runtime 1. **Group** : com.google.android. **Name** : annotations. **Version** : 4.1.1.4. @@ -4234,12 +4234,12 @@ This report was generated on **Tue Oct 31 23:41:14 WET 2023** using [Gradle-Lice The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Oct 31 23:41:15 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Wed Nov 01 15:46:10 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:spine-testutil-server:2.0.0-SNAPSHOT.161` +# Dependencies of `io.spine.tools:spine-testutil-server:2.0.0-SNAPSHOT.170` ## Runtime 1. **Group** : com.google.android. **Name** : annotations. **Version** : 4.1.1.4. @@ -5213,4 +5213,4 @@ This report was generated on **Tue Oct 31 23:41:15 WET 2023** using [Gradle-Lice The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Oct 31 23:41:15 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file +This report was generated on **Wed Nov 01 15:46:10 WET 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file diff --git a/pom.xml b/pom.xml index 6b4a78f8508..db7a8121183 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ all modules and does not describe the project structure per-subproject. io.spine spine-base - 2.0.0-SNAPSHOT.190 + 2.0.0-SNAPSHOT.191 compile From 0a665f69cdb70bdd503812dcaba99c314a8df1e7 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 Nov 2023 17:12:41 +0000 Subject: [PATCH 30/32] Improve documentation --- server/src/main/java/io/spine/server/bus/Bus.java | 4 ++-- .../io/spine/server/bus/DelegatingDispatcher.java | 3 ++- .../java/io/spine/server/bus/DispatcherDelegate.java | 6 ++++++ .../server/commandbus/CommandDispatcherDelegate.java | 8 ++++---- .../commandbus/DelegatingCommandDispatcher.java | 4 ++-- .../spine/server/event/EventDispatcherDelegate.java | 12 ++++++++---- 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/server/src/main/java/io/spine/server/bus/Bus.java b/server/src/main/java/io/spine/server/bus/Bus.java index 19af0625301..b7b2771dd15 100644 --- a/server/src/main/java/io/spine/server/bus/Bus.java +++ b/server/src/main/java/io/spine/server/bus/Bus.java @@ -121,7 +121,7 @@ public void unregister(DispatcherDelegate delegate) { } /** - * Adds the passed listener to the bus. + * Adds the given listener to the bus. */ public void add(Listener listener) { checkNotNull(listener); @@ -129,7 +129,7 @@ public void add(Listener listener) { } /** - * Removes the passed listener from the bus. + * Removes the given listener from the bus. */ public void remove(Listener listener) { checkNotNull(listener); diff --git a/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java b/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java index 5c524851ff0..118158e68d9 100644 --- a/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java +++ b/server/src/main/java/io/spine/server/bus/DelegatingDispatcher.java @@ -31,12 +31,13 @@ import io.spine.type.MessageClass; /** - * A dispatcher which delegates the responsibilities to a {@link DispatcherDelegate}. + * A dispatcher which passes the responsibilities to its {@linkplain #delegate() delegate}. * * @param * the type of the message class * @param * the type of the message envelope + * @see DispatcherDelegate */ @Internal public interface DelegatingDispatcher, diff --git a/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java b/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java index fefadc4f679..1f10177f3d1 100644 --- a/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java +++ b/server/src/main/java/io/spine/server/bus/DispatcherDelegate.java @@ -32,6 +32,11 @@ /** * A delegate of a {@link DelegatingDispatcher}. * + *

    This is a common interface for objects which need to dispatch messages of + * one kind (e.g., events), but are unable to implement {@link MessageDispatcher} because + * they already implement this interface with generic parameters for another + * kind of messages (e.g., commands). + * * @param * the type of the dispatched message class * @param @@ -44,6 +49,7 @@ * a {@link DelegatingDispatcher} to which this delegate belongs. * Even though they are not used in this interface directly, * they bring semantic meaning to the interface and code clarity at the usage sites. + * @see DelegatingDispatcher */ @SuppressWarnings("unused") // see the API note above. public interface DispatcherDelegate, E extends MessageEnvelope> { diff --git a/server/src/main/java/io/spine/server/commandbus/CommandDispatcherDelegate.java b/server/src/main/java/io/spine/server/commandbus/CommandDispatcherDelegate.java index 2fb22dd56c7..bb9fb59df66 100644 --- a/server/src/main/java/io/spine/server/commandbus/CommandDispatcherDelegate.java +++ b/server/src/main/java/io/spine/server/commandbus/CommandDispatcherDelegate.java @@ -38,18 +38,18 @@ * the {@link io.spine.server.commandbus.CommandDispatcher CommandDispatcher}. * *

    A typical example of a {@code CommandDispatcherDelegate} usage is a routine, which - * simultaneously dispatches different types of {@linkplain com.google.protobuf.Message messages} - * in addition to {@code Command}s. + * simultaneously dispatches different types of messages (e.g., {@code Event}s) in addition + * to {@code Command}s. * *

    In this case such a class would have to implement several * {@link io.spine.server.bus.MessageDispatcher MessageDispatcher} child interfaces * (such as {@link io.spine.server.commandbus.CommandDispatcher CommandDispatcher} or * {@link io.spine.server.event.EventDispatcher EventDispatcher}). However, it is impossible * to implement the same {@link io.spine.server.bus.MessageDispatcher#messageClasses() - * getMessageClasses()} method several times with the different types of {@code MessageClass}es + * messageClasses()} method several times with the different types of {@code MessageClass}es * returned. * - *

    The same interference takes place in attempt to implement + *

    The same interference takes place in an attempt to implement * {@link io.spine.server.bus.UnicastDispatcher#dispatch(io.spine.server.type.MessageEnvelope) * UnicastDispatcher.dispatch(MessageEnvelope)} method with the different types of * {@code MessageEnvelope}s dispatches simultaneously. diff --git a/server/src/main/java/io/spine/server/commandbus/DelegatingCommandDispatcher.java b/server/src/main/java/io/spine/server/commandbus/DelegatingCommandDispatcher.java index 50e1829ffe5..5b6e32ce512 100644 --- a/server/src/main/java/io/spine/server/commandbus/DelegatingCommandDispatcher.java +++ b/server/src/main/java/io/spine/server/commandbus/DelegatingCommandDispatcher.java @@ -34,8 +34,8 @@ import io.spine.server.type.CommandEnvelope; /** - * A {@link CommandDispatcher}, that delegates the responsibilities to an aggregated - * {@linkplain CommandDispatcherDelegate delegate instance}. + * A dispatcher of commands which delegates the responsibilities to + * a {@link CommandDispatcherDelegate}. * * @see CommandDispatcherDelegate */ diff --git a/server/src/main/java/io/spine/server/event/EventDispatcherDelegate.java b/server/src/main/java/io/spine/server/event/EventDispatcherDelegate.java index ad6b8fcacae..bd613d56b7e 100644 --- a/server/src/main/java/io/spine/server/event/EventDispatcherDelegate.java +++ b/server/src/main/java/io/spine/server/event/EventDispatcherDelegate.java @@ -37,10 +37,14 @@ * A common interface for objects which need to dispatch {@linkplain io.spine.core.Event events}, * but are unable to implement {@link io.spine.server.event.EventDispatcher EventDispatcher}. * - *

    This interface defines own contract (instead of extending {@link - * io.spine.server.bus.MessageDispatcher MessageDispatcher} to allow classes that dispatch - * messages other than events (by implementing {@link io.spine.server.bus.MessageDispatcher - * MessageDispatcher}), and dispatch events by implementing this interface. + *

    This interface defines own contract for dispatching events, instead of extending + * the {@link io.spine.server.bus.MessageDispatcher MessageDispatcher} interface. + * Such an arrangement allows classes that dispatch messages other than events + * (by implementing {@link io.spine.server.bus.MessageDispatcher MessageDispatcher}), also + * dispatch events by implementing this interface. + * + *

    Also this interface provides separate methods for obtaining + * {@linkplain #domesticEvents() domestic} and {@linkplain #externalEvents() external} event types. * * @see DelegatingEventDispatcher */ From ea90a4640f57120b524101e2feeebc7bd5788e79 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 Nov 2023 18:36:50 +0000 Subject: [PATCH 31/32] Update dependencies of Windows build workflow --- .github/workflows/build-on-windows.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-on-windows.yml b/.github/workflows/build-on-windows.yml index c910b2009fe..47fefa69164 100644 --- a/.github/workflows/build-on-windows.yml +++ b/.github/workflows/build-on-windows.yml @@ -17,9 +17,9 @@ jobs: java-version: 11 distribution: zulu cache: gradle - + # See: https://github.com/al-cheb/configure-pagefile-action - name: Configure Pagefile - uses: al-cheb/configure-pagefile-action@v1.2 + uses: al-cheb/configure-pagefile-action@v1.3 - name: Build project and run tests shell: cmd @@ -28,7 +28,7 @@ jobs: # See: https://github.com/marketplace/actions/junit-report-action - name: Publish Test Report - uses: mikepenz/action-junit-report@v3.7.6 + uses: mikepenz/action-junit-report@v4.0.3 if: always() # always run even if the previous step fails with: report_paths: '**/build/test-results/**/TEST-*.xml' From 43937de6ff122fa8aa8108404c35a249ff019fb0 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 1 Nov 2023 18:37:28 +0000 Subject: [PATCH 32/32] Update dependency of Ubuntu build workflow --- .github/workflows/build-on-ubuntu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-on-ubuntu.yml b/.github/workflows/build-on-ubuntu.yml index 788b891736b..55bb482f644 100644 --- a/.github/workflows/build-on-ubuntu.yml +++ b/.github/workflows/build-on-ubuntu.yml @@ -24,7 +24,7 @@ jobs: # See: https://github.com/marketplace/actions/junit-report-action - name: Publish Test Report - uses: mikepenz/action-junit-report@v3.7.6 + uses: mikepenz/action-junit-report@v4.0.3 if: always() # always run even if the previous step fails with: report_paths: '**/build/test-results/**/TEST-*.xml'