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/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd http://cxf.apache.org/jaxws http://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 { @Autowired private 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 { @Autowired private MesTaskService mesTaskService; @Resource private WebServiceContext context; @Autowired private WsIdentityService wsIdentityService; @Autowired private TaskAllCheckService taskAllCheckService; @Autowired private TaskAllcheckUnqudetailsService taskAllcheckUnqudetailsService; @Autowired private 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/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd http://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 { @Override public 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" }) @Override public 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; } @Override public boolean handleFault(LogicalMessageContext context) { // TODO Auto-generated method stub return false; } @Override public 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
参考资料: