Доступ к CDI SessionScoped bean не работает в параллельном потоке Java 8
У меня возникли проблемы с пониманием, почему этот код не работает. В основном я хочу получить доступ к Бину CDI SessionScoped из Бина CDI ViewScoped во время функции parallelStream (), я получаю это исключение:
WELD-001303: No active contexts for scope type javax.enterprise.context.SessionScoped
Это работает в Wildfly 10.1.
Боб с видовой областью:
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
@ViewScoped
@Named
public class TestController implements Serializable {
private static final long serialVersionUID = 1L;
@Inject SessionController sessionController;
public void works() {
List<Function<String, String>> functions = new ArrayList<>();
functions.add((String input) -> {
return sessionController.getSomething();
});
functions.add((String input) -> {
return sessionController.getSomethingElse();
});
functions.stream().forEach(f -> f.apply("input"));
}
public void doesNotWork() {
List<Function<String, String>> functions = new ArrayList<>();
functions.add((String input) -> {
return sessionController.getSomething();
});
functions.add((String input) -> {
return sessionController.getSomethingElse();
});
functions.parallelStream().forEach(f -> f.apply("input"));
}
}
Фасоль SessionScoped:
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
@Named
@SessionScoped
public class SessionController implements Serializable {
private static final long serialVersionUID = 1L;
public String getSomething() {
return "something";
}
public String getSomethingElse() {
return "else";
}
}
XHTML:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head />
<h:body>
<h:form>
<p:commandButton value="Works" action="#{testController.works}" />
<br />
<p:commandButton value="Does Not Work" action="#{testController.doesNotWork}" />
</h:form>
</h:body>
</html>
Stacktrace:
Caused by: org.jboss.weld.context.ContextNotActiveException: WELD-001303: No active contexts for scope type javax.enterprise.context.SessionScoped
at org.jboss.weld.manager.BeanManagerImpl.getContext(BeanManagerImpl.java:689)
at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:90)
at org.jboss.weld.bean.ContextualInstanceStrategy$CachingContextualInstanceStrategy.getIfExists(ContextualInstanceStrategy.java:165)
at org.jboss.weld.bean.ContextualInstance.getIfExists(ContextualInstance.java:63)
at org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance(ContextBeanInstance.java:83)
at org.jboss.weld.bean.proxy.ProxyMethodHandler.getInstance(ProxyMethodHandler.java:125)
at com.SessionController$Proxy$_$$_WeldClientProxy.getSomething(Unknown Source)
at com.TestController.lambda$3(TestController.java:33)
at com.TestController.lambda$5(TestController.java:38)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:291)
at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
У нас есть теории, что сессия может быть нить конкретная, но никаких твердых доказательств.
Также любопытно, есть ли обходной путь. Реальный код намного сложнее этого, поэтому мы не можем предварительно загрузить результаты SessionController заранее, не теряя преимущества параллельного потока.
2 ответов:
Я думаю, что вы столкнулись с тем, что использование параллельных потоков означает, что вы будете работать в нескольких потоках. Теперь это проблема с CDI и контекстами, потому что вам потребуется распространение контекста - например, в главном потоке, у которого в данный момент есть (например) активный контекст сеанса, но когда вы создаете другой поток, он там не активен.
Глава 6.3 в спецификации описывает его более подробно, но чтобы дать вам короткую историю - распространение контекста на другие потоки не делает работа по умолчанию . И для этого есть веские причины - это было бы очень дорого (синхронизация), и вам нужно было бы решить множество очень странных ситуаций, таких как наличие одного из потоков сеанса недействительности, следовательно, деактивация контекста сеанса, в то время как другие потоки работают на нем. И еще много таких ситуаций.
Кроме того, для этого нет встроенного обходного пути. Что вы могли бы сделать, так это реализовать свою собственную область или расширить существующую область сеанса, но это было бы очень важно. наверное, это сложно.
Если вы хотите распараллелить запросы в Java EE, я предлагаю вам использовать ManagedExecutorService для этого. Пул Fork join, который используют параллельные потоки, предназначен для распараллеливания вычислений, а не ввода-вывода.
Когда дело доходит до распространения контекста CDI, вы можете найти BoundSessionContext полезным, предполагая, что вы используете WELD в качестве реализации CDI. Подробнее читайте в http://john-ament.blogspot.fi/2014/01/bridging-netty-resteasy-and-weld.html
Вероятно, вы можете обойтись без связанный контекст сеанса. Просто соберите все необходимые данные из контекста сеанса и передайте их рабочим потокам.
Comments