您的位置:首页 > 其它

为无状态 Session Bean 部署一个简单的客户端

2010-04-25 18:36 381 查看
在 WebSphere 上部署了无状态会话 Bean 之后,一般我们用 J2EE Application Client 来访问它,但客户端需要 WebSphere J2EE Application Client 运行时库的支持,要求在客户端安装或复制了 WebSphere 客户端库,还有一大堆连接参数需要配置,像我们多数情况下客户只负责收集参数并把服务器的返回值显示在界面上,我们并没有使用多数高级的内容,因此想弄一个最简单的客户端连接器。

通过对比 WebSphere 的 JNDI InitialConextFactory 实现发现,原来我们只需要用 Sun JDK 自带的 JNDI 实现。我们用 CNCtxFactory 作为

Initial Context factory (并使用 IIOP URL 协议访问)。关于 JNDI SPI 及连接参数配置等可以看这里另外一篇文章 <<JNDI 备忘录 -回答论坛的一个问题>>。

这里列出如何连接到 WebSphere Naming Context,其实熟悉 JNDI 的话,这就很简单了,我们只需要向 InitialContext 初始化的参数列表的两个参数换成支持 IIOP 协议的实现就可以了,相应 iiop://192.168.1.10:2809 这样的 URL 做过 WebSphere 上开发部署的人很熟悉。

Properties config = new Properties();

config.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory"); // step 1

config.setProperty(Context.PROVIDER_URL, "iiop://192.168.1.10:2809"); // step 2

InitialContext context = new InitialContext(config); // step 3.

那现在我们来个 context.lookup("ejb/hello/jboss/ejb2/session/HelloSessionHome") 是不是就可以了呢?很不幸,答案是否定的,为了能让我们的代码工作,首先我们需要对 WebSphere 的 naming 实现有初步的了解。在 WebSphere 中,服务器管理有 Cell / Node / Server 三个级别的管理单位,平时我们开发时都不需要了解这些,对于生产环境来说就有讲究了,比如要构建集群或一个服务器远程管理该 Cell 里面的所有 server,在 WebSphere 中我们的组件会部署在一个 server 上,但并不是所有情况下都这么简单,因为在 WebSphere 上在查询我们的 EJB 组件时,我们也需要指定一个起始范围节点(比如 server1)。。通过我们在 J2EE 组件(比如 Session Bean) 中查找一个组件引用或资源引用时,我们使用 new InitialContext().lookup() 是相对当前 Context Root,这个 Context Root 是当前发起请求的组件所部署的 server root。而对于瘦客户端从其它机器或进程发起请求的话,我们需要明确指定Context Root,了解下面2种情况:

当我们把组件部署到单一的服务器时,我们使用 cell/nodes/$nodeName/servers/$serverName 作为 context root 查找,EJB 组件在J2EE应用程序被应用服务器启动时也是用这个 context root 来绑定的,也就是说这个 EJB 的全局 JNDI 名是:
cell/nodes/$nodeName/servers/$serverName/ejb/hello/jboss/ejb2/session/HelloSessionHome

当我们把组件部署到集群中时,我们使用 cell/clusters/$clusterName 作为 context root 来找。

下面这张图中展示了如何查看 WebSphere Naming Tree。我们看到里面有 cell 为主节点的一个树型结构,像我们一个应用程序的 EJB 会话 bean 等组件在开发测试时通常是部署在像 server1 这样的节点上的。那么我们的 EJB 组件默认情况下部署在 server1 上时我们需要先跳到指定的起始节点,然后再来 lookup 我们的 EJB 的 JNDI,如果使用下图中的部署参数,我们跳到 server 节点需要用下面这个代码,其中 localhost 是 node 名,server1 是 server 名,这两个会因部署环境可能不同,WebSphere Test Environemtn 一般默认如此:

// ... 参数配置省略。

InitialContext context = new InitialContext(config);

Context start = (Context) context.lookup("cell/nodes/localhost/servers/server1"); //跳到指定起始节点。

// ... 现在我们可以像一般的远程访问 EJB 一样的操作了。

不过,这里需要说明,J2EE Application Client 中可以为远程 EJB 创建一个 EJB Reference,然后给一个短名。我们现在未使用 J2EE Application Client 容器,因此我们不用直接使用 EJB Reference 中指定的 ejb/HelloSessionHome 这样的短名,而是使用全局 JNDI 名。我们通过上面的 naming 起始节点等与服务器相关的参数写到一个 properties 文件中,就可以把上面这部分代码封装成一个 Service Locator,这样的话,在外面的应用程序看来就像在 Application Client 中一样的使用。



部署:

下面给出了完整的 JNDI 方式访问远程 WebSphere 中的 EJB组件的代码,同时也给了一份直接使用 CORBA 来访问的,其实 CNCtxFactory 也是使用 CORBA 来访问,只是被包装成 JNDI 接口。使用这段代码,我们部署一个客户就只有下面相当简单的步骤了:

1. 像普通 java 命令行应用程序,把需要的 jar 包,外加一个 j2ee.jar 取代以前完整的 J2EE Application Client Runtime 库(不需要把整个 WebSphere Application Client Runtime Library 全部拷贝到客户机,也不用在客户机上安装 WebSphere)。

2. 开发时为EJB 项目创建一个 EJB Client 项目,就可以把服务端和客户端分离开,部署时就可以选择客户端只复制 EJB Client 包。

