0

I have a page (myView.xhtml) from where I start an async REST Call and inform the user with a message, that the async operation has been started. The async operation is a long running task which probably runs several minutes on the backend. I do it async because I don't want to block the view for the user until completion. So the user is allowed to do other actions on other pages (myOtherView.xhtml), while the async operation runs on the backend.

BUT, when the operation has been done, I want to inform the user about that with a message.

Problem now is, that in MyCallback#completed there is no FacesContext available! So how can I inform the user via messages when the async operation has been finished?!

Thanks for any hint!

myView.xhtml

<p:menuItem id="doAsync" value="Do async operation" action="#{myView.doAsync}" update=":messages"/>
</ui:composition>

myOtherView.xhtml

<ui:composition template="/WEB-INF/template.xhtml">
</ui:composition>

template.xhtml

<p:messages id="messages" />

MyView.java

@Named
@ViewScoced
public class MyView implements Serializable {

    @Inject
    MyCallback callback;
    
    public void doAsync() {
        // start async operation
        getClient().target("myuri")
                .path("doAsync")
                .request()
                .async()
                .post(Entity.json(params), callback);
        // inform user that async operation has been started
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "INFO", "Async started"));
    }
}

MyCallback.java

public class MyCallback implements InvocationCallback<Result>, Serializable {
    @Override
    public void completed(Result result) {
        // create message that async done
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "INFO", "Async completed"));
    }

    @Override
    public void failed(Throwable throwable) {
        // create message that async failed
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "ERROR", "Async failed"));
    }
}

MyResource.java

//
// REST EndPoint
//
public class MyResource {
    @Resource
    ManagedExecutorService executor;
    
    @POST
    @Path("doAsync")
    public void doAsync(@Suspended final AsyncResponse asyncResponse) {
        asyncResponse.register((CompletionCallback) throwable -> {
            if (throwable == null) {
                // no throwable - the processing ended successfully
                // (response already written to the client)
                logger.info("CompletionCallback completed");
            }
            else {
                logger.info("CompletionCallback failed");
                logger.catching(throwable);
            }
        });
        
        executor.execute(() -> {
            final var result = doAsync();
            asyncResponse.resume(result);
        });

    }
}
raho
  • 129
  • 4
  • 18
  • You want to use a websocket to do so. See for example https://stackoverflow.com/questions/25947790/real-time-updates-from-database-using-jsf-java-ee – Jasper de Vries Mar 17 '23 at 10:17
  • I'll give it a try with WebSockets, but I want only inform the user who initiated the async operation. In your example ALL active sessions receive a notification. – raho Mar 21 '23 at 14:35

0 Answers0