Glassfish 中 EJB 常见问题解答

来源:互联网 发布:mysql ngram 编辑:程序博客网 时间:2024/04/30 00:37

 

这里回答了一些关于在 SUN 公司的应用服务器内使用 Enterprise Java Beans (EJB)的问题。在这里还有更多资料。如果你有问题或评论,请发到 ejb@glassfish.dev.java.net.

[译者注] 原文:https://glassfish.dev.java.net/javaee5/ejb/EJB_FAQ.html  这里的内容适用于 Glassfish, JavaEE SDK 5, J2EE SDK 1.4, Sun Java System Application Server 8.x, 9.x。

EJB 客户

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

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

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

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

本地访问 EJB

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

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


全局 JNDI 名称

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

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

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



怎样从单立的 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");
原创粉丝点击