I think I misunderstand java ee asynchronous method invocation. I'm following the idea from this official tutorial. For void methods it's fine, but I want my asyncronous method to return a value:

@Asynchronous
public Future<String> processPayment(Order order) {
    ...
    String status = ...;
    return new AsyncResult<String>(status);
}

I have many such long running tasks in background, so I collect List<Future<String>> and loop through them to check if the task is completed. I do this check by

for(Future<String> future: listOfFutures) {
    if(future.isDone())
       // do something
}

But the code fails with IllegalStateException:"Object does not represent an acutal Future" when the method AsyncResult.isDone() gets called.

Well, then I read this and it made me very confused: official tutorial says "Use AsyncResult.isDone()" to check the status of asynchronous method whereas oficial javadoc says "Don't dare using AsyncResult.isDone()".

I know that I could use FutureTask<T> together with Callable<T>, but this is java se part.

Could you explain me if it is possible to use java ee asynchronous method invocation?

Thank you!

P.S. I'm using Glassfish 3.1.2 and EJB 3.1

有帮助吗?

解决方案

So, your question is "is it possible to use java ee asynchronous method invocation?" The answer is yes. And the 2 little bits of code in your question are correct. To fully diagnose what has gone wrong in your case, however, we would probably need to see more of the code.

A possibility is that you are not using the annotation "correctly". A super short code example of how is used can be read here: https://tomee.apache.org/examples-trunk/async-methods/README.html

The important information on that example is that your Asynchronous method has to be declared an Singleton or Stateless bean (let's call it container bean), and in a different bean (or Servlet, or POJO with access to the right JNDI context, like in the link above) that we can call the client you make a call to your container bean. So to be clear, your for loop is in the client. Then your AsyncResult will behave like you want it to.

If you read the javadoc you linked to carefully, it says:

The value specified in the constructor will be retrieved by the container and made available to the client.

and

none of its instance methods should be called by the application

This means is that the AsyncResult class is sort of a "dumb wrapper" to let you implement the interface easily, but is not supposed to be used "for real".

Please notice that using the ExecutorService is the way to perform asynchronous calls when you are not implementing a EJB, or are outside a EJB container. The fact that is working for you makes me thing that you have a direct reference to the container class, instead of via the @EJB annotation or by a JNDI lookup.

其他提示

If use Spring Framework, Use

org.springframework.scheduling.annotation.AsyncResult

instead of

javax.ejb.AsyncResult

@Holger pointed me to the right direction in some sense: my problem is the implementation of Future, AsyncResult in this case. It turned out to be rather interesting. I looked into the AsyncResult class code and that is what i saw:

public boolean cancel(boolean mayInterruptIfRunning)
{
  throw new IllegalStateException("Object does not represent an acutal Future");
}

public boolean isCancelled()
{
  throw new IllegalStateException("Object does not represent an acutal Future");
}

public boolean isDone()
{
  throw new IllegalStateException("Object does not represent an acutal Future");
}

So it explains why I got that exception all the time.

The workaround for me was to return java.util.concurrent.ExecutorService.submit(new MyCallable<MyClass>(myClassInstance)) as java.util.concurrent.Future<MyClass> implementation. After it worked as expected.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top