j2ee专题

怎样从单立的 java 客户程序访问远程 EJB?

步骤1. 在您的代码中使用没有参数的 InitialContext() 构造方法。

 

开发员碰到的最常见的问题就是往 InitialContext(args) 中传入特定的 JNDI 引导(bootstrapping)属性。那些访问 Java EE 服务的单立的 java 客户程序,从本质上说就不是可移植的,所以每个 Java EE 产品都有不同的要求,怎样引导名称服务提供者。(更多关于 可移植的客户程序的信息,请看 这里)。我们已经的一个 jndi.properties 文件放在了 appserv-rt.jar 中,这样开发员就不需要硬编码特定的 JNDI 引导属性。在使用无参数构造方法 InitialContext() 时,J2SE 中的JNDI 机制会自动检测到这个文件,并启动正确的名称服务提供者。

步骤2.  把远程 EJB 的全局 JNDI 名称传入 InitialContext.lookup()

单立的 java 客户程序无法使用组件的命名环境 (java:comp/env) 或者 @EJB 注释,所以它们必须明确地使用全局 JNDI 名称来查找远程 EJB。(关于怎样分配全局名称给 EJB,请参看 这里)。假设远程 EJB 的全局名称是 "FooEJB" :

针对 EJB 2.1 和更早的 session/entity bean:

  InitialContext ic = new InitialContext();
  Object homeObj = ic.lookup("FooEJB");
  FooHome fooHome = (FooHome) PortableRemoteObject.narrow(homeObj, FooHome.class);  
  Foo foo = fooHome.create(...)
  ...

 针对有远程业务接口的 EJB 3.0 Bean:

  InitialContext ic = new InitialContext();
  Foo foo = (Foo) ic.lookup("FooEJB");

请注意,在 EJB 3.0 中查找的结果可被直接转型成远程业务接口,不需要使用 PortableRemoteObject.narrow()。

步骤3. 把 appserv-rt.jar 和 javaee.jar 加入 java 客户程序的类路径。

在步骤 1 中讲到,你需要 appserv-rt.jar 在我们的应用服务器中正确地启动命名服务提供者。javaee.jar 包含了Java EE 5 中的 API 类。例如,假设应用程序类在 /home/user1/myclasses,客户程序主类是 acme.MyClient:

  java -classpath $APS_HOME/lib/appserv-rt.jar:$APS_HOME/lib/javaee.jar:/home/user1/myclasses acme.MyClient

步骤4.  如果必要的话,设置服务器主机属性:

如果单立的 java 客户程序和服务器运行在不同的主机上,在客户端 JVM 设置系统属性 -Dorg.omg.CORBA.ORBInitialHost。例如,假设服务器运行在主机 com.acme.Host1:

  java -Dorg.omg.CORBA.ORBInitialHost=com.acme.Host1
         -classpath $APS_HOME/lib/appserv-rt.jar:$APS_HOME/lib/javaee.jar:/home/user1/myclasses acme.MyClient

该属性的默认值是 localhost,所以如果客户端的服务器运行在同一个主机上,就不需要设这个属性。

步骤5.  如果必要的话,设置命名服务端口属性:

应用服务器中默认的命名服务端口是 3700。 如果命名服务运行在别地端口,你就需要在启动客户端 JVM 时设这个系统属性:-Dorg.omg.CORBA.ORBInitialPort。如果要核查一个服务器实例上的命名服务端口,只需看一下该服务器实例的 domain.xml 中的 "orb-listener-1" 元素。或者也可以在 Amin GUI 中查看 Applications --> Configuration --> ORB --> IIOP Listeners --> orb-listener-1。

假设服务器的命名服务运行在端口 9876,客户端和服务器运行在同一个主机上:

java -Dorg.omg.CORBA.ORBInitialPort=9876
         -classpath $APS_HOME/lib/appserv-rt.jar:$APS_HOME/lib/javaee.jar:/home/user1/myclasses acme.MyClient

单立的 java 客户程序是不是可移植的?它和应用程序客户机(Application Client)组件有什么区别?

Java EE 平台专门定义、设计了一种组件,从应用服务器以外的 JVM 来可移植地访问 Java EE 服务。它叫作 Java EE 应用程序客户机(Java EE Application Client)。它自从 J2EE 首次发布(J2EE 1.2)就是 J2EE 开台的一部分。   和其它 Java EE 组件一样,它也是运行在开发商实现提供的容器中。应用程序客户机的主要优势是可移植性,让你使用与 web 和 ejb 组件相同的编程模式来定义、访问资源。它沿袭了 Java EE 平台的一贯思路,那就是系统层次的工作,或 "plumbing" ,应该尽可能让容器来实现,而不应该成为应用程序一部分。就也就意味着,可以放心使用无参数的InitialContext 构造方法,和一个私有的组件命名上下文 (java:comp/env) ,而且在 Java EE 5 中还可以使用平台定义的注释和注射。

