Marinos An
Marinos An

Reputation: 10868

Associate async task's completion/progress monitor with session

I want to be able to perform an asynchronous task in java and be able to keep a completion (and if possible progress) monitor associated to the user's session. Is this possible, and if yes what is the way to do it?

Currently the task is implemented synchronously as a stateless session bean method, which is called from a jax-rs endpoint.

I looked at https://docs.oracle.com/javaee/7/tutorial/ejb-async001.htm but AsyncResult is not serializable so I guess I cannot add it to session.

Upvotes: 5

Views: 931

Answers (2)

Oleg Gritsak
Oleg Gritsak

Reputation: 622

JSF example, works in Wildfly:

1 inside in view (xhtml) we have an upload form and progress meter

<h:form>
    <div align="justify">
    <p:fileUpload style="width: auto" fileUploadListener="#{fileUploadCtrl.handleFileUpload}" mode="advanced" label="Please pick XLS file" update="messages" auto="true" sizeLimit="1000000" allowTypes="/(\.|\/)(xls|xlsx)$/" />
    <p:growl id="messages" showDetail="false" life="4000"/>
    </div>
</h:form>

 <h:form id="wholeform">
    <h:outputText id="statusot" value="#{fileUploadCtrl.message}" />
    <p:spacer width="10" height="10"/>
    <p:poll interval="1" listener="#{fileUploadCtrl.updateStatus}" update="wholeform" />
</h:form>

2 in controller, which is a managed bean, we process file and once a second update status

@ManagedBean
@ViewScoped

public class FileUploadCtrl {

@EJB
private SomeBusinessLogicClass model;

@EJB
private ProgressTracker progress;

private Future<List<String>> asyncResult;
private int progressId = 0;
private String message;
private boolean busy = false;

public void handleFileUpload(FileUploadEvent event) {

    Set<String> ids = model.populate(event.getFile().getContents());

    progressId = progress.newIndex();
    asyncResult = model.process(ids);
    busy = true;

    FacesMessage message = new FacesMessage("Loaded " + ids.size() + " objects", "");
    FacesContext.getCurrentInstance().addMessage(null, message);
}

public void updateStatus() {

    if (!busy)
        return;

    try {

        if (asyncResult.isDone()) {
            List<String> r = asyncResult.get();
            message = "Job done";
            busy = false;
            progress.delIndex(progressId);
        } else {
            message = progress.getIndex(progressId)+"-th element in work";
        }

    } catch (Exception e) {
        System.out.println("updateStatus " + e.toString());
    }
}

3 All business logic is in EJBs like SomeBusinessLogicClass or many others. Also we need a simple progress-manager EJB, I post it completely

@Singleton
public class ProgressTracker {

private Map<Integer,Integer> indexes = new HashMap<>();

public Map<Integer, Integer> getIndexes() {
    return indexes;
}

public void setIndexes(Map<Integer, Integer> indexes) {
    this.indexes = indexes;
}

public Integer newIndex() {
    Integer size = indexes.size();
    indexes.put(size,0);
    return size;
}

public void incIndex(final Integer index) {

    int old = indexes.get(index);
    old++;
    indexes.put(index,old);
}

public Integer getIndex(final Integer index) {
    return indexes.get(index);
}

public void delIndex(Integer index) {
    indexes.remove(index);
}

}

Maybe this example is not elegant, I'm almost newbie with frontends, but it is working and better, than nothing.

Upvotes: 0

Srinivas Lakshman
Srinivas Lakshman

Reputation: 489

Using the Spring annotation @Async, you can make any bean/method asynchronous. The container will create a new thread and method will be executed asynchronously. You can as well pass a session object into this method and upon completion, you can mark an attribute in the session object. Example:- https://spring.io/guides/gs/async-method/

Upvotes: 3

Related Questions