This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch v4
in repository https://gitbox.apache.org/repos/asf/causeway.git
The following commit(s) were added to refs/heads/v4 by this push:
new 0feaec87457 CAUSEWAY-3901: bit of refactoring; unable to fix download
issue with long running blob action
0feaec87457 is described below
commit 0feaec8745728a0e5aa78f2a2e17d34374a48dd2
Author: Andi Huber <[email protected]>
AuthorDate: Fri Aug 29 13:49:49 2025 +0200
CAUSEWAY-3901: bit of refactoring; unable to fix download issue with
long running blob action
---
.../components/widgets/actionlink/ActionLink.java | 5 +-
...dHandlerFactory.java => LobRequestHandler.java} | 71 +++++++++++-----------
.../causeway/viewer/wicket/ui/exec/Mediator.java | 35 +++++------
.../viewer/wicket/ui/exec/MediatorFactory.java | 10 +--
4 files changed, 53 insertions(+), 68 deletions(-)
diff --git
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/actionlink/ActionLink.java
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/actionlink/ActionLink.java
index 25a6d1b7229..1d09eba3ade 100644
---
a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/actionlink/ActionLink.java
+++
b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/components/widgets/actionlink/ActionLink.java
@@ -232,8 +232,9 @@ private void startDialogWithParams(final AjaxRequestTarget
target) {
castTo(ActionPromptWithExtraContent.class, actionPrompt)
.ifPresent(promptWithExtraContent->{
-
BSGridPanelFactory.extraContentForMixin(promptWithExtraContent.getExtraContentId(),
actionModel)
-
.ifPresent(gridPanel->promptWithExtraContent.setExtraContentPanel(gridPanel,
target));
+ BSGridPanelFactory
+
.extraContentForMixin(promptWithExtraContent.getExtraContentId(), actionModel)
+
.ifPresent(gridPanel->promptWithExtraContent.setExtraContentPanel(gridPanel,
target));
});
}
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/LobRequestHandler.java
similarity index 63%
rename from
viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/exec/DownloadHandlerFactory.java
rename to
viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/exec/LobRequestHandler.java
index 9eeca477cc1..134c1862487 100644
---
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/LobRequestHandler.java
@@ -21,8 +21,10 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.Serializable;
import java.time.Duration;
+import org.apache.wicket.request.IRequestCycle;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.handler.resource.ResourceStreamRequestHandler;
import org.apache.wicket.request.resource.ContentDisposition;
@@ -30,32 +32,50 @@
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
import org.apache.wicket.util.resource.StringResourceStream;
+import org.jspecify.annotations.Nullable;
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.commons.internal.exceptions._Exceptions;
import org.apache.causeway.core.metamodel.spec.feature.ObjectAction;
-import lombok.experimental.UtilityClass;
+public record LobRequestHandler(
+ NamedWithMimeType lob,
+ /**
+ * Duration for which the resource will be cached by the browser.
+ * Set to Duration.ZERO to disable browser caching.
+ */
+ @Nullable Duration cacheDuration) implements IRequestHandler, Serializable
{
-@UtilityClass
-final class DownloadHandlerFactory {
-
- public IRequestHandler downloadHandler(
+ public static LobRequestHandler 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);
+ if(value instanceof NamedWithMimeType lob) {
+ return new LobRequestHandler(lob,
action.getSemantics().isIdempotentOrCachable()
+ ? null
+ : Duration.ZERO);
}
return null;
}
+ @Override
+ public void respond(IRequestCycle requestCycle) {
+ var handler = new ResourceStreamRequestHandler(
+ lob instanceof Blob blob
+ ? resourceStream(blob)
+ : lob instanceof Clob clob
+ ? resourceStream(clob)
+ : resourceStreamUnmatched(),
+ lob.name());
+ handler.setContentDisposition(ContentDisposition.ATTACHMENT);
+ handler.setCacheDuration(cacheDuration);
+ handler.respond(requestCycle);
+ }
+
// -- HELPER
- private IResourceStream resourceStreamFor(final Blob blob) {
+ private IResourceStream resourceStream(Blob blob) {
final IResourceStream resourceStream = new AbstractResourceStream() {
private static final long serialVersionUID = 1L;
@Override public InputStream getInputStream() throws
ResourceStreamNotFoundException {
@@ -70,35 +90,12 @@ private IResourceStream resourceStreamFor(final Blob blob) {
return resourceStream;
}
- private IResourceStream resourceStreamFor(final Clob clob) {
+ private IResourceStream resourceStream(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;
+ private IResourceStream resourceStreamUnmatched() {
+ throw _Exceptions.unmatchedCase(lob);
}
}
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 b708cd3f019..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,20 +125,24 @@ 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
- final IRequestHandler requestHandler = handler();
- if(requestHandler instanceof ResourceStreamRequestHandler
scheduledHandler) {
- var streamingBehavior = new
StreamAfterAjaxResponseBehavior(scheduledHandler);
+ if(requestHandler instanceof LobRequestHandler
lobRequestHandler) {
+ var streamingBehavior = new
StreamAfterAjaxResponseBehavior(lobRequestHandler);
ajaxTarget.getPage().add(streamingBehavior);
- scheduleJs(ajaxTarget,
javascriptFor_sameWindow(streamingBehavior.getCallbackUrl()), 100);
+
+ 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()
@@ -200,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: {