开发员面临的一个共同的问题就是如何在一个访问 EJB 的客户中初始化命名上下文。如果该客户没有写成应用程序客户机,那它就被叫作单立的 Java 客户程序。这些单立的 java 客户程序本质上就不是可移植的,所以每个开发商都定义了自己的方法来引导启动命名服务。这不仅让写客户程序更困难,而且在不同的 Java EE 实现之间移植应用时也会有问题。

和所有的 Java EE 组件一样,需要一些额外的步骤来实现应用程序客户机所提供的可移植性。例如,定义一个部署描述符(deployment descriptor),包装应用客户.jar 文件,以及学习运行应用程序客户机容器。不过,这些步骤在 Java EE 5 和 SUN 公司的 Java EE 5 实现中已经被简化了。

关于应用程序客户机的更多细节,请看:

Java EE 5 tutorial 第22 章 (开始学习 EJB)

简单的 EJB 3.0 session / message-driven bean 源代码示例

我们的开发员指南中的客户章节

从 java 客户程序访问 EJB 是不是需要 RMI stubs?

在我们的 J2EE 1.4 SDK 和 Java EE 5 SDK 实现中 (Sun Java System Application Server 8.x, 9.x) 不需要。我们的实现中用到了动态 RMI-IIOP 的性能,它能在运行时产生所需要的 RMI stub,应用程序完全不知道这些细节。这使得部署起来更快,而且避免了许多把静态 stub 加到客户中引起的配置问题。不过,动态 RMI-IIOP 性能只有当客户使用应用服务器的命名服务提供者时才生效。如果客户是单立的 java 程序,那就要按 这里的步骤,或改用应用程序客户机。

如果已有一个单立的 java 客户程序,通过 CosNaming JNDI provider 来访问 EJB,那应该怎么办?  怎样得到静态的 RMI-IIOP stubs ?

首先看一下 可移植的 Java EE 客户"怎样写一个单立的客户"。 最好的选择是把客户改成应用程序客户机(Application Client),或者按照我们的建议写单立的 java 客户程序。如果不可能这样做,你可以在部署时要求产生静态 RMI-IIOP stub。你需要在运行 asadmin deploy 命令时用--generatermistubs 选项。在这种情况下,静态 RMI-IIOP stub 会被放在 client.jar 文件里,例如:

  asadmin deploy --generatermistubs --retrieve /home/user1/clientstubdir  fooapp.ear

部署命令运行后,包含静态 RMI-IIOP stub 的 client.jar 会被放在 /home/user1/clientstubdir/fooappClient.jar。

在这种情况下仍然需要静态 RMI-IIOP stub 的原因是,当实例化 CosNaming 提供者时,并没有用到我们应用服务器中的客户命名服务提供者。它用到了 J2SE 中的 ORB, 而它是不支持动态 RMI-IIOP 的。

我有一个具有本地接口的 EJB,能不能从应用程序客户机(Application Client)或单立的 java客户程序访问它?

不能。EJB 本地视图是一种优化了的调用途径,它使用了引用调用语意(call-by-reference semantics)。它只适用于那些和目标 EJB 在同一个应用中的 web 组件和  ejb 组件。这就是为什么 application-client.xml schema 中没有 ejb-local-ref 的原因。  要想从应用程序客户机或单立 java 程序中访问 EJB,你必需用 EJB 3.0 远程业务接口,EJB 2.x Home 接口,或者 web 服务。

我有一个具有本地接口的 EJB,能不能从另外一个应用的 WEB 组件来访问它?

在我们的服务器中不行。EJB 技术规范只要求支持同一个 JVM 中的同一个应用可访问本地 EJB。

全局 JNDI 名称是怎样分配到 Session / Entity beans 的?    

首先,全局 JNDI 名只适用于具有某种远程接口的 Session/Entity bean。如果 Session/Entity bean 只有一些本地接口或 Web 服务接口,那么全局JNDI 名就不适用。在这种情况下,任何指明的全局 JNDI  都会被忽略。

在我们的 J2EE 1.4 实现中 (应用服务器 8.x) ,只有一种方法把全局 JNDI 名分配给一个 session/entity bean 的远程视图:

在 sun-ejb-jar.xml 中的 <ejb> 元素中把 bean 的 <ejb-name> 映射到 <jndi-name>,例如:

<sun-ejb-jar>
  <enterprise-beans>
    <ejb>
      <ejb-name>FooBean</ejb-name>
      <jndi-name>FooEJB</jndi-name>
    </ejb>
  </enterprise-beans>
</sun-ejb-jar>

在我们的 Java EE 5 实现中 (应用服务器 9.x),有四种方法来为 session/entity bean 分配全局 JNDI 名。  按照它们的优先次序(最优先的在前),分别是:

1. 使用 sun-ejb-jar.xml (和上面一样)

2. 在 ejb-jar.xml 中用 mapped-name 元素指明 bean 的全局 JNDI 名

