Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import org.apache.causeway.applib.services.clock.ClockService;
import org.apache.causeway.applib.services.command.CommandExecutorService;
import org.apache.causeway.applib.services.iactn.InteractionService;
import org.apache.causeway.applib.services.repository.RepositoryService;
import org.apache.causeway.applib.services.xactn.TransactionService;
import org.apache.causeway.core.config.util.SpringProfileUtil;
import org.apache.causeway.extensions.commandlog.applib.app.CommandLogMenu;
import org.apache.causeway.extensions.commandlog.applib.contributions.HasInteractionId_commandLogEntry;
Expand All @@ -36,8 +38,14 @@
import org.apache.causeway.extensions.commandlog.applib.dom.mixins.CommandLogEntry_childCommands;
import org.apache.causeway.extensions.commandlog.applib.dom.mixins.CommandLogEntry_openResultObject;
import org.apache.causeway.extensions.commandlog.applib.dom.mixins.CommandLogEntry_siblingCommands;
import org.apache.causeway.extensions.commandlog.applib.dom.replay.CommandExportManager;
import org.apache.causeway.extensions.commandlog.applib.dom.replay.CommandReplayManager;
import org.apache.causeway.extensions.commandlog.applib.dom.replay.ReplayContext;
import org.apache.causeway.extensions.commandlog.applib.dom.replay.ReplayableCommand_delete;
import org.apache.causeway.extensions.commandlog.applib.dom.replay.ReplayableCommand_excludeFromReplay;
import org.apache.causeway.extensions.commandlog.applib.dom.replay.ReplayableCommand_makeExportable;
import org.apache.causeway.extensions.commandlog.applib.dom.replay.ReplayableCommand_openCommandLogEntry;
import org.apache.causeway.extensions.commandlog.applib.dom.replay.ReplayableCommand_replayOrRetry;
import org.apache.causeway.extensions.commandlog.applib.fakescheduler.FakeScheduler;
import org.apache.causeway.extensions.commandlog.applib.job.BackgroundCommandsJobControl;
import org.apache.causeway.extensions.commandlog.applib.job.RunBackgroundCommandsJob;
Expand All @@ -59,6 +67,24 @@
CommandLogEntry_childCommands.class,
CommandLogEntry_openResultObject.class,
CommandLogEntry_siblingCommands.class,
ReplayableCommand_makeExportable.class,
ReplayableCommand_openCommandLogEntry.class,
ReplayableCommand_replayOrRetry.class,
ReplayableCommand_excludeFromReplay.class,
ReplayableCommand_delete.class,
CommandExportManager.changeSince.class,
CommandExportManager.previousHour.class,
CommandExportManager.nextHour.class,
CommandExportManager.exportSelected.class,
CommandExportManager.makeSelectedExportable.class,
CommandReplayManager.changeSince.class,
CommandExportManager.previousHour.class,
CommandReplayManager.nextHour.class,
CommandReplayManager.importCommands.class,
CommandReplayManager.replayOrRetrySelected.class,
CommandReplayManager.excludeSelectedFromReplay.class,
CommandReplayManager.deleteSelectedSucceededOrExcluded.class,
CommandReplayManager.deleteSelectedPendingOrFailed.class,

