分享

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

 chanvy 2008-11-14

使用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的要素如下:

<definitions .... >
                        <!-- Java binding -->
                        <binding ... >
                        <java:binding/>
                        <format:typeMapping style="uri" encoding="..."/>?
                        <format:typeMap typeName="qname"|elementName="qname" formatType="nmtoken"/>*
                        </format:typeMapping>
                        <operation>*
                        <java:operation
                        methodName="nmtoken"
                        parameterOrder="nmtoken"?
                        returnPart="nmtoken"?
                        methodType="instance|static|constructor"? />?
                        <input name="nmtoken"? />?
                        <output name="nmtoken"? />?
                        <fault name="nmtoken"? />?
                        </operation>
                        </binding>
                        <service ... >
                        <port>*
                        <java:address
                        className="nmtoken"
                        classPath="nmtoken"?
                        classLoader="nmtoken"? />
                        </port>
                        </service>
                        </definitions>
                        

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

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的要素如下:

<definitions .... >
                        <!-- EJB binding -->
                        <binding ... >
                        <ejb:binding/>
                        <format:typeMapping style="uri" encoding="..."/>?
                        <format:typeMap typeName="qname"|elementName="qname" formatType="nmtoken"/>*
                        </format:typeMapping>
                        <operation>*
                        <ejb:operation
                        methodName="nmtoken"
                        parameterOrder="nmtoken"?
                        returnPart="nmtoken"?
                        interface="home|remote"? />?
                        <input name="nmtoken"? />?
                        <output name="nmtoken"? />?
                        <fault name="nmtoken"? />?
                        </operation>
                        </binding>
                        <service ... >
                        <port>*
                        <ejb:address
                        className="nmtoken"
                        jndiName="nmtoken"?
                        initialContextFactory="nmtoken"?
                        jndiProviderURL="url"?
                        archive="nmtoken"? />
                        </port>
                        </service>
                        </definitions>
                        

分析它和本地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类,供大家参考:

<?xml version="1.0" ?>
                        <definitions targetNamespace="http://wsifservice.helloworld/"
                        xmlns:tns="http://wsifservice.helloworld/"
                        xmlns:typens="http://wsiftypes.helloworld.ient.localjava/"
                        xmlns:xsd="http://www./1999/XMLSchema"
                        xmlns:soap="http://schemas./wsdl/soap/"
                        xmlns:format="http://schemas./wsdl/formatbinding/"
                        xmlns:java="http://schemas./wsdl/java/"
                        xmlns="http://schemas./wsdl/">
                        <!-下面这一段定义了服务调用中使用的消息-->
                        <message name="getHelloStringRequestMessage">
                        <part name="param" type="xsd:string"/>
                        </message>
                        <message name="getHelloStringResponseMessage">
                        <part name="resp" type="xsd:string"/>
                        </message>
                        <!-- port type declns -->
                        <portType name="HelloWorld">
                        <operation name="getHelloString">
                        <input name="getHelloStringRequest" message="tns:getHelloStringRequestMessage"/>
                        <output name="getHelloStringResponse" message="tns:getHelloStringResponseMessage"/>
                        </operation>
                        </portType>
                        <!-- binding declns -->
                        <binding name="JavaBinding" type="tns:HelloWorld">
                        <java:binding/>
                        <format:typeMapping encoding="Java" style="Java">
                        <format:typeMap typeName="xsd:string" formatType="java.lang.String" />
                        </format:typeMapping>
                        <operation name="getHelloString">
                        <java:operation
                        methodName="getHelloString"
                        parameterOrder="param"
                        methodType="instance" />
                        <input name="getHelloStringRequest"/>
                        <output name="getHelloStringResponse"/>
                        </operation>
                        </binding>
                        <!-- service decln -->
                        <service name="HelloWorldService">
                        <port name="JavaPort" binding="tns:JavaBinding">
                        <java:address className="service.HelloWorld"/>
                        </port>
                        </service>
                        </definitions>
                        

其中的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,供大家参考:

<?xml version="1.0" ?>
                        <definitions targetNamespace="http://wsifservice.helloworld/"
                        xmlns:tns="http://wsifservice.helloworld/"
                        xmlns:typens="http://wsiftypes.rvice.ejb/"
                        xmlns:xsd="http://www./1999/XMLSchema"
                        xmlns:soap="http://schemas./wsdl/soap/"
                        xmlns:format="http://schemas./wsdl/formatbinding/"
                        xmlns:ejb="http://schemas./wsdl/ejb/"
                        xmlns="http://schemas./wsdl/">
                        <!-- message declns -->
                        <message name="GetHelloStringRequestMessage">
                        <part name="param" type="xsd:string"/>
                        </message>
                        <message name="GetHelloStringResponseMessage">
                        <part name="resp" type="xsd:string"/>
                        </message>
                        <!-- port type declns -->
                        <portType name="HelloWorld">
                        <operation name="getHelloString">
                        <input name="GetHelloStringRequest" message="tns:GetHelloStringRequestMessage"/>
                        <output name="GetHelloStringResponse" message="tns:GetHelloStringResponseMessage"/>
                        </operation>
                        </portType>
                        <!-- binding declns -->
                        <binding name="EJBBinding" type="tns:HelloWorld">
                        <ejb:binding/>
                        <format:typeMapping encoding="Java" style="Java">
                        <format:typeMap typeName="xsd:string" formatType="java.lang.String" />
                        </format:typeMapping>
                        <operation name="getHelloString">
                        <ejb:operation
                        methodName="getHelloString"
                        parameterOrder="param"
                        interface="remote"
                        returnPart="resp" />
                        <input name="GetHelloStringRequest"/>
                        <output name="GetHelloStringResponse"/>
                        </operation>
                        </binding>
                        <!-- service decln -->
                        <service name="HelloStringService">
                        <port name="EJBPort" binding="tns:EJBBinding">
                        <ejb:address className="org.vivianj.wsif.HelloWorldHome"
                        jndiName="ejb/HelloWorld"
                        initialContextFactory="weblogic.jndi.WLInitialContextFactory"
                        jndiProviderURL="t3://202.197.40.36:7001"
                        archive="e:/wsifTest/ejbbinding/wsifejb.jar;D:/bea/weblogic81/server/lib/weblogic.jar" />
                        </port>
                        </service>
                        </definitions>
                        

其中的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的修改,不会影响到服务调用段代码的修改。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多