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 2021/09/24 08:00:03 UTC

[isis] branch ISIS-2735-command-log updated: ISIS-2835: converts jpa queries etc (wip)

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

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


The following commit(s) were added to refs/heads/ISIS-2735-command-log by this push:
     new 6ec66fb  ISIS-2835: converts jpa queries etc (wip)
6ec66fb is described below

commit 6ec66fbb5adcdf1fabcd7e5c06b6e1177c1dac82
Author: Dan Haywood <da...@haywood-associates.co.uk>
AuthorDate: Fri Sep 24 08:59:51 2021 +0100

    ISIS-2835: converts jpa queries etc (wip)
---
 .../system/hooks/HasInteractionId_see-also.adoc    |   2 +-
 .../roles-mixins-contributees/contributee.adoc     |   2 +-
 .../applib/IsisModuleExtCommandLogApplib.java      |   5 +-
 .../commandlog/applib/app/CommandServiceMenu.java  | 140 +++++
 .../dom/PublishedCommandRepositoryAbstract.java    |   7 +-
 .../subscriptions/PublishedCommandSubscriber.java} |  63 ++-
 .../jdo/src/main/java/META-INF/persistence.xml     |   2 +-
 .../commandlog/jdo/IsisModuleExtCommandLogJdo.java |  32 +-
 .../PublishedCommand.java}                         | 182 +++---
 .../PublishedCommandRepository.java}               |  25 +-
 .../commandlog/jdo/ui/CommandServiceMenu.java      | 141 -----
 .../commandlog/jpa/IsisModuleExtCommandLogJpa.java |  36 +-
 .../commandlog/jpa/dom/PublishedCommand.java       | 504 +++++++++++++++++
 .../jpa/dom/PublishedCommandRepository.java}       |  27 +-
 .../commandlog/jpa/entities/CommandJpa.java        | 620 ---------------------
 .../jpa/entities/CommandJpaRepository.java         | 323 -----------
 .../secman/jdo/role/dom/ApplicationRole.java       |  35 +-
 .../pages/hints-and-tips/jdoql-and-timestamps.adoc |   2 +-
 18 files changed, 871 insertions(+), 1277 deletions(-)

diff --git a/antora/components/refguide-index/modules/applib/pages/index/mixins/system/hooks/HasInteractionId_see-also.adoc b/antora/components/refguide-index/modules/applib/pages/index/mixins/system/hooks/HasInteractionId_see-also.adoc
index e473ba0..70d5411 100644
--- a/antora/components/refguide-index/modules/applib/pages/index/mixins/system/hooks/HasInteractionId_see-also.adoc
+++ b/antora/components/refguide-index/modules/applib/pages/index/mixins/system/hooks/HasInteractionId_see-also.adoc
@@ -8,7 +8,7 @@
 Some of the extension/mapping modules that have entities that implement this mixee are:
 
 * xref:security:audit-trail:about.adoc[Auditer] module: `AuditEntry` entity
-* xref:userguide:command-log:about.adoc[Command Log] module: `PublishedCommandForJdo` entity
+* xref:userguide:command-log:about.adoc[Command Log] module: `PublishedCommand` entity
 * xref:mappings:outbox-publisher:about.adoc[Outbox Publisher] module: `OutboxEvent` entity
 
 
diff --git a/antora/components/refguide/modules/applib-classes/pages/roles-mixins-contributees/contributee.adoc b/antora/components/refguide/modules/applib-classes/pages/roles-mixins-contributees/contributee.adoc
index 763c42e..4c70c38 100644
--- a/antora/components/refguide/modules/applib-classes/pages/roles-mixins-contributees/contributee.adoc
+++ b/antora/components/refguide/modules/applib-classes/pages/roles-mixins-contributees/contributee.adoc
@@ -36,7 +36,7 @@ Some of the extension/mapping modules that have domain entity/ies that implement
 
 * xref:security:ROOT:about.adoc[SecMan extension]: `ApplicationUser` entity
 * xref:security:audit-trail:about.adoc[Auditer] module: `AuditEntry` entity
-* xref:userguide:command-log:about.adoc[Command Log] module: `PublishedCommandForJdo` entity
+* xref:userguide:command-log:about.adoc[Command Log] module: `PublishedCommand` entity
 * xref:mappings:outbox-publisher:about.adoc[Outbox Publisher] module: `OutboxEvent` entity
 * xref:security:session-log:about.adoc[Session Log] module: `SessionLogEntry` entity
 * xref:subdomains:settings:about.adoc[Settings] module: `UserSettingJdo` entity
diff --git a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/IsisModuleExtCommandLogApplib.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/IsisModuleExtCommandLogApplib.java
index fc2a2f3..1b3eba3 100644
--- a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/IsisModuleExtCommandLogApplib.java
+++ b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/IsisModuleExtCommandLogApplib.java
@@ -22,7 +22,9 @@ package org.apache.isis.extensions.commandlog.applib;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
+import org.apache.isis.extensions.commandlog.applib.app.CommandServiceMenu;
 import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand;
+import org.apache.isis.extensions.commandlog.applib.subscriptions.PublishedCommandSubscriber;
 import org.apache.isis.testing.fixtures.applib.IsisModuleTestingFixturesApplib;
 
 /**
@@ -33,8 +35,9 @@ import org.apache.isis.testing.fixtures.applib.IsisModuleTestingFixturesApplib;
         // modules
         IsisModuleTestingFixturesApplib.class,
 
-
         // @Services
+        CommandServiceMenu.class,
+        PublishedCommandSubscriber.class,
         PublishedCommand.TitleProvider.class,
         PublishedCommand.TableColumnOrderDefault.class,
 
diff --git a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/app/CommandServiceMenu.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/app/CommandServiceMenu.java
new file mode 100644
index 0000000..dbbf7e4
--- /dev/null
+++ b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/app/CommandServiceMenu.java
@@ -0,0 +1,140 @@
+/*
+ *  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.commandlog.applib.app;
+
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.BookmarkPolicy;
+import org.apache.isis.applib.annotation.Domain;
+import org.apache.isis.applib.annotation.DomainService;
+import org.apache.isis.applib.annotation.DomainServiceLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
+import org.apache.isis.applib.annotation.NatureOfService;
+import org.apache.isis.applib.annotation.Optionality;
+import org.apache.isis.applib.annotation.Parameter;
+import org.apache.isis.applib.annotation.ParameterLayout;
+import org.apache.isis.applib.annotation.PriorityPrecedence;
+import org.apache.isis.applib.annotation.RestrictTo;
+import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.applib.services.clock.ClockService;
+import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommandRepository;
+
+import lombok.RequiredArgsConstructor;
+
+/**
+ * @since 2.0 {@index}
+ */
+@DomainService(
+    nature = NatureOfService.VIEW,
+    logicalTypeName = CommandServiceMenu.LOGICAL_TYPE_NAME
+)
+@DomainServiceLayout(
+    named = "Activity",
+    menuBar = DomainServiceLayout.MenuBar.SECONDARY
+)
+@Named(CommandServiceMenu.LOGICAL_TYPE_NAME)
+@javax.annotation.Priority(PriorityPrecedence.MIDPOINT)
+@RequiredArgsConstructor
+public class CommandServiceMenu {
+
+    public final static String LOGICAL_TYPE_NAME = IsisModuleExtCommandLogApplib.NAMESPACE + ".CommandServiceMenu";
+
+    public static abstract class ActionDomainEvent<T> extends IsisModuleExtCommandLogApplib.ActionDomainEvent<T> { }
+
+    final ClockService clockService;
+    final PublishedCommandRepository publishedCommandRepository;
+
+
+    @Action(domainEvent = activeCommands.DomainEvent.class, semantics = SemanticsOf.SAFE)
+    @ActionLayout(bookmarking = BookmarkPolicy.AS_ROOT, cssClassFa = "fa-bolt", sequence="10")
+    public class activeCommands {
+        public class DomainEvent extends ActionDomainEvent<CommandServiceMenu.activeCommands> { }
+
+        @MemberSupport public List<PublishedCommand> act() {
+            return publishedCommandRepository.findCurrent();
+        }
+        @MemberSupport public boolean hideAct() {
+            return publishedCommandRepository == null;
+        }
+    }
+
+
+    @Action(domainEvent = findCommands.DomainEvent.class, semantics = SemanticsOf.SAFE)
+    @ActionLayout(cssClassFa = "fa-search", sequence="20")
+    public class findCommands {
+        public class DomainEvent extends ActionDomainEvent<CommandServiceMenu.findCommands> { }
+
+        @MemberSupport public List<PublishedCommand> act(
+                final @Nullable LocalDate from,
+                final @Nullable LocalDate to) {
+            return publishedCommandRepository.findByFromAndTo(from, to);
+        }
+        @MemberSupport public boolean hideAct() {
+            return publishedCommandRepository == null;
+        }
+        @MemberSupport public LocalDate default0Act() {
+            return now().minusDays(7);
+        }
+        @MemberSupport public LocalDate default1Act() {
+            return now();
+        }
+    }
+
+
+    @Action(domainEvent = findCommandById.DomainEvent.class, semantics = SemanticsOf.SAFE)
+    @ActionLayout(cssClassFa = "fa-crosshairs", sequence="30")
+    public class findCommandById {
+        public class DomainEvent extends ActionDomainEvent<CommandServiceMenu.findCommandById> { }
+
+        @MemberSupport public PublishedCommand act(
+                final UUID transactionId) {
+            return publishedCommandRepository.findByInteractionId(transactionId).orElse(null);
+        }
+        @MemberSupport public boolean hideAct() {
+            return publishedCommandRepository == null;
+        }
+    }
+
+
+    @Action(domainEvent = truncateLog.DomainEvent.class, semantics = SemanticsOf.IDEMPOTENT_ARE_YOU_SURE, restrictTo = RestrictTo.PROTOTYPING)
+    @ActionLayout(cssClassFa = "fa-trash", sequence="40")
+    public class truncateLog {
+        public class DomainEvent extends ActionDomainEvent<CommandServiceMenu.truncateLog> { }
+
+        @MemberSupport public void act() {
+            publishedCommandRepository.truncateLog();
+        }
+    }
+
+    private LocalDate now() {
+        return clockService.getClock().nowAsLocalDate(ZoneId.systemDefault());
+    }
+}
+
diff --git a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/PublishedCommandRepositoryAbstract.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/PublishedCommandRepositoryAbstract.java
index 2b329b4..ba19aa0 100644
--- a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/PublishedCommandRepositoryAbstract.java
+++ b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/PublishedCommandRepositoryAbstract.java
@@ -43,6 +43,7 @@ import org.apache.isis.applib.services.bookmark.Bookmark;
 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.commons.internal.factory._InstanceUtil;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 import org.apache.isis.schema.cmd.v2.CommandsDto;
 import org.apache.isis.schema.cmd.v2.MapDto;
