使用WSIF中的WSDL扩展特性调用本地类、EJB

来源:百度文库 编辑:神马文学网 时间:2024/06/03 05:11:31

使用WSIF中的WSDL扩展特性调用本地类、EJB

级别: 初级

肖菁 (jing.xiao@chinacreator.com), 软件工程师, 湖南省长沙铁道学院科创计算机系统集成有限公司软件中心

2004 年 1 月 01 日

本文将重点描述如何使用WSIF提供的WSDL扩展将本地java类、EJB用WSDL文档描述出来并且使用WSIF提供的统一方法进行调用,并且详细的介绍了WSIF中针对本地java类、EJB提供的WSDL扩展功能。

WSIF 提供的API允许使用统一的方法调用可以用WSDL描述的服务,而不需要了解该服务的实现机制和调用方法。WSIF中提供的WSDL扩展允许编程者将本地java类、EJB、JMS的消息队列、可以使用java连接器机制访问的其他应用虚拟成web服务,然后使用统一的调用方法访问这些服务。本文中作者将重点描述如何使用WSIF提供的WSDL扩展将本地java类、EJB用WSDL文档描述出来并且使用WSIF提供的统一方法进行调用,并且详细的介绍了WSIF中针对本地java类、EJB提供的WSDL扩展功能。

1 WSIF简介


WSIF是apache的web服务项目的一个子项目,目前版本是2.0,实际上是WSIF被提交给ASF后的第一次发布版本,命名为2.0是和以前非Apache发布的1.x版本相区别。

WSIF提供了一组简单的API来调用web服务而不需要了解该web服务的实现方式,更深入的说,WSIF是一组基于WSDL文件的API,他调用可以用WSDL文件描述的任何服务。

WSIF中提供的API允许编程者通过WSDL描述内容和web服务调用的抽象层打交道,而不是直接使用SOAP来调用web服务。编程者使用WSIF后就可以使用统一的编程模型来调用web服务而不需要了解该web服务是如何实现和被访问的。

WSIF 2.0中里面提供了下列内容的支持: SOAP(可以使用apache SOAP或者axis实现)、本地java类、EJBs、JMS services和其它可以通过java connector访问的应用。WSIF规定了特别的WSDL扩展使这些资源可以被当成WSDL描述的服务访问。

WSIF允许通过运行时分析web服务描述的元数据来实现无stub或者动态的调用一个web服务。他允许在运行时将更新的绑定实现插入到WSIF中,他允许调用的服务在运行时之前选择自己的绑定实现。

WSIF具有以下几个主要特征:

  • 以WSDL为中心和服务的抽象定义(portType)打交道,隐藏实现细节(协议绑定和服务位置)

  • 可插入式允许增加心得提供者使应用可以通过修改WSDL就可以应用新的web服务而不需要修改应用的代码

  • 可扩展很容易使用新的WSDL扩展进行试验

  • 灵活性很容易定制很容易和JNDI结合使用是服务被提供的位置更加透明





2 本地java绑定的WSDL扩展


WSIF中本地java绑定的WSDL扩展允许将WSDL中的抽象功能直接映射到本地java类的实际功能实现上,这种扩展意味着我们可以使用WSDL来描述一个本地Java类,然后使用WSIF提供的基于WSDL文件的服务调用方式来调用这个Java类。

在WSIF中,描述一个本地java类的WSDL的要素如下:

                                                                                                ?                        *                                                *                        ?                        ?                        ?                        ?                                                                                                *                                                                                                                        

下面的四个小节将详细介绍这些扩展的具体含义.

2.1 java:binding元素


使用该元素表示该绑定是一个java绑定

2.2 format:typemapping


format:typemapping元素允许定义WSDL消息中的抽象类型(在抽象服务描述中)和java类型的映射,他们表示同样的信息。元素中的Style属性用来规定目标类型系统(比如:使用本地类型系统表示抽象信息);在java类型系统中,这个属性的值必须是"java";这个属性的使用允许这种扩展被其他类型的绑定重用。Encoding属性必须是一个URI,这个URI用来指示本地类型和抽象类型的协调方式。WSIF中的历了一种特殊的encoding--"java" encoding-它告诉我们如何在WSDL扩展中的Java绑定中建立java类和一个抽象schema类型之间相对关系。Java encoding的详细情况下面有介绍。使用encoding属性允许我们建立自己的encoding来实现抽象类型和java类型之间的映射。

2.2.1 Java encoding

WSIF中的Java encoding是没有详细说明的,不需要详细说明是encoding信息只有在java对象中包含的信息通过某种方式变换时才有--比如序列化到SOAP消息中或者转化为其他类型系统中的某种表示。如果我们使用的WSIF消息只包含java类型系统中的类型,而且调用相应的java服务,那么我们只需要确保每个消息的部分使用java对象的正确类型表示就可以了(就像java绑定扩展中的typemapping元素定义的那样)。

