2017-04-06 51 views
3

我无法理解此代码无法工作的原因。基本上我想要一个parallelStream()函数期间访问从CDI ViewScoped豆一CDI SessionScoped豆,我得到这个异常:访问CDI SessionScoped bean在Java 8并行流中不起作用

WELD-001303: No active contexts for scope type javax.enterprise.context.SessionScoped 

这在Wildfly 10.1运行。

的ViewScoped豆:

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> 

堆栈跟踪:

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结果而不会失去并行流的好处。

+0

我还没有看到过一个真实世界的情况,其中parellel流是出于性能原因至关重要 –

+0

在这种情况下,我在每个函数中引发不同的SQL查询,所以我只需要等待最长的一个完成对我们的SQL Server。 –

+2

并行流并不意味着在IO操作中使用。相反,它们旨在用于执行并行计算,例如通过将列表划分为N个部分并且并行执行N个部分列表的总和并最终总结子结果来对总长列表的元素进行求和,并最终对子结果进行求和 –

回答

3

我认为你碰到的是使用并行流意味着你将运行在多个线程中。现在,这是CDI和上下文的问题,因为您需要上下文传播 - 例如,在当前主线程中(例如)会话上下文处于活动状态,但是当您创建另一个线程时,此线程在此处不活动。

Chapter 6.3 in spec描述它更深入,但给你的短故事 - 上下文传播到其他线程默认情况下不工作。有很好的理由 - 这将会是非常昂贵的(同步),你需要解决一些非常奇怪的情况,例如有一个线程失效会话,因此在其他线程操作时会禁用会话上下文。还有更多这样的情况。

另外,这里没有内置的解决方法。你可以做什么,是实现你自己的范围,还是增强现有的会话范围,但这将是非常复杂的,我猜。

+0

谢谢,我没有意识到范围与线程密切相关。欣赏你的时间和努力到这个答案。 –

1

如果你想在Java EE中并行化查询,我建议你为此使用ManagedExecutorService。并行流使用的分叉连接池意味着并行化计算,而不是IO。

当谈到CDI上下文传播时,您可能会发现BoundSessionContext有用,假设您使用WELD作为CDI实现。阅读更多信息http://john-ament.blogspot.fi/2014/01/bridging-netty-resteasy-and-weld.html

也许您可以在没有绑定会话上下文的情况下进行管理。只需从会话上下文中收集所有必要的数据并将其传递给工作线程。

相关问题