摘要
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工件是多么地容易。但是,创建定制文件却并非易事。我们可以通过定制文件影响工件的生成流程。不过也不用担心,我将在后续的几篇文章中介绍如何创建定制文件。
参考资料
以下链接提供了与本文内容相关的其他一些信息:
|