Reputation: 10868
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
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
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