2017-04-06 52 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

也許您可以在沒有綁定會話上下文的情況下進行管理。只需從會話上下文中收集所有必要的數據並將其傳遞給工作線程。

相關問題