in viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/exec/Mediator.java [45:228]
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);
}
}
}