@@ -327,8 +328,6 @@ implements PublishedCommandRepository {
         return publishedCommand;
     }
 
-    protected abstract PublishedCommand newPublishedCommand();
-
     @Override
     public void persist(final PublishedCommand publishedCommand) {
         repositoryService().persist(publishedCommand);
@@ -339,6 +338,10 @@ implements PublishedCommandRepository {
         repositoryService().removeAll(PublishedCommand.class);
     }
 
+    public PublishedCommand newPublishedCommand() {
+        return _InstanceUtil.createInstance(publishedCommandClass, PublishedCommand.class);
+    }
+
     private RepositoryService repositoryService() {
         return repositoryServiceProvider.get();
     }
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/CommandSubscriberForJdo.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/subscriptions/PublishedCommandSubscriber.java
similarity index 53%
rename from extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/CommandSubscriberForJdo.java
rename to extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/subscriptions/PublishedCommandSubscriber.java
index 13a9e24..2d8a541 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/CommandSubscriberForJdo.java
+++ b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/subscriptions/PublishedCommandSubscriber.java
@@ -16,9 +16,10 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.extensions.commandlog.jdo;
+package org.apache.isis.extensions.commandlog.applib.subscriptions;
+
+import java.util.UUID;
 
-import javax.inject.Inject;
 import javax.inject.Named;
 
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -28,25 +29,24 @@ import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.applib.services.publishing.spi.CommandSubscriber;
 import org.apache.isis.applib.util.JaxbUtil;
-import org.apache.isis.extensions.commandlog.jdo.entities.PublishedCommandForJdo;
-import org.apache.isis.extensions.commandlog.jdo.entities.PublishedCommandForJdoRepository;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommandRepositoryAbstract;
 
 import lombok.RequiredArgsConstructor;
-import lombok.val;
 import lombok.extern.log4j.Log4j2;
+import lombok.val;
 
 /**
  * @since 2.0 {@index}
  */
 @Service
-@Named("isis.ext.commandLog.CommandSubscriberForJdo")
+@Named("isis.ext.commandLog.PublishedCommandSubscriber")
 @javax.annotation.Priority(PriorityPrecedence.MIDPOINT) // after JdoPersistenceLifecycleService
-@Qualifier("Jdo")
+@Qualifier("Default")
 @Log4j2
 @RequiredArgsConstructor
-public class CommandSubscriberForJdo implements CommandSubscriber {
+public class PublishedCommandSubscriber implements CommandSubscriber {
 
-    @Inject final PublishedCommandForJdoRepository commandJdoRepository;
+    final PublishedCommandRepositoryAbstract<?> publishedCommandRepository;
 
     @Override
     public void onCompleted(Command command) {
@@ -55,33 +55,38 @@ public class CommandSubscriberForJdo implements CommandSubscriber {
             return;
         }
 
-        val existingCommandJdoIfAny =
-                commandJdoRepository.findByInteractionId(command.getInteractionId());
-        if(existingCommandJdoIfAny.isPresent()) {
+        var commandInteractionId = command.getInteractionId();
+        final var existingCommandIfAny =
+                publishedCommandRepository.findByInteractionId(commandInteractionId);
+
+        if(existingCommandIfAny.isPresent()) {
+            // this isn't expected to happen; we just log the fact if it does
+            log.warn("Unexpectedly found existing command with interactionId '{}'; ignoring", commandInteractionId);
+
             if(log.isDebugEnabled()) {
-                // this isn't expected to happen ... we just log the fact if it does
-                val existingCommandDto = existingCommandJdoIfAny.get().getCommandDto();
 
-                val existingCommandDtoXml = JaxbUtil.toXml(existingCommandDto).presentElse("Dto to Xml failure");
-                val commandDtoXml = JaxbUtil.toXml(command.getCommandDto()).presentElse("Dto to Xml failure");
+                val existingCommandDto = existingCommandIfAny.get().getCommandDto();
+
+                val existingCommandDtoXml = JaxbUtil.toXml(existingCommandDto).presentElse("Could not extract XML for existing command");
+                val commandDtoXml = JaxbUtil.toXml(command.getCommandDto()).presentElse("Could not extract XML for received command");
 
                 log.debug("existing: \n{}", existingCommandDtoXml);
-                log.debug("proposed: \n{}", commandDtoXml);
+                log.debug("received: \n{}", commandDtoXml);
             }
-        } else {
-            val commandJdo = new PublishedCommandForJdo(command);
-            val parent = command.getParent();
-            val parentJdo =
-                parent != null
-                    ? commandJdoRepository
-                        .findByInteractionId(parent.getInteractionId())
-                        .orElse(null)
-                    : null;
-            commandJdo.setParent(parentJdo);
-            commandJdoRepository.persist(commandJdo);
+            return;
         }
 
-
+        val publishedCommand = publishedCommandRepository.newPublishedCommand();
+        val parent = command.getParent();
+        val parentCommand =
+            parent != null
+                ? publishedCommandRepository
+                    .findByInteractionId(parent.getInteractionId())
+                    .orElse(null)
+                : null;
+        publishedCommand.setParent(parentCommand);
+        publishedCommandRepository.persist(publishedCommand);
     }
 
+
 }
diff --git a/extensions/core/command-log/jdo/src/main/java/META-INF/persistence.xml b/extensions/core/command-log/jdo/src/main/java/META-INF/persistence.xml
index 9a6b5df..974fec7 100644
--- a/extensions/core/command-log/jdo/src/main/java/META-INF/persistence.xml
+++ b/extensions/core/command-log/jdo/src/main/java/META-INF/persistence.xml
@@ -14,6 +14,6 @@
     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
 
     <persistence-unit name="org-apache-isis-extensions-commandlog">
-        <class>org.apache.isis.extensions.commandlog.jdo.entities.PublishedCommandForJdo</class>
+        <class>org.apache.isis.extensions.commandlog.jdo.dom.PublishedCommand</class>
     </persistence-unit>
 </persistence>
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java
index 2a06374..0d71b9a 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java
+++ b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java
@@ -22,11 +22,11 @@ import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
-import org.apache.isis.extensions.commandlog.jdo.entities.PublishedCommandForJdo;
-import org.apache.isis.extensions.commandlog.jdo.entities.PublishedCommandForJdoRepository;
-import org.apache.isis.extensions.commandlog.jdo.ui.CommandServiceMenu;
 import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
+import org.apache.isis.extensions.commandlog.jdo.dom.PublishedCommand;
+import org.apache.isis.extensions.commandlog.jdo.dom.PublishedCommandRepository;
 import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
+import org.apache.isis.testing.fixtures.applib.modules.ModuleWithFixtures;
 import org.apache.isis.testing.fixtures.applib.teardown.jdo.TeardownFixtureJdoAbstract;
 
 /**
@@ -34,34 +34,34 @@ import org.apache.isis.testing.fixtures.applib.teardown.jdo.TeardownFixtureJdoAb
  */
 @Configuration
 @Import({
-        // @DomainService's
-        PublishedCommandForJdoRepository.class
-        , CommandServiceMenu.class
+        // module dependencies
+        IsisModuleExtCommandLogApplib.class
 
-        // @Service's
-        , PublishedCommandForJdo.TableColumnOrderDefault.class
+        // @DomainService's
+        , PublishedCommandRepository.class
 
         // entities
-        , PublishedCommandForJdo.class
+        , PublishedCommand.class
 })
 @ComponentScan(
         basePackageClasses= {
                 IsisModuleExtCommandLogJdo.class
         })
-public class IsisModuleExtCommandLogJdo
-implements IsisModuleExtCommandLogApplib {
-
-    public static final String NAMESPACE = "isis.ext.commandLog";
+public class IsisModuleExtCommandLogJdo {
 
     /**
      * For tests that need to delete the command table first.
-     * Should be run in the @Before of the test.
+     * Should be run in the <code>@Before</code> of the test.
+     *
+     * <p>
+     *     NOTE: this class deliberately does <i>not</i> implement {@link ModuleWithFixtures}.
+     * </p>
      */
-    public FixtureScript getTeardownFixtureWillDelete() {
+    public FixtureScript getTeardownFixture() {
         return new TeardownFixtureJdoAbstract() {
             @Override
             protected void execute(final ExecutionContext executionContext) {
-                deleteFrom(PublishedCommandForJdo.class);
+                deleteFrom(PublishedCommand.class);
             }
         };
     }
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/PublishedCommandForJdo.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/dom/PublishedCommand.java
similarity index 66%
rename from extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/PublishedCommandForJdo.java
rename to extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/dom/PublishedCommand.java
index b0d2679..13982e5 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/PublishedCommandForJdo.java
+++ b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/dom/PublishedCommand.java
@@ -16,20 +16,31 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.extensions.commandlog.jdo.entities;
+package org.apache.isis.extensions.commandlog.jdo.dom;
 
 import java.sql.Timestamp;
 import java.util.UUID;
 
+import javax.jdo.annotations.DatastoreIdentity;
+import javax.jdo.annotations.IdGeneratorStrategy;
 import javax.jdo.annotations.IdentityType;
+import javax.jdo.annotations.Index;
+import javax.jdo.annotations.Indices;
+import javax.jdo.annotations.Inheritance;
+import javax.jdo.annotations.InheritanceStrategy;
+import javax.jdo.annotations.PersistenceCapable;
+import javax.jdo.annotations.Queries;
+import javax.jdo.annotations.Query;
+import javax.jdo.annotations.Unique;
+import javax.jdo.annotations.Uniques;
 
 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.mixins.system.DomainChangeRecord;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.types.MemberIdentifierType;
 import org.apache.isis.commons.internal.base._Casts;
-import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand;
 import org.apache.isis.extensions.commandlog.applib.dom.ReplayState;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
@@ -38,162 +49,176 @@ import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.Setter;
 
-@javax.jdo.annotations.PersistenceCapable(
+@PersistenceCapable(
         identityType=IdentityType.APPLICATION,
         schema = "isisExtensionsCommandLog",
-        table = "PublishedCommand")
-
+        table = "PublishedCommand"
+)
+@Uniques({
+        @Unique(
+                name = "PublishedCommand__interactionId__UNQ",
+                members = {
+                        "interactionId"
+                })
+})
+@Indices({
+        @Index(
+                name = "PublishedCommand__startedAt__timestamp__IDX",
+                members = {"startedAt", "timestamp"}),
+        @Index(
+                name = "PublishedCommand__timestamp__IDX",
+                members = {"timestamp"}
+        ),
+})
+@Inheritance(
+        strategy = InheritanceStrategy.NEW_TABLE)
+@DatastoreIdentity(
+        strategy = IdGeneratorStrategy.NATIVE, column = "id")
 // queries that use RANGE 0,2 should instead be RANGE 0,1, however this results in DataNucleus submitting "FETCH NEXT ROW ONLY"
 // which SQL Server doesn't understand.  However, as workaround, SQL Server *does* understand FETCH NEXT 2 ROWS ONLY
-@javax.jdo.annotations.Queries( {
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_BY_INTERACTION_ID,
+@Queries( {
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_INTERACTION_ID,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE interactionId == :interactionId "),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_BY_PARENT,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_PARENT,
             value="SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE parent == :parent "),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_CURRENT,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_CURRENT,
             value="SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE completedAt == null "
                     + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_COMPLETED,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_COMPLETED,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE completedAt != null "
                     + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_RECENT_BY_TARGET,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_RECENT_BY_TARGET,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE target == :target "
                     + "ORDER BY this.timestamp DESC "
                     + "RANGE 0,30"),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_BY_TARGET_AND_TIMESTAMP_BETWEEEN,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_TARGET_AND_TIMESTAMP_BETWEEEN,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE target == :target "
                     + "&& timestamp >= :from "
                     + "&& timestamp <= :to "
                     + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_BY_TARGET_AND_TIMESTAMP_AFTER,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_TARGET_AND_TIMESTAMP_AFTER,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE target == :target "
                     + "&& timestamp >= :from "
                     + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_BY_TARGET_AND_TIMESTAMP_BEFORE,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_TARGET_AND_TIMESTAMP_BEFORE,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE target == :target "
                     + "&& timestamp <= :to "
                     + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_BY_TARGET,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_TARGET,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE target == :target "
                     + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_BY_TIMESTAMP_BETWEEN,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_TIMESTAMP_BETWEEN,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE timestamp >= :from "
                     + "&&    timestamp <= :to "
                     + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_BY_TIMESTAMP_AFTER,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_TIMESTAMP_AFTER,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE timestamp >= :from "
                     + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_BY_TIMESTAMP_BEFORE,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_TIMESTAMP_BEFORE,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE timestamp <= :to "
                     + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_RECENT_BY_USERNAME,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_RECENT_BY_USERNAME,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE username == :username "
                     + "ORDER BY this.timestamp DESC "
                     + "RANGE 0,30"),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_FIRST,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_FIRST,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
-                    + " WHERE startedAt   != null "
+                    + "FROM " + PublishedCommand.FQCN
+                    + " WHERE startedAt  != null "
                     + "   && completedAt != null "
                     + "ORDER BY this.timestamp ASC "
                     + "RANGE 0,2"),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_SINCE,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_SINCE,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE timestamp > :timestamp "
                     + "   && startedAt != null "
                     + "   && completedAt != null "
                     + "ORDER BY this.timestamp ASC"),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_MOST_RECENT_REPLAYED,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_MOST_RECENT_REPLAYED,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE (replayState == 'OK' || replayState == 'FAILED') "
                     + "ORDER BY this.timestamp DESC "
                     + "RANGE 0,2"),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_MOST_RECENT_COMPLETED,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_MOST_RECENT_COMPLETED,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE startedAt   != null "
                     + "   && completedAt != null "
                     + "ORDER BY this.timestamp DESC "
                     + "RANGE 0,2"),
-    @javax.jdo.annotations.Query(
-            name = PublishedCommand.NAMED_QUERY_FIND_NOT_YET_REPLAYED,
+    @Query(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_NOT_YET_REPLAYED,
             value = "SELECT "
-                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + "FROM " + PublishedCommand.FQCN
                     + " WHERE replayState == 'PENDING' "
                     + "ORDER BY this.timestamp ASC "
                     + "RANGE 0,10"),    // same as batch size
 })
-@javax.jdo.annotations.Indices({
-        @javax.jdo.annotations.Index(name = "CommandJdo__startedAt__timestamp__IDX", members = { "startedAt", "timestamp" }),
-        @javax.jdo.annotations.Index(name = "CommandJdo__timestamp__IDX", members = { "timestamp" }),
-})
 @DomainObject(
-        logicalTypeName = PublishedCommand.LOGICAL_TYPE_NAME
-)
-@DomainObjectLayout(
-        named = "Published Command"
+        logicalTypeName = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.LOGICAL_TYPE_NAME,
+        editing = Editing.DISABLED
 )
 @NoArgsConstructor(access = AccessLevel.PACKAGE)
-public class PublishedCommandForJdo extends PublishedCommand {
+public class PublishedCommand extends org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand {
 
-    protected final static String FQCN = "org.apache.isis.extensions.commandlog.jdo.entities.CommandJdo";
+    protected final static String FQCN = "org.apache.isis.extensions.commandlog.jdo.dom.PublishedCommand";
 
     // EVENTS
     // (see superclass)
 
     // CONSTRUCTORS
 
-    public PublishedCommandForJdo(
+    public PublishedCommand(
             final CommandDto commandDto,
             final ReplayState replayState,
             final int targetIndex) {
@@ -294,15 +319,15 @@ public class PublishedCommandForJdo extends PublishedCommand {
 
     @javax.jdo.annotations.Persistent
     @javax.jdo.annotations.Column(name="parentId", allowsNull="true")
-    private PublishedCommandForJdo parent;
+    private PublishedCommand parent;
 
     @Parent
     @Override
-    public PublishedCommand getParent() {
+    public org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand getParent() {
         return parent;
     }
     @Override
-    public void setParent(PublishedCommand parent) {
+    public void setParent(org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand parent) {
         this.parent = _Casts.uncheckedCast(parent);
     }
 
@@ -338,7 +363,6 @@ public class PublishedCommandForJdo extends PublishedCommand {
     // LOGICAL MEMBER IDENTIFIER
 
     @javax.jdo.annotations.Column(allowsNull="false", length = MemberIdentifierType.Meta.MAX_LEN)
-    @Getter @Setter
     private String logicalMemberIdentifier;
 
     @LogicalMemberIdentifier
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/PublishedCommandForJdoRepository.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/dom/PublishedCommandRepository.java
similarity index 63%
copy from extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/PublishedCommandForJdoRepository.java
copy to extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/dom/PublishedCommandRepository.java
index 7f38ebc..12c0043 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/PublishedCommandForJdoRepository.java
+++ b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/dom/PublishedCommandRepository.java
@@ -16,37 +16,28 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.extensions.commandlog.jdo.entities;
+package org.apache.isis.extensions.commandlog.jdo.dom;
 
-import javax.inject.Inject;
 import javax.inject.Named;
 
-import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Service;
 
-import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand;
+import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommandRepositoryAbstract;
-import org.apache.isis.persistence.jdo.applib.services.JdoSupportService;
 
-import lombok.RequiredArgsConstructor;
-
-/**
- * Provides supporting functionality for querying and persisting
- * {@link PublishedCommandForJdo command} entities.
- */
 @Service
 @Named("isis.ext.commandLog.PublishedCommandRepository")
 @javax.annotation.Priority(PriorityPrecedence.MIDPOINT)
 @Qualifier("Jdo")
-public class PublishedCommandForJdoRepository
-extends PublishedCommandRepositoryAbstract<PublishedCommandForJdo> {
+public class PublishedCommandRepository
+extends PublishedCommandRepositoryAbstract<PublishedCommand> {
 
-    public PublishedCommandForJdoRepository() {
-        super(PublishedCommandForJdo.class);
+    public PublishedCommandRepository() {
+        super(PublishedCommand.class);
     }
 
-    @Override protected PublishedCommand newPublishedCommand() {
-        return new PublishedCommandForJdo();
+    @Override public PublishedCommand newPublishedCommand() {
+        return new PublishedCommand();
     }
 }
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/ui/CommandServiceMenu.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/ui/CommandServiceMenu.java
deleted file mode 100644
index e50a186..0000000
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/ui/CommandServiceMenu.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- *  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.commandlog.jdo.ui;
-
-import java.time.LocalDate;
-import java.time.ZoneId;
-import java.util.List;
-import java.util.UUID;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import org.springframework.beans.factory.annotation.Qualifier;
-
-import org.apache.isis.applib.annotation.Action;
-import org.apache.isis.applib.annotation.ActionLayout;
-import org.apache.isis.applib.annotation.BookmarkPolicy;
-import org.apache.isis.applib.annotation.DomainService;
-import org.apache.isis.applib.annotation.DomainServiceLayout;
-import org.apache.isis.applib.annotation.MemberSupport;
-import org.apache.isis.applib.annotation.NatureOfService;
-import org.apache.isis.applib.annotation.Optionality;
-import org.apache.isis.applib.annotation.Parameter;
-import org.apache.isis.applib.annotation.ParameterLayout;
-import org.apache.isis.applib.annotation.PriorityPrecedence;
-import org.apache.isis.applib.annotation.RestrictTo;
-import org.apache.isis.applib.annotation.SemanticsOf;
-import org.apache.isis.applib.services.clock.ClockService;
-import org.apache.isis.extensions.commandlog.jdo.IsisModuleExtCommandLogJdo;
-import org.apache.isis.extensions.commandlog.jdo.entities.PublishedCommandForJdo;
-import org.apache.isis.extensions.commandlog.jdo.entities.PublishedCommandForJdoRepository;
-
-import lombok.RequiredArgsConstructor;
-
-/**
- * @since 2.0 {@index}
- */
-@DomainService(
-    nature = NatureOfService.VIEW,
-    logicalTypeName = CommandServiceMenu.LOGICAL_TYPE_NAME
-)
-@DomainServiceLayout(
-    named = "Activity",
-    menuBar = DomainServiceLayout.MenuBar.SECONDARY
-)
-@Named(CommandServiceMenu.LOGICAL_TYPE_NAME)
-@javax.annotation.Priority(PriorityPrecedence.EARLY)
-@Qualifier("Jdo")
-@RequiredArgsConstructor(onConstructor_ = { @Inject })
-public class CommandServiceMenu {
-
-    public final static String LOGICAL_TYPE_NAME = IsisModuleExtCommandLogJdo.NAMESPACE + ".CommandServiceMenu";
-
-    public static abstract class PropertyDomainEvent<T>
-            extends IsisModuleExtCommandLogJdo.PropertyDomainEvent<CommandServiceMenu, T> { }
-    public static abstract class CollectionDomainEvent<T>
-            extends IsisModuleExtCommandLogJdo.CollectionDomainEvent<CommandServiceMenu, T> { }
-    public static abstract class ActionDomainEvent
-            extends IsisModuleExtCommandLogJdo.ActionDomainEvent<CommandServiceMenu> {
-    }
-
-    final PublishedCommandForJdoRepository commandServiceRepository;
-    final ClockService clockService;
-
-    public static class ActiveCommandsDomainEvent extends ActionDomainEvent { }
-    @Action(domainEvent = ActiveCommandsDomainEvent.class, semantics = SemanticsOf.SAFE)
-    @ActionLayout(bookmarking = BookmarkPolicy.AS_ROOT, cssClassFa = "fa-bolt", sequence="10")
-    public List<PublishedCommandForJdo> activeCommands() {
-        return commandServiceRepository.findCurrent();
-    }
-    @MemberSupport public boolean hideActiveCommands() {
-        return commandServiceRepository == null;
-    }
-
-
-    public static class FindCommandsDomainEvent extends ActionDomainEvent { }
-    @Action(domainEvent = FindCommandsDomainEvent.class, semantics = SemanticsOf.SAFE)
-    @ActionLayout(cssClassFa = "fa-search", sequence="20")
-    public List<PublishedCommandForJdo> findCommands(
-            @Parameter(optionality= Optionality.OPTIONAL)
-            @ParameterLayout(named="From")
-            final LocalDate from,
-            @Parameter(optionality= Optionality.OPTIONAL)
-            @ParameterLayout(named="To")
-            final LocalDate to) {
-        return commandServiceRepository.findByFromAndTo(from, to);
-    }
-    @MemberSupport public boolean hideFindCommands() {
-        return commandServiceRepository == null;
-    }
-    @MemberSupport public LocalDate default0FindCommands() {
-        return now().minusDays(7);
-    }
-    @MemberSupport public LocalDate default1FindCommands() {
-        return now();
-    }
-
-
-    public static class FindCommandByIdDomainEvent extends ActionDomainEvent { }
-    @Action(domainEvent = FindCommandByIdDomainEvent.class, semantics = SemanticsOf.SAFE)
-    @ActionLayout(cssClassFa = "fa-crosshairs", sequence="30")
-    public PublishedCommandForJdo findCommandById(
-            @ParameterLayout(named="Transaction Id")
-            final UUID transactionId) {
-        return commandServiceRepository.findByInteractionId(transactionId).orElse(null);
-    }
-    @MemberSupport public boolean hideFindCommandById() {
-        return commandServiceRepository == null;
-    }
-
-
-    public static class TruncateLogDomainEvent extends ActionDomainEvent { }
-    @Action(domainEvent = TruncateLogDomainEvent.class, semantics = SemanticsOf.IDEMPOTENT_ARE_YOU_SURE, restrictTo = RestrictTo.PROTOTYPING)
-    @ActionLayout(cssClassFa = "fa-trash", sequence="40")
-    public void truncateLog() {
-        commandServiceRepository.truncateLog();
-    }
-
-
-    private LocalDate now() {
-        return clockService.getClock().nowAsLocalDate(ZoneId.systemDefault());
-    }
-
-}
-
diff --git a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/IsisModuleExtCommandLogJpa.java b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/IsisModuleExtCommandLogJpa.java
index 41a55ac..ac8b754 100644
--- a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/IsisModuleExtCommandLogJpa.java
+++ b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/IsisModuleExtCommandLogJpa.java
@@ -22,23 +22,26 @@ import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
-import org.apache.isis.extensions.commandlog.jpa.entities.CommandJpa;
-import org.apache.isis.extensions.commandlog.jpa.entities.CommandJpaRepository;
+import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
+import org.apache.isis.extensions.commandlog.jpa.dom.PublishedCommand;
+import org.apache.isis.extensions.commandlog.jpa.dom.PublishedCommandRepository;
+import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
+import org.apache.isis.testing.fixtures.applib.modules.ModuleWithFixtures;
+import org.apache.isis.testing.fixtures.applib.teardown.jdo.TeardownFixtureJdoAbstract;
 
 /**
  * @since 2.0 {@index}
  */
 @Configuration
 @Import({
-        // @DomainService's
-        CommandJpaRepository.class
-//TODO        , CommandServiceMenu.class
+        // module dependencies
+        IsisModuleExtCommandLogApplib.class
 
-        // @Service's
-        , CommandJpa.TableColumnOrderDefault.class
+        // @DomainService's
+        , PublishedCommandRepository.class
 
         // entities
-        , CommandJpa.class
+        , PublishedCommand.class
 })
 @ComponentScan(
         basePackageClasses= {
@@ -46,6 +49,21 @@ import org.apache.isis.extensions.commandlog.jpa.entities.CommandJpaRepository;
         })
 public class IsisModuleExtCommandLogJpa {
 
-    public static final String NAMESPACE = "isis.ext.commandLog";
+    /**
+     * For tests that need to delete the command table first.
+     * Should be run in the <code>@Before</code> of the test.
+     *
+     * <p>
+     *     NOTE: this class deliberately does <i>not</i> implement {@link ModuleWithFixtures}.
+     * </p>
+     */
+    public FixtureScript getTeardownFixture() {
+        return new TeardownFixtureJdoAbstract() {
+            @Override
+            protected void execute(final ExecutionContext executionContext) {
+                deleteFrom(PublishedCommand.class);
+            }
+        };
+    }
 
 }
diff --git a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/dom/PublishedCommand.java b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/dom/PublishedCommand.java
new file mode 100644
index 0000000..53967a4
--- /dev/null
+++ b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/dom/PublishedCommand.java
@@ -0,0 +1,504 @@
+/*
+ *  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.commandlog.jpa.dom;
+
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+import javax.persistence.Entity;
+import javax.persistence.EntityListeners;
+import javax.persistence.Index;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.Editing;
+import org.apache.isis.applib.annotation.MemberSupport;
+import org.apache.isis.applib.annotation.PriorityPrecedence;
+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.mixins.system.DomainChangeRecord;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.applib.services.command.CommandOutcomeHandler;
+import org.apache.isis.applib.services.tablecol.TableColumnOrderForCollectionTypeAbstract;
+import org.apache.isis.applib.types.MemberIdentifierType;
+import org.apache.isis.commons.internal.base._Casts;
+import org.apache.isis.commons.internal.base._Strings;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
+import org.apache.isis.extensions.commandlog.applib.dom.ReplayState;
+import org.apache.isis.extensions.commandlog.applib.util.BigDecimalUtils;
+import org.apache.isis.extensions.commandlog.applib.util.StringUtils;
+import org.apache.isis.persistence.jpa.applib.integration.IsisEntityListener;
+import org.apache.isis.schema.cmd.v2.CommandDto;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.val;
+
+@Entity
+@Table(
+        schema = "isisExtensionsCommandLog",
+        name = "PublishedCommand",
+        uniqueConstraints =
+        @UniqueConstraint(
+                name = "PublishedCommand__interactionId__UNQ",
+                columnNames = {
+                        "interactionId"
+                }),
+        indexes = {
+                @Index(
+                        name = "PublishedCommand__startedAt__timestamp__IDX",
+                        columnList = "startedAt, timestamp"
+                ),
+                @Index(
+                        name = "PublishedCommand__timestamp__IDX",
+                        columnList = "timestamp"
+                )
+        }
+)
+@NamedQueries({
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_INTERACTION_ID,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.interactionId = :interactionId"),
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_PARENT,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.parent = :parent "),
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_CURRENT,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.completedAt is null "
+                    + "ORDER BY pc.timestamp DESC"),
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_COMPLETED,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.completedAt is not null "
+                    + "ORDER BY pc.timestamp DESC"),
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_RECENT_BY_TARGET,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.target = :target "
+                    + "ORDER BY pc.timestamp DESC "),
+                    // TODO: query.setMaxResults(30)
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_TARGET_AND_TIMESTAMP_BETWEEEN,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.target = :target "
+                    + "   AND pc.timestamp >= :from "
+                    + "   AND pc.timestamp <= :to "
+                    + "ORDER BY pc.timestamp DESC"),
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_TARGET_AND_TIMESTAMP_AFTER,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.target = :target "
+                    + "   AND pc.timestamp >= :from "
+                    + "ORDER BY pc.timestamp DESC"),
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_TARGET_AND_TIMESTAMP_BEFORE,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.target = :target "
+                    + "   AND pc.timestamp <= :to "
+                    + "ORDER BY pc.timestamp DESC"),
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_TARGET,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.target = :target "
+                    + "ORDER BY pc.timestamp DESC"),
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_TIMESTAMP_BETWEEN,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.timestamp >= :from "
+                    + "   AND pc.timestamp <= :to "
+                    + "ORDER BY pc.timestamp DESC"),
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_TIMESTAMP_AFTER,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.timestamp >= :from "
+                    + "ORDER BY pc.timestamp DESC"),
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_BY_TIMESTAMP_BEFORE,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.timestamp <= :to "
+                    + "ORDER BY pc.timestamp DESC"),
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " ORDER BY pc.timestamp DESC"),
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_RECENT_BY_USERNAME,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.username = :username "
+                    + "ORDER BY pc.timestamp DESC "),
+                    // TODO: query.setMaxResults(30)
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_FIRST,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.startedAt   is not null "
+                    + "   and pc.completedAt is not null "
+                    + "ORDER BY pc.timestamp ASC "),
+                    // TODO: query.setMaxResults(2)
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_SINCE,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.timestamp > :timestamp "
+                    + "   AND pc.startedAt   is not null "
+                    + "   AND pc.completedAt is not null "
+                    + "ORDER BY pc.timestamp ASC"),
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_MOST_RECENT_REPLAYED,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.replayState = 'OK' OR pc.replayState = 'FAILED' "
+                    + "ORDER BY pc.timestamp DESC "),
+                    // TODO: query.setMaxResults(2)
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_MOST_RECENT_COMPLETED,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.startedAt   is not null "
+                    + "   AND pc.completedAt is not null "
+                    + "ORDER BY pc.timestamp DESC "),
+                    // TODO: query.setMaxResults(2)
+    @NamedQuery(
+            name = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.NAMED_QUERY_FIND_NOT_YET_REPLAYED,
+            query = "SELECT pc "
+                    + "FROM PublishedCommand pc"
+                    + " WHERE pc.replayState = 'PENDING' "
+                    + "ORDER BY pc.timestamp ASC "),
+                    // TODO: query.setMaxResults(2) // same as batch size
+})
+@EntityListeners(IsisEntityListener.class)
+@DomainObject(
+        logicalTypeName = org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand.LOGICAL_TYPE_NAME,
+        editing = Editing.DISABLED
+)
+@NoArgsConstructor
+public class PublishedCommand extends org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand {
+
+    protected final static String FQCN = "org.apache.isis.extensions.commandlog.jpa.dom.PublishedCommand";
+
+    // EVENTS
+    // (see superclass)
+
+    // CONSTRUCTORS
+
+    public PublishedCommand(
+            final CommandDto commandDto,
+            final ReplayState replayState,
+            final int targetIndex) {
+
+        super(commandDto, replayState, targetIndex);
+    }
+
+
+    // TITLE
+    // (see superclass)
+
+
+    // (CHANGE) TYPE
+    // (derived - see superclass)
+
+
+    // INTERACTION ID
+
+    // TODO
+    private UUID interactionId;
+
+    @DomainChangeRecord.InteractionId
+    @Override
+    public UUID getInteractionId() {
+        return interactionId;
+    }
+    @Override
+    public void setInteractionId(UUID interactionId) {
+        this.interactionId = interactionId;
+    }
+
+
+    // USER NAME
+
+    // TODO
+    private String username;
+
+    @DomainChangeRecord.Username
+    @Override
+    public String getUsername() { return username; }
+    @Override
+    public void setUsername(String username) { this.username = username; }
+
+
+    // TIMESTAMP
+
+    // TODO
+    private Timestamp timestamp;
+
+    @DomainChangeRecord.TimestampMeta
+    @Override
+    public Timestamp getTimestamp() {
+        return timestamp;
+    }
+    @Override
+    public void setTimestamp(Timestamp timestamp) {
+        this.timestamp = timestamp;
+    }
+
+
+
+    // REPLAY STATE
+
+    // TODO
+    private ReplayState replayState;
+
+    @ReplayStateMeta
+    @Override
+    public ReplayState getReplayState() {
+        return replayState;
+    }
+    @Override
+    public void setReplayState(ReplayState replayState) {
+        this.replayState = replayState;
+    }
+
+
+    // REPLAY STATE FAILURE REASON
+
+    // TODO
+    private String replayStateFailureReason;
+
+    @ReplayStateFailureReason
+    @Override
+    public String getReplayStateFailureReason() {
+        return replayStateFailureReason;
+    }
+    @Override
+    public void setReplayStateFailureReason(String replayStateFailureReason) {
+        this.replayStateFailureReason = replayStateFailureReason;
+    }
+
+
+    // PARENT
+
+    // TODO
+    private PublishedCommand parent;
+
+    @Parent
+    @Override
+    public org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand getParent() {
+        return parent;
+    }
+    @Override
+    public void setParent(org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand parent) {
+        this.parent = _Casts.uncheckedCast(parent);
+    }
+
+
+
+    // TARGET
+
+    // TODO
+    private Bookmark target;
+
+    @DomainChangeRecord.TargetMeta
+    @Override
+    public Bookmark getTarget() {
+        return target;
+    }
+    @Override
+    public void setTarget(Bookmark target) {
+        this.target = target;
+    }
+
+
+    // TARGET MEMBER
+    // (derived - see superclass)
+
+
+
+    // LOCAL MEMBER
+    // (derived - see superclass)
+
+
+
+    // LOGICAL MEMBER IDENTIFIER
+
+    // TODO
+    private String logicalMemberIdentifier;
+
+    @LogicalMemberIdentifier
+    @Override
+    public String getLogicalMemberIdentifier() {
+        return logicalMemberIdentifier;
+    }
+    @Override
+    public void setLogicalMemberIdentifier(String logicalMemberIdentifier) {
+        this.logicalMemberIdentifier = logicalMemberIdentifier;
+    }
+
+
+    // COMMAND DTO
+
+    // TODO
+    private CommandDto commandDto;
+
+    @CommandDtoMeta
+    @Override
+    public CommandDto getCommandDto() {
+        return commandDto;
+    }
+    @Override
+    public void setCommandDto(CommandDto commandDto) {
+        this.commandDto = commandDto;
+    }
+
+
+    // STARTED AT
+
+    // TODO
+    private Timestamp startedAt;
+
+    @StartedAt
+    @Override
+    public Timestamp getStartedAt() {
+        return startedAt;
+    }
+    @Override
+    public void setStartedAt(Timestamp startedAt) {
+        this.startedAt = startedAt;
+    }
+
+
+    // COMPLETED AT
+
+    // TODO
+    private Timestamp completedAt;
+
+    @CompletedAt
+    @Override
+    public Timestamp getCompletedAt() {
+        return completedAt;
+    }
+    @Override
+    public void setCompletedAt(Timestamp completedAt) {
+        this.completedAt = completedAt;
+    }
+
+
+
+    // DURATION
+    // (derived - see superclass)
+
+
+
+    // COMPLETE
+    // (derived - see superclass)
+
+
+
+    // RESULT SUMMARY
+    // (derived - see superclass)
+
+
+
+    // RESULT
+
+    // TODO
+    private Bookmark result;
+
+    @Result
+    @Override
+    public Bookmark getResult() {
+        return result;
+    }
+    @Override
+    public void setResult(Bookmark result) {
+        this.result = result;
+    }
+
+
+    // EXCEPTION
+
+    // TODO
+    private String exception;
+
+    @ExceptionMeta
+    @Override
+    public String getException() {
+        return exception;
+    }
+    @Override
+    public void setException(final String exception) {
+        this.exception = exception;
+    }
+
+
+    // CAUSED EXCEPTION
+    // (derived - see superclass)
+
+
+
+    // PRE VALUE
+    // (derived - see superclass)
+
+
+
+    // POST VALUE
+    // (derived - see superclass)
+
+
+
+    // PROGRAMMATIC
+    // (see superclass)
+
+
+    // TO STRING etc
+    // (see superclass)
+
+
+}
+
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/PublishedCommandForJdoRepository.java b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/dom/PublishedCommandRepository.java
similarity index 63%
rename from extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/PublishedCommandForJdoRepository.java
rename to extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/dom/PublishedCommandRepository.java
index 7f38ebc..d95ac4b 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/PublishedCommandForJdoRepository.java
+++ b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/dom/PublishedCommandRepository.java
@@ -16,37 +16,28 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.extensions.commandlog.jdo.entities;
+package org.apache.isis.extensions.commandlog.jpa.dom;
 
-import javax.inject.Inject;
 import javax.inject.Named;
 
-import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Service;
 
-import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand;
+import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommandRepositoryAbstract;
-import org.apache.isis.persistence.jdo.applib.services.JdoSupportService;
 
-import lombok.RequiredArgsConstructor;
-
-/**
- * Provides supporting functionality for querying and persisting
- * {@link PublishedCommandForJdo command} entities.
- */
 @Service
 @Named("isis.ext.commandLog.PublishedCommandRepository")
 @javax.annotation.Priority(PriorityPrecedence.MIDPOINT)
-@Qualifier("Jdo")
-public class PublishedCommandForJdoRepository
-extends PublishedCommandRepositoryAbstract<PublishedCommandForJdo> {
+@Qualifier("Jpa")
+public class PublishedCommandRepository
+extends PublishedCommandRepositoryAbstract<PublishedCommand> {
 
-    public PublishedCommandForJdoRepository() {
-        super(PublishedCommandForJdo.class);
+    public PublishedCommandRepository() {
+        super(PublishedCommand.class);
     }
 
-    @Override protected PublishedCommand newPublishedCommand() {
-        return new PublishedCommandForJdo();
+    @Override public PublishedCommand newPublishedCommand() {
+        return new PublishedCommand();
     }
 }
diff --git a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpa.java b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpa.java
deleted file mode 100644
index 27c19f8..0000000
--- a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpa.java
+++ /dev/null
@@ -1,620 +0,0 @@
-/*
- *  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.commandlog.jpa.entities;
-
-import java.math.BigDecimal;
-import java.sql.Timestamp;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.UUID;
-import java.util.function.Consumer;
-
-import javax.persistence.Entity;
-
-import org.springframework.context.event.EventListener;
-import org.springframework.stereotype.Service;
-
-import org.apache.isis.applib.annotation.DomainObject;
-import org.apache.isis.applib.annotation.Editing;
-import org.apache.isis.applib.annotation.MemberSupport;
-import org.apache.isis.applib.annotation.PriorityPrecedence;
-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.JavaSqlXMLGregorianCalendarMarshalling;
-import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandOutcomeHandler;
-import org.apache.isis.applib.services.commanddto.conmap.UserDataKeys;
-import org.apache.isis.applib.services.tablecol.TableColumnOrderForCollectionTypeAbstract;
-import org.apache.isis.applib.util.TitleBuffer;
-import org.apache.isis.commons.functional.Result;
-import org.apache.isis.commons.internal.base._Strings;
-import org.apache.isis.commons.internal.exceptions._Exceptions;
-import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand;
-import org.apache.isis.extensions.commandlog.jpa.IsisModuleExtCommandLogJpa;
-import org.apache.isis.extensions.commandlog.applib.dom.ReplayState;
-import org.apache.isis.extensions.commandlog.applib.util.BigDecimalUtils;
-import org.apache.isis.extensions.commandlog.applib.util.StringUtils;
-import org.apache.isis.schema.cmd.v2.CommandDto;
-import org.apache.isis.schema.cmd.v2.MapDto;
-
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-import lombok.val;
-
-/**
- * A persistent representation of a {@link Command}.
- *
- * <p>
- *     Use cases requiring persistence including auditing, and for replay of
- *     commands for regression testing purposes.
- * </p>
- *
- * Note that this class doesn't subclass from {@link Command} ({@link Command}
- * is not an interface).
- */
-/* TODO migrate to JPA
-@javax.jdo.annotations.PersistenceCapable(
-        identityType=IdentityType.APPLICATION,
-        schema = "isisExtensionsCommandLog",
-        table = "Command")
-@javax.jdo.annotations.Queries( {
-    @javax.jdo.annotations.Query(
-            name="findByInteractionIdStr",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE interactionIdStr == :interactionIdStr "),
-    @javax.jdo.annotations.Query(
-            name="findByParent",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE parent == :parent "),
-    @javax.jdo.annotations.Query(
-            name="findCurrent",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE completedAt == null "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findCompleted",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE completedAt != null "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findRecentByTarget",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE target == :target "
-                    + "ORDER BY this.timestamp DESC "
-                    + "RANGE 0,30"),
-    @javax.jdo.annotations.Query(
-            name="findByTargetAndTimestampBetween",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE target == :target "
-                    + "&& timestamp >= :from "
-                    + "&& timestamp <= :to "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTargetAndTimestampAfter",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE target == :target "
-                    + "&& timestamp >= :from "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTargetAndTimestampBefore",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE target == :target "
-                    + "&& timestamp <= :to "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTarget",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE target == :target "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTimestampBetween",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE timestamp >= :from "
-                    + "&&    timestamp <= :to "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTimestampAfter",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE timestamp >= :from "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTimestampBefore",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE timestamp <= :to "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="find",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findRecentByUsername",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE username == :username "
-                    + "ORDER BY this.timestamp DESC "
-                    + "RANGE 0,30"),
-    @javax.jdo.annotations.Query(
-            name="findFirst",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE startedAt   != null "
-                    + "   && completedAt != null "
-                    + "ORDER BY this.timestamp ASC "
-                    + "RANGE 0,2"),
-        // this should be RANGE 0,1 but results in DataNucleus submitting "FETCH NEXT ROW ONLY"
-        // which SQL Server doesn't understand.  However, as workaround, SQL Server *does* understand FETCH NEXT 2 ROWS ONLY
-    @javax.jdo.annotations.Query(
-            name="findSince",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE timestamp > :timestamp "
-                    + "   && startedAt != null "
-                    + "   && completedAt != null "
-                    + "ORDER BY this.timestamp ASC"),
-    // most recent (replayed) command previously replicated from primary to
-    // secondary.  This should always exist except for the very first times
-    // (after restored the prod DB to secondary).
-    @javax.jdo.annotations.Query(
-            name="findMostRecentReplayed",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE (replayState == 'OK' || replayState == 'FAILED') "
-                    + "ORDER BY this.timestamp DESC "
-                    + "RANGE 0,2"), // this should be RANGE 0,1 but results in DataNucleus submitting "FETCH NEXT ROW ONLY"
-                                    // which SQL Server doesn't understand.  However, as workaround, SQL Server *does* understand FETCH NEXT 2 ROWS ONLY
-    // the most recent completed command, as queried on the
-    // secondary, corresponding to the last command run on primary before the
-    // production database was restored to the secondary
-    @javax.jdo.annotations.Query(
-            name="findMostRecentCompleted",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE startedAt   != null "
-                    + "   && completedAt != null "
-                    + "ORDER BY this.timestamp DESC "
-                    + "RANGE 0,2"),
-        // this should be RANGE 0,1 but results in DataNucleus submitting "FETCH NEXT ROW ONLY"
-        // which SQL Server doesn't understand.  However, as workaround, SQL Server *does* understand FETCH NEXT 2 ROWS ONLY
-    @javax.jdo.annotations.Query(
-            name="findNotYetReplayed",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE replayState == 'PENDING' "
-                    + "ORDER BY this.timestamp ASC "
-                    + "RANGE 0,10"),    // same as batch size
-})
-@javax.jdo.annotations.Indices({
-        @javax.jdo.annotations.Index(name = "CommandJdo__startedAt__timestamp__IDX", members = { "startedAt", "timestamp" }),
-        @javax.jdo.annotations.Index(name = "CommandJdo__timestamp__IDX", members = { "timestamp" }),
-})
-*/
-@Entity
-@DomainObject(
-        logicalTypeName = CommandJpa.LOGICAL_TYPE_NAME,
-        editing = Editing.DISABLED
-)
-//@Log4j2
-@NoArgsConstructor
-public class CommandJpa extends PublishedCommand {
-
-    public final static String LOGICAL_TYPE_NAME = IsisModuleExtCommandLogJpa.NAMESPACE + ".Command";
-    protected final static String FQCN = "org.apache.isis.extensions.commandlog.jpa.entities.CommandJpa";
-
-    /**
-     * Intended for use on primary system.
-     *
-     * @param command
-     */
-    public CommandJpa(final Command command) {
-
-        setInteractionIdStr(command.getInteractionId().toString());
-        setUsername(command.getUsername());
-        setTimestamp(command.getTimestamp());
-
-        setCommandDto(command.getCommandDto());
-        setTarget(command.getTarget());
-        setLogicalMemberIdentifier(command.getLogicalMemberIdentifier());
-
-        setStartedAt(command.getStartedAt());
-        setCompletedAt(command.getCompletedAt());
-
-        setResult(command.getResult());
-        setException(command.getException());
-
-        setReplayState(ReplayState.UNDEFINED);
-    }
-
-
-    /**
-     * Intended for use on secondary (replay) system.
-     *
-     * @param commandDto - obtained from the primary system as a representation of a command invocation
-     * @param replayState - controls whether this is to be replayed
-     * @param targetIndex - if the command represents a bulk action, then it is flattened out when replayed; this indicates which target to execute against.
-     */
-    public CommandJpa(
-            final CommandDto commandDto,
-            final ReplayState replayState,
-            final int targetIndex) {
-
-        setInteractionIdStr(commandDto.getInteractionId());
-        setUsername(commandDto.getUser());
-        setTimestamp(JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(commandDto.getTimestamp()));
-
-        setCommandDto(commandDto);
-        setTarget(Bookmark.forOidDto(commandDto.getTargets().getOid().get(targetIndex)));
-        setLogicalMemberIdentifier(commandDto.getMember().getLogicalMemberIdentifier());
-
-        // the hierarchy of commands calling other commands is only available on the primary system, and is
-        setParent(null);
-
-        setStartedAt(JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(commandDto.getTimings().getStartedAt()));
-        setCompletedAt(JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(commandDto.getTimings().getCompletedAt()));
-
-        copyOver(commandDto, UserDataKeys.RESULT, value -> this.setResult(Bookmark.parse(value).orElse(null)));
-        copyOver(commandDto, UserDataKeys.EXCEPTION, this::setException);
-
-        setReplayState(replayState);
-    }
-
-    static void copyOver(
-            final CommandDto commandDto,
-            final String key, final Consumer<String> consume) {
-        commandDto.getUserData().getEntry()
-                .stream()
-                .filter(x -> Objects.equals(x.getKey(), key))
-                .map(MapDto.Entry::getValue)
-                .filter(Objects::nonNull)
-                .filter(x -> x.length() > 0)
-                .findFirst()
-                .ifPresent(consume);
-    }
-
-    @Service
-    public static class TitleProvider {
-
-        @EventListener(TitleUiEvent.class)
-        public void on(final TitleUiEvent ev) {
-            if(!Objects.equals(ev.getTitle(), "Command Jdo") || ev.getTranslatableTitle() != null) {
-                return;
-            }
-            ev.setTitle(title((CommandJpa)ev.getSource()));
-        }
-
-        private static String title(final CommandJpa source) {
-            // nb: not thread-safe
-            // formats defined in https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
-            val format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
-
-            val buf = new TitleBuffer();
-            buf.append(format.format(source.getTimestamp()));
-            buf.append(" ").append(source.getLogicalMemberIdentifier());
-            return buf.toString();
-        }
-    }
-
-
-    public static class InteractionIdDomainEvent extends PropertyDomainEvent<String> { }
-    /**
-     * Implementation note: persisted as a string rather than a UUID as fails
-     * to persist if using h2 (perhaps would need to be mapped differently).
-     * @see <a href="https://www.datanucleus.org/products/accessplatform/jdo/mapping.html#_other_types">www.datanucleus.org</a>
-     */
-//    @javax.jdo.annotations.PrimaryKey
-//    @javax.jdo.annotations.Persistent
-//    @javax.jdo.annotations.Column(allowsNull="false", name = "interactionId", length = 36)
-    @Property(domainEvent = InteractionIdDomainEvent.class)
-    @PropertyLayout(named = "Interaction Id")
-    @Getter @Setter
-    private String interactionIdStr;
-    @Override
-    @Programmatic
-    public UUID getInteractionId() {return UUID.fromString(getInteractionIdStr());}
-
-
-    public static class UsernameDomainEvent extends PropertyDomainEvent<String> { }
-//    @javax.jdo.annotations.Column(allowsNull="false", length = 50)
-    @Property(domainEvent = UsernameDomainEvent.class)
-    @Getter @Setter
-    private String username;
-
-
-    public static class TimestampDomainEvent extends PropertyDomainEvent<Timestamp> { }
-//    @javax.jdo.annotations.Persistent
-//    @javax.jdo.annotations.Column(allowsNull="false")
-    @Property(domainEvent = TimestampDomainEvent.class)
-    @Getter @Setter
-    private Timestamp timestamp;
-
-
-
-    @Override
-    public ChangeType getType() {
-        return ChangeType.COMMAND;
-    }
-
-
-    public static class ReplayStateDomainEvent extends PropertyDomainEvent<ReplayState> { }
-    /**
-     * For a replayed command, what the outcome was.
-     */
-//    @javax.jdo.annotations.Column(allowsNull="true", length=10)
-    @Property(domainEvent = ReplayStateDomainEvent.class)
-    @Getter @Setter
-    private ReplayState replayState;
-
-
-    public static class ReplayStateFailureReasonDomainEvent extends PropertyDomainEvent<ReplayState> { }
-    /**
-     * For a {@link ReplayState#FAILED failed} replayed command, what the reason was for the failure.
-     */
-//    @javax.jdo.annotations.Column(allowsNull="true", length=255)
-    @Property(domainEvent = ReplayStateFailureReasonDomainEvent.class)
-    @PropertyLayout(hidden = Where.ALL_TABLES, multiLine = 5)
-    @Getter @Setter
-    private String replayStateFailureReason;
-    @MemberSupport public boolean hideReplayStateFailureReason() {
-        return getReplayState() == null || !getReplayState().isFailed();
-    }
-
-
-    public static class ParentDomainEvent extends PropertyDomainEvent<Command> { }
-//    @javax.jdo.annotations.Persistent
-//    @javax.jdo.annotations.Column(name="parentId", allowsNull="true")
-    @Property(domainEvent = ParentDomainEvent.class)
-    @PropertyLayout(hidden = Where.ALL_TABLES)
-    @Getter @Setter
-    private CommandJpa parent;
-
-
-    public static class TargetDomainEvent extends PropertyDomainEvent<String> { }
-//    @javax.jdo.annotations.Persistent
-//    @javax.jdo.annotations.Column(allowsNull="true", length = 2000, name="target")
-    @Property(domainEvent = TargetDomainEvent.class)
-    @PropertyLayout(named = "Object")
-    @Getter @Setter
-    private Bookmark target;
-
-    public String getTargetStr() {
-        return Optional.ofNullable(getTarget()).map(Bookmark::toString).orElse(null);
-    }
-
-    @Override
-    public String getTargetMember() {
-        return getCommandDto().getMember().getLogicalMemberIdentifier();
-    }
-
-    @Property(domainEvent = TargetDomainEvent.class)
-    @PropertyLayout(named = "Member")
-    public String getLocalMember() {
-        val targetMember = getTargetMember();
-        return targetMember.substring(targetMember.indexOf("#") + 1);
-    }
-
-    public static class LogicalMemberIdentifierDomainEvent extends PropertyDomainEvent<String> { }
-    @Property(domainEvent = LogicalMemberIdentifierDomainEvent.class)
-    @PropertyLayout(hidden = Where.ALL_TABLES)
-//    @javax.jdo.annotations.Column(allowsNull="false", length = MemberIdentifierType.Meta.MAX_LEN)
-    @Getter @Setter
-    private String logicalMemberIdentifier;
-
-
-    public static class CommandDtoDomainEvent extends PropertyDomainEvent<CommandDto> { }
-//    @javax.jdo.annotations.Persistent
-//    @javax.jdo.annotations.Column(allowsNull="true", jdbcType="CLOB")
-    @Property(domainEvent = CommandDtoDomainEvent.class)
-    @PropertyLayout(multiLine = 9)
-    @Getter @Setter
-    private CommandDto commandDto;
-
-
-    public static class StartedAtDomainEvent extends PropertyDomainEvent<Timestamp> { }
-//    @javax.jdo.annotations.Persistent
-//    @javax.jdo.annotations.Column(allowsNull="true")
-    @Property(domainEvent = StartedAtDomainEvent.class)
-    @Getter @Setter
-    private Timestamp startedAt;
-
-
-    public static class CompletedAtDomainEvent extends PropertyDomainEvent<Timestamp> { }
-//    @javax.jdo.annotations.Persistent
-//    @javax.jdo.annotations.Column(allowsNull="true")
-    @Property(domainEvent = CompletedAtDomainEvent.class)
-    @Getter @Setter
-    private Timestamp completedAt;
-
-
-    public static class DurationDomainEvent extends PropertyDomainEvent<BigDecimal> { }
-    /**
-     * The number of seconds (to 3 decimal places) that this interaction lasted.
-     *
-     * <p>
-     * Populated only if it has {@link #getCompletedAt() completed}.
-     */
-//    @javax.jdo.annotations.NotPersistent
-    @javax.validation.constraints.Digits(integer=5, fraction=3)
-    @Property(domainEvent = DurationDomainEvent.class)
-    public BigDecimal getDuration() {
-        return BigDecimalUtils.durationBetween(getStartedAt(), getCompletedAt());
-    }
-
-
-    public static class IsCompleteDomainEvent extends PropertyDomainEvent<Boolean> { }
-//    @javax.jdo.annotations.NotPersistent
-    @Property(domainEvent = IsCompleteDomainEvent.class)
-    @PropertyLayout(hidden = Where.OBJECT_FORMS)
-    public boolean isComplete() {
-        return getCompletedAt() != null;
-    }
-
-
-    public static class ResultSummaryDomainEvent extends PropertyDomainEvent<String> { }
-//    @javax.jdo.annotations.NotPersistent
-    @Property(domainEvent = ResultSummaryDomainEvent.class)
-    @PropertyLayout(hidden = Where.OBJECT_FORMS, named = "Result")
-    public String getResultSummary() {
-        if(getCompletedAt() == null) {
-            return "";
-        }
-        if(!_Strings.isNullOrEmpty(getException())) {
-            return "EXCEPTION";
-        }
-        if(getResult() != null) {
-            return "OK";
-        } else {
-            return "OK (VOID)";
-        }
-    }
-
-
-    public static class ResultDomainEvent extends PropertyDomainEvent<String> { }
-//    @javax.jdo.annotations.Persistent
-//    @javax.jdo.annotations.Column(allowsNull="true", length = 2000, name="result")
-    @Property(domainEvent = ResultDomainEvent.class)
-    @PropertyLayout(hidden = Where.ALL_TABLES, named = "Result Bookmark")
-    @Getter @Setter
-    private Bookmark result;
-
-    public static class ExceptionDomainEvent extends PropertyDomainEvent<String> { }
-    /**
-     * Stack trace of any exception that might have occurred if this interaction/transaction aborted.
-     *
-     * <p>
-     * Not part of the applib API, because the default implementation is not persistent
-     * and so there's no object that can be accessed to be annotated.
-     */
-//    @javax.jdo.annotations.Column(allowsNull="true", jdbcType="CLOB")
-    @Property(domainEvent = ExceptionDomainEvent.class)
-    @PropertyLayout(hidden = Where.ALL_TABLES, multiLine = 5, named = "Exception (if any)")
-    @Getter
-    private String exception;
-    public void setException(final String exception) {
-        this.exception = exception;
-    }
-    public void setException(final Throwable exception) {
-        setException(_Exceptions.asStacktrace(exception));
-    }
-
-    public static class IsCausedExceptionDomainEvent extends PropertyDomainEvent<Boolean> { }
-//    @javax.jdo.annotations.NotPersistent
-    @Property(domainEvent = IsCausedExceptionDomainEvent.class)
-    @PropertyLayout(hidden = Where.OBJECT_FORMS)
-    public boolean isCausedException() {
-        return getException() != null;
-    }
-
-
-    @Override
-    public String getPreValue() {
-        return null;
-    }
-
-    @Override
-    public String getPostValue() {
-        return null;
-    }
-
-
-    @Override
-    public void saveAnalysis(final String analysis) {
-        if (analysis == null) {
-            setReplayState(ReplayState.OK);
-        } else {
-            setReplayState(ReplayState.FAILED);
-            setReplayStateFailureReason(StringUtils.trimmed(analysis, 255));
-        }
-
-    }
-
-    @Override
-    public String toString() {
-        return toFriendlyString();
-    }
-
-    @Override
-    public CommandOutcomeHandler outcomeHandler() {
-        return new CommandOutcomeHandler() {
-            @Override
-            public Timestamp getStartedAt() {
-                return CommandJpa.this.getStartedAt();
-            }
-
-            @Override
-            public void setStartedAt(final Timestamp startedAt) {
-                CommandJpa.this.setStartedAt(startedAt);
-            }
-
-            @Override
-            public void setCompletedAt(final Timestamp completedAt) {
-                CommandJpa.this.setCompletedAt(completedAt);
-            }
-
-            @Override
-            public void setResult(final Result<Bookmark> resultBookmark) {
-                CommandJpa.this.setResult(resultBookmark.getValue().orElse(null));
-                CommandJpa.this.setException(resultBookmark.getFailure().orElse(null));
-            }
-
-        };
-    }
-
-    @Service
-    @javax.annotation.Priority(PriorityPrecedence.LATE - 10) // before the framework's own default.
-    public static class TableColumnOrderDefault extends TableColumnOrderForCollectionTypeAbstract<CommandJpa> {
-
-        public TableColumnOrderDefault() { super(CommandJpa.class); }
-
-        @Override
-        protected List<String> orderParented(final Object parent, final String collectionId, final List<String> propertyIds) {
-            return ordered(propertyIds);
-        }
-
-        @Override
-        protected List<String> orderStandalone(final List<String> propertyIds) {
-            return ordered(propertyIds);
-        }
-
-        private List<String> ordered(final List<String> propertyIds) {
-            return Arrays.asList(
-                "timestamp", "target", "targetMember", "username", "complete", "resultSummary", "interactionIdStr"
-            );
-        }
-    }
-}
-
diff --git a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpaRepository.java b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpaRepository.java
deleted file mode 100644
index f337d44..0000000
--- a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpaRepository.java
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- *  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.commandlog.jpa.entities;
-
-import java.sql.Timestamp;
-import java.time.Instant;
-import java.time.LocalDate;
-import java.time.ZoneId;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import java.util.UUID;
-
-import org.springframework.lang.Nullable;
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Provider;
-
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.stereotype.Service;
-
-import org.apache.isis.applib.annotation.PriorityPrecedence;
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
-import org.apache.isis.applib.query.Query;
-import org.apache.isis.applib.query.QueryRange;
-import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.iactn.InteractionProvider;
-import org.apache.isis.applib.services.repository.RepositoryService;
-import org.apache.isis.applib.util.schema.CommandDtoUtils;
-import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommandRepository;
-import org.apache.isis.extensions.commandlog.applib.dom.ReplayState;
-import org.apache.isis.schema.cmd.v2.CommandDto;
-import org.apache.isis.schema.cmd.v2.CommandsDto;
-import org.apache.isis.schema.cmd.v2.MapDto;
-import org.apache.isis.schema.common.v2.InteractionType;
-import org.apache.isis.schema.common.v2.OidDto;
-
-import lombok.RequiredArgsConstructor;
-import lombok.val;
-
-/**
- * Provides supporting functionality for querying and persisting
- * {@link CommandJdo command} entities.
- */
-@Service
-@Named("isis.ext.commandLog.CommandJdoRepository")
-@javax.annotation.Priority(PriorityPrecedence.MIDPOINT)
-@Qualifier("Jpa")
-@RequiredArgsConstructor
-//@Log4j2
-public class CommandJpaRepository
-implements PublishedCommandRepository<CommandJpa> {
-
-    @Inject final Provider<InteractionProvider> interactionProviderProvider;
-    @Inject final Provider<RepositoryService> repositoryServiceProvider;
-
-
-    @Override
-    public List<CommandJpa> findByFromAndTo(
-            final @Nullable LocalDate from,
-            final @Nullable LocalDate to) {
-        final Timestamp fromTs = toTimestampStartOfDayWithOffset(from, 0);
-        final Timestamp toTs = toTimestampStartOfDayWithOffset(to, 1);
-
-        final Query<CommandJpa> query;
-        if(from != null) {
-            if(to != null) {
-                query = Query.named(CommandJpa.class, "findByTimestampBetween")
-                        .withParameter("from", fromTs)
-                        .withParameter("to", toTs);
-            } else {
-                query = Query.named(CommandJpa.class, "findByTimestampAfter")
-                        .withParameter("from", fromTs);
-            }
-        } else {
-            if(to != null) {
-                query = Query.named(CommandJpa.class, "findByTimestampBefore")
-                        .withParameter("to", toTs);
-            } else {
-                query = Query.named(CommandJpa.class, "find");
-            }
-        }
-        return repositoryService().allMatches(query);
-    }
-
-    @Override
-    public Optional<CommandJpa> findByInteractionId(final UUID interactionId) {
-        return repositoryService().firstMatch(
-                Query.named(CommandJpa.class, "findByInteractionIdStr")
-                    .withParameter("interactionIdStr", interactionId.toString()));
-    }
-
-    @Override
-    public List<CommandJpa> findByParent(final CommandModel parent) {
-        return repositoryService().allMatches(
-                Query.named(CommandJpa.class, "findByParent")
-                    .withParameter("parent", parent));
-    }
-
-    @Override
-    public List<CommandJpa> findCurrent() {
-        return repositoryService().allMatches(
-                Query.named(CommandJpa.class, "findCurrent"));
-    }
-
-    @Override
-    public List<CommandJpa> findCompleted() {
-        return repositoryService().allMatches(
-                Query.named(CommandJpa.class, "findCompleted"));
-    }
-
-    @Override
-    public List<CommandJpa> findByTargetAndFromAndTo(
-            final Bookmark target,
-            final @Nullable LocalDate from,
-            final @Nullable LocalDate to) {
-
-        final Timestamp fromTs = toTimestampStartOfDayWithOffset(from, 0);
-        final Timestamp toTs = toTimestampStartOfDayWithOffset(to, 1);
-
-        final Query<CommandJpa> query;
-        if(from != null) {
-            if(to != null) {
-                query = Query.named(CommandJpa.class, "findByTargetAndTimestampBetween")
-                        .withParameter("target", target)
-                        .withParameter("from", fromTs)
-                        .withParameter("to", toTs);
-            } else {
-                query = Query.named(CommandJpa.class, "findByTargetAndTimestampAfter")
-                        .withParameter("target", target)
-                        .withParameter("from", fromTs);
-            }
-        } else {
-            if(to != null) {
-                query = Query.named(CommandJpa.class, "findByTargetAndTimestampBefore")
-                        .withParameter("target", target)
-                        .withParameter("to", toTs);
-            } else {
-                query = Query.named(CommandJpa.class, "findByTarget")
-                        .withParameter("target", target);
-            }
-        }
-        return repositoryService().allMatches(query);
-    }
-
-    private static Timestamp toTimestampStartOfDayWithOffset(
-            final @Nullable LocalDate dt,
-            final int daysOffset) {
-
-        return dt!=null
-                ? new java.sql.Timestamp(
-                        Instant.from(dt.atStartOfDay().plusDays(daysOffset).atZone(ZoneId.systemDefault()))
-                        .toEpochMilli())
-                : null;
-    }
-
-    @Override
-    public List<CommandJpa> findRecentByUsername(final String username) {
-        return repositoryService().allMatches(
-                Query.named(CommandJpa.class, "findRecentByUsername")
-                    .withParameter("username", username));
-    }
-
-    @Override
-    public List<CommandJpa> findRecentByTarget(final Bookmark target) {
-        return repositoryService().allMatches(
-                Query.named(CommandJpa.class, "findRecentByTarget")
-                    .withParameter("target", target));
-    }
-
-    @Override
-    public List<CommandJpa> findSince(final UUID interactionId, final Integer batchSize) {
-        if(interactionId == null) {
-            return findFirst();
-        }
-        final CommandJpa from = findByInteractionIdElseNull(interactionId);
-        if(from == null) {
-            return Collections.emptyList();
-        }
-        return findSince(from.getTimestamp(), batchSize);
-    }
-
-    private List<CommandJpa> findFirst() {
-        Optional<CommandJpa> firstCommandIfAny = repositoryService().firstMatch(
-                Query.named(CommandJpa.class, "findFirst"));
-        return firstCommandIfAny
-                .map(Collections::singletonList)
-                .orElse(Collections.emptyList());
-    }
-
-
-    private CommandJpa findByInteractionIdElseNull(final UUID interactionId) {
-        return null;
-        //TODO migrate to JPA
-//        val tsq = jdoSupport.newTypesafeQuery(CommandJpa.class);
-//        val cand = QCommandJdo.candidate();
-//        val q = tsq.filter(
-//                cand.interactionIdStr.eq(tsq.parameter("interactionIdStr", String.class))
-//        );
-//        q.setParameter("interactionIdStr", interactionId.toString());
-//        return q.executeUnique();
-    }
-
-    private List<CommandJpa> findSince(
-            final Timestamp timestamp,
-            final Integer batchSize) {
-
-        // DN generates incorrect SQL for SQL Server if count set to 1; so we set to 2 and then trim
-        // XXX that's a historic workaround, should rather be fixed upstream
-        val needsTrimFix = batchSize != null && batchSize == 1;
-
-        val q = Query.named(CommandJpa.class, "findSince")
-                .withParameter("timestamp", timestamp)
-                .withRange(QueryRange.limit(
-                        needsTrimFix ? 2L : batchSize
-                ));
-
-        final List<CommandJpa> commandJdos = repositoryService().allMatches(q);
-        return needsTrimFix && commandJdos.size() > 1
-                    ? commandJdos.subList(0,1)
-                    : commandJdos;
-    }
-
-
-    @Override
-    public Optional<CommandJpa> findMostRecentReplayed() {
-
-        return repositoryService().firstMatch(
-                Query.named(CommandJpa.class, "findMostRecentReplayed"));
-    }
-
-    @Override
-    public Optional<CommandJpa> findMostRecentCompleted() {
-        return repositoryService().firstMatch(
-                Query.named(CommandJpa.class, "findMostRecentCompleted"));
-    }
-
-    @Override
-    public List<CommandJpa> findNotYetReplayed() {
-        return repositoryService().allMatches(
-                Query.named(CommandJpa.class, "findNotYetReplayed"));
-    }
-
-    @Override
-    public List<CommandJpa> findReplayedOnSecondary() {
-        return repositoryService().allMatches(
-                Query.named(CommandJpa.class, "findReplayableMostRecentStarted"));
-    }
-
-    @Override
-    public List<CommandJpa> saveForReplay(final CommandsDto commandsDto) {
-        List<CommandDto> commandDto = commandsDto.getCommandDto();
-        List<CommandJpa> commands = new ArrayList<>();
-        for (final CommandDto dto : commandDto) {
-            commands.add(saveForReplay(dto));
-        }
-        return commands;
-    }
-
-    @Programmatic
-    @Override
-    public CommandJpa saveForReplay(final CommandDto dto) {
-
-        if(dto.getMember().getInteractionType() == InteractionType.ACTION_INVOCATION) {
-            final MapDto userData = dto.getUserData();
-            if (userData == null ) {
-                throw new IllegalStateException(String.format(
-                        "Can only persist action DTOs with additional userData; got: \n%s",
-                        CommandDtoUtils.toXml(dto)));
-            }
-        }
-
-        final CommandJpa commandJdo = new CommandJpa();
-
-        commandJdo.setInteractionIdStr(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));
-        commandJdo.setCommandDto(dto);
-        commandJdo.setLogicalMemberIdentifier(dto.getMember().getLogicalMemberIdentifier());
-
-        persist(commandJdo);
-
-        return commandJdo;
-    }
-
-    @Override
-    public void persist(final CommandJpa commandJdo) {
-        repositoryService().persist(commandJdo);
-    }
-
-    @Override
-    public void truncateLog() {
-        repositoryService().removeAll(CommandJpa.class);
-    }
-
-    private RepositoryService repositoryService() {
-        return repositoryServiceProvider.get();
-    }
-
-}
diff --git a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/role/dom/ApplicationRole.java b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/role/dom/ApplicationRole.java
index a2c8d94..08dc094 100644
--- a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/role/dom/ApplicationRole.java
+++ b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/role/dom/ApplicationRole.java
@@ -41,30 +41,28 @@ import org.apache.isis.applib.annotation.DomainObjectLayout;
 import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.extensions.secman.applib.user.dom.ApplicationUser;
 
-
 @PersistenceCapable(
         identityType = IdentityType.DATASTORE,
         schema = "isisExtensionsSecman",
         table = "ApplicationRole")
-@Inheritance(
-        strategy = InheritanceStrategy.NEW_TABLE)
-@DatastoreIdentity(
-        strategy = IdGeneratorStrategy.NATIVE, column = "id")
 @Uniques({
-    @Unique(
-            name = "ApplicationRole_name_UNQ", members = { "name" })
+        @Unique(
+                name = "ApplicationRole_name_UNQ",
+                members = { "name" })
 })
+@Inheritance(strategy = InheritanceStrategy.NEW_TABLE)
+@DatastoreIdentity(strategy = IdGeneratorStrategy.NATIVE, column = "id")
 @Queries({
-    @Query(
-            name = org.apache.isis.extensions.secman.applib.role.dom.ApplicationRole.NAMED_QUERY_FIND_BY_NAME,
-            value = "SELECT "
-                    + "FROM " + ApplicationRole.FQCN
-                    + " WHERE name == :name"),
-    @Query(
-            name = org.apache.isis.extensions.secman.applib.role.dom.ApplicationRole.NAMED_QUERY_FIND_BY_NAME_CONTAINING,
-            value = "SELECT "
-                    + "FROM " + ApplicationRole.FQCN
-                    + " WHERE name.matches(:regex) ")
+        @Query(
+                name = org.apache.isis.extensions.secman.applib.role.dom.ApplicationRole.NAMED_QUERY_FIND_BY_NAME,
+                value = "SELECT "
+                        + "FROM " + ApplicationRole.FQCN
+                        + " WHERE name == :name"),
+        @Query(
+                name = org.apache.isis.extensions.secman.applib.role.dom.ApplicationRole.NAMED_QUERY_FIND_BY_NAME_CONTAINING,
+                value = "SELECT "
+                        + "FROM " + ApplicationRole.FQCN
+                        + " WHERE name.matches(:regex) ")
 })
 @DomainObject(
         bounding = Bounding.BOUNDED,
@@ -76,7 +74,7 @@ import org.apache.isis.extensions.secman.applib.user.dom.ApplicationUser;
         bookmarking = BookmarkPolicy.AS_ROOT
 )
 public class ApplicationRole
-    extends org.apache.isis.extensions.secman.applib.role.dom.ApplicationRole {
+        extends org.apache.isis.extensions.secman.applib.role.dom.ApplicationRole {
 
     protected final static String FQCN = "org.apache.isis.extensions.secman.jdo.role.dom.ApplicationRole";
 
@@ -131,3 +129,4 @@ public class ApplicationRole
 
 
 }
+
diff --git a/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips/jdoql-and-timestamps.adoc b/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips/jdoql-and-timestamps.adoc
index fe6586f..ce1f539 100644
--- a/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips/jdoql-and-timestamps.adoc
+++ b/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips/jdoql-and-timestamps.adoc
@@ -6,7 +6,7 @@
 
 Beware of entities with a property called "timestamp": you run the risk of "timestamp" being treated as a keyword in certain contexts, probably not as you intended.
 
-By way of example, the xref:userguide:command-log:about.adoc[Command Log] module has an entity called `PublishedCommandForJdo`.
+By way of example, the xref:userguide:command-log:about.adoc[Command Log] module has an entity called `PublishedCommand`.
 This has a property called "timestamp", of type `java.sql.Timestamp`.
 
 This defines a query using JDOQL: