UML软件工程组织

 

 

在WEBLOGIC SERVER 10中使用JAX-WS和JAXB

2008-04-22 作者:Mike Wooten 出处:dev2dev.bea.com.cn

 


摘要

JAX-WS(Java Architecture for Web Services)是JAX-RPC的后续版本。它是一种基于标准的API,可用于编写、汇编和部署Java Web services。JAXB(Java Architecture for XML Binding)是一种Java/XML绑定技术。JAX-WS将使用JAXB处理所有的Java绑定操作。

本文将简要介绍BEA WebLogic Server 10.1中所支持的JAX-WS 2.0和JAXB 2.0。读者可通过文章中的示例代码入门。

JAXB 2.0 Java开发人员快速指南

JAX-WS使用JAXB处理所有的Java绑定操作,因此本文将重点讨论与JAX-WS有关的JAXB。熟练的Java开发人员通常都很繁忙。考虑到这一点,我将主要讨论以下两方面内容:

  • 目前使用JAXB 2.0可以完成的任务。
  • 目前使用JAXB 2.0无法完成的任务。

如果您忙得连阅读这篇文章的时间都没有,请直接 下载 本文所提供的示例代码。压缩包中的README文件介绍了具体的操作步骤。

使用JAXB 2.0可以完成那些任务?

下面列出了使用JAXB 2.0可以实现的一些比较有趣的任务。我的意思并不是说使用其他Java-to-XML/XML-to-Java绑定技术就不能实现这些操作。我只是列出了使用JAXB 2.0可以完成的操作:

  • 通过含有一个或多个<xs:schema>元素的WSDL生成Java对象图。这些<xs:schema>元素可以使用<xs:import>和<xs:include>元素引用其他的<xs:schema>元素。
  • 通过Java对象图生成XML Schema文档。
  • 利用快速信息集分析程序(SAX和StAX)和串行器(serializer)。
  • 随机访问XML文档的XML信息集。
  • 直接在XML Schema文件或外部绑定自定义文件中嵌入绑定声明。
  • 在取消编组(unmarshalling)时使用基于事件的流模型。
  • 编组二进制数据(比如说,处理MTOM和MIME附件)。
  • 开发自己的插件,扩展JAXB代码生成功能。然后,这些插件(作为类封装在一个.jar文件中)可以访问JAXB生成的代码,还可生成另外的类/方法/字段/注释/评论。
  • 编写定制代码将已有类转化为由JAXB模式编译程序生成的类。

使用JAXB 2.0无法完成的操作

下面列出了使用JAXB 2.0无法完成的操作(或者说是我不知道如何实现):

  • 通过XML文档生成XML模式。实际上,这并没有什么大不了的。因为其他工具(如Stylus Studio、XMLSpy和XMLBuddy Pro)可以实现这一功能。
  • 匹配StAX或SAX解析的性能指数。解析同一XML文档时,SAX需要10ms,StAX需要46ms,而JAXB 2.0需要59ms。

使用JAXB进行Java绑定的示例POJO JAX-WS Web服务

JAX-WS是一种基于标准的API,可用于编写、汇编和部署Java Web services。它使用JAXB处理所有与此相关的Java绑定操作。JAX-WS 2.0/2.1并不支持JAX-RPC或Apache Beehive XMLBean类型的使用,只支持JAXB类型的XMLBean。

JAX-WS提供了两个编程模型,用于开发Web服务端点。

1.从Java开始(Start from Java)——此编程模型使您能够充分地控制Web服务端的方法签名(method signature)中所使用的Java数据类型。在该模型中,您可以手动编写(或者使用工具生成)Java对象,这些Java对象将作为Web服务操作及JWS注释的输入参数和返回值使用。

BEA为“从Java开始”编程模型提供了jwsc Ant任务。它将Glassfish wsimport Ant任务封装在内部(即从内部调用)。因此我们不用直接在build.xml文件中使用Ant任务。

