No request is available when opening a view from @EventListener triggered by background task

Hi Jmix Team,

I want to open a dialog (CcrisprocessErrorScreen) from my main view (CcrisProcess) after a background task finishes. I am using an event (CcrisProgressEvent) to communicate back.

In the background task’s done() method, I publish the event:

uiEventPublisher..publishEventForCurrentUI(new CcrisProgressEvent(this, ...);

And I listen:

@EventListener
protected void onCcrisProgressEvent(CcrisProgressEvent event) {
    if (!event.getResult()) {
        userSession.setSynNaviContext(synNaviContext);
        viewBuilders.view(this, CcrisprocessErrorScreen.class)
                .withOpenMode(ViewOpenMode.DIALOG)
                .open();
    }
}

What happens
When the background task finishes and the event is published, I get the following stack trace:

java.lang.IllegalStateException: No request is available. This method can only be used with an active VaadinServletRequest
    at com.vaadin.flow.server.auth.AccessAnnotationChecker.hasAccess(...)
    at io.jmix.flowui.sys.UiAccessChecker.isViewPermitted(...)
    at ...

I expect that after the background task finishes, the event listener should be able to open the dialog in the main UI without throwing No request is available.

  • Is this the expected behavior because the background task is running outside of a Vaadin request context?
  • Is there an official Jmix-recommended pattern for opening a view from a background task completion handler?

Thank you.

Best Regard,
Chee Hao

Hi @chfoong

Is there a reason why you don’t use the done() method ?
https://docs.jmix.io/jmix/flow-ui/background-tasks.html#done

Best regards
Felix

Hi

yes. I used done() method to trigger the event.

    @Override
        public void done(Boolean result) {
            this.result = result;
            events.publishEventForCurrentUI(new CcrisProgressEvent(this, result, processCd, userSession.getUserId() + functionCode));
        }

but it always show this execption when using viewBuilders

java.lang.IllegalStateException: No request is available. This method can only be used with an active VaadinServletRequest
	at com.vaadin.flow.server.auth.AccessAnnotationChecker.hasAccess(AccessAnnotationChecker.java:92)
	at io.jmix.flowui.sys.UiAccessChecker.isViewPermitted(UiAccessChecker.java:81)
	at io.jmix.flowui.sys.UiAccessChecker.checkViewPermitted(UiAccessChecker.java:115)
	at io.jmix.tabbedmode.builder.AbstractViewBuilderProcessor.createView(AbstractViewBuilderProcessor.java:30)
	at io.jmix.tabbedmode.builder.ViewBuilderProcessor.build(ViewBuilderProcessor.java:24)
	at io.jmix.tabbedmode.builder.AbstractViewBuilder.build(AbstractViewBuilder.java:103)
	at io.jmix.tabbedmode.builder.AbstractViewBuilder.open(AbstractViewBuilder.java:109)
	at com.infopro.synergyloan.view.ccris.ccrisprocess.CcrisProcess.onCcrisProgressEvent(CcrisProcess.java:282)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at io.jmix.flowui.sys.event.UiEventListenerMethodAdapter.doInvoke(UiEventListenerMethodAdapter.java:229)
	at io.jmix.flowui.sys.event.UiEventListenerMethodAdapter.processEvent(UiEventListenerMethodAdapter.java:153)
	at io.jmix.flowui.sys.event.UiEventListenerMethodAdapter.onApplicationEvent(UiEventListenerMethodAdapter.java:112)
	at io.jmix.flowui.sys.event.UiEventsManager.invokeListener(UiEventsManager.java:160)
	at io.jmix.flowui.sys.event.UiEventsManager.publish(UiEventsManager.java:153)
	at io.jmix.flowui.UiEventPublisher.publish(UiEventPublisher.java:152)
	at io.jmix.flowui.UiEventPublisher.publishEventForCurrentUI(UiEventPublisher.java:137)
	at com.infopro.synergyloan.view.ccris.ccrisprocess.CcrisProcess$ValidateCcris.done(CcrisProcess.java:343)
	at com.infopro.synergyloan.view.ccris.ccrisprocess.CcrisProcess$ValidateCcris.done(CcrisProcess.java:317)
	at io.jmix.flowui.backgroundtask.LocalizedTaskWrapper.done(LocalizedTaskWrapper.java:158)
	at io.jmix.flowui.backgroundtask.impl.BackgroundWorkerImpl$TaskExecutorImpl.handleDone(BackgroundWorkerImpl.java:297)
	at io.jmix.flowui.backgroundtask.impl.BackgroundWorkerImpl$TaskExecutorImpl$1.lambda$done$f7fe4649$1(BackgroundWorkerImpl.java:194)
	at com.vaadin.flow.component.UI.accessSynchronously(UI.java:498)
	at com.vaadin.flow.component.UI$2.execute(UI.java:573)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
	at com.vaadin.flow.server.VaadinService.runPendingAccessTasks(VaadinService.java:2173)
	at com.vaadin.flow.server.VaadinSession.unlock(VaadinSession.java:755)
	at com.vaadin.flow.server.VaadinService.ensureAccessQueuePurged(VaadinService.java:2137)
	at com.vaadin.flow.server.VaadinService.accessSession(VaadinService.java:2104)
	at com.vaadin.flow.server.VaadinSession.access(VaadinSession.java:1059)
	at com.vaadin.flow.component.UI.access(UI.java:570)
	at com.vaadin.flow.component.UI.access(UI.java:553)
	at io.jmix.flowui.backgroundtask.impl.BackgroundWorkerImpl$TaskExecutorImpl$1.done(BackgroundWorkerImpl.java:193)
	at java.base/java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:434)
	at java.base/java.util.concurrent.FutureTask.set(FutureTask.java:285)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:325)
	at io.jmix.flowui.backgroundtask.impl.BackgroundWorkerImpl$TaskExecutorImpl.lambda$startExecution$0(BackgroundWorkerImpl.java:396)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
	at java.base/java.lang.Thread.run(Thread.java:1583)

