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

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


The following commit(s) were added to refs/heads/v3 by this push:
     new cd9dd621788 CAUSEWAY-3958: [v3] backports action result mediation from 
v4
cd9dd621788 is described below

commit cd9dd62178826fbf6c5273ed77b87736ec0b680b
Author: andi-huber <[email protected]>
AuthorDate: Sun Jan 18 17:32:01 2026 +0100

    CAUSEWAY-3958: [v3] backports action result mediation from v4
---
 .../wicket/ui/exec/DownloadHandlerFactory.java     | 104 ---------------------
 .../causeway/viewer/wicket/ui/exec/Mediator.java   |  72 ++++++--------
 .../viewer/wicket/ui/exec/MediatorFactory.java     |  10 +-
 3 files changed, 32 insertions(+), 154 deletions(-)

diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/exec/DownloadHandlerFactory.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/exec/DownloadHandlerFactory.java
deleted file mode 100644
index 9eeca477cc1..00000000000
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/exec/DownloadHandlerFactory.java
+++ /dev/null
@@ -1,104 +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.exec;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.time.Duration;
-
-import org.apache.wicket.request.IRequestHandler;
-import org.apache.wicket.request.handler.resource.ResourceStreamRequestHandler;
-import org.apache.wicket.request.resource.ContentDisposition;
-import org.apache.wicket.util.resource.AbstractResourceStream;
-import org.apache.wicket.util.resource.IResourceStream;
-import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
-import org.apache.wicket.util.resource.StringResourceStream;
-
-import org.apache.causeway.applib.value.Blob;
-import org.apache.causeway.applib.value.Clob;
-import org.apache.causeway.applib.value.NamedWithMimeType;
-import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
-
-import lombok.experimental.UtilityClass;
-
-@UtilityClass
-final class DownloadHandlerFactory {
-
-    public IRequestHandler downloadHandler(
-            final ObjectAction action,
-            final Object value) {
-        if(value instanceof Clob clob) {
-            return handlerFor(action, resourceStreamFor(clob), clob);
-        }
-        if(value instanceof Blob blob) {
-            return handlerFor(action, resourceStreamFor(blob), blob);
-        }
-        return null;
-    }
-
-    // -- HELPER
-
-    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.bytes());
-            }
-            @Override public String getContentType() {
-                return blob.mimeType().toString();
-            }
-            @Override public void close() throws IOException {
-            }
-        };
-        return resourceStream;
-    }
-
-    private IResourceStream resourceStreamFor(final Clob clob) {
-        return new StringResourceStream(clob.chars(), 
clob.mimeType().toString());
-    }
-
-    private IRequestHandler handlerFor(
-            final ObjectAction action,
-            final IResourceStream resourceStream,
-            final NamedWithMimeType namedWithMimeType) {
-        var handler =
-                new ResourceStreamRequestHandler(resourceStream, 
namedWithMimeType.name());
-        handler.setContentDisposition(ContentDisposition.ATTACHMENT);
-
-        //CAUSEWAY-1619, prevent clients from caching the response content
-        return action.getSemantics().isIdempotentOrCachable()
-                ? handler
-                : enforceNoCacheOnClientSide(handler);
-    }
-
-    // -- CLIENT SIDE CACHING ASPECTS ...
-
-    private static IRequestHandler enforceNoCacheOnClientSide(final 
IRequestHandler downloadHandler){
-        if(downloadHandler==null) {
-            return downloadHandler;
-        }
-        if(downloadHandler instanceof ResourceStreamRequestHandler)
-            ((ResourceStreamRequestHandler) downloadHandler)
-            .setCacheDuration(Duration.ZERO);
-
-        return downloadHandler;
-    }
-
-}
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/exec/Mediator.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/exec/Mediator.java
index 98b5225364b..428181c499a 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/exec/Mediator.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/exec/Mediator.java
@@ -18,21 +18,17 @@
  */
 package org.apache.causeway.viewer.wicket.ui.exec;
 
-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.commons.io.TextUtils;
 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;
