我正在尝试使用规范中描述的 EJBContainer.createEJBContainer API(JSR 318:Enterprise JavaBeans,版本 3.1,第 22 章:可嵌入用法)中描述的可嵌入 JBoss,而不是使用 JBoss 特定 API的各种前辈。
概述
- 当我正常调用我的 main 方法时,会话 bean 调用成功。但似乎存在一些类加载问题,因为
"java:jboss/UserTransaction"
无法将 JNDI 对象转换为 javax.transaction.UserTransaction。 - 所以我想这里需要JBoss 模块的类加载魔法。通过我的尝试,createEJBContainer 没有找到任何 EJBContainerProviders 并抛出 EJBException。
请注意,并非我的所有问题都需要阅读所有后续详细信息。因此,如果阅读内容过多,请查看问题部分。谢谢!
环境
JBoss AS 7.1.1.Final(jboss-as-7.1.1.Final.zip,仅在standalone.xml 中更改了日志配置)
(最初是 JBoss EAP 6.0.1 GA,这是实际的目标环境 - 同样的问题)
Oracle JDK 1.7 .0_11
Windows 7 教授 64 位
细节
无状态会话 bean
package test.helloworld.impl;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import test.helloworld.api.HelloWorld;
@Stateless
@Remote(HelloWorld.class)
public class HelloWorldBean implements HelloWorld
{
@Override
public String salute()
{
return "Hello, world";
}
}
...及其业务接口:
package test.helloworld.api;
public interface HelloWorld
{
String salute();
}
客户端程序
package test.helloworld.client;
import static java.lang.System.out;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;
import javax.transaction.UserTransaction;
import test.helloworld.api.HelloWorld;
public class HelloWorldEmbeddedEjbTestClient
{
public static void main(String[] args)
{
int status = 1;
try
{
main();
status = 0;
}
catch (Throwable e)
{
e.printStackTrace(System.err);
System.err.flush();
}
finally
{
// Simply returning from main leaves some thread (and
// hence the JVM) running for another 60s, so force exit
System.exit(status);
}
}
private static void main() throws Exception
{
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(EJBContainer.MODULES, new File[]{new File("./HelloWorld-EJB.jar")});
EJBContainer container = EJBContainer.createEJBContainer(properties);
try
{
Context jndiContext = container.getContext();
Object serviceObj = jndiContext.lookup("java:global/HelloWorld-EJB/HelloWorldBean");
out.println("service:\t" + serviceObj);
HelloWorld service = (HelloWorld) serviceObj;
out.println("result:\t" + service.salute());
callInTx(service, jndiContext);
}
finally
{
out.println("closing EJBContainer...");
container.close();
out.println("EJBContainer closed.");
}
}
private static void callInTx(HelloWorld service, Context jndiContext) throws Exception
{
UserTransaction tx = (UserTransaction) jndiContext.lookup("java:jboss/UserTransaction");
tx.begin();
out.println("result in tx:\t" + service.salute());
tx.commit();
}
}
打包
C:\eclipse\projects\HelloWorldSlSB-Client\rt\HelloWorld-API.jar:
META-INF/MANIFEST.MF
test/helloworld/api/HelloWorld.class
C:\eclipse\projects\HelloWorldSlSB-Client\rt\HelloWorld-EJB.jar:
META-INF/MANIFEST.MF
META-INF/jboss-deployment-structure.xml
test/helloworld/impl/HelloWorldBean.class
test/helloworld/api/HelloWorld.class
HelloWorld-EJB.jar:META-INF/jboss-deployment-structure.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<jboss-deployment-structure>
<deployment>
<dependencies></dependencies>
<exclusions>
<module name="Classpath"/>
</exclusions>
</deployment>
</jboss-deployment-structure>
C:\eclipse\projects\HelloWorldSlSB-Client\rt\modules\test\helloworld\client\main\HelloWorldSlSB-Client.jar:
META-INF/MANIFEST.MF
test/helloworld/client/HelloWorldEmbeddedEjbTestClient.class
C:\eclipse\projects\HelloWorldSlSB-Client\rt\modules\test\helloworld\client\main\module.xml:
<main-class name="test.helloworld.client.HelloWorldEmbeddedEjbTestClient"/>
<resources>
<resource-root path="HelloWorldSlSB-Client.jar"/>
</resources>
<dependencies>
<module name="javax.api"/>
<module name="javax.ejb.api"/>
<module name="org.jboss.as.embedded" export="true"/>
<!--
<module name="org.jboss.as.server" export="true"/>
-->
</dependencies>
</module>
所有 META-INF/MANIFEST.MF 文件只包含“Manifest-Version: 1.0”。
结果
直接调用
使用-Xmx512m -XX:MaxPermSize=256m
类路径 直接调用 HelloWorldEmbeddedEjbTestClient.main
C:\eclipse\output\HelloWorldSlSB-Client
C:\java\jboss-as-7\jboss-modules.jar
C:\java\jboss-as-7\modules\org\jboss\as\embedded\main\jboss-as-embedded-7.1.1.Final.jar
C:\java\jboss-as-7\modules\javax\ejb\api\main\jboss-ejb-api_3.1_spec-1.0.1.Final.jar
C:\java\jboss-as-7\modules\javax\transaction\api\main\jboss-transaction-api_1.1_spec-1.0.0.Final.jar
C:\java\jboss-as-7\modules\org\jboss\logging\main\jboss-logging-3.1.0.GA.jar
C:\java\jboss-as-7\modules\org\jboss\as\controller-client\main\jboss-as-controller-client-7.1.1.Final.jar
C:\java\jboss-as-7\modules\org\jboss\logmanager\main\jboss-logmanager-1.2.2.GA.jar
C:\java\jboss-as-7\modules\org\jboss\dmr\main\jboss-dmr-1.1.1.Final.jar
C:\eclipse\projects\HelloWorldSlSB-Client\rt\HelloWorld-API.jar
和系统属性
-Duser.language=en
-Djboss.home=c:/java/jboss-as-7
-Djboss.home.dir=c:/java/jboss-as-7
-Dorg.jboss.as.embedded.ejb3.BARREN=true
-Dfile.encoding=ISO-8859-1
EJB 调用成功,但无法将 JNDI 对象“java:jboss/UserTransaction”强制转换为 javax.transaction.UserTransaction:
...
19:21:01,875 INFO [org.jboss.ejb.client] (main) JBoss EJB Client version 1.0.5.Final
service: Proxy for remote EJB StatelessEJBLocator{appName='', moduleName='HelloWorld-EJB', distinctName='', beanName='HelloWorldBean', view='interface test.helloworld.api.HelloWorld'}
result: Hello, world
closing EJBContainer...
19:21:06,362 INFO [org.jboss.as.server.deployment] (MSC service thread 1-7) JBAS015877: Stopped deployment HelloWorld-EJB.jar in 90ms
19:21:06,370 INFO [org.jboss.as.repository] (pool-9-thread-1) JBAS014901: Content removed from location c:\java\jboss-as-7\standalone\data\content\35\424415b9a67d64fe8a6dc7ee0700480282f34b\content
19:21:06,370 INFO [org.jboss.as.server] (pool-9-thread-1) JBAS018558: Undeployed "HelloWorld-EJB.jar"
19:21:06,390 INFO [org.jboss.as.osgi] (MSC service thread 1-1) JBAS011942: Stopping OSGi Framework
EJBContainer closed.
java.lang.ClassCastException: org.jboss.tm.usertx.client.ServerVMClientUserTransaction cannot be cast to javax.transaction.UserTransaction
at test.helloworld.client.HelloWorldEmbeddedEjbTestClient.callInTx(HelloWorldEmbeddedEjbTestClient.java:65)
at test.helloworld.client.HelloWorldEmbeddedEjbTestClient.main(HelloWorldEmbeddedEjbTestClient.java:52)
at test.helloworld.client.HelloWorldEmbeddedEjbTestClient.main(HelloWorldEmbeddedEjbTestClient.java:21)
(看起来有点奇怪,因为异常被捕获并打印在 main 的末尾。当然,它发生在容器关闭之前。)
调试器显示 JNDI 对象的 getClass().getClassLoader() 是 org.jboss.modules.ModuleClassLoader
ModuleClassLoader for Module "org.jboss.jboss-transaction-spi:main" from local module loader @4b436982 (roots: c:\java\jboss-as-7\modules)
及其 getClass().getInterfaces()[0] /* == interface javax.transaction.UserTransaction */.getClassLoader()但是,UserTransaction.class.getClassLoader() 的类型是
ModuleClassLoader for Module "javax.transaction.api:main" from local module loader @4b436982 (roots: c:\java\jboss-as-7\modules)
sun.misc.Launcher$AppClassLoader。
jboss-modules.jar 调用
java -jar jboss-modules.jar
通过带有类路径的org.jboss.modules.Main.main 调用(即还有什么作用)
C:\java\jboss-as-7\jboss-modules.jar
如上所述的系统属性和参数
-mp "C:\java\jboss-as-7\modules;C:\eclipse\projects\HelloWorldSlSB-Client\rt\modules"
test.helloworld.client
它已经在 createEJBContainer 中失败了:
javax.ejb.EJBException: Unable to instantiate container with factories []
at javax.ejb.embeddable.EJBContainer.createEJBContainer(EJBContainer.java:97)
at test.helloworld.client.HelloWorldEmbeddedEjbTestClient.main(HelloWorldEmbeddedEjbTestClient.java:42)
at test.helloworld.client.HelloWorldEmbeddedEjbTestClient.main(HelloWorldEmbeddedEjbTestClient.java:21)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.jboss.modules.Module.run(Module.java:260)
at org.jboss.modules.Main.main(Main.java:291)
调试显示这是因为在方法 EJBContainer.findAllFactories() 中,在线程上下文类加载器中找不到资源“META-INF/services/javax.ejb.spi.EJBContainerProvider”,即
ModuleClassLoader for Module "test.helloworld.client:main" from local module loader @1afec586 (roots: C:\java\jboss-as-7\modules,C:\eclipse\projects\HelloWorldSlSB-Client\rt\modules)
问题
- 有谁知道如何解决这个问题或有任何类似的工作?
- 正常的 JBoss 安装是正确的起点,还是我需要特定的可嵌入变体?
- UserTransaction 甚至应该在可嵌入场景中使用吗?
- 如果是,这是否意味着我也可以为 JPA 2 CMT 实体和旧版 JDBC 代码使用一个 XA 数据源?两者都可以参与同一个事务(例如,JDBC 代码以 java.sql.Connection.setAutoCommit(false) 开始 tx,然后使用 TransactionAttributeType.REQUIRED 调用 EJB)?
- 有人可以指点我一些文件吗?我发现的只是带有 JBoss 特定 API 的旧东西,而不是标准的 EJB 3.1 方式。
- 最好记录如何在没有任何测试框架的情况下使用,因为我正在评估它以供生产使用。
感谢您阅读全部(或部分:-)!