Web Service 和 J2EE |
作者:佚名 发布时间:2005-04-02 来源:不详
|
在前面的文章中,我们 解了一些高级的Web servic 全。本文中我们将开始研究
|
已经掌握了如何开发、配置和使 e概念,这其中包括状态Web ser Web service是如何在J2EE环境
|
用简单的 Web service。我们也了 vice、远程调用和Web service 安 中工作的。
|
当今,许多应用将三层 service几乎使它们被普遍 组件体系结构使得这个过程
|
业务逻辑作为标准的J2EE组件来 应用,同时这也提供了一种集成 变得相对容易。
|
实现。将这些组件用于SOAP Web 这些组件的简单机制。J2EE的标准
|
本文中,我们将解释如 Java Message Service (JM Beans(EJBs),这是因为EJB 方法也适用于其他的J2EE组
|
何使J2EE组件以Web service的 S)可靠地发送SOAP报文。我们将 s是业务逻辑实现中使用最为广 件,例如JDBC数据源和JMS队列
|
形式发布,同时也会演示如何应用 主要讨论Enterprise Java 泛的J2EE组件,当然所有演示过的 。
|
我们首先来概括一下有关J2EE平台的 用JNDI查找服务器方的J2EE组件。例如, 客户端代理。客户端在后面将使用这个代 发生在RMI上。
|
一些重要事实。传统上来说,J2EE客户端应用程序使 客户端应用程序查找JNDI中的EJB引用并收到一个EJB 理来访问EJB组件。所有的J2EE通信在正常情况下都
|
有两种通过SOAP访问J2EE资源的方法 在EJB周围创建Web service wrapper。该 接映射到单独EJB并且还需要J2EE 组件的 码实现透明集成的方法。这种方法的主要 service的形式动态发布。这种方法允许 有的基于SOAP的J2EE应用。
|
。我们首先来看看最显而易见的方法,这种方法可以 方法特别适合于这样的情况:Web service应用不直 附加信息。我们在第二个例子中将介绍一种以较少代 目标就是将现有的J2EE应用尽可能迅速的以Web 我们无须编写或者修改任何代码就可以高效的访问现
|
在这个演示样本中,我 无状态session bean:股票 应用。各种实现之间只存在 。这种方法要求所实现的We 到SOAP与 RMI之间的桥梁作 成适合EJB 组件的RMI 请求 J2EE资源。为了用该方法来 除孤立的有状态资源。
|
们将介绍EJB wrapper Web serv 报价EJB。该方法虽然很简单但 一些细微的区别,这些实现通常 b service包含一个或者多个现 用。客户端将SOAP请求到这个wr 。我们特别推荐将该方法主要用 访问有状态的资源,您将需要设
|
ice 方法,该方法可以访问简单的 是却在许多SOAP框架中得到了广泛 会与开发流程的自动化水平相吻合 有的J2EE 组件。而这个wrapper起 apper,然后由wrapper把请求转换 于诸如无状态Bean之类的无状态 置额外的生命周期服务来正确地删
|
注意:如果您还没有下载用来创建指 需要下载演示样本的源代码demo sources c:wasp_demo目录。指导实例中所提到的 得到的src子目录中找到。它们位于com.s 程序都位于bin子目录。尽管您并不需要 烈推荐您这样做。
|
导实例的软件,请参考30分钟创建WebService。您还 。 这里我们假设您已经把这个文件拷贝到 所有Java源代码都可以在解压缩文件demo sources后 ystinet.demos.包中。同样,演示样本中的所有脚本 下载和使用该软件来理解这些文章,但是我们还是强
|
额外的安装步骤:我们 website下载所需的软件) 行时间以便使用Sun J2EE R 目录中的 env.bat脚本程序
|
将在J2EE环境中使用Sun J2EE 1 。在安装完J2EE 1.3 RI之后, I。这里提到的配置可以通过修 来实现。首先将下面一行变为注
|
.3。(您可以从Sun的 Java 您需要配置WASP Web service 运 改WASP Advanced高级安装的Bin子 释(在该行开始之处添加rem):
|
set INSTALLATION_TYPE=standalone |
set INSTALLATION_TYPE=j2ee |
您需要修改位于c:was WASP_HOME和WASP_DEMO设定
|
p_demobin directory的env.ba 正确的取值。
|
t文件。请为环境变量J2EE_HOME,
|
一旦您完成了上面提到 J2EE 服务器并运行WASP We
|
的安装和配置步骤,请通过使用 b service。
|
startJ2EE 和 startserver启动
|
下一步,运行deploy_j2ee 来编译Ja
|
va源代码并配置我们在演示样本使用的EJBs。
|
注:在配置EJB之后,您需要重新启动J2EE 服务器。 |
如果您查看com.systinet.demos.sto StockQuoteHome类 和StockQuoteBean 类 状态session bean。我们已经通过调用de J2EE的管理工具对所有的EJBs进行了正确 动管理工具。
|
ck包中的Java源代码,就会发现StockQuote, 使用一个简单getQuote方法实现了一个相当简单的无 ploy 脚本程序对这个EJB进行了配置。您要确保使用 配置。调用演示样本bin目录下的J2EEAdmin程序将启
|
现在我们来集中关注图 法包含一个简单的EJB 调用 它将在EJB上调用方法 getQ 端。在下面的代码中您可以
|
2中wrapper Web service 的实 。首先,它从JNDI得到EJB的有 uote,然后删除该EJB。最后, 看到这些步骤:
|
现。它实现了方法getQuote,该方 效引用并创建一个EJB实例。然后 调用结果返回到Web service 客户
|
package com.systinet.demos.stock; |
import javax.naming.InitialContext; |
import javax.naming.Context; |
import javax.naming.NamingException; |
import java.rmi.RemoteException; |
public class StockQuoteService { |
public double ge
|
tQuote(String symbol) throws
|
Exception {
|
// get the JNDI initial context |
System.err.println("
|
;Getting J2EE initial context");
|
Context jndi
|
Context = new InitialContext
|
();
|
System.err.p
|
rintln("Looking up EJB
|
Home");
|
Object homeRef = jndiCon
|
text.lookup("Stock");
|
(StockQuoteH
|
ome)javax.rmi.PortableRemote
|
Object.narrow(
|
homeRef, StockQuoteHome.class); |
// create the EJB instance |
System.err.println("
|
;Creating EJB");
|
StockQuote ejb = home.create(); |
// call the getQuote method |
System.err.p
|
rintln("Calling getQuot
|
e");
|
double quote
|
= ejb.getQuote("SUNW&q
|
uot;);
|
System.err.println("
|
;SUNW "+quote);
|
System.err.p
|
rintln("Removing EJB&qu
|
ot;);
|
图 2:简单的 Web service EJB wrap
|
per (StockQuoteService.java)
|
现在我们可以通过运行deploy_serv 后,运行run_wrapper 脚本程序来启动We service来调用EJB。
|
ice 脚本程序来配置EJB wrapper Web services。然 b service 客户端。客户端将会通过wrapper Web
|
注:为了说明wrapper 的应用通常会复杂一些。wr 情况下,wrapper服务通常
|
方法的基本原来,我们尽可能使 apper 服务常用于集成多个EJBs 会显现不同的编程接口而不是原
|
这个演示样本简单。然而,实际中 的功能和其他的J2EE资源。在这些 始的beans。
|
访问J2EE资源的另一方 一个wrapper服务或者改变 通过因特网访问J2EE资源,
|
法就是使用透明的集成框架。这 最初的J2EE代码。如果您希望SO 这种方法就非常有用了。
|
里,透明是指我们没有必要来编写 AP客户端利用现有的J2EE资源或者
|
下面所描述的透明的J2EE 集成框架 问J2EE资源的抽象机制。正如我们先前所 的lookup方法,而客户端的JNDI提供者把 JNDI给客户端返回一个 J2EE代理。客户 个示例中,我们所使用的客户端JNDI的提 ,当客户端使用这个提供者发布一个JNDI 。 这个JNDI web service将在应用程序 JNDI web service将一个基于SOAP的客户 能够使用这个远程引用来调用J2EE资源。 代理会把请求重新定向到实际的J2EE资源 进行修改。只需在客户端实施一个配置更
|
将充分利用JNDI框架的优势,这种优势提供了一种访 说的,在J2EE的正常流程中,J2EE客户端将调用JNDI 这个请求通过RMI传递给J2EE服务器中的JNDI服务。 端使用这个代理,通过RMI远程调用J2EE资源。在这 供者支持SOAP而不支持RMI。正如您在图3中所看到的 请求时,该请求将通过SOAP发送到JNDI web service 服务器JNDI中进行实际的查找并获得J2EE代理。然后 端远程引用返回给J2EE代理。客户端应用程序此时就 每一种方法调用都将通过SOAP传递给J2EE代理。J2EE 。您可能会注意到J2EE资源或者客户端代码都不需要 改而已,即指向基于SOAP的JNDI提供者。
|
注:大多数Web service运行时间服 。因此,调用重定向方法将会非常迅速,
|
务器和应用程序服务器是在相同的上下文环境中运转 并且不会降低性能。
|
这种方法也适用于非Ja 任何的SOAP客户端都能够充 端可以调用JNDI Web servi
|
va的客户端。因为JNDI Web ser 分利用它的透明调用框架。例如 ce上的方法查询并且获得所请求
|
vice是一种标准的Web service, , Microsoft Visual Basic 客户 的J2EE资源的Web service代理。
|
对于在Web service运 集。当客户端应用程序显式 。但是在松散连接条件下的 要跟踪并管理所有动态创建 资源既快速又透明的SOAP访 无状态和有状态的session JDBC和其他的J2EE资源。对 合于通过SOAP对现有系统提
|
行时间创建的所有组件,JNDI W 丢弃远程组件时,根据相关请求 Web services并不能保证做到适 资源的原因。这种方法的主要优 问方式。这里所说的JNDI中的J2 beans、entity beans和 messag 这些J2EE资源无需进行任何修改 供快速而又简便的访问。我们来
|
eb service将执行自动远程碎片账 ,这些组件中的大多数也会被丢弃 当删除。这就是LifeCycle服务需 势就在于它提供了对JNDI中的J2EE EE资源包括所有类型的EJB组件 ( e-driven beans),还有JMS、 和包装。很显然,这种方法非常适 看一个具体的示例吧。
|
这个演示样本将说明一 EJB。
|
个Web service调用了运行在Sun
|
的 J2EE 1.3 引用实现引擎中的
|
我们首先来检验一下服 个标准的有状态session be
|
务器端的EJB代码。正如您在下 an,它将保存简单计算器的状态
|
面的代码列表中所看到的,这是一 。
|
package com.systinet.demos.counter; |
import javax.ejb.CreateException; |
import javax.ejb.SessionBean; |
import javax.ejb.SessionContext; |
import javax.ejb.Ses
|
sionSynchronization;
|
public class CounterEJB implemen
|
ts SessionBean {
|
private SessionContext context; |
* No argument c
|
onstructor required by conta
|
iner.
|
* Create method
|
specified in EJB 1.1 sectio
|
n 6.10.3
|
public void ejbCreate() { |
/* Methods required by Sessi
|
onBean Interface. EJB 1.1 section 6.5.1. */
|
* @see javax.ej
|
b.SessionBean#setContext(jav
|
ax.ejb.SessionContext)
|
public void setSessionContex
|
t(SessionContext context){
|
* @see javax.ej
|
b.SessionBean#ejbActivate()
|
public void ejbActivate() { |
* @see javax.ej
|
b.SessionBean#ejbPassivate()
|
public void ejbPassivate() { |
* @see javax.ejb.SessionBea
|
n#ejbRemove()
|
public void ejbRemove() { |
图 4: 简单的EJB计数器——服务器端EJB代码 |
其他的 EJB资源就相当 。我们已经在第一个演示样 们并不需要将任何特定的We
|
明显了。您可以看到Counter远 本中通过调用deploy_j2ee命令 b service配置为Web service
|
程和CounterHome本地接口的代码 ,并对EJB进行了配置。请注意我 运行时间。
|
客户端应用程序是一个标准的EJB客 使用了不同的JNDI属性。
|
户端,不同之处就是它在方法getInitialContext中
|
package com.systinet.demos.counter; |
import javax.naming.InitialContext; |
import javax.naming.Context; |
import javax.naming.NamingException; |
import java.rmi.RemoteException; |
public class CounterClient { |
public static void main(Strin
|
g [] args){
|
// get th
|
e JNDI initial context
|
System.err.println(&q
|
uot;Getting J2EE initial context");
|
Context jndiContext =
|
getInitialContext();
|
System.err.println(&q
|
uot;Looking up EJB Home");
|
Object homeRef = jndi
|
Context.lookup("Counter");
|
(CounterHome)javax.rmi.PortableR ;
|
emoteObject.narrow(homeRef, CounterHome.class)
|
// create the EJB instance |
System.er
|
r.println("Creating EJB
|
");
|
Counter ejb = home.create(); |
System.ou
|
t.println("Calling coun
|
t "+ejb.getCount());
|
System.out.println(&q
|
uot;Calling count "+ejb.getCount());
|
System.out.println(&q
|
uot;Calling count "+ejb.getCount());
|
System.err.println(&q
|
uot;Removing EJB");
|
catch(java.rmi.RemoteExc
|
eption re) {
|
static public Co g.NamingException {
|
ntext getInitialContext() th
|
rows javax.namin
|
java.util.Properties jnd
|
iProperties = new java.util.Properties();
|
jndiProperties.put("
|
;java.naming.factory.initial",
|
"com.idoox.jndi.InitialCont |
jndiProperti
|
es.put("java.naming.pro
|
vider.url","
|
return new InitialContex
|
t(jndiProperties);
|
图 5:简单的EJB客户端计数器(CounterClient.java) |
请注意为了简单起见, 重新定向到JNDI Web servi java.naming.provider.url 是将其代码固化在客户端应
|
我们固化了所有的JNDI特定参数 ce: 参数java.naming.factory 。这些参数通常是存储在应用程 用程序中。这种情况下,就没有
|
。需要定义两个参数来把JNDI查询 .initial 和 参数 序的properties属性文件中,而不 必要对代码再次进行编译。
|
下一步就是使用run脚本来编译和运 getCount被调用了三次。客户端应用程序 您也会看到状态(计数器的取值)也得到了
|
行J2EE 客户端应用程序。您将会看到EJB的方法 与服务器方的所有通信都是通过 SOAP报文实现的。 适当的保存和处理。
|
当今,大多数的Web service使用HTT 要优势就是通过HTTP代理和防火墙来实现 能满足需求。HTTP 具有单向性并且缺少 且,在异步报文的路由选择中,HTTP所提 是使用JMS传送SOAP报文。JMS可以为企业 保证的报文传送、报文处理中的入队和出 优于HTTP协议的性能和稳定性。
|
P传输协议进行通信。HTTP适合于多种应用。它的主 应用集成的灵活性。 但是对一些应用而言, HTTP不 企业级的功能,例如可靠性、持久性和事务处理。而 供的支持也不是很理想。表述这些问题的一种方法就 级应用的通信提供充分的益处。这是因为JMS支持有 队以及同步报文和异步报文的语义。JMS也可以提供
|
下一个示例,SOAP是具 的Web service,但是这次 一下传送给客户端lookup
|
有真正意义上的传输协议独立性 我们将要通过JMS发送SOAP消息 方法的查找URL即可:
|
。我们将访问在第一个示例中开发 。实现非常简单。我们只需要修改
|
package com.systinet.demos.jms; |
import org.idoox.wasp.Context; |
import org.idoox.webservice.clie
|
nt.WebServiceLookup;
|
public final class StockClient { |
public static void main( Str
|
ing[] args ) throws Exception {
|
System.setProperty(&
|
quot;java.naming.factory.ini
|
tial",
|
"com.idoox.jndi.InitialCont
|
extFacto
|
System.setProp
|
erty("java.naming.provi
|
der.url","
|
System.setProperty("i "true");
|
doox.demo.transport.j2ee",
|
WebServiceLookup lookup = |
(WebServiceLookup)Context.getIns
|
tance(Context.WEBSERVICE_LOOKUP);
|
// bind to StockQuoteService |
StockQuoteServ oxy)lookup.lookup
|
iceProxy quoteService = (Sto
|
ckQuoteServicePr
|
("jms://jms/Que
|
ue@jms/QueueConnectionFactor
|
y~
|
/StockEJBService/&qu
|
ot;,StockQuoteServiceProx
|
System.out.println("G
|
etting SUNW quote");
|
System.out.println("-
|
-----------------------");
|
System.out.pri ("SUNW"));
|
ntln("SUNW "+quote
|
Service.getQuote
|
图 6: 简单的JMS客户端(StockClient.java) |
我们通过运行run_jms 提供完全透明的映射。所以 services、远程引用和 SOA
|
脚本程序来编译和运行JMS客户 ,无需修改Java代码,前面提到 P故障等)都可以在JMS之上正常
|
端示例。SOAP框架为底层传输协议 的所有功能(例如,有状态Web 运转。
|
注:预先配置Web serv
|
ice运行时间来侦听jms/Queue J
|
MS 队列中的SOAP报文。
|
既然我们完成了所有的 用程序服务器和Web servic
|
演示样本,请使用undeploy_j2e e运行时间的Web service中删除
|
e 和 undeploy_service 脚本从应 EJB。
|
|
|
|
|
|