1、用JAXM开发Web服务阅读本文前您需要以下的知识和工具:JavaTM Web Services Developer Pack 1.1,并且会使用初步使用;至少会使用一种EJB容器来开发、部署EJB,并且了解怎么在客户端访问EJB组件;一般的Java编程知识。在J2EE平台里,要开发一个Web服务,我们通常有两种选择:使用JAX-RPC(Java API for XML-based RPC)使用JAXM(Java API for XML Messaging)作为对JAXM开发技术的入门,本文先不比较它们的技术特点。我将结合一个具体的案例来讨论JAXM的开发技术方方面面。JAXM相关概念介绍通常
2、我们说的JAXM API,它包括两个包:Javax.xml.soap:它是发送SOAP消息的基本包,主要包含了发送带有附件的SOAP消息的API(SOAP with Attachments API for Java ,SAAJ)。它是SOAP消息的基本包,它为构建SOAP包和解析SOAP包提供了重要的支持。它包含了发送请求响应消息相关的API。Javax.xml.messaging:定义了JAXM的规范,包含了发送和接收消息所需的API。JAXM包含了以下几个概念:消息(Message)、连接(Connection)、消息提供者(Messaging providers)。消息JAXM消息遵循S
3、OAP标准,我们可以通过JAXM API方便的创建SOAP 消息。有两种类型的消息,带附件的消息和不带附加的消息。不带附件的消息结构如图1所示。如图1所示,在SAAJ API中,它使用SOAPMessage类来代表SOAPMessage,相应的,使用SOAPPart类来代表SOAPPart,SOAPBody类代表SOAP Body。图1 不带附件的SOAP消息其中Header和SOAPFault是可选的,Header可以多个,Body只有一个,如果有SOAP Fault,那么它一定在SOAP Body后面。带附加的SOAP消息如图2所示。图2 带附件SOAP消息可以看出,一个SOAP消息可以有
4、一个或者多个附件。SAAJ API使用AttachmentPart类来代表SOAP消息的附件。每个AttachmentPart有一个MIME Header来表示附件的类型。连接有两种类型的连接,它们是:消息发送者到接收者的直接连接,javax.xml.soap.SOAPConnection表示了这种类型的连接,由于它是点对点的,所以比较容易使用,即使不在Servlet或者J2EE容器里也能使用;到消息提供者的连接,javax.xml.messaging.ProviderConnection表示了这种连接,这种方式需要消息提供者,消息发送者和消息使用者通过消息提供者来交互。消息提供者消息提供者主
5、要负责传送消息,它把消息路由到目的地,一个消息发出后,可能要经过多个消息提供者才能到达目的地。如果使用MessageProvider,可以达到以下的目的:除了能够发送request-response类型的消息外,还可以发送One-way(单向)消息;(消息)客户端有时也可以作为服务端来使用。案例介绍在本文,我将结合一个具体的案例来介绍JAXM Web服务的开发。此案例具体情况如下。某图书城决定使用Web服务来对外提供图书信息查询服务,图书城现有的系统运行在J2EE平台上,客户端通过JAXM来使用图书城提供的Web服务。系统的体系结构如图3所示:图3 系统体系结构客户端可以是一般的java GU
6、I程序(当然也可以是JSP、Servlet等)。客户端通过SOAP消息和Servlet容器里运行的JAXM Servlet进行交互,JAXM Servlet是服务提供者,EJB容器里运行的是业务组件,它们为JAXM Servlet提供服务。客户端请求传递的过程如图4所示:图4 请求传递的过程可以看出,客户端通过SOAP和JAXM 服务端通信,JAXM使用EJB组件来获得业务服务。系统为客户端提供了三种查询服务:查询所有图书,按类别查询图书,按图书名搜索某本特定的图书。这三种服务分别有服务端的三个JAXM Servlet实现。它们是:ListAllBook:查询所有的图书信息;ListByCat
7、egory:按类别查询图书信息;BookDetail:查询某个特定名称的图书信息。客户端是用Swing编写的GUI界面,使用界面如图5所示。图5 客户端界面客户端和服务端传输图书信息时采用例程1所示的格式。例程1 传输图书信息的格式(book.dtd)这个信息包含在SOAP消息的Body里,按照这个格式,传输的SOAP消息结构如例程2所示。例程2 传输的SOAP消息的格式(book.msg) J2EE企业应用开发 电子工业出版社 60 计算机类 非常好的介绍J2EE企业应用开发的书 陈亚强 刘晓华 为了传输数据的便利,我把图书信息用一个专门的值对象来表示,如例程3所示。例程3 BookVO值对
8、象package com.hellking.webservice;import java.util.Collection;public class BookVO implements java.io.Serializable private String name;/图书名字 private String publisher;/图书出版社 private float price;/图书价格 private String isbn;/图书ISBN private String description;/图书的简介 private String category;/图书的类别 private Co
9、llection authors;/图书的作者,因一本书可以有多个作者,故把它表示成Collection。public void setName(String name) this.name=name; public String getName() return this.name; 其它的getter和setter方法可以看出,BookVO其实是和例程1中的DTD是对应的。需要指出的是,BookVO可以使用JAXB(Java API for XML Binding)中的工具来生成。EJB组件介绍本案例使用了两个EJB组件,它们分别是BookEntityEJB和BookServiceFaca
10、deEJB。其中BookEntityEJB是实体Bean,它代表了每本书的详细信息;BookServiceFacadeEJB为有状态会话Bean,它是一个会话门面,为JAXM Servlet提供业务服务。BookServiceFacadeEJB组件的远程接口如例程4所示。例程4 BookServiceFacadeEJB组件的远程接口package com.hellking.webservice.ejb;import java.rmi.RemoteException;import javax.ejb.*;public interface BookServiceFacade extends EJB
11、Object /* * J2EE_METHOD - getAllBook,查找所有的书 */ public java.util.Collection getAllBook () throws RemoteException; /* * J2EE_METHOD - findByCategory,按类别查找 */ public java.util.Collection findByCategory (String category) throws RemoteException;/* * J2EE_METHOD - getBookDetail ,按名字查找 */ public java.util.
12、Collection getBookDetail (String name) throws RemoteException;可以看出,它提供了三个业务服务,分别是getAllBook(),findByCategory(String category),getBookDetail(String name)。这三个业务方法返回的都是java.util.Collection。其实,getBookDetail( String name )方法返回的应该是一个值对象,但是为了方便统一处理,也通过处理让它返回java.util.Collection类型,这一点以后的代码中体现出来。在BookEntityE
13、JB Home接口也提供了对应的查找方法,如例程5所示。例程5 BookEntityEJB的Home接口package com.hellking.webservice.ejb;import java.rmi.RemoteException;import javax.ejb.*;public interface BookEntityHome extends EJBHome public BookEntity findByPrimaryKey (String primaryKey) throws RemoteException, FinderException; public BookEntity
14、 create(String isbn) throws RemoteException, CreateException; /* * J2EE_METHOD - getAllBook,查找所有的书 */public java.util.Collection findAllBook () throws RemoteException, FinderException; /* * J2EE_METHOD - findByCategory,按类别查找 */ public java.util.Collection findByCategory (String category) throws Remo
15、teException, FinderException; /* * J2EE_METHOD - getBookDetail ,按名字查找 */ public BookEntity findByName (String name) throws RemoteException, FinderException; 开发服务端下面开发服务端,我们前面说过,服务端共有三个JAXM Servlet,它们分别提供三种不同的查询服务。由于使用了点对点的消息模型,故服务端需要实现javax.xml.messaging. ReqRespListener接口,并且需要继承javax.xml.messaging.
16、JAXMServlet类。javax.xml.messaging.JAXMServlet是一个Servlet,它为开发消息服务的Servlet提供了一个框架。需要指出的是,javax.xml.messaging. ReqRespListener接口定义了一个public SOAPMessage onMessage (SOAPMessage message)方法,故我们开发的JAXM服务端Servlet必须实现这个方法。 onMessage 方法就是当此Servlet接收到SOAPMessage时激发的方法,它通过此方法对外界提供服务(我们可以把这个方法简单的比喻成普通的HttpServlet中
17、的doGet()、doPost()方法,HttpServlet正是通过doGet()、doPost()来为客户端提供服务)。ListAllBook的部分代码如例程6所示。例程6 ListAllBook的部分代码public class ListAllBook extends JAXMServlet implements ReqRespListener public void init (ServletConfig servletConfig) throws ServletException super.init(servletConfig); public SOAPMessage onMess
18、age (SOAPMessage message) System.out.println(from ListAllBook Servlet:receive a message); try System.out.println(from ListAllBook Servlet:); message.writeTo(System.out);/在控制台打印收到的消息 /调用其它类来实现业务方法 SOAPMessage msg=new XMLBusinessDelegate().listAllBook(); System.out.println(this is the reply.); msg.wri
19、teTo(System.out); msg.saveChanges();/注意,在返回消息之前要调用这个方法 return msg;/返回消息 catch(Exception ex) ex.printStackTrace(); /处理错误. return null; 在这个例子里,由于接收到的消息不含任何参数,故没有对它进行处理,一般情况下,接收的消息是有参数的,并且服务端需要使用这个参数来调用业务层组件,如当用户按类别查找图书时,服务端需要获得类别的名字,例程7是按类别查找图书的服务端Servlet的部分代码,我们看服务端是怎么获得客户端的请求参数。例程7 ListByCategory的部分
20、代码public SOAPMessage onMessage (SOAPMessage message) try SOAPEnvelope env=message.getSOAPPart().getEnvelope(); Iterator it=env.getBody().getChildElements(env.createName(books,GetBookByCategory,); SOAPElement books=(SOAPElement)it.next(); Iterator it2=books.getChildElements(env.createName(category,Ge
21、tBookByCategory,); String category=(SOAPElement)it2.next().getValue(); /这里的category是从SOAP 消息中读出的参数 SOAPMessage msg=new XMLBusinessDelegate().listByCategory(category); msg.saveChanges(); return msg;/返回处理消息 catch(Exception ex) ex.printStackTrace(); /处理错误. return null;/如果出错,一般返回SOAPFault,这里简化了。 SAAJ AP
22、I为解析SOAP 消息提供了很好的支持,注意上面的黑体子,它是读取获得查找图书类别参数category的代码,这个参数是客户端设置的,在以后我们将看到客户端怎么设置这个参数。总结一下,JAXM服务端Servlet处理消息的步骤是:1. 获得消息(onMessage)2. 读取消息中需要的参数3. 利用参数调用对应的业务处理4. 构建响应SOAP消息5. 返回处理后的消息从上面的例子可以看出,JAXM服务端Servlet并没有处理具体的业务,而是把业务处理交给一个叫XMLBusinessDelegate业务代表的类来处理。我们来看一下XMLBusinessDelegate是怎么来进行业务处理的。
23、如例程8所示。例程8 XMLBusinessDelegate的部分代码public class XMLBusinessDelegateInitialContext init=null; BookServiceFacadeHome facadeHome;OTDEngine otd;/对象到数据的转换器 public XMLBusinessDelegate()throws NamingException init=this.getInitialContext();otd=new BeanToSOAPEngine(); /生成对象到数据转换器实例 public static InitialConte
24、xt getInitialContext() throws javax.naming.NamingException /更据不同的EJB容器,使用不同的url和连接工厂来获得上下文,然后返回/业务方法,按类别查找图书public SOAPMessage listByCategory(String category) try Object objref = init.lookup(ejb/bookservicefacade); facadeHome = (BookServiceFacadeHome)javax.rmi.PortableRemoteObject.narrow(objref, Boo
25、kServiceFacadeHome.class); System.out.println(call jboss=); Collection result=facadeHome.create().findByCategory(category);/调用业务方法 System.out.println(get result=); System.out.println(result.size(); / 使用BeanToSOAPEngine把调用结果转换成SOAP消息otd.init(result,GetAllBooks); SOAPMessage ret=otd.getResult(); retur
26、n ret; catch(Exception e) e.printStackTrace(); return null; 可以看出,XMLBusinessDelegate只是调用EJB组件的业务方法,然后把构建SOAP消息的任务交给BeanToSOAPEngine,BeanToSOAPEngine是负责把包含了BookVO 的Collection转换成SOAP消息的专门的类。BeanToSOAPEngine的部分代码如例程9所示。例程9 BeanToSOAPEngine的部分代码public class BeanToSOAPEngine implements OTDEngine Collecti
27、on bookVos;/要处理的信息 SOAPMessage msg;/待返回的消息 String type;/type为返回消息的名字空间,如GetBookByCategory public BeanToSOAPEngine() try MessageFactory mf = MessageFactory.newInstance();/获得MessageFactory的实例 msg = mf.createMessage();/从MessageFactory建立一个空的Message catch(Exception ex) ex.printStackTrace(); public void init() this.bookVos=c; this.type=type; public SOAPMessage getResult() build(); return msg; public void build() try SOAPPart part = msg.getSOAPPart(); SOAPEnvelope envelop
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1