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

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


The following commit(s) were added to refs/heads/main by this push:
     new 8c9669e5981 CAUSEWAY-3896: AsyncProxy API and Javadoc polishing
8c9669e5981 is described below

commit 8c9669e5981a90a9588d9e587255de820535e7db
Author: Andi Huber <ahu...@apache.org>
AuthorDate: Sat Jun 28 07:39:45 2025 +0200

    CAUSEWAY-3896: AsyncProxy API and Javadoc polishing
---
 .../applib/services/wrapper/WrapperFactory.java    | 115 ++++++++++++---------
 .../causeway/commons/functional/TryFuture.java     |  58 +++++++++++
 .../wrapper/AsyncProxyInternal.java                |  31 ++----
 .../wrapper/WrapperFactoryDefault.java             |  13 ---
 .../BackgroundService_IntegTestAbstract.java       |  24 ++---
 .../jdo/publishing/PublishingTestFactoryJdo.java   |   9 +-
 .../jpa/publishing/PublishingTestFactoryJpa.java   |  14 +--
 .../integtests/WrapperFactory_async_IntegTest.java |  12 +--
 .../testdomain/interact/CommandArgumentTest.java   |   6 +-
 .../WrapperInteraction_Caching_IntegTest.java      |   9 +-
 10 files changed, 167 insertions(+), 124 deletions(-)

diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/services/wrapper/WrapperFactory.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/services/wrapper/WrapperFactory.java
index 1237f360695..21924f3e541 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/services/wrapper/WrapperFactory.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/services/wrapper/WrapperFactory.java
@@ -20,17 +20,19 @@
 
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
 
 import org.springframework.util.function.ThrowingConsumer;
 import org.springframework.util.function.ThrowingFunction;
 
 import org.apache.causeway.applib.exceptions.recoverable.InteractionException;
 import org.apache.causeway.applib.services.factory.FactoryService;
+import org.apache.causeway.applib.services.iactnlayer.InteractionContext;
+import org.apache.causeway.applib.services.repository.RepositoryService;
 import org.apache.causeway.applib.services.wrapper.control.AsyncControl;
 import org.apache.causeway.applib.services.wrapper.control.SyncControl;
 import org.apache.causeway.applib.services.wrapper.events.InteractionEvent;
 import 
org.apache.causeway.applib.services.wrapper.listeners.InteractionListener;
+import org.apache.causeway.commons.functional.TryFuture;
 
 /**
  * Provides the ability to 'wrap' a domain object such that it can
@@ -75,18 +77,30 @@
 public interface WrapperFactory {
 
     /**
+     * The result of an async proxy instantiation,
+     * that allows to submit an async invocation on the wrapped domain object.
+     *
+     * <p>The framework takes care, that an async invocation is scoped within 
both an interaction-context and a transaction.
+     * Further more, {@link TryFuture}'s success values are unwrapped and 
detached.
+     *
+     * <p>Terminology:
+     * <ul>
+     *   <li>interaction-context: who/how/when {@link InteractionContext}</li>
+     *   <li>unwrapped: plain object, not proxied {@link 
WrapperFactory#unwrap}</li>
+     *   <li>detached: object not attached to a persistence session 
(applicable to entities only)
+     *      {@link RepositoryService#detach(Object)}</li>
+     * </ul>
+     *
      * @since 3.4 {@index}
-     * @see CompletableFuture
+     * @see TryFuture
      */
     interface AsyncProxy<T> {
-        AsyncProxy<Void> thenAcceptAsync(ThrowingConsumer<? super T> action);
-        <U> AsyncProxy<U> thenApplyAsync(ThrowingFunction<? super T, ? extends 
U> fn);
-        AsyncProxy<T> orTimeout(long timeout, TimeUnit unit);
-        T join();
+        TryFuture<Void> acceptAsync(ThrowingConsumer<? super T> action);
+        <U> TryFuture<U> applyAsync(ThrowingFunction<? super T, ? extends U> 
fn);
     }
 
     /**
-     * Provides the &quot;wrapper&quot; of a domain object against which to 
invoke the action.
+     * Provides the'wrapper' of a domain object against which to invoke the 
action.
      *
      * <p>The provided {@link SyncControl} determines whether business rules 
are checked first, and conversely
      * whether the action is executed.  There are therefore three typical 
cases:
@@ -100,48 +114,49 @@ interface AsyncProxy<T> {
      *
      * <p>Otherwise, will do all the validations (raise exceptions as required
      * etc.), but doesn't modify the model.
+     *
+     * <p>Any exceptions will be propagated, not swallowed.
      */
