47 votes

Est-il sûr de démarrer un nouveau thread dans un bean géré JSF?

Je ne pouvais pas trouver une réponse définitive à savoir si il est sûr de frayer les discussions au sein de la session de l'étendue du JSF géré les haricots. Le fil doit appeler les méthodes de l'EJB stateless exemple (qui a été dépendance à injection pour le managed bean).

Le fond est que nous avons un rapport qui prend du temps à générer. Cela a provoqué la requête HTTP à temps à cause des paramètres du serveur, nous ne pouvons pas changer. Donc l'idée est de démarrer un nouveau thread et laisser générer le rapport, et de stocker temporairement il. Dans l'intervalle, la JSF page affiche une barre de progression, les sondages le managed bean jusqu'à la génération est terminée, puis fait une seconde demande pour télécharger le rapport stocké. Cela semble fonctionner, mais je voudrais être sûr de ce que je fais n'est pas un hack.

53voto

David Blevins Points 10502

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.

48voto

BalusC Points 498232

La ponte des threads au sein d'une session d'étendue de managed bean n'est pas nécessairement un hack tant qu'il ne le travail que vous voulez. Mais le frai fils à sa propre doit être fait avec un soin extrême. Le code ne doit pas être écrit de cette manière qu'un utilisateur peut par exemple frayer un nombre illimité de fils par session et/ou que les fils continuent de fonctionner même après la session de la destruction. Il ferait exploser votre application tôt ou tard.

Le code doit être écrit de cette façon que vous pouvez vous assurer qu'un utilisateur peut, par exemple, ne jamais se reproduire plus d'un thread d'arrière-plan par session et que le fil est garanti d'être interrompue une fois la session de la destruction. Pour de multiples tâches au sein d'une session, vous avez besoin de la file d'attente des tâches.

Aussi, tous les threads doivent de préférence être servi par un fil commun de la piscine de sorte que vous pouvez mettre une limite sur le montant total de donné naissance à des threads au niveau de l'application. Le moyen serveur d'application Java EE offre gérée par le conteneur de pool de threads que vous pouvez utiliser via entre autres les EJB @Asynchronous et @Schedule. À conteneur indépendant, vous pouvez également utiliser le Java 1.5 est Util Simultanées ExecutorService et ScheduledExecutorService pour cette.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X