为了简化WebService的开发,使用了CXF框架。单独创建一个应用,用于请求外部WebService服务、接收其他服务的调用请求,用这个应用与我们自己的应用系统采用Servlet的方式通信。
1. 创建WebService应用,创建一个Web工程,将Cxf2.7.4压缩包中的jar文件放到WEB-INF/lib目录下
修改 web.xml 文件,增加CXF的相关配置,如下:
2. 在源代码的 src 目录下,增加xml文件:applicationContext-server.xml(名字自取),并配置到 web.xml 文件中,如下:
3. web.xml中还需增加spring的配置(因为只是个接收和发送请求的应用,不需要太多的Spring的东西----其实本人对Spring了解也不多,尴尬ing...):
4. applicationContext-server.xml的内容:
简单解释一下:
这一段是加载cxf的一些配置,必须有。
此处定义了一个拦截器(具体问度娘,本人也是一知半解),当对方调用本应用时,用来过滤SOAPHeader中的信息,可参见后面的Java代码。
此处定义了一个服务,明确了其接收请求的拦截器(id="inMessageInterceptor"的bean)、提供服务的类(com.ydfw.cb.ScSbjYdjkForYDZXImpl)、服务调用地址url(/services/scSbjYdjkForYDZX)
5. 服务类的写法,服务类是指接收其他 webservice 调用请求的类,该类需要配置在applicationContext-server.xml中,即com.ydfw.cb.ScSbjYdjkForYDZXImpl及其接口 com.ydfw.dzservice.ScSbjYdjkForYDZX
首先是接口 com.ydfw.dzservice.ScSbjYdjkForYDZX,如下:
实现类 com.ydfw.cb.ScSbjYdjkForYDZXImpl 的代码:
由于代码较多且涉及到公司业务相关代码,此处将方法体略去。解释一下该方法中几个对象变量:
1) RemoteObj,自定义对象,将SoapHeader中的相应变量作为该类的属性,并提供setter和getter方法,在拦截器类中初始化该类的值,并放将其放到 request的 RemoteObjForCbd 中;
2) wsContext,应该是cxf的内置对象,这里只需要定义为类变量,由cxf框架负责初始化。在服务方法里直接使用即可;
3) MessageGZIP 该类是第三方厂商提供的压缩类。
6. 拦截器类:com.ydfw.pub.MessageInterceptor
作用是将Webservice的Soap请求中的存储在 SoapHeader 中的变量取出来并放到RemoteObj对象中,供服务方法使用,代码如下:
关于消息拦截器,建议参考Spring或者是CXF的官方文档。
8. 上面叙述的是作为被调用方的程序写法,下面是作为调用方的写法(只列出import部分和方法体代码):
解释几个变量及常量:
1)Names.SPT_SERVICEADDRESS ,对方webservice的地址,格式如下:
http://IP:PORT/webproject/services/scydzxServer?wsdl
IP 和 PORT分别指 IP地址 和 端口
2)Names.SPT_NAMESPACE 命名空间,输入上面的地址看到的targetName
3)Names.SPT_USERNAME 和 Names.SPT_PASSWORD,调用对方需要验证的用户名和密码。可参看前面作为被调用方的消息拦截器中,会取到这两个密码,在服务方法中可进行校验。
4)CallSptYdZxAddSoapHeader 该类用于添加SOAPHeader信息,代码如下:
9. 该项目与业务系统通过Servlet的通信简介:
调用业务系统代码如下:
这里特别注意,
不能写成
这种方式在tomcat下没问题,但是在weblogic中无法将字节流写到servlet中。
因为需要传递xml,采用字节流的方式写到servlet中,接收方代码如下:
方法doPost中,
其他说明:
平时工作中webservice用的不是特别多,本人也是新接触,为了某个项目而突击找资料学习的,多有不足之处;
由于项目中使用了某公司提供的部分代码,不便于将程序包(war)放到附件中,如有需要的可给我发邮件,邮箱为 jackeysion@126.com。
再就是有一个问题还未解决,用cxf创建的项目在tomcat下可以正常运行,但是部署到weblogic上貌似会提示jar包冲突,问度娘没找到好的解决方案,如果大家有好的解决方案还望告知~
1. 创建WebService应用,创建一个Web工程,将Cxf2.7.4压缩包中的jar文件放到WEB-INF/lib目录下
修改 web.xml 文件,增加CXF的相关配置,如下:
<listener>
<servlet>
<servlet-name>CXFService</servlet-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CXFService</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
2. 在源代码的 src 目录下,增加xml文件:applicationContext-server.xml(名字自取),并配置到 web.xml 文件中,如下:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:applicationContext-server.xml
</param-value>
</context-param>
3. web.xml中还需增加spring的配置(因为只是个接收和发送请求的应用,不需要太多的Spring的东西----其实本人对Spring了解也不多,尴尬ing...):
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<listener>
<listener-class>
org.springframework.web.util.IntrospectorCleanupListener
</listener-class>
</listener>
4. applicationContext-server.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:aop="http://www.springframework.org/schema/aop"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<!-- webservice start-->
<!-- -->
<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" />
<!-- 注意下面的address,这里的address的名称就是访问的WebService的name -->
<!--
<jaxws:server id="forTest"
serviceClass="com.dw.dzyd.jy.IComplexUserService"
address="/forTest">
</jaxws:server>
-->
<bean id="inMessageInterceptor"
class="com.ydfw.pub.MessageInterceptor">
<constructor-arg value="receive" />
</bean>
<jaxws:endpoint id="forTest1"
implementor="com.ydfw.cb.ScSbjYdjkForYDZXImpl"
address="/services/forTest1">
</jaxws:endpoint>
<jaxws:server id="scSbjYdjk"
serviceClass="com.ydfw.dzservice.ScSbjYdjkForYDZX"
address="/services/scSbjYdjkForYDZX">
<jaxws:inInterceptors>
<ref bean="inMessageInterceptor" />
</jaxws:inInterceptors>
<jaxws:serviceBean>
<bean class="com.ydfw.cb.ScSbjYdjkForYDZXImpl" />
</jaxws:serviceBean>
</jaxws:server>
</beans>
简单解释一下:
<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" />
这一段是加载cxf的一些配置,必须有。
<bean id="inMessageInterceptor"
class="com.ydfw.pub.MessageInterceptor">
<constructor-arg value="receive" />
</bean>
此处定义了一个拦截器(具体问度娘,本人也是一知半解),当对方调用本应用时,用来过滤SOAPHeader中的信息,可参见后面的Java代码。
<jaxws:server id="scSbjYdjk"
serviceClass="com.ydfw.dzservice.ScSbjYdjkForYDZX"
address="/services/scSbjYdjkForYDZX">
<jaxws:inInterceptors>
<ref bean="inMessageInterceptor" />
</jaxws:inInterceptors>
<jaxws:serviceBean>
<bean class="com.ydfw.cb.ScSbjYdjkForYDZXImpl" />
</jaxws:serviceBean>
</jaxws:server>
此处定义了一个服务,明确了其接收请求的拦截器(id="inMessageInterceptor"的bean)、提供服务的类(com.ydfw.cb.ScSbjYdjkForYDZXImpl)、服务调用地址url(/services/scSbjYdjkForYDZX)
5. 服务类的写法,服务类是指接收其他 webservice 调用请求的类,该类需要配置在applicationContext-server.xml中,即com.ydfw.cb.ScSbjYdjkForYDZXImpl及其接口 com.ydfw.dzservice.ScSbjYdjkForYDZX
首先是接口 com.ydfw.dzservice.ScSbjYdjkForYDZX,如下:
import java.io.UnsupportedEncodingException;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
@WebService(targetNamespace="http://dzservice.ydfw.com")
@SOAPBinding(style = SOAPBinding.Style.RPC)
public interface ScSbjYdjkForYDZX{
@WebMethod(operationName = "scsydzxCall")
public byte[] scsydzxCall(@WebParam(name = "inputData")
byte[] inputByte) throws UnsupportedEncodingException;
}
实现类 com.ydfw.cb.ScSbjYdjkForYDZXImpl 的代码:
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.annotation.Resource;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.servlet.http.HttpServletRequest;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;
import org.apache.cxf.transport.http.AbstractHTTPDestination;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import com.dw.pojo.RemoteObj;
import com.util.LogUtil;
import com.ydfw.dzservice.ScSbjYdjkForYDZX;
import com.ydfw.pub.MessageGZIP;
import com.ydfw.pub.Names;
@SOAPBinding(style = SOAPBinding.Style.RPC)
@WebService(endpointInterface = "com.ydfw.dzservice.ScSbjYdjkForYDZX")
public class ScSbjYdjkForYDZXImpl implements ScSbjYdjkForYDZX{
@Resource
private WebServiceContext wsContext;
@Override
public byte[] scsydzxCall(@WebParam(name = "inputData")
byte[] inputByte) throws UnsupportedEncodingException {
StringBuffer logBF = new StringBuffer();
MessageContext mc = wsContext.getMessageContext();
HttpServletRequest request = (HttpServletRequest) mc.get(AbstractHTTPDestination.HTTP_REQUEST);
RemoteObj ro = (RemoteObj) request.getAttribute("RemoteObjForCbd");
if (ro == null) {
throw new RuntimeException("参保地解析xml出错,没有获取到正确的Soap Head信息!");
}
String jybh = ro.getJybh();
String jylsh = ro.getJylsh();
String username = ro.getUsername();
String password = ro.getPassword();
String jyyzm = ro.getJyyzm();
<<方法体代码略去>>
return MessageGZIP.compressToByte(returnXML, "utf-8");
}
由于代码较多且涉及到公司业务相关代码,此处将方法体略去。解释一下该方法中几个对象变量:
1) RemoteObj,自定义对象,将SoapHeader中的相应变量作为该类的属性,并提供setter和getter方法,在拦截器类中初始化该类的值,并放将其放到 request的 RemoteObjForCbd 中;
2) wsContext,应该是cxf的内置对象,这里只需要定义为类变量,由cxf框架负责初始化。在服务方法里直接使用即可;
3) MessageGZIP 该类是第三方厂商提供的压缩类。
6. 拦截器类:com.ydfw.pub.MessageInterceptor
作用是将Webservice的Soap请求中的存储在 SoapHeader 中的变量取出来并放到RemoteObj对象中,供服务方法使用,代码如下:
import java.io.UnsupportedEncodingException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.xml.soap.SOAPException;
import org.apache.cxf.binding.soap.SoapHeader;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.transport.http.AbstractHTTPDestination;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import com.dw.pojo.RemoteObj;
/**
* <b>function:</b> 自定义消息拦截器
*
* @author hoojo
* @createDate Mar 17, 2011 8:10:49 PM
* @file MessageInterceptor.java
* @package com.hoo.interceptor
* @project CXFWebService
* @blog http://blog.csdn.net/IBM_hoojo
* @email hoojo_@126.com
* @version 1.0
*/
public class MessageInterceptor extends AbstractPhaseInterceptor<SoapMessage>{
// 至少要一个带参的构造函数
public MessageInterceptor() {
super(Phase.PRE_INVOKE);
System.out.println("no para");
}
public MessageInterceptor(String phase) {
super(Phase.PRE_INVOKE);
System.out.println("phase:" + phase);
}
public void handleMessage(SoapMessage message) throws Fault {
// 获取SOAP消息的全部头
List<Header> headers = message.getHeaders();
if (null == headers || headers.size() < 1) {
throw new Fault(new SOAPException("SOAP消息头格式不对哦!"));
}
SoapHeader soapHeader = (SoapHeader) headers.get(0);
Element element = (Element) soapHeader.getObject();
NodeList userIdNodes = element.getElementsByTagName("userName");
NodeList pwdNodes = element.getElementsByTagName("passWord");
// 读取自定义的节点
String username = userIdNodes.item(0).getTextContent();
String password = pwdNodes.item(0).getTextContent();
String jylsh = element.getElementsByTagName("jylsh")
.item(0)
.getTextContent();
String jybh = element.getElementsByTagName("jybh")
.item(0)
.getTextContent();
String jyyzm = element.getElementsByTagName("jyyzm")
.item(0)
.getTextContent();
// rsa解密
try {
username = new String(RsaUtil.pubDecrypt(username, GetSptPublicKey.getInstance()), "utf-8");
password = new String(RsaUtil.pubDecrypt(password, GetSptPublicKey.getInstance()), "utf-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
}
// 去掉交易流水号
username = username.replaceFirst(jylsh, "");
password = password.replaceFirst(jylsh, "");
HttpServletRequest request = (HttpServletRequest) message.get(AbstractHTTPDestination.HTTP_REQUEST);// 这句可以获取到request
RemoteObj ro = new RemoteObj(username, password, jybh, jyyzm, jylsh);
request.setAttribute("RemoteObjForCbd", ro);
}
}
关于消息拦截器,建议参考Spring或者是CXF的官方文档。
8. 上面叙述的是作为被调用方的程序写法,下面是作为调用方的写法(只列出import部分和方法体代码):
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import com.dareway.framework.util.DataObject;
import com.dareway.framework.workFlow.BPOSupport;
import com.util.LogUtilJyd;
import com.ydfw.pub.MessageGZIP;
import com.ydfw.pub.Names;
public DataObject requestService(DataObject para) throws Exception {
DataObject vdo = new DataObject();
StringBuffer logBF = new StringBuffer();
// 接收业务系统传参
// 交易流水号
String jylsh = para.getString("jylsh", "");
String jyyzm = para.getString("jyyzm", "");
// 交易编号
String jybh = para.getString("jybh", "");
// xml参数
String str_xml = para.getString("str_xml", "");
org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory dcf = org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory.newInstance();
org.apache.cxf.endpoint.Client client = dcf.createClient(Names.SPT_SERVICEADDRESS);//
client.getOutInterceptors().clear();
client.getOutInterceptors()
.add(new CallSptYdZxAddSoapHeader(Names.SPT_NAMESPACE, Names.SPT_USERNAME, Names.SPT_PASSWORD, jyyzm, jylsh, jybh));
// 设置输入字符串
byte[] xmlzip = MessageGZIP.compressToByte(str_xml, "utf-8");
// 调用服务
Object[] objects = client.invoke(Names.SPT_SERVICENAME, new Object[] { xmlzip });
// 获取返回字符串
String result = MessageGZIP.uncompressToString((byte[]) objects[0], "UTF-8");
vdo.put("returnXML", result);
return vdo;
}
解释几个变量及常量:
1)Names.SPT_SERVICEADDRESS ,对方webservice的地址,格式如下:
http://IP:PORT/webproject/services/scydzxServer?wsdl
IP 和 PORT分别指 IP地址 和 端口
2)Names.SPT_NAMESPACE 命名空间,输入上面的地址看到的targetName
3)Names.SPT_USERNAME 和 Names.SPT_PASSWORD,调用对方需要验证的用户名和密码。可参看前面作为被调用方的消息拦截器中,会取到这两个密码,在服务方法中可进行校验。
4)CallSptYdZxAddSoapHeader 该类用于添加SOAPHeader信息,代码如下:
public class CallSptYdZxAddSoapHeader extends AbstractSoapInterceptor{
private String nameURI;
private String userName;
private String passWord;
private String jyyzm;
private String jylsh;
private String jybh;
public CallSptYdZxAddSoapHeader() {
super(Phase.WRITE);
}
public CallSptYdZxAddSoapHeader(String nameURI, String userName,
String passWord, String jyyzm, String jylsh, String jybh) {
super(Phase.WRITE);
this.nameURI = nameURI;
this.userName = userName;
this.passWord = passWord;
this.jyyzm = jyyzm;
this.jylsh = jylsh;
this.jybh = jybh;
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
QName qname = new QName("RequestSOAPHeader");
Document doc = DOMUtils.createDocument();
// 用户名
Element el_username = doc.createElement("userName");
// 密码
Element el_password = doc.createElement("passWord");
try {
el_username.setTextContent(RsaUtil.pubEncrypt((jylsh + userName).getBytes("utf-8"), GetSptPublicKey.getInstance()));
el_password.setTextContent(RsaUtil.pubEncrypt((jylsh + passWord).getBytes("utf-8"), GetSptPublicKey.getInstance()));
} catch (DOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
}
Element el_jylsh = doc.createElement("jylsh");
el_jylsh.setTextContent(jylsh);
Element el_jyyzm = doc.createElement("jyyzm");
el_jyyzm.setTextContent(jyyzm);
Element el_jybh = doc.createElement("jybh");
el_jybh.setTextContent(jybh);
Element root = doc.createElementNS(nameURI, "in:system");
root.appendChild(el_username);
root.appendChild(el_password);
root.appendChild(el_jylsh);
root.appendChild(el_jyyzm);
root.appendChild(el_jybh);
SoapHeader head = new SoapHeader(qname, root);
List<Header> headers = message.getHeaders();
headers.add(head);
}
}
9. 该项目与业务系统通过Servlet的通信简介:
调用业务系统代码如下:
public DataObject requestService(DataObject para) throws Exception {
DataObject vdo = new DataObject();
try {
String urlS = "http://10.160.32.239:9090/dzydfw/servlet/ServiceForJYDServlet";
HttpURLConnection urlConnection = null;
URL url = new URL(urlS);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("Content-Type", "text/xml;charset=GBK");
urlConnection.setRequestProperty("Charset", "GBK");
urlConnection.setDoOutput(true);
urlConnection.setDoInput(true);
urlConnection.setUseCaches(false);
urlConnection.setConnectTimeout(20000);
StringBuffer param = new StringBuffer();
param.append(genXML(para.getString("jylsh"), para.getString("jybh"), para.getString("jyyzm"), para.getString("username"), para.getString("password"), para.getString("inputxml")));
BufferedOutputStream outStream = new BufferedOutputStream(urlConnection.getOutputStream());
outStream.write(param.toString().getBytes());
outStream.flush();
outStream.close();
InputStream in = urlConnection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
StringBuffer temp = new StringBuffer();
String line = bufferedReader.readLine();
while (line != null) {
temp.append(line);
line = bufferedReader.readLine();
}
bufferedReader.close();
in.close();
urlConnection.disconnect();
vdo.put("returnXML", temp.toString());
return vdo;
} catch (Exception e) {
e.printStackTrace();
}
}
这里特别注意,
BufferedOutputStream outStream = new BufferedOutputStream(urlConnection.getOutputStream());
outStream.write(param.toString().getBytes());
outStream.flush();
outStream.close();
不能写成
urlConnection.getOutputStream().write(param.toString().getBytes());
urlConnection.getOutputStream().flush();
urlConnection.getOutputStream().close();
这种方式在tomcat下没问题,但是在weblogic中无法将字节流写到servlet中。
因为需要传递xml,采用字节流的方式写到servlet中,接收方代码如下:
方法doPost中,
BufferedReader curReader;
curReader = request.getReader();
String strtmp = curReader.readLine();
for (; strtmp != null; strtmp = curReader.readLine()) {
sb.append(strtmp);
}
curReader.close();
其他说明:
平时工作中webservice用的不是特别多,本人也是新接触,为了某个项目而突击找资料学习的,多有不足之处;
由于项目中使用了某公司提供的部分代码,不便于将程序包(war)放到附件中,如有需要的可给我发邮件,邮箱为 jackeysion@126.com。
再就是有一个问题还未解决,用cxf创建的项目在tomcat下可以正常运行,但是部署到weblogic上貌似会提示jar包冲突,问度娘没找到好的解决方案,如果大家有好的解决方案还望告知~