阅读更多
1. 调用EJB的三种方法
调用EAP 6 EJB的第一种方法,使用JBoss API,如下:
Properties p = new Properties();
p.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
p.put("remote.connections", "default");
p.put("remote.connection.default.host", "localhost");
p.put("remote.connection.default.port", "4447");
p.put("remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS", "false");
EJBClientConfiguration cc = new PropertiesBasedEJBClientConfiguration(p);
ContextSelector selector = new ConfigBasedEJBClientContextSelector(cc);
EJBClientContext.setSelector(selector);
Properties props = new Properties();
props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
InitialContext context = new InitialContext(props);
final boolean useScopedExample = Boolean.getBoolean("UseScopedContext");
final String rcal = "ejb:jboss-ejb-multi-server-app-main/ejb//" + (useScopedExample ? "MainAppSContextBean" : "MainAppBean") + "!" + MainApp.class.getName();
final MainApp remote = (MainApp) context.lookup(rcal);
这种方法在Standalone client (client is not running inside of JBoss EAP 6)能正常调用,但在EAP 6环境中会报java.lang.SecurityException: EJBCLIENT000021: EJB client context selector may not be changed。
第二种方法使用scoped context,代码如下:
Properties props = new Properties();
props.put("org.jboss.ejb.client.scoped.context", "true");
props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
//props.put(Context.OBJECT_FACTORIES, "org.jboss.ejb.client.naming.ejb.ejbURLContextFactory");
props.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
props.put("remote.connections", "default");
props.put("remote.connection.default.port", 4447);
props.put("remote.connection.default.host", host);
//props.put("remote.connection.default.username", username);
//props.put("remote.connection.default.password", password);
props.put("remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT", "false");
props.put("jboss.naming.client.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT", "false");
Context context = (Context) new InitialContext(props).lookup("ejb:");
try {
final TimerExample bean = (TimerExample) context.lookup("TestTimer/TestTimerEJB/TimerExampleBean!org.example.jboss.timer.TimerExample");
bean.doSomething();
} finally {
try {
context.close();
} catch(Exception e) { }
}
这种方式,可以完全通过代码实现配置,但由客户端来创建和管理Connection,因此需要更多的资源,性能不高。还有一个严重的问题,当使用CMT,事物跨多个EJB时,因每次调用EJB后都关闭context(如不close,connection会一直保持),这将造成事务commit/rollback失败。这种情况下,就得使用第三种方式,也是推荐的方式:
先来看代码:
Properties props = new Properties();
props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
final Context context = new javax.naming.InitialContext(props);
final Greeter bean = (Greeter) context.lookup("ejb:myapp/myejb/GreeterBean!" + org.myapp.ejb.Greeter.class.getName());
bean.doSometing();
代码很简单,注意没有调用close()方法。那这是怎么找到远程主机的呢?需要增加一些配置,先在war的WEB-INF或ear的META-INF中新建一个文件jboss-ejb-client.xml,内容如下:
然后在standalone.xml增加如下配置,建立与远程主机的关联:
...
调用EJB后,来查看一下connection情况:netstat -aon | findstr "4447"(netstat -np | grep 4447)
2. Could not register a EJB receiver: java.lang.RuntimeException: Operation failed with status WAITING
EJB调用配置中包含以下三个timeout参数:
The timeout for the EJB handshake or method invocation request/response cycle. The value is in milliseconds.
The invocation of any method throws a java.util.concurrent.TimeoutException if the execution takes longer than the timeout period. The execution completes and the server is not interrupted.
The timeout for the background reconnect tasks. The value is in milliseconds.
If a number of connections are down, the next client EJB invocation will use an algorithm to decide if a reconnect is necessary to find the right node.
- remote.connection.CONNECTION_NAME.connect.timeout
The timeout period for the initial connection. After that, the reconnect task will periodically check whether the connection can be established. The value is in milliseconds.
remote.connection.CONNECTION_NAME.connect.timeout默认值为5000ms(参见代码InitialContextFactory.getInitialContext),当在timeout时间内连接不上时,就会报以上错误。
3. 关于Rollback
EJB方法抛出RuntimeException会引起rollback,那有方法指定某个Exception是否引起Rollback么?可以使用注解@ApplicationException,如下:
@ApplicationException(rollback = true)
public class MyException extends Exception {
// ...
}
4. How to close scoped EJB client contexts?
Internally, when a lookup happens for a ejb: URL string, a relevant javax.naming.Context is created for that ejb: lookup.
So we first create a JNDI context and then use it to lookup an EJB.
final Properties props = new Properties();
// mark it for scoped EJB client context
props.put("org.jboss.ejb.client.scoped.context","true");
// add other properties
props.put(....);
...
Context jndiCtx = new InitialContext(props);
Context ejbRootNamingContext = (Context) jndiCtx.lookup("ejb:");
try {
final MyBean bean = ejbRootNamingContext.lookup("app/module/distinct/bean!interface"); // rest of the EJB jndi lookup string
bean.doSomething();
} finally {
try {
// close the EJB naming JNDI context
ejbRootNamingContext.close();
} catch (Throwable t) {
// log and ignore
}
try {
// also close our other JNDI context since we are done with it too
jndiCtx.close();
} catch (Throwable t) {
// log and ignore
}
}
Invoking Session Beans
Scoped EJB client contexts
How to configure an EJB client in JBoss EAP 6