Ant Task Reference for jwsc 这篇BEA文档介绍了如何使用<jws>元素的type="JAXWS"属性生成JAXB工件。jwsc Ant任务中的<binding>子元素可用于指定所要使用的JAXB绑定自定义文件。

2.从WSDL开始(Start from WSDL)——此编程框架将通过WSDL的内容生成Web服务端点的主干代码。WSDL中<xs:schema>部分生成的Java数据类型将作为Web服务操作的输入参数和返回值使用。

BEA为“从WSDL开始”编程模型提供了wsdlc Ant任务。它将Glassfish wsimport Ant任务封装在内部。因此我们不用直接在build.xml文件中使用Ant任务。

Ant Task Reference for wsdlc 这篇BEA文档介绍了如何使用<wsdlc>元素的type="JAXWS"属性生成JAXB工件。wsdlc Ant任务中的<binding>子元素可用于指定所要使用的JAXB绑定自定义文件。

文章的其余部分将介绍如何创建、部署和测试基于POJO(Plain-Old Java Object)的JAX-WS Web服务端点,即DataStagingService。

创建定制文件

第一步需要创建一个JAX-WS定制文件。该文件还可以充当JAXB绑定定制文件,并允许我们控制JAX-WS和JAXB构建时流程,以及它们生成的工件。

定制文件是一个XML文档,它符合XML模式的http://java.sun.com/xml/ns/jaxws和http://java.sun.com/xml/ns/jaxb名称空间。有关创建该定制文件的详细信息,请参阅 JAX-WS 2.0 Beta Customizations

DataStagingService Web服务的JAX-WS定制文件相当小,因此我列出了该文件的内容:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<bindings
 xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
 xmlns="http://java.sun.com/xml/ns/jaxws"
 wsdlLocation="DataStagingService2.wsdl"
> 
  <bindings
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    node="wsdl:definitions"
  >
    <package name="services.datastaging">
      <jxb:javadoc>
        <![CDATA[<body>Package level documentation for generated 
                       package services.datastaging.</body>]]>
      </jxb:javadoc>
    </package>
    <jxb:schemaBindings>
      <jxb:package name="com.acmeworld.irad.services.datastaging"/>
    </jxb:schemaBindings>
  </bindings>
</bindings>

我将主要使用该定制文件控制生成类的Java包名。您可以在该定制文件中实现更多其他功能,请参阅 JAX-WS 2.0 Beta Customizations

下面的清单展示了DataStagingService Web服务的WSDL中所使用的XML Schema:

<xs:schema
 targetNamespace="http://services.irad.acmeworld.com/datastaging"
 xmlns:tns="http://services.irad.acmeworld.com/datastaging"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 elementFormDefault="qualified"
> 
  <xs:complexType name="DataStaging">
    <xs:sequence>
      <xs:element name="inputURIs">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="inputURI" maxOccurs="unbounded">
              <xs:complexType>
                <xs:sequence>
                  <xs:element name="uri" type="xs:anyURI"/>
                </xs:sequence>
              </xs:complexType>
            </xs:element>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
 
  <xs:complexType name="DataStagingResponse">
    <xs:sequence>
      <xs:element name="outputURIs">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="outputURI" maxOccurs="unbounded">
              <xs:complexType>
                <xs:sequence>
                  <xs:element name="uri" type="xs:anyURI"/>
                </xs:sequence>
              </xs:complexType>
            </xs:element>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
 
  <xs:element name="dataStaging" type="tns:DataStaging"/>
  <xs:element name="dataStagingResponse" type="tns:DataStagingResponse"/>
</xs:schema>

此WSDL的XML Schema部分中的内容相当普通。请求和响应消息是由普通的(具有匿名和显式内容模型的)全局complexType元素一起创建的。DataStagingService是一个doc/literal样式的Web服务,因此其中含有全局元素与WSDL的<part>元素结合使用。

使用build.xml文件构建

