一、前言
其实网上有很多文章均说过CXF如何与Spring结合发布服务,以及如何构建测试客户端的话题。但纸上得来终觉浅,既然本次项目遇到了,且自己动手实践了,就做个记录。记录是最好的成长。且其中遇到了一些啼笑皆非的异常事件。
二、思路
实现正常的java接口以及实现类。业务实现逻辑暂且不表,这不是该文章的内容。
如下:
com.***.fax.facade.tomi.CallBackNotifyFacade
com.***.fax.facade.tomi.CallBackNotifyFacadeImpl
在CallBackNotifyFacade上加入如下注解:@WebService
在CallBackNotifyFacadeImpl上加入如下注解:
@WebService(endpointInterface = "com.qunar.fax.facade.tomi.CallBackNotifyFacade")
@Service("callBackNotifyFacadeImpl")
xml配置如下:
<import resource="classpath:META-INF/cxf/cxf.xml"/> <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/> <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/> <!-- http://localhost:8081/callBackNotifyFacade/callBackNotifyFacade?wsdl --> <jaxws:endpoint id="callBackNotifyFacade" implementor="com.***.fax.facade.tomi.CallBackNotifyFacadeImpl" address="/callBackNotifyFacade" > <jaxws:inInterceptors> <bean class="com.***.fax.facade.interceptor.CxfLoggingInInterceptor"/> </jaxws:inInterceptors> <jaxws:outInterceptors> <bean class="com.***.fax.facade.interceptor.CxfLoggingOutInterceptor" /> </jaxws:outInterceptors> </jaxws:endpoint>
其中CxfLoggingInInterceptor与CxfLoggingOutInterceptor分别为入口和出口拦截器。代码如下:
public class CxfLoggingInInterceptor extends AbstractLoggingInterceptor { private static final Logger LOG = LogUtils.getLogger(LoggingInInterceptor.class); public CxfLoggingInInterceptor() { super(Phase.RECEIVE); } @Override protected Logger getLogger() { return LOG; } @Override public void handleMessage(Message message) throws Fault { // 日志增加唯一标识 long startTime = SystemTimer.currentTimeMillis(); NDC.push(startTime + "_" + Thread.currentThread().getId()); } }
public class CxfLoggingOutInterceptor extends AbstractLoggingInterceptor { private static final Logger LOG = LogUtils.getLogger(CxfLoggingOutInterceptor.class); public CxfLoggingOutInterceptor() { super(Phase.PRE_STREAM); } @Override protected Logger getLogger() { return LOG; } @Override public void handleMessage(Message message) throws Fault { // 释放内存,清除标记 NDC.pop(); NDC.remove(); NDC.clear(); } }
pom.xml配置信息
<!-- cxf --> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf</artifactId> <version>${cxf.version}</version> <type>pom</type> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>${cxf.version}</version> <exclusions> <exclusion> <artifactId>geronimo-javamail_1.4_spec</artifactId> <groupId>org.apache.geronimo.specs</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> <version>${cxf.version}</version> </dependency>
cxf.version 为 2.7.0
服务端的代码就这些了。
三、客户端测试代码
/** * 测试发送状态通知接口 */ @Test public void testSendFaxCallBack(){ try{ //生成动态客户端 String wsdlUrl="fax/test/callback.wsdl"; JaxWsDynamicClientFactory dynamicClient = JaxWsDynamicClientFactory.newInstance(); Client client = dynamicClient.createClient(wsdlUrl); //设置超时时间 HTTPConduit http = (HTTPConduit) client.getConduit(); HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy(); httpClientPolicy.setConnectionTimeout( 36000 ); httpClientPolicy.setAllowChunking( false ); httpClientPolicy.setReceiveTimeout( 32000 ); http.setClient(httpClientPolicy); //调用参数 String xml="。。。。。"; //调用,并返回结果 Object[] res = client.invoke("sendCallBack", xml); System.out.println("response: " + res[0]); }catch (Exception e){ logger.warn("",e); } }
四、开发过程出现的异常
1、异常信息如下:
HTTP Status 500 - Servlet.init() for servlet CXFServlet threw exception type Exception report message Servlet.init() for servlet CXFServlet threw exception description The server encountered an internal error that prevented it from fulfilling this request. exception javax.servlet.ServletException: Servlet.init() for servlet CXFServlet threw exception org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293) org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:861) org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:606) org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489) java.lang.Thread.run(Thread.java:662) root cause org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'cxf' is defined org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:568) org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1099) org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:278) org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198) org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1121) org.apache.cxf.transport.servlet.CXFServlet.loadBus(CXFServlet.java:77) org.apache.cxf.transport.servlet.CXFNonSpringServlet.init(CXFNonSpringServlet.java:71) org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293) org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:861) org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:606) org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
原因,以下配置信息未引入生效:
<import resource="classpath:META-INF/cxf/cxf.xml"/> <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/> <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
2、异常信息如下:
Caused by: org.apache.cxf.binding.soap.SoapFault: Error reading XMLStreamReader. at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.handleMessage(Soap11FaultInInterceptor.java:70) at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.handleMessage(Soap11FaultInInterceptor.java:35) at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:221) at org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:96) at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:65) at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:34) at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:221) at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:429)
原因:这个错误是最让我没有头绪的,因为一切配置正常,在本地测试也很正常,但是一上服务器就报错。
查了很多的原因,更换了很多的版本,均无效,最后偶然发现在发布的脚本job里面,有一个checkurl,用来检查最终发布是否成功,被我写成了wsdl发布的url,结果造成ws服务未完全起来的时候,该url就会被不断重试。导致异常。