addresslocation="/>
在部署的时候,JAX-RPCStub生成工具会把WSDL端口转换成远程接口和Stub,端口和服务Stub可能是下面的样子:
publicInterfaceBookPriceServiceextendsjavax.xml.rpc.Service{
publicBookPricegetBookPrice()throwsRemoteException;
}
publicInterfaceBookPriceextendsjava.rmi.Remote{
publicfloatgetBookPrice(Stringisbn)
throwsRemoteException;
}
这里只是一个简单的例子,这个服务只有一个端口,而实际上一个服务会有多个端口,每个端口有相应的接口和Stub。
一旦接口和Stub产生并被绑定到JNDIENC之后,它们就可以在运行期调用WebService的“操作”了,在下面的无序会话Bean里,BookCatalogEJB利用JAX-RPC从.NETWebWebvices查找一本书的批发价格。
publicclassBookCatalogimplementsjavax.ejb.SessionBean{
...
publicfloatgetWholeSalePrice(Stringisbn){
try{
InitialContextjndiContext=newInitialContext();
BookPriceServiceservice=
jndiContext.lookup("java:
comp/env/service/BookPriceService");
BookPricebookPrice_port=service.getBookPrice();
floatprice=bookPrice_port.getBookPrice(isbn);
returnprice;
catch(RemoteExceptionre){
}catch(ServiceExceptionse){
}catch(NamingExceptionne){
}
}
...
}
当调用getBookPrice()方法时,JAX-RPCStub向.NETWebService发送SOAP信息,Stub产生的SOAP信息可能会是下面的样子:
xmlversion='1.0'?
>
Envelope
xmlns:
env="http:
//schemas.xmlsoap.org/soap/envelope/"
xmlns:
xsi="http:
//www.w3.org/2001/XMLSchema-instance"
xmlns:
xyz='
encodingStyle="http:
//schemas.xmlsoap.org/soap/encoding/">
getBookPrice>
type="string">1565928695
getBookPrice>
Envelope>
.NETErbServices处理SOAP信息,并把结果返回到Stub,Stub分析结果,最后向客户端发送最终结果。
JAX-RPCStub中的方法可以有参数,参数类型可以是基本数据类型,如int,long等;基本包装类型,如java.lang.Interger,java.lang.Long等;数组;Java标准类型,如String,Date等;也可以是自定义对象类型。
自定义对象必须符合JAX-RPC规范的规则。
除了产生Stub外,JAX-RPC也支持动态代理服务,除了它的远程接口和Stub的实现是在运行时动态产生的之外,动态代理服务的作用和Stub一样。
下面的例子就是JAX-RPC产生动态Stub的:
publicclassBookCatalogimplementsjavax.ejb.SessionBean{
...
publicfloatgetWholeSalePrice(Stringisbn){
try{
InitialContextjndiContext=newInitialContext();
javax.xml.rpc.Serviceservice=
jndiContext.lookup("java:
comp/env/service/DynamicService");
BookPricebookPrice_port=service.getPort(BookPrice.class);
floatprice=bookPrice_port.getBookPrice(isbn);
returnprice;
catch(RemoteExceptionre){}
catch(ServiceExceptionse){}
catch(NamingExceptionne){}
}
...
}
在运行时,getPort()方法自动把BookPrice接口映射到WSDL文档里定义的相应端口,然后产生Stub实现接口的工作。
JAX-RPC还支持名为DII(DynamicInvocationInterface)的动态API,DII允许开发人员在运行时调用SOAP方法。
如果你使用过CORBADynamicInvocationInterface的话,那你对JAX-RPCDII一定很容易理解。
JAX-RPCDII类似于Java的反射(Reflection),它允许你以方法的形式得到一个代表WebService操作的对象的参考,调用那个方法,就无需再访问ServiceFactory或者再使用Stub和远端接口。
下面的例子就是企业Bean访问BookPrice端口的getBookPrice()操作:
publicclassBookCatalogimplementsjavax.ejb.SessionBean{
...
publicfloatgetWholeSalePrice(Stringisbn){
try{
InitialContextjndiContext=newInitialContext();
javax.xml.rpc.Serviceservice=
jndiContext.lookup("java:
comp/env/service/DynamicService");
QNameport=newQName("","BookPrice");
QNameoperation=newQName("
"getBookPrice");
CallcallObject=service.createCall(port,operation);
Object[]args=newObject[1];args[0]=isbn;
Floatprice=(Float)callObject.invoke(args);
returnprice.floatValue();
}
catch(JAXRPCExceptionse){}
catch(NamingExceptionne){}
}
}
...
}
实际上,你可以在运行期配置参数、类型、编码等,你能用WSDL配置的所有信息都可以用DII动态配置。
JAX-RPC另外还是一个称为终端接口(EndpointInterface)的新型组件,这个新接口允许我们把无序的会话Bean作为WebService来实现,这个终端接口简化了javax.rmi.Remote接口的实现,并且遵守JAX-RPC规范中的规则。
把一个无序的会话Bean作为WebService来实现是非常简单的:
只需定义Bean类和远端接口,然后使用开发商的提供的工具来实现。
一旦建立了WebService,它的方法就能够被任何SOAP兼容的、来自任何语言和平台的工具包来调用,比如:
.NET,Perl,ApacheAxis,C,C++等等。
如下图所示:
由于JAX-RPC仅仅是JavaRMI的另外一种形式,因此,利用它访问企业Bean是很自然的,我们以前已经利用RMI-IIOP和RMI访问过。
利用JAX-RPC与EJB进行通信意味着EJB可以当作WebService来使用,至少无序的Bean是可以的。
EJB2.1允许我们利用JAX-RPC,但只能是应用于无序的Bean,这主要是因为SOAP是一种无序的消息协议,它没有对象识别的概念,因此它不能应用在有序的和实体的Bean中。
EJB2.1为无序的Bean定义了一个新的WebService终端(EndPoint)接口,WebService界的人使用“终端”来称呼发送和接收SOAP信息的任何东西。
在EJB中,终端就是一个无序的会话Bean,它可以通过SOAP来访问,并且遵照JAX-RPC规范中定义的从Java-to-WSDL到Java-to-SOAP的映射规则。
使用JAX-RPC最为EJB终端的基础是顺理成章的,因为JAX-RPC规范中定义了SOAP消息和Java方法调用以及从Java远程接口产生WSDL文档的详细规则。
不象EJB开发者已经很熟悉的远端和本地接口,终端接口并没有继承EJB对象类型,如EJBObject或EJBLocalObject。
相反,终端接口直接继承了javax.ejb.Remote接口。
例如:
在上面的例子中的BookPrice的WebService能够很轻易实现为EJB中的终端。
下面的代码说明了一个BookPrice的终端接口,和实现为WebService的无序会话Bean的部分列表。
publicinterfaceBookPriceextendsjavax.rmi.Remote{
publicStringgetBookPrice(Stringisbn)throwsjavax.rmi.RemoteException;
}
publicclassBookPriceWSimplementsBookPrice,javax.ejb.SessionBean{
publicfloatgetBookPrice(Stringisbn){
Connectioncon=null;
Statementstmt=null;
ResultSetrs;
try{
DataSourceds=jdniEnc.lookup("java:
comp/env/jdbc/DataSource");
con=ds.getConneciton();
stmt=con.createStatement();
rs=stmt.executeQuery("SELECTwholesaleFROMCATALOGWHEREisbn=\'"
+isbn+"\'");
if(rs.next()){
floatprice=rs.getFloat("wholesale");
returnprice;
}else{
return0;
}
}
catch(SQLExceptionse){
//处理异常
}
}
...
}
终端接口比远程和本地接口一个很明显的好处就是它不会带来象EJBObject或者EJBLocalObect无用方法等形式的额外负担,此外,终端接口没有包括home接口,SOAP不支持按引用传值。
因此,你不能要求一个WebServices接口(home接口)按引用传递到另外一个远端接口,更进一步讲,你不能创建或移除一个WebService。
当我们把一个无序会话Bean开发成WebService时,首先定义一个终端接口,然后利用它产生JAX-RPC的客户端Stub和WSDL文档或者如果你产生JAX-RPC客户端Stub,无需做任何改变,你可以把它包装成J2EE客户端JAR,利用它去访问无序会话Bean,利用SOAP做通讯协议。
如果你从终端接口产生WSDL文档,其它的SOAP工具包也能够使用这个文档去访问你的无序Bean。
WSDL和SOAP是WebService的基础,因此,为EJBWebServices发布WSDL可以实现与其它平台的交互。
SAAJSAAJ(SOAPwithAttachmentsAPIforJava)是一个基于API的SOAP工具包,它定义了SOAPMessageswithAttachments(SwA)和SOAP用的MIME信息格式。
Java开发人员能够利用SAAJ来创建、读取或者修改SOAP信息。
这个API包含许多类和接口,用来定义SOAP元素(Envelope,Body,Header,Fault等),XML名称空间,属性,文字节点以及MIME附件。
你可以使用SAAJ操作简单的、没有附件的XML格式的SOAP信息,也可以操作更加复杂的、带Mime附件的SOAP信息。
SAAJ可以与JAX-RPC结合使用,但也可以单独使用,它有自己的、通过HTTP1.1实现的请求/应答方式的消息机制。
SAAJ是基于AbstractFactory模式的。
SAAJ是类型的抽象集合,每一种类型的对象都是由SAAJ集合中另外的对象产生的。
在AbstractFactory的SAAJ实现中,MessageFactory类是根,它负责创建自己的实例,反过来创建SOAPMessage,SOAPMessage包含SOAPPart,它代表SOAP文档、0个和多个AttachmentPart(代表附件的对象,如GIF,PDF等)。
SOAPPart包含SOAPEnvelope、SOAPBody、SOAPHeader和其它类