Hello!

The done() method is invoked from the background task thread. Using the UI#access() method, it gets access to UI features, but the JavaDocs says:

Please note that the command might be invoked on a different thread or later on the current thread, which means that custom thread locals might not have the expected values when the command is executed. getCurrent(), VaadinSession.getCurrent() and VaadinService.getCurrent() are set according to this UI before executing the command. Other standard CurrentInstance values such as VaadinService.getCurrentRequest() and VaadinService.getCurrentResponse() will not be defined.

This is why the exception java.lang.IllegalStateException: No request is available is fired.

I experience similar problem in Jmix 2.6.0.

I cannot open a dialog using DialogWindows from the done() method of the BackgroundTask.
It fails with “java.lang.IllegalStateException: No request is available. This method can only be used with an active VaadinServletRequest”.

Looks like the UI isn’t fully functional inside of done() method. Isn’t it a bug?

        BackgroundTask<Integer, FooSendResult> sendTask = new BackgroundTask<>(60, TimeUnit.SECONDS, this) {
            @Override
            public FooSendResult run(TaskLifeCycle<Integer> taskLifeCycle) {
                return fooService.sendOrder(order.getId());
            }

            @Override
            public void done(FooSendResult result) {
                dialogWindows.view(FooView.this, OrderSendFailDialog.class)
                    .open();
            }
        };

        dialogs.createBackgroundTaskDialog(sendTask)
                .withHeader(messages.getMessage(getClass(), "..."))
                .open();

Found a kind of workaround:

@Override
     public void done(FooSendResult result) {
            VaadinServletRequest request = VaadinServletRequest.getCurrent();
            System.out.println("#1 Request is: " + request);

            // workaround for bug
            // you cannot open views directly from done() handler
            getElement().executeJs("").then(ignored -> {
                VaadinServletRequest request2 = VaadinServletRequest.getCurrent();
                System.out.println("#2 Request is: " + request2);

                dialogWindows.view(FooView.this, OrderSendFailDialog.class)
                    .open();
            });
       }
1 Like

Hi,

The JavaDocs for the done() method state that the request will not be available during its execution. It may not be obvious that opening dialog views requires a request. Here, we rely on Vaadin security role checks to verify that the view can be opened, which requires the request.

I’ve created an issue to discuss what we can do in this case: Dialog view cannot be opened from the done method of BackgroundTask · Issue #4664 · jmix-framework/jmix · GitHub.