当然,这些特殊的需要也会存在,不过WSIF中目前还没有提供,相信在WSIF的后续版本中会解决这个问题。

2.3 format:typemap

每个typemap元素将一种WSDL抽象类映射到一些方便的类型系统的某个类;在java绑定中,这种类型系统就是java类型系统。Typename属性是被映射抽象类型的类型标识符(必须是WSDL中已经被预定义的schema类型或者是本WSDL中定义的某种类型)。Elementname属性用来规定一个元素来代替一个类型(因为WSDL消息的内容可以被他们二者的任意一种描述)。Formtype属性是对应该抽象类型或者元素的java类型。它的值必须是某种原始java类型(char、byte、short、int、long、float、double)或者是某个java类的全名(包括包和类名).

2.4 java:operation

java:operation定义了抽象的WSDL操作和java方法之间的映射。Methodname属性规定和抽象操作对应的java方法的名字。Parameterorder属性和抽象操作中的parameterorder规定类似而且重载了这个规定。它定义了服务调用时输入参数的顺序;在java绑定中,它定义了被调用方法的签名。使用parameterorder属性允许我们映射一个抽象操作和一个java方法,即使他们签名对应的参数顺序不一致。Returnpart属性和java方法的返回类型对应的抽象输出消息。Methodtype属性规定被映射到的java方法是一个构造器、一个静态方法还是一个实例方法。

2.5 java:address


java:address元素是WSDL port元素的扩展,它允许通过java绑定将一个java对象定以成服务的endpoint。这种方式定义的port只能是java绑定形式的。Classname属性定了服务调用中要用到的java类的全名(包括包和类名,如:service.HellowWorld),可选择的classpath属性定义了调用之前需要设置的classpath,可选择的classloader属性定义了装载服务类的类装载器。如果是调用一个实例方法,服务使用者可以装载和实例化一个服务类,这告诉服务提供者要保证每个服务类有一个public的无参数的构造器可用。其他的被映射的java方法和构造器也必须在服务类中是定以成public的。

WSDL中的其他元素来自于WSDL中预定义的元素,大家可以参考WSDL规范的相关内容和说明。

好了,理论的东西讲了这么多,现在给大家演示一个例子,看看如何在应用中将本地的java类使用一个WSDL文件描述出来,并且使用WSIF的统一调用方法进行调用。





3 EJB绑定的WSDL扩展


WSIF中EJB绑定的WSDL扩展允许将WSDL中的抽象功能直接映射到EJB的实际功能实现上,这种扩展意味着我们可以使用WSDL来描述一个EJB,然后使用WSIF提供的基于WSDL文件的服务调用方式来调用这个EJB。

在WSIF中,描述一个EJB的WSDL的要素如下:

                                                                                                ?                        *                                                *                        ?                        ?                        ?                        ?                                                                                                *                                                                                                                        

分析它和本地java绑定的WSDL扩展,可以看到他们的不同在于ejb:binding、ejb:operation、ejb:address三个元素,下面的三个小节将介绍这三个元素的定义。

3.1 ejb:binding元素


使用该元素表示该绑定是一个ejb绑定

3.2 ejb:operation


ejb:operation定义了抽象的WSDL操作和ejb接口中提供的方法之间的映射。Methodname属性规定和抽象操作对应的java方法的名字。Parameterorder属性和抽象操作中的parameterorder规定类似而且重载了这个规定。它定义了服务调用时输入参数的顺序;在ejb绑定中,它定义了被调用方法的签名。使用parameterorder属性允许我们映射一个抽象操作和一个java方法,即使他们签名对应的参数顺序不一致。Returnpart属性和java方法的返回类型对应的抽象输出消息。EjbInterface属性用于规定被映射的方法由EJB的远程(Remote)还是本地(home)接口提供。系统默认被映射的方法由EJB的远程(Remote)接口提供。

3.3 ejb:address


java:address元素是WSDL port元素的扩展,它允许通过ejb绑定将一个ejb对象定义成服务的endpoint。这种方式定义的port只能是ejb绑定形式的。Classname属性定了服务调用中要用到的EJB的本地接口类的全名(包括包和类名,如:org.vivianj.wsif.HelloWorldHome),可选择的aechive属性定义了调用之前需要设置的classpath,可选择的classloader属性定义了装载服务类的类装载器。所有被映射的方法必须在服务类中是定义成public的。InitialContextFactory(初始化上下文的工厂类)和jndiProviderURL(JNDI提供者的URL)属性用于指定完成EJB的jndi查询时需要设置的相关参数。