<enterprise-beans>
  <session>
    <ejb-name>FooBean</ejb-name>
    <mapped-name>FooEJB</mapped-name>
    ...
  </session>
</enterprise-beans>

3. 在 @Stateless/@Stateful 注释的 mappedName() 属性中指明 bean 的全局 JNDI 名,例如:

@Stateless(mappedName="FooEJB")
public class FooBean implements Foo { ... }

4. 如果没有指明全局 JNDI 名称,服务器会根据下表产生一个默认的全局 JNDI 名:

Bean 有没有 EJB 2.x 的 Home/Remote 接口
EJB 3.0 远程业务接口的总个数默认 JNDI 名
例子

0
不适用
不适用

0
Home 接口的全限定名
com.acme.FooHome
1
远程业务接口的全限定名com.acme.FooBusiness
2 或更多
没有默认值 -- 必须指明 JNDI 名
不适用

1 或更多
没有默认值 -- 必须指明 JNDI 名 不适用


怎样指定一个消息驱动 Bean 应使用的队列(Queue)和主题(Topic)?

J2EE 1.4 实现中 (应用服务器 8.x) :

在 sun-ejb-jar.xml 中,把这个 bean 的 <ejb-name> 映射到队列或主题的全局名称:

<sun-ejb-jar>
  <enterprise-beans>
    <ejb>
      <ejb-name>FooMessageBean</ejb-name>
      <jndi-name>jms/NotificationQueue</jndi-name>
    </ejb>
  </enterprise-beans>
</sun-ejb-jar>

在我们的 Java EE 5 实现中 ( 应用服务器 9.x),有三种方法要用。按照它们的优先次序(最优先的在前),分别是:

1. 使用 sun-ejb-jar.xml (和上面一样)

2. 在 ejb-jar.xml 中用 mapped-name 元素来指明队列或主题的全局名称:

<enterprise-beans>
  <message-driven>
    <ejb-name>FooMessageBean</ejb-name>
    <mapped-name>jms/NotificationQueue</mapped-name>
    ...
  </message-driven>
</enterprise-beans>

3. 用 @MessageDriven mappedName() 属性来指明队列或主题的全局名称。例如:

@MessageDriven(mappedName="jms/NotificationQueue")
public class FooMessageBean implements javax.jms.MessageListener { ... }

我有一个 EJB 3.0 Session bean 具有多个远程业务接口。从单立的 java客户程序,该怎样查找一特定的远程业务接口? 

可以查找每一个远程业务接口,使用的名称是把目标 EJB 的全局 JNDI 名称和特定的远程业务接口合并起来,以 "#" 分隔开。例如,如果这个Session Bean 的 sun-ejb-jar.xml 是这样:

<sun-ejb-jar>
  <enterprise-beans>
    <ejb>
      <ejb-name>FooBean</ejb-name>
      <jndi-name>FooEJB</jndi-name>
    </ejb>
  </enterprise-beans>
</sun-ejb-jar>

而且它有两个远程业务接口 { com.acme.FooBusiness1, com.acme.FooBusiness2 },那么单立的 java 客户程序就应该这样查找每个远程业务接口:

   InitialContext ic = new InitialContext();
   FooBusiness1 bean1 = (FooBusiness1) ic.lookup("FooEJB#com.acme.FooBusiness1");
   FooBusiness2 bean2 = (FooBusiness2) ic.lookup("FooEJB#com.acme.FooBusiness2");

请注意,一个 bean 一般只有一个远程业务接口,这种全限定名称是不需要的。在这种情况下就可以直接使用这个 Bean 的 JNDI 名:

   FooBusiness bean = (FooBusiness) ic.lookup("FooEJB");
glassfish的InitialContext():
Accessing a Remote EJB from a non-Java EE web container is similar to the stand-alone java client case.    However, the complication is that most Java web servers set the default JNDI name provider for the JVM, which prevents our appserver naming provider from being instantiated when the application uses the no-arg InitialContext() constructor.   The solution is to explicitly instantiate an InitialContext(Hashtable) with the properties for our naming provider, as contained in appserv-rt.jar's jndi.properties file.  

Step 1.  Instantiate the InitialContext 

    Properties props = new Properties();
    props.setProperty("java.naming.factory.initial",
                             "com.sun.enterprise.naming.SerialInitContextFactory");
    props.setProperty("java.naming.factory.url.pkgs",
                             "com.sun.enterprise.naming");
    props.setProperty("java.naming.factory.state",
                             "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");

    // optional.  Defaults to localhost.  Only needed if web server is running
    // on a different host than the appserver   
    props.setProperty(" org.omg.CORBA.ORBInitialHost", "localhost ");

    // optional.  Defaults to 3700.  Only needed if target orb port is not 3700.
    props.setProperty(" org.omg.CORBA.ORBInitialPort", "3700");

    InitialContext ic = new InitialContext(props);
另外加上四个文件到库appserv-rt.jar,appserv-ext.jar,appserv-deployment-client.jar,javaee.jar
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值