通过CXF JAX-WS Dispatch和JAXB访问SOAP Web Service

如今Restful Web Service很流行,介绍如何写Restful Web Service的文章也很多,自己也很喜欢Restful Web Service,用JSON传输,效率比XML好很多。但是,Restful Web Service不像SOAP Web Service可以用WSDL来作为Service的定义,在设计时需要写很多文档,来对服务接口进行说明。同时,在开发客户端的时候,并不能直接生成所需要的Bean,需要用户自己手工写代码,或者由服务端提供,这就给日后的管理和维护带来了很多潜在的问题。最近正好要用Java访问一个SOAP Web Service,自己搞了一遍后,发现其实SOAP Web Service也是挺好用的。如果不是传输很大Payload用XML效率太低的服务,完全可以不使用Restful Web Service。在这里我把自己写的访问web service的代码整理了一下,希望可以对有同样需要的朋友有些帮助。

在这个代码的例子中,我访问的是一个开放的天气SOAP服务,这个服务的具体信息我是在http://www.service-repository.com/service/overview/1132083200找到的。在这个例子中,我们只访问GetWeatherInformation这个服务。

首先我们先在IntelliJ里建立一个新的Java Project,在这个例子中我使用了Gradle作为build工具,所以需要添加build.gradle文件。接着我们从http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL下载这个SOAP服务的WSDL定义,并添加到resources里。下面,我们要配置Gradle来使用JAXB根据WSDL生成访问服务所需要的Bean:

task jaxb() {
    jaxbTargetDir = file( "src/generated-sources/java" )

    doLast {
        jaxbTargetDir.mkdirs()


        ant.taskdef(
                name: 'xjc',
                classname: 'com.sun.tools.xjc.XJCTask',
                classpath: configurations.jaxb.asPath
        )
        ant.jaxbTargetDir = jaxbTargetDir


        ant.xjc(
                destdir: '${jaxbTargetDir}',
                package: 'weblog.soap.client.example.model',
                schema: 'src/main/resources/wsdl.xml'
        ) {
            arg(value: "-wsdl")
        }
    }
}

compileJava.dependsOn jaxb
这段代码定义在Gradle编译Java代码前调用xjc来根据WSDL生成类,这些类会生成在generated-sources/java下,这样,我们可以在IDE中看到生成的源代码。

sourceSets.main.java.srcDirs "src/generated-sources/java"
clean.delete "src/generated-sources"
上面第一行代码把生成的类添加到sourceSets中,这样,在Gradle编译Java代码时,生成的代码也会被编译。第二行代码在gradle运行clean的时候会删除生成的类。


有了上面的Gradle配置,我们运行Gradle,就可以看到访问Web service需要的JAXB类自动根据WSDL生成好并添加到我们的代码中了。下面,我们看如何访问SOAP Web service。在这个例子中,我使用的是CXF JAX-WS Dispatch来访问服务。在Java的第三方库中,其实还有很多其他的包可以用,CXF本身也不止支持Dispatch的访问方法。我选择CXF JAX-WS Dispatch,是因为喜欢它的简洁,通过下面的很短的代码,我们就可以访问这个服务了:

public class TestClient {
    public static void main(String[] args) throws Exception {
        QName serviceName = new QName("http://ws.cdyne.com/WeatherWS/", "Weather");
        QName portName = new QName("http://ws.cdyne.com/WeatherWS/", "WeatherSoap");
        Service service = Service.create(new URL("http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL"),
                serviceName);

        JAXBContext context = JAXBContext.newInstance("weblog.soap.client.example.model");

        Dispatch<Object> dispatch = service.createDispatch(
                portName,
                context,
                Service.Mode.PAYLOAD);

        dispatch.getRequestContext().put("find.dispatch.operation", Boolean.TRUE);

        GetWeatherInformation input = new GetWeatherInformation();

        System.out.println("Start querying the web service");

        Object responseObj = dispatch.invoke(input);

        System.out.println("Got response:");

        GetWeatherInformationResponse response = (GetWeatherInformationResponse)responseObj;

        for (WeatherDescription description : response.getGetWeatherInformationResult().getWeatherDescription())
        {
            System.out.println("Weather Id: " + description.getWeatherID());
            System.out.println("Weather Description: " + description.getDescription());
            System.out.println();
        }
    }
}
代码本身其实很好懂,唯一需要说明的就是:

dispatch.getRequestContext().put("find.dispatch.operation", Boolean.TRUE);

如果我们comment out这行代码的话我们会得到以下的异常:

Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: Server did not recognize the value of HTTP Header SOAPAction: .

这是因为我们使用的这个Web service的例子在一个port上有多个operation,由于CXF不能确定使用哪个operation,所以没有在request上设置好正确的SOAPAction。添加了上面的代码,CXF就会根据我们request的类来自动匹配operation了。如果访问的服务只有一个operation的话,则不会遇到这个问题。


完整的代码我已经上传到Github上了,可以在https://github.com/mcai4gl2/soap-client-example找到。最后,对这个代码唯一的遗憾是我还没有找到如何配置CXF Dispatch的timeout的时间。这对访问提供大数据量的SOAP Web service是一个很重要的功能,如果哪位朋友知道的话,请一定告诉我,先谢谢了!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值