You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by da...@apache.org on 2023/03/03 18:38:39 UTC
[causeway] 01/01: CAUSEWAY-3366: wip, adding in onReady and onStarted callbacks
This is an automated email from the ASF dual-hosted git repository.
danhaywood pushed a commit to branch CAUSEWAY-3366
in repository https://gitbox.apache.org/repos/asf/causeway.git
commit 356bf204b632a6320601c4de021d4663c25a1371
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Fri Mar 3 18:22:56 2023 +0000
CAUSEWAY-3366: wip, adding in onReady and onStarted callbacks
---
.../causeway/applib/services/command/Command.java | 16 ++++-
.../services/publishing/log/CommandLogger.java | 17 ++++-
.../services/publishing/spi/CommandSubscriber.java | 29 ++++++--
.../interaction/session/CausewayInteraction.java | 19 +++--
.../metamodel/execution/InteractionInternal.java | 3 +
.../publish/command/CommandPublishingFacet.java | 17 -----
.../services/publishing/CommandPublisher.java | 16 ++++-
.../command/CommandExecutorServiceDefault.java | 8 +--
.../executor/MemberExecutorServiceDefault.java | 38 ++++++++--
.../publish/CommandPublisherDefault.java | 40 ++++++++++-
.../core/runtimeservices/publish/_Xray.java | 32 +++++++--
.../session/InteractionServiceDefault.java | 4 +-
.../commandlog/applib/dom/BackgroundService.java | 1 -
.../subscriber/CommandSubscriberForCommandLog.java | 81 +++++++++++++++++++++-
.../subscriber/CommandSubscriberForTesting.java | 27 ++++++--
15 files changed, 288 insertions(+), 60 deletions(-)
diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/command/Command.java b/api/applib/src/main/java/org/apache/causeway/applib/services/command/Command.java
index f03d85f9ca..cab8bbd14a 100644
--- a/api/applib/src/main/java/org/apache/causeway/applib/services/command/Command.java
+++ b/api/applib/src/main/java/org/apache/causeway/applib/services/command/Command.java
@@ -236,18 +236,28 @@ public class Command implements HasInteractionId, HasUsername, HasCommandDto {
public static enum CommandPublishingPhase {
/** initial state: do not publish (yet) */
ONHOLD,
- /** publishing is enabled */
+ /**
+ * publishing is enabled, and the command will be executed.
+ */
READY,
- /** publishing has completed */
+ /**
+ * The command has started to be executed.
+ */
+ STARTED,
+ /**
+ * The command has completed its execution.
+ */
COMPLETED;
public boolean isOnhold() {return this==ONHOLD;}
public boolean isReady() {return this==READY;}
+ public boolean isStarted() {return this==STARTED;}
public boolean isCompleted() {return this==COMPLETED;}
}
/**
* Whether this command has been enabled for publishing,
- * that is {@link CommandSubscriber}s will be notified when this Command completes.
+ * that is {@link CommandSubscriber}s will be notified when this Command becomes {@link CommandPublishingPhase#READY ready},
+ * has {@link CommandPublishingPhase#STARTED started}, and when it {@link CommandPublishingPhase#COMPLETED completes}.
*/
@Getter private CommandPublishingPhase publishingPhase = CommandPublishingPhase.ONHOLD;
diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/publishing/log/CommandLogger.java b/api/applib/src/main/java/org/apache/causeway/applib/services/publishing/log/CommandLogger.java
index ce062b3c73..3760ca8431 100644
--- a/api/applib/src/main/java/org/apache/causeway/applib/services/publishing/log/CommandLogger.java
+++ b/api/applib/src/main/java/org/apache/causeway/applib/services/publishing/log/CommandLogger.java
@@ -53,13 +53,28 @@ public class CommandLogger implements CommandSubscriber {
return log.isDebugEnabled();
}
+ @Override
+ public void onReady(Command command) {
+ on("ready", command);
+ }
+
+ @Override
+ public void onStarted(Command command) {
+ on("started", command);
+ }
+
@Override
public void onCompleted(final Command command) {
+ on("completed", command);
+ }
+
+ private static void on(String verb, Command command) {
val commandDto = command.getCommandDto();
val xml = CommandDtoUtils.dtoMapper().toString(commandDto);
- log.debug("completed: {} \n{}",
+ log.debug("{}: {} \n{}",
+ verb,
command.getLogicalMemberIdentifier(),
xml);
}
diff --git a/api/applib/src/main/java/org/apache/causeway/applib/services/publishing/spi/CommandSubscriber.java b/api/applib/src/main/java/org/apache/causeway/applib/services/publishing/spi/CommandSubscriber.java
index b412dbcc38..f4ca48fd65 100644
--- a/api/applib/src/main/java/org/apache/causeway/applib/services/publishing/spi/CommandSubscriber.java
+++ b/api/applib/src/main/java/org/apache/causeway/applib/services/publishing/spi/CommandSubscriber.java
@@ -22,19 +22,40 @@ import org.apache.causeway.applib.services.command.Command;
import org.apache.causeway.commons.having.HasEnabling;
/**
- * Part of the <i>Publishing SPI</i>. A component to receive {@link Command}s
+ * Part of the <i>Publishing SPI</i>. A component to receive {@link Command}s
* (with publishing enabled) that just completed.
- *
+ *
* @since 2.0 {@index}
*/
public interface CommandSubscriber extends HasEnabling {
/**
- * Notifies that the command has completed.
+ * Notifies that the command will be published, and has transitioned to {@link org.apache.causeway.applib.services.command.Command.CommandPublishingPhase#READY}.
+ *
+ * <p>
+ * This is an opportunity for implementations to process the command,
+ * for example to persist an initial representation of it.
+ * </p>
+ */
+ void onReady(Command command);
+
+ /**
+ * Notifies that the command has started to execute, and has transitioned to {@link org.apache.causeway.applib.services.command.Command.CommandPublishingPhase#STARTED}.
+ *
+ * <p>
+ * This is an opportunity for implementations to process the command,
+ * for example to update any persisted representation of it.
+ * </p>
+ */
+ void onStarted(Command command);
+
+
+ /**
+ * Notifies that the command has completed and has transitioned to {@link org.apache.causeway.applib.services.command.Command.CommandPublishingPhase#COMPLETED}
*
* <p>
* This is an opportunity for implementations to process the command,
- * for example to persist a representation of it.
+ * for example to update any persisted representations of it.
* </p>
*/
void onCompleted(Command command);
diff --git a/core/interaction/src/main/java/org/apache/causeway/core/interaction/session/CausewayInteraction.java b/core/interaction/src/main/java/org/apache/causeway/core/interaction/session/CausewayInteraction.java
index 9d3b12aa28..c8fc97a9f0 100644
--- a/core/interaction/src/main/java/org/apache/causeway/core/interaction/session/CausewayInteraction.java
+++ b/core/interaction/src/main/java/org/apache/causeway/core/interaction/session/CausewayInteraction.java
@@ -36,6 +36,7 @@ import org.apache.causeway.commons.internal.base._Casts;
import org.apache.causeway.commons.internal.collections._Lists;
import org.apache.causeway.commons.internal.exceptions._Exceptions;
import org.apache.causeway.core.metamodel.execution.InteractionInternal;
+import org.apache.causeway.core.metamodel.services.publishing.CommandPublisher;
import lombok.Getter;
import lombok.NonNull;
@@ -99,9 +100,10 @@ implements InteractionInternal {
final ActionInvocation actionInvocation,
final ClockService clockService,
final MetricsService metricsService,
+ final CommandPublisher commandPublisher,
final Command command) {
- pushAndStart(actionInvocation, clockService, metricsService, command);
+ pushAndStart(actionInvocation, clockService, metricsService, commandPublisher, command);
try {
return executeInternal(memberExecutor, actionInvocation);
} finally {
@@ -109,9 +111,14 @@ implements InteractionInternal {
}
}
- private void pushAndStart(final ActionInvocation actionInvocation, final ClockService clockService, final MetricsService metricsService, final Command command) {
+ private void pushAndStart(
+ final ActionInvocation actionInvocation,
+ final ClockService clockService,
+ final MetricsService metricsService,
+ final CommandPublisher commandPublisher,
+ final Command command) {
push(actionInvocation);
- start(actionInvocation, clockService, metricsService, command);
+ start(actionInvocation, clockService, metricsService, commandPublisher, command);
}
@Override
@@ -120,10 +127,11 @@ implements InteractionInternal {
final PropertyEdit propertyEdit,
final ClockService clockService,
final MetricsService metricsService,
+ final CommandPublisher commandPublisher,
final Command command) {
push(propertyEdit);
- start(propertyEdit, clockService, metricsService, command);
+ start(propertyEdit, clockService, metricsService, commandPublisher, command);
try {
return executeInternal(memberExecutor, propertyEdit);
} finally {
@@ -186,6 +194,7 @@ implements InteractionInternal {
final Execution<?,?> execution,
final ClockService clockService,
final MetricsService metricsService,
+ final CommandPublisher commandPublisher,
final Command command) {
// set the startedAt (and update command if this is the top-most member execution)
// (this isn't done within Interaction#execute(...) because it requires the DTO
@@ -193,7 +202,9 @@ implements InteractionInternal {
val startedAt = execution.start(clockService, metricsService);
if(command.getStartedAt() == null) {
command.updater().setStartedAt(startedAt);
+ command.updater().setPublishingPhase(Command.CommandPublishingPhase.STARTED);
}
+ commandPublisher.start(command);
}
/**
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/execution/InteractionInternal.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/execution/InteractionInternal.java
index d10a6f860f..bf67058c81 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/execution/InteractionInternal.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/execution/InteractionInternal.java
@@ -29,6 +29,7 @@ import org.apache.causeway.applib.services.iactn.Interaction;
import org.apache.causeway.applib.services.iactn.PropertyEdit;
import org.apache.causeway.applib.services.metrics.MetricsService;
import org.apache.causeway.applib.services.wrapper.WrapperFactory;
+import org.apache.causeway.core.metamodel.services.publishing.CommandPublisher;
import lombok.NonNull;
import lombok.val;
@@ -61,6 +62,7 @@ extends Interaction {
final ActionInvocation actionInvocation,
final ClockService clockService,
final MetricsService metricsService,
+ final CommandPublisher commandPublisher,
final Command command);
/**
@@ -77,6 +79,7 @@ extends Interaction {
final PropertyEdit propertyEdit,
final ClockService clockService,
final MetricsService metricsService,
+ final CommandPublisher commandPublisher,
final Command command);
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/command/CommandPublishingFacet.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/command/CommandPublishingFacet.java
index dfaf4c2ce1..58d58db465 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/command/CommandPublishingFacet.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/command/CommandPublishingFacet.java
@@ -49,21 +49,4 @@ public interface CommandPublishingFacet extends Facet {
public static boolean isPublishingEnabled(final @NonNull FacetHolder facetHolder) {
return facetHolder.containsFacet(CommandPublishingFacet.class);
}
-
- /**
- * Will set the command's CommandPublishingPhase to READY,
- * if command and objectMember have a matching member-id
- * and if the facetHoler has a CommandPublishingFacet (has commandPublishing=ENABLED).
- */
- public static void prepareCommandForPublishing(
- final @NonNull Command command,
- final @NonNull InteractionHead interactionHead,
- final @NonNull ObjectMember objectMember,
- final @NonNull FacetHolder facetHolder) {
-
- if(IdentifierUtil.isCommandForMember(command, interactionHead, objectMember)
- && isPublishingEnabled(facetHolder)) {
- command.updater().setPublishingPhase(CommandPublishingPhase.READY);
- }
- }
}
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/publishing/CommandPublisher.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/publishing/CommandPublisher.java
index d8fd9e2bf8..cecd04379c 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/publishing/CommandPublisher.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/publishing/CommandPublisher.java
@@ -30,8 +30,20 @@ import lombok.NonNull;
public interface CommandPublisher {
/**
- * "Completes" the command, meaning that all {@link CommandSubscriber}s
- * are notified throuhg {@link CommandSubscriber#onCompleted(Command)}.
+ * Notifies all {@link CommandSubscriber}s (through {@link CommandSubscriber#onReady(Command)}) that the
+ * {@link Command} has been created/is ready for execution
+ */
+ void ready(@NonNull Command command);
+
+ /**
+ * Notifies all {@link CommandSubscriber}s (through {@link CommandSubscriber#onStarted(Command)}) that the
+ * {@link Command} has started.
+ */
+ void start(@NonNull Command command);
+
+ /**
+ * Notifies all {@link CommandSubscriber}s (through {@link CommandSubscriber#onCompleted(Command)}) that the
+ * {@link Command} has completed.
*/
void complete(@NonNull Command command);
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/command/CommandExecutorServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/command/CommandExecutorServiceDefault.java
index 115d1f38be..5b64abe069 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/command/CommandExecutorServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/command/CommandExecutorServiceDefault.java
@@ -38,7 +38,6 @@ import org.apache.causeway.applib.services.command.Command;
import org.apache.causeway.applib.services.command.CommandExecutorService;
import org.apache.causeway.applib.services.command.CommandOutcomeHandler;
import org.apache.causeway.applib.services.iactnlayer.InteractionLayerTracker;
-import org.apache.causeway.applib.services.iactnlayer.InteractionService;
import org.apache.causeway.applib.services.sudo.SudoService;
import org.apache.causeway.applib.services.xactn.TransactionService;
import org.apache.causeway.applib.util.schema.CommandDtoUtils;
@@ -82,10 +81,9 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
@Inject final SudoService sudoService;
@Inject final ClockService clockService;
@Inject final TransactionService transactionService;
- @Inject final InteractionLayerTracker iInteractionLayerTracker;
+ @Inject final InteractionLayerTracker interactionLayerTracker;
@Inject final SchemaValueMarshaller valueMarshaller;
- @Inject @Getter final InteractionService interactionService;
@Inject @Getter final SpecificationLoader specificationLoader;
@Override
@@ -123,7 +121,7 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
final CommandDto dto,
final CommandOutcomeHandler commandOutcomeHandler) {
- val interaction = iInteractionLayerTracker.currentInteractionElseFail();
+ val interaction = interactionLayerTracker.currentInteractionElseFail();
val command = interaction.getCommand();
if(command.getCommandDto() != dto) {
command.updater().setCommandDto(dto);
@@ -171,7 +169,7 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
private void copyStartedAtFromInteractionExecution(
final CommandOutcomeHandler commandOutcomeHandler) {
- val interaction = iInteractionLayerTracker.currentInteractionElseFail();
+ val interaction = interactionLayerTracker.currentInteractionElseFail();
val currentExecution = interaction.getCurrentExecution();
val startedAt = currentExecution != null
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/executor/MemberExecutorServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/executor/MemberExecutorServiceDefault.java
index 30f4cbae10..12e41e74d2 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/executor/MemberExecutorServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/executor/MemberExecutorServiceDefault.java
@@ -18,7 +18,6 @@
*/
package org.apache.causeway.core.runtimeservices.executor;
-import java.lang.reflect.Method;
import java.util.Optional;
import javax.annotation.Priority;
@@ -26,6 +25,9 @@ import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
+import org.apache.causeway.core.metamodel.facets.actions.action.invocation.IdentifierUtil;
+import org.apache.causeway.core.metamodel.services.publishing.CommandPublisher;
+import org.apache.causeway.core.metamodel.spec.feature.ObjectMember;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@@ -51,7 +53,6 @@ import org.apache.causeway.core.metamodel.consent.InteractionInitiatedBy;
import org.apache.causeway.core.metamodel.execution.InteractionInternal;
import org.apache.causeway.core.metamodel.execution.MemberExecutorService;
import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
-import org.apache.causeway.core.metamodel.facets.members.publish.command.CommandPublishingFacet;
import org.apache.causeway.core.metamodel.facets.members.publish.execution.ExecutionPublishingFacet;
import org.apache.causeway.core.metamodel.facets.properties.property.modify.PropertySetterOrClearFacetForDomainEventAbstract.EditingVariant;
import org.apache.causeway.core.metamodel.interactions.InteractionHead;
@@ -77,6 +78,8 @@ import lombok.SneakyThrows;
import lombok.val;
import lombok.extern.log4j.Log4j2;
+import static org.apache.causeway.core.metamodel.facets.members.publish.command.CommandPublishingFacet.isPublishingEnabled;
+
@Service
@Named(CausewayModuleCoreRuntimeServices.NAMESPACE + ".MemberExecutorServiceDefault")
@Priority(PriorityPrecedence.EARLY)
@@ -96,6 +99,7 @@ implements MemberExecutorService {
private final @Getter Provider<ExecutionPublisher> executionPublisherProvider;
private final @Getter MetamodelEventService metamodelEventService;
private final @Getter TransactionService transactionService;
+ private final Provider<CommandPublisher> commandPublisherProvider;
private MetricsService metricsService() {
return metricsServiceProvider.get();
@@ -137,7 +141,7 @@ implements MemberExecutorService {
val interaction = getInteractionElseFail();
val command = interaction.getCommand();
- CommandPublishingFacet.prepareCommandForPublishing(command, head, owningAction, facetHolder);
+ prepareCommandForPublishing(command, head, owningAction, facetHolder);
val xrayHandle = _Xray.enterActionInvocation(interactionLayerTracker, interaction, owningAction, head, argumentAdapters);
@@ -156,7 +160,7 @@ implements MemberExecutorService {
val memberExecutor = actionExecutorFactory.createExecutor(owningAction, head, argumentAdapters);
// sets up startedAt and completedAt on the execution, also manages the execution call graph
- interaction.execute(memberExecutor, actionInvocation, clockService, metricsService(), command);
+ interaction.execute(memberExecutor, actionInvocation, clockService, metricsService(), commandPublisherProvider.get(), command);
// handle any exceptions
val priorExecution = interaction.getPriorExecutionOrThrowIfAnyException(actionInvocation);
@@ -195,6 +199,7 @@ implements MemberExecutorService {
return result;
}
+
@Override
public ManagedObject setOrClearProperty(
final @NonNull OneToOneAssociation owningProperty,
@@ -211,7 +216,7 @@ implements MemberExecutorService {
return head.getTarget();
}
- CommandPublishingFacet.prepareCommandForPublishing(command, head, owningProperty, facetHolder);
+ prepareCommandForPublishing(command, head, owningProperty, facetHolder);
val xrayHandle = _Xray.enterPropertyEdit(interactionLayerTracker, interaction, owningProperty, head, newValueAdapter);
@@ -227,7 +232,7 @@ implements MemberExecutorService {
interactionInitiatedBy, editingVariant);
// sets up startedAt and completedAt on the execution, also manages the execution call graph
- val targetPojo = interaction.execute(executor, propertyEdit, clockService, metricsService(), command);
+ val targetPojo = interaction.execute(executor, propertyEdit, clockService, metricsService(), commandPublisherProvider.get(), command);
// handle any exceptions
final Execution<?, ?> priorExecution = interaction.getPriorExecution();
@@ -313,4 +318,25 @@ implements MemberExecutorService {
: null;
}
+
+ /**
+ * Will set the command's CommandPublishingPhase to READY,
+ * if command and objectMember have a matching member-id
+ * and if the facetHolder has a CommandPublishingFacet (has commandPublishing=ENABLED).
+ */
+ void prepareCommandForPublishing(
+ final @NonNull Command command,
+ final @NonNull InteractionHead interactionHead,
+ final @NonNull ObjectMember objectMember,
+ final @NonNull FacetHolder facetHolder) {
+
+ if(IdentifierUtil.isCommandForMember(command, interactionHead, objectMember)
+ && isPublishingEnabled(facetHolder)) {
+ command.updater().setPublishingPhase(Command.CommandPublishingPhase.READY);
+ }
+
+ commandPublisherProvider.get().ready(command);
+
+ }
+
}
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/CommandPublisherDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/CommandPublisherDefault.java
index e56288f116..c3f2836f52 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/CommandPublisherDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/CommandPublisherDefault.java
@@ -64,17 +64,53 @@ public class CommandPublisherDefault implements CommandPublisher {
.filter(HasEnabling::isEnabled);
}
+ @Override
+ public void ready(@NonNull Command command) {
+
+ val handle = _Xray.enterCommandReadyPublishing(
+ interactionServiceProvider.get(),
+ command,
+ enabledSubscribers,
+ ()->getCannotPublishReason(command));
+
+ if(canPublish(command)) {
+ log.debug("about to PUBLISH command {}: {} to {}", "ready", command, enabledSubscribers);
+ enabledSubscribers.forEach(subscriber -> subscriber.onReady(command));
+ command.updater().setPublishingPhase(CommandPublishingPhase.READY);
+ }
+
+ _Xray.exitPublishing(handle);
+ }
+
+ @Override
+ public void start(@NonNull Command command) {
+
+ val handle = _Xray.enterCommandStartedPublishing(
+ interactionServiceProvider.get(),
+ command,
+ enabledSubscribers,
+ ()->getCannotPublishReason(command));
+
+ if(canPublish(command)) {
+ log.debug("about to PUBLISH command {}: {} to {}", "started", command, enabledSubscribers);
+ enabledSubscribers.forEach(subscriber -> subscriber.onStarted(command));
+ command.updater().setPublishingPhase(CommandPublishingPhase.STARTED);
+ }
+
+ _Xray.exitPublishing(handle);
+ }
+
@Override
public void complete(final @NonNull Command command) {
- val handle = _Xray.enterCommandPublishing(
+ val handle = _Xray.enterCommandCompletedPublishing(
interactionServiceProvider.get(),
command,
enabledSubscribers,
()->getCannotPublishReason(command));
if(canPublish(command)) {
- log.debug("about to PUBLISH command: {} to {}", command, enabledSubscribers);
+ log.debug("about to PUBLISH command {}: {} to {}", "completed", command, enabledSubscribers);
enabledSubscribers.forEach(subscriber -> subscriber.onCompleted(command));
command.updater().setPublishingPhase(CommandPublishingPhase.COMPLETED); // one shot
}
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/_Xray.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/_Xray.java
index edbd162bc2..a4789bf742 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/_Xray.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/publish/_Xray.java
@@ -47,12 +47,36 @@ final class _Xray {
// -- COMMAND
- static SequenceHandle enterCommandPublishing(
+ static SequenceHandle enterCommandReadyPublishing(
final @NonNull InteractionLayerTracker iaTracker,
final @Nullable Command command,
final @NonNull Can<CommandSubscriber> enabledSubscribers,
final @NonNull Supplier<String> cannotPublishReasonSupplier) {
+ return enterCommandPublishing(iaTracker, command, enabledSubscribers, cannotPublishReasonSupplier, "created");
+ }
+
+ static SequenceHandle enterCommandStartedPublishing(
+ final @NonNull InteractionLayerTracker iaTracker,
+ final @Nullable Command command,
+ final @NonNull Can<CommandSubscriber> enabledSubscribers,
+ final @NonNull Supplier<String> cannotPublishReasonSupplier) {
+
+ return enterCommandPublishing(iaTracker, command, enabledSubscribers, cannotPublishReasonSupplier, "started");
+
+ }
+
+ static SequenceHandle enterCommandCompletedPublishing(
+ final @NonNull InteractionLayerTracker iaTracker,
+ final @Nullable Command command,
+ final @NonNull Can<CommandSubscriber> enabledSubscribers,
+ final @NonNull Supplier<String> cannotPublishReasonSupplier) {
+
+ return enterCommandPublishing(iaTracker, command, enabledSubscribers, cannotPublishReasonSupplier, "completed");
+ }
+
+
+ private static SequenceHandle enterCommandPublishing(InteractionLayerTracker iaTracker, Command command, Can<CommandSubscriber> enabledSubscribers, Supplier<String> cannotPublishReasonSupplier, String verb) {
if(!XrayUi.isXrayEnabled()) {
return null;
}
@@ -60,10 +84,11 @@ final class _Xray {
val cannotPublishReason = cannotPublishReasonSupplier.get();
val canPublish = cannotPublishReason==null;
val enteringLabel = canPublish
- ? String.format("publishing command to %d subscriber(s):\n%s",
+ ? String.format("publishing command %s to %d subscriber(s):\n%s",
+ verb,
enabledSubscribers.size(),
toText(command))
- : String.format("not publishing command:\n%s", cannotPublishReason);
+ : String.format("not publishing command %s:\n%s", verb, cannotPublishReason);
val handleIfAny = XrayUtil.createSequenceHandle(iaTracker, "cmd-publisher");
handleIfAny.ifPresent(handle->{
@@ -85,7 +110,6 @@ final class _Xray {
});
return handleIfAny.orElse(null);
-
}
// -- EXECUTION
diff --git a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/session/InteractionServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/session/InteractionServiceDefault.java
index f7bcfafcec..79b517cd9e 100644
--- a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/session/InteractionServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/session/InteractionServiceDefault.java
@@ -19,7 +19,6 @@
package org.apache.causeway.core.runtimeservices.session;
import java.io.File;
-import java.sql.Timestamp;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -374,6 +373,7 @@ implements
}
private void preInteractionClosed(final CausewayInteraction interaction) {
+
completeAndPublishCurrentCommand();
RuntimeException flushException = null;
@@ -441,7 +441,7 @@ implements
// the guard is in case we're here as the result of a redirect following a previous exception;just ignore.
val priorInteractionExecution = interaction.getPriorExecution();
- final Timestamp completedAt =
+ val completedAt =
priorInteractionExecution != null
?
// copy over from the most recent (which will be the top-level) interaction
diff --git a/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/dom/BackgroundService.java b/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/dom/BackgroundService.java
index 7af674425a..5eba5af1d2 100644
--- a/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/dom/BackgroundService.java
+++ b/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/dom/BackgroundService.java
@@ -112,7 +112,6 @@ public class BackgroundService {
public static class PersistCommandExecutorService implements ExecutorService {
@Inject CommandLogEntryRepository<? extends CommandLogEntry> commandLogEntryRepository;
- @Inject InteractionService interactionService;
private final static JavaSqlJaxbAdapters.TimestampToXMLGregorianCalendarAdapter gregorianCalendarAdapter = new JavaSqlJaxbAdapters.TimestampToXMLGregorianCalendarAdapter();;
diff --git a/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/subscriber/CommandSubscriberForCommandLog.java b/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/subscriber/CommandSubscriberForCommandLog.java
index 50f8c56a78..13f48d5b5e 100644
--- a/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/subscriber/CommandSubscriberForCommandLog.java
+++ b/extensions/core/commandlog/applib/src/main/java/org/apache/causeway/extensions/commandlog/applib/subscriber/CommandSubscriberForCommandLog.java
@@ -22,6 +22,9 @@ import javax.annotation.Priority;
import javax.inject.Inject;
import javax.inject.Named;
+import org.apache.causeway.applib.services.clock.ClockService;
+import org.apache.causeway.applib.services.repository.RepositoryService;
+import org.apache.causeway.schema.cmd.v2.CommandDto;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@@ -57,7 +60,9 @@ public class CommandSubscriberForCommandLog implements CommandSubscriber {
static final String LOGICAL_TYPE_NAME = CausewayModuleExtCommandLogApplib.NAMESPACE + ".CommandSubscriberForCommandLog";
final CommandLogEntryRepository<? extends CommandLogEntry> commandLogEntryRepository;
+ final RepositoryService repositoryService;
final CausewayConfiguration causewayConfiguration;
+ final ClockService clockService;
@Override
public boolean isEnabled() {
@@ -65,8 +70,7 @@ public class CommandSubscriberForCommandLog implements CommandSubscriber {
}
@Override
- public void onCompleted(final Command command) {
-
+ public void onReady(Command command) {
if (!isEnabled()) {
return;
}
@@ -74,6 +78,7 @@ public class CommandSubscriberForCommandLog implements CommandSubscriber {
val existingCommandLogEntryIfAny =
commandLogEntryRepository.findByInteractionId(command.getInteractionId());
if(existingCommandLogEntryIfAny.isPresent()) {
+
val commandLogEntry = existingCommandLogEntryIfAny.get();
switch (commandLogEntry.getExecuteIn()) {
case FOREGROUND:
@@ -95,10 +100,80 @@ public class CommandSubscriberForCommandLog implements CommandSubscriber {
// need to do anything else.
break;
}
+
} else {
- val parentInteractionId = command.getParentInteractionId();
+ val parentInteractionId = command.getParentInteractionId(); // will be null in most (all?) cases
commandLogEntryRepository.createEntryAndPersist(command, parentInteractionId, ExecuteIn.FOREGROUND);
}
+
+ }
+
+ @Override
+ public void onStarted(Command command) {
+ if (!isEnabled()) {
+ return;
+ }
+
+ val existingCommandLogEntryIfAny =
+ commandLogEntryRepository.findByInteractionId(command.getInteractionId());
+ existingCommandLogEntryIfAny.ifPresent(commandLogEntry -> {
+ commandLogEntry.setStartedAt(clockService.getClock().nowAsJavaSqlTimestamp());
+ });
+
+ }
+
+ @Override
+ public void onCompleted(final Command command) {
+
+ if (!isEnabled()) {
+ return;
+ }
+
+ val existingCommandLogEntryIfAny =
+ commandLogEntryRepository.findByInteractionId(command.getInteractionId());
+
+ val onlyIfSystemChanged = causewayConfiguration.getExtensions().getCommandLog().getPublishPolicy().isOnlyIfSystemChanged();
+ if (onlyIfSystemChanged && !command.isSystemStateChanged()) {
+
+ // we don't need the CommandLogEntry after all.
+ existingCommandLogEntryIfAny.ifPresent(repositoryService::remove);
+
+ } else {
+
+ existingCommandLogEntryIfAny.ifPresent(commandLogEntry -> {
+ CommandDto commandDto = commandLogEntry.getCommandDto();
+ commandLogEntry
+ .setResult(null);
+ });
+ if(existingCommandLogEntryIfAny.isPresent()) {
+ val commandLogEntry = existingCommandLogEntryIfAny.get();
+ switch (commandLogEntry.getExecuteIn()) {
+ case FOREGROUND:
+ // this isn't expected to happen ... we just log the fact if it does
+ if(log.isWarnEnabled()) {
+ val existingCommandDto = existingCommandLogEntryIfAny.get().getCommandDto();
+
+ val existingCommandDtoXml = Try.call(()->CommandDtoUtils.dtoMapper().toString(existingCommandDto))
+ .getValue().orElse("Dto to Xml failure");
+ val commandDtoXml = Try.call(()->CommandDtoUtils.dtoMapper().toString(command.getCommandDto()))
+ .getValue().orElse("Dto to Xml failure");
+
+ log.warn("existing: \n{}", existingCommandDtoXml);
+ log.warn("proposed: \n{}", commandDtoXml);
+ }
+ break;
+ case BACKGROUND:
+ // this is expected behaviour; the command was already persisted when initially scheduled; we don't
+ // need to do anything else.
+ break;
+ }
+ } else {
+ val parentInteractionId = command.getParentInteractionId();
+ commandLogEntryRepository.createEntryAndPersist(command, parentInteractionId, ExecuteIn.FOREGROUND);
+ }
+
+ }
+
}
}
diff --git a/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/publishing/subscriber/CommandSubscriberForTesting.java b/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/publishing/subscriber/CommandSubscriberForTesting.java
index 6f78b4d89f..8940bc1d53 100644
--- a/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/publishing/subscriber/CommandSubscriberForTesting.java
+++ b/regressiontests/stable/src/main/java/org/apache/causeway/testdomain/publishing/subscriber/CommandSubscriberForTesting.java
@@ -53,17 +53,32 @@ implements CommandSubscriber {
log.info("about to initialize");
}
+ @Override
+ public void onReady(Command command) {
+ on("readyCommands", command);
+ log.debug("publish ready command {}", ()->command.getCommandDto());
+ }
+
+ @Override
+ public void onStarted(Command command) {
+ on("startedCommands", command);
+ log.debug("publish started command {}", ()->command.getCommandDto());
+ }
+
@Override
public void onCompleted(Command command) {
+ on("completedCommands", command);
+ log.debug("publish completed command {}", ()->command.getCommandDto());
+ }
+
+ private void on(String verb, Command command) {
@SuppressWarnings("unchecked")
- val publishedCommands =
- (List<Command>) kvStore.get(this, "publishedCommands").orElseGet(ArrayList::new);
+ val commands = (List<Command>) kvStore.get(this, verb).orElseGet(ArrayList::new);
- publishedCommands.add(command);
+ commands.add(command);
- kvStore.put(this, "publishedCommands", publishedCommands);
- log.debug("publish command {}", ()->command.getCommandDto());
+ kvStore.put(this, verb, commands);
}
// -- UTILITIES
@@ -71,7 +86,7 @@ implements CommandSubscriber {
@SuppressWarnings("unchecked")
public static Can<Command> getPublishedCommands(KVStoreForTesting kvStore) {
return Can.ofCollection(
- (List<Command>) kvStore.get(CommandSubscriberForTesting.class, "publishedCommands")
+ (List<Command>) kvStore.get(CommandSubscriberForTesting.class, "completedCommands")
.orElse(null));
}