开发工具(比如 WSAD) 在部署 EJB 组件到 WebSphere 时会为我们生成 RMI Stub 代码,它会放到 EJB Home/Remote 接口所在的项目的源文件夹中,如果有 EJB Client,这些代码就会放到 EJB Client 项目中,EJB 实现代码和调用 EJB 应用的程序就依赖 EJB Client 项目而不是 EJB 实现类所在的项目了。

运行这个示例程序,我只需要下面图中的资源,注意,其中作为客户端程序,我只用到 EJB 接口和部署生成的 Stub 之类的代码。不需要添加 websphere app client 启动时的一大堆参数和类库路径,请看图中的 jar 只需要 3 个,命令行很简单。

总结:

这里尝试不使用 WebSphere 等应用服务器提供的 J2EE Application Client 运行时库和容器而是使用 CORBA 或直接使用 JNDI 的 CNCtxFactory ( IIOP 协议) 来获得 EJB Home 接口,我们的目标是绕过 Application Client 运行时容器所伴随而来的复杂的部署过程和环境依赖,同时也可以用同样的方法连接其它厂商的应用服务器 (比如:JBOSS ),我们仅需要把这个 StandaloneJNDIClient 类来把 JNDI 连接参数作一些改动便可以使用了。EJB 2.0 的时候 RMI 协议已经有了与 CORBA 兼容的版本,支持 J2EE 1.3 (包括了 EJB 2.0) 的应用服务器应该都支持 CORBA,其中EJB 2.0 Local EJB 要求 CORBA 2.3 value type 的支持,尤其是当 Java EE 5.0 中把 ORB 作为标准化组件放入容器提供服务之后。为了达到这个目标,我们并不需要太复杂的技术,从学习成本上看,学习一个厂商的应用服务器提供的 Application Client 的部署过程和了解一下这个应用服务器的 naming 管理相比并没有多少的差别,部署过程要做的工作却简单了很多。



直接使用 JNDI 访问:

/*

* Created on 2010-1-31

* @author daniel.yang@atreides.vicp.net

*/

package hello.was.client;

import hello.jboss.ejb2.session.HelloSession;

import hello.jboss.ejb2.session.HelloSessionHome;

import java.util.Properties;

import javax.naming.Context;

import javax.naming.InitialContext;

import javax.rmi.PortableRemoteObject;

/**

* @author daniel.yang@atreides.vicp.net

*/

public class StandaloneJNDIClient {

public static void main(String[] args) {

Properties config = new Properties();

String jndi = "ejb/hello/jboss/ejb2/session/HelloSessionHome";

config.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory");

config.setProperty(Context.PROVIDER_URL, "iiop://192.168.1.10:2809");

try {

InitialContext context = new InitialContext(config);

Context start = (Context) context.lookup("cell/nodes/localhost/servers/server1");

Object objectRef = start.lookup(jndi);

HelloSessionHome home = (HelloSessionHome) PortableRemoteObject.narrow(objectRef, HelloSessionHome.class);

HelloSession service = home.create();

String echo = service.greet("Daniel");

System.out.println("Echo from server : " + echo);

} catch (Exception e) {

e.printStackTrace();

}

}

}

直接使用 CORBA 访问:

/*

* Created on 2009-9-4

*/

package hello.was.client;

import hello.jboss.ejb2.session.HelloSession;

import hello.jboss.ejb2.session.HelloSessionHome;

import java.util.Properties;

import javax.rmi.PortableRemoteObject;

import org.omg.CORBA.ORB;

import org.omg.CosNaming.Binding;

import org.omg.CosNaming.NameComponent;

import org.omg.CosNaming.NamingContextExt;

import org.omg.CosNaming.NamingContextExtHelper;

import org.omg.CosNaming.NamingContextPackage.CannotProceed;

import org.omg.CosNaming.NamingContextPackage.InvalidName;

import org.omg.CosNaming.NamingContextPackage.NotFound;

/**

* @author Daniel Yeung@atreides.vicp.net

*/

public class StandaloneCORBAClient {

public static void main(String[] args) {

String[] init = new String[] { "-ORBInitailHost", "192.168.1.10", "-ORBInitialPort", "2809" };

Properties config = new Properties();

// 如果使用 IBM JRE 的话,用下面2行参数,如果是 Sun 公司的 JRE,把下面2行注释掉。

config.setProperty("org.omg.CORBA.ORBClass", "com.ibm.CORBA.iiop.ORB");

config.setProperty("org.omg.CORBA.ORBSingletonClass", "com.ibm.CORBA.iiop.ORB");

// JNDI: ejb/hello/jboss/ejb2/session/HelloSessionHome

NameComponent[] path =

new NameComponent[] {

new NameComponent("ejb", ""),

new NameComponent("hello", ""),

new NameComponent("jboss", ""),

new NameComponent("ejb2", ""),

new NameComponent("session", ""),

new NameComponent("HelloSessionHome", "")

};

try {

ORB orb = ORB.init(init, config);

org.omg.CORBA.Object objectRef = orb.resolve_initial_references("NameServiceServerRoot");

NamingContextExt namingService = NamingContextExtHelper.narrow(objectRef);

objectRef = namingService.resolve(path);

HelloSessionHome home = (HelloSessionHome)

PortableRemoteObject.narrow(objectRef, HelloSessionHome.class);

HelloSession service = home.create();

String echo = service.greet("Daniel");

System.out.println("Echo from server : " + echo);

} catch (NotFound e) {

e.printStackTrace();

} catch (CannotProceed e) {

e.printStackTrace();

} catch (InvalidName e) {

e.printStackTrace();

} catch (Exception e) {

e.printStackTrace();

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: