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 d27bc33a148 CAUSEWAY-3859: Java record refactoring (part 45)
d27bc33a148 is described below

commit d27bc33a148bdc82957e838229ded8cfb1e218bb
Author: Andi Huber <[email protected]>
AuthorDate: Tue Feb 25 21:10:58 2025 +0100

    CAUSEWAY-3859: Java record refactoring (part 45)
---
 .../ui/actionresponse/ActionResultResponse.java    |  97 ---------
 .../ActionResultResponseHandlingStrategy.java      | 180 ----------------
 ...oadHandler.java => DownloadHandlerFactory.java} |  29 +--
 .../ui/actionresponse/ExecutionResultHandler.java  |  41 ++++
 .../viewer/wicket/ui/actionresponse/Mediator.java  | 228 +++++++++++++++++++++
 .../{_ResponseUtil.java => MediatorFactory.java}   |  95 +++++----
 .../ui/actionresponse/PageRedirectRequest.java     |  18 +-
 .../wicket/ui/actionresponse/_RedirectHandler.java |  51 -----
 .../wicket/ui/panels/FormExecutorDefault.java      |  19 +-
 9 files changed, 348 insertions(+), 410 deletions(-)

diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/ActionResultResponse.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/ActionResultResponse.java
deleted file mode 100644
index 9f0917118c2..00000000000
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/ActionResultResponse.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.causeway.viewer.wicket.ui.actionresponse;
-
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.request.IRequestHandler;
-
-import org.jspecify.annotations.Nullable;
-
-import org.apache.causeway.applib.value.OpenUrlStrategy;
-import org.apache.causeway.core.metamodel.object.ManagedObject;
-import org.apache.causeway.viewer.wicket.model.models.ActionModel;
-import org.apache.causeway.viewer.wicket.ui.pages.obj.DomainObjectPage;
-
-import org.jspecify.annotations.NonNull;
-
-/**
- * The response to provide as a result of interpreting the response;
- * either to show a {@link #toPage(PageRedirectRequest) page}, or to
- * {@link #withHandler(IRequestHandler) redirect} to a
- * handler (eg a download).
- */
-public record ActionResultResponse(
-    ActionResultResponseHandlingStrategy handlingStrategy,
-    /**
-     * Populated only if {@link #handlingStrategy()}
-     * is {@link ActionResultResponseHandlingStrategy#SCHEDULE_HANDLER}
-     */
-    IRequestHandler handler,
-    /**
-     * Populated only if {@link #handlingStrategy()}
-     * is {@link ActionResultResponseHandlingStrategy#REDIRECT_TO_PAGE}
-     */
-    PageRedirectRequest<?> pageRedirect,
-    /**
-     * Populated only if {@link #handlingStrategy()} is
-     * either {@link 
ActionResultResponseHandlingStrategy#OPEN_URL_IN_NEW_BROWSER_WINDOW}
-     * or {@link 
ActionResultResponseHandlingStrategy#OPEN_URL_IN_SAME_BROWSER_WINDOW}
-     */
-    AjaxRequestTarget ajaxTarget,
-    /**
-     * Populated only if {@link #handlingStrategy()} is
-     * either {@link 
ActionResultResponseHandlingStrategy#OPEN_URL_IN_NEW_BROWSER_WINDOW}
-     * or {@link 
ActionResultResponseHandlingStrategy#OPEN_URL_IN_SAME_BROWSER_WINDOW}
-     */
-    String url) {
-
-    public static ActionResultResponse toDomainObjectPage(final @NonNull 
ManagedObject entityOrViewmodel) {
-        var pageRedirectRequest = PageRedirectRequest.forPageClassAndBookmark(
-                DomainObjectPage.class, 
entityOrViewmodel.refreshBookmark().orElseThrow());
-        return ActionResultResponse.toPage(pageRedirectRequest);
-    }
-
-    public static ActionResultResponse determineAndInterpretResult(
-            final ActionModel actionModel,
-            final @Nullable AjaxRequestTarget targetIfAny,
-            final @Nullable ManagedObject resultAdapter) {
-        return _ResponseUtil.determineAndInterpretResult(actionModel, 
targetIfAny, resultAdapter);
-    }
-
-    static ActionResultResponse withHandler(final IRequestHandler handler) {
-        return new ActionResultResponse(
-                ActionResultResponseHandlingStrategy.SCHEDULE_HANDLER, 
handler, null, null, null);
-    }
-
-    static ActionResultResponse toPage(final PageRedirectRequest<?> page) {
-        return new ActionResultResponse(
-                ActionResultResponseHandlingStrategy.REDIRECT_TO_PAGE, null, 
page, null, null);
-    }
-
-    static ActionResultResponse openUrlInBrowser(
-            final AjaxRequestTarget ajaxTarget,
-            final String url,
-            final @NonNull OpenUrlStrategy openUrlStrategy) {
-        return new ActionResultResponse(
-                openUrlStrategy.isNewWindow()
-                    ? 
ActionResultResponseHandlingStrategy.OPEN_URL_IN_NEW_BROWSER_WINDOW
-                    : 
ActionResultResponseHandlingStrategy.OPEN_URL_IN_SAME_BROWSER_WINDOW,
-                null, null, ajaxTarget, url);
-    }
-}
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/ActionResultResponseHandlingStrategy.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/ActionResultResponseHandlingStrategy.java
deleted file mode 100644
index 638f03a5d9b..00000000000
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/ActionResultResponseHandlingStrategy.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.causeway.viewer.wicket.ui.actionresponse;
-
-import java.time.Duration;
-
-import org.apache.wicket.Component;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.behavior.AbstractAjaxBehavior;
-import org.apache.wicket.request.IRequestHandler;
-import org.apache.wicket.request.Url;
-import org.apache.wicket.request.cycle.RequestCycle;
-import org.apache.wicket.request.handler.resource.ResourceStreamRequestHandler;
-import org.apache.wicket.request.resource.ContentDisposition;
-import org.apache.wicket.util.resource.IResourceStream;
-
-import org.apache.causeway.commons.internal.exceptions._Exceptions;
-import org.apache.causeway.core.metamodel.context.MetaModelContext;
-import 
org.apache.causeway.viewer.wicket.model.models.RedirectRequestHandlerWithOpenUrlStrategy;
-
-public enum ActionResultResponseHandlingStrategy {
-    REDIRECT_TO_PAGE {
-        @Override
-        public void handleResults(
-                final ActionResultResponse resultResponse) {
-
-            // force any changes in state etc to happen now prior to the 
redirect;
-            // in the case of an object being returned, this should cause our 
page mementos
-            // (eg EntityModel) to hold the correct state.  I hope.
-            
MetaModelContext.instance().ifPresent(mmc->mmc.getTransactionService().flushTransaction());
-
-            // "redirect-after-post"
-            resultResponse.pageRedirect().apply();
-        }
-    },
-    SCHEDULE_HANDLER {
-        @Override
-        public void handleResults(
-                final ActionResultResponse resultResponse) {
-
-            final RequestCycle requestCycle = RequestCycle.get();
-            var ajaxTarget = 
requestCycle.find(AjaxRequestTarget.class).orElse(null);
-
-            if (ajaxTarget == null) {
-                // non-Ajax request => just stream the Lob to the browser
-                // or if this is a no-arg action, there also will be no parent 
for the component
-                
requestCycle.scheduleRequestHandlerAfterCurrent(resultResponse.handler());
-            } else {
-                // otherwise,
-                // Ajax request => respond with a redirect to be able to 
stream the Lob to the client
-                final IRequestHandler requestHandler = 
resultResponse.handler();
-                if(requestHandler instanceof ResourceStreamRequestHandler 
scheduledHandler) {
-                    var streamingBehavior = new 
StreamAfterAjaxResponseBehavior(scheduledHandler);
-                    var page = ajaxTarget.getPage();
-                    page.add(streamingBehavior);
-                    CharSequence callbackUrl = 
streamingBehavior.getCallbackUrl();
-                    scheduleJs(ajaxTarget, 
javascriptFor_sameWindow(callbackUrl), 10);
-                } else if(requestHandler instanceof 
RedirectRequestHandlerWithOpenUrlStrategy redirectHandler) {
-
-                    final String url = redirectHandler.getRedirectUrl();
-                    final String fullUrl = expanded(requestCycle, url);
-
-                    if(redirectHandler.getOpenUrlStrategy().isNewWindow()) {
-                        scheduleJs(ajaxTarget, 
javascriptFor_newWindow(fullUrl), 100);
-                    } else {
-                        scheduleJs(ajaxTarget, 
javascriptFor_sameWindow(fullUrl), 100);
-                    }
-                } else {
-                    throw _Exceptions.unrecoverable(
-                            "no logic implemented to handle IRequestHandler of 
type %s",
-                            requestHandler.getClass().getName());
-                }
-
-            }
-        }
-    },
-    OPEN_URL_IN_NEW_BROWSER_WINDOW {
-        @Override
-        public void handleResults(
-                final ActionResultResponse resultResponse) {
-
-            final String url = resultResponse.url();
-            final RequestCycle requestCycle = RequestCycle.get();
-            final String fullUrl = expanded(requestCycle, url);
-
-            scheduleJs(resultResponse.ajaxTarget(), 
javascriptFor_newWindow(fullUrl), 100);
-        }
-    },
-    OPEN_URL_IN_SAME_BROWSER_WINDOW {
-        @Override
-        public void handleResults(
-                final ActionResultResponse resultResponse) {
-
-            final String url = resultResponse.url();
-            final RequestCycle requestCycle = RequestCycle.get();
-            final String fullUrl = expanded(requestCycle, url);
-
-            scheduleJs(resultResponse.ajaxTarget(), 
javascriptFor_sameWindow(fullUrl), 100);
-        }
-    };
-
-    public abstract void handleResults(
-            ActionResultResponse resultResponse);
-
-    /**
-     * @see #expanded(String)
-     */
-    public static String expanded(final RequestCycle requestCycle, final 
String url) {
-        String urlStr = expanded(url);
-        return requestCycle.getUrlRenderer().renderFullUrl(Url.parse(urlStr));
-    }
-
-    /**
-     * very simple template support, the idea being that 
"antiCache=${currentTimeMillis}"
-     * will be replaced automatically.
-     */
-    public static String expanded(String urlStr) {
-        if(urlStr.contains("antiCache=${currentTimeMillis}")) {
-            urlStr = urlStr.replace("antiCache=${currentTimeMillis}", 
"antiCache="+System.currentTimeMillis());
-        }
-        return urlStr;
-    }
-
-    private static String javascriptFor_newWindow(final CharSequence url) {
-        return 
"function(){Wicket.Event.publish(Causeway.Topic.OPEN_IN_NEW_TAB, '" + url + 
"');}";
-    }
-
-    private static String javascriptFor_sameWindow(final CharSequence url) {
-        return "\"window.location.href='" + url + "'\"";
-    }
-
-    private static void scheduleJs(final AjaxRequestTarget target, final 
String js, final int millis) {
-        // the timeout is needed to let Wicket release the channel
-        target.appendJavaScript(String.format("setTimeout(%s, %d);", js, 
millis));
-    }
-
-    /**
-     * A special Ajax behavior that is used to stream the contents of a Lob 
after
-     * an Ajax request.
-     */
-    private static class StreamAfterAjaxResponseBehavior extends 
AbstractAjaxBehavior {
-        private static final long serialVersionUID = 1L;
-
-        private final String fileName;
-        private final IResourceStream resourceStream;
-        private final Duration cacheDuration;
-
-        public StreamAfterAjaxResponseBehavior(final 
ResourceStreamRequestHandler scheduledHandler) {
-            this.fileName = scheduledHandler.getFileName();
-            this.resourceStream = scheduledHandler.getResourceStream();
-            this.cacheDuration = scheduledHandler.getCacheDuration();
-        }
-
-        @Override
-        public void onRequest() {
-            ResourceStreamRequestHandler handler = new 
ResourceStreamRequestHandler(resourceStream, fileName);
-            handler.setCacheDuration(cacheDuration);
-            handler.setContentDisposition(ContentDisposition.ATTACHMENT);
-            Component page = getComponent();
-            page.getRequestCycle().scheduleRequestHandlerAfterCurrent(handler);
-            page.remove(this);
-        }
-    }
-}
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/_DownloadHandler.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/DownloadHandlerFactory.java
similarity index 83%
rename from 
viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/_DownloadHandler.java
rename to 
viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/DownloadHandlerFactory.java
index dc45a292226..ba4aa7d6e7d 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/_DownloadHandler.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/DownloadHandlerFactory.java
@@ -39,17 +39,15 @@
 import lombok.experimental.UtilityClass;
 
 @UtilityClass
