You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2022/07/12 14:05:37 UTC

[isis] 01/01: ISIS-3002: adds jdo impl for ExecutionLogEntry

This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch ISIS-3002
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 78ffa60905531807cef3273e978e1217678e2d0d
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Tue Jul 12 15:04:52 2022 +0100

    ISIS-3002: adds jdo impl for ExecutionLogEntry
    
    also:
    * adds UUID as type/attribute converter; simplify persistence model for CommandLogEntry and ExecutionLogEntry
    * rationalize imports for both command log and execution log (in modules)
    * copies over some useful scripts
    * adds timeline extension
---
 .../applib/mixins/system/HasInteractionId.java     |  20 +-
 build.sh                                           | 190 ++++++++++++++++++
 dependency-graph.sh                                |  10 +
 examples/demo/domain/pom.xml                       |   2 +-
 examples/demo/pom.xml                              |   2 +-
 .../applib/IsisModuleExtCommandLogApplib.java      |   7 +
 ...mandLogServiceMenu.java => CommandLogMenu.java} |   8 +-
 .../commandlog/applib/dom/CommandLogEntry.java     |  32 +--
 .../applib/dom/CommandLogEntryRepository.java      |  21 +-
 .../commandlog/jdo/IsisModuleExtCommandLogJdo.java |  12 --
 .../commandlog/jdo/dom/CommandLogEntry.java        |  21 +-
 .../jdo/dom/CommandLogEntryRepository.java         |   1 -
 .../commandlog/jpa/IsisModuleExtCommandLogJpa.java |   9 +-
 .../commandlog/jpa/dom/CommandLogEntry.java        |  22 ++-
 .../jpa/dom/CommandLogEntryRepository.java         |   1 -
 .../applib/IsisModuleExtExecutionLogApplib.java    |   9 +
 .../executionlog/applib/app/ExecutionLogMenu.java  |  69 +++++++
 .../executionlog/applib/dom/ExecutionLogEntry.java |  67 ++++---
 .../persistence-jdo/logging-dn-enhance.properties  |  48 +++++
 .../core/executionlog/persistence-jdo/pom.xml      |   6 +-
 .../jdo/IsisModuleExtExecutionLogJdo.java}         |  29 +--
 .../executionlog/jdo/dom/ExecutionLogEntry.java    | 218 +++++++++++++++++++++
 pom.xml                                            |  18 +-
 serve-timeline.sh                                  |   6 +
 24 files changed, 681 insertions(+), 147 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/mixins/system/HasInteractionId.java b/api/applib/src/main/java/org/apache/isis/applib/mixins/system/HasInteractionId.java
index 07b6111480..681b9559e2 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/mixins/system/HasInteractionId.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/mixins/system/HasInteractionId.java
@@ -41,30 +41,18 @@ import org.apache.isis.applib.services.iactn.Interaction;
 public interface HasInteractionId {
 
     @Property(
-            hidden = Where.EVERYWHERE,
-            maxLength = InteractionIdStr.MAX_LENGTH
+            editing = Editing.DISABLED,
+            maxLength = InteractionId.MAX_LENGTH
     )
     @Parameter(
-            maxLength = InteractionIdStr.MAX_LENGTH
-    )
-    @Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
-    @Retention(RetentionPolicy.RUNTIME)
-    @interface InteractionIdStr {
-        int MAX_LENGTH = 36;
-        boolean NULLABLE = InteractionId.NULLABLE;
-        String ALLOWS_NULL = InteractionId.ALLOWS_NULL;
-        String NAME = "interactionId";
-    }
-
-
-    @Property(
-            editing = Editing.DISABLED
+            maxLength = InteractionId.MAX_LENGTH
     )
     @java.lang.annotation.Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
     @Retention(RetentionPolicy.RUNTIME)
     public @interface InteractionId {
         boolean NULLABLE = false;
         String ALLOWS_NULL = "false";
+        int MAX_LENGTH = 36;
     }
 
 
diff --git a/build.sh b/build.sh
new file mode 100644
index 0000000000..e4acb48cd8
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,190 @@
+#!/usr/bin/env bash
+
+#
+# prereq for '-t' flag
+#
+# git clone https://gitlab.com/ecpnv.devops/infrastructure/maven-timeline.git
+# mvn clean install
+#
+#
+# see serve-timeline.sh to serve up the generated website (requires JDK18)
+#
+
+BASENAME_0=$(basename $0)
+
+usage() {
+ echo ""                                                                                               >&2
+ echo "$BASENAME_0 options:"                                                                           >&2
+ echo ""                                                                                               >&2
+ echo "  -p run 'git pull --ff-only' first"                                                            >&2
+ echo "  -c include 'clean' goal"                                                                      >&2
+ echo "  -s specify settings"                                                                          >&2
+ echo "  -t add '-Dmaven-timeline.version=1.8-SNAPSHOT' for improved timeline output"                  >&2
+ echo "  -y use 'verify' rather than 'install'.  Cannot combine with '-k'"                             >&2
+ echo "  -k use 'package' rather than 'install'.  Does not run integ tests.  Cannot combine with '-y'" >&2
+ echo "  -O do NOT add '-o' (offline) flag, ie bring down any new dependencies"                        >&2
+ echo "  -F do NOT search for Failures and Errors at the end"                                          >&2
+ echo "  -S do NOT print summary or last 50 lines at the end"                                          >&2
+ echo "  -w whatif - don't run the command but do print it out.  Implies -v (verbose)"                 >&2
+ echo "  -v verbose"                                                                                   >&2
+ echo ""                                                                                               >&2
+}
+
+GIT_PULL=false
+CLEAN=false
+SETTINGS=false
+TIMELINE=false
+SKIP_OFFLINE=false
+PACKAGE_ONLY=false
+VERIFY_ONLY=false
+WHATIF=false
+SINGLE_THREADED=false
+SKIP_SEARCH_FOR_FAILURES=false
+SKIP_SUMMARY=false
+VERBOSE=false
+
+MVN_LOG=/tmp/$BASENAME_0.$$.log
+
+while getopts 'prcstlkyOFSwvh' opt
+do
+  case $opt in
+    p) export GIT_PULL=true ;;
+    c) export CLEAN=true ;;
+    s) export SETTINGS=true ;;
+    t) export TIMELINE=true ;;
+    O) export SKIP_OFFLINE=true ;;
+    l) export SINGLE_THREADED=true ;;
+    k) export PACKAGE_ONLY=true ;;
+    y) export VERIFY_ONLY=true ;;
+    F) export SKIP_SEARCH_FOR_FAILURES=true ;;
+    S) export SKIP_SUMMARY=true ;;
+    w) export WHATIF=true ;;
+    v) export VERBOSE=true ;;
+    h) usage
+       exit 1
+       ;;
+    *) echo "unknown option $opt - aborting" >&2
+       usage
+       exit 1
+      ;;
+  esac
+done
+
+shift $((OPTIND-1))
+
+echo ""
+
+if [ "$VERBOSE" = "true" ]; then
+  echo "-p GIT_PULL                 : $GIT_PULL"
+  echo "-c CLEAN                    : $CLEAN"
+  echo "-s SETTINGS                 : $SETTINGS"
+  echo "-t TIMELINE                 : $TIMELINE"
+  echo "-l SINGLE_THREADED          : $SINGLE_THREADED"
+  echo "-k PACKAGE_ONLY             : $PACKAGE_ONLY"
+  echo "-y VERIFY_ONLY              : $VERIFY_ONLY"
+  echo "-O SKIP_OFFLINE             : $SKIP_OFFLINE"
+  echo "-F SKIP_SEARCH_FOR_FAILURES : $SKIP_SEARCH_FOR_FAILURES"
+  echo "-S SKIP_SUMMARY             : $SKIP_SUMMARY"
+  echo "-w WHATIF                   : $WHATIF"
+  echo "-v VERBOSE                  : $VERBOSE"
+  echo ""
+fi
+
+if [ "$PACKAGE_ONLY" = "true" ] && [ "$VERIFY_ONLY" = "true" ]; then
+  echo "$BASENAME_0 : cannot use '-y' and '-k' flags together"  >&2
+  usage
+  exit 1
+fi
+
+
+OPTS=""
+
+if [ "$SETTINGS" = "true" ]; then
+  OPTS="$OPTS -s _pipeline-resources/build/deployable/.m2/settings.xml"
+fi
+
+if [ "$CLEAN" = "true" ]; then
+  OPTS="$OPTS clean"
+fi
+
+if [ "$TIMELINE" = "true" ]; then
+  OPTS="$OPTS -Dmaven-timeline.version=1.8-SNAPSHOT"
+fi
+
+if [ "$SKIP_OFFLINE" = "false" ]; then
+  OPTS="$OPTS -o"
+fi
+
+if [ "$SINGLE_THREADED" = "false" ]; then
+  OPTS="$OPTS -T1C"
+fi
+
+if [ "$PACKAGE_ONLY" = "true" ]; then
+  OPTS="$OPTS package"
+else
+  if [ "$VERIFY_ONLY" = "true" ]; then
+    OPTS="$OPTS verify"
+  else
+    OPTS="$OPTS install"
+  fi
+fi
+
+if [ "$WHATIF" = "true" ]; then
+
+  if [ "$GIT_PULL" = "true" ]; then
+    echo git pull --ff-only
+  fi
+
+  if [ "$VERBOSE" = "true" ]; then
+    echo "mvn $OPTS $* 2>&1 | tee $MVN_LOG "
+  else
+    OPTS="$OPTS --log-file $MVN_LOG"
+    echo mvn $OPTS "$@"
+  fi
+
+
+  if [ "$SKIP_SEARCH_FOR_FAILURES" = "false" ]; then
+    echo "... grep for failures/errors"
+  fi
+
+  if [ "$SKIP_SUMMARY" = "false" ]; then
+    echo "... print summary"
+  fi
+
+
+  echo ""
+else
+
+  if [ "$GIT_PULL" = "true" ]; then
+    git pull --ff-only
+    if [ $? -ne 0 ]; then
+      echo "git pull --ff-only failed; aborting" >&2
+      exit 1
+    fi
+  fi
+
+  if [ "$VERBOSE" = "true" ]; then
+    echo "mvn $OPTS $* 2>&1 | tee $MVN_LOG"
+    mvn $OPTS "$@" 2>&1 | tee $MVN_LOG
+  else
+    OPTS="$OPTS --log-file $MVN_LOG"
+    echo "mvn $OPTS $*"
+    mvn $OPTS "$@"
+  fi
+
+  if [ "$SKIP_SEARCH_FOR_FAILURES" = "false" ]; then
+    grep -in -E 'Failures:\s[1-9]+, Errors: [0-9]+, Skipped: [0-9]+$' -B 20 $MVN_LOG
+    grep -in -E 'Failures:\s[0-9]+, Errors: [1-9]+, Skipped: [0-9]+$' -B 20 $MVN_LOG
+  fi
+
+  if [ "$SKIP_SUMMARY" = "false" ]; then
+    if grep -n "Segment walltime" $MVN_LOG ; then
+      tail -n +$(grep -n "Segment walltime" $MVN_LOG | cut -f1 -d:) $MVN_LOG
+    else
+      tail -n 50 $MVN_LOG
+    fi
+  fi
+fi
+
+
+
diff --git a/dependency-graph.sh b/dependency-graph.sh
new file mode 100644
index 0000000000..443a937d9e
--- /dev/null
+++ b/dependency-graph.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+GRAPHFORMAT=dot
+#GRAPHFORMAT=puml
+#GRAPHFORMAT=text
+mvn com.github.ferstl:depgraph-maven-plugin:aggregate -DgraphFormat=$GRAPHFORMAT
+
+echo "generated:"
+echo "target/dependency-graph.dot"
+echo "target/dependency-graph.png"
\ No newline at end of file
diff --git a/examples/demo/domain/pom.xml b/examples/demo/domain/pom.xml
index 2405ff9707..0c3cdb3cbc 100644
--- a/examples/demo/domain/pom.xml
+++ b/examples/demo/domain/pom.xml
@@ -62,7 +62,7 @@
 
 		<dependency>
 			<groupId>org.apache.isis.extensions</groupId>