@@ -129,36 +125,35 @@ void handle() {
             case SCHEDULE_HANDLER -> {
                 var requestCycle = RequestCycle.get();
                 var ajaxTarget = 
requestCycle.find(AjaxRequestTarget.class).orElse(null);
+                final IRequestHandler requestHandler = handler();
 
                 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());
+                    
requestCycle.scheduleRequestHandlerAfterCurrent(requestHandler);
+                    return;
+                }
+                // otherwise,
+                // Ajax request => respond with a redirect to be able to 
stream the Lob to the client
+                if(requestHandler instanceof LobRequestHandler 
lobRequestHandler) {
+                    var streamingBehavior = new 
StreamAfterAjaxResponseBehavior(lobRequestHandler);
+                    ajaxTarget.getPage().add(streamingBehavior);
+
+                    var relativeDownloadPageUri = 
TextUtils.cutter(streamingBehavior.getCallbackUrl().toString())
+                        .keepAfterLast("/")
+                        .getValue();
+                    scheduleJs(ajaxTarget, 
javascriptFor_sameWindow(relativeDownloadPageUri), 10);
+                } else if(requestHandler instanceof 
RedirectRequestHandlerWithOpenUrlStrategy redirectHandler) {
+                    var fullUrl = expanded(requestCycle, 
redirectHandler.getRedirectUrl());
+                    var js = redirectHandler.getOpenUrlStrategy().isNewWindow()
+                        ? javascriptFor_newWindow(fullUrl)
+                        : javascriptFor_sameWindow(fullUrl);
+
+                    scheduleJs(ajaxTarget, js, 100);
                 } 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());
-                    }
+                    throw _Exceptions.unrecoverable(
+                            "no logic implemented to handle IRequestHandler of 
type %s",
+                            requestHandler.getClass().getName());
                 }
             }
         }
@@ -195,7 +190,7 @@ private static String javascriptFor_sameWindow(final 
CharSequence 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));
+        target.appendJavaScript("setTimeout(%s, %d);".formatted(js, millis));
     }
 
     /**
@@ -205,22 +200,15 @@ private static void scheduleJs(final AjaxRequestTarget 
target, final String js,
     private static class StreamAfterAjaxResponseBehavior extends 
AbstractAjaxBehavior {
         private static final long serialVersionUID = 1L;
 
-        private final String fileName;
-        private final IResourceStream resourceStream;
-        private final Duration cacheDuration;
+        private final LobRequestHandler lobRequestHandler;
 
-        public StreamAfterAjaxResponseBehavior(final 
ResourceStreamRequestHandler scheduledHandler) {
-            this.fileName = scheduledHandler.getFileName();
-            this.resourceStream = scheduledHandler.getResourceStream();
-            this.cacheDuration = scheduledHandler.getCacheDuration();
+        StreamAfterAjaxResponseBehavior(final LobRequestHandler 
lobRequestHandler) {
+            this.lobRequestHandler = lobRequestHandler;
         }
 
         @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.getRequestCycle().scheduleRequestHandlerAfterCurrent(lobRequestHandler);
             page.remove(this);
         }
     }
diff --git 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/exec/MediatorFactory.java
 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/exec/MediatorFactory.java
index 71f1d451cbb..8de2add58a6 100644
--- 
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/exec/MediatorFactory.java
+++ 
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/exec/MediatorFactory.java
@@ -135,16 +135,10 @@ private Mediator actionResultResponse(
             var pageRedirectRequest = 
PageRedirectRequest.forPage(ValuePage.class, valuePage);
             return Mediator.toPage(pageRedirectRequest);
         }
-        case VALUE_BLOB: {
-            final Object value = resultAdapter.getPojo();
-            IRequestHandler handler =
-                    
DownloadHandlerFactory.downloadHandler(actionModel.getAction(), value);
-            return Mediator.withHandler(handler);
-        }
+        case VALUE_BLOB:
         case VALUE_CLOB: {
             final Object value = resultAdapter.getPojo();
-            IRequestHandler handler =
-                    
DownloadHandlerFactory.downloadHandler(actionModel.getAction(), value);
+            IRequestHandler handler = 
LobRequestHandler.downloadHandler(actionModel.getAction(), value);
             return Mediator.withHandler(handler);
         }
         case VALUE_LOCALRESPATH_AJAX: {

Reply via email to