-    <T> T wrap(T domainObject,
-               SyncControl syncControl);
+    <T> T wrap(T domainObject, SyncControl syncControl);
 
     /**
-     * A convenience overload for {@link #wrap(Object, SyncControl)},
-     * returning a wrapper to invoke the action synchronously, enforcing 
business rules.
-     * Any exceptions will be propagated, not swallowed.
+     * A convenience overload for {@link #wrap(Object, SyncControl)} with 
{@code SyncControl.defaults()}.
+     * @see #wrap(Object, SyncControl)
      */
-    <T> T wrap(T domainObject);
+    default <T> T wrap(T domainObject) {
+        return wrap(domainObject, SyncControl.defaults());
+    }
 
     /**
      * Provides the wrapper for a {@link FactoryService#mixin(Class, Object) 
mixin}, against which to invoke the action.
      *
-     * <p>
-     *     The provided {@link SyncControl} determines whether business rules 
are checked first, and conversely
-     *     whether the action is executed.  See {@link #wrap(Object, 
SyncControl)} for more details on this.
-     * </p>
+     * <p>The provided {@link SyncControl} determines whether business rules 
are checked first, and conversely
+     * whether the action is executed.
+     *
+     * <p>Any exceptions will be propagated, not swallowed.
+     * @see #wrap(Object, SyncControl)
      */
-    <T> T wrapMixin(Class<T> mixinClass, Object mixee,
-                    SyncControl syncControl);
+    <T> T wrapMixin(Class<T> mixinClass, Object mixee, SyncControl 
syncControl);
+
+    /**
+     * A convenience overload for {@link #wrapMixin(Class, Object, 
SyncControl)} with {@code SyncControl.defaults()}.
+     * @see #wrapMixin(Class, Object, SyncControl)
+     */
+    default <T> T wrapMixin(Class<T> mixinClass, Object mixee) {
+        return wrapMixin(mixinClass, mixee, SyncControl.defaults());
+    }
 
     /**
      * Provides the wrapper for a {@link Mixin typesafe} {@link 
FactoryService#mixin(Class, Object) mixin}, against which to invoke the action.
      *
-     * <p>
-     *     The provided {@link SyncControl} determines whether business rules 
are checked first, and conversely
+     * <p>The provided {@link SyncControl} determines whether business rules 
are checked first, and conversely
      *     whether the action is executed.  See {@link #wrap(Object, 
SyncControl)} for more details on this.
-     * </p>
      */
     default <T extends Mixin<MIXEE>, MIXEE> T wrapMixinT(Class<T> mixinClass, 
MIXEE mixee,
                     SyncControl syncControl) {
         return wrapMixin(mixinClass, mixee, syncControl);
     }
 
-    /**
-     * A convenience overload for {@link #wrapMixin(Class, Object, 
SyncControl)},
-     * returning a wrapper to invoke the action synchronously, enforcing 
business rules.
-     * Any exceptions will be propagated, not swallowed.
-     */
-    <T> T wrapMixin(Class<T> mixinClass, Object mixee);
-
     /**
      * A convenience overload for {@link #wrapMixinT(Class, Object, 
SyncControl)},
      * returning a wrapper to invoke the action synchronously, enforcing 
business rules.
@@ -154,8 +169,7 @@ default <T extends Mixin<MIXEE>, MIXEE> T 
wrapMixinT(Class<T> mixinClass, MIXEE
     /**
      * Obtains the underlying domain object, if wrapped.
      *
-     * <p>
-     * If the object {@link #isWrapper(Object) is not wrapped}, then
+     * <p>If the object {@link #isWrapper(Object) is not wrapped}, then
      * should just return the object back unchanged.
      */
     <T> T unwrap(T possibleWrappedDomainObject);
