Découvrez EJB 3.1 @Asynchronous methods
. C'est exactement ce qu'ils sont pour.
Petit exemple qui utilise OpenEJB 4.0.0-Instantanés. Ici, nous avons un @Singleton
bean avec une méthode marquée @Asynchronous
. Chaque fois que la méthode est invoquée par quiconque, dans ce cas, votre JSF managed bean, il va retourner immédiatement indépendamment de combien de temps la méthode prend en fait.
@Singleton
public class JobProcessor {
@Asynchronous
@Lock(READ)
@AccessTimeout(-1)
public Future<String> addJob(String jobName) {
// Pretend this job takes a while
doSomeHeavyLifting();
// Return our result
return new AsyncResult<String>(jobName);
}
private void doSomeHeavyLifting() {
try {
Thread.sleep(SECONDS.toMillis(10));
} catch (InterruptedException e) {
Thread.interrupted();
throw new IllegalStateException(e);
}
}
}
Voici un petit cas de test qui invoque @Asynchronous
méthode plusieurs fois dans une rangée.
Chaque invocation retourne un Futur objet qui, pour l'essentiel commence à vide et auront plus tard sa valeur remplie par le récipient lorsque l'appel de la méthode se termine en réalité.
import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class JobProcessorTest extends TestCase {
public void test() throws Exception {
final Context context = EJBContainer.createEJBContainer().getContext();
final JobProcessor processor = (JobProcessor) context.lookup("java:global/async-methods/JobProcessor");
final long start = System.nanoTime();
// Queue up a bunch of work
final Future<String> red = processor.addJob("red");
final Future<String> orange = processor.addJob("orange");
final Future<String> yellow = processor.addJob("yellow");
final Future<String> green = processor.addJob("green");
final Future<String> blue = processor.addJob("blue");
final Future<String> violet = processor.addJob("violet");
// Wait for the result -- 1 minute worth of work
assertEquals("blue", blue.get());
assertEquals("orange", orange.get());
assertEquals("green", green.get());
assertEquals("red", red.get());
assertEquals("yellow", yellow.get());
assertEquals("violet", violet.get());
// How long did it take?
final long total = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start);
// Execution should be around 9 - 21 seconds
assertTrue("" + total, total > 9);
assertTrue("" + total, total < 21);
}
}
Exemple de code source
Sous les couvertures ce qui rend ce travail est la suivante:
- L'
JobProcessor
l'appelant voit n'est pas une instance de JobProcessor
. C'est plutôt une sous-classe ou de proxy qui a toutes les méthodes redéfinies. Les méthodes qui sont censés être asynchrones sont traitées différemment.
- Les appels à une méthode asynchrone simplement résultat en
Runnable
en cours de création qui encapsule la méthode et les paramètres que vous avez donné. Cet exécutable est donné à un Exécuteur testamentaire qui est tout simplement une file d'attente de travail attaché à un pool de threads.
- Après avoir ajouté les travaux de la file d'attente, le proxy version de la méthode renvoie une mise en œuvre de l'
Future
qui est lié à l' Runnable
qui est maintenant en attente dans la file d'attente.
- Lorsque l'
Runnable
enfin exécute la méthode sur le réel JobProcessor
de l'instance, il va prendre la valeur de retour et dans l' Future
de la rendre disponible à l'appelant.
Important de noter que l' AsyncResult
objet l' JobProcessor
renvoie n'est pas le même Future
objet de l'appelant est maintenant. Il aurait été soigné si, en réalité, JobProcessor
pourrait juste retour String
et le numéro de version de l' JobProcessor
pourrait revenir Future<String>
, mais nous n'avons vu aucun moyen de le faire sans ajouter plus de complexité. Si l' AsyncResult
est un simple objet wrapper. Le conteneur va tirer l' String
, mettez l' AsyncResult
loin, puis mettre la String
dans le réel Future
que l'appelant est maintenant.
Pour obtenir des progrès en cours de route, il suffit de passer un thread-safe objet comme AtomicInteger à l' @Asynchronous
méthode et avoir le code d'haricot mettre à jour périodiquement avec le pourcentage d'achèvement.