我将使用WebLogic Server 10中附带的Eclipse IDE继续演示。WebLogic for Workshop 10中并没有特定的Eclipse IDE插件可用于JAX-WS和JAXB开发,因此我将使用build.xml Ant脚本和Ant View窗口。

这个build.xml文件的内容太长,因此我只列出了其中的重要部分。

<path id="jaxws.classpath">
         <fileset dir="${bea.home}/modules">
                 <include name="javax.xml.stream_1.0.0.0.jar"/>
                 <include name="javax.jws_2.0.jar"/>
                 <include name="javax.xml.bind_2.0.jar"/>
                 <include name="javax.xml.ws_2.0.jar"/>
                 <include name="javax.xml.soap_1.3.0.0.jar"/>
                 <include name="javax.activation_1.1.jar"/>
                 <include name="glassfish.jaxws.rt_2.0.1.jar"/>
                 <include name="glassfish.jaxb_2.0.5.jar"/>
                 <include name="glassfish.jaxws.saaj.impl_2.0.1.jar"/>
                 <include name="glassfish.jaxws.tools_2.0.1.jar"/>
         </fileset>
</path>

上面的清单指出了build.xml文件中所使用的<path>元素。它表示WebLogic Server 10将在其JAX-WS和JAXB实现中使用共享的Java EE库模块来实现类。如您所见,这些类来自Glassfish RI JAR,而并非由WebLogic Server 10 Web Services栈开发小组编写。共享的Java EE库模块有利于Glassfish JAR的封装,并允许将它们的单个副本用于各种内部(对于BEA)或外部用途。众所周知,这些库模块位于${BEA_HOME}/modules目录。

上述清单中的JAR位于用户定义的库中。这样更易于编写在Eclipse IDE内部使用JAX-WS和JAXB API的代码。

生成JAX-WS服务端点和JAXB类

接下来,我们将实现DataStagingService Web服务的代码。首先,运行BEA wsdlc Ant任务,目的是生成主干JAX-WS端点实现和JAXB类。

WebLogic Server 10.1将使用Glassfish Project项目中的jar文件用于其JAX-WS和JAXB实现。type="JAXWS"属性将指定使用JAX-WS和JAXB,而不是JAX-RPC。

<target name="run-wsdlc" depends="clean">
 <taskdef name="wsdlc" classname="weblogic.wsee.tools.anttasks.WsdlcTask" 
          classpathref="compile.classpath" />
 
 <property name="client.binding" value="custom-client.xjb"/>
 
 <wsdlc  type="JAXWS"
         srcWsdl="etc/${wsdl.file.name}.wsdl"
         destJwsDir="WebContent/WEB-INF/lib" 
         destImplDir="${src.dir}" 
         explode="false" 
         verbose="${verbose}"
         debug="${debug}"
         failonerror="true">
          <binding dir="etc" includes="${client.binding}"/>
         <classpath>
                  <path refid="compile.classpath"/>
          </classpath>
 </wsdlc>
;/target>

type="JAXWS"属性和<binding>子元素需要格外注意。

这时,我们应该能够运行Ant构建文件了。结果将生成JAX-WS端点实现的代码。由于我提供了一个定制文件,因此生成代码将位于com.acmeworld.irad.services.datastaging包中。JAX-WS端点实现的完整代码比较长,因此我只摘录了其中的一部分:

