以异步方式触发CDI事件并等待所有消费者完成

时间:2014-07-11 12:27:06

标签: jsf cdi

是否可以异步触发NamedBean中的事件并等待所有消费者(观察者)完成?消费者(观察者)应该异步运行,但如果所有事件都已完成,则应该完成该事件。

e.g。我有3个Beans(1个Bean可以激活事件,2个可以观察事件)。

触发事件的Bean:

@Named
@Stateless
public class TestExecuter implements Serializable {

    @Inject
    @MyEvent
    private Event<QueryTO> myEvent;

    public void run() {
        QueryTO queryTO = new QueryTO(null, null);

        FilterQualifier[] qualifiers = new FilterQualifier[3];
        qualifiers[0] = new FilterQualifier(TestA.FILTER_ID);
        qualifiers[1] = new FilterQualifier(TestB.FILTER_ID);

        System.out.println("Fire event");
        myEvent.select(qualifiers).fire(queryTO);
        //wait till observers are finished
        System.out.println("Event finished");
    }
}

观察

的Bean 1
@Named
@Stateless
public class TestA implements Serializable {
    public static final String FILTER_ID = "TestA";

    public void generateFilterQueryEvent(
            @Observes @Filter(FILTER_ID) @MyEvent QueryTO queryTo) {
        System.out.println("TestA called");

        try {
            Thread.sleep(3000);
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
        }

        System.out.println("TestA finished");
    }
}

观察

的Bean 2
@Named
@Stateless
public class TestB implements Serializable {
    public static final String FILTER_ID = "TestB";

    public void generateFilterQueryEvent(
            @Observes @Filter(FILTER_ID) @MyEvent QueryTO queryTo) {
        System.out.println("TestB called");

        try {
            Thread.sleep(2000);
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
        }

        System.out.println("TestB finished");
    }
}

预期结果:两个bean同时执行代码,3秒后事件结束并且事件结束。目前它执行TestA,然后执行TestB,所以我必须等待5秒。

1 个答案:

答案 0 :(得分:1)

AFAIK it's not supported by CDI虽然您可以使用自定义AsyncEvent类来解决它,该类获取观察者并在单独的线程中调用它们。这是一个概念证明示例:

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.ObserverMethod;
import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Inject;

public class AsyncEvent<T> {

    private static final AnnotationLiteral<Any> ANY = 
        new AnnotationLiteral<Any>() {
    };

    @Inject
    private BeanManager beanManager;

    private ExecutorService executorService;

    public AsyncEvent() {
    }

    @PostConstruct
    public void init() {
        executorService = Executors.newFixedThreadPool(5);
    }

    @PreDestroy
    public void shutdown() {
        try {
            executorService.shutdown();
            final boolean terminated = executorService.awaitTermination(10, TimeUnit.SECONDS);
            if (!terminated) {
                throw new RuntimeException("awaitTermination timeout");
            }
        } catch (final InterruptedException ie) {
            throw new RuntimeException(ie);
        }
    }

    public void fire(final T event) {
        fire(event, ANY);
    }

    public void fire(final T event, final Annotation... qualifiers) {
        final Set<ObserverMethod<? super T>> observers = beanManager.resolveObserverMethods(event, qualifiers);
        final Set<Callable<Void>> tasks = createCallablesForObservers(event, observers);
        invokeAll(tasks);
    }

    private Set<Callable<Void>> createCallablesForObservers(final T event, 
            final Set<ObserverMethod<? super T>> observers) {
        final Set<Callable<Void>> tasks = new HashSet<Callable<Void>>();
        for (final ObserverMethod<? super T> observer: observers) {
            final Callable<Void> callable = createCallable(event, observer);
            tasks.add(callable);
        }
        return tasks;
    }

    private Callable<Void> createCallable(final T event, 
            final ObserverMethod<? super T> observer) {
        final Callable<Void> callable = new Callable<Void>() {
            @Override
            public Void call() {
                observer.notify(event);
                return null;
            }
        };
        return callable;
    }

    private void invokeAll(final Set<Callable<Void>> tasks) {
        try {
            executorService.invokeAll(tasks);
        } catch (final InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

用法:

private static final AnnotationLiteral<Updated> UPDATED = new AnnotationLiteral<Updated>() {
};

@Inject
private AsyncEvent<Document> event;

public void run() {
    System.out.println("Fire event");
    final Document document = new Document("test event");
    event.fire(document);
    event.fire(document, UPDATED);
    ...
}