-final class _DownloadHandler {
+final class DownloadHandlerFactory {
 
     public IRequestHandler downloadHandler(
             final ObjectAction action,
             final Object value) {
-        if(value instanceof Clob) {
-            var clob = (Clob)value;
+        if(value instanceof Clob clob) {
             return handlerFor(action, resourceStreamFor(clob), clob);
         }
-        if(value instanceof Blob) {
-            var blob = (Blob)value;
+        if(value instanceof Blob blob) {
             return handlerFor(action, resourceStreamFor(blob), blob);
         }
         return null;
@@ -59,28 +57,21 @@ public IRequestHandler downloadHandler(
 
     private IResourceStream resourceStreamFor(final Blob blob) {
         final IResourceStream resourceStream = new AbstractResourceStream() {
-
             private static final long serialVersionUID = 1L;
-
-            @Override
-            public InputStream getInputStream() throws 
ResourceStreamNotFoundException {
-                return new ByteArrayInputStream(blob.getBytes());
+            @Override public InputStream getInputStream() throws 
ResourceStreamNotFoundException {
+                return new ByteArrayInputStream(blob.bytes());
             }
-
-            @Override
-            public String getContentType() {
-                return blob.getMimeType().toString();
+            @Override public String getContentType() {
+                return blob.mimeType().toString();
             }
-
-            @Override
-            public void close() throws IOException {
+            @Override public void close() throws IOException {
             }
         };
         return resourceStream;
     }
 
     private IResourceStream resourceStreamFor(final Clob clob) {
-        return new StringResourceStream(clob.getChars(), 
clob.getMimeType().toString());
+        return new StringResourceStream(clob.chars(), 
clob.mimeType().toString());
     }
 
     private IRequestHandler handlerFor(
@@ -88,7 +79,7 @@ private IRequestHandler handlerFor(
             final IResourceStream resourceStream,
             final NamedWithMimeType namedWithMimeType) {
         var handler =
-                new ResourceStreamRequestHandler(resourceStream, 
namedWithMimeType.getName());
+                new ResourceStreamRequestHandler(resourceStream, 
namedWithMimeType.name());
         handler.setContentDisposition(ContentDisposition.ATTACHMENT);
 
         //CAUSEWAY-1619, prevent clients from caching the response content
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/ExecutionResultHandler.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/ExecutionResultHandler.java
new file mode 100644
index 00000000000..84425641fa4
--- /dev/null
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/ExecutionResultHandler.java
@@ -0,0 +1,41 @@
+/*
+ *  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.viewer.wicket.ui.actionresponse;
+
+import org.apache.wicket.ajax.AjaxRequestTarget;
+
+import org.apache.causeway.commons.functional.Either;
+import org.apache.causeway.core.metamodel.object.ManagedObject;
+import org.apache.causeway.viewer.wicket.model.models.ActionModel;
+import org.apache.causeway.viewer.wicket.model.models.PropertyModel;
+
+public record ExecutionResultHandler(Either<ActionModel, PropertyModel> 
actionOrPropertyModel) {
+
+    public void handle(final AjaxRequestTarget ajaxTarget, final ManagedObject 
result) {
+        // triggers ManagedObject.getBookmarkRefreshed()
+        var mediator = actionOrPropertyModel.fold(
+                act->Mediator.determineAndInterpretResult(act, ajaxTarget, 
result),
+                prop->Mediator.toDomainObjectPage(result));
+
+        // redirect using associated strategy
+        // on property edit, triggers SQL update (on JPA)
+        mediator.handle();
+    }
+
+}
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/Mediator.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/Mediator.java
new file mode 100644
index 00000000000..3b8a9f908af
--- /dev/null
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/Mediator.java
@@ -0,0 +1,228 @@
+/*
+ *  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.viewer.wicket.ui.actionresponse;
+
+import java.time.Duration;
+
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.behavior.AbstractAjaxBehavior;
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.Url;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.handler.resource.ResourceStreamRequestHandler;
+import org.apache.wicket.request.resource.ContentDisposition;
+import org.apache.wicket.util.resource.IResourceStream;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
+
+import org.apache.causeway.applib.value.OpenUrlStrategy;
+import org.apache.causeway.commons.internal.exceptions._Exceptions;
+import org.apache.causeway.core.metamodel.context.MetaModelContext;
+import org.apache.causeway.core.metamodel.object.ManagedObject;
+import org.apache.causeway.viewer.wicket.model.models.ActionModel;
+import 
org.apache.causeway.viewer.wicket.model.models.RedirectRequestHandlerWithOpenUrlStrategy;
+import org.apache.causeway.viewer.wicket.ui.pages.obj.DomainObjectPage;
+
+/**
+ * {@link #handle()} handles the execution response using given {@link 
ExecutionResultHandlingStrategy}.
+ */
+record Mediator(
+    ExecutionResultHandlingStrategy handlingStrategy,
+    /**
+     * Populated only if {@link #handlingStrategy()}
+     * is {@link ExecutionResultHandlingStrategy#SCHEDULE_HANDLER}
+     */
+    IRequestHandler handler,
+    /**
+     * Populated only if {@link #handlingStrategy()}
+     * is {@link ExecutionResultHandlingStrategy#REDIRECT_TO_PAGE}
+     */
+    PageRedirectRequest<?> pageRedirect,
+    /**
+     * Populated only if {@link #handlingStrategy()} is
+     * either {@link 
ExecutionResultHandlingStrategy#OPEN_URL_IN_NEW_BROWSER_WINDOW}
+     * or {@link 
ExecutionResultHandlingStrategy#OPEN_URL_IN_SAME_BROWSER_WINDOW}
+     */
+    AjaxRequestTarget ajaxTarget,
+    /**
+     * Populated only if {@link #handlingStrategy()} is
+     * either {@link 
ExecutionResultHandlingStrategy#OPEN_URL_IN_NEW_BROWSER_WINDOW}
+     * or {@link 
ExecutionResultHandlingStrategy#OPEN_URL_IN_SAME_BROWSER_WINDOW}
+     */
+    String url) {
+
+    enum ExecutionResultHandlingStrategy {
+        REDIRECT_TO_PAGE,
+        OPEN_URL_IN_NEW_BROWSER_WINDOW,
+        OPEN_URL_IN_SAME_BROWSER_WINDOW,
+        SCHEDULE_HANDLER
+    }
+
+    static Mediator toDomainObjectPage(final @NonNull ManagedObject 
entityOrViewmodel) {
+        var pageRedirectRequest = PageRedirectRequest.forPageClassAndBookmark(
+                DomainObjectPage.class, 
entityOrViewmodel.refreshBookmark().orElseThrow());
+        return Mediator.toPage(pageRedirectRequest);
+    }
+
+    static Mediator determineAndInterpretResult(
+            final ActionModel actionModel,
+            final @Nullable AjaxRequestTarget targetIfAny,
+            final @Nullable ManagedObject resultAdapter) {
+        return MediatorFactory.determineAndInterpretResult(actionModel, 
targetIfAny, resultAdapter);
+    }
+
+    static Mediator withHandler(final IRequestHandler handler) {
+        return new Mediator(
+                ExecutionResultHandlingStrategy.SCHEDULE_HANDLER, handler, 
null, null, null);
+    }
+
+    static Mediator toPage(final PageRedirectRequest<?> page) {
+        return new Mediator(
+                ExecutionResultHandlingStrategy.REDIRECT_TO_PAGE, null, page, 
null, null);
+    }
+
+    static Mediator openUrlInBrowser(
+            final AjaxRequestTarget ajaxTarget,
+            final String url,
+            final @NonNull OpenUrlStrategy openUrlStrategy) {
+        return new Mediator(
+                openUrlStrategy.isNewWindow()
+                    ? 
ExecutionResultHandlingStrategy.OPEN_URL_IN_NEW_BROWSER_WINDOW
+                    : 
ExecutionResultHandlingStrategy.OPEN_URL_IN_SAME_BROWSER_WINDOW,
+                null, null, ajaxTarget, url);
+    }
+
+    void handle() {
+        switch(handlingStrategy()) {
+            case REDIRECT_TO_PAGE->{
+                // force any changes in state etc to happen now prior to the 
redirect;
+                // in the case of an object being returned, this should cause 
our page mementos
+                // (eg EntityModel) to hold the correct state.  I hope.
+                
MetaModelContext.instance().ifPresent(mmc->mmc.getTransactionService().flushTransaction());
+                // "redirect-after-post"
+                this.pageRedirect().apply();
+            }
+            case OPEN_URL_IN_NEW_BROWSER_WINDOW -> {
+                final String fullUrl = expanded(RequestCycle.get(), url());
+                scheduleJs(ajaxTarget(), javascriptFor_newWindow(fullUrl), 
100);
+            }
+            case OPEN_URL_IN_SAME_BROWSER_WINDOW -> {
+                final String fullUrl = expanded(RequestCycle.get(), url());
+                scheduleJs(ajaxTarget(), javascriptFor_sameWindow(fullUrl), 
100);
+            }
+            case SCHEDULE_HANDLER -> {
+                var requestCycle = RequestCycle.get();
+                var ajaxTarget = 
requestCycle.find(AjaxRequestTarget.class).orElse(null);
+
+                if (ajaxTarget == null) {
+                    // non-Ajax request => just stream the Lob to the browser
+                    // or if this is a no-arg action, there also will be no 
parent for the component
+                    requestCycle.scheduleRequestHandlerAfterCurrent(handler());
+                } else {
+                    // otherwise,
+                    // Ajax request => respond with a redirect to be able to 
stream the Lob to the client
+                    final IRequestHandler requestHandler = handler();
+                    if(requestHandler instanceof ResourceStreamRequestHandler 
scheduledHandler) {
+                        var streamingBehavior = new 
StreamAfterAjaxResponseBehavior(scheduledHandler);
+                        var page = ajaxTarget.getPage();
+                        page.add(streamingBehavior);
+                        CharSequence callbackUrl = 
streamingBehavior.getCallbackUrl();
+                        scheduleJs(ajaxTarget, 
javascriptFor_sameWindow(callbackUrl), 10);
+                    } else if(requestHandler instanceof 
RedirectRequestHandlerWithOpenUrlStrategy redirectHandler) {
+
+                        final String url = redirectHandler.getRedirectUrl();
+                        final String fullUrl = expanded(requestCycle, url);
+
+                        if(redirectHandler.getOpenUrlStrategy().isNewWindow()) 
{
+                            scheduleJs(ajaxTarget, 
javascriptFor_newWindow(fullUrl), 100);
+                        } else {
+                            scheduleJs(ajaxTarget, 
javascriptFor_sameWindow(fullUrl), 100);
+                        }
+                    } else {
+                        throw _Exceptions.unrecoverable(
+                                "no logic implemented to handle 
IRequestHandler of type %s",
+                                requestHandler.getClass().getName());
+                    }
+                }
+            }
+        }
+    }
+
+    // -- HELPER
+
+    /**
+     * @see #expanded(String)
+     */
+    private static String expanded(final RequestCycle requestCycle, final 
String url) {
+        String urlStr = expanded(url);
+        return requestCycle.getUrlRenderer().renderFullUrl(Url.parse(urlStr));
+    }
+
+    /**
+     * very simple template support, the idea being that 
"antiCache=${currentTimeMillis}"
+     * will be replaced automatically.
+     */
+    private static String expanded(String urlStr) {
+        if(urlStr.contains("antiCache=${currentTimeMillis}")) {
+            urlStr = urlStr.replace("antiCache=${currentTimeMillis}", 
"antiCache="+System.currentTimeMillis());
+        }
+        return urlStr;
+    }
+
+    private static String javascriptFor_newWindow(final CharSequence url) {
+        return 
"function(){Wicket.Event.publish(Causeway.Topic.OPEN_IN_NEW_TAB, '" + url + 
"');}";
+    }
+
+    private static String javascriptFor_sameWindow(final CharSequence url) {
+        return "\"window.location.href='" + url + "'\"";
+    }
+
+    private static void scheduleJs(final AjaxRequestTarget target, final 
String js, final int millis) {
+        // the timeout is needed to let Wicket release the channel
+        target.appendJavaScript(String.format("setTimeout(%s, %d);", js, 
millis));
+    }
+
+    /**
+     * A special Ajax behavior that is used to stream the contents of a Lob 
after
+     * an Ajax request.
+     */
+    private static class StreamAfterAjaxResponseBehavior extends 
AbstractAjaxBehavior {
+        private static final long serialVersionUID = 1L;
+
+        private final String fileName;
+        private final IResourceStream resourceStream;
+        private final Duration cacheDuration;
+
+        public StreamAfterAjaxResponseBehavior(final 
ResourceStreamRequestHandler scheduledHandler) {
+            this.fileName = scheduledHandler.getFileName();
+            this.resourceStream = scheduledHandler.getResourceStream();
+            this.cacheDuration = scheduledHandler.getCacheDuration();
+        }
+
+        @Override public void onRequest() {
+            var handler = new ResourceStreamRequestHandler(resourceStream, 
fileName);
+            handler.setCacheDuration(cacheDuration);
+            handler.setContentDisposition(ContentDisposition.ATTACHMENT);
+            var page = getComponent();
+            page.getRequestCycle().scheduleRequestHandlerAfterCurrent(handler);
+            page.remove(this);
+        }
+    }
+
+}
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/_ResponseUtil.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/MediatorFactory.java
similarity index 72%
rename from 
viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/_ResponseUtil.java
rename to 
viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/MediatorFactory.java
index 6d712192737..f04c241e95a 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/_ResponseUtil.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/MediatorFactory.java
@@ -24,7 +24,7 @@
 import org.apache.wicket.request.IRequestHandler;
 import org.apache.wicket.request.cycle.PageRequestHandlerTracker;
 import org.apache.wicket.request.cycle.RequestCycle;
-
+import org.jspecify.annotations.NonNull;
 import org.jspecify.annotations.Nullable;
 
 import org.apache.causeway.applib.value.LocalResourcePath;
@@ -33,28 +33,30 @@
 import org.apache.causeway.commons.internal.base._Casts;
 import org.apache.causeway.commons.internal.base._NullSafe;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
+import org.apache.causeway.core.config.viewer.web.WebAppContextPath;
 import org.apache.causeway.core.metamodel.context.MetaModelContext;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.object.PackedManagedObject;
 import org.apache.causeway.viewer.wicket.model.models.ActionModel;
 import org.apache.causeway.viewer.wicket.model.models.ActionResultModel;
 import 
org.apache.causeway.viewer.wicket.model.models.FormExecutor.ActionResultResponseType;
-import 
org.apache.causeway.viewer.wicket.model.models.coll.CollectionModelStandalone;
 import org.apache.causeway.viewer.wicket.model.models.PageType;
+import 
org.apache.causeway.viewer.wicket.model.models.RedirectRequestHandlerWithOpenUrlStrategy;
 import org.apache.causeway.viewer.wicket.model.models.ValueModel;
 import org.apache.causeway.viewer.wicket.model.models.VoidModel;
+import 
org.apache.causeway.viewer.wicket.model.models.coll.CollectionModelStandalone;
+import 
org.apache.causeway.viewer.wicket.ui.actionresponse.Mediator.ExecutionResultHandlingStrategy;
 import org.apache.causeway.viewer.wicket.ui.pages.PageClassRegistry;
 import 
org.apache.causeway.viewer.wicket.ui.pages.standalonecollection.StandaloneCollectionPage;
 import org.apache.causeway.viewer.wicket.ui.pages.value.ValuePage;
 import org.apache.causeway.viewer.wicket.ui.pages.voidreturn.VoidReturnPage;
 
-import org.jspecify.annotations.NonNull;
 import lombok.experimental.UtilityClass;
 
 @UtilityClass
-class _ResponseUtil {
+class MediatorFactory {
 
-    ActionResultResponse determineAndInterpretResult(
+    Mediator determineAndInterpretResult(
             final @NonNull ActionModel actionModel,
             final @Nullable AjaxRequestTarget ajaxTarget,
             final @Nullable ManagedObject resultAdapterIfAny) {
@@ -68,12 +70,12 @@ ActionResultResponse determineAndInterpretResult(
                 // identity op
                 case NONE -> response;
                 // force full page reload
-                case FORCE_STAY_ON_PAGE -> new ActionResultResponse(
-                    
ActionResultResponseHandlingStrategy.OPEN_URL_IN_SAME_BROWSER_WINDOW,
+                case FORCE_STAY_ON_PAGE -> new Mediator(
+                    
ExecutionResultHandlingStrategy.OPEN_URL_IN_SAME_BROWSER_WINDOW,
                     null, null, ajaxTarget, response.pageRedirect().toUrl()); 
// page redirect should point to current page
                 // open result page in new browser tab/win
-                case FORCE_NEW_BROWSER_WINDOW -> new ActionResultResponse(
-                    
ActionResultResponseHandlingStrategy.OPEN_URL_IN_NEW_BROWSER_WINDOW,
+                case FORCE_NEW_BROWSER_WINDOW -> new Mediator(
+                    
ExecutionResultHandlingStrategy.OPEN_URL_IN_NEW_BROWSER_WINDOW,
                     null, null, ajaxTarget, response.pageRedirect().toUrl()); 
// page redirect should point to action result
                 }
             : response;
@@ -81,7 +83,25 @@ ActionResultResponse determineAndInterpretResult(
 
     // -- HELPER
 
-    private ActionResultResponse actionResultResponse(
+    private IRequestHandler redirectHandler(
+            final Object value,
+            final @NonNull OpenUrlStrategy openUrlStrategy,
+            final @NonNull WebAppContextPath webAppContextPath) {
+
+        if(value instanceof java.net.URL) {
+            var url = (java.net.URL) value;
+            return new 
RedirectRequestHandlerWithOpenUrlStrategy(url.toString());
+        }
+        if(value instanceof LocalResourcePath) {
+            var localResourcePath = (LocalResourcePath) value;
+            return new RedirectRequestHandlerWithOpenUrlStrategy(
+                    
localResourcePath.getEffectivePath(webAppContextPath::prependContextPath),
+                    localResourcePath.openUrlStrategy());
+        }
+        return null;
+    }
+
+    private Mediator actionResultResponse(
             final @NonNull ActionModel actionModel,
             final @Nullable AjaxRequestTarget ajaxTarget,
             final @NonNull ActionResultModel actionResultModel) {
@@ -96,73 +116,73 @@ private ActionResultResponse actionResultResponse(
                     .forActionModel((PackedManagedObject)resultAdapter, 
actionModel);
             var pageRedirectRequest = PageRedirectRequest.forPage(
                     StandaloneCollectionPage.class, new 
StandaloneCollectionPage(collectionModel));
-            return ActionResultResponse.toPage(pageRedirectRequest);
+            return Mediator.toPage(pageRedirectRequest);
         }
         case OBJECT: {
             determineScalarAdapter(actionModel.getMetaModelContext(), 
resultAdapter); // intercepts collections
-            return ActionResultResponse.toDomainObjectPage(resultAdapter);
+            return Mediator.toDomainObjectPage(resultAdapter);
         }
         case SIGN_IN: {
             var signInPage = actionModel.getMetaModelContext()
                     .lookupServiceElseFail(PageClassRegistry.class)
                     .getPageClass(PageType.SIGN_IN);
             var pageRedirectRequest = 
PageRedirectRequest.forPageClass(signInPage);
-            return ActionResultResponse.toPage(pageRedirectRequest);
+            return Mediator.toPage(pageRedirectRequest);
         }
         case VALUE: {
             var valueModel = ValueModel.of(actionModel.getAction(), 
resultAdapter);
             valueModel.setActionHint(actionModel);
             var valuePage = new ValuePage(valueModel);
             var pageRedirectRequest = 
PageRedirectRequest.forPage(ValuePage.class, valuePage);
-            return ActionResultResponse.toPage(pageRedirectRequest);
+            return Mediator.toPage(pageRedirectRequest);
         }
         case VALUE_BLOB: {
             final Object value = resultAdapter.getPojo();
             IRequestHandler handler =
-                    _DownloadHandler.downloadHandler(actionModel.getAction(), 
value);
-            return ActionResultResponse.withHandler(handler);
+                    
DownloadHandlerFactory.downloadHandler(actionModel.getAction(), value);
+            return Mediator.withHandler(handler);
         }
         case VALUE_CLOB: {
             final Object value = resultAdapter.getPojo();
             IRequestHandler handler =
-                    _DownloadHandler.downloadHandler(actionModel.getAction(), 
value);
-            return ActionResultResponse.withHandler(handler);
+                    
DownloadHandlerFactory.downloadHandler(actionModel.getAction(), value);
+            return Mediator.withHandler(handler);
         }
         case VALUE_LOCALRESPATH_AJAX: {
             final LocalResourcePath localResPath = 
(LocalResourcePath)resultAdapter.getPojo();
             var webAppContextPath = 
actionModel.getMetaModelContext().getWebAppContextPath();
-            return ActionResultResponse
+            return Mediator
                     .openUrlInBrowser(ajaxTarget, 
localResPath.getEffectivePath(webAppContextPath::prependContextPath), 
localResPath.openUrlStrategy());
         }
         case VALUE_LOCALRESPATH_NOAJAX: {
             // open URL server-side redirect
             final LocalResourcePath localResPath = 
(LocalResourcePath)resultAdapter.getPojo();
             var webAppContextPath = 
actionModel.getMetaModelContext().getWebAppContextPath();
-            IRequestHandler handler = 
_RedirectHandler.redirectHandler(localResPath, localResPath.openUrlStrategy(), 
webAppContextPath);
-            return ActionResultResponse.withHandler(handler);
+            IRequestHandler handler = redirectHandler(localResPath, 
localResPath.openUrlStrategy(), webAppContextPath);
+            return Mediator.withHandler(handler);
         }
         case VALUE_URL_AJAX: {
             final URL url = (URL)resultAdapter.getPojo();
-            return ActionResultResponse
+            return Mediator
                     .openUrlInBrowser(ajaxTarget, url.toString(), 
OpenUrlStrategy.NEW_WINDOW); // default behavior
         }
         case VALUE_URL_NOAJAX: {
             // open URL server-side redirect
             final Object value = resultAdapter.getPojo();
             var webAppContextPath = 
actionModel.getMetaModelContext().getWebAppContextPath();
-            IRequestHandler handler = _RedirectHandler.redirectHandler(value, 
OpenUrlStrategy.NEW_WINDOW, webAppContextPath); // default behavior
-            return ActionResultResponse.withHandler(handler);
+            IRequestHandler handler = redirectHandler(value, 
OpenUrlStrategy.NEW_WINDOW, webAppContextPath); // default behavior
+            return Mediator.withHandler(handler);
         }
         case VOID_AS_EMPTY: {
             var pageRedirectRequest = PageRedirectRequest
                 .forPage(VoidReturnPage.class, new VoidReturnPage(new 
VoidModel(), actionModel.getFriendlyName()));
-            return ActionResultResponse.toPage(pageRedirectRequest);
+            return Mediator.toPage(pageRedirectRequest);
         }
         case RELOAD: {
             var currentPage = 
PageRequestHandlerTracker.getLastHandler(RequestCycle.get()).getPage();
             var pageClass = currentPage.getClass();
             var pageRedirectRequest = PageRedirectRequest.forPage(pageClass, 
_Casts.uncheckedCast(currentPage));
-            return ActionResultResponse.toPage(pageRedirectRequest);
+            return Mediator.toPage(pageRedirectRequest);
         }
         default:
             throw _Exceptions.unmatchedCase(responseType);
@@ -173,18 +193,17 @@ private ManagedObject determineScalarAdapter(
             final @NonNull MetaModelContext mmc,
             final @NonNull ManagedObject resultAdapter) {
 
-        if (resultAdapter.getSpecification().isSingular()) {
-            return resultAdapter;
-        } else {
-            // will only be a single element
-            final Object pojo = _NullSafe
-                    .streamAutodetect(resultAdapter.getPojo())
-                    .findFirst()
-                    .orElseThrow(_Exceptions::noSuchElement);
-
-            final var scalarAdapter = mmc.getObjectManager().adapt(pojo);
-            return scalarAdapter;
-        }
+        if (resultAdapter.getSpecification().isSingular()) return 
resultAdapter;
+
+        // will only be a single element
+        final Object pojo = _NullSafe
+                .streamAutodetect(resultAdapter.getPojo())
+                .findFirst()
+                .orElseThrow(_Exceptions::noSuchElement);
+
+        final var scalarAdapter = mmc.getObjectManager().adapt(pojo);
+        return scalarAdapter;
+
     }
 
 }
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/PageRedirectRequest.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/PageRedirectRequest.java
index 5c38f6b51c4..c27c54e078a 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/PageRedirectRequest.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/PageRedirectRequest.java
@@ -23,26 +23,24 @@
 import org.apache.wicket.request.component.IRequestablePage;
 import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
-
+import org.jspecify.annotations.NonNull;
 import org.jspecify.annotations.Nullable;
 
 import org.apache.causeway.applib.services.bookmark.Bookmark;
 import org.apache.causeway.viewer.wicket.model.util.PageParameterUtils;
 
-import org.jspecify.annotations.NonNull;
-
-public record PageRedirectRequest<T extends IRequestablePage>(
+record PageRedirectRequest<T extends IRequestablePage>(
     @NonNull Class<T> pageClass,
     @Nullable PageParameters pageParameters,
     @Nullable IRequestablePage pageInstance) {
 
-    public static <T extends IRequestablePage> PageRedirectRequest<T> 
forPageClass(
+    static <T extends IRequestablePage> PageRedirectRequest<T> forPageClass(
             final @NonNull Class<T> pageClass,
             final @NonNull PageParameters pageParameters) {
         return new PageRedirectRequest<>(pageClass, pageParameters, null);
     }
 
-    public static <T extends IRequestablePage> PageRedirectRequest 
forPageClassAndBookmark(
+    static <T extends IRequestablePage> PageRedirectRequest<T> 
forPageClassAndBookmark(
             final @NonNull Class<T> pageClass,
             final @NonNull Bookmark bookmark) {
         return forPageClass(
@@ -50,25 +48,25 @@ public static <T extends IRequestablePage> 
PageRedirectRequest forPageClassAndBo
                         
PageParameterUtils.createPageParametersForBookmark(bookmark));
     }
 
-    public static <T extends IRequestablePage> PageRedirectRequest<T> 
forPageClass(
+    static <T extends IRequestablePage> PageRedirectRequest<T> forPageClass(
             final @NonNull Class<T> pageClass) {
         return new PageRedirectRequest<>(pageClass, null, null);
     }
 
-    public static <T extends IRequestablePage> PageRedirectRequest<T> forPage(
+    static <T extends IRequestablePage> PageRedirectRequest<T> forPage(
             final @NonNull Class<T> pageClass,
             final @NonNull T pageInstance) {
         return new PageRedirectRequest<>(pageClass, null, pageInstance);
     }
 
-    public String toUrl() {
+    String toUrl() {
         var handler = new BookmarkablePageRequestHandler(
                 new PageProvider(pageClass, pageParameters));
         return RequestCycle.get().urlFor(handler)
             .toString();
     }
 
-    public void apply() {
+    void apply() {
         var requestCycle = RequestCycle.get();
         if(requestCycle==null) return;
         if(pageParameters!=null) {
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/_RedirectHandler.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/_RedirectHandler.java
deleted file mode 100644
index 9bb04f3d1d6..00000000000
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/actionresponse/_RedirectHandler.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.causeway.viewer.wicket.ui.actionresponse;
-
-import org.apache.wicket.request.IRequestHandler;
-
-import org.apache.causeway.applib.value.LocalResourcePath;
-import org.apache.causeway.applib.value.OpenUrlStrategy;
-import org.apache.causeway.core.config.viewer.web.WebAppContextPath;
-import 
org.apache.causeway.viewer.wicket.model.models.RedirectRequestHandlerWithOpenUrlStrategy;
-
-import org.jspecify.annotations.NonNull;
-import lombok.experimental.UtilityClass;
-
-@UtilityClass
-final class _RedirectHandler {
-
-    public IRequestHandler redirectHandler(
-            final Object value,
-            final @NonNull OpenUrlStrategy openUrlStrategy,
-            final @NonNull WebAppContextPath webAppContextPath) {
-
-        if(value instanceof java.net.URL) {
-            var url = (java.net.URL) value;
-            return new 
RedirectRequestHandlerWithOpenUrlStrategy(url.toString());
-        }
-        if(value instanceof LocalResourcePath) {
-            var localResourcePath = (LocalResourcePath) value;
-            return new RedirectRequestHandlerWithOpenUrlStrategy(
-                    
localResourcePath.getEffectivePath(webAppContextPath::prependContextPath),
-                    localResourcePath.openUrlStrategy());
-        }
-        return null;
-    }
-}
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/panels/FormExecutorDefault.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/panels/FormExecutorDefault.java
index 93a3c7ecbde..202c5287722 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/panels/FormExecutorDefault.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/panels/FormExecutorDefault.java
@@ -22,7 +22,7 @@
 
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.markup.html.form.Form;
-
+import org.jspecify.annotations.NonNull;
 import org.jspecify.annotations.Nullable;
 
 import org.apache.causeway.applib.services.exceprecog.Category;
@@ -34,9 +34,8 @@
 import org.apache.causeway.viewer.wicket.model.models.FormExecutorContext;
 import org.apache.causeway.viewer.wicket.model.models.HasCommonContext;
 import org.apache.causeway.viewer.wicket.model.models.PropertyModel;
-import 
org.apache.causeway.viewer.wicket.ui.actionresponse.ActionResultResponse;
+import 
org.apache.causeway.viewer.wicket.ui.actionresponse.ExecutionResultHandler;
 
-import org.jspecify.annotations.NonNull;
 import lombok.extern.log4j.Log4j2;
 
 @Log4j2
@@ -110,18 +109,8 @@ public FormExecutionOutcome executeAndProcessResults(
                 return 
FormExecutionOutcome.SUCCESS_IN_NESTED_CONTEXT_SO_STAY_ON_PAGE;
             }
 
-            //XXX triggers ManagedObject.getBookmarkRefreshed()
-            var resultResponse = actionOrPropertyModel.fold(
-                    act->ActionResultResponse
-                            .determineAndInterpretResult(act, ajaxTarget, 
resultAdapter),
-                    prop->ActionResultResponse
-                            .toDomainObjectPage(resultAdapter));
-
-            // redirect using associated strategy
-            // XXX note: on property edit, triggers SQL update (on JPA)
-            resultResponse
-                .handlingStrategy()
-                .handleResults(resultResponse);
+            new ExecutionResultHandler(actionOrPropertyModel)
+                .handle(ajaxTarget, resultAdapter);
 
             return FormExecutionOutcome.SUCCESS_AND_REDIRECED_TO_RESULT_PAGE; 
// success (valid args), allow redirect
 

Reply via email to