public com.acmeworld.irad.services.datastaging.DataStagingResponse.OutputURIs dataStaging
(com.acmeworld.irad.services.datastaging.DataStaging.InputURIs inputURIs) {     DataStagingResponse dataStagingResponse = null;     InputStream inputstream = null;     try     {         //DataStaging.InputURIs contains zero or more         //DataStaging.InputURIs.InputURI JAXB objects.         //We loop through them, and use one of their getter         //methods to print out a bound value.         DataStaging.InputURIs.InputURI inputURI = null;         List inputURIList = inputURIs.getInputURI();         for (int i = 0; i < inputURIList.size(); i++)         {             inputURI = inputURIList.get(i); log("dataStaging(InputURIs)", "inputURI.getUri()=" + inputURI.getUri());         }         //Next, we show one way to use the JAXB API, to convert         //the DataStaging.InputURIs input parameter to a byte[].         //This byte[] will contain an XML representation of that         //input parameter.         JAXBContext jc = JAXBContext.newInstance(DataStaging.InputURIs.class);         Marshaller marshaller = jc.createMarshaller();         marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);         ByteArrayOutputStream baos = new ByteArrayOutputStream();         JAXBElement je = new JAXBElement(             new QName("http://services.irad.acmeworld.com/datastaging","inputURIs"),             DataStaging.InputURIs.class,             inputURIs         );         marshaller.marshal(je, baos);         //We use an existing XML file for the response from the         //Web service operation. We’ll load this XML file from         //the WEB-INF/classes directory, and use the JAXB API         //to create the JAXB object of our response.         inputstream = Thread.currentThread().getContextClassLoader().getResourceAsStream("SampleDataStagingResponseDocument.xml");         jc = JAXBContext.newInstance(DataStagingResponse.class);         Unmarshaller unmarshaller = jc.createUnmarshaller();         dataStagingResponse = (DataStagingResponse)unmarshaller.unmarshal(inputstream);     }     catch (Exception e)     {         throw new RuntimeException(e);     }     finally     {         if (inputstream != null) try {} catch (Exception e){}      }               return dataStagingResponse.getOutputURIs(); }

其中的代码注释很好地解释了其过程,因此我在此处就不再赘述了。

编译JAX-WS服务端点

BEA jwsc Ant任务用于编译JAX-WS服务端点并生成待部署的WAR文件。

<target name="run-jwsc">
  <taskdef name="jwsc" classname="weblogic.wsee.tools.anttasks.JwscTask"
           classpathref="compile.classpath" />
 
  <jwsc
    destdir="${domain.home}/deployments/${project.name}"
    srcdir="${src.dir}/${package.path}"
    classpath="WebContent/WEB-INF/lib/${wsdl.file.name}_wsdl.jar"
    keepGenerated="${keep}"
  >
    <binding dir="etc" includes="${client.binding}"/>
    <module explode="false" name="${portType.name}Impl">
      <jws
        type="JAXWS"
           file="${service.name}Impl.java"
           compiledWsdl="WebContent/WEB-INF/lib/${wsdl.file.name}_wsdl.jar"
      >
        <WLHttpTransport
             contextPath="datastaging"
          serviceUri="DataStagingService"
          portName="DataStagingServicePort"
        />
      </jws>
    </module>
    <classpath>
      <path refid="compile.classpath"/>
      <pathelement location="WebContent/WEB-INF/lib/${wsdl.file.name}_wsdl.jar"/>
    </classpath>
  </jwsc> 
</target>

同样,此处的type="JAXWS"属性和<binding>子元素值得格外注意。该代码还演示了如何避免将特定于WebLogic注释(比如说@WLHttpTransport)放在JWS中。

使用build.xml文件部署

JAX-WS中有一个倍爱好评的特性,即部署描述符的使用是可选的。这种特性很好,因为它解决了多个供应商所提供的JAX-WS实现之间的可移植性问题。

基于POJO的JAX-WS Web services已封装为Java EE Web应用程序,并且WebLogic Server 10所提供的wsdeploy Ant任务可以将这些应用程序部署到运行中的WLS实例上。此处,我将在build.xml文件中使用该wsdeploy Ant任务。

<target name="deploy">
  <property name="wls.username" value="weblogic"/>
  <property name="wls.password" value="weblogic"/>
  <property name="wls.hostname" value="localhost"/>
  <property name="wls.port" value="7031"/>
  <property name="wls.server.name" value="W4WPAdminServer"/>
 
  <taskdef name="wldeploy" 
           classname="weblogic.ant.taskdefs.management.WLDeploy"
           classpathref="compile.classpath" />
  
  <wldeploy
    action="deploy"
    name="${project.name}"
    source="${domain.home}/deployments/${project.name}"
    user="${wls.username}"
    password="${wls.password}"
    verbose="true"
    adminurl="t3://${wls.hostname}:${wls.port}" 
    targets="${wls.server.name}"
  />
</target>

使用WebLogic Test Client进行测试

WebLogic Test Client是一个Java EE Web应用程序。和WebLogic Server Administration Console (console.war)一样,它自动部署于您的WebLogic Server 10.1实例中。该应用程序的URL为:

http://<host>:<port>/wls_utc

DataStagingService的WSDL的URL为:

http://<host>:<port>/datastaging/DataStagingService?WSDL

输入该WSDL URL之后,单击“Test”按钮,然后便可在接下来出现的页面中输入测试请求XML。下面是一个示例请求XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<dataStaging xmlns="http://services.irad.acmeworld.com/datastaging">
  <inputURIs>
    <inputURI>
      <uri>http://www.altova.com</uri>
    </inputURI>
    <inputURI>
      <uri>http://www.amazon.com</uri>
    </inputURI>
  </inputURIs>
</dataStaging>

WebLogic Test Client相当优秀,但是它实际上并不能测试在客户端上使用JAXB和JAX-WS。最后一步将实现这一任务。

使用JAX-WS Web服务客户端进行测试

最后,我们将使用JAX-WS Web服务客户端调用DataStagingService Web服务。最后一部分的代码要比JAX-WS服务端点的代码稍微复杂一些,因此它需要使用JAXB API。

try
{
    DataStagingService service = new DataStagingService(
        new URL("http://localhost:7031/datastaging/DataStagingService?WSDL"),
        new QName("http://services.irad.acmeworld.com/datastaging",
                  _properties.getProperty("datastaging.service.portName"))
    );

    Dispatch sourceDispatch = service.createDispatch(
        new QName("http://services.irad.acmeworld.com/datastaging",
                  "DataStagingService"),
        Source.class, 
        Service.Mode.PAYLOAD
    );

    InputStream inputstream = Thread.currentThread().getContextClassLoader().
                 getResourceAsStream("SampleDataStagingRequestDocument.xml");

    Source responseSource = sourceDispatch.invoke(new StreamSource(inputstream));

    javax.xml.transform.TransformerFactory factory =
                         javax.xml.transform.TransformerFactory.newInstance();

    javax.xml.transform.Transformer transformer = factory.newTransformer();
    transformer.setOutputProperty(javax.xml.transform.OutputKeys.OMIT_XML_DECLARATION, "yes");
    transformer.setOutputProperty(javax.xml.transform.OutputKeys.METHOD, "xml");

    javax.xml.transform.stream.StreamResult streamResult = 
                               new javax.xml.transform.stream.StreamResult();

    streamResult.setOutputStream(new java.io.ByteArrayOutputStream());
    transformer.transform(responseSource, streamResult);

    System.out.println((new StringBuffer()).append("response=").
                        append(streamResult.getOutputStream()).toString());
}
catch(Throwable throwable)
{
    throwable.printStackTrace();
    System.out.println((new StringBuffer()).append("Unexpected exception: ").
                        append(throwable.getMessage()).toString());
}

其中大多数代码专门用于执行XSL转换,并且使用了JDK中的JAXP API类。首先,创建一个DataStagingService服务桩(stub)。该服务桩将接收一个URL对象(指向服务端点的WSDL)和一些与XML相关的内容,用于建立要调用的服务。后者将在检索WSDL之后使用。

注意,DataStagingService服务桩和相关类是由BEA clientgen Ant任务生成的。我并没展示这些代码,不过该Ant任务也拥有一个type="JAXWS"和一个<binding>子元素。

摘录代码的其余部分包括:

  • 创建Dispatch对象,该对象用于调用Web服务操作。Dispatch对象在功能上等同于JAX-RPC中的Call对象(与DII编程模型一起使用)。
  • 通过XML文档创建有效负载。这违背了“为各个元素实例化一个对象”的实践,JAX-RPC需要这样。
  • 调用Web服务操作。此外需要格外小心,因为“操作名称”包装器元素并不会自动添加。如果服务端点需要一个,则需要自己添加。
  • 使用JAXP转换类将响应从Web服务操作编组到java.io.ByteArrayOutputStream中。您可以处理所需的一切内容(比如说byte[]、String和XML)。同样,这远没有在JAX-RPC中处理JavaBean图(或javax.xml.soap.SOAPElement对象)费力。

以上是整个步骤的总结。希望您可更好的理解如何在WebLogic Server 10中使用JAX-WS 2.0和JAXB 2.0实现。这些实现中的API即可以在服务提供者端使用,也可以在服务用户端使用。请查看我所提供的示例代码,您会发现其中没有使用(或导入)任何特定于WebLogic的类。这表示该代码是完全可移植的,并且应该能够在Axis2上编译,而无需对代码(或定制文件)进行任何修改。但是,您需要修改build.xml文件,使JAX-WS和JAXB实现使用Axis2所使用的.jar文件。还需要修改用于JAX-WS、JAXB和部署的Ant任务。

一些最佳实践

以下列出了在WebLogic Server 10中使用JAX-WS和JAXB 实现的一些最佳实践:

  • 避免在代码中使用特定于供应商的注释。这样在尝试不同供应商的JAX-WS实现时就无需对代码进行修改。通常,JAX-WS实现供应商都提供了在Ant任务中访问特定于供应商注释的方法。WebLogic Server使用的是这一选项,因此您应该利用这一特性,以避免在JWS中使用特定于供应商的注释。
  • 在可能的地方使用JAXP StreamSource和 and StreamResult。javax.xml.transform.stream.StreamSource和javax.xml.transform.stream.StreamResult所类提供的方法可以最有效地处理Java串行化(和并行化)问题。因此,您应该尽可能地使用它们。
  • 缓存JAXBContext对象。javax.xml.bind.JAXBContext对象是一个用于通过Java类创建JAXB对象的工厂。创建JAXBContext对象需要的开销比较大,因此应该缓存它们从而便于重用。
  • 使用JAXBElement编组内部类。JAXB 2.0将使用内部类用于匿名complexType元素。如果您之后在Web服务操作的方法签名中使用其中某个元素,那么将在构建时接收到一个javax.xml.bind.MarshalException异常。问题是内部类并不是高级元素,因此并没有@XmlRootElement注释。解决的方法是为内部类创建一个JAXBElement对象。要了解如何创建该对象,请在DataStagingServiceImpl.java文件中搜索"JAXBElement"。

下载

结束语

JAX-WS和JAXB是用于构建下一代基于Java的Web服务的最有前途的两个API。如今,WebLogic Server 10 Web Services栈通过Glassfish JAR和BEA Ant任务这两个API都提供了支持。

您可以使用WebLogic Server 10 Web Services栈为任何JAX-WS实现编写、构建和部署JAX-WS Web服务,而不仅限于WebLogic Server 10中的实现。Jwsc和wsdlc Ant任务已经经过修改,现在可允许您指定生成JAX-WS和JAXB工件时所使用的定制文件。可以使用<binding>子元素实现这一目的。Jwsc和wsdlc Ant任务将从内部调用Sun的wsimport Ant任务。

希望您可以通过本文了解使用WebLogic Server 10 Web Services栈生成JAX-WS和JAXB工件是多么地容易。但是,创建定制文件却并非易事。我们可以通过定制文件影响工件的生成流程。不过也不用担心,我将在后续的几篇文章中介绍如何创建定制文件。

参考资料

以下链接提供了与本文内容相关的其他一些信息:

 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号