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 2020/09/02 22:00:03 UTC

[isis] 03/17: ISIS-2222: brings in incode-platform's 'command-replay' and 'quartz'

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

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

commit 606022a8d100cd28875941882226df6d46450701
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Sun Aug 30 11:48:02 2020 +0100

    ISIS-2222: brings in incode-platform's 'command-replay' and 'quartz'
---
 antora/playbooks/site.yml                          |  3 +
 .../modules/ROOT/partials/component-nav.adoc       |  1 +
 core/pom.xml                                       |  6 ++
 extensions/core/command-replay/impl/pom.xml        | 45 +++++++------
 .../replay/IsisModuleExtCommandReplayImpl.java     |  4 ++
 .../module/command/replay/impl/CommandFetcher.java | 25 ++++----
 ...kgroundCommandsWithReplicationAndReplayJob.java | 35 +++++-----
 .../command/replay/impl/SlaveConfiguration.java    | 32 +++++-----
 .../command/replay/impl/TickingClockService.java   | 39 +++++++-----
 .../replay/impl/mixins/CommandJdo_download.java    | 39 +++++-------
 .../impl/mixins/CommandJdo_openOnMaster.java       | 43 +++++--------
 .../replay/impl/mixins/CommandJdo_replayNext.java  | 74 ++++++----------------
 .../replay/impl/mixins/CommandJdo_replayQueue.java | 55 +++++-----------
 .../command/replay/spi/CommandReplayAnalyser.java  |  1 -
 .../replay/spi/CommandReplayAnalyserAbstract.java  |  4 +-
 .../spi/CommandReplayAnalyserExceptionStr.java     |  9 +--
 ...mandReplayAnalyserNumberBackgroundCommands.java | 15 ++---
 .../replay/spi/CommandReplayAnalyserResultStr.java |  9 +--
 .../spi/ReplayCommandExecutionController.java      |  1 -
 .../CommandReplayAnalysisService_trimmed_Test.java |  2 +-
 ...ndCommandsWithReplicationAndReplayJob_Test.java | 16 +++--
 .../flyway/adoc/modules/flyway/pages/about.adoc    |  2 +-
 extensions/core/quartz/adoc/antora.yml             | 19 ++++++
 .../core/quartz/adoc/modules/quartz/nav.adoc       |  4 ++
 .../quartz/adoc/modules/quartz/pages/about.adoc    |  5 ++
 .../adoc/modules/quartz/partials/module-nav.adoc   |  5 ++
 extensions/core/quartz/impl/pom.xml                | 72 +++++++++++++++++++++
 .../module/quartz/dom/IsisModuleExtQuartzImpl.java | 15 +++++
 .../quartz/dom/jobs/RunBackgroundCommandsJob.java  | 40 ++++++++++++
 extensions/core/quartz/pom.xml                     | 39 ++++++++++++
 extensions/pom.xml                                 |  8 +++
 31 files changed, 401 insertions(+), 266 deletions(-)

diff --git a/antora/playbooks/site.yml b/antora/playbooks/site.yml
index 41ee499..bd0ad45 100644
--- a/antora/playbooks/site.yml
+++ b/antora/playbooks/site.yml
@@ -115,6 +115,9 @@ content:
     - url: .
       start_path: extensions/core/model-annotation/adoc # extensions
       branches: HEAD
+    - url: .
+      start_path: extensions/core/quartz/adoc # userguide
+      branches: HEAD
 
     - url: .
       start_path: extensions/security/audit-trail/adoc # security
diff --git a/api/adoc/userguide/modules/ROOT/partials/component-nav.adoc b/api/adoc/userguide/modules/ROOT/partials/component-nav.adoc
index f35ad96..227dd73 100644
--- a/api/adoc/userguide/modules/ROOT/partials/component-nav.adoc
+++ b/api/adoc/userguide/modules/ROOT/partials/component-nav.adoc
@@ -4,5 +4,6 @@ include::userguide:btb:partial$module-nav.adoc[]
 * Extensions
 
 include::userguide:flyway:partial$module-nav.adoc[]
+include::userguide:quartz:partial$module-nav.adoc[]
 
 
diff --git a/core/pom.xml b/core/pom.xml
index 89a8dcc..9adfae8 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -1367,6 +1367,12 @@
 			</dependency>
 
 			<dependency>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-starter-quartz</artifactId>
+				<version>${spring-boot.version}</version>
+			</dependency>
+
+			<dependency>
 				<groupId>org.jmock</groupId>
 				<artifactId>jmock</artifactId>
 				<version>${jmock.version}</version>
diff --git a/extensions/core/command-replay/impl/pom.xml b/extensions/core/command-replay/impl/pom.xml
index 3b9a214..94bbfc8 100644
--- a/extensions/core/command-replay/impl/pom.xml
+++ b/extensions/core/command-replay/impl/pom.xml
@@ -42,40 +42,43 @@
         </dependency>
 
         <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-schema</artifactId>
+        </dependency>
+
+        <dependency>
             <groupId>org.apache.isis.testing</groupId>
             <artifactId>isis-testing-fixtures-applib</artifactId>
         </dependency>
 
         <dependency>
             <groupId>org.apache.isis.mappings</groupId>
+            <artifactId>isis-mappings-jaxrsclient-applib</artifactId>
+            <version>2.0.0-SNAPSHOT</version>   <!-- TODO: remove, shouldn't be necessary -->
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.mappings</groupId>
             <artifactId>isis-mappings-jaxrsclient-impl</artifactId>
-            <version>2.0.0-SNAPSHOT</version> <!-- TODO: shouldn't be required -->
+            <version>2.0.0-SNAPSHOT</version>   <!-- TODO: remove, shouldn't be necessary -->
         </dependency>
 
         <dependency>
             <groupId>org.apache.isis.extensions</groupId>
             <artifactId>isis-extensions-command-log-impl</artifactId>
         </dependency>
-        <!--
-
-                <dependency>
-                    <groupId>org.isisaddons.module.quartz</groupId>
-                    <artifactId>isis-module-quartz-dom</artifactId>
-                </dependency>
-
-
-                <dependency>
-                    <groupId>org.apache.isis.core</groupId>
-                    <artifactId>isis-core-unittestsupport</artifactId>
-                    <scope>test</scope>
-                </dependency>
-
-                <dependency>
-                    <groupId>org.assertj</groupId>
-                    <artifactId>assertj-core</artifactId>
-                    <scope>test</scope>
-                </dependency>
-        -->
+
+        <dependency>
+            <groupId>org.apache.isis.extensions</groupId>
+            <artifactId>isis-extensions-quartz-impl</artifactId>
+            <version>2.0.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+            <version>RELEASE</version>
+            <scope>compile</scope>
+        </dependency>
 
     </dependencies>
 