@@ -163,47 +177,54 @@ default <T extends Mixin<MIXEE>, MIXEE> T 
wrapMixinT(Class<T> mixinClass, MIXEE
     /**
      * Whether the supplied object is a wrapper around a domain object.
      *
-     * @param <T>
      * @param possibleWrappedDomainObject
      *            - object that might or might not be a wrapper.
      */
     <T> boolean isWrapper(T possibleWrappedDomainObject);
 
-    //
     // -- ASYNC WRAPPING
-    //
 
     /**
      * Returns a {@link CompletableFuture} holding a proxy object for the 
provided {@code domainObject},
      * through which one can execute the action asynchronously (in another 
thread).
      *
      * @param <T> - the type of the domain object
-     * @param domainObject
-     * @param asyncControl
      *
      * @since 3.4
      */
     <T> AsyncProxy<T> asyncWrap(T domainObject, AsyncControl asyncControl);
 
     /**
-     * Returns a {@link CompletableFuture} holding a proxy object for the 
provided {@code mixinClass},
+     * A convenience overload for {@link #asyncWrap(Object, AsyncControl)} 
with {@code AsyncControl.defaults()}.
+     * @see #asyncWrap(Object, AsyncControl)
+     *
+     * @since 3.4
+     */
+    default <T> AsyncProxy<T> asyncWrap(T domainObject) {
+        return asyncWrap(domainObject, AsyncControl.defaults());
+    }
+
+    /**
+     * Returns a {@link AsyncProxy} holding a proxy object for the provided 
{@code mixinClass},
      * through which one can execute the action asynchronously (in another 
thread).
      *
      * @param <T> - the type of the mixin
-     * @param mixinClass
-     * @param mixee
-     * @param asyncControl
      *
      * @since 3.4
      */
-    <T> AsyncProxy<T> asyncWrapMixin(
-                   Class<T> mixinClass,
-                   Object mixee,
-                   AsyncControl asyncControl);
+    <T> AsyncProxy<T> asyncWrapMixin(Class<T> mixinClass, Object mixee, 
AsyncControl asyncControl);
+
+    /**
+     * A convenience overload for {@link #asyncWrapMixin(Class, Object, 
AsyncControl)} with {@code AsyncControl.defaults()}.
+     * @see #asyncWrapMixin(Class, Object, AsyncControl)
+     *
+     * @since 3.4
+     */
+    default <T> AsyncProxy<T> asyncWrapMixin(Class<T> mixinClass, Object 
mixee) {
+        return asyncWrapMixin(mixinClass, mixee, AsyncControl.defaults());
+    }
 
-    //
     // -- INTERACTION EVENT HANDLING
-    //
 
     /**
      * All {@link InteractionListener}s that have been registered using
@@ -239,4 +260,6 @@ boolean removeInteractionListener(
                     InteractionListener listener);
 
     void notifyListeners(InteractionEvent ev);
+
+
 }
diff --git 
a/commons/src/main/java/org/apache/causeway/commons/functional/TryFuture.java 
b/commons/src/main/java/org/apache/causeway/commons/functional/TryFuture.java
new file mode 100644
index 00000000000..9635d33b2e7
--- /dev/null
+++ 
b/commons/src/main/java/org/apache/causeway/commons/functional/TryFuture.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.commons.functional;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import lombok.NonNull;
+
+/**
+ * Wraps a {@link Future} and catches any exceptions within a {@link Try},
+ * such that no exceptions escape the future's {@code get(..)} methods.
+ *
+ * @param <T> The result type returned by this TryFuture's {@code tryGet(..)} 
methods
+ * @see Future
+ * @see Try
+ * @since 3.4
+ */
+public record TryFuture<T>(@NonNull Future<T> future) {
+
+    /**
+     * @throws RejectedExecutionException if the task cannot be
+     *         scheduled for execution
+     * @throws NullPointerException if the task or executor is null
+     */
+    public TryFuture(@NonNull Callable<T> task, @NonNull ExecutorService 
executor) {
+        this(executor.submit(task));
+    }
+
+    public Try<T> tryGet() {
+        return Try.call(future::get);
+    }
+
+    public Try<T> tryGet(long timeout, TimeUnit unit) {
+        return Try.call(()->future.get(timeout, unit));
+    }
+
+}
+
diff --git 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/AsyncProxyInternal.java
 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/AsyncProxyInternal.java
index e475eb4dccb..84c5439c0fb 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/AsyncProxyInternal.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/AsyncProxyInternal.java
@@ -18,37 +18,28 @@
  */
 package org.apache.causeway.core.runtimeservices.wrapper;
 
-import java.util.concurrent.Callable;
 import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
 
 import org.springframework.util.function.ThrowingConsumer;
 import org.springframework.util.function.ThrowingFunction;
 
 import org.apache.causeway.applib.services.wrapper.WrapperFactory.AsyncProxy;
+import org.apache.causeway.commons.functional.TryFuture;
 
-import lombok.SneakyThrows;
-
-//TODO this is just a proof of concept; chaining makes non sense once future 
no longer holds a proxy
 record AsyncProxyInternal<T>(
         Future<T> future,
         AsyncExecutor executor) implements AsyncProxy<T> {
 
-    @Override public AsyncProxy<Void> thenAcceptAsync(ThrowingConsumer<? super 
T> action) {
-        return thenApplyAsync(adapt(action));
-    }
-
-    @Override public <U> AsyncProxy<U> thenApplyAsync(ThrowingFunction<? super 
T, ? extends U> fn) {
-        return map(()->fn.apply(future.get()));
+    @Override
+    public TryFuture<Void> acceptAsync(
+            ThrowingConsumer<? super T> action) {
+        return applyAsync(adapt(action));
     }
 
-    @Override public AsyncProxy<T> orTimeout(long timeout, TimeUnit unit) {
-        return map(()->future.get(timeout, unit));
-    }
-
-    @SneakyThrows
-    @Override public T join() {
-        return future.get();
+    @Override
+    public <U> TryFuture<U> applyAsync(
+            ThrowingFunction<? super T, ? extends U> fn) {
+        return new TryFuture<>(()->fn.apply(future.get()), executor);
     }
 
     // -- HELPER
@@ -58,8 +49,4 @@ private ThrowingFunction<? super T, Void> 
adapt(ThrowingConsumer<? super T> acti
         return t->{action.accept(t); return (Void)null; };
     }
 
-    private <U> AsyncProxy<U> map(Callable<U> callable) {
-        return new AsyncProxyInternal<>(executor.submit(callable), executor);
-    }
-
 }
\ No newline at end of file
diff --git 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefault.java
 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefault.java
index 0fcb27685b0..fa90285387f 100644
--- 
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefault.java
+++ 
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefault.java
@@ -149,12 +149,6 @@ public void close() {
 
     // -- WRAPPING
 
-    @Override
-    public <T> T wrap(
-            final @NonNull T domainObject) {
-        return wrap(domainObject, SyncControl.defaults());
-    }
-
     @Override
     public <T> T wrap(
             final @NonNull T domainObject,
@@ -169,13 +163,6 @@ public <T> T wrap(
         return createProxy(domainObject, syncControl);
     }
 
-    @Override
-    public <T> T wrapMixin(
-            final @NonNull Class<T> mixinClass,
-            final @NonNull Object mixee) {
-        return wrapMixin(mixinClass, mixee, SyncControl.defaults());
-    }
-
     @Override
     public <T> T wrapMixin(
             final @NonNull Class<T> mixinClass,
diff --git 
a/extensions/core/commandlog/applib/src/test/java/org/apache/causeway/extensions/commandlog/applib/integtest/BackgroundService_IntegTestAbstract.java
 
b/extensions/core/commandlog/applib/src/test/java/org/apache/causeway/extensions/commandlog/applib/integtest/BackgroundService_IntegTestAbstract.java
index 7fbbf988dde..8193b62f7c2 100644
--- 
a/extensions/core/commandlog/applib/src/test/java/org/apache/causeway/extensions/commandlog/applib/integtest/BackgroundService_IntegTestAbstract.java
+++ 
b/extensions/core/commandlog/applib/src/test/java/org/apache/causeway/extensions/commandlog/applib/integtest/BackgroundService_IntegTestAbstract.java
@@ -40,7 +40,6 @@
 import org.apache.causeway.applib.services.iactnlayer.InteractionService;
 import org.apache.causeway.applib.services.wrapper.WrapperFactory;
 import org.apache.causeway.applib.services.wrapper.WrapperFactory.AsyncProxy;
-import org.apache.causeway.applib.services.wrapper.control.AsyncControl;
 import org.apache.causeway.applib.services.xactn.TransactionService;
 import org.apache.causeway.core.config.environment.CausewaySystemEnvironment;
 import org.apache.causeway.extensions.commandlog.applib.dom.BackgroundService;
@@ -115,28 +114,24 @@ void async_using_default_executor_service() {
         transactionService.runTransactional(Propagation.REQUIRES_NEW, () -> {
             var counter = bookmarkService.lookup(bookmark, 
Counter.class).orElseThrow();
 
-            var control = AsyncControl.defaults();
-
-            asyncProxyUnderTest1.set(wrapperFactory.asyncWrap(counter, 
control));
+            asyncProxyUnderTest1.set(wrapperFactory.asyncWrap(counter));
 
         }).ifFailureFail();
 
         // execute async and wait till done
         {
             asyncProxyUnderTest1.get()
-                .thenApplyAsync(Counter::bumpUsingDeclaredAction)
-                .orTimeout(5, TimeUnit.SECONDS)
-                .join(); // wait till done
+                .applyAsync(Counter::bumpUsingDeclaredAction)
+                .tryGet(5, TimeUnit.SECONDS); // wait till done
         }
 
         // then
         transactionService.runTransactional(Propagation.REQUIRES_NEW, () -> {
             var counter = bookmarkService.lookup(bookmark, 
Counter.class).orElseThrow();
             assertThat(counter.getNum()).isEqualTo(1L);
-            var control = AsyncControl.defaults();
 
             // store the async proxy for later use below
-            
asyncProxyUnderTest2.set(wrapperFactory.asyncWrapMixin(Counter_bumpUsingMixin.class,
 counter, control));
+            
asyncProxyUnderTest2.set(wrapperFactory.asyncWrapMixin(Counter_bumpUsingMixin.class,
 counter));
 
         }).ifFailureFail();
 
@@ -144,10 +139,10 @@ void async_using_default_executor_service() {
         {
             // returns the detached counter entity, so we can immediately 
check whether the action was executed
             var counter = asyncProxyUnderTest2.get()
-                    .thenApplyAsync(Counter_bumpUsingMixin::act)
+                    .applyAsync(Counter_bumpUsingMixin::act)
                     // let's wait max 5 sec to allow executor to complete 
before continuing
-                    .orTimeout(5, TimeUnit.SECONDS)
-                    .join(); // wait till done
+                    .tryGet(5, TimeUnit.SECONDS)
+                    .valueAsNonNullElseFail();
             assertThat(counter.getNum()).isEqualTo(2L);
         }
 
@@ -181,10 +176,9 @@ void using_background_service() {
         // execute async and wait till done
         {
             asyncProxyUnderTest.get()
-                .thenAcceptAsync(Counter::bumpUsingDeclaredAction)
+                .acceptAsync(Counter::bumpUsingDeclaredAction)
                 // let's wait max 5 sec to allow executor to complete before 
continuing
-                .orTimeout(5, TimeUnit.SECONDS)
-                .join(); // wait till done
+                .tryGet(5, TimeUnit.SECONDS); // wait till done
         }
 
         // then no change to the counter
diff --git 
a/regressiontests/base-jdo/src/main/java/org/apache/causeway/testdomain/jdo/publishing/PublishingTestFactoryJdo.java
 
b/regressiontests/base-jdo/src/main/java/org/apache/causeway/testdomain/jdo/publishing/PublishingTestFactoryJdo.java
index e48f9aa88ca..ed3d91b307a 100644
--- 
a/regressiontests/base-jdo/src/main/java/org/apache/causeway/testdomain/jdo/publishing/PublishingTestFactoryJdo.java
+++ 
b/regressiontests/base-jdo/src/main/java/org/apache/causeway/testdomain/jdo/publishing/PublishingTestFactoryJdo.java
@@ -365,12 +365,11 @@ protected void wrapperAsyncExecutionNoRules(
 
             // when - running asynchronous
             return wrapper.asyncWrap(book, asyncControl)
-                    .thenAcceptAsync(bk->bk.setName("Book #2"));
+                    .acceptAsync(bk->bk.setName("Book #2"));
         });
 
         future
-            .orTimeout(10, TimeUnit.SECONDS)
-            .join(); // wait till done
+            .tryGet(10, TimeUnit.SECONDS); // wait till done
     }
 
     @Override
@@ -383,13 +382,13 @@ protected void wrapperAsyncExecutionWithRules(
 
             context.runGiven();
 
-            // when - running synchronous
+            // when - running asynchronous
             var asyncControl = AsyncControl.defaults().withCheckRules(); // 
enforce rules
 
             //assertThrows(DisabledException.class, ()->{
                 // should fail with DisabledException (synchronous) within the 
calling Thread
                 wrapper.asyncWrap(book, asyncControl)
-                    .thenAcceptAsync(bk->bk.setName("Book #2"));
+                    .acceptAsync(bk->bk.setName("Book #2"));
 
             //});
 
diff --git 
a/regressiontests/base-jpa/src/main/java/org/apache/causeway/testdomain/jpa/publishing/PublishingTestFactoryJpa.java
 
b/regressiontests/base-jpa/src/main/java/org/apache/causeway/testdomain/jpa/publishing/PublishingTestFactoryJpa.java
index dfd7a5212c4..77805fae6c1 100644
--- 
a/regressiontests/base-jpa/src/main/java/org/apache/causeway/testdomain/jpa/publishing/PublishingTestFactoryJpa.java
+++ 
b/regressiontests/base-jpa/src/main/java/org/apache/causeway/testdomain/jpa/publishing/PublishingTestFactoryJpa.java
@@ -360,12 +360,11 @@ protected void wrapperAsyncExecutionNoRules(
 
             // when - running asynchronous
             return wrapper.asyncWrap(book, asyncControl)
-                    .thenAcceptAsync(bk->bk.setName("Book #2"));
+                    .acceptAsync(bk->bk.setName("Book #2"));
         });
 
         future
-            .orTimeout(10, TimeUnit.SECONDS)
-            .join(); // wait till done
+            .tryGet(10, TimeUnit.SECONDS); // wait till done
 
     }
 
@@ -375,16 +374,13 @@ protected void wrapperAsyncExecutionWithRules(
 
         context.bind(commitListener);
 
-        // when
+        // when enforce rules
         withBookDo(book->{
 
-            // when - running synchronous
-            var asyncControl = AsyncControl.defaults().withCheckRules(); // 
enforce rules
-
             //assertThrows(DisabledException.class, ()->{
                 // should fail with DisabledException (synchronous) within the 
calling Thread
-            wrapper.asyncWrap(book, asyncControl)
-                .thenAcceptAsync(bk->bk.setName("Book #2"));
+            wrapper.asyncWrap(book)
+                .acceptAsync(bk->bk.setName("Book #2"));
 
             //});
 
diff --git 
a/regressiontests/core-wrapperfactory/src/test/java/org/apache/causeway/regressiontests/core/wrapperfactory/integtests/WrapperFactory_async_IntegTest.java
 
b/regressiontests/core-wrapperfactory/src/test/java/org/apache/causeway/regressiontests/core/wrapperfactory/integtests/WrapperFactory_async_IntegTest.java
index 76df8059a4a..4284d616169 100644
--- 
a/regressiontests/core-wrapperfactory/src/test/java/org/apache/causeway/regressiontests/core/wrapperfactory/integtests/WrapperFactory_async_IntegTest.java
+++ 
b/regressiontests/core-wrapperfactory/src/test/java/org/apache/causeway/regressiontests/core/wrapperfactory/integtests/WrapperFactory_async_IntegTest.java
@@ -98,9 +98,9 @@ void async_using_default_executor_service(final String 
displayName, final Execut
         // execute async and wait till done
         {
             asyncProxyUnderTest1.get()
-                .thenApplyAsync(Counter::increment)
-                .orTimeout(5_000, TimeUnit.MILLISECONDS)
-                .join(); // let's wait max 5 sec to allow executor to complete 
before continuing
+                .applyAsync(Counter::increment)
+                // let's wait max 5 sec to allow executor to complete before 
continuing
+                .tryGet(5, TimeUnit.SECONDS);
         }
 
         // then
@@ -125,10 +125,10 @@ void async_using_default_executor_service(final String 
displayName, final Execut
         {
             // returns the detached counter entity, so we can immediately 
check whether the action was executed
             var counter = asyncProxyUnderTest2.get()
-                    .thenApplyAsync(Counter_bumpUsingMixin::act)
+                    .applyAsync(Counter_bumpUsingMixin::act)
                     // let's wait max 5 sec to allow executor to complete 
before continuing
-                    .orTimeout(5, TimeUnit.SECONDS)
-                    .join(); // wait till done
+                    .tryGet(5, TimeUnit.SECONDS)
+                    .valueAsNonNullElseFail();
             assertThat(counter.getNum()).isEqualTo(2L);
         }
 
diff --git 
a/regressiontests/interact/src/test/java/org/apache/causeway/testdomain/interact/CommandArgumentTest.java
 
b/regressiontests/interact/src/test/java/org/apache/causeway/testdomain/interact/CommandArgumentTest.java
index 516def8849b..ab1f1bfbbb9 100644
--- 
a/regressiontests/interact/src/test/java/org/apache/causeway/testdomain/interact/CommandArgumentTest.java
+++ 
b/regressiontests/interact/src/test/java/org/apache/causeway/testdomain/interact/CommandArgumentTest.java
@@ -125,9 +125,9 @@ void listParam_shouldAllowAsyncInvocation() throws 
InterruptedException, Executi
         var control = AsyncControl.defaults();
 
         var stringified = wrapperFactory.asyncWrap(commandArgDemo, control)
-            .thenApplyAsync(commandResult->commandResult.list(List.of(1L, 2L, 
3L)))
-            .orTimeout(3L, TimeUnit.DAYS)
-            .join() // wait till done
+            .applyAsync(commandResult->commandResult.list(List.of(1L, 2L, 3L)))
+            .tryGet(3L, TimeUnit.DAYS) // wait till done
+            .valueAsNonNullElseFail()
             .getResultAsString();
 
         assertEquals("[1, 2, 3]", stringified);
diff --git 
a/regressiontests/interact/src/test/java/org/apache/causeway/testdomain/interact/WrapperInteraction_Caching_IntegTest.java
 
b/regressiontests/interact/src/test/java/org/apache/causeway/testdomain/interact/WrapperInteraction_Caching_IntegTest.java
index 53d1141fdc9..96b8e9c53ca 100644
--- 
a/regressiontests/interact/src/test/java/org/apache/causeway/testdomain/interact/WrapperInteraction_Caching_IntegTest.java
+++ 
b/regressiontests/interact/src/test/java/org/apache/causeway/testdomain/interact/WrapperInteraction_Caching_IntegTest.java
@@ -69,7 +69,6 @@ static class StatefulCalculator {
     @Action
     @RequiredArgsConstructor
     public static class StatefulCalculator_add {
-        @SuppressWarnings("unused")
         private final StatefulCalculator mixee;
         public Integer act(final int amount) {
             return mixee.inc(amount);
@@ -119,17 +118,17 @@ void async_wrapped() throws ExecutionException, 
InterruptedException, TimeoutExc
         // when
         var asyncControlForCalculator1 = AsyncControl.defaults();
         var asyncCalculator1 = wrapperFactory.asyncWrap(calculator1, 
asyncControlForCalculator1)
-                .thenApplyAsync(calc->calc.inc(12));
+                .applyAsync(calc->calc.inc(12));
 
         var asyncControlForCalculator2 = AsyncControl.defaults();
         var asyncCalculator2 = wrapperFactory.asyncWrap(calculator2, 
asyncControlForCalculator2)
-                .thenApplyAsync(calc->calc.inc(24));
+                .applyAsync(calc->calc.inc(24));
 
         // then
-        Assertions.assertThat(asyncCalculator1.orTimeout(10, 
TimeUnit.SECONDS).join().intValue()).isEqualTo(12);
+        Assertions.assertThat(asyncCalculator1.tryGet(10, 
TimeUnit.SECONDS).valueAsNonNullElseFail().intValue()).isEqualTo(12);
         Assertions.assertThat(calculator1.getTotal()).isEqualTo(12);
 
-        Assertions.assertThat(asyncCalculator2.orTimeout(10, 
TimeUnit.SECONDS).join().intValue()).isEqualTo(24);
+        Assertions.assertThat(asyncCalculator2.tryGet(10, 
TimeUnit.SECONDS).valueAsNonNullElseFail().intValue()).isEqualTo(24);
         Assertions.assertThat(calculator2.getTotal()).isEqualTo(24);
     }
 

Reply via email to