-			<artifactId>isis-extensions-exceldownload-ui</artifactId>
+			<artifactId>isis-extensions-exceldownload-wicket-ui</artifactId>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.isis.extensions</groupId>
diff --git a/examples/demo/pom.xml b/examples/demo/pom.xml
index 75873ae884..145b406e6a 100644
--- a/examples/demo/pom.xml
+++ b/examples/demo/pom.xml
@@ -190,7 +190,7 @@
 		</dependency>
 		<dependency>
 			<groupId>org.apache.isis.extensions</groupId>
-			<artifactId>isis-extensions-exceldownload-ui</artifactId>
+			<artifactId>isis-extensions-exceldownload-wicket-ui</artifactId>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.isis.extensions</groupId>
diff --git a/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/IsisModuleExtCommandLogApplib.java b/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/IsisModuleExtCommandLogApplib.java
index b83122a8f7..21cb86ec76 100644
--- a/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/IsisModuleExtCommandLogApplib.java
+++ b/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/IsisModuleExtCommandLogApplib.java
@@ -22,10 +22,17 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
 import org.apache.isis.core.config.util.SpringProfileUtil;
+import org.apache.isis.extensions.commandlog.applib.app.CommandLogMenu;
+import org.apache.isis.extensions.commandlog.applib.dom.CommandLogEntry.TableColumnOrderDefault;
+import org.apache.isis.extensions.commandlog.applib.subscriber.CommandSubscriberForCommandLog;
 import org.apache.isis.testing.fixtures.applib.modules.ModuleWithFixtures;
 
 @Configuration
 @Import({
+        // @DomainService's
+        CommandLogMenu.class,
+        CommandSubscriberForCommandLog.class,
+        TableColumnOrderDefault.class,
 })
 public class IsisModuleExtCommandLogApplib
 implements ModuleWithFixtures {
diff --git a/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/app/CommandLogServiceMenu.java b/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/app/CommandLogMenu.java
similarity index 97%
rename from extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/app/CommandLogServiceMenu.java
rename to extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/app/CommandLogMenu.java
index feda33b913..200f5d9c0d 100644
--- a/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/app/CommandLogServiceMenu.java
+++ b/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/app/CommandLogMenu.java
@@ -47,7 +47,7 @@ import lombok.RequiredArgsConstructor;
 /**
  * @since 2.0 {@index}
  */
-@Named(CommandLogServiceMenu.LOGICAL_TYPE_NAME)
+@Named(CommandLogMenu.LOGICAL_TYPE_NAME)
 @DomainService(
     nature = NatureOfService.VIEW
 )
@@ -57,13 +57,13 @@ import lombok.RequiredArgsConstructor;
 )
 @javax.annotation.Priority(PriorityPrecedence.EARLY)
 @RequiredArgsConstructor(onConstructor_ = { @Inject })
