用JAXM开发Web服务Word文件下载.docx
《用JAXM开发Web服务Word文件下载.docx》由会员分享,可在线阅读,更多相关《用JAXM开发Web服务Word文件下载.docx(33页珍藏版)》请在冰点文库上搜索。
图2带附件SOAP消息
可以看出,一个SOAP消息可以有一个或者多个附件。
SAAJAPI使用AttachmentPart类来代表SOAP消息的附件。
每个AttachmentPart有一个MIMEHeader来表示附件的类型。
连接
有两种类型的连接,它们是:
消息发送者到接收者的直接连接,javax.xml.soap.SOAPConnection表示了这种类型的连接,由于它是点对点的,所以比较容易使用,即使不在Servlet或者J2EE容器里也能使用;
到消息提供者的连接,javax.xml.messaging.ProviderConnection表示了这种连接,这种方式需要消息提供者,消息发送者和消息使用者通过消息提供者来交互。
消息提供者
消息提供者主要负责传送消息,它把消息路由到目的地,一个消息发出后,可能要经过多个消息提供者才能到达目的地。
如果使用MessageProvider,可以达到以下的目的:
除了能够发送request-response类型的消息外,还可以发送One-way(单向)消息;
(消息)客户端有时也可以作为服务端来使用。
案例介绍
在本文,我将结合一个具体的案例来介绍JAXMWeb服务的开发。
此案例具体情况如下。
某图书城决定使用Web服务来对外提供图书信息查询服务,图书城现有的系统运行在J2EE平台上,客户端通过JAXM来使用图书城提供的Web服务。
系统的体系结构如图3所示:
图3系统体系结构
客户端可以是一般的javaGUI程序(当然也可以是JSP、Servlet等)。
客户端通过SOAP消息和Servlet容器里运行的JAXMServlet进行交互,JAXMServlet是服务提供者,EJB容器里运行的是业务组件,它们为JAXMServlet提供服务。
客户端请求传递的过程如图4所示:
图4请求传递的过程
可以看出,客户端通过SOAP和JAXM服务端通信,JAXM使用EJB组件来获得业务服务。
系统为客户端提供了三种查询服务:
查询所有图书,按类别查询图书,按图书名搜索某本特定的图书。
这三种服务分别有服务端的三个JAXMServlet实现。
它们是:
ListAllBook:
查询所有的图书信息;
ListByCategory:
按类别查询图书信息;
BookDetail:
查询某个特定名称的图书信息。
客户端是用Swing编写的GUI界面,使用界面如图5所示。
图5客户端界面
客户端和服务端传输图书信息时采用例程1所示的格式。
例程1传输图书信息的格式(book.dtd)
<
!
ELEMENTbooks(book*)>
ELEMENTbook(name,publisher,price,author+,category,description)>
ELEMENTname(#PCDATA)>
ELEMENTpublisher(#PCDATA)>
ELEMENTprice(#PCDATA)>
ELEMENTauthor(#PCDATA)>
ELEMENTcategory(#PCDATA)>
ELEMENTdescription(#PCDATA)>
ATTLISTbookidCDATA#REQUIRED>
这个信息包含在SOAP消息的Body里,按照这个格式,传输的SOAP消息结构如例程2所示。
例程2传输的SOAP消息的格式(book.msg)
soap-env:
Envelopexmlns:
soap-env="
http:
//schemas.xmlsoap.org/soap/envelope/"
>
<
Header/>
Body>
books:
GetAllBooksxmlns:
books="
"
bookid="
2-1234-4455-4"
name>
J2EE企业应用开发<
/books:
publisher>
电子工业出版社<
price>
60<
category>
计算机类<
description>
非常好的介绍J2EE企业应用开发的书<
author>
陈亚强<
刘晓华<
book>
GetAllBooks>
/soap-env:
Envelope>
为了传输数据的便利,我把图书信息用一个专门的值对象来表示,如例程3所示。
例程3BookVO值对象
packagecom.hellking.webservice;
importjava.util.Collection;
publicclassBookVOimplementsjava.io.Serializable
{
privateStringname;
//图书名字
privateStringpublisher;
//图书出版社
privatefloatprice;
//图书价格
privateStringisbn;
//图书ISBN
privateStringdescription;
//图书的简介
privateStringcategory;
//图书的类别
privateCollectionauthors;
//图书的作者,因一本书可以有多个作者,故把它表示成Collection。
publicvoidsetName(Stringname)
{
this.name=name;
}
publicStringgetName()
returnthis.name;
…其它的getter和setter方法
可以看出,BookVO其实是和例程1中的DTD是对应的。
需要指出的是,BookVO可以使用JAXB(JavaAPIforXMLBinding)中的工具来生成。
EJB组件介绍
本案例使用了两个EJB组件,它们分别是BookEntityEJB和BookServiceFacadeEJB。
其中BookEntityEJB是实体Bean,它代表了每本书的详细信息;
BookServiceFacadeEJB为有状态会话Bean,它是一个会话门面,为JAXMServlet提供业务服务。
BookServiceFacadeEJB组件的远程接口如例程4所示。
例程4BookServiceFacadeEJB组件的远程接口
packagecom.hellking.webservice.ejb;
importjava.rmi.RemoteException;
importjavax.ejb.*;
publicinterfaceBookServiceFacadeextendsEJBObject
/**
*@J2EE_METHOD--getAllBook,查找所有的书
*/
publicjava.util.CollectiongetAllBook()throwsRemoteException;
*@J2EE_METHOD--findByCategory,按类别查找
publicjava.util.CollectionfindByCategory(Stringcategory)throwsRemoteException;
/**
*@J2EE_METHOD--getBookDetail,按名字查找
publicjava.util.CollectiongetBookDetail(Stringname)throwsRemoteException;
}
可以看出,它提供了三个业务服务,分别是getAllBook(),findByCategory(Stringcategory),getBookDetail(Stringname)。
这三个业务方法返回的都是java.util.Collection。
其实,getBookDetail(Stringname)方法返回的应该是一个值对象,但是为了方便统一处理,也通过处理让它返回java.util.Collection类型,这一点以后的代码中体现出来。
在BookEntityEJBHome接口也提供了对应的查找方法,如例程5所示。
例程5BookEntityEJB的Home接口
publicinterfaceBookEntityHomeextendsEJBHome
publicBookEntityfindByPrimaryKey(StringprimaryKey)
throwsRemoteException,FinderException;
publicBookEntitycreate(Stringisbn)throwsRemoteException,CreateException;
publicjava.util.CollectionfindAllBook()
publicjava.util.CollectionfindByCategory(Stringcategory)
publicBookEntityfindByName(Stringname)
开发服务端
下面开发服务端,我们前面说过,服务端共有三个JAXMServlet,它们分别提供三种不同的查询服务。
由于使用了点对点的消息模型,故服务端需要实现javax.xml.messaging.ReqRespListener接口,并且需要继承javax.xml.messaging.JAXMServlet类。
javax.xml.messaging.JAXMServlet是一个Servlet,它为开发消息服务的Servlet提供了一个框架。
需要指出的是,javax.xml.messaging.ReqRespListener接口定义了一个
publicSOAPMessageonMessage(SOAPMessagemessage)
方法,故我们开发的JAXM服务端Servlet必须实现这个方法。
onMessage方法就是当此Servlet接收到SOAPMessage时激发的方法,它通过此方法对外界提供服务(我们可以把这个方法简单的比喻成普通的HttpServlet中的doGet()、doPost()方法,HttpServlet正是通过doGet()、doPost()来为客户端提供服务)。
ListAllBook的部分代码如例程6所示。
例程6ListAllBook的部分代码
publicclassListAllBookextendsJAXMServletimplementsReqRespListener
publicvoidinit(ServletConfigservletConfig)throwsServletException
super.init(servletConfig);
publicSOAPMessageonMessage(SOAPMessagemessage)
{
System.out.println("
fromListAllBookServlet:
receiveamessage"
);
try
message.writeTo(System.out);
//在控制台打印收到的消息
//调用其它类来实现业务方法
SOAPMessagemsg=newXMLBusinessDelegate().listAllBook();
thisisthereply....."
msg.writeTo(System.out);
msg.saveChanges();
//注意,在返回消息之前要调用这个方法
returnmsg;
//返回消息
catch(Exceptionex)
ex.printStackTrace();
//处理错误….
returnnull;
在这个例子里,由于接收到的消息不含任何参数,故没有对它进行处理,一般情况下,接收的消息是有参数的,并且服务端需要使用这个参数来调用业务层组件,如当用户按类别查找图书时,服务端需要获得类别的名字,例程7是按类别查找图书的服务端Servlet的部分代码,我们看服务端是怎么获得客户端的请求参数。
例程7ListByCategory的部分代码
publicSOAPMessageonMessage(SOAPMessagemessage)
…
SOAPEnvelopeenv=message.getSOAPPart().getEnvelope();
Iteratorit=env.getBody().getChildElements(
env.createName("
books"
"
GetBookByCategory"
));
SOAPElementbooks=(SOAPElement)it.next();
Iteratorit2=books.getChildElements(
category"
Stringcategory=((SOAPElement)it2.next()).getValue();
//这里的category是从SOAP消息中读出的参数
SOAPMessagemsg=newXMLBusinessDelegate().listByCategory(category);
//返回处理消息
//如果出错,一般返回SOAPFault,这里简化了。
SAAJAPI为解析SOAP消息提供了很好的支持,注意上面的黑体子,它是读取获得查找图书类别参数category的代码,这个参数是客户端设置的,在以后我们将看到客户端怎么设置这个参数。
总结一下,JAXM服务端Servlet处理消息的步骤是:
1.获得消息(onMessage)
2.读取消息中需要的参数
3.利用参数调用对应的业务处理
4.构建响应SOAP消息
5.返回处理后的消息
从上面的例子可以看出,JAXM服务端Servlet并没有处理具体的业务,而是把业务处理交给一个叫XMLBusinessDelegate业务代表的类来处理。
我们来看一下XMLBusinessDelegate是怎么来进行业务处理的。
如例程8所示。
例程8XMLBusinessDelegate的部分代码
…
publicclassXMLBusinessDelegate
InitialContextinit=null;
BookServiceFacadeHomefacadeHome;
OTDEngineotd;
//对象到数据的转换器
publicXMLBusinessDelegate()throwsNamingException
init=this.getInitialContext();
otd=newBeanToSOAPEngine();
//生成对象到数据转换器实例
publicstaticInitialContextgetInitialContext()throwsjavax.naming.NamingException
//更据不同的EJB容器,使用不同的url和连接工厂来获得上下文,然后返回…
//业务方法,按类别查找图书
publicSOAPMessagelistByCategory(Stringcategory)
Objectobjref=init.lookup("
ejb/bookservicefacade"
facadeHome=(BookServiceFacadeHome)
javax.rmi.PortableRemoteObject.narrow(objref,BookServiceFacadeHome.class);
calljboss======>
Collectionresult=facadeHome.create().findByCategory(category);
//调用业务方法
getresult======>
System.out.println(result.size());
//使用BeanToSOAPEngine把调用结果转换成SOAP消息
otd.init(result,"
GetAllBooks"
SOAPMessageret=otd.getResult();
returnret;
catch(Exceptione)
e.printStackTrace();
}
…
可以看出,XMLBusinessDelegate只是调用EJB组件的业务方法,然后把构建SOAP消息的任务交给BeanToSOAPEngine,BeanToSOAPEngine是负责把包含了BookVO的Collection转换成SOAP消息的专门的类。
BeanToSOAPEngine的部分代码如例程9所示。
例程9BeanToSOAPEngine的部分代码
publicclassBeanToSOAPEngineimplementsOTDEngine
CollectionbookVos;
//要处理的信息
SOAPMessagemsg;
//待返回的消息
Stringtype;
//type为返回消息的名字空间,如GetBookByCategory
publicBeanToSOAPEngine()
MessageFactorymf=MessageFactory.newInstance();
//获得MessageFactory的实例
msg=mf.createMessage();
//从MessageFactory建立一个空的Message
publicvoidinit()
this.bookVos=c;
this.type=type;
publicSOAPMessagegetResult()
build();
publicvoidbuild()
SOAPPartpart=msg.getSOAPPart();
SOAPEnvelopeenvelop