remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=localhost
remote.connection.default.port = 8080
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
#remote.connection.default.connect.options.org.xnio.Options.SSL_STARTTLS=true
更改主机名和端口以匹配您的服务器。 默认端口号为8080.对于安全连接,请将SSL_ENABLED行设置为true并取消注释SSL_STARTTLS行。 容器中的Remoting界面支持使用相同端口的安全和不安全的连接。
<dependency>
<groupId>org.jboss.quickstarts.eap</groupId>
<artifactId>jboss-ejb-remote-server-side</artifactId>
<type>ejb-client</type>
<version>${project.version}</version>
</dependency>
/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=remote-ejb:add(host=localhost, port=8080)
final RemoteCalculator statelessRemoteCalculator = (RemoteCalculator) context.lookup(
"ejb:/jboss-ejb-remote-server-side//CalculatorBean!" +
RemoteCalculator.class.getName());
会话bean JNDI名称使用特殊语法定义。 有关更多信息,请参阅EJB JNDI命名参考。
4.调用方法:现在您有一个代理bean对象,您可以调用远程业务界面中包含的任何方法。
调用远程方法代码示例:
int a = 204;
int b = 340;
System.out.println("Adding " + a + " and " + b + " via the remote stateless calculator deployed on the server");
int sum = statelessRemoteCalculator.add(a, b);
System.out.println("Remote calculator returned sum = " + sum);
代理bean将方法调用请求传递到服务器上执行的会话bean。结果返回给代理bean,然后将其返回给调用者。代理bean和远程会话bean之间的通信对调用者来说是透明的。
public class MyApplication {
public static void main(String args[]) {
final javax.naming.Context ctxOne = new javax.naming.InitialContext();
final MyBeanInterface beanOne = ctxOne.lookup("ejb:app/module/distinct/bean!interface");
beanOne.doSomething();
}
}
远程客户端JNDI查找通常由jboss-ejb-client.properties文件支持,该文件用于设置EJB客户机上下文和EJB接收器。此配置还包括安全凭据,然后用于创建连接到JBoss EAP服务器的EJB接收器。当调用上述代码时,EJB客户端API会查找EJB客户机上下文,然后用于选择将接收和处理EJB调用请求的EJB接收器。在这种情况下,只有单个EJB客户端上下文,因此上述代码用于调用该上下文。使用JNDI远程调用会话bean的过程将在此更详细地描述:使用JNDI远程调用会话Bean。
public class MyApplication {
public static void main(String args[]) {
// Use the "foo" security credential connect to the server and invoke this bean instance
final javax.naming.Context ctxOne = new javax.naming.InitialContext();
final MyBeanInterface beanOne = ctxOne.lookup("ejb:app/module/distinct/bean!interface");
beanOne.doSomething();
...
// Use the "bar" security credential to connect to the server and invoke this bean instance
final javax.naming.Context ctxTwo = new javax.naming.InitialContext();
final MyBeanInterface beanTwo = ctxTwo.lookup("ejb:app/module/distinct/bean!interface");
beanTwo.doSomething();
...
}
}
在这种情况下,应用程序想要连接到同一个服务器实例来调用该服务器上托管的EJB,但是想要在连接到服务器时使用两种不同的凭据。 因为客户机应用程序具有单个EJB客户机上下文,每个服务器实例只能有一个EJB接收器,这意味着上述代码只使用一个凭据来连接到服务器,并且代码不按应用程序期望执行。
解决方案
范围的EJB客户端上下文为此问题提供了解决方案。它们提供了一种对EJB客户端上下文及其相关联的JNDI上下文(通常用于EJB调用)的更多控制的方法。 有关范围限定的EJB客户机上下文的更多信息,请参阅使用Scoped EJB客户端上下文并使用Scoped EJB客户端上下文配置EJB。
服务器到服务器调用
必须处理分布式JBoss EAP应用程序的事务属性,以便在同一服务器上调用应用程序。要终止事务,目标方法必须使用不同的接口标记为REQUIRES_NEW。
可以使用以下任一方法调用EJB:
1.EJB远程呼叫
2.互联网ORB协议(IIOP)远程呼叫
注意:
如果两个服务器都是JBoss EAP,则JBoss EAP不需要Java事务服务(JTS)来进行服务器到服务器EJB调用的事务传播。 JBoss EJB客户端API库自身处理它。
EJB远程呼叫
要使用JBoss EAP独立客户端调用EJB会话bean,客户端必须对使用EJB代理或UserTransaction的InitialContext对象引用。在使用EJB代理或UserTransaction时,保持InitialContext对象也是很重要的。连接的控制将在由InitialContext创建的类与属性的内部。
以下代码示例显示了一个持有对InitialContext对象的引用的EJB客户端。该代码示例来自JBoss EAP附带的ejb-multi-server quickstart。
EJB客户端代码示例package org.jboss.as.quickstarts.ejb.multi.server;
import java.util.Date;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.jboss.as.quickstarts.ejb.multi.server.app.MainApp;
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;
public class Client {
/**
* @param args no args needed
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// suppress output of client messages
Logger.getLogger("org.jboss").setLevel(Level.OFF);
Logger.getLogger("org.xnio").setLevel(Level.OFF);
Properties p = new Properties();
p.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", "false");
p.put("remote.connections", "one");
p.put("remote.connection.one.port", "8080");
p.put("remote.connection.one.host", "localhost");
p.put("remote.connection.one.username", "quickuser");
p.put("remote.connection.one.password", "quick-123");
EJBClientConfiguration cc = new PropertiesBasedEJBClientConfiguration(p);
ContextSelector<EJBClientContext> 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 String rcal = "ejb:jboss-ejb-multi-server-app-main/ejb//" + ("MainAppBean") + "!" + MainApp.class.getName();
final MainApp remote = (MainApp) context.lookup(rcal);
final String result = remote.invokeAll("Client call at "+new Date());
System.out.println("InvokeAll succeed: "+result);
}
}
注意
对于具有范围的EJB客户端上下文以及使用远程命名协议的调用的方案,不支持在客户机上获取UserTransaction引用。 这是因为在这些情况下,InitialContext封装了自己的EJB客户端上下文实例; 这不能使用EJBClient类的静态方法访问。当EJBClient.getUserTransaction()被调用时,它从默认(全局)EJB客户端上下文(可能未被初始化)而不是从所需的EJB客户端上下文返回一个事务。
UserTransaction参考在客户端
以下示例显示如何在独立客户端上获取UserTransaction引用。
import org.jboss.ejb.client.EJBClient;
import javax.transaction.UserTransaction;
...
Context context = null;
UserTransaction tx = null;
try {
Properties props = new Properties();
// REMEMBER: there must be a jboss-ejb-client.properties with the
// connection parameter in the client's classpath
props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
context = new InitialContext(props);
System.out.println("\n\tGot initial Context: "+context);
tx = EJBClient.getUserTransaction("yourServerName");
System.out.println("UserTransaction = "+tx.getStatus());
tx.begin();
// do some work
...
} catch (Exception e) {
e.printStackTrace();
tx.rollback();
} finally{
if(context != null) {
context.close();
}
}
注意
在客户端获取UserTransaction参考; 使用以下系统属性-Djboss.node.name = yourServerName启动服务器,然后在客户端使用它,如下所示:tx = EJBClient.getUserTransaction("yourServerName");
将“yourServerName”替换为服务器的名称。 如果在节点上启动用户事务,那么节点上的所有调用都是粘性的,并且节点必须具有所有需要的EJB。 不可能使用UserTransaction与远程命名协议和作用域上下文。
互联网ORB协议(IIOP)远程呼叫
要使用IIOP远程调用调用EJB bean,必须首先在服务器上启用IIOP。
要启用IIOP,您必须安装iiop-openjdk子系统,并在ejb3子系统配置中显示<iiop />元素。发行版附带的standalone-full.xml配置都启用了这两个配置。
对于通过IIOP远程调用可以访问的bean,需要使用EJB 2和家庭接口缩小。有关IIOP远程调用的更多详细信息,请参阅配置IIOP远程EJB调用。
注意
IIOP远程调用和EJB远程调用之间的主要区别是:
当客户端打算通过IIOP远程调用调用EJB bean以在客户端启动事务时,必须使用JTS事务实现。另一方面,如果客户端打算通过EJB远程调用调用EJB bean,则必须使用JTA事务实现。
对于通过IIOP远程调用的EJB调用,事务在客户机上创建并通过调用传播到服务器。而通过EJB远程调用进行EJB调用,事务在服务器上查找并在客户端进行管理。
要在服务器上启用JTS事务,必须在iiop-openjdk子系统中将事务属性从值spec更改为完整,并将事务子系统中的jts属性设置为true。您可以使用以下管理CLI命令来实现此目的。/subsystem=iiop-openjdk/:write-attribute(name=transactions,value=full)
/subsystem=transactions/:write-attribute(name=jts,value=true)
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import com.arjuna.ats.arjuna.recovery.RecoveryManager;
import com.arjuna.ats.internal.jts.ORBManager;
import com.arjuna.ats.internal.jts.context.ContextPropagationManager;
import com.arjuna.ats.jts.OTSManager;
import com.sun.corba.se.impl.orbutil.ORBConstants;
import com.arjuna.orbportability.ORB;
import com.arjuna.orbportability.OA;
final String host = "localhost";
final int port = 3528;
// For client we define how the Narayana will behave
System.setProperty("com.arjuna.ats.jts.alwaysPropagateContext", "true");
// Set orb to be initialized on client and being able to start ORB txn
Properties properties = new Properties();
properties.setProperty(ORBConstants.PERSISTENT_SERVER_PORT_PROPERTY, "15151");
properties.setProperty(ORBConstants.ORB_SERVER_ID_PROPERTY, "1");
// registers the appropriate filter with the ORB
new ContextPropagationManager();
org.omg.CORBA.ORB sunOrb = org.omg.CORBA.ORB.init(new String[0], properties);
ORB orb = null;
try {
orb = com.arjuna.orbportability.ORB.getInstance("ClientSide");
orb.setOrb(sunOrb);
OA oa = OA.getRootOA(orb);
org.omg.PortableServer.POA rootPOA = org.omg.PortableServer.POAHelper.narrow(sunOrb.resolve_initial_references("RootPOA"));
oa.setPOA(rootPOA);
oa.initOA();
ORBManager.setORB(orb);
ORBManager.setPOA(oa);
// Recovery manager has to be started on client when we want recovery to work at client
RecoveryManager.manager().startRecoveryManagerThread();
// Getting context to lookup
System.setProperty("com.sun.CORBA.ORBUseDynamicStub", "true");
final Properties prope = new Properties();
prope.put(Context.PROVIDER_URL, "corbaloc::" + host + ":" + port + "/JBoss/Naming/root");
prope.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.iiop.naming:org.jboss.naming.client");
prope.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory");
Context context = new InitialContext(prope);
// Bean lookup
final Object iiopObj = context.lookup(IIOPBeanMandatory.class.getSimpleName());
final IIOPBeanHome beanHome = (IIOPBeanHome) PortableRemoteObject.narrow(iiopObj, IIOPBeanHome.class);
final IIOPRemote bean = beanHome.create();
// Starting orb transaction
OTSManager.get_current().begin();
// Call bean - business logic
bean.sayHello();
// Manage the commit of the work
OTSManager.get_current().commit(true);
// or rollback
// OTSManager.get_current().rollback();
} finally {
// It's good to release resources - do it only once at the end
if (orb != null) {
orb.shutdown();
}
RecoveryManager.manager().terminate();
}
有关详细信息,请参阅“
JBoss EAP配置指南”中的配置事务。
以下示例演示如何从另一个远程JBoss EAP服务器实例调用部署在JBoss EAP服务器实例上的EJB。 为了便于参考,我们使用以下别名:
*客户端服务器:EJB调用发生的服务器。
*目标服务器:部署EJB的服务器。
先决条件:
*在目标服务器上配置具有所需凭据的用户。 有关详细信息,请参阅“JBoss EAP配置指南”中的添加管理用户。
*启动目标服务器。
./standalone.sh -server-config=standalone-full.xml
部署应用程序 有关详细信息,请参阅“JBoss EAP配置指南”中的部署应用程序。
注意
每个服务器实例必须具有唯一的jboss.node.name系统属性。 您可以通过将其传递给启动脚本来设置此值:./standalone.sh -server-config=standalone-full.xml -Djboss.node.name=<add appropriate value here>
5.5.1 配置客户端服务器
您必须让客户端服务器知道目标服务器的EJB远程连接器,并在EJB调用期间进行通信。 要实现此目的,您必须在客户端服务器上的远程处理子系统中添加一个远程出站连接。远程出站连接配置指示将从此客户端服务器向远程服务器实例创建出站连接。 远程出站连接必须具有自己配置的出站套接字绑定,指向远程主机和目标服务器的远程端口。
1、启动客户端服务器:/standalone.sh -server-config=standalone-full.xml -Djboss.socket.binding.port-offset=100
2、在客户端服务器上创建一个安全领域,以与安全的目标服务器进行通信。 客户端服务器必须向目标服务器提供用户凭据。 要实现此目的,您需要在客户端服务器上创建安全领域,这将传递为添加到目标服务器的用户提供的用户信息。
echo -n “password” | openssl dgst -sha256 -binary | openssl base64
/core-service=management/security-realm=ejb-security-realm:add()
/core-service=management/security-realm=ejb-security-realm/server-identity=secret:add(value=<base64-encoded password>)
您可能会注意到,管理CLI显示消息“process-state”⇒“reload-required”,因此您必须重新启动服务器才能使用此更改。
<management>
<security-realms>
...
<security-realm name="ejb-security-realm">
<server-identities>
<secret value=<base64-encoded password>/>
</server-identities>
</security-realm>
</security-realms>
...
上面的代码片段使用base64编码的密码创建了一个名为ejb-security-realm的安全领域。
4.在客户端服务器上创建出站套接字绑定。 您现在必须创建一个指向目标服务器的主机和端口的出站套接字绑定。
/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=remote-ejb:add(host=localhost, port=8080)
上面的命令创建一个名为remote-ejb的出站套接字绑定,它指向localhost作为主机,端口8080作为目标端口。 请注意,主机信息应与目标服务器的主机/ IP相匹配。 在这个例子中,我们在同一台机器上运行客户机和目标服务器,所以我们使用localhost。 类似地,端口信息应该与ejb3子系统使用的http-remoting连接器端口匹配; 默认为8080。
当此命令成功运行时,您将看到在suite-binding-group中使用以下outbound-socket-binding更新了standalone-full.xml:
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
...
<outbound-socket-binding name="remote-ejb">
<remote-destination host="localhost" port="8080"/>
</outbound-socket-binding>
</socket-binding-group>
5.创建使用此新创建的出站套接字绑定的远程出站连接。现在让我们创建一个远程出站连接,它将使用新创建的outbound-socket-binding指向目标服务器的EJB remoting连接器:
/subsystem=remoting/remote-outbound-connection=remote-ejb-connection:add(outbound-socket-binding-ref=remote-ejb, protocol=http-remoting, security-realm=ejb-security-realm, username=ejb)
此步骤将在客户端服务器上创建到远程目标服务器的出站连接,并将用户名设置为允许与该目标服务器进行通信的用户。它还将安全领域设置为能够传递用户凭据(在这种情况下为密码)的预配置安全领域。这样当必须从客户端服务器建立连接到目标服务器时,连接创建逻辑将具有必要的安全凭证来传递并建立成功的安全连接。
让我们运行以下两个操作来设置出站连接的一些默认连接创建选项:/subsystem=remoting/remote-outbound-connection=remote-ejb-connection/property=SASL_POLICY_NOANONYMOUS:add(value=false)
/subsystem=remoting/remote-outbound-connection=remote-ejb-connection/property=SSL_ENABLED:add(value=false)
最终,在成功调用此命令后,将在远程处理子系统中创建以下配置:
<subsystem xmlns="urn:jboss:domain:remoting:1.1">
...
<outbound-connections>
<remote-outbound-connection name="remote-ejb-connection" outbound-socket-binding-ref="remote-ejb" protocol="http-remoting" security-realm="ejb-security-realm" username="ejb">
<properties>
<property name="SASL_POLICY_NOANONYMOUS" value="false"/>
<property name="SSL_ENABLED" value="false"/>
</properties>
</remote-outbound-connection>
</outbound-connections>
</subsystem>
这完成了我们在客户端服务器上的配置。 我们的下一步是在客户端服务器上部署应用程序,该服务器将调用目标服务器上部署的bean。
5.5.2将jboss-ejb-client.xml添加到客户端应用程序
将jboss-ejb-client.xml添加到客户端应用程序作为META-INF / jboss-ejb-client.xml:<jboss-ejb-client xmlns="urn:jboss:ejb-client:1.0">
<client-context>
<ejb-receivers>
<remoting-ejb-receiver outbound-connection-ref="remote-ejb-connection"/>
</ejb-receivers>
</client-context>
</jboss-ejb-client>
请注意,我们已将此应用程序的EJB客户机上下文配置为使用指向远程出站连接的远程远程接收器,该远程出站连接(我们之前创建的)指向remote-ejb-connection。这将链接EJB客户端上下文以使用指向目标服务器上的EJB remoting连接器的remote-ejb-connection。
5.5.3 调用Bean
以下片段显示如何调用该bean:import javax.naming.Context;
import java.util.Hashtable;
import javax.naming.InitialContext;
…
public void invokeOnBean() {
try {
final Hashtable props = new Hashtable();
// setup the ejb: namespace URL factory
props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
// create the InitialContext
final Context context = new javax.naming.InitialContext(props);
// Lookup the Greeter bean using the ejb: namespace syntax which is explained here https://docs.jboss.org/author/display/AS71/EJB+invocations+from+a+remote+client+using+JNDI
final Greeter bean = (Greeter) context.lookup("ejb:" + "myapp" + "/" + "myejb" + "/" + "" + "/" + "GreeterBean" + "!" + org.myapp.ejb.Greeter.class.getName());
// invoke on the bean
final String greeting = bean.greet("Tom");
System.out.println("Received greeting: " + greeting);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
上面的代码将调用目标服务器上部署的bean并返回结果。
5.5.4部署客户端应用程序
让我们在客户端服务器上部署客户端应用程序。您可以使用CLI或管理控制台或IDE或手动部署到EAP_HOME/ standalone / deployments文件夹。确保应用程序已部署成功。
有关详细信息,请参阅“JBossEAP配置指南”中的部署应用程序。
概要
调用EJB在早期版本的JBoss EAP中,通常会创建一个JNDI上下文并传递给指向目标服务器的PROVIDER_URL。对使用该JNDI上下文查找的EJB代理进行的任何调用都将终止于该服务器。使用范围的EJB客户端上下文,用户应用程序可以控制哪个EJB接收器用于特定的调用。
在远程独立客户端中使用范围化的EJB客户端上下文
在引入范围限定的EJB客户端上下文之前,上下文通常作用于客户端应用程序。范围客户端上下文现在允许使用JNDI上下文来限定EJB客户机上下文范围。以下是使用范围的EJB客户端上下文调用相同的bean两次的独立远程客户端应用程序的示例:public class MyApplication {
public static void main(String args[]) {
// Use the "foo" security credential connect to the server and invoke this bean instance
final Properties ejbClientContextPropsOne = getPropsForEJBClientContextOne():
final javax.naming.Context ctxOne = new javax.naming.InitialContext(ejbClientContextPropsOne);
final MyBeanInterface beanOne = ctxOne.lookup("ejb:app/module/distinct/bean!interface");
beanOne.doSomething();
...
ctxOne.close();
// Use the "bar" security credential to connect to the server and invoke this bean instance
final Properties ejbClientContextPropsTwo = getPropsForEJBClientContextTwo():
final javax.naming.Context ctxTwo = new javax.naming.InitialContext(ejbClientContextPropsTwo);
final MyBeanInterface beanTwo = ctxTwo.lookup("ejb:app/module/distinct/bean!interface");
beanTwo.doSomething();
...
ctxTwo.close();
}
}
要使用作用域的EJB客户端上下文,可以以编程方式配置EJB客户端属性,并在上下文创建时传递属性。属性与标准jboss-ejb-client.properties文件中使用的属性相同。要将EJB客户端上下文作用于JNDI上下文,您还必须指定org.jboss.ejb.client.scoped.context属性并将其值设置为true。该属性通知EJB客户机API,它必须创建一个由EJB接收器支持的EJB客户机上下文,然后创建的上下文仅对创建它的JNDI上下文进行作用域或可见。使用此JNDI上下文查找或调用的任何EJB代理将只知道与此JNDI上下文相关联的EJB客户机上下文。应用程序用于查找和调用EJB的其他JNDI上下文不会知道其他作用域的EJB客户端上下文。
不通过org.jboss.ejb.client.scoped.context属性并且不限定为EJB客户端上下文的JNDI上下文将使用默认行为,即使用通常与该EJB客户机上下文相关联的现有EJB客户机上下文整个应用程序。
范围EJB客户端上下文为用户应用程序提供了与以前版本的JBoss EAP中基于JNP的JNDI调用相关联的灵活性。它为用户应用程序提供了更多的控制权,JNDI上下文与哪个服务器通信,以及如何连接到该服务器。
注意
使用范围上下文,底层资源不再由容器或API处理,因此当不再需要InitialContext时,必须关闭它们。当InitialContext关闭时,资源将立即释放。与它绑定的代理不再有效,任何调用将抛出异常。未能关闭InitialContext可能会导致资源和性能问题。
5.6.1。使用范围EJB客户端上下文配置EJB
可以使用基于地图的作用域上下文配置EJB。这是通过使用jboss-ejb-client.properties中找到的标准属性以编程方式填充属性映射来实现的,为org.jboss.ejb.client.scoped.context属性指定true,并在InitialContext创建时传递属性。
使用范围上下文的好处是允许您在不直接引用EJB或导入JBoss类的情况下配置访问。它还提供了一种在多线程环境中在运行时配置和负载平衡主机的方法。
使用基于映射的作用域上下文配置EJB:
1、设置属性:
以编程方式配置EJB客户端属性,指定与标准jboss-ejb-client.properties文件中使用的相同的属性集。要启用范围上下文,您必须指定org.jboss.ejb.client.scoped.context属性并将其值设置为true。以下是以编程方式配置属性的示例。// Configure EJB Client properties for the InitialContext
Properties ejbClientContextProps = new Properties();
ejbClientContextProps.put("remote.connections","name1");
ejbClientContextProps.put("remote.connection.name1.host","localhost");
ejbClientContextProps.put("remote.connection.name1.port","8080");
// Property to enable scoped EJB client context which will be tied to the JNDI context
ejbClientContextProps.put("org.jboss.ejb.client.scoped.context", "true");
2、传递上下文创建中的属性:
// Create the context using the configured properties
InitialContext ic = new InitialContext(ejbClientContextProps);
MySLSB bean = ic.lookup("ejb:myapp/ejb//MySLSBBean!" + MySLSB.class.getName());
3.关闭范围限定的EJB客户端上下文:
查找ejb:string的根JNDI上下文以获取EJB命名上下文。然后使用ejbRootNamingContext实例来查找其余的EJB JNDI名称来获取EJB代理。使用close()方法来关闭ejbRootNamingContext和EJB JNDI上下文。关闭ejbRootNamingContext确保与JNDI上下文相关联的范围限定的EJB客户端上下文也被关闭。有效地,这将关闭与该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 = (MyBean)ejbRootNamingContext.lookup("app/module/distinct/bean!interface");
} 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
}
}
由查找EJB代理生成的上下文由此作用域上下文绑定,并且仅使用相关的连接参数。这使得可以创建不同的上下文来访问客户端应用程序中的数据,或者使用不同的登录来独立访问服务器。
在客户端中,作用域的InitialContext和作用域代理都传递给线程,允许每个线程使用给定的上下文。也可以将代理传递给可以同时使用的多个线程。
范围上下文EJB代理在远程调用上被序列化,然后在服务器上反序列化。当它被反序列化时,范围上下文信息被删除,并且返回到其默认状态。如果在远程服务器上使用反序列化代理,因为它不再具有在创建时使用的作用域上下文,这可能会导致EJBCLIENT000025错误,或者可能通过使用EJB名称来调用不需要的目标。
概要
下表列出了可以通过编程方式或jboss-ejb-client.properties文件中配置的属性。
EJB客户端全局属性
下表列出了在同一范围内对整个库有效的属性。
表5.1 Global 属性
Property Name | Description |
endpoint.name | 客户终端的名称。如果未设置,则默认值为客户机端点。 这可能有助于区分不同的端点设置,因为线程名称包含此属性。 |
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED | 指定是否为所有连接启用SSL协议的布尔值。警告 Red Hat建议在所有受影响的程序包中明确禁用SSLv2,SSLv3和TLSv1.0,以支持TLSv1.1或TLSv1.2。 |
deployment.node.selector | org.jboss.ejb.client.DeploymentNodeSelector的完整限定名称。 这用于负载平衡EJB的调用。 |
invocation.timeout | EJB握手或方法调用请求/响应周期的超时。值以毫秒为单位。 如果执行时间超过超时时间,任何方法的调用将抛出java.util.concurrent.TimeoutException异常。执行完成,服务器不中断。 |
reconnect.tasks.timeout | 背景重新连接任务的超时。值以毫秒为单位。 如果多个连接断开,则下一个客户端EJB调用将使用算法来确定是否需要重新连接来查找正确的节点。 |
org.jboss.ejb.client.scoped.context | 指定是否启用作用域EJB客户机上下文的布尔值。默认值为false。 如果设置为true,则EJB Client将使用与JNDI上下文相关联的作用域上下文。否则,EJB客户端上下文将使用JVM中的全局选择器来确定用于调用远程EJB和主机的属性。 |
表5.2 连接属性
Property Name | Description |
remote.connections | 活动连接名称的逗号分隔列表。 每个连接都使用此名称进行配置。 |
remote.connection.CONNECTION_NAME.host | 连接的主机名或IP。 |
remote.connection.CONNECTION_NAME.port | 连接端口。 默认值为8080。 |
remote.connection.CONNECTION_NAME.username | 用于验证连接安全性的用户名。 |
remote.connection.CONNECTION_NAME.password | 用于验证用户的密码。 |
remote.connection.CONNECTION_NAME.connect.timeout | 初始连接的超时时间。 之后,重新连接任务将定期检查连接是否可以建立。 值以毫秒为单位。 |
remote.connection.CONNECTION_NAME.callback.handler.class | 完全限定名称的CallbackHandler类。 它将用于建立连接,只要连接打开,就不能更改。 |
remote.connection.CONNECTION_NAME.channel.options.org.jboss.remoting3.RemotingOptions.MAX_OUTBOUND_MESSAGES | 指定最大出站请求数的整数值。 默认值为80。 从客户机(JVM)到服务器只有一个连接来处理所有的调用。 |
remote.connection.CONNECTION_NAME.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS | 确定凭据是否必须由客户端提供以连接成功的布尔值。 默认值为true。 如果设置为true,客户端必须提供凭据。 如果设置为false,只要远程连接器不请求安全域,就允许调用。 |
remote.connection.CONNECTION_NAME.connect.options.org.xnio.Options.SASL_DISALLOWED_MECHANISMS | 禁用在连接创建期间用于认证的某些SASL机制。 JBOSS-LOCAL-USER表示当客户机和服务器位于同一台机器时使用的静默认证机制被禁用。 |
remote.connection.CONNECTION_NAME.connect.options.org.xnio.Options.SASL_POLICY_NOPLAINTEXT | 在验证期间启用或禁用纯文本消息的布尔值。 如果使用JAAS,则必须将其设置为false以允许纯文本密码。 |
remote.connection.CONNECTION_NAME.connect.options.org.xnio.Options.SSL_ENABLED | 指定是否为此连接启用SSL协议的布尔值。 警告 Red Hat建议在所有受影响的程序包中明确禁用SSLv2,SSLv3和TLSv1.0,以支持TLSv1.1或TLSv1.2。 |
remote.connection.CONNECTION_NAME.connect.options.org.jboss.remoting3.RemotingOptions.HEARTBEAT_INTERVAL | 间隔在客户端和服务器之间发送心跳以防止自动关闭,例如,在防火墙的情况下。 值以毫秒为单位。 |
表5.3。 群集属性
Property Name | Description |
remote.cluster.CLUSTER_NAME.clusternode.selector | org.jboss.ejb.client.ClusterNodeSelector的完整限定名称。 此类,而不是org.jboss.ejb.client.DeploymentNodeSelector用于在集群环境中负载平衡EJB调用。 如果集群完全关闭,则调用将失败,并显示消息No ejb receiver可用。 |
remote.cluster.CLUSTER_NAME.channel.options.org.jboss.remoting3.RemotingOptions.MAX_OUTBOUND_MESSAGES | 指定可以对整个集群进行的最大出站请求数的整数值。 |
remote.cluster.CLUSTER_NAME.node.NODE_NAME. channel.options.org.jboss.remoting3.RemotingOptions.MAX_OUTBOUND_MESSAGES | 指定可以对此特定集群节点进行的最大出站请求数的整数值。 |
以前版本的JBoss EAP包括一个功能,其中包含EJB协议消息的消息流可以被压缩。 此功能已包含在JBoss EAP 6.3及更高版本中。
注意
压缩目前只能通过应该在客户端和服务器端的EJB接口上的注释来指定。 当前没有等效的XML来指定压缩提示。
数据压缩提示可以通过JBoss注释org.jboss.ejb.client.annotation.CompressionHint来指定。 提示值指定是否压缩请求,响应或请求和响应。添加@CompressionHint默认为compressResponse= true和compressRequest = true。
可以在接口级别指定注释以应用于EJB接口中的所有方法,如:
import org.jboss.ejb.client.annotation.CompressionHint;
@CompressionHint(compressResponse = false)
public interface ClassLevelRequestCompressionRemoteView {
String echo(String msg);
}
或者注释可以应用于EJB接口中的特定方法,如:
import org.jboss.ejb.client.annotation.CompressionHint;
public interface CompressableDataRemoteView {
@CompressionHint(compressResponse = false, compressionLevel = Deflater.BEST_COMPRESSION)
String echoWithRequestCompress(String msg);
@CompressionHint(compressRequest = false)
String echoWithResponseCompress(String msg);
@CompressionHint
String echoWithRequestAndResponseCompress(String msg);
String echoWithNoCompress(String msg);
}
上面显示的compressionLevel设置可以具有以下值:
*BEST_COMPRESSION
*BEST_SPEED
*DEFAULT_COMPRESSION
*NO_COMPRESSION
compressionLevel设置默认为Deflater.DEFAULT_COMPRESSION。
使用方法级覆盖的类级别注释:
@CompressionHint
public interface MethodOverrideDataCompressionRemoteView {
@CompressionHint(compressRequest = false)
String echoWithResponseCompress(final String msg);
@CompressionHint(compressResponse = false)
String echoWithRequestCompress(final String msg);
String echoWithNoExplicitDataCompressionHintOnMethod(String msg);
}
在客户端确保将org.jboss.ejb.client.view.annotation.scan.enabled系统属性设置为true。 该属性告知JBoss EJB Client扫描注释。
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=localhost
remote.connection.default.port=8080
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
默认连接器
默认连接器是http-remoting。
*如果客户端应用程序使用来自JBoss EAP 6的EJB客户端库,并且想要连接到JBoss EAP 7服务器,则服务器必须配置为在8080以外的端口上公开远程连接器。然后客户端必须使用新配置的连接器。
*使用JBoss EAP 7的EJB客户端库并想要连接到JBoss EAP 6服务器的客户端应用程序必须知道服务器实例不使用http-remoting连接器,而是使用远程连接器。这是通过定义一个新的客户端连接属性来实现的。remote.connection.default.protocol=remote
注意
仅JBoss EAP 6支持EJB远程调用。
除了EJB客户端远程处理互操作性,您还可以使用以下选项连接到旧客户端:
*在JBoss EAP配置指南中为JTS事务配置ORB。
JBoss EAP支持基于对JBoss EAP部署的EJB的基于CORBA/ IIOP的访问。
<iiop>元素用于启用IIOP,CORBA,调用EJB。这个元素的存在意味着安装了iiop-openjdk子系统。 <iiop>元素包括以下两个属性:
*enable-by-default:如果这是真的,那么所有EJB 2.x home接口的EJB都通过IIOP公开。否则,它们必须通过jboss-ejb3.xml显式启用。
*use-qualified-name:如果这是true,那么EJB将绑定到CORBA命名上下文,其绑定名称包含部署的应用程序和模块名称,如myear / myejbjar / MyBean。如果这是false,那么默认绑定名称就是bean的名称。
重要
IIOP调用只能使用EJB 2 bean完成。 JBoss EAP 7.0中的IIOP不支持EJB 3 bean。
启用IIOP
要启用IIOP,您必须安装IIOP OpenJDK ORB子系统,并且ejb3子系统配置中存在<iiop />元素。发行版附带的standalone-full.xml配置都启用了这两个配置。
IIOP在服务器配置文件的iiop-openjdk子系统中配置。<subsystem xmlns="urn:jboss:domain:iiop-openjdk:1.0">
使用以下管理CLI命令访问和更新iiop-openjdk子系统。
/subsystem=iiop-openjdk
IIOP元素具有两个控制服务器默认行为的属性。
<subsystem xmlns="urn:jboss:domain:ejb3:1.2">
...
<iiop enable-by-default="false" use-qualified-name="false"/>
...
</subsystem>
以下管理CLI命令在ejb3子系统下添加<iiop>元素:
/subsystem=ejb3/service=iiop:add(enable-by-default=false, use-qualified-name=false)
创建使用IIOP进行通信的EJB
以下示例演示如何从客户端进行远程IIOP调用:
1.在服务器上创建一个EJB 2 bean:@Remote(IIOPRemote.class)
@RemoteHome(IIOPBeanHome.class)
@Stateless
public class IIOPBean {
public String sayHello() throws RemoteException {
return "hello";
}
}
2. 创建一个home实现,它具有强制方法create()。 此方法由客户端调用以获取远程接口的代理来调用业务方法:
public interface IIOPBeanHome extends EJBHome {
public IIOPRemote create() throws RemoteException;
}
3. 创建用于远程连接到EJB的远程接口:
public interface IIOPRemote extends EJBObject {
String sayHello() throws RemoteException;
}
4. 通过在META-INF中创建一个描述符文件jboss-ejb3.xml来引入远程调用的bean:
<?xml version="1.0" encoding="UTF-8"?>
<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:iiop="urn:iiop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd
http://java.sun.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-spec-2_0.xsd
urn:iiop jboss-ejb-iiop_1_0.xsd"
version="3.1"
impl-version="2.0">
<assembly-descriptor>
<iiop:iiop>
<ejb-name>*</ejb-name>
</iiop:iiop>
</assembly-descriptor>
</jboss:ejb-jar>
注意
打包的bean以及JAR文件中的描述符现在可以部署到JBoss EAP容器。
5.在客户端创建一个上下文:
System.setProperty("com.sun.CORBA.ORBUseDynamicStub", "true");
final Properties props = new Properties();
props.put(Context.PROVIDER_URL, "corbaloc::localhost:3528/JBoss/Naming/root");
props.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.iiop.naming:org.jboss.naming.client");
props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory");
props.put(Context.OBJECT_FACTORIES, "org.jboss.tm.iiop.client.IIOPClientUserTransactionObjectFactory");
注意
客户端将需要将wildfly iiop openjdk库添加到其类路径中。客户端还可能需要将org.wildfly:wildfly-iiop-openjdk工件添加为Maven依赖关系。
6. 使用上下文查找缩小对IIOPBeanHome主界面的引用。 然后调用home接口create()方法来访问远程接口,这样就可以调用它的方法:
try {
Context context = new InitialContext(props);
final Object iiopObj = context.lookup(IIOPBean.class.getSimpleName());
final IIOPBeanHome beanHome = (IIOPBeanHome) PortableRemoteObject.narrow(iiopObj, IIOPBeanHome.class);
final IIOPRemote bean = beanHome.create();
System.out.println("Bean saying: " + bean.sayHello());
} catch (Exception e) {
e.printStackTrace();
}