IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  SOA and Web services  >

使用EJB2.1无状态会话Bean作为Web服务端点

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 初级

陈亚强 (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服务。

阅读本文前您需要以下的知识和工具:

  • J2EESDK1.4BETA,可以从 http://java.sun.com/j2ee下载;
  • 了解Web服务的相关概念,了解Web服务动态调用、动态代理客户端的概念;
  • 了解EJB等相关概念;
  • 一般的Java编程知识。

本文的参考资料见 参考资料

本文的全部代码在这里 下载

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和他联系。




对本文的评价

太差! (1)
需提高 (2)
一般;尚可 (3)
好文章 (4)
真棒!(5)

建议?







回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款