You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2019/09/29 12:25:57 UTC

[isis] branch v2 updated: ISIS-2158: wrapper: extend async programming model to also match method references that don't return a value (like Runnable)

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

ahuber pushed a commit to branch v2
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/v2 by this push:
     new f22bce0  ISIS-2158: wrapper: extend async programming model to also match method references that don't return a value (like Runnable)
f22bce0 is described below

commit f22bce0794685570574cd9b05780d9369e7353da
Author: Andi Huber <ah...@apache.org>
AuthorDate: Sun Sep 29 14:25:50 2019 +0200

    ISIS-2158: wrapper: extend async programming model to also match method
    references that don't return a value (like Runnable)
    
    - BackgroundService seems now obsolete, need to check whether it can be
    removed
---
 .../services/background/BackgroundService.java     |   4 +
 .../isis/applib/services/command/Command.java      |   2 +-
 .../isis/applib/services/wrapper/AsyncWrap.java    | 106 +++++++++++++++++----
 .../org/apache/isis/wrapper/AsyncWrapDefault.java  |  36 +++----
 .../background/BackgroundServiceDefault.java       |   5 +-
 .../java/domainapp/dom/events/EventSubscriber.java |  22 ++---
 .../testdomain/auditing/AuditerServiceTest.java    |  19 ++--
 .../commandexecution/BackgroundExecutionTest.java  |  12 +--
 .../testdomain/commandexecution/WrapperTest.java   |  14 ++-
 .../publishing/PublisherServiceTest.java           |  19 ++--
 .../bootstrapping/builtin-singleton.list           |   2 +-
 11 files changed, 163 insertions(+), 78 deletions(-)

diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/background/BackgroundService.java b/core/applib/src/main/java/org/apache/isis/applib/services/background/BackgroundService.java
index ef23918..e44840b 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/background/BackgroundService.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/background/BackgroundService.java
@@ -18,6 +18,8 @@
  */
 package org.apache.isis.applib.services.background;
 