4 HelloWorld实例


在我们的例子中,我们提供了两种实现,一种是本地类HelloWorld.java,一种是一个Session EJB,他们实现同样的功能。

本地类一个方法(getHelloString),它根据传入的参数返回一个字符串say Hello To + %输入的参数%,该类的实现代码如下:

4.1 HelloWorld.java实例


//HelloWorld.java                        package service;                        public class  HelloWorld                        {                        public String getHelloString(String param){                        return "Say hello to : " + param;                        }                        }                        

Session EJB的实现请查看文章后面的工程(wsif-ejb.jar)中的实现,下面是该ejb实现类的代码。

4.2 EJB实现类的代码

package org.vivianj.wsif;                        import java.rmi.RemoteException;                        public class HelloWorldBean implements javax.ejb.SessionBean {                        private javax.ejb.SessionContext mySessionCtx;                        public javax.ejb.SessionContext getSessionContext() { return mySessionCtx;}                        public void setSessionContext(javax.ejb.SessionContext ctx) { mySessionCtx = ctx; }                        public void ejbCreate() throws javax.ejb.CreateException {}                        public void ejbActivate() {}                        public void ejbPassivate() {}                        public void ejbRemove() {}                        public String getHelloString(String param){                        String result = "Say hello to : " + param;                        return result;                        };                        }                        

4.3 如何编写WSDL文档来描述本地类

现在我们将根据WSIF中java绑定的WSDL扩展来使用WSDL文档描述这个java类,下面是作者根据WSIF中的localjava例子中的WSDL文档编写的一个WSDL文档(HelloWorld.wsdl),它描述了我们编写的HelloWorld.java类,供大家参考:

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        

其中的binding元素是我们重点关注的:

  • 它的第一个子元素就是java:binding,表示这是一个java绑定类型

  • format:typeMapping的元素和含义请参考上面关于java绑定的WSDL扩展的相关内容

  • format:typeMap定义了java.lang.String和xsd:String之间的类型映射

  • java:operation定义了WSDL中的抽象方法getHelloString和java方法中的getHelloString方法的映射。它的parameterorder属性规定了输入参数的顺序,methodtype属性表示这是一个实例方法调用。它的子元素input和output定了该方法调用用到的输入输出参数等

service元素的java:address子元素表示这是一个绑定到本地java类的port.

其他的元素都是WSDL规范中关于web服务的相关预定义元素,请大家参考WSDL规范中的相关定义。

4.4 如何编写WSDL文档来描述EJB

现在我们将根据WSIF中ejb绑定的WSDL扩展来使用WSDL文档描述这个EJB,下面是作者针对HelloWorld实例编写的一个WSDL文档(EJBWsdl.wsdl),它描述了我们编写的HelloWorld实例EJB,供大家参考:

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        

其中的binding元素是我们重点关注的:

  • 它的第一个子元素就是ejb:binding,表示这是一个ejb绑定类型

  • format:typeMapping的元素和含义请参考上面关于ejb绑定的WSDL扩展的相关内容

  • format:typeMap定义了java.lang.String和xsd:String之间的类型映射

  • ejb:operation定义了WSDL中的抽象方法getHelloString和EJB中的getHelloString方法的映射。它的parameterorder属性规定了输入参数的顺序,interface属性表示这个方法由EJB的远程方法提供。它的子元素input和output定了该方法调用用到的输入输出参数等。

service元素的ejb:address子元素表示这是一个绑定到ejb的port. InitialContextFactory和jndiProviderURL定义了初始化工厂和提供者的URL。

4.5 使用WSIF提供的API调用

好了,现在我们已经将自己编写的本地类、EJB用WSDL描述出来了,接下来的工作将是演示如何使用WSIF提供的API通过使用我们编写的WSDL文档来调用这个服务。

下面这段代码是作者编写的调用这两个服务的Run.java, 供大家参考。它需要我们的WSDL文件名作为参数,处理结果是将服务调用的返回结果打印在控制台上。

