1. cxf简介
Web Services 的一种实现方式。
Apache CXF = Celtix + XFire,后更名为 Apache CXF ,简称为 CXF。
CXF 继承了 Celtix 和 XFire 两大开源项目的精华,提供了对 JAX-WS 全面的支持,并且提供了多种 Binding 、DataBinding、Transport 以及各种 Format 的支持,并且可以根据实际项目的需要,采用代码优先(Code First)或者 WSDL 优先(WSDL First)来轻松地实现 Web Services 的发布和使用。
2. JAX-WS
JAX-WS(Java API for XML Web Services)规范是一组XML web services的JAVA API。
在 JAX-WS中,一个远程调用可以转换为一个基于XML的协议例如SOAP,在使用JAX-WS过程中,*开发者不需要编写任何生成和处理SOAP消息的代码*。JAX-WS的运行时实现会将这些API的调用转换成为对应的SOAP消息。
* 在服务器端,用户只需要通过Java语言定义远程调用所需要实现的接口SEI(service endpoint interface),并提供相关的实现,通过调用JAX-WS的服务发布接口就可以将其发布为WebService接口。(发布服务 详见5.1)
* 在客户端,用户可以通过JAX-WS的API创建一个代理(用本地对象来替代远程的服务)来实现对于远程服务器端的调用。(创建代理 详见5.2)
JAX-WS 也提供了一组针对底层消息进行操作的API调用,你可以通过Dispatch 直接使用SOAP消息或XML消息发送请求或者使用Provider处理SOAP或XML消息。
3. cxf特点
部署灵活、支持多种编程语言、多种传输方式。
4. 代码生成
Java to WSDL;
WSDL to Java;
XSD to WSDL;
WSDL to XML;
WSDL to SOAP;
WSDL to Service;
5. 实践
5.1 服务器端
Spring配置文件 cxf.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xmlns:task="http://www.springframework.org/schema/task"xmlns:jaxws="http://cxf.apache.org/jaxws"xmlns:cxf="http://cxf.apache.org/core"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsdhttp://cxf.apache.org/jaxwshttp://cxf.apache.org/schemas/jaxws.xsd"><!-- cxf webservice --><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"/><bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor" /><bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor"/><cxf:bus><cxf:inInterceptors><ref bean="loggingInInterceptor"/></cxf:inInterceptors><cxf:outInterceptors><ref bean="loggingOutInterceptor"/></cxf:outInterceptors><cxf:outFaultInterceptors><ref bean="loggingOutInterceptor"/></cxf:outFaultInterceptors><cxf:inFaultInterceptors><ref bean="loggingInInterceptor"/></cxf:inFaultInterceptors></cxf:bus><bean id="serverPasswordCallback" class="util.web.ServerPasswordCallback" /> <bean id="authentication_server" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken" /> <entry key="passwordType" value="PasswordText" /> <entry key="user" value="FHDServer" /> <entry key="passwordCallbackRef"> <ref bean="serverPasswordCallback" /> </entry> </map> </constructor-arg> </bean>
<!-- 注意下面的address,这里的address的名称就是访问的WebService的name --><!-- 外协完工登记 --><bean id="mesTaskServiceImpl" class="com.outsideasy.ws.mes.wxdata.MesTaskServiceImpl"></bean> <jaxws:server id="mesTaskServiceInter" serviceClass="com.outsideasy.ws.mes.wxdata.MesTaskServiceInter" address="/wxdata/mesTask"> <!-- 这里是发布的地址 --><jaxws:serviceBean><ref bean="mesTaskServiceImpl"/></jaxws:serviceBean><jaxws:inInterceptors><ref bean="authentication_server"></ref></jaxws:inInterceptors> </jaxws:server><cxf:bus><cxf:features><cxf:logging/></cxf:features></cxf:bus> </beans>
ServerPasswordCallback.java (身份认证的callback,接收客户端的callback对象,获取认证信息进行验证)
package util.web;import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map;import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import javax.servlet.http.HttpServletRequest;import org.apache.cxf.message.Exchange; import org.apache.cxf.message.Message; import org.apache.cxf.staxutils.W3CDOMStreamWriter; import org.apache.log4j.Logger; import org.apache.ws.security.WSPasswordCallback; import org.apache.ws.security.handler.RequestData; import org.springframework.beans.factory.annotation.Autowired; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList;import common.sysmodule.model.WsIdentity; import common.sysmodule.service.WsIdentityService;public class ServerPasswordCallback implements CallbackHandler {@Autowiredprivate WsIdentityService wsIdentityService;protected static Logger logger = Logger.getLogger("service");public void handle(Callback[] callbacks) throws IOException,UnsupportedCallbackException {WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]; //获取客户端Handler中传递过来的callback对象String identify = pc.getIdentifier(); //获取账户信息//身份验证List<WsIdentity> list=wsIdentityService.getEnabledIdentity(identify); //通过账户在服务端表中查询账户信息,进行认证if (list!=null && list.size()>0 && list.get(0).getIdentify().equals(identify)) { pc.setPassword(list.get(0).getPsw());//验证用户名后,在设置密码就可以自动验证} else {throw new SecurityException("验证失败");}}}
MesTaskServiceInter.java (服务端申明接口)
package com.outsideasy.ws.mes.wxdata;import javax.jws.WebService;import com.outsideasy.ws.mes.wxdata.model.AttachedFileWithParams; import com.outsideasy.ws.mes.wxdata.model.TaskRandomCheckAndFileDetails;@WebService //这里的申明是必需的* public interface MesTaskServiceInter {/*** @Description: 新增 发送者的 平台任务单 以及关联的工段 工序 bom材料。* @param json格式的 信息主体 TaskAndGX,该对象包含了 任务单信息,工序 ,工段和bom材料信息* @return json格式的 CXFResponse<PfTask>* 如果success=true,新增成功,并返回PfTask对象;* 如果success=false,新增失败,并返回失败信息errorMessage;*/public String addMesTaskAndTaskGx(String jsonobj);/*** @Description: 根据发送者的任务单号获取工段产量* @param rwdh* @return json格式的 CXFResponse<PfTaskOutput>* 如果success=true,新增成功,并返回List<PfTaskOutput>;* 如果success=false,新增失败,并返回失败信息errorMessage;*/public String getPfTaskOutputList(String rwdh); }
MesTaskServiceImpl.java
package com.outsideasy.ws.mes.wxdata;import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map;import javax.annotation.Resource; import javax.jws.WebService; import javax.jws.soap.SOAPBinding; import javax.jws.soap.SOAPBinding.Style; import javax.xml.ws.WebServiceContext;import com.outsideasy.ws.common.vo.CXFResponse; import com.outsideasy.ws.mes.wxdata.model.AttachedFileWithParams; import com.outsideasy.ws.mes.wxdata.model.TaskCheckAndUnquDetails; import com.outsideasy.ws.mes.wxdata.model.TaskRandomCheckAndFileDetails; import common.sysmodule.model.WsIdentity; import common.sysmodule.service.WsIdentityService;@WebService //必需申明* @SOAPBinding(style = Style.RPC) //必需申明* public class MesTaskServiceImpl implements MesTaskServiceInter {@Autowiredprivate MesTaskService mesTaskService;@Resourceprivate WebServiceContext context;@Autowiredprivate WsIdentityService wsIdentityService;@Autowiredprivate TaskAllCheckService taskAllCheckService;@Autowiredprivate TaskAllcheckUnqudetailsService taskAllcheckUnqudetailsService; @Autowiredprivate TaskRandomCheckService taskRandomCheckService;public String getPfTaskOutputList(String rwdh){Map<String,Object> params2=new HashMap<String,Object>();params2.put("rwdh", rwdh);params2.put("send_company", getsendInfo().getCompany_id());List<PfTaskOutput> list=mesTaskService.getPfTaskOutputList(params2);CXFResponse<PfTaskOutput> res=new CXFResponse<PfTaskOutput>();if(list!=null && list.size()>0){res.setSuccess(Const.SOAP_TRUE);res.setList(list);}else{res.setSuccess(Const.SOAP_FALSE);res.setErrorMessage("同步失败,生产方未在平台中录入产量");}return MyJsonUtil.obj2string(res);}public String addMesTaskAndTaskGx(String jsonobj) { int company_id=getsendInfo().getCompany_id();TaskAndGX mtAndGx=MyJsonUtil.str2obj(jsonobj, TaskAndGX.class);CXFResponse<PfTask> res=new CXFResponse<PfTask>();res.setSuccess(Const.SOAP_TRUE);if(mtAndGx.getTask()==null){res.setErroeResponseInfo("发送失败,发送的任务单为空");}else if(mtAndGx.getGxlist()==null || (mtAndGx.getGxlist()!=null && mtAndGx.getGxlist().size()==0)){res.setErroeResponseInfo("发送失败,发送的工序为空");}else if(mtAndGx.getBomlist()==null || (mtAndGx.getBomlist()!=null && mtAndGx.getBomlist().size()==0)){res.setErroeResponseInfo("发送失败,发送的bom材料为空");}else{mesTaskService.addMesTaskAndTaskGx(res,company_id,mtAndGx);}return MyJsonUtil.obj2string(res);} }
5.2 客户端(创建代理)
Spring 配置文件 cxf.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:jaxws="http://cxf.apache.org/jaxws"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:cxf="http://cxf.apache.org/core"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsdhttp://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"><context:property-placeholder location="classpath:sysconfig/publishInfo.properties" /><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"/><bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor" /><bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor"/><cxf:bus><cxf:inInterceptors><ref bean="loggingInInterceptor"/></cxf:inInterceptors><cxf:outInterceptors><ref bean="loggingOutInterceptor"/></cxf:outInterceptors><cxf:outFaultInterceptors><ref bean="loggingOutInterceptor"/></cxf:outFaultInterceptors><cxf:inFaultInterceptors><ref bean="loggingInInterceptor"/></cxf:inFaultInterceptors></cxf:bus><bean id="clientPasswordCallback" class="erp.util.web.ClientPasswordCallback"/><bean id="authentication_client" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"> <constructor-arg> <map> <entry key="action" value="UsernameToken" /> <entry key="passwordType" value="PasswordText" /> <entry key="user" value="FHDClient" /> <entry key="passwordCallbackRef"> <ref bean="clientPasswordCallback" /> </entry> </map> </constructor-arg> </bean> <jaxws:client id="supplierInter" serviceClass="com.outsideasy.ws.erp.supplier.SupplierInter" address="http://${cxf_url}/ws/erp/supplier/supplierInter"><jaxws:handlers><bean class="erp.util.web.SessionLogicalHandler"></bean></jaxws:handlers><jaxws:outInterceptors> <!-- <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> --> <!-- SAAJInInterceptor只在CXF是2.0.X版本时或之前版本时才是必须的 --> <!-- <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor"/> --> <ref bean="authentication_client"></ref></jaxws:outInterceptors> </jaxws:client><jaxws:client id="supplierAccessInter" serviceClass="com.outsideasy.ws.erp.supplier.SupplierAccessInter" address="http://${cxf_url}/ws/erp/supplier/supplierAccessInter"> <!-- 访问地址 cxf_url = 192.168.1.101:8995 *--><jaxws:handlers><bean class="erp.util.web.SessionLogicalHandler"></bean></jaxws:handlers><jaxws:outInterceptors> <!-- <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> --> <!-- SAAJInInterceptor只在CXF是2.0.X版本时或之前版本时才是必须的 --> <!-- <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor"/> --> <ref bean="authentication_client"></ref></jaxws:outInterceptors> </jaxws:client><cxf:bus><cxf:features><cxf:logging/></cxf:features></cxf:bus> </beans>
客户端callback对象 ClientPasswordCallback.java
package erp.util.web;import java.io.IOException;import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException;import org.apache.ws.security.WSPasswordCallback;public class ClientPasswordCallback implements CallbackHandler {@Overridepublic void handle(Callback[] callbacks) throws IOException,UnsupportedCallbackException { WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]; pc.setPassword("topsun"); // 验证密码pc.setIdentifier("admin"); // 验证账号}}
SessionLogicalHandler.java
package erp.util.web;import java.util.HashMap; import java.util.List; import java.util.Map;import javax.xml.ws.handler.LogicalHandler; import javax.xml.ws.handler.LogicalMessageContext; import javax.xml.ws.handler.MessageContext;import org.apache.cxf.jaxws.handler.logical.LogicalMessageContextImpl;public class SessionLogicalHandler implements LogicalHandler<LogicalMessageContext>{public SessionLogicalHandler() {super();}public static List<String> http_headers_cookie=null;//静态 所有的 SessionLogicalHandler 共享 一个 cookie@SuppressWarnings({ "unchecked", "rawtypes" })@Overridepublic boolean handleMessage(LogicalMessageContext context) {// TODO Auto-generated method stub// javax.xml.ws.handler.MessageContext.Scope s=// context.getScope(MessageContext.HTTP_REQUEST_HEADERS);// Set-Cookie=[JSESSIONID=48B10A68BB05F69F8ED82A33F566C5D8; Path=/myapp;// HttpOnly] LogicalMessageContextImpl c = (LogicalMessageContextImpl) context;if (c.get(MessageContext.HTTP_RESPONSE_HEADERS) != null) {//response 时 记录服务端返回的session信息Map<String, List> header = (Map<String, List>) c.get(MessageContext.HTTP_RESPONSE_HEADERS);List<String> ls = header.get("Set-Cookie");//获取header 中cookie的信息if(ls!=null){SessionLogicalHandler.SetHttp_headers_cookie(ls);}}else if(c.get( MessageContext.HTTP_REQUEST_HEADERS)!=null && http_headers_cookie!=null){//request 请求的时候 把cookie信息设置进去Map<String, List> header = (Map<String, List>) c.get( MessageContext.HTTP_REQUEST_HEADERS);header.put("cookie", http_headers_cookie);}else if(c.get( MessageContext.HTTP_REQUEST_HEADERS)==null && http_headers_cookie!=null){//request 请求的时候 把cookie信息设置进去Map<String, List> header = new HashMap<String, List>();header.put("cookie", http_headers_cookie);c.put(MessageContext.HTTP_REQUEST_HEADERS, header);}return true;}@Overridepublic boolean handleFault(LogicalMessageContext context) {// TODO Auto-generated method stubreturn false;}@Overridepublic void close(MessageContext context) {// TODO Auto-generated method stub }private synchronized static void SetHttp_headers_cookie(List<String> ls){if(http_headers_cookie==null){http_headers_cookie=ls;}} }
SupplierAccessInter.java (客户端的代理接口,客户端直接调用此接口即可访问ws服务端)
package com.outsideasy.ws.erp.supplier;import javax.jws.WebService;@WebService //必需申明 * public interface SupplierAccessInter {/**获取历史审核记录* 参数:json格式的map * 包含 company_id 公司编号* 返回值:json 格式CXFResponse* 2016-5-19*/public String getPfAuthcationInfoList(String jsonmap);/**供应商变更 *参数:json格式的map*返回值:String *2016-5-17**/String updateSupplierChangeStateByWS(String jsonmap); }
6.总结
6.1 cxf 是 WebService的实现方式,符合ws规范
6.2 利用 jax-ws 可以轻松发布一个WebService服务
6.3 此项目是利用 cxf + jax-ws 发布的一个WebService服务
6.4 主要使用的是 代理设计模式
7. 项目支持(仅作为个人笔记使用)
ws + erp
参考资料:
1. http://baike.baidu.com/link?url=WOTPK-J7UxJuXZ6hMr8OPYDMItaEHwva8wzOEUSM3wKXF5KgBBXRM8VfgA-kUE_RrAoNMYFipZY0VK_kCtpw8_