diff --git a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/IsisModuleExtCommandReplayImpl.java b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/IsisModuleExtCommandReplayImpl.java
index 563f51e..e9e308a 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/IsisModuleExtCommandReplayImpl.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/IsisModuleExtCommandReplayImpl.java
@@ -9,6 +9,7 @@ import org.isisaddons.module.command.replay.impl.CommandExecutorServiceWithTime;
 import org.isisaddons.module.command.replay.impl.CommandReplayAnalysisService;
 import org.isisaddons.module.command.replay.impl.CommandReplayOnMasterService;
 import org.isisaddons.module.command.replay.impl.CommandReplayOnSlaveService;
+import org.isisaddons.module.command.replay.impl.SlaveConfiguration;
 import org.isisaddons.module.command.replay.impl.TickingClockService;
 
 @Configuration
@@ -23,6 +24,9 @@ import org.isisaddons.module.command.replay.impl.TickingClockService;
         CommandReplayOnSlaveService.class,
         TickingClockService.class,
 
+        // @Service's
+        SlaveConfiguration.class,
+
 })
 public class IsisModuleExtCommandReplayImpl {
 
diff --git a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/CommandFetcher.java b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/CommandFetcher.java
index a3d3d15..591e7f0 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/CommandFetcher.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/CommandFetcher.java
@@ -4,13 +4,10 @@ import java.net.URI;
 import java.util.List;
 import java.util.UUID;
 
-
+import javax.inject.Inject;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.services.command.Command;
@@ -28,8 +25,6 @@ import lombok.extern.log4j.Log4j2;
 @Log4j2
 public class CommandFetcher {
 
-    private final static Logger LOG = LoggerFactory.getLogger(CommandFetcher.class);
-
     static final String URL_SUFFIX =
             "services/isiscommand.CommandReplayOnMasterService/actions/findCommandsOnMasterSince/invoke";
 
@@ -49,7 +44,7 @@ public class CommandFetcher {
             final SlaveConfiguration slaveConfig)
             throws StatusException {
 
-        LOG.debug("finding command on master ...");
+        log.debug("finding command on master ...");
 
         final CommandsDto commandsDto = fetchCommands(previousHwm, slaveConfig);
 
@@ -72,7 +67,7 @@ public class CommandFetcher {
             final SlaveConfiguration slaveConfig) throws StatusException {
         final UUID transactionId = previousHwm != null ? previousHwm.getUniqueId() : null;
 
-        LOG.debug("finding commands on master ...");
+        log.debug("finding commands on master ...");
 
         final URI uri = buildUri(transactionId, slaveConfig);
 
@@ -100,7 +95,7 @@ public class CommandFetcher {
                         slaveConfig.masterBaseUrl, URL_SUFFIX, slaveConfig.masterBatchSize)
         );
         final URI uri = uriBuilder.build();
-        LOG.info("uri = {}", uri);
+        log.info("uri = {}", uri);
         return uri;
     }
 
@@ -115,14 +110,14 @@ public class CommandFetcher {
             if(status != Response.Status.OK.getStatusCode()) {
                 final String entity = readEntityFrom(response);
                 if(entity != null) {
-                    LOG.warn("status: {}, entity: \n{}", status, entity);
+                    log.warn("status: {}, entity: \n{}", status, entity);
                 } else {
-                    LOG.warn("status: {}, unable to read entity from response", status);
+                    log.warn("status: {}, unable to read entity from response", status);
                 }
                 throw new StatusException(SlaveStatus.REST_CALL_FAILING);
             }
         } catch(Exception ex) {
-            LOG.warn("rest call failed", ex);
+            log.warn("rest call failed", ex);
             throw new StatusException(SlaveStatus.REST_CALL_FAILING, ex);
         }
         return response;
@@ -135,9 +130,9 @@ public class CommandFetcher {
             entity = readEntityFrom(response);
             final JaxbService jaxbService = new JaxbService.Simple();
             commandsDto = jaxbService.fromXml(CommandsDto.class, entity);
-            LOG.debug("commands:\n{}", entity);
+            log.debug("commands:\n{}", entity);
         } catch(Exception ex) {
-            LOG.warn("unable to unmarshal entity from {} to CommandsDto.class; was:\n{}", uri, entity);
+            log.warn("unable to unmarshal entity from {} to CommandsDto.class; was:\n{}", uri, entity);
             throw new StatusException(SlaveStatus.FAILED_TO_UNMARSHALL_RESPONSE, ex);
         }
         return commandsDto;
@@ -151,4 +146,6 @@ public class CommandFetcher {
         }
     }
 
+    @Inject SlaveConfiguration slaveConfiguration;
+
 }
\ No newline at end of file
diff --git a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/RunBackgroundCommandsWithReplicationAndReplayJob.java b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/RunBackgroundCommandsWithReplicationAndReplayJob.java
index 36b0618..814f92d 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/RunBackgroundCommandsWithReplicationAndReplayJob.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/RunBackgroundCommandsWithReplicationAndReplayJob.java
@@ -5,7 +5,6 @@ import java.util.Map;
 import javax.inject.Inject;
 
 import com.google.common.base.Splitter;
-import com.google.common.collect.Iterables;
 
 import org.quartz.DisallowConcurrentExecution;
 import org.quartz.Job;
@@ -14,10 +13,10 @@ import org.quartz.PersistJobDataAfterExecution;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import org.apache.isis.core.commons.authentication.AuthenticationSession;
-import org.apache.isis.core.metamodel.services.configinternal.ConfigurationServiceInternal;
-import org.apache.isis.core.runtime.authentication.standard.SimpleSession;
-import org.apache.isis.core.runtime.sessiontemplate.AbstractIsisSessionTemplate;
+import org.apache.isis.core.config.IsisConfiguration;
+import org.apache.isis.core.runtime.iactn.template.AbstractIsisInteractionTemplate;
+import org.apache.isis.core.security.authentication.AuthenticationSession;
+import org.apache.isis.core.security.authentication.standard.SimpleSession;
 
 import org.isisaddons.module.command.dom.BackgroundCommandExecutionFromBackgroundCommandServiceJdo;
 
@@ -26,6 +25,7 @@ import static org.isisaddons.module.command.replay.impl.ConfigurationKeys.SLAVE_
 import static org.isisaddons.module.command.replay.impl.ConfigurationKeys.SLAVE_USER_DEFAULT;
 import static org.isisaddons.module.command.replay.impl.ConfigurationKeys.SLAVE_USER_QUARTZ_KEY;
 
+import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
 @DisallowConcurrentExecution
