关键?/strong>Q?JAX-RPC web服务
阅读本文前您需要以下的知识和工P
- JavaTM Web Services Developer Pack 1.1Qƈ且会使用初步使用Q?
- 辽倩崾褂靡恢諩JB容器来开发、部|EJBQƈ且了解怎么在客L讉KEJBlgQ?
- 对Apache axis Web服务开发工h基本的了解;
- 基本的Java~程知识?
如果使用JAX-RPC开发Web服务Q我们几U选择Q?/p>
- Servlet作ؓWeb服务端点Q?
- 无状态会话Bean作ؓWeb服务端点Q?
- Z消息Q如JMSQ的应用E序作ؓWeb服务端点?
本文以Servlet作ؓWeb服务端点的情冉|介绍JAX-RPC Web服务开发,关于本篇文章中案例的介绍详见本系列文章第一: 《用JAXM开发Web服务?/a>?
本文的参考资料见 参考资?/a>
本文的全部代码在q里 下蝲
JAX-RPC快速入?/span>
JAX-RPCQJava?API for XMLbased RPCQ顾名思义Q它是一U远E方法调用(或者说q程q程调用Q,那么它和其它的远E方法调用(RPCQ?COMQCORBAQRMIQ有什么区别呢Q我们看一般的q程Ҏ调用的结构,如图1所C?/p>

l合比较常用的远E方法调用技术,它们有以下的共性:
- 在客L和服务端有通用~程接口Q?
- 在客L有StubQ在服务端有TieQ有的叫SkeletonQ;
- 客户端和服务端有专门的协议进行数据传输?
对于通用接口的描qͼ比如CORBA有IDL of CORBAQJava RMI有Java RMI interface in RMIQ对于XMLbased RPC来说QIDL是WSDLQWeb服务描述语言Q。那么XMLbased RPC来说Q什么是q个l构中的"传输协议"Q当然是SOAPQSOAP消息通过以传输文本ؓ基础的协议(HTTP、SMTP、FTPQؓ载体来用的Q也是_SOAP消息的传输徏立在HTTP、SMTP、FTP传输协议之上?/p>
JAX-RPC的构架如下?/p>
![? JAX-RPC 的构? src=]()
从上囑֏以看出,客户端调用的是JAX-RPC服务端点(Service Endpoint)Q这个服务端Ҏ通过WSDL语言描述的。在q个体系l构中,对于客户端,可以是JS2E、J2ME或者J2EEq_q行环境Q对于服务端Q可以是J2EE1.3或者J2EE1.4容器QServlet容器或者EJB容器Q。Apache axis是一个很好的JAX-RPCq行环境实现Q同时也提供了优U的开发工P本文用它q行开发?/p>
使用Servlet作ؓ服务端点Q本案例的基本构架如下图所C?/p>
![? 案例的基本构? src=]()
客户端通过SOAP消息和JAX-RPC服务端交互,JAX-RPC服务端运行在Servlet容器中,它通过调用EJB容器中的EJBlg来处理具体的业务逻辑?/p>
使用JAX-RPC开发Web服务Q可以按照以下的步骤q行Q?/p>
- 服务端点定义Q?
- 服务描述Q?
- 服务端点实现Q?
- 服务端点部vQ?
- 服务发布和发现?
注意Q对于服务的发布和发玎ͼ׃机制比较复杂Q本文不讨论Q可能会在本pd文章q行专题讨论?/p>
开发快速入?/span>
一个完整的JAX-RPC开发实例,按照上面的5个步骤进行,但是我们也可以用非常简单的方式来发布一个Web服务。在介绍我们的案例前Q让我们用一分钟快速开发一个Web服务?/p>
首先安装好JWSDPQ你可以?http://java.sun.com/webservices下蝲?
把本案例源代码中的\src\bookservice.ear\web.war目录拯?JWSDP_HOME%\webapps目录下,web.war文g里已l包括了Apache axisq行环境?/p>
?JWSDP_HOME%\classes目录下新Z个HelloWorld.java文gQ它的代码如下:
例程1 最单的Web服务HelloWorld
package com.hellking.webservice;
public class HelloWorld
{
public String sayHello(String name)
{
return "Hello! "+name;
}
}
|
~译q个c,然后~辑%JWSDP_HOME%\webapps\WEB-INF\server-config.wsdd文gQ找到记Q在其后面加入以下内容:
在浏览器例输入:
http://localhost:8080/web/services/HelloWorld?wsdl如果出现部vWSDL描述文gQ那么最单的Web服务已经部v成功Q?/p>
下面我们使用最单的方式来调用这个Web服务Q在览器里输入Q?/p>http://localhost:8080/web/services/HelloWorld?wsdl&method=sayHello&name=hellking
那么在浏览器会昄以下内容Q?/p>
例程2 在浏览器里调用Web服务
如果l果是这P那么最单的Web服务已经部v成功Qƈ且测试也通过了。注?HelloQhellking"是调用Web服务q回的结果,它是我们期望的。下面我们来看一个完整的Web服务开发的例子?/p>
服务端点定义
服务端点定义的工作主要是定"服务定义接口"QService Definition InterfaceQ,有时也叫Web服务端点接口QWeb services endpoint interfaceQ。服务端点定义有两中Ҏ获得Q?/p>
- 使用某些工具从WSDL文g获得Q?
- 直接使用Java语言~写?
Apache axis提供了从WSDL文g中获得Web服务端点的工兗您可以q样使用Q?/p>
java org.apache.axis.wsdl.WSDL2Java (WSDL-file-URL)
|
使用q个命o前先讄好以下的环境变量Q后面的介绍中还会用axis工具Q它们也要这栯|环境变量:
SET AXIS_HOME=
set CLASSPATH=%CLASSPATH%;
%AXIS_HOME%/axis-1_1/lib/axis.jar;
%AXIS_HOME%/axis-1_1/lib/jaxrpc.jar;
%AXIS_HOME%/axis-1_1/lib/saaj.jar;
%AXIS_HOME%/axis-1_1/lib/commons-logging.jar;
%AXIS_HOME%/axis-1_1/lib/commons-discovery.jar;
%AXIS_HOME%/axis-1_1/lib/wsdl4j.jar;.
|
关于WSDL2Java的更详细的用,请参考Apache axis的User GuidesQ?http://ws.apache.org/axis/Q?
我们q里直接使用Java~写服务端点接口的方法。在本案例中Q定义了三个业务ҎQ它们分别是查找所有的图书、按书名查找图书、按cd查找图书。那么安照这三个业务ҎQ可以定义出以下的服务端Ҏ口:
例程3 服务端点定义QBookServiceInterface.javaQ?/p>
package com.hellking.webservice.servlet;
/**
*@author hellking
*/
import java.util.Collection;
import com.hellking.webservice.BookVO;
public interface BookServiceInterface
{
/**
* @return Vector
*/
public Collection getAllBooks();//查找所有的图书
/**
* @param name
* @return BookVO
*/
public BookVO getTheBookDetail(String name);//按照书名查找图书
/**
* @return Collection
*/
public Collection getBookByCategory(String category);//按类别查?
}
|
上面代码中的BookVO是一个序列化的对象,它有以下属性,每个属性都提供了getter和setterҎ?/p>
例程4 BookVO的部分代?/p>
public class BookVO implements java.io.Serializable
{
private String name;
private String publisher;
private float price;
private String isbn;
private String description;
private String category;
private Collection authors;
public void setName(String name)
{
this.name=name;
}
public String getName()
{
return this.name;
}
?
}
|
~译好这两个cR?/p>
服务端点描述
可以使用Java2WSDL从以上定义的服务端点接口中获得服务描qͼWSDL文gQ。用Apache axis工具Q只要用以下命令即可:
java org.apache.axis.wsdl.Java2WSDL -o temp.wsdl
-l"http://localhost:8080/axis/services/BookServletService"
-n "urn:BookServletService"
-p"com.hellking.webservice" "urn:BookServletService"
com.hellking.webservice.servlet.BookServiceInterface
|
以上命o的解释:
-oQ生成的WSDL文gQ?
-lQWeb服务的位|;
-nQ这个WSDL文g的名字空_
-pQ包到名字空间的映射Q?
最后一个参数是Web服务端点接口?/p>
使用以上命o后,生成一个名为temp.wsdl Web服务描述文?/p>
服务端点实现
有了服务描述文gQ就可以使用它来生成JAX-RPC 的框Ӟq个框架使得我们~程变得单,当然您也可以直接~写实现代码Q然后部|Ԍ但是那样~程会变得困难?/p>
使用以下的命令就可以生成q个框架Q?/p>
java org.apache.axis.wsdl.WSDL2Java -o . -d Session -s -S true
-Nurn:BookServletService com.hellking.webservice.servlet temp.wsdl
|
使用q个命o后将生成以下文gQ?/p>
BookServiceInterface.javaQ新的BookServiceInterface接口Q它扩展了java.rmi.Remote接口Q?/p>
BookServiceInterfaceService.javaQ客L服务接口Q用来获得BookServiceInterface对象的引用;
BookServiceInterfaceServiceLocator.javaQ在客户端用,主要用来服务定位Q?/p>
BookServletServiceSoapBindingImpl.javaQ服务端实现c,它实CBookServiceInterface接口Q服务端的业务方法实C码就在这里编写;
BookServletServiceSoapBindingSkeleton.javaQ服务端SkeletonQ?/p>
BookServletServiceSoapBindingStub.javaQ客LStubQ?/p>
BookVO.javaQ新的BookVO序列化对象;
deploy.wsddQ部|这个Web服务的脚本;
undeploy.wsddQ卸载这个Web服务的脚本?/p>
服务端点实现cȝ基本框架已经生成出来了,我们的Q务就是往里面增加具体的业务内宏V下面我们来看具体的服务端点的实现。如例程3所C?/p>
例程5 服务端点实现c?/p>
package com.hellking.webservice.servlet;
import java.util.*;
import javax.naming.*;
import com.hellking.webservice.ejb.*;
public class BookServletServiceSoapBindingImpl
implements com.hellking.webservice.servlet.BookServiceInterface{
InitialContext init=null;
BookServiceFacadeHome facadeHome;
public BookServletServiceSoapBindingImpl()
{
try
{
init=new InitialContext();
}
catch(Exception e)
{
}
}
//业务ҎQ查找所有的图书
public java.lang.Object[] getAllBooks() throws java.rmi.RemoteException {
System.out.println("getAllBooks");
try
{
Object objref = init.lookup("ejb/bookfacade");
facadeHome = (BookServiceFacadeHome)javax.rmi.PortableRemoteObject.narrow(
objref, BookServiceFacadeHome.class);
Collection result=facadeHome.create().getAllBook();
System.out.println(result.size());
Object[] ret=new Object[result.size()];
Iterator it=result.iterator();
int i=0;
while(it.hasNext())
{
ret[i++]=it.next();
}
// System.out.println(((BookVO)ret[0]).getName());
return ret;
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
//业务ҎQ按书名查找图书
public com.hellking.webservice.BookVO getTheBookDetail(java.lang.String in0)
throws java.rmi.RemoteException {
com.hellking.webservice.BookVO ret=null;
try
{
Object objref = init.lookup("ejb/bookfacade");
facadeHome = (BookServiceFacadeHome)javax.rmi.PortableRemoteObject.narrow(
objref, BookServiceFacadeHome.class);
Collection result=facadeHome.create().getBookDetail(in0);
Iterator it=result.iterator();
while(it.hasNext())
{
ret=( com.hellking.webservice.BookVO)it.next();
}
}
catch(Exception e)
{
}
return ret;
}
//业务ҎQ按cd查找图书
public java.lang.Object[] getBookByCategory(java.lang.String in0)
throws java.rmi.RemoteException {
try
{
Object objref = init.lookup("ejb/bookfacade");
facadeHome = (BookServiceFacadeHome)javax.rmi.PortableRemoteObject.narrow(
objref, BookServiceFacadeHome.class);
System.out.println(in0);
Collection result=facadeHome.create().findByCategory(in0);
Object[] ret=new Object[result.size()];
Iterator it=result.iterator();
int i=0;
while(it.hasNext())
{
ret[i++]=it.next();
System.out.println(i);
}
return ret;
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
}
|
可以看出Q服务端点的主要d是调用EJBlg来完成业务逻辑的?/p>
需要向读者说明的是,Z和本pdW一文章中的客L框架兼容Q客L使用的值对象是com.hellking.webservice.BookVOQ而这里由WSDL2Java生成的值对象是com.hellking.webservice.servlet.BookVOQ。我们需要做以下的改动:
把生成的q些代码中的com.hellking.webservice.servlet.BookVO全部改ؓcom.hellking.webservice.BookVOQ然后在Apache axis服务配置文g中申明这个BeanMappingQ具体的声明Ҏ在后面介l?/p>
接下来的工作是编译服务端相关的文ӞBookServletServiceSoapBindingImpl、BookServletServiceSoapBindingSkeleton、BookServiceInterfaceService、BookServiceInterface?/p>
服务端点部v
启动服务器,q个服务器可以是M能够q行Apache引擎Web服务器,当然最好是同时有EJB容器和EJB容器的服务器Q如Webphere 、Weblogic、JBOSSQ如果没有EJB容器Q还需要一个额外的EJB容器Qƈ且需要更改BookServletServiceSoapBindingImpl中获得上下文QInitialContextQ的ҎQ如Q?/p>
例程6 获得上下文环?/p>
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "xxxxx");
p.put(Context.URL_PKG_PREFIXES, "xxxx");
p.put(Context.PROVIDER_URL, "xxxx");
init=new javax.naming.InitialContext(p);
|
在控制台中,转到deploy.wsdd目录下,执行以下的命令就可以完成部vQ?/p>
java org.apache.axis.client.AdminClient deploy.wsdd
|
׃我们使用的是自己的序列化Bean对象Q故要在%Web-Apps%/WEB-INF/ server-config.wsdd文g中做以下更改Q?
扑ֈ
在中间加入以下内容:
部v后您必须保?Web-Apps%/WEB-INF/classes目录下有服务端相关的c(BookServletServiceSoapBindingImpl、BookServletServiceSoapBindingSkeleton{)?/p>
在浏览器里输入(q个地址您需要根据具体情冉|改)Q?/p>
http://localhost:8080/axis/services/BookServletService?wsdl
来验证Web服务是否已经部v成功Q如果部|不成功Q您可以先尝试重新启动服务器?/p>
客户?/span>
如果服务端已l成功部|Ԍ下一步的工作是~写客户端程序了。由于用WSDL2Java已经生成了客L的框Ӟ所以我们的d相对简单了?/p>
客户端编EQ务主要有以下几个Q?/p>
- 在BookServletServiceSoapBindingStub里注册BeanMappingQ?
- ~写客户端业务代表,q里使用了JAXRPCDelegateQ?
- 更改以前的BookGUI的部分程序?
在BookServletServiceSoapBindingStub里注册BeanMapping
׃在SOAP消息中用了序列化的BookVO对象Q故在BookServletServiceSoapBindingStub中要q行BeanMapping注册。具体方法:
扑ֈBookServletServiceSoapBindingStub中的getAllBooksQgetTheBookDetailQgetBookByCategoryҎQ在每个Ҏ中的
java.lang.Object _resp = _call.invoke(new java.lang.Object[] {in0});
|
前加入以下代码:
例程7 在BookServletServiceSoapBindingStub注册BeanMapping
QName qn = new QName( "BookServletService", "BookServletService" );
_call.registerTypeMapping(com.hellking.webservice.BookVO.class, qn,
new org.apache.axis.encoding.ser.BeanSerializerFactory(com.hellking.webservice.BookVO.class, qn),
new org.apache.axis.encoding.ser.BeanDeserializerFactory(com.hellking.webservice.BookVO.class, qn));
|
注意q里的Qname要和server-config.wsdd中描q的名称I间一致。在中server-config.wsddQ我们用了以下的映:
在编写业务代表程序前Q我们先来对Web服务做一个调用测试。在试前您必须保证数据库里已经有图书信息。如果EJB和Web Application都部|好Q您可以通过以下面来往数据库里增加数据Q?/p>
http://localhost:8080/axis/insert_data.jsp
试代码如下Q?/p>
例程8 试Web服务
package com.hellking.webservice.servlet;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import com.hellking.webservice.ejb.*;
public class Client {
public static void main(String[] args) {
try {
BookServiceInterfaceServiceLocator locator=new BookServiceInterfaceServiceLocator();
BookServiceInterface myProxy=locator.getBookServletService();
Object[] c=myProxy.getAllBooks();
com.hellking.webservice.BookVO
book=(com.hellking.webservice.BookVO)c[0];
System.out.println(book.getName());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
|
如果在控制台里打印出某个图书的名字,那么验证了客户端和服务端的部v是正的。在q行下面的工作前Q请保q个试是成功的?/p>
~写客户端业务代?/span>
对于客户端程序来_业务代表直接和Web服务打交道,获得Web服务q回的数据,q做对应的处理,然后把数据返回给GUIE序QGUIE序只负责数据显C。业务代表的代码如下Q?/p>
例程9 JAXRPCDelegate业务代表
package com.hellking.webservice.servlet;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import com.hellking.webservice.ejb.*;
import java.util.*;
public class JAXRPCDelegate implements com.hellking.webservice.BookBusiness
{
BookServiceInterfaceServiceLocator locator;
com.hellking.webservice.servlet.BookServiceInterface bookService;
public JAXRPCDelegate()
{
try
{
locator=new BookServiceInterfaceServiceLocator();
bookService=locator.getBookServletService();
}
catch(Exception e)
{
}
}
public Collection getBookByCategory(String category)
{
System.out.println("by_category");
Collection ret=new ArrayList();
try
{
Object[] books=bookService.getBookByCategory(category);
System.out.println(category);
int i=0;
while(true)
{
ret.add(books[i++]);
System.out.println(i);
}
}
catch(Exception e)
{
}
return ret;
}
public Collection getAllBooks()
{
Collection ret=new ArrayList();
try
{
Object[] books=bookService.getAllBooks();
int i=0;
while(true)
{
ret.add(books[i++]);
}
}
catch(Exception e)
{
}
return ret;
}
public com.hellking.webservice.BookVO getTheBookDetail(String name)
{
System.out.println("bookdetail");
com.hellking.webservice.BookVO ret=new com.hellking.webservice.BookVO();
try
{
ret=bookService.getTheBookDetail(name);
}
catch(Exception e)
{
}
return ret;
}
}
|
和第一文章介l的JAXMDelegate一PJAXRPCDelegate 同样实现了BookBusiness接口QBookBusiness接口是以前设计的接口Q我们在q里q行重用Q这L好处是BookClientGUIE序几只要做很少的更改就可以q行?/p>
更改以前的BookClientGUI的部分程?/span>
在BookGUI构造方法里增加以下内容Q?/p>
例程10 更改BookGUIE序
public BookClientGUI()
{
business=new JAXRPCDelegate();
?
}
|
好了Q经q以上的奋战Q让我们来看q行的结果吧?/p>
java com.hellking.webservice.BookClientGUI
q行l果如图4所C?/p>

ȝ
通过以上的介l,怿读者对JAX-RPC Web服务开发已l有一个比较深ȝ认识。ȝ一下,使用JAX-RPC开发Web服务Ӟ主要有以下的工作Q?/p>
- 服务端点定义Q?
- 服务描述Q?
- 服务端点实现Q?
- 服务端点部vQ?
下一?/span>
本文已经介绍了把Servlet作ؓWeb服务端点开发Web服务的全q程Q下一将是把EJB作ؓWeb服务端点来开发?/p>
参考资?
Apache axis User's GuidesQ?http://ws.apache.org/axis/ Sun jwsdp-1_1-tutorialQ?http://java.sun.com/webservices/downloads/webservicestutorial.htmlhttp://www.ibm.com/developerworks/cn/xml/index.htmlXML & Web services专区 JAX-RPC API http://java.sun.com/webservices Jwdp1.1 http://java.sun.com/webservices 下蝲 样例代码
关于作?/span> 陈亚强:北京华园天一U技有限公司高软g工程师,擅长J2EE技术,曑֏与多个J2EE目的设计和开发,对Web服务有很大的兴趣q且有一定的目l验。热爱学习,喜欢新技术。即由电子工业出版Cև版的《J2EE企业应用开发》正在最l定EKD,目前正从事J2EE斚w的开发和J2EE Web服务斚w的图书写作。您可以通过 cyqcims@mail.tsinghua.edu.cn和他联系?/td> |

]]>