//Run.java                        package dynamic;                        import javax.xml.namespace.QName;                        import org.apache.wsif.WSIFMessage;                        import org.apache.wsif.WSIFOperation;                        import org.apache.wsif.WSIFPort;                        import org.apache.wsif.WSIFService;                        import org.apache.wsif.WSIFServiceFactory;                        public class Run {                        public static void main(String[] args) throws Exception {                        // 第一个参数是 WSDL文件的位置和名字                        // 创建服务工厂(service factory)                        WSIFServiceFactory factory = WSIFServiceFactory.newInstance();                        WSIFService service =                        factory.getService(                        args[0],                        null,                        null,                        "http://wsifservice.helloworld/",                        "HelloWorld");                        // 参数http://wsifservice.helloworld/对应于我们WSDL文档中的targetNamespace                        //参数HelloWorld对应于WSDL文档中的porttype                        // 参数类型映射                        service.mapType(                        new QName("http://wsifservice.helloworld", "param"),                        Class.forName(                        "java.lang.String"));                        // 获得port                        WSIFPort port = service.getPort();                        // 创建操作(operation)                        WSIFOperation operation = port.createOperation("getHelloString");                        // 创建和该操作调用相关的输入/输出/错误信息                        WSIFMessage input = operation.createInputMessage();                        WSIFMessage output = operation.createOutputMessage();                        WSIFMessage fault = operation.createFaultMessage();                        // 组装输入信息                        input.setObjectPart("param", "xiaojing");                        // 执行调用                        if (operation.executeRequestResponseOperation(input, output, fault)) {                        // 调用成功,从返回信息中获取我们需要的信息                        // message                        String zipInfo =                        (String) output.getObjectPart("resp");                        // resp就是我们的WSDL文档中的output对应的消息(message元素)中的参数(part元素)的名字                        System.out.println(zipInfo);                        } else {                        System.out.println("Invocation failed");                        // 调用失败,处理错误信息                        }                        }                        }                        

[注] 调用这两个服务的代码实际上是一模一样的,除了输入的WSDL文件名不同。这也是WSIF的重要特性:

  • 通过WSDL文件调用服务,不管他是如何实现和被访问的。

  • 尽可能的将修改限制在WSDL文件中而不需要修改应用代码

4.6 编译运行

4.6.1 设置环境变量

由于编译和运行这个程序需要将大量的.jar文件设置到classpath中,所以请各位参考下载的WSIF包中的classpath.bat和lcp.bat编写自己的脚本来实现环境变量的设置。后面的下载的包里面有作者编写的环境变量设置脚本,供大家参考。

4.6.2 运行

设置好环境变量后,编译程序,然后就可以使用下面的命令来执行这个程序了:

java dynamic.Run HelloWorld.wsdl                        

它执行后的显示结果如下图:



4.7 可改进的地方

如果各位有心的话,应该可以发现,其实我们编写的Run.java中的有些参数仍然来自于我们的WSDL文档、和客户应用基本没有关联(比如:factory.getService方法调用的第四个参数和第五个参数,他们来源于WSDL中的一些元素,而且和客户应用基本没有关联),所以如果你可以做的话,你可以自己编写一个基于SAX的XML文档解析类,将这些和WSDL关联而和应用代码不很密切的内容从WSDL中直接取值,而不是依赖于编程者的输入。





5 复杂类型


如果被输入的参数或者返回的参数不是java的原始类型,那么我们的类型映射就没有这么简单了。大家可以参考WSIF中提供的localjava的例子,它的返回类型都用到了 一个address的类型,它是该实例中用到的一个自定义的类型,对应于一个java类,WSDL文档的编写也和一般的不同,请参考WSIF中提供的AddressBook.wsdl实例。不过一点可以给大家提示的是,编写好了wsdl文档后,其中复杂类型对应的java类不需要自己编写,可以使用Axis提供的wsdl2java功能来生成,命令的使用类似于:java org.apache.axis.wsdl.WSDL2Java (WSDL-file-URL),详细的帮助请大家参考Axis项目的用户指导(User Guide)





6 相关工具


研究到这一步的时候,作者想起来另外两个提供了类似功能的工具:AXIS和WSAD(Websphere studion application develop).

  • Axis中的Java2wsdl功能可以为一个本地类生成将它发布为web服务的WSDL文件,只需要设置一些参数而已,这个作者经过研究发现原来WSIF中已经包含了Axis的jar文件,应该是WSIF直接继承了这项功能,所以应该是使用了同样的原理,只是使用范围不同而已。

  • WSAD中提供的向导功能支持将java类、EJB等内容通过向导发布成web服务,相信也是采用了和这个相同的原理




7 总结


WSIF允许使用者用WSDL文件描述本地java类、EJB,也就是将本地java类、EJB虚拟成一个Web服务,然后使用WSIF提供的API调用这个服务,达到类似于通过服务调用本地类、EJB的效果。

本文中作者详细的介绍了WSIF中提供的对java、EJB绑定的WSDL的支持,如何编写WSDL文档来描述和映射一个本地的java类、EJB,给出了编写的WSDL文档的实例和简单的说明,同时给出了如何通过WSIF提供的统一的调用方式通过WSDL文档调用本地类、EJB的例子。通过实例大家可以看到我们实现了服务调用和服务实现的分离,任何服务实现和相关的映射基本上只会影响到服务的实现和WSDL的修改,不会影响到服务调用段代码的修改。