@@ -99,15 +99,14 @@ public class RunBackgroundCommandsWithReplicationAndReplayJob implements Job {
     private Map<String,String> lookupIsisConfigurationAsMap(final AuthenticationSession authSession) {
 
         final Holder<Map<String,String>> holder = new Holder<>();
-        new AbstractIsisSessionTemplate() {
+        new AbstractIsisInteractionTemplate() {
             @Override
             protected void doExecuteWithTransaction(final Object unused) {
-                Map<String, String> map = configurationServiceInternal.asMap();
-                holder.setObject(map);
+                holder.setObject(isisConfiguration.getAsMap());
             }
 
             @Inject
-            ConfigurationServiceInternal configurationServiceInternal;
+            IsisConfiguration isisConfiguration;
         }.execute(authSession, null);
 
         return holder.getObject();
@@ -116,7 +115,7 @@ public class RunBackgroundCommandsWithReplicationAndReplayJob implements Job {
     private boolean lookupTickingClockServiceStatus(final AuthenticationSession authSession) {
 
         final Holder<Boolean> holder = new Holder<>();
-        new AbstractIsisSessionTemplate() {
+        new AbstractIsisInteractionTemplate() {
             @Override
             protected void doExecuteWithTransaction(final Object unused) {
                 holder.setObject(tickingClockService.isInitialized());
@@ -135,10 +134,10 @@ public class RunBackgroundCommandsWithReplicationAndReplayJob implements Job {
             return getString(quartzContext, SLAVE_USER_QUARTZ_KEY, SLAVE_USER_DEFAULT);
         }
 
-        private static String[] getRoles(final JobExecutionContext quartzContext) {
-            return Iterables.toArray(Splitter.on(",").split(
-                    getString(quartzContext, SLAVE_ROLES_QUARTZ_KEY, SLAVE_ROLES_DEFAULT)),
-                    String.class);
+        private static Iterable<String> getRoles(final JobExecutionContext quartzContext) {
+            val slaveRoles = getString(quartzContext, SLAVE_ROLES_QUARTZ_KEY, SLAVE_ROLES_DEFAULT);
+            return Splitter.on(",").split(
+                    slaveRoles);
         }
 
         SimpleSessionFromQuartz(final JobExecutionContext quartzContext) {
@@ -146,7 +145,6 @@ public class RunBackgroundCommandsWithReplicationAndReplayJob implements Job {
         }
     }
 
-    //region > quartz configuration; getSlaveStatus/setSlaveStatus
 
     private static final String KEY_SLAVE_STATUS = "slaveStatus";
 
@@ -162,25 +160,24 @@ public class RunBackgroundCommandsWithReplicationAndReplayJob implements Job {
         }
     }
 
+
     /**
      * Save into quartz configuration for this job, for next invocation.
      */
     private static void setString(JobExecutionContext context, String key, String value) {
         context.getJobDetail().getJobDataMap().put(key, value);
     }
-
-    private SlaveStatus getSlaveStatus(
+    private static SlaveStatus getSlaveStatus(
             final JobExecutionContext context,
             final SlaveStatus defaultStatus) {
         String mode = getString(context, KEY_SLAVE_STATUS, defaultStatus.name());
         return SlaveStatus.valueOf(mode);
     }
 
-    private void setSlaveStatus(final JobExecutionContext context, final SlaveStatus mode) {
+    private static void setSlaveStatus(final JobExecutionContext context, final SlaveStatus mode) {
         setString(context, KEY_SLAVE_STATUS, mode.name());
     }
 
-    //endregion
 
 }
 
diff --git a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/SlaveConfiguration.java b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/SlaveConfiguration.java
index 39c632f..b43c296 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/SlaveConfiguration.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/SlaveConfiguration.java
@@ -2,14 +2,22 @@ package org.isisaddons.module.command.replay.impl;
 
 import java.util.Map;
 
+import javax.validation.constraints.NotNull;
+
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.core.config.IsisConfiguration;
+
 import static org.isisaddons.module.command.replay.impl.ConfigurationKeys.MASTER_BASE_URL_ISIS_KEY;
 import static org.isisaddons.module.command.replay.impl.ConfigurationKeys.MASTER_BATCH_SIZE_ISIS_DEFAULT;
 import static org.isisaddons.module.command.replay.impl.ConfigurationKeys.MASTER_BATCH_SIZE_ISIS_KEY;
 import static org.isisaddons.module.command.replay.impl.ConfigurationKeys.MASTER_PASSWORD_ISIS_KEY;
 import static org.isisaddons.module.command.replay.impl.ConfigurationKeys.MASTER_USER_ISIS_KEY;
 
+import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
+@Service
 @Log4j2
 public class SlaveConfiguration {
 
@@ -18,24 +26,16 @@ public class SlaveConfiguration {
     final String masterBaseUrl;
     final int masterBatchSize;
 
-    public SlaveConfiguration(final Map<String, String> map) {
-        masterUser = map.get(MASTER_USER_ISIS_KEY);
-        masterPassword = map.get(MASTER_PASSWORD_ISIS_KEY);
-        String masterBaseUrl = map.get(MASTER_BASE_URL_ISIS_KEY);
-        if(masterBaseUrl != null && !masterBaseUrl.endsWith("/")) {
-            masterBaseUrl = masterBaseUrl + "/";
-        }
-        this.masterBaseUrl= masterBaseUrl;
-        this.masterBatchSize = batchSizeFrom(map);
+    public SlaveConfiguration(@NotNull IsisConfiguration isisConfiguration) {
+        val masterConfig = isisConfiguration.getExtensions().getCommandReplay().getMaster();
+        masterUser = masterConfig.getUser().orElse(null);
+        masterPassword = masterConfig.getPassword().orElse(null);
+        masterBaseUrl = masterConfig.getBaseUrl()
+                            .map(x -> !x.endsWith("/") ? x + "/" : x)
+                            .orElse(null);
+        masterBatchSize = masterConfig.getBatchSize();
     }
 
-    private static int batchSizeFrom(final Map<String, String> map) {
-        try {
-            return Integer.parseInt(map.get(MASTER_BATCH_SIZE_ISIS_KEY));
-        } catch (NumberFormatException e) {
-            return MASTER_BATCH_SIZE_ISIS_DEFAULT;
-        }
-    }
 
     public boolean isConfigured() {
         return masterUser != null &&
diff --git a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/TickingClockService.java b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/TickingClockService.java
index d223039..e0cfca0 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/TickingClockService.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/TickingClockService.java
@@ -2,6 +2,7 @@ package org.isisaddons.module.command.replay.impl;
 
 import java.sql.Timestamp;
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.Callable;
 
 import javax.annotation.PostConstruct;
@@ -18,6 +19,7 @@ import org.apache.isis.applib.clock.Clock;
 import org.apache.isis.core.config.IsisConfiguration;
 import org.apache.isis.testing.fixtures.applib.clock.TickingFixtureClock;
 
+import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
 /**
@@ -40,17 +42,20 @@ public class TickingClockService {
 
     @PostConstruct
     public void init() {
-        isisConfiguration.getExtensions().getCommandReplay().
-        if( notDefined(ConfigurationKeys.MASTER_BASE_URL_ISIS_KEY) ||
-            notDefined(ConfigurationKeys.MASTER_USER_ISIS_KEY) ||
-            notDefined(ConfigurationKeys.MASTER_PASSWORD_ISIS_KEY)) {
-            LOG.info(
+        Optional<String> baseUrl = isisConfiguration.getExtensions().getCommandReplay().getMaster().getBaseUrl();
+        Optional<String> user = isisConfiguration.getExtensions().getCommandReplay().getMaster().getUser();
+        Optional<String> password = isisConfiguration.getExtensions().getCommandReplay().getMaster().getPassword();
+
+        if( !baseUrl.isPresent()||
+            !user.isPresent() ||
+            !password.isPresent()) {
+            log.info(
                     "init() - skipping, one or more {}.* configuration constants missing",
                     ConfigurationKeys.ISIS_KEY_PREFIX);
             return;
         }
 
-        LOG.info("init() - replacing existing clock with TickingFixtureClock");
+        log.info("init() - replacing existing clock with TickingFixtureClock");
         TickingFixtureClock.replaceExisting();
     }
 
@@ -73,15 +78,15 @@ public class TickingClockService {
     public void at(Timestamp timestamp, Runnable runnable) {
         ensureInitialized();
 
-        final TickingFixtureClock instance = (TickingFixtureClock) TickingFixtureClock.getInstance();
-        final long previous = TickingFixtureClock.getTimeAsMillis();
-        final long wallTime0 = System.currentTimeMillis();
+        val tickingFixtureClock = (TickingFixtureClock) TickingFixtureClock.getInstance();
+        val previous = TickingFixtureClock.getEpochMillis();
+        val wallTime0 = System.currentTimeMillis();
         try {
-            instance.setTime(timestamp);
+            tickingFixtureClock.setTime(timestamp);
             runnable.run();
         } finally {
             final long wallTime1 = System.currentTimeMillis();
-            instance.setTime(previous + wallTime1 - wallTime0);
+            tickingFixtureClock.setTime(previous + wallTime1 - wallTime0);
         }
     }
 
@@ -98,17 +103,19 @@ public class TickingClockService {
     public <T> T at(Timestamp timestamp, Callable<T> callable) {
         ensureInitialized();
 
-        final TickingFixtureClock instance = (TickingFixtureClock) TickingFixtureClock.getInstance();
-        final long previous = TickingFixtureClock.getTimeAsMillis();
-        final long wallTime0 = System.currentTimeMillis();
+        val tickingFixtureClock = (TickingFixtureClock) TickingFixtureClock.getInstance();
+
+        val previous = TickingFixtureClock.getEpochMillis();
+        val wallTime0 = System.currentTimeMillis();
+
         try {
-            instance.setTime(timestamp);
+            tickingFixtureClock.setTime(timestamp);
             return callable.call();
         } catch (Exception e) {
             throw new ApplicationException(e);
         } finally {
             final long wallTime1 = System.currentTimeMillis();
-            instance.setTime(previous + wallTime1 - wallTime0);
+            tickingFixtureClock.setTime(previous + wallTime1 - wallTime0);
         }
     }
 
diff --git a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/mixins/CommandJdo_download.java b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/mixins/CommandJdo_download.java
index 3a01aba..b610306 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/mixins/CommandJdo_download.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/mixins/CommandJdo_download.java
@@ -5,48 +5,41 @@ import javax.inject.Inject;
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.CommandPersistence;
-import org.apache.isis.applib.annotation.Contributed;
 import org.apache.isis.applib.annotation.MemberOrder;
-import org.apache.isis.applib.annotation.Mixin;
 import org.apache.isis.applib.annotation.ParameterLayout;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.value.Clob;
 
-import org.isisaddons.module.command.CommandModule;
 import org.isisaddons.module.command.dom.CommandJdo;
+import org.isisaddons.module.command.replay.IsisModuleExtCommandReplayImpl;
 import org.isisaddons.module.command.replay.impl.CommandReplayOnMasterService;
-import org.isisaddons.module.command.replay.impl.StatusException;
 
-@Mixin(method = "act")
+@Action(
+        semantics = SemanticsOf.NON_IDEMPOTENT,
+        domainEvent = CommandJdo_download.ActionDomainEvent.class,
+        commandPersistence = CommandPersistence.NOT_PERSISTED
+)
+@ActionLayout(
+        cssClassFa = "fa-download",
+        position = ActionLayout.Position.PANEL
+)
 public class CommandJdo_download {
 
-    private final CommandJdo commandJdo;
+    public static class ActionDomainEvent
+            extends IsisModuleExtCommandReplayImpl.ActionDomainEvent<CommandJdo_download> { }
 
-    //region > constructor
+    private final CommandJdo commandJdo;
     public CommandJdo_download(CommandJdo commandJdo) {
         this.commandJdo = commandJdo;
     }
-    //endregion
-
-
-    public static class ActionDomainEvent extends CommandModule.ActionDomainEvent<CommandJdo_download> { }
-    @Action(
-            semantics = SemanticsOf.NON_IDEMPOTENT,
-            domainEvent = ActionDomainEvent.class,
-            commandPersistence = CommandPersistence.NOT_PERSISTED
-    )
-    @ActionLayout(
-            contributed = Contributed.AS_ACTION,
-            cssClassFa = "fa-download",
-            position = ActionLayout.Position.PANEL
-    )
+
+
     @MemberOrder(name = "arguments", sequence = "1")
     public Clob act(
             @ParameterLayout(named="Filename prefix")
-            final String fileNamePrefix) throws StatusException {
+            final String fileNamePrefix) {
         return commandReplayOnMasterService.downloadCommandById(commandJdo.getTransactionId(), fileNamePrefix);
     }
-
     public String default0Act() {
         return "command";
     }
diff --git a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/mixins/CommandJdo_openOnMaster.java b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/mixins/CommandJdo_openOnMaster.java
index 4db1266..ca2e765 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/mixins/CommandJdo_openOnMaster.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/mixins/CommandJdo_openOnMaster.java
@@ -2,6 +2,7 @@ package org.isisaddons.module.command.replay.impl.mixins;
 
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.Optional;
 
 import javax.inject.Inject;
 
@@ -10,31 +11,28 @@ import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.Contributed;
 import org.apache.isis.applib.annotation.MemberOrder;
-import org.apache.isis.applib.annotation.Mixin;
 import org.apache.isis.applib.annotation.SemanticsOf;
-import org.apache.isis.applib.services.bookmark.BookmarkService2;
-import org.apache.isis.core.metamodel.services.configinternal.ConfigurationServiceInternal;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.core.config.IsisConfiguration;
 
-import org.isisaddons.module.command.CommandModule;
 import org.isisaddons.module.command.dom.CommandJdo;
+import org.isisaddons.module.command.replay.IsisModuleExtCommandReplayImpl;
 import org.isisaddons.module.command.replay.impl.ConfigurationKeys;
 
-@Mixin(method = "act")
+@Action(
+        semantics = SemanticsOf.SAFE,
+        domainEvent = CommandJdo_openOnMaster.ActionDomainEvent.class
+)
 public class CommandJdo_openOnMaster<T> {
 
+    public static class ActionDomainEvent
+            extends IsisModuleExtCommandReplayImpl.ActionDomainEvent<CommandJdo_openOnMaster> { }
+
     private final CommandJdo commandJdo;
     public CommandJdo_openOnMaster(CommandJdo commandJdo) {
         this.commandJdo = commandJdo;
     }
 
-    public static class ActionDomainEvent extends CommandModule.ActionDomainEvent<CommandJdo_openOnMaster> { }
-    @Action(
-            semantics = SemanticsOf.SAFE,
-            domainEvent = ActionDomainEvent.class
-    )
-    @ActionLayout(
-            contributed = Contributed.AS_ACTION
-    )
     @MemberOrder(name = "transactionId", sequence = "1")
     public URL act() {
         final String baseUrlPrefix = lookupBaseUrlPrefix();
@@ -52,20 +50,13 @@ public class CommandJdo_openOnMaster<T> {
     }
 
     private String lookupBaseUrlPrefix() {
-        String masterBaseUrlEndUser = configurationServiceInternal.asMap()
-                .get(ConfigurationKeys.MASTER_BASE_URL_END_USER_URL_ISIS_KEY);
-        if(masterBaseUrlEndUser == null) {
-            return null;
-        }
-        if(!masterBaseUrlEndUser.endsWith("/")) {
-            masterBaseUrlEndUser = masterBaseUrlEndUser + "/";
-        }
-        return masterBaseUrlEndUser + "wicket/entity/";
+        return isisConfiguration.getExtensions().getCommandReplay().getMaster().getBaseUrlEndUser()
+                .map(x -> !x.endsWith("/") ? x + "/" : x)
+                .map(x -> x + "wicket/entity/")
+                .orElse(null);
     }
 
-    @Inject
-    ConfigurationServiceInternal configurationServiceInternal;
-    @Inject
-    BookmarkService2 bookmarkService2;
+    @Inject IsisConfiguration isisConfiguration;
+    @Inject BookmarkService bookmarkService2;
 
 }
diff --git a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/mixins/CommandJdo_replayNext.java b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/mixins/CommandJdo_replayNext.java
index 9d4ef6c..258b92d 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/mixins/CommandJdo_replayNext.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/mixins/CommandJdo_replayNext.java
@@ -5,46 +5,36 @@ import java.util.List;
 import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Action;
-import org.apache.isis.applib.annotation.ActionLayout;
 import org.apache.isis.applib.annotation.CommandPersistence;
-import org.apache.isis.applib.annotation.Contributed;
 import org.apache.isis.applib.annotation.MemberOrder;
-import org.apache.isis.applib.annotation.Mixin;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.services.command.CommandExecutorService;
 import org.apache.isis.applib.services.message.MessageService;
-import org.apache.isis.core.metamodel.services.configinternal.ConfigurationServiceInternal;
-import org.apache.isis.schema.cmd.v1.CommandDto;
+import org.apache.isis.core.config.IsisConfiguration;
+import org.apache.isis.schema.cmd.v2.CommandDto;
 
-import org.isisaddons.module.command.CommandModule;
 import org.isisaddons.module.command.dom.CommandJdo;
 import org.isisaddons.module.command.dom.CommandServiceJdoRepository;
+import org.isisaddons.module.command.replay.IsisModuleExtCommandReplayImpl;
 import org.isisaddons.module.command.replay.impl.CommandFetcher;
 import org.isisaddons.module.command.replay.impl.CommandReplayAnalysisService;
 import org.isisaddons.module.command.replay.impl.SlaveConfiguration;
 import org.isisaddons.module.command.replay.impl.StatusException;
 
-@Mixin(method = "act")
+@Action(
+        semantics = SemanticsOf.NON_IDEMPOTENT,
+        domainEvent = CommandJdo_replayNext.ActionDomainEvent.class,
+        commandPersistence = CommandPersistence.NOT_PERSISTED
+)
 public class CommandJdo_replayNext {
 
-    private final CommandJdo commandJdo;
+    public static class ActionDomainEvent extends IsisModuleExtCommandReplayImpl.ActionDomainEvent<CommandJdo_replayNext> { }
 
-    //region > constructor
+    private final CommandJdo commandJdo;
     public CommandJdo_replayNext(CommandJdo commandJdo) {
         this.commandJdo = commandJdo;
     }
-    //endregion
-
-
-    public static class ActionDomainEvent extends CommandModule.ActionDomainEvent<CommandJdo_replayNext> { }
-    @Action(
-            semantics = SemanticsOf.NON_IDEMPOTENT,
-            domainEvent = ActionDomainEvent.class,
-            commandPersistence = CommandPersistence.NOT_PERSISTED
-    )
-    @ActionLayout(
-            contributed = Contributed.AS_ACTION
-    )
+
     @MemberOrder(name = "executeIn", sequence = "3")
     public CommandJdo act() throws StatusException {
 
@@ -62,7 +52,6 @@ public class CommandJdo_replayNext {
         }
 
         execute(nextHwm);
-
         analysisService.analyse(nextHwm);
 
         return nextHwm;
@@ -70,9 +59,7 @@ public class CommandJdo_replayNext {
 
 
     private CommandJdo fetchNext() throws StatusException {
-        SlaveConfiguration slaveConfig = getSlaveConfig();
-
-        final CommandDto commandDto = commandFetcher.fetchCommand(this.commandJdo, slaveConfig);
+        final CommandDto commandDto = commandFetcher.fetchCommand(this.commandJdo, getSlaveConfig());
         return commandDto == null
                 ? null
                 : commandServiceJdoRepository.saveForReplay(commandDto);
@@ -115,36 +102,11 @@ public class CommandJdo_replayNext {
         return !getSlaveConfig().isConfigured();
     }
 
-    // //////////////////////////////////////
-
-    /**
-     * lazily loaded
-     */
-    SlaveConfiguration slaveConfig;
-    private SlaveConfiguration getSlaveConfig() {
-        if (slaveConfig == null){
-            slaveConfig = new SlaveConfiguration(configurationServiceInternal.asMap());
-        }
-        return slaveConfig;
-    }
-
-    // //////////////////////////////////////
-
-    @javax.inject.Inject
-    CommandServiceJdoRepository commandServiceJdoRepository;
-
-    @javax.inject.Inject
-    CommandFetcher commandFetcher;
-
-    @javax.inject.Inject
-    CommandExecutorService commandExecutorService;
-
-    @javax.inject.Inject
-    ConfigurationServiceInternal configurationServiceInternal;
-
-    @javax.inject.Inject
-    MessageService messageService;
 
-    @Inject
-    CommandReplayAnalysisService analysisService;
+    @Inject CommandServiceJdoRepository commandServiceJdoRepository;
+    @Inject CommandFetcher commandFetcher;
+    @Inject CommandExecutorService commandExecutorService;
+    @Inject IsisConfiguration isisConfiguration;
+    @Inject MessageService messageService;
+    @Inject CommandReplayAnalysisService analysisService;
 }
diff --git a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/mixins/CommandJdo_replayQueue.java b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/mixins/CommandJdo_replayQueue.java
index 810d736..9973d57 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/mixins/CommandJdo_replayQueue.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/impl/mixins/CommandJdo_replayQueue.java
@@ -2,70 +2,45 @@ package org.isisaddons.module.command.replay.impl.mixins;
 
 import java.util.List;
 
-import org.apache.isis.applib.annotation.Action;
-import org.apache.isis.applib.annotation.ActionLayout;
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.Collection;
 import org.apache.isis.applib.annotation.CollectionLayout;
-import org.apache.isis.applib.annotation.Contributed;
 import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.annotation.Mixin;
-import org.apache.isis.applib.annotation.SemanticsOf;
-import org.apache.isis.core.metamodel.services.configinternal.ConfigurationServiceInternal;
 
-import org.isisaddons.module.command.CommandModule;
 import org.isisaddons.module.command.dom.CommandJdo;
 import org.isisaddons.module.command.dom.CommandServiceJdoRepository;
+import org.isisaddons.module.command.replay.IsisModuleExtCommandReplayImpl;
 import org.isisaddons.module.command.replay.impl.SlaveConfiguration;
 
+@Collection(
+        domainEvent = CommandJdo_replayQueue.CollectionDomainEvent.class
+)
+@CollectionLayout(
+        defaultView = "table"
+)
 @Mixin(method = "coll")
 public class CommandJdo_replayQueue {
 
-
-    public static class ActionDomainEvent
-            extends CommandModule.ActionDomainEvent<CommandJdo_replayQueue> { }
-
+    public static class CollectionDomainEvent
+            extends IsisModuleExtCommandReplayImpl.CollectionDomainEvent<CommandJdo_replayQueue, CommandJdo> { }
 
     private final CommandJdo commandJdo;
     public CommandJdo_replayQueue(final CommandJdo commandJdo) {
         this.commandJdo = commandJdo;
     }
 
-
-    @Action(
-            domainEvent = ActionDomainEvent.class,
-            semantics = SemanticsOf.SAFE
-    )
-    @ActionLayout(
-            contributed = Contributed.AS_ASSOCIATION
-    )
-    @CollectionLayout(
-            defaultView = "table"
-    )
     @MemberOrder(sequence = "100.100")
     public List<CommandJdo> coll() {
         return commandServiceJdoRepository.findReplayedOnSlave();
     }
 
     public boolean hideColl() {
-        return !getSlaveConfig().isConfigured();
+        return !slaveConfiguration.isConfigured();
     }
 
-    /**
-     * lazily loaded
-     */
-    SlaveConfiguration slaveConfig;
-    private SlaveConfiguration getSlaveConfig() {
-        if (slaveConfig == null){
-            slaveConfig = new SlaveConfiguration(configurationServiceInternal.asMap());
-        }
-        return slaveConfig;
-    }
-
-    // //////////////////////////////////////
-
-    @javax.inject.Inject
-    CommandServiceJdoRepository commandServiceJdoRepository;
-
-    @javax.inject.Inject
-    ConfigurationServiceInternal configurationServiceInternal;
+    @Inject SlaveConfiguration slaveConfiguration;
+    @Inject CommandServiceJdoRepository commandServiceJdoRepository;
 
 }
diff --git a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyser.java b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyser.java
index 747bb38..b13c7bc 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyser.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyser.java
@@ -9,5 +9,4 @@ public interface CommandReplayAnalyser {
     @Programmatic
     String analyzeReplay(final Command command, final CommandDto dto);
 
-
 }
diff --git a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyserAbstract.java b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyserAbstract.java
index 04e8dd1..152b161 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyserAbstract.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyserAbstract.java
@@ -6,8 +6,8 @@ import javax.annotation.PostConstruct;
 
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.core.metamodel.facets.object.domainobject.Util;
-import org.apache.isis.schema.cmd.v1.CommandDto;
+import org.apache.isis.core.config.metamodel.facets.Util;
+import org.apache.isis.schema.cmd.v2.CommandDto;
 
 public abstract class CommandReplayAnalyserAbstract implements CommandReplayAnalyser {
 
diff --git a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyserExceptionStr.java b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyserExceptionStr.java
index 0a0cada..7313f416 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyserExceptionStr.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyserExceptionStr.java
@@ -3,15 +3,12 @@ package org.isisaddons.module.command.replay.spi;
 import com.google.common.base.Objects;
 
 import org.apache.isis.applib.annotation.DomainService;
-import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.applib.services.command.CommandWithDto;
-import org.apache.isis.schema.cmd.v1.CommandDto;
-import org.apache.isis.schema.utils.CommandDtoUtils;
+import org.apache.isis.applib.util.schema.CommandDtoUtils;
+import org.apache.isis.schema.cmd.v2.CommandDto;
 
-@DomainService(
-        nature = NatureOfService.DOMAIN
-)
+@DomainService()
 public class CommandReplayAnalyserExceptionStr extends CommandReplayAnalyserAbstract {
 
     public static final String ANALYSIS_KEY = "isis.services."
diff --git a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyserNumberBackgroundCommands.java b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyserNumberBackgroundCommands.java
index a2bf570..b998626 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyserNumberBackgroundCommands.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyserNumberBackgroundCommands.java
@@ -5,18 +5,15 @@ import java.util.List;
 import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.DomainService;
-import org.apache.isis.applib.annotation.NatureOfService;
-import org.apache.isis.applib.conmap.spi.CommandDtoProcessorService;
 import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.schema.cmd.v1.CommandDto;
-import org.apache.isis.schema.utils.CommandDtoUtils;
+import org.apache.isis.applib.services.conmap.command.spi.CommandDtoProcessorService;
+import org.apache.isis.applib.util.schema.CommandDtoUtils;
+import org.apache.isis.schema.cmd.v2.CommandDto;
 
 import org.isisaddons.module.command.dom.CommandJdo;
 import org.isisaddons.module.command.dom.CommandServiceJdoRepository;
 
-@DomainService(
-        nature = NatureOfService.DOMAIN
-)
+@DomainService()
 public class CommandReplayAnalyserNumberBackgroundCommands
         extends CommandReplayAnalyserAbstract
         implements CommandDtoProcessorService {
@@ -74,7 +71,6 @@ public class CommandReplayAnalyserNumberBackgroundCommands
                     masterNumBackgroundCommandsStr, USERDATA_KEY_NUMBER_BACKGROUND_COMMANDS);
         }
 
-
         final CommandJdo commandJdo = (CommandJdo) command;
         final List<CommandJdo> backgroundCommands =
                 commandServiceJdoRepository.findBackgroundCommandsByParent(commandJdo);
@@ -88,7 +84,6 @@ public class CommandReplayAnalyserNumberBackgroundCommands
                 masterNumBackgroundCommands, slaveNumBackgroundCommands);
     }
 
-    @Inject
-    CommandServiceJdoRepository commandServiceJdoRepository;
+    @Inject CommandServiceJdoRepository commandServiceJdoRepository;
 
 }
diff --git a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyserResultStr.java b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyserResultStr.java
index 7562b65..a0fadcd 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyserResultStr.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/CommandReplayAnalyserResultStr.java
@@ -3,17 +3,14 @@ package org.isisaddons.module.command.replay.spi;
 import com.google.common.base.Objects;
 
 import org.apache.isis.applib.annotation.DomainService;
-import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.applib.services.command.CommandWithDto;
-import org.apache.isis.schema.cmd.v1.CommandDto;
-import org.apache.isis.schema.utils.CommandDtoUtils;
+import org.apache.isis.applib.util.schema.CommandDtoUtils;
+import org.apache.isis.schema.cmd.v2.CommandDto;
 
 import org.isisaddons.module.command.dom.CommandJdo;
 
-@DomainService(
-        nature = NatureOfService.DOMAIN
-)
+@DomainService()
 public class CommandReplayAnalyserResultStr extends CommandReplayAnalyserAbstract {
 
     public static final String ANALYSIS_KEY = "isis.services."
diff --git a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/ReplayCommandExecutionController.java b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/ReplayCommandExecutionController.java
index 6dd223d..5ce80f5 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/ReplayCommandExecutionController.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/isisaddons/module/command/replay/spi/ReplayCommandExecutionController.java
@@ -13,7 +13,6 @@ public interface ReplayCommandExecutionController {
      * The current state, or <tt>null</tt> if the service implementing this SPI has not yet been initialized.
      * @return
      */
-    @Programmatic
     State getState();
 
 }
diff --git a/extensions/core/command-replay/impl/src/test/java/org/isisaddons/module/command/replay/impl/CommandReplayAnalysisService_trimmed_Test.java b/extensions/core/command-replay/impl/src/test/java/org/isisaddons/module/command/replay/impl/CommandReplayAnalysisService_trimmed_Test.java
index d9754bb..d500095 100644
--- a/extensions/core/command-replay/impl/src/test/java/org/isisaddons/module/command/replay/impl/CommandReplayAnalysisService_trimmed_Test.java
+++ b/extensions/core/command-replay/impl/src/test/java/org/isisaddons/module/command/replay/impl/CommandReplayAnalysisService_trimmed_Test.java
@@ -1,7 +1,7 @@
 package org.isisaddons.module.command.replay.impl;
 
 import org.assertj.core.api.Assertions;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
 public class CommandReplayAnalysisService_trimmed_Test {
 
diff --git a/extensions/core/command-replay/impl/src/test/java/org/isisaddons/module/command/replay/impl/RunBackgroundCommandsWithReplicationAndReplayJob_Test.java b/extensions/core/command-replay/impl/src/test/java/org/isisaddons/module/command/replay/impl/RunBackgroundCommandsWithReplicationAndReplayJob_Test.java
index 6774a20..3408da0 100644
--- a/extensions/core/command-replay/impl/src/test/java/org/isisaddons/module/command/replay/impl/RunBackgroundCommandsWithReplicationAndReplayJob_Test.java
+++ b/extensions/core/command-replay/impl/src/test/java/org/isisaddons/module/command/replay/impl/RunBackgroundCommandsWithReplicationAndReplayJob_Test.java
@@ -4,21 +4,23 @@ import java.net.URI;
 
 import javax.ws.rs.core.UriBuilder;
 
-import org.junit.Ignore;
-import org.junit.Test;
+
+import org.junit.jupiter.api.Test;
 
 import org.apache.isis.applib.services.jaxb.JaxbService;
-import org.apache.isis.schema.cmd.v1.CommandsDto;
+import org.apache.isis.extensions.jaxrsclient.applib.client.JaxRsClient;
+import org.apache.isis.extensions.jaxrsclient.applib.client.JaxRsResponse;
+import org.apache.isis.extensions.jaxrsclient.impl.client.JaxRsClientDefault;
+import org.apache.isis.schema.cmd.v2.CommandsDto;
+
+import lombok.val;
 
-import org.incode.module.jaxrsclient.dom.JaxRsClient;
-import org.incode.module.jaxrsclient.dom.JaxRsResponse;
 
 public class RunBackgroundCommandsWithReplicationAndReplayJob_Test {
 
-    @Ignore
     @Test
     public void testing_the_unmarshalling() throws Exception {
-        JaxRsClient.Default jaxRsClient = new JaxRsClient.Default();
+        val jaxRsClient = new JaxRsClientDefault();
         final UriBuilder uriBuilder = UriBuilder.fromUri(
                         String.format(
                         "%s%s?batchSize=%d",
diff --git a/extensions/core/flyway/adoc/modules/flyway/pages/about.adoc b/extensions/core/flyway/adoc/modules/flyway/pages/about.adoc
index 5bcf792..cd51b74 100644
--- a/extensions/core/flyway/adoc/modules/flyway/pages/about.adoc
+++ b/extensions/core/flyway/adoc/modules/flyway/pages/about.adoc
@@ -2,7 +2,7 @@
 
 :Notice: 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 ag [...]
 
-This module provides an very thin layer to use Spring Boot's integration with https://flywaydb.org[Flyway]
+This module provides a very thin layer to use Spring Boot's integration with https://flywaydb.org[Flyway]
 
 == Configuration
 
diff --git a/extensions/core/quartz/adoc/antora.yml b/extensions/core/quartz/adoc/antora.yml
new file mode 100644
index 0000000..448d819
--- /dev/null
+++ b/extensions/core/quartz/adoc/antora.yml
@@ -0,0 +1,19 @@
+#  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.
+
+name: userguide
+version: latest
diff --git a/extensions/core/quartz/adoc/modules/quartz/nav.adoc b/extensions/core/quartz/adoc/modules/quartz/nav.adoc
new file mode 100644
index 0000000..35b7262
--- /dev/null
+++ b/extensions/core/quartz/adoc/modules/quartz/nav.adoc
@@ -0,0 +1,4 @@
+
+:Notice: 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 ag [...]
+
+include::userguide:ROOT:partial$component-nav.adoc[]
diff --git a/extensions/core/quartz/adoc/modules/quartz/pages/about.adoc b/extensions/core/quartz/adoc/modules/quartz/pages/about.adoc
new file mode 100644
index 0000000..d37878d
--- /dev/null
+++ b/extensions/core/quartz/adoc/modules/quartz/pages/about.adoc
@@ -0,0 +1,5 @@
+= Quartz
+
+:Notice: 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 ag [...]
+
+CAUTION: TODO v2 - to document
diff --git a/extensions/core/quartz/adoc/modules/quartz/partials/module-nav.adoc b/extensions/core/quartz/adoc/modules/quartz/partials/module-nav.adoc
new file mode 100644
index 0000000..6915212
--- /dev/null
+++ b/extensions/core/quartz/adoc/modules/quartz/partials/module-nav.adoc
@@ -0,0 +1,5 @@
+
+
+** xref:userguide:quartz:about.adoc[Quartz]
+
+
diff --git a/extensions/core/quartz/impl/pom.xml b/extensions/core/quartz/impl/pom.xml
new file mode 100644
index 0000000..b16d8ba
--- /dev/null
+++ b/extensions/core/quartz/impl/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>org.apache.isis.extensions</groupId>
+		<artifactId>isis-extensions-quartz</artifactId>
+		<version>2.0.0-SNAPSHOT</version>
+	</parent>
+
+	<artifactId>isis-extensions-quartz-impl</artifactId>
+	<name>Apache Isis Ext - Quartz Impl</name>
+
+	<properties>
+		<jar-plugin.automaticModuleName>org.apache.isis.extensions.quartz.impl</jar-plugin.automaticModuleName>
+		<git-plugin.propertiesDir>org/apache/isis/extensions/quartz/impl</git-plugin.propertiesDir>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.isis.core</groupId>
+			<artifactId>isis-core-config</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>com.google.guava</groupId>
+			<artifactId>guava</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-quartz</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.isis.core</groupId>
+			<artifactId>isis-core-security</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.isis.extensions</groupId>
+			<artifactId>isis-extensions-command-log-impl</artifactId>
+		</dependency>
+
+		<!--
+                <dependency>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-context</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-autoconfigure</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-jdbc</artifactId>
+                </dependency>
+        -->
+
+	</dependencies>
+
+</project>
diff --git a/extensions/core/quartz/impl/src/main/java/org/isisaddons/module/quartz/dom/IsisModuleExtQuartzImpl.java b/extensions/core/quartz/impl/src/main/java/org/isisaddons/module/quartz/dom/IsisModuleExtQuartzImpl.java
new file mode 100644
index 0000000..e0c7168
--- /dev/null
+++ b/extensions/core/quartz/impl/src/main/java/org/isisaddons/module/quartz/dom/IsisModuleExtQuartzImpl.java
@@ -0,0 +1,15 @@
+package org.isisaddons.module.quartz.dom;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+@Configuration
+@Import({
+        // @Configuration's
+
+        // @DomainService's
+
+})
+
+public class IsisModuleExtQuartzImpl {
+}
diff --git a/extensions/core/quartz/impl/src/main/java/org/isisaddons/module/quartz/dom/jobs/RunBackgroundCommandsJob.java b/extensions/core/quartz/impl/src/main/java/org/isisaddons/module/quartz/dom/jobs/RunBackgroundCommandsJob.java
new file mode 100644
index 0000000..d46260f
--- /dev/null
+++ b/extensions/core/quartz/impl/src/main/java/org/isisaddons/module/quartz/dom/jobs/RunBackgroundCommandsJob.java
@@ -0,0 +1,40 @@
+package org.isisaddons.module.quartz.dom.jobs;
+
+
+import com.google.common.base.Splitter;
+
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+
+import org.apache.isis.core.security.authentication.AuthenticationSession;
+import org.apache.isis.core.security.authentication.standard.SimpleSession;
+
+import org.isisaddons.module.command.dom.BackgroundCommandExecutionFromBackgroundCommandServiceJdo;
+
+import lombok.val;
+import lombok.extern.log4j.Log4j2;
+
+@Log4j2
+public class RunBackgroundCommandsJob implements Job {
+
+    public void execute(final JobExecutionContext context) {
+
+        final AuthenticationSession authSession = newAuthSession(context);
+
+        log.debug("Running background commands");
+        new BackgroundCommandExecutionFromBackgroundCommandServiceJdo().execute(authSession, null);
+    }
+
+    protected String getKey(JobExecutionContext context, String key) {
+        return context.getMergedJobDataMap().getString(key);
+    }
+
+    protected AuthenticationSession newAuthSession(JobExecutionContext context) {
+        val user = getKey(context, "user");
+        val rolesStr = getKey(context, "roles");
+        val roles = Splitter.on(",").split(rolesStr);
+        return new SimpleSession(user, roles);
+    }
+
+
+}
diff --git a/extensions/core/quartz/pom.xml b/extensions/core/quartz/pom.xml
new file mode 100644
index 0000000..fe676b8
--- /dev/null
+++ b/extensions/core/quartz/pom.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>org.apache.isis.extensions</groupId>
+		<artifactId>isis-extensions</artifactId>
+		<version>2.0.0-SNAPSHOT</version>
+		<relativePath>../../pom.xml</relativePath>
+	</parent>
+
+	<artifactId>isis-extensions-quartz</artifactId>
+	<name>Apache Isis Ext - Quartz</name>
+	<description>Integrates Quartz</description>
+
+	<packaging>pom</packaging>
+
+	<dependencyManagement>
+		<dependencies>
+		</dependencies>
+	</dependencyManagement>
+
+	<modules>
+		<module>impl</module>
+	</modules>
+
+</project>
diff --git a/extensions/pom.xml b/extensions/pom.xml
index 2eab228..39afdf3 100644
--- a/extensions/pom.xml
+++ b/extensions/pom.xml
@@ -70,6 +70,12 @@
 
 			<dependency>
 				<groupId>org.apache.isis.extensions</groupId>
+				<artifactId>isis-extensions-quartz-impl</artifactId>
+				<version>2.0.0-SNAPSHOT</version>
+			</dependency>
+
+			<dependency>
+				<groupId>org.apache.isis.extensions</groupId>
 				<artifactId>isis-extensions-command-replay-impl</artifactId>
 				<version>2.0.0-SNAPSHOT</version>
 			</dependency>
@@ -185,6 +191,8 @@
 		<module>core/command-replay</module>
 		<module>core/flyway</module>
 		<module>core/model-annotation</module>
+		<module>core/quartz</module>
+
 		<module>security/secman</module>
 		<module>security/shiro-realm-ldap</module>