+import org.apache.isis.applib.services.wrapper.WrapperFactory;
+
 /**
  * Submit actions to be invoked in the background.
  *
@@ -33,6 +35,8 @@ package org.apache.isis.applib.services.background;
  * &#64;javax.inject.Inject
  * private BackgroundService backgroundService;
  * </pre>
+ * 
+ * @deprecated replaced by the {@link WrapperFactory#async(Object)}
  */
 public interface BackgroundService {
 
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/command/Command.java b/core/applib/src/main/java/org/apache/isis/applib/services/command/Command.java
index ce84f41..aa9d09e 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/command/Command.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/command/Command.java
@@ -29,7 +29,6 @@ import org.apache.isis.applib.clock.Clock;
 import org.apache.isis.applib.events.domain.ActionDomainEvent;
 import org.apache.isis.applib.services.HasUniqueId;
 import org.apache.isis.applib.services.background.BackgroundCommandService;
-import org.apache.isis.applib.services.background.BackgroundService;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.bookmark.BookmarkService;
 import org.apache.isis.applib.services.iactn.Interaction;
@@ -55,6 +54,7 @@ import org.apache.isis.schema.cmd.v1.CommandDto;
  * </p>
  *
  * <p>
+ *     TODO[2158], no longer true!
  *     One of the responsibilities of the command is to generate unique sequence numbers for a given transactionId.
  *     This is done by {@link #next(String)}.  There are three possible sequences that might be generated:
  *     the sequence of changed domain objects being published by the {@link org.apache.isis.applib.services.publish.PublisherService#publish(Interaction.Execution)}; the
diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/wrapper/AsyncWrap.java b/core/applib/src/main/java/org/apache/isis/applib/services/wrapper/AsyncWrap.java
index f011e60..613c0cd 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/wrapper/AsyncWrap.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/wrapper/AsyncWrap.java
@@ -35,31 +35,58 @@ import org.apache.isis.applib.services.wrapper.WrapperFactory.ExecutionMode;
  */
 public interface AsyncWrap<T> {
 
-    // -- METHOD REFERENCE MATCHERS
+    // -- METHOD REFERENCE MATCHERS (WITHOUT RETURN VALUE)
     
     @FunctionalInterface
-    public static interface Invoke0<T, R> {
-        R invoke(T obj);
+    public static interface Run0<T> {
+        void run(T obj);
     }
     
     @FunctionalInterface
-    public static interface Invoke1<T, R, A1> {
-        R invoke(T obj, A1 arg1);
+    public static interface Run1<T, A1> {
+        void run(T obj, A1 arg1);
     }
     
     @FunctionalInterface
-    public static interface Invoke2<T, R, A1, A2> {
-        R invoke(T obj, A1 arg1, A2 arg2);
+    public static interface Run2<T, A1, A2> {
+        void run(T obj, A1 arg1, A2 arg2);
     }
     
     @FunctionalInterface
-    public static interface Invoke3<T, R, A1, A2, A3> {
-        R invoke(T obj, A1 arg1, A2 arg2, A3 arg3);
+    public static interface Run3<T, A1, A2, A3> {
+        void run(T obj, A1 arg1, A2 arg2, A3 arg3);
     }
     
     @FunctionalInterface
-    public static interface Invoke4<T, R, A1, A2, A3, A4> {
-        R invoke(T obj, A1 arg1, A2 arg2, A3 arg3, A4 arg4);
+    public static interface Run4<T, A1, A2, A3, A4> {
+        void run(T obj, A1 arg1, A2 arg2, A3 arg3, A4 arg4);
+    }
+    
+    // -- METHOD REFERENCE MATCHERS (WITH RETURN VALUE)
+    
+    @FunctionalInterface
+    public static interface Call0<T, R> {
+        R call(T obj);
+    }
+    
+    @FunctionalInterface
+    public static interface Call1<T, R, A1> {
+        R call(T obj, A1 arg1);
+    }
+    
+    @FunctionalInterface
+    public static interface Call2<T, R, A1, A2> {
+        R call(T obj, A1 arg1, A2 arg2);
+    }
+    
+    @FunctionalInterface
+    public static interface Call3<T, R, A1, A2, A3> {
+        R call(T obj, A1 arg1, A2 arg2, A3 arg3);
+    }
+    
+    @FunctionalInterface
+    public static interface Call4<T, R, A1, A2, A3, A4> {
+        R call(T obj, A1 arg1, A2 arg2, A3 arg3, A4 arg4);
     }
     
     // -- CONFIGURATION
@@ -72,20 +99,65 @@ public interface AsyncWrap<T> {
     
     EnumSet<ExecutionMode> getExecutionMode();
     
-    // -- INVOCATION
+    // -- INVOCATION (WITH RETURN VALUE)
     
-    <R> Future<R> invoke(Invoke0<? super T, ? extends R> action);
+    <R> Future<R> call(Call0<? super T, ? extends R> action);
     
-    <R, A1> Future<R> invoke(Invoke1<? super T, ? extends R, A1> action, 
+    <R, A1> Future<R> call(Call1<? super T, ? extends R, A1> action, 
             A1 arg1);
     
-    <R, A1, A2> Future<R> invoke(Invoke2<? super T, ? extends R, A1, A2> action, 
+    <R, A1, A2> Future<R> call(Call2<? super T, ? extends R, A1, A2> action, 
             A1 arg1, A2 arg2);
     
-    <R, A1, A2, A3> Future<R> invoke(Invoke3<? super T, ? extends R, A1, A2, A3> action, 
+    <R, A1, A2, A3> Future<R> call(Call3<? super T, ? extends R, A1, A2, A3> action, 
             A1 arg1, A2 arg2, A3 arg3);
     
-    <R, A1, A2, A3, A4> Future<R> invoke(Invoke4<? super T, ? extends R, A1, A2, A3, A4> action, 
+    <R, A1, A2, A3, A4> Future<R> call(Call4<? super T, ? extends R, A1, A2, A3, A4> action, 
             A1 arg1, A2 arg2, A3 arg3, A4 arg4);
     
+    // -- INVOCATION (WITHOUT RETURN VALUE)
+    
+    default Future<Void> run(Run0<? super T> action) {
+        return call(obj->{
+            action.run(obj); 
+            return null;
+        });
+    }
+    
+    default <A1> Future<Void> run(Run1<? super T, A1> action, 
+            A1 arg1) {
+        
+        return call(obj->{
+            action.run(obj, arg1); 
+            return null;
+        });
+    }
+    
+    default <A1, A2> Future<Void> run(Run2<? super T, A1, A2> action, 
+            A1 arg1, A2 arg2) {
+        
+        return call(obj->{
+            action.run(obj, arg1, arg2); 
+            return null;
+        });
+    }
+    
+    default <A1, A2, A3> Future<Void> run(Run3<? super T, A1, A2, A3> action, 
+            A1 arg1, A2 arg2, A3 arg3) {
+        
+        return call(obj->{
+            action.run(obj, arg1, arg2, arg3); 
+            return null;
+        });
+    }
+    
+    default <A1, A2, A3, A4> Future<Void> run(Run4<? super T, A1, A2, A3, A4> action, 
+            A1 arg1, A2 arg2, A3 arg3, A4 arg4) {
+        
+        return call(obj->{
+            action.run(obj, arg1, arg2, arg3, arg4); 
+            return null;
+        });
+    }
+    
 }
diff --git a/core/runtime-extensions/src/main/java/org/apache/isis/wrapper/AsyncWrapDefault.java b/core/runtime-extensions/src/main/java/org/apache/isis/wrapper/AsyncWrapDefault.java
index 086c0c0..490b45f 100644
--- a/core/runtime-extensions/src/main/java/org/apache/isis/wrapper/AsyncWrapDefault.java
+++ b/core/runtime-extensions/src/main/java/org/apache/isis/wrapper/AsyncWrapDefault.java
@@ -68,22 +68,24 @@ class AsyncWrapDefault<T> implements AsyncWrap<T> {
     @Getter(onMethod = @__({@Override})) 
     @With(value = AccessLevel.PUBLIC, onMethod = @__(@Override)) 
     private Consumer<Exception> exceptionHandler;  
-    
 
+        
+    // -- METHOD REFERENCE MATCHERS (WITH RETURN VALUE)
+    
     @Override
-    public <R> Future<R> invoke(Invoke0<? super T, ? extends R> action) {
+    public <R> Future<R> call(Call0<? super T, ? extends R> action) {
         
         if(shouldValidate()) {
             // do validation synchronous (with the calling thread)
             val proxy_validateOnly = wrapper.wrap(domainObject, ExecutionMode.NO_EXECUTE);
-            action.invoke(proxy_validateOnly);
+            action.call(proxy_validateOnly);
         }
         
         if(shouldExecute()) {
             // to also trigger domain events, we need a proxy, but validation (if required)
             // was already done above
             val proxy_executeOnly = wrapper.wrap(domainObject, ExecutionMode.SKIP_RULES);
-            return submit(()->action.invoke(proxy_executeOnly));
+            return submit(()->action.call(proxy_executeOnly));
         }
         
         return CompletableFuture.completedFuture(null);
@@ -91,38 +93,38 @@ class AsyncWrapDefault<T> implements AsyncWrap<T> {
 
 
     @Override
-    public <R, A1> Future<R> invoke(Invoke1<? super T, ? extends R, A1> action, A1 arg1) {
+    public <R, A1> Future<R> call(Call1<? super T, ? extends R, A1> action, A1 arg1) {
         
         if(shouldValidate()) {
             // do validation synchronous (with the calling thread)
             val proxy_validateOnly = wrapper.wrap(domainObject, ExecutionMode.NO_EXECUTE);
-            action.invoke(proxy_validateOnly, arg1);
+            action.call(proxy_validateOnly, arg1);
         }
         
         if(shouldExecute()) {
             // to also trigger domain events, we need a proxy, but validation (if required)
             // was already done above
             val proxy_executeOnly = wrapper.wrap(domainObject, ExecutionMode.SKIP_RULES);
-            return submit(()->action.invoke(proxy_executeOnly, arg1));
+            return submit(()->action.call(proxy_executeOnly, arg1));
         }
         
         return CompletableFuture.completedFuture(null);
     }
 
     @Override
-    public <R, A1, A2> Future<R> invoke(Invoke2<? super T, ? extends R, A1, A2> action, A1 arg1, A2 arg2) {
+    public <R, A1, A2> Future<R> call(Call2<? super T, ? extends R, A1, A2> action, A1 arg1, A2 arg2) {
 
         if(shouldValidate()) {
             // do validation synchronous (with the calling thread)
             val proxy_validateOnly = wrapper.wrap(domainObject, ExecutionMode.NO_EXECUTE);
-            action.invoke(proxy_validateOnly, arg1, arg2);
+            action.call(proxy_validateOnly, arg1, arg2);
         }
         
         if(shouldExecute()) {
             // to also trigger domain events, we need a proxy, but validation (if required)
             // was already done above
             val proxy_executeOnly = wrapper.wrap(domainObject, ExecutionMode.SKIP_RULES);
-            return submit(()->action.invoke(proxy_executeOnly, arg1, arg2));
+            return submit(()->action.call(proxy_executeOnly, arg1, arg2));
         }
         
         return CompletableFuture.completedFuture(null);
@@ -130,20 +132,20 @@ class AsyncWrapDefault<T> implements AsyncWrap<T> {
     
 
     @Override
-    public <R, A1, A2, A3> Future<R> invoke(Invoke3<? super T, ? extends R, A1, A2, A3> action, 
+    public <R, A1, A2, A3> Future<R> call(Call3<? super T, ? extends R, A1, A2, A3> action, 
             A1 arg1, A2 arg2, A3 arg3) {
         
         if(shouldValidate()) {
             // do validation synchronous (with the calling thread)
             val proxy_validateOnly = wrapper.wrap(domainObject, ExecutionMode.NO_EXECUTE);
-            action.invoke(proxy_validateOnly, arg1, arg2, arg3);
+            action.call(proxy_validateOnly, arg1, arg2, arg3);
         }
         
         if(shouldExecute()) {
          // to also trigger domain events, we need a proxy, but validation (if required)
             // was already done above
             val proxy_executeOnly = wrapper.wrap(domainObject, ExecutionMode.SKIP_RULES);
-            return submit(()->action.invoke(proxy_executeOnly, arg1, arg2, arg3));
+            return submit(()->action.call(proxy_executeOnly, arg1, arg2, arg3));
         }
         
         return CompletableFuture.completedFuture(null);
@@ -151,20 +153,20 @@ class AsyncWrapDefault<T> implements AsyncWrap<T> {
 
 
     @Override
-    public <R, A1, A2, A3, A4> Future<R> invoke(Invoke4<? super T, ? extends R, A1, A2, A3, A4> action, 
+    public <R, A1, A2, A3, A4> Future<R> call(Call4<? super T, ? extends R, A1, A2, A3, A4> action, 
             A1 arg1, A2 arg2, A3 arg3, A4 arg4) {
 
         if(shouldValidate()) {
             // do validation synchronous (with the calling thread)
             val proxy_validateOnly = wrapper.wrap(domainObject, ExecutionMode.NO_EXECUTE);
-            action.invoke(proxy_validateOnly, arg1, arg2, arg3, arg4);
+            action.call(proxy_validateOnly, arg1, arg2, arg3, arg4);
         }
         
         if(shouldExecute()) {
             // to also trigger domain events, we need a proxy, but validation (if required)
             // was already done above
             val proxy_executeOnly = wrapper.wrap(domainObject, ExecutionMode.SKIP_RULES);
-            return submit(()->action.invoke(proxy_executeOnly, arg1, arg2, arg3, arg4));
+            return submit(()->action.call(proxy_executeOnly, arg1, arg2, arg3, arg4));
         }
         
         return CompletableFuture.completedFuture(null);
@@ -219,7 +221,7 @@ class AsyncWrapDefault<T> implements AsyncWrap<T> {
     private boolean shouldExecute() {
         return !executionMode.contains(ExecutionMode.SKIP_EXECUTION);
     }
-    
+
 
 
 }
diff --git a/core/runtime-services/src/main/java/org/apache/isis/runtime/services/background/BackgroundServiceDefault.java b/core/runtime-services/src/main/java/org/apache/isis/runtime/services/background/BackgroundServiceDefault.java
index 618c1f7..e8311d6 100644
--- a/core/runtime-services/src/main/java/org/apache/isis/runtime/services/background/BackgroundServiceDefault.java
+++ b/core/runtime-services/src/main/java/org/apache/isis/runtime/services/background/BackgroundServiceDefault.java
@@ -49,9 +49,10 @@ import lombok.val;
  * For command-reification depends on an implementation of
  * {@link org.apache.isis.applib.services.background.BackgroundCommandService} to
  * be configured.
+ * @deprecated compare with v1.x and check whether we can cleanup the API
  */
-@Service
-public class BackgroundServiceDefault implements BackgroundService {
+//@Service
+class BackgroundServiceDefault implements BackgroundService {
 
     private InvocationHandlerFactory invocationHandlerFactory;  
 
diff --git a/examples/apps/demo/src/main/java/domainapp/dom/events/EventSubscriber.java b/examples/apps/demo/src/main/java/domainapp/dom/events/EventSubscriber.java
index 580ac75..0965003 100644
--- a/examples/apps/demo/src/main/java/domainapp/dom/events/EventSubscriber.java
+++ b/examples/apps/demo/src/main/java/domainapp/dom/events/EventSubscriber.java
@@ -27,9 +27,8 @@ import org.springframework.stereotype.Component;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.events.domain.AbstractDomainEvent;
-import org.apache.isis.applib.services.background.BackgroundService;
 import org.apache.isis.applib.services.eventbus.EventBusService;
-import org.apache.isis.applib.services.xactn.TransactionService;
+import org.apache.isis.applib.services.wrapper.WrapperFactory;
 
 import static domainapp.utils.DemoUtils.emphasize;
 
@@ -40,10 +39,10 @@ import lombok.extern.log4j.Log4j2;
 @Log4j2 @Component
 public class EventSubscriber {
 
-    @Inject private BackgroundService backgroundService;
+    @Inject private WrapperFactory wrapper;
     @Inject private EventBusService eventBusService;
     @Inject private EventLogRepository eventLogRepository;
-    @Inject private TransactionService transactionService;
+//    @Inject private TransactionService transactionService;
 
     public static class EventSubscriberEvent extends AbstractDomainEvent<Object> {}
 
@@ -61,15 +60,14 @@ public class EventSubscriber {
         }
 
         log.info(emphasize("DomainEvent: "+ev.getClass().getName()));
-
-        //backgroundService.execute(this).storeEvent(EventLogEntry.of(ev));
-
+        
         // store in event log
-        //eventLog.add(EventLogEntry.of(ev));
-
-        transactionService.executeWithinTransaction(()->{
-            storeEvent(ev);
-        });
+        wrapper.async(this)
+        .run(EventSubscriber::storeEvent, ev);
+        
+//        transactionService.executeWithinTransaction(()->{
+//            storeEvent(ev);
+//        });
 
     }
 
diff --git a/examples/smoketests/src/test/java/org/apache/isis/testdomain/auditing/AuditerServiceTest.java b/examples/smoketests/src/test/java/org/apache/isis/testdomain/auditing/AuditerServiceTest.java
index 665215e..a794742 100644
--- a/examples/smoketests/src/test/java/org/apache/isis/testdomain/auditing/AuditerServiceTest.java
+++ b/examples/smoketests/src/test/java/org/apache/isis/testdomain/auditing/AuditerServiceTest.java
@@ -20,6 +20,9 @@ package org.apache.isis.testdomain.auditing;
 
 import java.sql.Timestamp;
 import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 import javax.inject.Inject;
 
@@ -30,9 +33,10 @@ import org.springframework.stereotype.Service;
 import org.springframework.test.context.TestPropertySource;
 
 import org.apache.isis.applib.services.audit.AuditerService;
-import org.apache.isis.applib.services.background.BackgroundService;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.applib.services.wrapper.WrapperFactory;
+import org.apache.isis.applib.services.wrapper.WrapperFactory.ExecutionMode;
 import org.apache.isis.config.IsisPresets;
 import org.apache.isis.extensions.fixtures.fixturescripts.FixtureScripts;
 import org.apache.isis.runtime.system.context.IsisContext;
@@ -63,7 +67,7 @@ class AuditerServiceTest {
 
     @Inject private RepositoryService repository;
     @Inject private FixtureScripts fixtureScripts;
-    @Inject private BackgroundService backgroundService;
+    @Inject private WrapperFactory wrapper;
     @Inject private AuditerServiceProbe auditerService;
 
     @BeforeEach
@@ -105,7 +109,8 @@ class AuditerServiceTest {
     }
 
     @Test
-    void auditerServiceShouldBeAwareOfInventoryChanges_whenUsingBackgroundService() throws InterruptedException {
+    void auditerServiceShouldBeAwareOfInventoryChanges_whenUsingBackgroundService() 
+            throws InterruptedException, ExecutionException, TimeoutException {
 
         // given
         val books = repository.allInstances(Book.class);
@@ -114,11 +119,11 @@ class AuditerServiceTest {
         auditerService.clearHistory();
 
         // when - running within its own background task
-        backgroundService.execute(book)
-        .setName("Book #2");
-
-        Thread.sleep(1000); //TODO fragile test, find another way to sync on the background task
+        val future = wrapper.async(book, ExecutionMode.SKIP_RULES) //TODO why do we fail when not skipping rules?
+        .run(Book::setName, "Book #2");
 
+        future.get(1000, TimeUnit.SECONDS);
+        
         // then - after the commit
         assertEquals("targetClassName=Book,propertyName=name,preValue=Sample Book,postValue=Book #2;",
                 auditerService.getHistory());
diff --git a/examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/BackgroundExecutionTest.java b/examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/BackgroundExecutionTest.java
index 5df3860..8126584 100644
--- a/examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/BackgroundExecutionTest.java
+++ b/examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/BackgroundExecutionTest.java
@@ -32,7 +32,6 @@ import org.springframework.context.event.EventListener;
 import org.springframework.stereotype.Service;
 
 import org.apache.isis.applib.events.domain.AbstractDomainEvent.Phase;
-import org.apache.isis.applib.services.background.BackgroundService;
 import org.apache.isis.applib.services.factory.FactoryService;
 import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.applib.services.wrapper.WrapperFactory;
@@ -65,9 +64,8 @@ class BackgroundExecutionTest {
 
     @Inject FixtureScripts fixtureScripts;
     @Inject RepositoryService repository;
-    @Inject BackgroundService backgroundService;
     @Inject FactoryService facoryService;
-    @Inject WrapperFactory wrapperFactory;
+    @Inject WrapperFactory wrapper;
     @Inject ActionDomainEventListener actionDomainEventListener;
 
     @BeforeEach
@@ -87,7 +85,8 @@ class BackgroundExecutionTest {
 
         assertEquals(99d, product.getPrice(), 1E-6);
 
-        backgroundService.execute(inventoryManager).updateProductPrice(product, 123);
+        wrapper.async(inventoryManager)
+        .run(InventoryManager::updateProductPrice, product, 123d);
 
         Thread.sleep(1000); //TODO fragile test, find another way to sync on the background task
         assertEquals(123d, product.getPrice(), 1E-6);
@@ -102,8 +101,9 @@ class BackgroundExecutionTest {
         assertEquals(99d, product.getPrice(), 1E-6);
 
         actionDomainEventListener.prepareLatch();
-
-        backgroundService.execute(inventoryManager).updateProductPrice(product, 123);
+        
+        wrapper.async(inventoryManager)
+        .run(InventoryManager::updateProductPrice, product, 123d);
 
         assertTrue(
                 actionDomainEventListener.getCountDownLatch()
diff --git a/examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/WrapperTest.java b/examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/WrapperTest.java
index c7d0fd7..67c76fd 100644
--- a/examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/WrapperTest.java
+++ b/examples/smoketests/src/test/java/org/apache/isis/testdomain/commandexecution/WrapperTest.java
@@ -33,7 +33,6 @@ import org.springframework.context.event.EventListener;
 import org.springframework.stereotype.Service;
 
 import org.apache.isis.applib.events.domain.AbstractDomainEvent.Phase;
-import org.apache.isis.applib.services.background.BackgroundService;
 import org.apache.isis.applib.services.factory.FactoryService;
 import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.applib.services.wrapper.WrapperFactory;
@@ -65,12 +64,11 @@ import lombok.val;
         })
 class WrapperTest {
 
-    @Inject FixtureScripts fixtureScripts;
-    @Inject RepositoryService repository;
-    @Inject BackgroundService backgroundService;
-    @Inject FactoryService facoryService;
-    @Inject WrapperFactory wrapper;
-    @Inject ActionDomainEventListener actionDomainEventListener;
+    @Inject private FixtureScripts fixtureScripts;
+    @Inject private RepositoryService repository;
+    @Inject private FactoryService facoryService;
+    @Inject private WrapperFactory wrapper;
+    @Inject private ActionDomainEventListener actionDomainEventListener;
 
     @BeforeEach
     void setUp() {
@@ -112,7 +110,7 @@ class WrapperTest {
 
         Future<Product> invocationResult = wrapper.async(inventoryManager)
                 .withExecutor(Executors.newCachedThreadPool()) // use of custom executor (optional)
-                .invoke(InventoryManager::updateProductPrice, product, 123d);
+                .call(InventoryManager::updateProductPrice, product, 123d);
 
 //XXX type-safety should prevent this snippet from being compiled!        
 //        Future<String> invocationResult2 = wrapper.async(inventoryManager)
diff --git a/examples/smoketests/src/test/java/org/apache/isis/testdomain/publishing/PublisherServiceTest.java b/examples/smoketests/src/test/java/org/apache/isis/testdomain/publishing/PublisherServiceTest.java
index 81a3c37..572a6d8 100644
--- a/examples/smoketests/src/test/java/org/apache/isis/testdomain/publishing/PublisherServiceTest.java
+++ b/examples/smoketests/src/test/java/org/apache/isis/testdomain/publishing/PublisherServiceTest.java
@@ -18,8 +18,11 @@
  */
 package org.apache.isis.testdomain.publishing;
 
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
 import javax.inject.Inject;
-import javax.inject.Singleton;
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.MethodOrderer;
@@ -29,11 +32,12 @@ import org.junit.jupiter.api.TestMethodOrder;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.stereotype.Service;
 
-import org.apache.isis.applib.services.background.BackgroundService;
 import org.apache.isis.applib.services.iactn.Interaction.Execution;
 import org.apache.isis.applib.services.publish.PublishedObjects;
 import org.apache.isis.applib.services.publish.PublisherService;
 import org.apache.isis.applib.services.repository.RepositoryService;
+import org.apache.isis.applib.services.wrapper.WrapperFactory;
+import org.apache.isis.applib.services.wrapper.WrapperFactory.ExecutionMode;
 import org.apache.isis.extensions.fixtures.fixturescripts.FixtureScripts;
 import org.apache.isis.runtime.system.context.IsisContext;
 import org.apache.isis.testdomain.Smoketest;
@@ -62,7 +66,7 @@ class PublisherServiceTest {
 
     @Inject private RepositoryService repository;
     @Inject private FixtureScripts fixtureScripts;
-    @Inject private BackgroundService backgroundService;
+    @Inject private WrapperFactory wrapper;
     @Inject private PublisherServiceProbe publisherService;
 
     @BeforeEach
@@ -102,17 +106,18 @@ class PublisherServiceTest {
     }
 
     @Test @Order(2)
-    void publisherServiceShouldBeAwareOfInventoryChanges_whenUsingBackgroundService() throws InterruptedException {
+    void publisherServiceShouldBeAwareOfInventoryChanges_whenUsingBackgroundService() 
+            throws InterruptedException, ExecutionException, TimeoutException {
 
         // given
         val book = repository.allInstances(Book.class).listIterator().next();
         publisherService.clearHistory();
 
         // when - running within its own background task
-        backgroundService.execute(book)
-        .setName("Book #2");
+        val future = wrapper.async(book, ExecutionMode.SKIP_RULES) //TODO why do we fail when not skipping rules?
+                .run(Book::setName, "Book #2");
 
-        Thread.sleep(1000); //TODO fragile test, find another way to sync on the background task
+        future.get(1000, TimeUnit.SECONDS);
 
         // then - after the commit
         assertEquals("publishedObjects=created=0,deleted=1,loaded=0,updated=2,modified=1,",
diff --git a/examples/smoketests/src/test/resources/org/apache/isis/testdomain/bootstrapping/builtin-singleton.list b/examples/smoketests/src/test/resources/org/apache/isis/testdomain/bootstrapping/builtin-singleton.list
index d45a8f7..4aff6a2 100644
--- a/examples/smoketests/src/test/resources/org/apache/isis/testdomain/bootstrapping/builtin-singleton.list
+++ b/examples/smoketests/src/test/resources/org/apache/isis/testdomain/bootstrapping/builtin-singleton.list
@@ -47,7 +47,7 @@ org.apache.isis.metamodel.specloader.MetaModelValidatorServiceDefault
 org.apache.isis.metamodel.specloader.ProgrammingModelServiceDefault
 org.apache.isis.metamodel.specloader.SpecificationLoaderDefault
 org.apache.isis.runtime.services.authsess.AuthenticationSessionProviderDefault
-org.apache.isis.runtime.services.background.BackgroundServiceDefault
+#org.apache.isis.runtime.services.background.BackgroundServiceDefault
 org.apache.isis.runtime.services.background.CommandExecutorServiceDefault
 org.apache.isis.runtime.services.bookmarks.BookmarkServiceInternalDefault
 org.apache.isis.runtime.services.command.CommandDtoServiceInternalDefault