Envelopexmlns:
soap-env="http:
//schemas.xmlsoap.org/soap/envelope/">
Header/>
Body>
GetAllBooksxmlns:
books="">
bookid="2-1234-4455-4">
name>J2EE企业应用开发
name>
publisher>电子工业出版社
publisher>
price>60
price>
category>计算机类
category>
description>非常好的介绍J2EE企业应用开发的书
description>
author>陈亚强
author>
author>刘晓华
author>
book>
GetAllBooks>
Body>
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接口
packagecom.hellking.webservice.ejb;
importjava.rmi.RemoteException;
importjavax.ejb.*;
publicinterfaceBookEntityHomeextendsEJBHome
{
publicBookEntityfindByPrimaryKey(StringprimaryKey)
throwsRemoteException,FinderException;
publicBookEntitycreate(Stringisbn)throwsRemoteException,CreateException;
/**
*@J2EE_METHOD--getAllBook,查找所有的书
*/
publicjava.util.CollectionfindAllBook()
throwsRemoteException,FinderException;
/**
*@J2EE_METHOD--findByCategory,按类别查找
*/
publicjava.util.CollectionfindByCategory(Stringcategory)
throwsRemoteException,FinderException;
/**
*@J2EE_METHOD--getBookDetail,按名字查找
*/
publicBookEntityfindByName(Stringname)
throwsRemoteException,FinderException;
}
开发服务端
下面开发服务端,我们前面说过,服务端共有三个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
{
System.out.println("fromListAllBookServlet:
");
message.writeTo(System.out);//在控制台打印收到的消息
//调用其它类来实现业务方法
SOAPMessagemsg=newXMLBusinessDelegate().listAllBook();
System.out.println("thisisthereply.....");
msg.writeTo(System.out);
msg.saveChanges();//注意,在返回消息之前要调用这个方法
returnmsg;//返回消息
}
catch(Exceptionex)
{
ex.printStackTrace();
//处理错误….
returnnull;
}
}
}
在这个例子里,由于接收到的消息不含任何参数,故没有对它进行处理,一般情况下,接收的消息是有参数的,并且服务端需要使用这个参数来调用业务层组件,如当用户按类别查找图书时,服务端需要获得类别的名字,例程7是按类别查找图书的服务端Servlet的部分代码,我们看服务端是怎么获得客户端的请求参数。
例程7ListByCategory的部分代码
publicSOAPMessageonMessage(SOAPMessagemessage)
{
try
{
…
SOAPEnvelopeenv=message.getSOAPPart().getEnvelope();
Iteratorit=env.getBody().getChildElements(
env.createName("books","GetBookByCategory",""));
SOAPElementbooks=(SOAPElement)it.next();
Iteratorit2=books.getChildElements(
env.createName("category","GetBookByCategory",""));
Stringcategory=((SOAPElement)it2.next()).getValue();
//这里的category是从SOAP消息中读出的参数
SOAPMessagemsg=newXMLBusinessDelegate().listByCategory(category);
msg.saveChanges();
returnmsg;//返回处理消息
}
catch(Exceptionex)
{
ex.printStackTrace();
//处理错误….
returnnull;//如果出错,一般返回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)
{
try
{
Objectobjref=init.lookup("ejb/bookservicefacade");
facadeHome=(BookServiceFacadeHome)
javax.rmi.PortableRemoteObject.narrow(objref,BookServiceFacadeHome.class);
System.out.println("calljboss======>>");
Collectionresult=facadeHome.create().findByCategory(category);//调用业务方法
System.out.println("getresult======>>");
System.out.println(result.size());
//使用BeanToSOAPEngine把调用结果转换成SOAP消息
otd.init(result,"GetAllBooks");
SOAPMessageret=otd.getResult();
returnret;
}
catch(Exceptione)
{
e.printStackTrace();
returnnull;
}
}
…
可以看出,XMLBusinessDelegate只是调用EJB组件的业务方法,然后把构建SOAP消息的任务交给BeanToSOAPEngine,BeanToSOAPEngine是负责把包含了BookVO的Collection转换成SOAP消息的专门的类。
BeanToSOAPEngine的部分代码如例程9所示。
例程9BeanToSOAPEngine的部分代码
…
publicclassBeanToSOAPEngineimplementsOTDEngine
{
CollectionbookVos;//要处理的信息
SOAPMessagemsg;//待返回的消息
Stringtype;//type为返回消息的名字空间,如GetBookByCategory
publicBeanToSOAPEngine()
{
try
{
MessageFactorymf=MessageFactory.newInstance();//获得MessageFactory的实例
msg=mf.createMessage();//从MessageFactory建立一个空的Message
}
catch(Exceptionex)
{
ex.printStackTrace();
}
}
publicvoidinit()
{
this.bookVos=c;
this.type=type;
}
publicSOAPMessagegetResult()
{
build();
returnmsg;
}
publicvoidbuild()
{
try
{
SOAPPartpart=msg.getSOAPPart();
SOAPEnvelopeenvelop