-public class CommandLogServiceMenu {
+public class CommandLogMenu {
 
     public static final String LOGICAL_TYPE_NAME =
-            IsisModuleExtCommandLogApplib.NAMESPACE + ".CommandLogServiceMenu";
+            IsisModuleExtCommandLogApplib.NAMESPACE + ".CommandLogMenu";
 
     public static abstract class ActionDomainEvent
-        extends IsisModuleExtCommandLogApplib.ActionDomainEvent<CommandLogServiceMenu> { }
+        extends IsisModuleExtCommandLogApplib.ActionDomainEvent<CommandLogMenu> { }
 
 
     final CommandLogEntryRepository<? extends CommandLogEntry> commandLogEntryRepository;
diff --git a/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/CommandLogEntry.java b/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/CommandLogEntry.java
index 0f7df847c8..6060e15910 100644
--- a/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/CommandLogEntry.java
+++ b/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/CommandLogEntry.java
@@ -112,7 +112,7 @@ implements Comparable<CommandLogEntry>, DomainChangeRecord, HasCommandDto {
 
     @UtilityClass
     public static class Nq {
-        public static final String FIND_BY_INTERACTION_ID_STR = LOGICAL_TYPE_NAME + ".findByInteractionIdStr";
+        public static final String FIND_BY_INTERACTION_ID = LOGICAL_TYPE_NAME + ".findByInteractionId";
         public static final String FIND_BY_PARENT = LOGICAL_TYPE_NAME + ".findByParent";
         public static final String FIND_CURRENT = LOGICAL_TYPE_NAME + ".findCurrent";
         public static final String FIND_COMPLETED = LOGICAL_TYPE_NAME + ".findCompleted";
@@ -140,7 +140,7 @@ implements Comparable<CommandLogEntry>, DomainChangeRecord, HasCommandDto {
      */
     public CommandLogEntry(final Command command) {
 
-        setInteractionIdStr(command.getInteractionId().toString());
+        setInteractionId(command.getInteractionId());
         setUsername(command.getUsername());
         setTimestamp(command.getTimestamp());
 
@@ -170,7 +170,7 @@ implements Comparable<CommandLogEntry>, DomainChangeRecord, HasCommandDto {
             final org.apache.isis.extensions.commandlog.applib.dom.ReplayState replayState,
             final int targetIndex) {
 
-        setInteractionIdStr(commandDto.getInteractionId());
+        setInteractionId(UUID.fromString(commandDto.getInteractionId()));
         setUsername(commandDto.getUser());
         setTimestamp(JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(commandDto.getTimestamp()));
 
@@ -231,30 +231,14 @@ implements Comparable<CommandLogEntry>, DomainChangeRecord, HasCommandDto {
     @Retention(RetentionPolicy.RUNTIME)
     public @interface InteractionId {
         class DomainEvent extends PropertyDomainEvent<UUID> {}
+        int MAX_LENGTH = HasInteractionId.InteractionId.MAX_LENGTH;
+        boolean NULLABLE = HasInteractionId.InteractionId.NULLABLE;
+        String ALLOWS_NULL = HasInteractionId.InteractionId.ALLOWS_NULL;
     }
     @Override
     @InteractionId
-    public UUID getInteractionId() {
-        return UUID.fromString(getInteractionIdStr());
-    }
-
-
-    /**
-     * This is the persistence model for {@link #getInteractionId()}; hidden everywhere.
-     */
-    @HasInteractionId.InteractionIdStr // hidden everywhere
-    @java.lang.annotation.Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
-    @Retention(RetentionPolicy.RUNTIME)
-    public @interface InteractionIdStr {
-        int MAX_LENGTH = HasInteractionId.InteractionIdStr.MAX_LENGTH;
-        boolean NULLABLE = HasInteractionId.InteractionIdStr.NULLABLE;
-        String ALLOWS_NULL = HasInteractionId.InteractionIdStr.ALLOWS_NULL;
-        String NAME = HasInteractionId.InteractionIdStr.NAME;
-    }
-    @InteractionIdStr
-    public abstract String getInteractionIdStr();
-    public abstract void setInteractionIdStr(String interactionIdStr);
-
+    public abstract UUID getInteractionId();
+    public abstract void setInteractionId(UUID interactionId);
 
 
     @Property(
diff --git a/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/CommandLogEntryRepository.java b/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/CommandLogEntryRepository.java
index ffbadfd97c..380831e5c1 100644
--- a/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/CommandLogEntryRepository.java
+++ b/extensions/core/commandlog/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/CommandLogEntryRepository.java
@@ -42,7 +42,6 @@ import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.applib.services.factory.FactoryService;
 import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.applib.util.schema.CommandDtoUtils;
-import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 import org.apache.isis.schema.cmd.v2.CommandsDto;
 import org.apache.isis.schema.cmd.v2.MapDto;
@@ -83,8 +82,8 @@ public abstract class CommandLogEntryRepository<C extends CommandLogEntry> {
 
     public Optional<C> findByInteractionId(final UUID interactionId) {
         return repositoryService().firstMatch(
-                Query.named(commandLogClass,  CommandLogEntry.Nq.FIND_BY_INTERACTION_ID_STR)
-                        .withParameter("interactionIdStr", interactionId.toString()));
+                Query.named(commandLogClass,  CommandLogEntry.Nq.FIND_BY_INTERACTION_ID)
+                        .withParameter("interactionId", interactionId));
     }
 
     public List<C> findByParent(final CommandLogEntry parent) {
@@ -277,14 +276,14 @@ public abstract class CommandLogEntryRepository<C extends CommandLogEntry> {
 
         final C commandJdo = factoryService.detachedEntity(commandLogClass);
 
-        commandJdo.setInteractionIdStr(dto.getInteractionId());
+        commandJdo.setInteractionId(UUID.fromString(dto.getInteractionId()));
         commandJdo.setTimestamp(JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(dto.getTimestamp()));
         commandJdo.setUsername(dto.getUser());
 
         commandJdo.setReplayState(ReplayState.PENDING);
 
-        final OidDto firstTarget = dto.getTargets().getOid().get(0);
-        commandJdo.setTarget(Bookmark.forOidDto(firstTarget));
+        val firstTargetOidDto = dto.getTargets().getOid().get(0);
+        commandJdo.setTarget(Bookmark.forOidDto(firstTargetOidDto));
         commandJdo.setCommandDto(dto);
         commandJdo.setLogicalMemberIdentifier(dto.getMember().getLogicalMemberIdentifier());
 
@@ -295,9 +294,9 @@ public abstract class CommandLogEntryRepository<C extends CommandLogEntry> {
 
 
     public List<C> saveForReplay(final CommandsDto commandsDto) {
-        List<CommandDto> commandDto = commandsDto.getCommandDto();
-        List<C> commands = new ArrayList<>();
-        for (final CommandDto dto : commandDto) {
+        val commandDtos = commandsDto.getCommandDto();
+        val commands = new ArrayList<C>();
+        for (val dto : commandDtos) {
             commands.add(saveForReplay(dto));
         }
         return commands;
@@ -329,8 +328,8 @@ public abstract class CommandLogEntryRepository<C extends CommandLogEntry> {
 
 
     private C findByInteractionIdElseNull(final UUID interactionId) {
-        val q = Query.named(commandLogClass, CommandLogEntry.Nq.FIND_BY_INTERACTION_ID_STR)
-                .withParameter("interactionIdStr", interactionId.toString());
+        val q = Query.named(commandLogClass, CommandLogEntry.Nq.FIND_BY_INTERACTION_ID)
+                .withParameter("interactionId", interactionId.toString());
         return repositoryService().uniqueMatch(q).orElse(null);
     }
 
diff --git a/extensions/core/commandlog/persistence-jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java b/extensions/core/commandlog/persistence-jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java
index 47812eae41..555781da2a 100644
--- a/extensions/core/commandlog/persistence-jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java
+++ b/extensions/core/commandlog/persistence-jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java
@@ -18,13 +18,10 @@
  */
 package org.apache.isis.extensions.commandlog.jdo;
 
-import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
 import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
-import org.apache.isis.extensions.commandlog.applib.subscriber.CommandSubscriberForCommandLog;
-import org.apache.isis.extensions.commandlog.applib.app.CommandLogServiceMenu;
 import org.apache.isis.extensions.commandlog.jdo.dom.CommandLogEntry;
 import org.apache.isis.extensions.commandlog.jdo.dom.CommandLogEntryRepository;
 import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
@@ -38,21 +35,12 @@ import org.apache.isis.testing.fixtures.applib.teardown.jdo.TeardownFixtureJdoAb
         // modules
         IsisModuleExtCommandLogApplib.class,
 
-        // @DomainService's
-        CommandLogServiceMenu.class,
-
         // @Service's
         CommandLogEntryRepository.class,
-        org.apache.isis.extensions.commandlog.applib.dom.CommandLogEntry.TableColumnOrderDefault.class,
-        CommandSubscriberForCommandLog.class,
 
         // entities
         CommandLogEntry.class
 })
-@ComponentScan(
-        basePackageClasses= {
-                IsisModuleExtCommandLogJdo.class
-        })
 public class IsisModuleExtCommandLogJdo {
 
     public static final String NAMESPACE = IsisModuleExtCommandLogApplib.NAMESPACE;
diff --git a/extensions/core/commandlog/persistence-jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/dom/CommandLogEntry.java b/extensions/core/commandlog/persistence-jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/dom/CommandLogEntry.java
index aaf80eef0c..52b444fbb7 100644
--- a/extensions/core/commandlog/persistence-jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/dom/CommandLogEntry.java
+++ b/extensions/core/commandlog/persistence-jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/dom/CommandLogEntry.java
@@ -18,6 +18,8 @@
  */
 package org.apache.isis.extensions.commandlog.jdo.dom;
 
+import java.util.UUID;
+
 import javax.inject.Named;
 import javax.jdo.annotations.Column;
 import javax.jdo.annotations.IdentityType;
@@ -35,10 +37,9 @@ import org.apache.isis.applib.annotation.Editing;
 import org.apache.isis.applib.jaxb.PersistentEntityAdapter;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.extensions.commandlog.jdo.IsisModuleExtCommandLogJdo;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
-import static org.apache.isis.extensions.commandlog.applib.dom.CommandLogEntry.*;
+import static org.apache.isis.extensions.commandlog.applib.dom.CommandLogEntry.Nq;
 
 import lombok.Getter;
 import lombok.NoArgsConstructor;
@@ -51,15 +52,15 @@ import lombok.Setter;
 @Indices({
         @Index(name = "Command__startedAt__timestamp__IDX", members = { "startedAt", "timestamp" }),
         @Index(name = "Command__timestamp__IDX", members = { "timestamp" }),
-//        @javax.jdo.annotations.Index(name = "CommandJdo__replayState__timestamp__startedAt_IDX", members = { "replayState", "timestamp", "startedAt"}),
-//        @javax.jdo.annotations.Index(name = "CommandJdo__replayState__startedAt__completedAt_IDX", members = {"startedAt", "replayState", "completedAt"}),
+//        @javax.jdo.annotations.Index(name = "Command__replayState__timestamp__startedAt_IDX", members = { "replayState", "timestamp", "startedAt"}),
+//        @javax.jdo.annotations.Index(name = "Command__replayState__startedAt__completedAt_IDX", members = {"startedAt", "replayState", "completedAt"}),
 })
 @Queries( {
     @Query(
-            name  = Nq.FIND_BY_INTERACTION_ID_STR,
+            name  = Nq.FIND_BY_INTERACTION_ID,
             value = "SELECT "
                   + "  FROM " + CommandLogEntry.FQCN + " "
-                  + " WHERE interactionIdStr == :interactionIdStr "),
+                  + " WHERE interactionId == :interactionId "),
     @Query(
             name  = Nq.FIND_BY_PARENT,
             value = "SELECT "
@@ -229,10 +230,10 @@ extends org.apache.isis.extensions.commandlog.applib.dom.CommandLogEntry {
     }
 
     @PrimaryKey
-    @Column(allowsNull = InteractionIdStr.ALLOWS_NULL, name = InteractionIdStr.NAME, length = InteractionIdStr.MAX_LENGTH)
-    @InteractionIdStr
+    @Column(allowsNull = InteractionId.ALLOWS_NULL, length = InteractionId.MAX_LENGTH)
+    @InteractionId
     @Getter @Setter
-    private String interactionIdStr;
+    private UUID interactionId;
 
 
     @Column(allowsNull = Username.ALLOWS_NULL, length = Username.MAX_LENGTH)
@@ -313,6 +314,4 @@ extends org.apache.isis.extensions.commandlog.applib.dom.CommandLogEntry {
     @Getter @Setter
     private String replayStateFailureReason;
 
-
 }
-
diff --git a/extensions/core/commandlog/persistence-jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/dom/CommandLogEntryRepository.java b/extensions/core/commandlog/persistence-jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/dom/CommandLogEntryRepository.java
index 77561536d7..0e47c7bd00 100644
--- a/extensions/core/commandlog/persistence-jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/dom/CommandLogEntryRepository.java
+++ b/extensions/core/commandlog/persistence-jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/dom/CommandLogEntryRepository.java
@@ -34,7 +34,6 @@ import org.apache.isis.extensions.commandlog.jdo.IsisModuleExtCommandLogJdo;
 @Named(CommandLogEntryRepository.LOGICAL_TYPE_NAME)
 @javax.annotation.Priority(PriorityPrecedence.MIDPOINT)
 @Qualifier("Jdo")
-//@Log4j2
 public class CommandLogEntryRepository
 extends org.apache.isis.extensions.commandlog.applib.dom.CommandLogEntryRepository<CommandLogEntry> {
 
diff --git a/extensions/core/commandlog/persistence-jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/IsisModuleExtCommandLogJpa.java b/extensions/core/commandlog/persistence-jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/IsisModuleExtCommandLogJpa.java
index 1fdbd4ca7a..66ab0eaa20 100644
--- a/extensions/core/commandlog/persistence-jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/IsisModuleExtCommandLogJpa.java
+++ b/extensions/core/commandlog/persistence-jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/IsisModuleExtCommandLogJpa.java
@@ -23,8 +23,6 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
 import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
-import org.apache.isis.extensions.commandlog.applib.app.CommandLogServiceMenu;
-import org.apache.isis.extensions.commandlog.applib.subscriber.CommandSubscriberForCommandLog;
 import org.apache.isis.extensions.commandlog.jpa.dom.CommandLogEntry;
 import org.apache.isis.extensions.commandlog.jpa.dom.CommandLogEntryRepository;
 
@@ -36,19 +34,14 @@ import org.apache.isis.extensions.commandlog.jpa.dom.CommandLogEntryRepository;
         // modules
         IsisModuleExtCommandLogApplib.class,
 
-        // @DomainService's
-        CommandLogServiceMenu.class,
-
         // @Service's
         CommandLogEntryRepository.class,
-        org.apache.isis.extensions.commandlog.applib.dom.CommandLogEntry.TableColumnOrderDefault.class,
-        CommandSubscriberForCommandLog.class,
 
         // entities
         CommandLogEntry.class
 })
 @EntityScan(basePackageClasses = {
-        CommandLogEntry.class,
+    CommandLogEntry.class,
 })
 public class IsisModuleExtCommandLogJpa {
 
diff --git a/extensions/core/commandlog/persistence-jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/dom/CommandLogEntry.java b/extensions/core/commandlog/persistence-jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/dom/CommandLogEntry.java
index b5f7b56239..6e7008224f 100644
--- a/extensions/core/commandlog/persistence-jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/dom/CommandLogEntry.java
+++ b/extensions/core/commandlog/persistence-jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/dom/CommandLogEntry.java
@@ -18,6 +18,8 @@
  */
 package org.apache.isis.extensions.commandlog.jpa.dom;
 
+import java.util.UUID;
+
 import javax.inject.Named;
 import javax.persistence.Basic;
 import javax.persistence.Column;
@@ -43,6 +45,7 @@ import org.apache.isis.applib.jaxb.PersistentEntityAdapter;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.persistence.jpa.applib.integration.IsisEntityListener;
+import org.apache.isis.persistence.jpa.integration.typeconverters.applib.IsisBookmarkConverter;
 import org.apache.isis.persistence.jpa.integration.typeconverters.schema.v2.IsisCommandDtoConverter;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
@@ -63,10 +66,10 @@ import lombok.Setter;
 )
 @NamedQueries({
     @NamedQuery(
-            name  = Nq.FIND_BY_INTERACTION_ID_STR,
+            name  = Nq.FIND_BY_INTERACTION_ID,
             query = "SELECT cl "
                   + "  FROM CommandLogEntry cl "
-                  + " WHERE cl.interactionIdStr = :interactionIdStr"),
+                  + " WHERE cl.interactionId = :interactionId"),
     @NamedQuery(
             name  = Nq.FIND_BY_PARENT,
             query = "SELECT cl "
@@ -219,10 +222,10 @@ public class CommandLogEntry extends org.apache.isis.extensions.commandlog.appli
     }
 
     @Id
-    @Column(nullable = InteractionIdStr.NULLABLE, name = InteractionIdStr.NAME, length = InteractionIdStr.MAX_LENGTH)
-    @InteractionIdStr
+    @Column(nullable = InteractionId.NULLABLE, length = InteractionId.MAX_LENGTH)
+    @InteractionId
     @Getter @Setter
-    private String interactionIdStr;
+    private UUID interactionId;
 
 
     @Column(nullable = Username.NULLABLE, length = Username.MAX_LENGTH)
@@ -237,6 +240,7 @@ public class CommandLogEntry extends org.apache.isis.extensions.commandlog.appli
     private java.sql.Timestamp timestamp;
 
 
+    @Convert(converter = IsisBookmarkConverter.class)
     @Column(nullable = Target.NULLABLE, length = Target.MAX_LENGTH)
     @Target
     @Getter @Setter
@@ -260,9 +264,9 @@ public class CommandLogEntry extends org.apache.isis.extensions.commandlog.appli
     private String logicalMemberIdentifier;
 
 
+    @Convert(converter = IsisCommandDtoConverter.class)
     @Lob @Basic(fetch = FetchType.LAZY)
     @Column(nullable = CommandDtoAnnot.NULLABLE, columnDefinition = "CLOB")
-    @Convert(converter = IsisCommandDtoConverter.class)
     @CommandDtoAnnot
     @Getter @Setter
     private CommandDto commandDto;
@@ -280,6 +284,7 @@ public class CommandLogEntry extends org.apache.isis.extensions.commandlog.appli
     private java.sql.Timestamp completedAt;
 
 
+    @Convert(converter = IsisBookmarkConverter.class)
     @Column(nullable = Result.NULLABLE, length = Result.MAX_LENGTH)
     @Result
     @Getter @Setter
@@ -292,7 +297,8 @@ public class CommandLogEntry extends org.apache.isis.extensions.commandlog.appli
     @Getter @Setter
     private String exception;
 
-    @Column(nullable = ReplayState.NULLABLE, length = ReplayState.MAX_LENGTH) @Enumerated(EnumType.STRING)
+    @Column(nullable = ReplayState.NULLABLE, length = ReplayState.MAX_LENGTH)
+    @Enumerated(EnumType.STRING)
     @ReplayState
     @Getter @Setter
     private org.apache.isis.extensions.commandlog.applib.dom.ReplayState replayState;
@@ -303,6 +309,4 @@ public class CommandLogEntry extends org.apache.isis.extensions.commandlog.appli
     @Getter @Setter
     private String replayStateFailureReason;
 
-
-
 }
diff --git a/extensions/core/commandlog/persistence-jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/dom/CommandLogEntryRepository.java b/extensions/core/commandlog/persistence-jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/dom/CommandLogEntryRepository.java
index b5d2284886..155c66ccfd 100644
--- a/extensions/core/commandlog/persistence-jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/dom/CommandLogEntryRepository.java
+++ b/extensions/core/commandlog/persistence-jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/dom/CommandLogEntryRepository.java
@@ -65,7 +65,6 @@ import lombok.val;
 @Named(CommandLogEntryRepository.LOGICAL_TYPE_NAME)
 @javax.annotation.Priority(PriorityPrecedence.MIDPOINT)
 @Qualifier("Jpa")
-//@Log4j2
 public class CommandLogEntryRepository
 extends org.apache.isis.extensions.commandlog.applib.dom.CommandLogEntryRepository<CommandLogEntry> {
 
diff --git a/extensions/core/executionlog/applib/src/main/java/org/apache/isis/extensions/executionlog/applib/IsisModuleExtExecutionLogApplib.java b/extensions/core/executionlog/applib/src/main/java/org/apache/isis/extensions/executionlog/applib/IsisModuleExtExecutionLogApplib.java
index ef25c85ea8..f3e5013d31 100644
--- a/extensions/core/executionlog/applib/src/main/java/org/apache/isis/extensions/executionlog/applib/IsisModuleExtExecutionLogApplib.java
+++ b/extensions/core/executionlog/applib/src/main/java/org/apache/isis/extensions/executionlog/applib/IsisModuleExtExecutionLogApplib.java
@@ -22,10 +22,19 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
 import org.apache.isis.core.config.util.SpringProfileUtil;
+import org.apache.isis.extensions.executionlog.applib.app.ExecutionLogMenu;
+import org.apache.isis.extensions.executionlog.applib.dom.ExecutionLogEntry;
+import org.apache.isis.extensions.executionlog.applib.spiimpl.ExecutionSubscriberForLog;
 import org.apache.isis.testing.fixtures.applib.modules.ModuleWithFixtures;
 
 @Configuration
 @Import({
+        // @DomainService's
+        ExecutionLogMenu.class,
+
+        // @Service's
+        ExecutionSubscriberForLog.class,
+        ExecutionLogEntry.TableColumnOrderDefault.class
 })
 public class IsisModuleExtExecutionLogApplib
 implements ModuleWithFixtures {
diff --git a/extensions/core/executionlog/applib/src/main/java/org/apache/isis/extensions/executionlog/applib/app/ExecutionLogMenu.java b/extensions/core/executionlog/applib/src/main/java/org/apache/isis/extensions/executionlog/applib/app/ExecutionLogMenu.java
new file mode 100644
index 0000000000..646ef37dc1
--- /dev/null
+++ b/extensions/core/executionlog/applib/src/main/java/org/apache/isis/extensions/executionlog/applib/app/ExecutionLogMenu.java
@@ -0,0 +1,69 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.extensions.executionlog.applib.app;
+
+import java.time.LocalDate;
+import java.time.ZoneId;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.DomainServiceLayout;
+import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.annotation.PriorityPrecedence;
+import org.apache.isis.applib.services.clock.ClockService;
+import org.apache.isis.extensions.executionlog.applib.IsisModuleExtExecutionLogApplib;
+import org.apache.isis.extensions.executionlog.applib.dom.ExecutionLogEntry;
+import org.apache.isis.extensions.executionlog.applib.dom.ExecutionLogEntryRepository;
+
+import lombok.RequiredArgsConstructor;
+
+/**
+ * @since 2.0 {@index}
+ */
+@Named(ExecutionLogMenu.LOGICAL_TYPE_NAME)
+@DomainService(
+    nature = NatureOfService.VIEW
+)
+@DomainServiceLayout(
+    named = "Activity",
+    menuBar = DomainServiceLayout.MenuBar.SECONDARY
+)
+@javax.annotation.Priority(PriorityPrecedence.EARLY)
+@RequiredArgsConstructor(onConstructor_ = { @Inject })
+public class ExecutionLogMenu {
+
+    public static final String LOGICAL_TYPE_NAME =
+            IsisModuleExtExecutionLogApplib.NAMESPACE + ".ExecutionLogMenu";
+
+    public static abstract class ActionDomainEvent
+        extends IsisModuleExtExecutionLogApplib.ActionDomainEvent<ExecutionLogMenu> { }
+
+
+    final ExecutionLogEntryRepository<? extends ExecutionLogEntry> executionLogEntryRepository;
+    final ClockService clockService;
+
+
+
+    private LocalDate now() {
+        return clockService.getClock().nowAsLocalDate(ZoneId.systemDefault());
+    }
+}
+
diff --git a/extensions/core/executionlog/applib/src/main/java/org/apache/isis/extensions/executionlog/applib/dom/ExecutionLogEntry.java b/extensions/core/executionlog/applib/src/main/java/org/apache/isis/extensions/executionlog/applib/dom/ExecutionLogEntry.java
index 646dd631a2..9fe2367ccc 100644
--- a/extensions/core/executionlog/applib/src/main/java/org/apache/isis/extensions/executionlog/applib/dom/ExecutionLogEntry.java
+++ b/extensions/core/executionlog/applib/src/main/java/org/apache/isis/extensions/executionlog/applib/dom/ExecutionLogEntry.java
@@ -33,6 +33,7 @@ import javax.validation.constraints.Digits;
 
 import org.springframework.stereotype.Service;
 
+import org.apache.isis.applib.Identifier;
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.DomainObjectLayout;
 import org.apache.isis.applib.annotation.Editing;
@@ -104,16 +105,25 @@ implements Comparable<ExecutionLogEntry>, DomainChangeRecord, HasInteractionIdAn
     public static class Nq {
     }
 
+    @UtilityClass
+    protected static class Util {
+        public static String abbreviated(String str, int maxLength) {
+            return str != null ? (str.length() < maxLength ? str : str.substring(0, maxLength - 3) + "...") : null;
+        }
+    }
+
+
+
     public ExecutionLogEntry(@NonNull Execution<? extends MemberExecutionDto,?> execution) {
 
-        val interactionIdStr = execution.getInteraction().getInteractionId().toString();
-        setInteractionIdStr(interactionIdStr);
+        val interactionId = execution.getInteraction().getInteractionId();
+        setInteractionId(interactionId);
 
         val memberExecutionDto = execution.getDto();
         setSequence(memberExecutionDto.getSequence());
 
         val interactionDto = new InteractionDto();
-        interactionDto.setInteractionId(interactionIdStr);
+        interactionDto.setInteractionId(interactionId.toString());
         interactionDto.setExecution(memberExecutionDto);
         setInteractionDto(interactionDto);
 
@@ -156,6 +166,14 @@ implements Comparable<ExecutionLogEntry>, DomainChangeRecord, HasInteractionIdAn
     }
 
 
+    /**
+     * The unique identifier (a GUID) of the {@link org.apache.isis.applib.services.iactn.Interaction} in which this execution occurred.
+     *
+     * <p>
+     * The combination of ({@link #getInteractionId() interactionId}, {@link #getSequence() sequence}) makes up the
+     * primary key.
+     * </p>
+     */
     @Property(
             domainEvent = InteractionId.DomainEvent.class
     )
@@ -164,33 +182,25 @@ implements Comparable<ExecutionLogEntry>, DomainChangeRecord, HasInteractionIdAn
     @Retention(RetentionPolicy.RUNTIME)
     public @interface InteractionId {
         class DomainEvent extends PropertyDomainEvent<UUID> {}
+        int MAX_LENGTH = HasInteractionId.InteractionId.MAX_LENGTH;
+        boolean NULLABLE = HasInteractionId.InteractionId.NULLABLE;
+        String ALLOWS_NULL = HasInteractionId.InteractionId.ALLOWS_NULL;
     }
     @Override
     @InteractionId
-    public UUID getInteractionId() {
-        return UUID.fromString(getInteractionIdStr());
-    }
+    public abstract UUID getInteractionId();
+    public abstract void setInteractionId(UUID interactionId);
 
 
 
     /**
-     * This is the persistence model for {@link #getInteractionId()}; hidden everywhere.
+     * The 0-based additional identifier of an execution event within the given {@link #getInteractionId() interaction}.
+     *
+     * <p>
+     * The combination of ({@link #getInteractionId() interactionId}, {@link #getSequence() sequence}) makes up the
+     * primary key.
+     * </p>
      */
-    @HasInteractionId.InteractionIdStr // hidden everywhere
-    @java.lang.annotation.Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
-    @Retention(RetentionPolicy.RUNTIME)
-    public @interface InteractionIdStr {
-        int MAX_LENGTH = HasInteractionId.InteractionIdStr.MAX_LENGTH;
-        boolean NULLABLE = HasInteractionId.InteractionIdStr.NULLABLE;
-        String ALLOWS_NULL = HasInteractionId.InteractionIdStr.ALLOWS_NULL;
-        String NAME = HasInteractionId.InteractionIdStr.NAME;
-    }
-    @InteractionIdStr
-    public abstract String getInteractionIdStr();
-    public abstract void setInteractionIdStr(String interactionIdStr);
-
-
-
     @Property(
             domainEvent = Sequence.DomainEvent.class
     )
@@ -218,6 +228,7 @@ implements Comparable<ExecutionLogEntry>, DomainChangeRecord, HasInteractionIdAn
         class DomainEvent extends PropertyDomainEvent<ExecutionLogEntryType> {}
         boolean NULLABLE = false;
         String ALLOWS_NULL = "false";
+        int MAX_LENGTH = 30;
     }
     @ExecutionType
     public abstract ExecutionLogEntryType getExecutionType();
@@ -281,6 +292,14 @@ implements Comparable<ExecutionLogEntry>, DomainChangeRecord, HasInteractionIdAn
 
 
 
+    /**
+     * String representation of the invoked action or edited property.
+     *
+     * <p>
+     * This is the <i>logical</i> member identifier because it does not matter whether the action/property is declared
+     * on the type or is contributed.
+     *
+     */
     @Property(
             domainEvent = LogicalMemberIdentifier.DomainEvent.class,
             editing = Editing.DISABLED
@@ -302,8 +321,6 @@ implements Comparable<ExecutionLogEntry>, DomainChangeRecord, HasInteractionIdAn
 
 
 
-
-
     @Property(
             domainEvent = InteractionDtoAnnot.DomainEvent.class
     )
@@ -337,6 +354,7 @@ implements Comparable<ExecutionLogEntry>, DomainChangeRecord, HasInteractionIdAn
         boolean NULLABLE = false;
         String ALLOWS_NULL = "false";
     }
+    @StartedAt
     public abstract java.sql.Timestamp getStartedAt();
     public abstract void setStartedAt(java.sql.Timestamp startedAt);
 
@@ -357,6 +375,7 @@ implements Comparable<ExecutionLogEntry>, DomainChangeRecord, HasInteractionIdAn
         boolean NULLABLE = false;
         String ALLOWS_NULL = "false";
     }
+    @CompletedAt
     public abstract java.sql.Timestamp getCompletedAt();
     public abstract void setCompletedAt(java.sql.Timestamp completedAt);
 
diff --git a/extensions/core/executionlog/persistence-jdo/logging-dn-enhance.properties b/extensions/core/executionlog/persistence-jdo/logging-dn-enhance.properties
new file mode 100644
index 0000000000..515c2a2966
--- /dev/null
+++ b/extensions/core/executionlog/persistence-jdo/logging-dn-enhance.properties
@@ -0,0 +1,48 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you 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
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+#
+
+log4j.appender.A1=org.apache.log4j.FileAppender
+log4j.appender.A1.File=datanucleus.log
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss,SSS} (%t) %-5p [%c] - %m%n
+
+
+# overriding all those below... 
+log4j.category.DataNucleus=ERROR
+
+log4j.category.DataNucleus.Persistence=INFO, A1
+log4j.category.DataNucleus.Transaction=INFO, A1
+log4j.category.DataNucleus.Connection=INFO, A1
+log4j.category.DataNucleus.Query=INFO, A1
+log4j.category.DataNucleus.Cache=INFO, A1
+log4j.category.DataNucleus.MetaData=INFO, A1
+log4j.category.DataNucleus.Datastore=INFO, A1
+log4j.category.DataNucleus.Datastore.Schema=INFO, A1
+log4j.category.DataNucleus.Datastore.Persist=INFO, A1
+log4j.category.DataNucleus.Datastore.Retrieve=INFO, A1
+#Log of all 'native' statements sent to the datastore
+log4j.category.DataNucleus.Datastore.Native=INFO, A1 
+log4j.category.DataNucleus.General=INFO, A1
+#All messages relating to object lifecycle changes
+log4j.category.DataNucleus.Lifecycle=INFO, A1
+log4j.category.DataNucleus.ValueGeneration=INFO, A1
+log4j.category.DataNucleus.Enhancer=INFO, A1
+log4j.category.DataNucleus.SchemaTool=INFO, A1
+log4j.category.DataNucleus.JDO=INFO, A1
+ 
\ No newline at end of file
diff --git a/extensions/core/executionlog/persistence-jdo/pom.xml b/extensions/core/executionlog/persistence-jdo/pom.xml
index 0696fc69bb..38db4e80e2 100644
--- a/extensions/core/executionlog/persistence-jdo/pom.xml
+++ b/extensions/core/executionlog/persistence-jdo/pom.xml
@@ -50,7 +50,7 @@
 
 	    <dependency>
 	        <groupId>org.apache.isis.extensions</groupId>
-	        <artifactId>isis-extensions-commandlog-applib</artifactId>
+	        <artifactId>isis-extensions-executionlog-applib</artifactId>
 	    </dependency>
 
         <!-- PERSISTENCE -->
@@ -73,6 +73,10 @@
 			<artifactId>isis-testing-integtestsupport-applib</artifactId>
 			<scope>test</scope>
 		</dependency>
+        <dependency>
+            <groupId>org.apache.isis.extensions</groupId>
+            <artifactId>isis-extensions-executionlog-applib</artifactId>
+        </dependency>
 
     </dependencies>
 
diff --git a/extensions/core/commandlog/persistence-jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java b/extensions/core/executionlog/persistence-jdo/src/main/java/org/apache/isis/extensions/executionlog/jdo/IsisModuleExtExecutionLogJdo.java
similarity index 65%
copy from extensions/core/commandlog/persistence-jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java
copy to extensions/core/executionlog/persistence-jdo/src/main/java/org/apache/isis/extensions/executionlog/jdo/IsisModuleExtExecutionLogJdo.java
index 47812eae41..288bdd2b1f 100644
--- a/extensions/core/commandlog/persistence-jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java
+++ b/extensions/core/executionlog/persistence-jdo/src/main/java/org/apache/isis/extensions/executionlog/jdo/IsisModuleExtExecutionLogJdo.java
@@ -16,17 +16,15 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.extensions.commandlog.jdo;
+package org.apache.isis.extensions.executionlog.jdo;
 
-import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
 import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
-import org.apache.isis.extensions.commandlog.applib.subscriber.CommandSubscriberForCommandLog;
-import org.apache.isis.extensions.commandlog.applib.app.CommandLogServiceMenu;
-import org.apache.isis.extensions.commandlog.jdo.dom.CommandLogEntry;
-import org.apache.isis.extensions.commandlog.jdo.dom.CommandLogEntryRepository;
+import org.apache.isis.extensions.executionlog.applib.IsisModuleExtExecutionLogApplib;
+import org.apache.isis.extensions.executionlog.applib.dom.ExecutionLogEntry;
+import org.apache.isis.extensions.executionlog.applib.dom.ExecutionLogEntryRepository;
 import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
 import org.apache.isis.testing.fixtures.applib.teardown.jdo.TeardownFixtureJdoAbstract;
 
@@ -36,24 +34,15 @@ import org.apache.isis.testing.fixtures.applib.teardown.jdo.TeardownFixtureJdoAb
 @Configuration
 @Import({
         // modules
-        IsisModuleExtCommandLogApplib.class,
-
-        // @DomainService's
-        CommandLogServiceMenu.class,
+        IsisModuleExtExecutionLogApplib.class,
 
         // @Service's
-        CommandLogEntryRepository.class,
-        org.apache.isis.extensions.commandlog.applib.dom.CommandLogEntry.TableColumnOrderDefault.class,
-        CommandSubscriberForCommandLog.class,
+        ExecutionLogEntryRepository.class,
 
         // entities
-        CommandLogEntry.class
+        ExecutionLogEntry.class
 })
-@ComponentScan(
-        basePackageClasses= {
-                IsisModuleExtCommandLogJdo.class
-        })
-public class IsisModuleExtCommandLogJdo {
+public class IsisModuleExtExecutionLogJdo {
 
     public static final String NAMESPACE = IsisModuleExtCommandLogApplib.NAMESPACE;
     public static final String SCHEMA = IsisModuleExtCommandLogApplib.SCHEMA;
@@ -66,7 +55,7 @@ public class IsisModuleExtCommandLogJdo {
         return new TeardownFixtureJdoAbstract() {
             @Override
             protected void execute(final ExecutionContext executionContext) {
-                deleteFrom(CommandLogEntry.class);
+                deleteFrom(ExecutionLogEntry.class);
             }
         };
     }
diff --git a/extensions/core/executionlog/persistence-jdo/src/main/java/org/apache/isis/extensions/executionlog/jdo/dom/ExecutionLogEntry.java b/extensions/core/executionlog/persistence-jdo/src/main/java/org/apache/isis/extensions/executionlog/jdo/dom/ExecutionLogEntry.java
new file mode 100644
index 0000000000..cc238548f7
--- /dev/null
+++ b/extensions/core/executionlog/persistence-jdo/src/main/java/org/apache/isis/extensions/executionlog/jdo/dom/ExecutionLogEntry.java
@@ -0,0 +1,218 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.extensions.executionlog.jdo.dom;
+
+import java.sql.Timestamp;
+import java.text.SimpleDateFormat;
+import java.util.UUID;
+
+import javax.inject.Named;
+import javax.jdo.annotations.Column;
+import javax.jdo.annotations.IdentityType;
+import javax.jdo.annotations.PersistenceCapable;
+import javax.jdo.annotations.Persistent;
+import javax.jdo.annotations.PrimaryKey;
+import javax.jdo.annotations.Queries;
+import javax.jdo.annotations.Query;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import org.apache.isis.applib.Identifier;
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.DomainObjectLayout;
+import org.apache.isis.applib.annotation.Editing;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.applib.jaxb.PersistentEntitiesAdapter;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.util.TitleBuffer;
+import org.apache.isis.extensions.executionlog.applib.IsisModuleExtExecutionLogApplib;
+import org.apache.isis.extensions.executionlog.applib.dom.ExecutionLogEntryPK;
+import org.apache.isis.extensions.executionlog.applib.dom.ExecutionLogEntryType;
+import org.apache.isis.schema.ixn.v2.InteractionDto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@PersistenceCapable(
+        identityType= IdentityType.APPLICATION,
+        schema = "isispublishmq",
+        table="PublishedEvent",
+        objectIdClass= ExecutionLogEntryPK.class)
+@Queries( {
+    @Query(
+            name="findByTransactionId", language="JDOQL",
+            value="SELECT "
+                    + "FROM com.ecpnv.platform.v1.extensions.executionlog.dom.jdo.events.PublishedEvent "
+                    + "WHERE transactionId == :transactionId "
+                    + "ORDER BY timestamp DESC, sequence DESC"),
+    @Query(
+            name="findByTransactionIdAndSequence", language="JDOQL",
+            value="SELECT "
+                    + "FROM com.ecpnv.platform.v1.extensions.executionlog.dom.jdo.events.PublishedEvent "
+                    + "WHERE transactionId == :transactionId "
+                    + "&&    sequence      == :sequence "),
+    @Query(
+            name="findByTargetAndTimestampBetween", language="JDOQL",
+            value="SELECT "
+                    + "FROM com.ecpnv.platform.v1.extensions.executionlog.dom.jdo.events.PublishedEvent "
+                    + "WHERE targetStr == :targetStr "
+                    + "&& timestamp >= :from "
+                    + "&& timestamp <= :to "
+                    + "ORDER BY timestamp DESC, transactionId DESC, sequence DESC"),
+    @Query(
+            name="findByTargetAndTimestampAfter", language="JDOQL",
+            value="SELECT "
+                    + "FROM com.ecpnv.platform.v1.extensions.executionlog.dom.jdo.events.PublishedEvent "
+                    + "WHERE targetStr == :targetStr "
+                    + "&& timestamp >= :from "
+                    + "ORDER BY timestamp DESC, transactionId DESC, sequence DESC"),
+    @Query(
+            name="findByTargetAndTimestampBefore", language="JDOQL",
+            value="SELECT "
+                    + "FROM com.ecpnv.platform.v1.extensions.executionlog.dom.jdo.events.PublishedEvent "
+                    + "WHERE targetStr == :targetStr "
+                    + "&& timestamp <= :to "
+                    + "ORDER BY timestamp DESC, transactionId DESC, sequence DESC"),
+    @Query(
+            name="findByTarget", language="JDOQL",
+            value="SELECT "
+                    + "FROM com.ecpnv.platform.v1.extensions.executionlog.dom.jdo.events.PublishedEvent "
+                    + "WHERE targetStr == :targetStr "
+                    + "ORDER BY timestamp DESC, transactionId DESC, sequence DESC"),
+    @Query(
+            name="findByTimestampBetween", language="JDOQL",
+            value="SELECT "
+                    + "FROM com.ecpnv.platform.v1.extensions.executionlog.dom.jdo.events.PublishedEvent "
+                    + "WHERE timestamp >= :from "
+                    + "&&    timestamp <= :to "
+                    + "ORDER BY timestamp DESC, transactionId DESC, sequence DESC"),
+    @Query(
+            name="findByTimestampAfter", language="JDOQL",
+            value="SELECT "
+                    + "FROM com.ecpnv.platform.v1.extensions.executionlog.dom.jdo.events.PublishedEvent "
+                    + "WHERE timestamp >= :from "
+                    + "ORDER BY timestamp DESC, transactionId DESC, sequence DESC"),
+    @Query(
+            name="findByTimestampBefore", language="JDOQL",
+            value="SELECT "
+                    + "FROM com.ecpnv.platform.v1.extensions.executionlog.dom.jdo.events.PublishedEvent "
+                    + "WHERE timestamp <= :to "
+                    + "ORDER BY timestamp DESC, transactionId DESC, sequence DESC"),
+    @Query(
+            name="find", language="JDOQL",
+            value="SELECT "
+                    + "FROM com.ecpnv.platform.v1.extensions.executionlog.dom.jdo.events.PublishedEvent "
+                    + "ORDER BY timestamp DESC, transactionId DESC, sequence DESC"),
+    @Query(
+            name="findRecentByUser", language="JDOQL",
+            value="SELECT "
+                    + "FROM com.ecpnv.platform.v1.extensions.executionlog.dom.jdo.events.PublishedEvent "
+                    + "WHERE user == :user "
+                    + "ORDER BY timestamp DESC, transactionId DESC, sequence DESC "
+                    + "RANGE 0,30"),
+    @Query(
+            name="findRecentByTarget", language="JDOQL",
+            value="SELECT "
+                    + "FROM com.ecpnv.platform.v1.extensions.executionlog.dom.jdo.events.PublishedEvent "
+                    + "WHERE targetStr == :targetStr "
+                    + "ORDER BY timestamp DESC, transactionId DESC, sequence DESC "
+                    + "RANGE 0,30")
+})
+@Named("isispublishmq.PublishedEvent")
+@DomainObject(
+        editing = Editing.DISABLED
+)
+@XmlJavaTypeAdapter(PersistentEntitiesAdapter.class)
+public class ExecutionLogEntry extends org.apache.isis.extensions.executionlog.applib.dom.ExecutionLogEntry {
+
+
+    @PrimaryKey
+    @InteractionId
+    @Column(allowsNull = InteractionId.ALLOWS_NULL, length=InteractionId.MAX_LENGTH)
+    @Getter @Setter
+    private UUID interactionId;
+
+
+    @PrimaryKey
+    @Sequence
+    @Column(allowsNull = Sequence.ALLOWS_NULL)
+    @Getter @Setter
+    private int sequence;
+
+
+    @Column(allowsNull = ExecutionType.ALLOWS_NULL, length = ExecutionType.MAX_LENGTH)
+    @ExecutionType
+    @Getter @Setter
+    private ExecutionLogEntryType executionType;
+
+
+    @Column(allowsNull = Username.ALLOWS_NULL, length = Username.MAX_LENGTH)
+    @Username
+    @Getter @Setter
+    private String username;
+
+
+    @Persistent
+    @Column(allowsNull = Timestamp.ALLOWS_NULL)
+    @Timestamp
+    @Getter @Setter
+    private java.sql.Timestamp timestamp;
+
+
+    @Persistent
+    @Column(allowsNull = Target.ALLOWS_NULL, length = Target.MAX_LENGTH)
+    @Target
+    @Getter @Setter
+    private Bookmark target;
+
+
+    @Column(allowsNull = LogicalMemberIdentifier.ALLOWS_NULL, length= LogicalMemberIdentifier.MAX_LENGTH)
+    @LogicalMemberIdentifier
+    @Getter
+    private String logicalMemberIdentifier;
+    public void setLogicalMemberIdentifier(final String logicalMemberIdentifier) {
+        this.logicalMemberIdentifier = Util.abbreviated(logicalMemberIdentifier, LogicalMemberIdentifier.MAX_LENGTH);
+    }
+
+
+    @Persistent
+    @Column(allowsNull = InteractionDtoAnnot.ALLOWS_NULL, jdbcType = "CLOB", sqlType = "LONGVARCHAR")
+    @InteractionDtoAnnot
+    @Getter @Setter
+    private InteractionDto interactionDto;
+
+
+    @Persistent
+    @Column(allowsNull = StartedAt.ALLOWS_NULL)
+    @StartedAt
+    @Getter @Setter
+    private java.sql.Timestamp startedAt;
+
+
+    @Persistent
+    @Column(allowsNull = CompletedAt.ALLOWS_NULL)
+    @CompletedAt
+    @Getter @Setter
+    private java.sql.Timestamp completedAt;
+
+
+
+}
diff --git a/pom.xml b/pom.xml
index 027ff9a263..1aa3bdf604 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,6 +30,8 @@
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <maven.deploy.skip>true</maven.deploy.skip> <!-- don't deploy the aggregator -->
+
+		<maven-timeline.version>1.6</maven-timeline.version>
     </properties>
 
 	<scm>
@@ -39,6 +41,16 @@
 		<tag>HEAD</tag>
 	</scm>
 
+	<build>
+		<extensions>
+			<extension>
+				<groupId>io.takari.maven</groupId>
+				<artifactId>maven-timeline</artifactId>
+				<version>${maven-timeline.version}</version>
+			</extension>
+		</extensions>
+	</build>
+
 	<profiles>
 
 		<!-- ESSENTIAL MODULES -->
@@ -126,7 +138,7 @@
 				<module>tooling</module>
 			</modules>
 		</profile>
-		
+
 		<profile>
 			<id>module-all-except-kroviz</id>
 			<activation>
@@ -137,14 +149,14 @@
 			<modules>
 				<module>antora</module>
 				<module>examples/demo</module>
-				
+
 				<!-- incubator, without kroviz -->
 				<module>incubator/viewers/graphql</module>
 				<module>incubator/viewers/javafx</module>
 				<module>incubator/viewers/vaadin</module>
 				<module>valuetypes/prism/vaadin</module>
 				<module>valuetypes/asciidoc/ui/vaadin</module>
-				
+
 				<module>regressiontests</module>
 				<module>tooling</module>
 			</modules>
diff --git a/serve-timeline.sh b/serve-timeline.sh
new file mode 100644
index 0000000000..2c6b653a09
--- /dev/null
+++ b/serve-timeline.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+#
+# requires JDK 18
+#
+jwebserver -d $(pwd)/target/timeline -b ::
\ No newline at end of file