级别: 初级 陈亚强 (cyqcims@mail.tsinghua.edu.cn), 高级软件工程师, 北京华园天一科技有限公司
2003 年 11 月 01 日 本文介绍怎样在J2EE1.4平台下使用EJB2.1规范开发、打包、部署Web服务。
本文是J2EE Web服务开发系列文章的第八篇,在前一篇文章中,介绍了J2EE1.4 平台下Web服务新的构架和Web服务开发的步骤等,我们已经知道,EJB2.1无状态会话Bean可以直接部署成Web服务。在本文将介绍怎样在J2EE1.4平台下使用EJB2.1规范开发、打包、部署Web服务。
阅读本文前您需要以下的知识和工具:
本文的参考资料见
参考资料。
本文的全部代码在这里
下载。
EJB2.1和Web服务
在J2EE1.3平台里,要把EJB暴露为Web服务,有两种选择:
- 使用相应的工具把EJB部署为Web服务;
- 通过Servlet作为中介,Servlet部署为Web服务端点,然后在Servlet调用EJB中的业务方法。
在J2EE1.4平台中,可以对EJB技术进行了升级,使得无状态会话Bean可以直接部署为Web服务端点。这样,在J2EE1.4平台下,开发Web服务将更加简单。
这样,EJB2.1中的无状态会话Bean可以有三种不同的客户端:本地接口的客户端、远程接口客户端和Web服务客户端。EJB的Web服务客户端视图通过WSDL文档描述。
下面介绍怎么在J2EE1.4平台下使用EJB2.1规范开发、打包、部署一个简单的Web服务。为了促进读者的理解,在开发中不使用任何IDE工具,全部采用手工的方式进行。
开发和打包
1、下载安装服务器后,需要设置环境变量。
可以使用以下的脚本来设置环境变量:
Set J2EE_HOME=J2EE安装目录
Set CLASSPATH =%CLASSPATH%;%J2EE_HOME%\\lib\\j2ee.jar;.
Set Path =%Path%;%J2EE_HOME%\\bin;.
|
2、创建初始目录。
创建一个如下的初始目录:
+EncryptionService
+META-INF(application.xml;sun-j2ee-ri.xml )
+ejb
+META-INF(ejb-jar.xml; webservices.xml;mapping.xml)
-*.java文件(EJB相关的类)
-config.xml
|
3、定义Web服务接口
在这里,我们使用EJB2.1规范来开发一个简单的Web服务,这个服务就是提供简单的加密服务。
在开发服务实现类前,需要定义一个服务接口,如例程1所示。
例程1 定义Web服务接口(EncryptionService.Java)
package com.hellking.webservice.ejb;
import java.rmi.RemoteException;
import java.rmi.Remote;
/**
*Web服务接口,定义了两个方法。
*/
public interface EncryptionService extends Remote
{
/**
*用户对数据加密。
*/
public String encrypt (String source) throws RemoteException;
/**
*用于对数据解密。
*/
public String decrypt (String source) throws RemoteException;
}
|
需要注意的是,这里使用的是EJB2.1无状态会话Bean,它作为Web服务端点时可以不提供Home接口和Remote、Locale接口,它提供的是Web服务端点接口,这个接口扩展了Remote接口。
4、实现服务端点接口
定义了服务端点接口,接下来的任务就是开发无状态会话Bean。无状态会话Bean同样需要实现SessionBean接口,服务端点接口定义的方法在会话Bean中实现,如例程2所示。
例程2 无状态会话Bean(EncryptionServiceEJB.java)
package com.hellking.webservice.ejb;
import java.rmi.RemoteException;
import javax.ejb.*;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.*;
/**
*实现Web服务接口的无状态会话Bean
*/
public class EncryptionServiceEJB implements SessionBean
{
/**
*以下是EJB的例行方法
*/
public void ejbCreate ()
{
}
public void ejbRemove ()
{ }
public void ejbActivate ()
{ }
public void ejbPassivate ()
{ }
public void setSessionContext (SessionContext sc)
{ }
/**
*业务方法:对输入数据进行加密后返回。
*/
public String encrypt (String source)
{
//这里只是一个示例,没有进行实现,只是简单的把输入的字符串进行反转。
StringBuffer ret=new StringBuffer();
for(int i=source.length()-1;i>-1;i--)
{
ret.append(source.charAt(i));
}
return ret.toString();
}
/**
*业务方法:对输入的数据进行解密后返回。
*/
public String decrypt (String source)
{
return encrypt(source);
}
}
|
在这里,EncryptionServiceEJB和一般的无状态会话Bean没有什么区别,它同样实现了SessionBean接口,注意它的create方法不能接收任何参数。
5、创建EJB描述:
用于部署成Web服务的无状态会话Bean的描述符和普通的无状态会话Bean不同,如例程3所示。
例程3 无状态会话Bean的部署描述(ejb-jar.xml)
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="2.1"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd">
<display-name>EncryptionServiceEJB</display-name>
<enterprise-beans>
<session>
<display-name>EncryptionServiceEJB</display-name>
<ejb-name>EncryptionServiceEJB</ejb-name>
<service-endpoint>com.hellking.webservice.ejb.EncryptionService</service-endpoint>
<ejb-class>com.hellking.webservice.ejb.EncryptionServiceEJB</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<security-identity>
<use-caller-identity/>
</security-identity>
</session>
</enterprise-beans>
<assembly-descriptor>
…
</ejb-jar>
|
在这个新的部署描述符中,使用<service-endpoint>指定了服务端点,同时,必须指定EJB为无状态会话Bean。
6、生成Web服务描述:
下面的任务就是生成一个Web服务描述,我们通常使用工具来生成这个描述符。在这里使用J2EE提供的wscompile工具来生成。
在使用wscompile工具生成web服务描述前,首先手工编写一个简单的XML描述,如例程4所示。
例程4 config.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration
xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<service
name="MyEncryptionService"
targetNamespace="urn:Encryption"
typeNamespace="urn:Encryption"
packageName="encryptionservice">
<interface name="com.hellking.webservice.ejb.EncryptionService"/>
</service>
</configuration>
|
在这个描述中,指定了目标的名称空间、包的名字和Web服务端点接口:EncryptionService。
有了这个描述,就可以使用以下的命令生成一个Web服务描述:
c:\\ EncryptionService\\ejb \\>wscompile -define -d . -nd . -classpath . config.xml
|
注意,在使用这个命令前,请确保 %J2EE_HOME%\\BIN目录包含在PATH环境变量中。
上面的命令生成了一个MyEncryptionService.wsdl Web服务描述,它包含的内容如例程5所示。
例程5 生成的MyEncryptionService.wsdl Web服务描述
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="MyEncryptionService" targetNamespace="urn:Encryption"
xmlns:tns="urn:Encryption" xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<types/>
<message name="EncryptionService_decrypt">
…
<service name="MyEncryptionService">
<port name="EncryptionServicePort" binding="tns:EncryptionServiceBinding">
<soap:address location="REPLACE_WITH_ACTUAL_URL"/>
</port></service></definitions>
|
这里的Web服务端点地址是REPLACE_WITH_ACTUAL_URL,在部署时会自动改变。
7、编写一个Web服务映射文件:
在ejb\\META-INF目录下新建一个mapping.xml文件,然后编辑这个描述符,如例程6所示。
例程6 mapping.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE java-wsdl-mapping PUBLIC
"-//IBM Corporation, Inc.//DTD J2EE JAX-RPC mapping 1.0//EN"
"http://www.ibm.com/standards/xml/webservices/j2ee/j2ee_jaxrpc_mapping_1_0.dtd">
<java-wsdl-mapping>
<package-mapping>
<package-type>Encryptionservice</package-type>
<namespaceURI>urn:Encryption</namespaceURI>
</package-mapping>
</java-wsdl-mapping>
|
在这个描述符中,指定了Java和WSDL之间的映射关系,在这里简单的指定了包的类型和名称空间URI之间的映射。
8、编写一个webservices的描述符:
在ejb\\META-INF目录下新建一个webservices.xml文件,然后编辑这个描述符,如例程7所示。
例程7 webservices.xml
<!DOCTYPE webservices PUBLIC "-//IBM Corporation, Inc.//DTD J2EE Web services 1.0//EN"
"http://www.ibm.com/standards/xml/webservices/j2ee/j2ee_web_services_1_0.dtd">
<webservices>
<description>desc</description>
<webservice-description>
<webservice-description-name>EncryptionServiceEJB</webservice-description-name>
<wsdl-file>META-INF/MyEncryptionService.wsdl</wsdl-file>
<jaxrpc-mapping-file>META-INF/mapping.xml</jaxrpc-mapping-file>
<port-component>
<description>port component description</description>
<port-component-name>EncryptionServicePort</port-component-name>
<wsdl-port>
<namespaceURI>urn:Encryption</namespaceURI>
<localpart>EncryptionServicePort</localpart>
</wsdl-port>
<service-endpoint-interface>com.hellking.webservice.ejb.EncryptionService
</service-endpoint-interface>
<service-impl-bean>
<ejb-link>EncryptionServiceEJB</ejb-link>
</service-impl-bean>
</port-component>
</webservice-description>
</webservices>
|
webservices.xml描述了Web服务,这个描述符是和J2EE平台相关的,不像MyEncryptionService.wsdl 描述文件是Web服务标准描述。webservices.xml中指定了Web服务描述文件MyEncryptionService.wsdl的位置,同时也指定了WSDL-PORT、服务端点接口和服务实现的EJB。
9、EJB打包
现在已经把相关的类和描述符都准备好了,接下来的任务就是打包。无状态会话 EJB 服务实现 Bean 打包成包含类文件和 WSDL 文件的 EJB-JAR。打包规则遵循那些由 Enterprise JavaBean 规范定义的规则。另外,Web 服务部署描述符在 EJB-JAR 文件中的位置是 META-INF/webservices.xml。
把生成的MyEncryptionService.wsdl拷贝到ejb\\META-INF目录下,然后使用以下的命令来打包:
c:\\ EncryptionService\\ejb\\>jar cvf ejb.jar com META-INF
|
10、Application打包
EncryptionService\\MEAT-INF目录下创建两个文件:application.xml和sun-j2ee-ri.xml,application.xml的内容如例程8所示。
例程8 application.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application PUBLIC '-//Sun Microsystems,
Inc.//DTD J2EE Application 1.3//EN'
'http://java.sun.com/dtd/application_1_3.dtd'>
<application>
<display-name>EncryptionServiceApp</display-name>
<description>Application description</description>
<module>
<ejb>ejb.jar</ejb>
</module>
</application>
|
sun-j2ee-ri.xml是和J2EESDK相关的描述,在这里就不介绍,请参考本文的源代码。
使用以下的命令把EJB和相关描述打包成EAR归档:
c:\\ EncryptionService\\ejb\\>copy ejb.jar ../
c:\\ EncryptionService\\ejb\\>cd..
c:\\ EncryptionService\\\\>jar cvf encryptservice.ear ejb.jar META-INF
|
部署
到目前为止,EJB和相关的描述已经打包好了,接下来的任务就是部署,你可以使用图形界面部署,也可以使用J2EESDK提供的命令来部署。在这里介绍使用命令部署的方式。
11、启动J2EE服务器
12、部署
在控制台下使用以下的命令来完成部署:
c:\\ EncryptionService\\>deploytool -deployModule encryptservice.ear
|
当在控制台接收到以下的信息时表示部署完成:
Remote message: Deployment of EncryptionServiceApp is complete..
|
如果部署时出现错误,请仔细阅读提供的错误。可以使用以下的命令来查看系统中部署的模块:
deploytool -listModules ear
|
如果部署是正确的,那么可以看到名称为"EncryptionServiceApp"的模块被列举出来。
13、测试部署是否成功:
最后可以在浏览器里输入以下的地址来查看Web服务描述:
http://127.0.0.1:8000/encryptionservice?WSDL
|
如果出现如图1所示的界面,那么Web服务就部署成功!
开发客户端
Web服务是跨平台的、跨语言的,所以Web服务的客户端使用的语言和平台和它的服务端没有必然的关系。比如我们可以在.Net下开发、部署Web服务,然后在J2EE平台下调用它,或者使用在J2EE平台下开发、部署Web服务,然后使用VB来调用它。
在这里,我们开发一个动态代理客户端来调用这个Web服务,如例程9所示。
例程9 Web服务动态代理客户端(J2SEClient.java)
package com.hellking.webservice;
…//imports
/**
*Web服务动态代理客户端,需要JAX-RPC运行类库来支持。
*/
public class J2SEClient
{
public static void main(String[] args) {
try {
/**
*在使用这个方式时调用Web服务时,需要指定以下参数:
*Web服务调用的地址:urlString;
*名称空间URI:nameSpaceUri;
*Web服务的名字:serviceName;
*服务的Port名字:portName。
*/
String UrlString = "http://127.0.0.1:8000/encryptionservice?WSDL";
String nameSpaceUri = "urn:Encryption";
String serviceName = "MyEncryptionService";
String portName = "EncryptionServicePort";
URL encryptWsdlUrl = new URL(UrlString);
//创建一个ServiceFactory对象。
ServiceFactory serviceFactory =
ServiceFactory.newInstance();
//通过ServiceFactory对象创建一个调用Web服务的Service对象。
Service encryptService =
serviceFactory.createService(encryptWsdlUrl,
new QName(nameSpaceUri, serviceName));
//获得服务端点实例。
com.hellking.webservice.ejb.EncryptionService myProxy =
( com.hellking.webservice.ejb.EncryptionService) encryptService.getPort(
new QName(nameSpaceUri, portName),
com.hellking.webservice.ejb.EncryptionService.class);
//调用Web服务。
String result=myProxy.encrypt("hello");
System.out.println("调用Web服务返回的结果:"+result);
String r2=myProxy.decrypt(result);
System.out.println("调用Web服务返回的结果:"+r2);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
|
启动J2EE服务器,然后运行这个客户端,如果一切正常,那么在客户端将打印从Web服务返回的调用结果。
总结
通过上面的一步步的学习,希望能在您的机器上也运行了第一个符合EJB2.1规范的Web服务。如果不能成功运行这个例子,那么首先检查J2EE服务器的配置是否正确。笔者在J2EESDK1.4Beta1下测试通过。
参考资料
关于作者  | |  | 陈亚强:北京华园天一科技有限公司高级软件工程师,擅长J2EE技术,曾参与多个J2EE项目的设计和开发,对Web服务有很大的兴趣并且有一定的项目经验。热爱学习,喜欢新技术,曾参与多本图书的写作。好交朋友,您可以通过
cyqcims@mail.tsinghua.edu.cn和他联系。
|
对本文的评价
|