我有一个具有这种结构的Maven项目:
-myproject
-myproject-ear
-myproject-service
-webservice
-myproject-ejb
在myproject-ejb
我有这个java包:
-src/main/java/
-src/test/java/
我有一个EJB和
中相应的bean实现-src/main/java/org/mypackage/MyBean.java
-src/main/java/org/mypackage/MyBeanImpl.java
在src/test/java/
中,我有一个名为MyBeanTest.java的测试,其代码如下:
import javax.ejb.EJB;
import org.mypackage.MyBean;
import org.junit.*;
public class MyBeanTest {
@EJB
private MyBean myBean;
@Test
public void testBean() {
System.out.println("myBean: "+myBean); // prints null
myBean.writeToDB("Hello", "World"); // fails since myBean is null
}
}
当我运行单元测试时,myBean
为空。我想知道为什么@EJB
注释不起作用。测试包与bean在同一个应用程序中,因此@EJB
应该可以正常工作。
有什么想法吗?
编辑1
我发现this link和我有同样的问题,但那里的解决方案似乎对我不起作用。我做错了吗?
package org.myproject.ejb;
import java.util.Hashtable;
import java.util.Properties;
import javax.ejb.EJB;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import org.myproject.ejb.MyBean;
import org.jboss.ejb.client.ContextSelector;
import org.jboss.ejb.client.EJBClientConfiguration;
import org.jboss.ejb.client.EJBClientContext;
import org.jboss.ejb.client.PropertiesBasedEJBClientConfiguration;
import org.jboss.ejb.client.remoting.ConfigBasedEJBClientContextSelector;
import org.junit.*;
public class MyBeanTest {
private MyBean myBean;
@Before
public void init() {
try {
Properties clientProp = new Properties();
clientProp.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
clientProp.put("remote.connections", "default");
clientProp.put("remote.connection.default.port", "4447");
clientProp.put("remote.connection.default.host", "localhost");
clientProp.put("remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS", "false");
EJBClientConfiguration cc = new PropertiesBasedEJBClientConfiguration(clientProp);
ContextSelector<EJBClientContext> selector = new ConfigBasedEJBClientContextSelector(cc);
EJBClientContext.setSelector(selector);
Properties env = new Properties();
env.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
env.put(Context.SECURITY_PRINCIPAL, "admin");
env.put(Context.SECURITY_CREDENTIALS, "testing");
InitialContext ctx = new InitialContext(env);
myBean = (MyBean) ctx.lookup("java:app/myproject-ejb-1.0-SNAPSHOT/MyBeanImpl");
}
catch(NamingException ex) {
ex.printStackTrace();
}
}
@Test
public void testBean() {
System.out.println("ejb: "+myBean); // prints null
}
}
我使用上述配置得到的错误是:
WARN: Unsupported message received with header 0xffffffff
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:662)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:307)
at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:344)
答案 0 :(得分:5)
容器资源注入(例如@EJB)需要填充的JNDI目录,并且只能在Java EE容器中执行的Java EE托管组件中工作。对单元测试来说是一个挑战。请参阅JSR318 Java EE 6平台规范,参见EE.5资源,命名和注入。
您现在正在尝试JNDI查找 - 远程连接其JNDI上下文的Java SE单元测试应用程序。缺点:必须部署完整的Java EE 6应用程序作为运行测试的前提条件; test-bugfix-build-deploy-retest生命周期可能会减慢速度。
有些问题:
"ejb:..."
而不是 "java:app/..."
,因为JBoss EJB-client-project代码使用了这个截取查找。同样来自Java EE 6平台规范EE.5.2.2: java:app
命名空间中的名称由单个Java EE应用程序中所有模块中的所有组件共享。如果您的测试是使用 java:app
的单独JSE应用程序,我怀疑JBoss将其视为与单个Java EE应用程序分开,并且查找将失败。尝试以下操作:
包含以下属性:
clientProp.put("remote.connection.default.username", "admin");
clientProp.put("remote.connection.default.password", "testing");
更改客户参考:
java:app/myproject-ejb-1.0-SNAPSHOT/MyBeanImpl to
ejb:<app-ear-name>/<module-jar-name>/<jboss-optional-distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-interface>
E.g。如果MyBean是在myproject-ejb-1.0-SNAPSHOT.jar中部署的无状态EJB(没有任何耳朵)。然后:
ejb:/myproject-ejb-1.0-SNAPSHOT//MyBeanImpl!org.mypackage.MyBean
如果它是一个有状态的EJB,那么添加&#34;?stateful&#34;字符串。
直接设置ejb-client.properties
(通过文件或程序)并直接应用于JNDI Context
。请参阅https://docs.jboss.org/author/display/AS72/EJB+invocations+from+a+remote+client+using+JNDI和https://docs.jboss.org/author/display/AS72/Scoped+EJB+client+contexts以及http://middlewaremagic.com/jboss/?p=1177
将来:使用CDI注射; JUnit + CDI @Mock
用于&#34; POJO&#34;单元测试; Arquillian for&#34; Java EE&#34;容器中的单元/模块测试。然后你可以避免/减少上面的(2)测试(JSE客户端 - &gt; EJB)。
CDI支持:
@EJB
注释)。这仍然需要部署的Java EE应用程序/组件和填充的JNDI目录进行查找。 托管bean作为POJO或Java EE组件(包括EJB) - 注入&#34;任何&#34;到&#34;任何&#34;优秀的@Inject注释。没有JNDI目录的工作,是类型安全和&amp; bean范围感知。
通过简单的模拟支持单元测试。使用@Mock
&amp; @Specializes
为任何bean声明替换版本。测试没有EJB的EJB客户端。将EJB测试为POJO。
要启用CDI,请包含beans.xml
文件(如果通过注释进行所有配置,则可以为空)。
声明托管bean:
@SessionScoped
@Inject
使用此方法注入参考:
@Inject (optional @MyDeclaredQualifier) private MyBean myBean;
Arquillian(&#34; JUnit for Java EE 6&#34;)在Java EE服务器上运行测试代码。它动态地将测试代码部署到配置的container(s)并运行测试。它支持@EJB注释,JNDI连接变得简单,您可以在单元测试中包含Java EE类,而无需进行模拟或重构以从中抽象出来。
答案 1 :(得分:2)
1)注释注入由容器完成。因此,未管理的类(容器管理)将无法执行注释注入。
2)现在,在这种情况下,您必须手动调用 JNDI 并检索 EJB 实例:
<强>即:强>
InitialContext ctx = new InitialContext();
MyBean bean = (MyBeanRemote) ctx.lookup("java:global/<portable jndi name of your bean>");
注意:使用no arg构造函数InitialContext()
。因为你的java类部署在我认为的服务器中。否则,如果您的类是独立的java类,则可能需要指定上下文工厂类,具体取决于供应商。
注意:如果从其他应用程序调用EJB,则需要Bean 远程接口(即:不同的战争,耳朵...)或者本地界面就足够了。
答案 2 :(得分:1)
如果无法创建初始上下文实现,则抛出此异常。 InitialContext类的文档中描述了如何选择初始上下文实现的策略。
在与InitialContext进行任何交互时,不仅在构造InitialContext时,都会抛出此异常。例如,只有在对其调用实际方法时,初始上下文的实现才可能懒惰地检索上下文。应用程序不应该依赖于何时确定初始上下文的存在。