由于项目需要对接别的系统,需要调用发布的webService接口上传数据。网上很多方式,最后选用看CXF,写客户端。
Apache CXF是一个开源的,全功能的,容易使用的Web服务框架。CXF是两个项目的结合:由IONA技术公司(现在是Progress的一部分)开发的Celtix和由Codehaus主持的团队开发的XFire,合并是由人们在在Apache软件基金会共同完成的。CXF的名字来源于"Celtix"和"XFire"的首字母。
第一步是需要下载依赖的jar包,项目的使用maven的方式下载jar包。
pom文件加入以下的配置。
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>2.7.14</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>2.7.14</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.codehaus.woodstox/stax2-api -->
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>stax2-api</artifactId>
<version>3.1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.codehaus.woodstox/woodstox-core-asl -->
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>
<version>4.2.1</version>
</dependency>
调用的工具类代码如下:
package com.minstone.utils;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.apache.cxf.service.model.BindingInfo;
import org.apache.cxf.service.model.BindingOperationInfo;
public class WebServiceUtils {
public static Boolean uploadData(Map data ,String webServiceUrl) {
String method = "uploadData";
List<Object> list = new ArrayList<Object>();
// list.add(data.getDataType());
// list.add(data.getContent());
// list.add(dateToXmlDate(data.getReportDate()));
// list.add("yongxiuToken");
Object[] parameters = list.toArray();
System.out .println(invokeRemoteMethod(webServiceUrl, method, parameters)[0]);
return (Boolean) parameters[0];
}
/**
* 将Date类转换为XMLGregorianCalendar
*
* @param date
* @return
*/
public static XMLGregorianCalendar dateToXmlDate(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
DatatypeFactory dtf = null;
try {
dtf = DatatypeFactory.newInstance();
} catch (DatatypeConfigurationException e) {
}
XMLGregorianCalendar dateType = dtf.newXMLGregorianCalendar();
dateType.setYear(cal.get(Calendar.YEAR));
// 由于Calendar.MONTH取值范围为0~11,需要加1
dateType.setMonth(cal.get(Calendar.MONTH) + 1);
dateType.setDay(cal.get(Calendar.DAY_OF_MONTH));
dateType.setHour(cal.get(Calendar.HOUR_OF_DAY));
dateType.setMinute(cal.get(Calendar.MINUTE));
dateType.setSecond(cal.get(Calendar.SECOND));
return dateType;
}
public static Object[] invokeRemoteMethod(String url, String operation,
Object[] parameters) {
JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
if (!url.endsWith("wsdl")) {
url += "?wsdl";
}
org.apache.cxf.endpoint.Client client = dcf.createClient(url);
// 处理webService接口和实现类namespace不同的情况,CXF动态客户端在处理此问题时,会报No operation was
// found with the name的异常
Endpoint endpoint = client.getEndpoint();
QName opName = new QName(endpoint.getService().getName()
.getNamespaceURI(), operation);
BindingInfo bindingInfo = endpoint.getEndpointInfo().getBinding();
if (bindingInfo.getOperation(opName) == null) {
for (BindingOperationInfo operationInfo : bindingInfo
.getOperations()) {
if (operation.equals(operationInfo.getName().getLocalPart())) {
opName = operationInfo.getName();
break;
}
}
}
Object[] res = null;
try {
res = client.invoke(opName, parameters);
} catch (Exception e) {
e.printStackTrace();
}
return res;
}
}
基本的方式如上面,不过在使用的过程中也出现了一些坑。
错误Caused by: javax.wsdl.WSDLException: WSDLException: faultCode=PARSER_ERROR这是因为没有添加 stax2-api-3.1.x.jar 和 woodstox-core-asl-4.2.x.jar 这两个jar包
还有很大的一坑是java.lang.RuntimeException: Cannot create a secure XMLInputFactory这个错误,查看源码
public static XMLInputFactory createXMLInputFactory(boolean nsAware) {
XMLInputFactory factory = null;
try {
factory = XMLInputFactory.newInstance();
} catch (Throwable t) {
factory = null;
}
if (factory == null || !setRestrictionProperties(factory)) {
try {
factory = createWoodstoxFactory();
} catch (Throwable t) {
//ignore for now
}
if (!setRestrictionProperties(factory)) {
if (allowInsecureParser) {
LOG.log(Level.WARNING, "INSECURE_PARSER_DETECTED", factory.getClass().getName());
} else {
throw new RuntimeException("Cannot create a secure XMLInputFactory");
}
}
}
setProperty(factory, XMLInputFactory.IS_NAMESPACE_AWARE, nsAware);
setProperty(factory, XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
setProperty(factory, XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.FALSE);
setProperty(factory, XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
factory.setXMLResolver(new XMLResolver() {
public Object resolveEntity(String publicID, String systemID,
String baseURI, String namespace)
throws XMLStreamException {
throw new XMLStreamException("Reading external entities is disabled");
}
});
return factory;
}
从源代码可以看出,抛出异常的原因是allowInsecureParser为false,那我们再看看allowInsecureParser到底是何方神圣
static {
int i = getInteger("org.apache.cxf.staxutils.pool-size", 20);
NS_AWARE_INPUT_FACTORY_POOL = new ArrayBlockingQueue<XMLInputFactory>(i);
OUTPUT_FACTORY_POOL = new ArrayBlockingQueue<XMLOutputFactory>(i);
//old names
innerElementCountThreshold = getInteger(INNER_ELEMENT_COUNT_SYSTEM_PROP, innerElementCountThreshold);
innerElementLevelThreshold = getInteger(INNER_ELEMENT_LEVEL_SYSTEM_PROP, innerElementLevelThreshold);
//new names
innerElementCountThreshold = getInteger(MAX_CHILD_ELEMENTS, innerElementCountThreshold);
innerElementLevelThreshold = getInteger(MAX_ELEMENT_DEPTH, innerElementLevelThreshold);
maxAttributeCount = getInteger(MAX_ATTRIBUTE_COUNT, maxAttributeCount);
maxAttributeSize = getInteger(MAX_ATTRIBUTE_SIZE, maxAttributeSize);
maxTextLength = getInteger(MAX_TEXT_LENGTH, maxTextLength);
maxElementCount = getLong(MAX_ELEMENT_COUNT, maxElementCount);
maxXMLCharacters = getLong(MAX_XML_CHARACTERS, maxXMLCharacters);
**String s = SystemPropertyAction.getPropertyOrNull(ALLOW_INSECURE_PARSER);**
if (!StringUtils.isEmpty(s)) {
allowInsecureParser = "1".equals(s) || Boolean.parseBoolean(s);
}
XMLInputFactory xif = null;
try {
xif = createXMLInputFactory(true);
String xifClassName = xif.getClass().getName();
if (!xifClassName.contains("ctc.wstx") && !xifClassName.contains("xml.xlxp")
&& !xifClassName.contains("xml.xlxp2") && !xifClassName.contains("bea.core")) {
xif = null;
}
} catch (Throwable t) {
//ignore, can always drop down to the pooled factories
xif = null;
}
SAFE_INPUT_FACTORY = xif;
XMLOutputFactory xof = null;
try {
xof = XMLOutputFactory.newInstance();
String xofClassName = xof.getClass().getName();
if (!xofClassName.contains("ctc.wstx") && !xofClassName.contains("xml.xlxp")
&& !xofClassName.contains("xml.xlxp2") && !xofClassName.contains("bea.core")) {
xof = null;
}
} catch (Throwable t) {
//ignore, can always drop down to the pooled factories
}
SAFE_OUTPUT_FACTORY = xof;
}
####从源代码可以看出,allowInsecureParser是类StaxUtils的一个静态变量,而这个变量的值取决于SystemPropertyAction.getPropertyOrNull(ALLOW_INSECURE_PARSER);
继续往下走,我们可以看看SystemPropertyAction是什么(SystemPropertyAction.java):
所以在项目加载的时候要先执行以下的代码
Properties props = System.getProperties();
props.setProperty("org.apache.cxf.stax.allowInsecureParser", "1");
props.setProperty("UseSunHttpHandler", "true");
不然调用会报错