// @Component's
RunBackgroundCommandsJob.class,
Expand Down Expand Up @@ -119,10 +145,12 @@ public static void honorSystemEnvironment() {
@Bean ReplayContext replayContext(
final RepositoryService repositoryService,
final InteractionService interactionService,
final TransactionService transactionService,
final CommandLogEntryRepository commandLogEntryRepository,
final CommandExecutorService commandExecutorService) {
return new ReplayContext(repositoryService, interactionService,
commandLogEntryRepository, commandExecutorService);
final CommandExecutorService commandExecutorService,
final ClockService clockService) {
return new ReplayContext(repositoryService, interactionService, transactionService,
commandLogEntryRepository, commandExecutorService, clockService);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
*/
package org.apache.causeway.extensions.commandlog.applib.app;

import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.List;

import jakarta.inject.Inject;
Expand All @@ -32,6 +34,7 @@
import org.apache.causeway.applib.annotation.DomainService;
import org.apache.causeway.applib.annotation.DomainServiceLayout;
import org.apache.causeway.applib.annotation.MemberSupport;
import org.apache.causeway.applib.annotation.ParameterLayout;
import org.apache.causeway.applib.annotation.PriorityPrecedence;
import org.apache.causeway.applib.annotation.Publishing;
import org.apache.causeway.applib.annotation.RestrictTo;
Expand Down Expand Up @@ -156,8 +159,19 @@ public class DomainEvent extends ActionDomainEvent<findAll> { }
public class exportManager {
public class DomainEvent extends ActionDomainEvent<exportManager> { }

@MemberSupport public CommandExportManager act() {
return new CommandExportManager(null, replayContext);
@MemberSupport public CommandExportManager act(
@ParameterLayout(
describedAs = "Limits the commands shown; "
+ "only commands since this timestamp are available for export. "
+ "Set to a time immediately before the commands to be replayed.")
final java.sql.Timestamp since
) {
return new CommandExportManager(since, replayContext);
}

@MemberSupport public java.sql.Timestamp defaultSince() {
final var now = clockService.getClock().nowAsJavaSqlTimestamp();
return truncatedTo(now, ChronoUnit.HOURS);
}
}

Expand All @@ -172,11 +186,25 @@ public class DomainEvent extends ActionDomainEvent<exportManager> { }
public class replayManager {
public class DomainEvent extends ActionDomainEvent<replayManager> { }

@MemberSupport public CommandReplayManager act() {
return new CommandReplayManager(null, replayContext);
@MemberSupport public CommandReplayManager act(
@ParameterLayout(
describedAs = "Limits the commands shown; "
+ "only commands since this timestamp are available for replay. "
+ "Set to a time immediately before the commands to be replayed.")
final java.sql.Timestamp since
) {
return new CommandReplayManager(since, replayContext);
}

@MemberSupport public java.sql.Timestamp defaultSince() {
final var now = clockService.getClock().nowAsJavaSqlTimestamp();
return truncatedTo(now, ChronoUnit.HOURS);
}
}

private static Timestamp truncatedTo(final Timestamp now, final ChronoUnit chronoUnit) {
return Timestamp.from(now.toInstant().truncatedTo(chronoUnit));
}

private LocalDate now() {
return clockService.getClock().nowAsLocalDate(ZoneId.systemDefault());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,9 @@ public static class Nq {
* primary before the production database was restored to the secondary.
*/
public static final String FIND_MOST_RECENT_COMPLETED = LOGICAL_TYPE_NAME + ".findMostRecentCompleted";
public static final String FIND_BY_REPLAY_STATE = LOGICAL_TYPE_NAME + ".findNotYetReplayed";
public static final String FIND_FOREGROUND_BY_TIMESTAMP_AFTER_AND_REPLAY_STATE = LOGICAL_TYPE_NAME + ".findForegroundByTimestampAfterAndReplayState";
public static final String FIND_FOREGROUND_BY_TIMESTAMP_AFTER_AND_REPLAY_STATES = LOGICAL_TYPE_NAME + ".findForegroundByTimestampAfterAndReplayStates";

public static final String FIND_BACKGROUND_AND_NOT_YET_STARTED = LOGICAL_TYPE_NAME + ".findBackgroundAndNotYetStarted";
public static final String FIND_RECENT_BACKGROUND_BY_TARGET = LOGICAL_TYPE_NAME + ".findRecentBackgroundByTarget";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.apache.causeway.extensions.commandlog.applib.dom;

import java.sql.Timestamp;
import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -120,6 +121,20 @@ List<CommandLogEntry> findByTargetAndFromAndTo(
*/
List<CommandLogEntry> findSince(UUID interactionId, Integer batchSize);

List<CommandLogEntry> findForegroundSinceTimestampAndCanBeExported(Timestamp since);

List<CommandLogEntry> findForegroundSinceTimestampAndHasBeenExported(Timestamp since);

/**
* Command Replay feature: Can replay or retry.
*/
List<CommandLogEntry> findForegroundSinceTimestampAndWithReplayPendingOrFailed(Timestamp since);

/**
* Command Replay feature: Cannot replay or retry.
*/
List<CommandLogEntry> findSinceAndWithReplayOkOrExcluded(Timestamp since);

/**
* Returns any persisted commands that have not yet started.
*
Expand Down Expand Up @@ -158,15 +173,6 @@ List<CommandLogEntry> findByTargetAndFromAndTo(
*/
Optional<CommandLogEntry> findMostRecentCompleted();

/**
* Command Replay feature: Can replay or retry.
*/
List<CommandLogEntry> findReplayPendingOrFailed();
/**
* Command Replay feature: Cannot replay or retry.
*/
List<CommandLogEntry> findReplaySucceededOrExcluded();

CommandLogEntry saveForReplay(CommandDto dto);

default List<CommandLogEntry> saveForReplay(@Nullable final List<CommandDto> commandDtoList) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,44 +332,13 @@ public Optional<CommandLogEntry> findMostRecentCompleted() {
Query.named(commandLogEntryClass, CommandLogEntry.Nq.FIND_MOST_RECENT_COMPLETED))
);
}
@Override
public List<CommandLogEntry> findReplayPendingOrFailed() {
return _Casts.uncheckedCast(
repositoryService().allMatches(
Query.named(commandLogEntryClass, CommandLogEntry.Nq.FIND_BY_REPLAY_STATE)
.withParameter("replayState1", ReplayState.PENDING)
.withParameter("replayState2", ReplayState.FAILED))
);
}
/**
* Command Replay feature: Cannot replay or retry.
*/
@Override
public List<CommandLogEntry> findReplaySucceededOrExcluded() {
return _Casts.uncheckedCast(
repositoryService().allMatches(
Query.named(commandLogEntryClass, CommandLogEntry.Nq.FIND_BY_REPLAY_STATE)
.withParameter("replayState1", ReplayState.OK)
.withParameter("replayState2", ReplayState.EXCLUDED))
);
}

@Override
public C saveForReplay(final CommandDto commandToReplay) {

//TODO why?
// if(commandToReplay.getMember().getInteractionType() == InteractionType.ACTION_INVOCATION) {
// final MapDto userData = commandToReplay.getUserData();
// if (userData == null )
// throw new IllegalStateException(String.format(
// "Can only persist action DTOs with additional userData; got: \n%s",
// CommandDtoUtils.dtoMapper().toString(commandToReplay)));
// }

final C entity = factoryService.detachedEntity(commandLogEntryClass);
entity.init(commandToReplay, ReplayState.PENDING, 0);
entity.setParentInteractionId(null); // n/a for replay
entity.setExecuteIn(null); // to be specified later depending on user action
entity.setExecuteIn(ExecuteIn.FOREGROUND); // only ever replay foreground commands

persist(entity);

Expand Down Expand Up @@ -425,6 +394,46 @@ private List<CommandLogEntry> findSince(
: commandJdos;
}

@Override
public List<CommandLogEntry> findForegroundSinceTimestampAndCanBeExported(final Timestamp since) {
return findForegroundSinceTimestampWithState(since, ReplayState.UNDEFINED);
}

@Override
public List<CommandLogEntry> findForegroundSinceTimestampAndHasBeenExported(final Timestamp since) {
return findForegroundSinceTimestampWithState(since, ReplayState.EXPORTED);
}

@Override
public List<CommandLogEntry> findForegroundSinceTimestampAndWithReplayPendingOrFailed(final Timestamp since) {
return findForegroundSinceTimestampWithStates(since, ReplayState.PENDING, ReplayState.FAILED);
}

/**
* Command Replay feature: Cannot replay or retry.
*/
@Override
public List<CommandLogEntry> findSinceAndWithReplayOkOrExcluded(final Timestamp since) {
return findForegroundSinceTimestampWithStates(since, ReplayState.OK, ReplayState.EXCLUDED);
}

private List<CommandLogEntry> findForegroundSinceTimestampWithState(final Timestamp from, final ReplayState replayState) {
return _Casts.uncheckedCast(
repositoryService().allMatches(
Query.named(commandLogEntryClass, CommandLogEntry.Nq.FIND_FOREGROUND_BY_TIMESTAMP_AFTER_AND_REPLAY_STATE)
.withParameter("from", from)
.withParameter("replayState", replayState)));
}

private List<CommandLogEntry> findForegroundSinceTimestampWithStates(final Timestamp from, final ReplayState replayState1, final ReplayState replayState2) {
return _Casts.uncheckedCast(
repositoryService().allMatches(
Query.named(commandLogEntryClass, CommandLogEntry.Nq.FIND_FOREGROUND_BY_TIMESTAMP_AFTER_AND_REPLAY_STATES)
.withParameter("from", from)
.withParameter("replayState1", replayState1)
.withParameter("replayState2", replayState2)));
}

private RepositoryService repositoryService() {
return repositoryServiceProvider.get();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,33 +54,40 @@ public enum ReplayState {
public boolean isExported() { return this == EXPORTED; }
public boolean isFailed() { return this == FAILED; }

public boolean canExport() {
public boolean isExportable() {
return this == ReplayState.UNDEFINED;
}

public boolean canReplayOrRetryOrMarkForExclusion() {
public boolean isPendingOrFailed() {
return this == ReplayState.PENDING
|| this == ReplayState.FAILED;
}

public boolean isOkOrExcluded() {
return this == ReplayState.OK
|| this == ReplayState.EXCLUDED;
}

// -- NULL SAFE

public static boolean canExport(final @Nullable ReplayState replayState) {
return replayState!=null
? replayState.canExport()
: true;
public static boolean isExportable(final @Nullable ReplayState replayState) {
return replayState == null
|| replayState.isExportable();
}

public static boolean isExported(final @Nullable ReplayState replayState) {
return replayState != null
&& replayState.isExported();
}

public static boolean canReplayOrRetryOrMarkForExclusion(final @Nullable ReplayState replayState) {
return replayState!=null
? replayState.canReplayOrRetryOrMarkForExclusion()
: false;
public static boolean isPendingOrFailed(final @Nullable ReplayState replayState) {
return replayState != null
&& replayState.isPendingOrFailed();
}

public static boolean isExported(final ReplayState replayState) {
return replayState!=null
? replayState.isExported()
: false;
public static boolean isOkOrExcluded(final @Nullable ReplayState replayState) {
return replayState != null
&& replayState.isOkOrExcluded();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#interactionId
timestamp
targetType
targetId
member
replayState
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#interactionId
timestamp
targetType
targetId
member
replayState
Loading