最近做的一个项目,需要读取华为一个叫iMaster NCE的网管软件的北向接口。这个iMaster NCE(以下简称NCE)用于管理项目的整个网络,尤其是光网络。业主要求我们访问该软件提供的对外接口,读取一些网络信息,比如网元、故障告警、性能等。
NCE的接口提供了多种协议的版本,给过来的资料是基于SOAP的WebService,据说目前最常用的http版本要收费。
基于SOAP的WebService,我以前用.NET的时候搞过。首先要在项目里访问WebService的WSDL,生成一个客户端(代理),然后本地通过此代理对接口进行访问。Java项目也是类似的套路。以下是d调用XML接口的开发步骤介绍,并附上一个读取NCE上告警信息数量的实例。
一、开发前准备
1、选择SOAP中间件
在开始开发之前,需要选择适当的SOAP中间件来实现WebService的客户端。
开源SOAP中间件主要包括:
Apache CXF
Apache AXIS
商业SOAP中间件主要包括:
IBM Websphere
BEA Weblogic
理论上可以自由选择任意符合SOAP规范的中间件以进行客户端的开发。华为选择Apache CXF作为服务端中间件,同时建议客户端也采用Apache CXF,以获得最大的兼容性。不过Apache CXF只支持使用JAVA作为编程语言。我们用的正是java,很自然就选用Apache CXF。
2、获取WSDL文件
接入XML接口,首先需要获取WSDL文件。获取WSDL的方式有两种,分别是使用发布文档中打包的WSDL文件,以及直接访问每一个XML接口所在服务地址来获取。可以通过北向XML接口服务地址获取WSDL,比如http://10.71.226.29:9997/ManagedElementRetrieval?wsdl
这种方式一次只能获取一种服务的WSDL。由于厂家直接给了所有WSDL,所以这一步就省了。
3、生成Stub代码
1)生成代码
获取到WSDL文件后,需要通过Apache CXF附带的wsdl2java工具将其转换成JAVA代码。wsdl2java可以到Apache CXF官网下载。需要注意的是,新的版本不支持JDK8,如果还是JDK8,最后的版本是apache-cxf-3.5.9。
生成语句示例:
apache-cxf不需安装,解压即用。
bash
cd D:\soft\develop\wsdl\apache-cxf-3.5.9\bin
wsdl2java -client -xjc-npa -d q:\test2 Q:\wsdl\ManageResourceInventory\IIS\wsdl\TopoViewRetrieval\TopoViewRetrievalHttp.wsdl
生成了一堆代码:
2)创建Stub代码项目
将生成的代码搭建一个项目,比如叫mtop项目。则可以将它生成一个jar包,比如叫mtop.jar。
3)应用Stub代码
然后将它放到我们读取NCE的项目中:
4、后面加入新的接口
上面只是生成了一个wsdl的Stub代码。如果后面需要加入新的wsdl,则如法炮制,先用wsdl2java生成Stub代码,然后将整个代码文件拷贝到Stub项目,遇到同名的忽略,不要覆盖,相当于只拷贝了新增的文件。然后重新生成jar包。
二、示例
以下是一个读取NCE告警数量的示例。
1、基本工具
java
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.landtool.jbh.entity.Otn;
import com.landtool.jbh.service.OtnService;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Component;
import org.tmforum.mtop.fmw.xsd.hdr.v1.CommunicationPatternType;
import org.tmforum.mtop.fmw.xsd.hdr.v1.CommunicationStyleType;
import org.tmforum.mtop.fmw.xsd.hdr.v1.Header;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.xml.ws.Holder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* otn的地址及账号信息,既定义于配置文件,也保存于数据库
* 从代码可看出,优先级上,数据库 > 配置文件
*/
@Component
public class WebServiceUtil {
// the type of communication pattern
private static CommunicationPatternType patternType = CommunicationPatternType.SIMPLE_RESPONSE;
// the type of communication style
private static CommunicationStyleType styleType = CommunicationStyleType.RPC;
// the Web Service URL
private static String strURL = "http://10.0.2.18:9997/";
public void setStrUrl(String value) {
WebServiceUtil.strURL = value;
}
// userName
private static String strUser = "BXuser";
public void setStrUser(String value) {
WebServiceUtil.strUser = value;
}
// password
private static String strPassword = "Changeme_147";
public void setStrPassword(String value) {
WebServiceUtil.strPassword = value;
}
// MD
private static String strMD = "Huawei/NCE";
@Value("${data-oc.md}")
public void setStrMD(String value) {
WebServiceUtil.strMD = value;
}
public static String getStrMD() {
return WebServiceUtil.strMD;
}
@Resource
private OtnService otnService;
@PostConstruct
public void init() {
Otn otn = new Otn();
List<Otn> list = this.otnService.queryByPage(otn, PageRequest.of(0, 1000)).getContent();
if (list.size() > 0) {
otn = list.get(0);
setStrPassword(otn.getPassword());
setStrUser(otn.getAccount());
setStrUrl(String.format("http://%s:%s/", otn.getIp(), otn.getPort()));
setStrMD(otn.getNote());
}
}
public static <T> T getWebService(String action, Class<T> clazz) {
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(clazz);
Map<String, Object> properties = factory.getProperties();
if (properties == null) {
properties = new HashMap<String, Object>();
factory.setProperties(properties);
}
properties.put("set-jaxb-validation-event-handler", Boolean.FALSE);
factory.setAddress(strURL + action);
T webservice = (T) factory.create();
try {
//TrustHttpsConnection();
//configureSSLClient(webservice);
} catch (Exception e) {
e.printStackTrace();
}
return webservice;
}
public static Holder<Header> getHeader() {
Header header = new Header();
header.setCommunicationPattern(patternType.value());
header.setCommunicationStyle(styleType.value());
header.setBatchSequenceNumber(1L);
header.setRequestedBatchSize(1000L);
header.setSecurity(strUser + ":" + strPassword);
return (new Holder<Header>(header));
}
}
2、应用示例
从下面密密麻麻的import可以看出,大部分都是引用Stub代码的。
java
import org.springframework.stereotype.Component;
import org.tmforum.mtop.fmw.xsd.gen.v1.NameAndAnyValueType;
import org.tmforum.mtop.fmw.xsd.gen.v1.ObjectEnumType;
import org.tmforum.mtop.fmw.xsd.nam.v1.NamingAttributeType;
import org.tmforum.mtop.fmw.xsd.nam.v1.RelativeDistinguishNameType;
import org.tmforum.mtop.mri.wsdl.mer.v1_0.ManagedElementRetrieval_RPC;
import org.tmforum.mtop.mri.wsdl.tlr.v1_0.GetAllTopLevelTopologicalLinksException;
import org.tmforum.mtop.mri.wsdl.tlr.v1_0.TopologicalLinkRetrievalRPC;
import org.tmforum.mtop.mri.wsdl.tpr.v1_0.GetAllPhysicalTerminationPointsWithoutFtpsException;
import org.tmforum.mtop.mri.wsdl.tpr.v1_0.GetContainedPotentialConnectionTerminationPointsException;
import org.tmforum.mtop.mri.wsdl.tpr.v1_0.TerminationPointRetrievalRPC;
import org.tmforum.mtop.mri.wsdl.tvr.v1_0.GetAllTopoViewNodesInfoException;
import org.tmforum.mtop.mri.wsdl.tvr.v1_0.TopoViewRetrievalRPC;
import org.tmforum.mtop.mri.xsd.mer.v1.GetAllManagedElementsRequest;
import org.tmforum.mtop.mri.xsd.mer.v1.MultipleMeObjectsResponseType;
import org.tmforum.mtop.mri.xsd.tlr.v1.GetAllTopLevelTopologicalLinksRequest;
import org.tmforum.mtop.mri.xsd.tlr.v1.MultipleTlObjectsResponseType;
import org.tmforum.mtop.mri.xsd.tpr.v1.GetAllConnectionTerminationPointsType;
import org.tmforum.mtop.mri.xsd.tpr.v1.GetAllTerminationPointsType;
import org.tmforum.mtop.mri.xsd.tpr.v1.MultipleTerminationPointObjectsResponseType;
import org.tmforum.mtop.mri.xsd.tvr.v1.GetAllTopoViewNodesInfoRequest;
import org.tmforum.mtop.mri.xsd.tvr.v1.GetAllTopoViewNodesInfoResponse;
import org.tmforum.mtop.nra.xsd.alm.v1.AlarmListType;
import org.tmforum.mtop.nra.xsd.alm.v1.AlarmType;
import org.tmforum.mtop.nra.xsd.pm.v1.PerformanceMonitoringParameterNameListType;
import org.tmforum.mtop.nra.xsd.pmdata.v1.PerformanceMonitoringDataType;
import org.tmforum.mtop.nra.xsd.pmmsrt.v1.PerformanceMonitoringMeasurementType;
import org.tmforum.mtop.nra.xsd.pmtgt.v1.ObjectFactory;
import org.tmforum.mtop.nra.xsd.pmtgt.v1.PerformanceMonitoringObjectSelectListType;
import org.tmforum.mtop.nra.xsd.pmtgt.v1.PerformanceMonitoringObjectSelectType;
import org.tmforum.mtop.nrf.xsd.me.v1.ManagedElementType;
import org.tmforum.mtop.nrf.xsd.tl.v1.TopologicalLinkType;
import org.tmforum.mtop.nrf.xsd.topo.v1.NodeType;
import org.tmforum.mtop.nrf.xsd.tp.v1.TerminationPointType;
import org.tmforum.mtop.rpm.wsdl.pmr.v1_0.GetAllCurrentPerformanceMonitoringDataException;
import org.tmforum.mtop.rpm.wsdl.pmr.v1_0.PerformanceManagementRetrieval;
import org.tmforum.mtop.rpm.xsd.pmr.v1.GetAllCurrentPerformanceMonitoringDataRequest;
import org.tmforum.mtop.rpm.xsd.pmr.v1.MultiplePerformanceMonitoringDataObjectsResponseType;
import org.tmforum.mtop.rtm.wsdl.ar.v1_0.AlarmRetrieval;
import org.tmforum.mtop.rtm.wsdl.ar.v1_0.GetActiveAlarmsCountException;
import org.tmforum.mtop.rtm.wsdl.ar.v1_0.GetActiveAlarmsException;
import org.tmforum.mtop.rtm.xsd.ar.v1.GetActiveAlarmsCountRequest;
import org.tmforum.mtop.rtm.xsd.ar.v1.GetActiveAlarmsCountResponse;
import org.tmforum.mtop.rtm.xsd.ar.v1.GetActiveAlarmsRequest;
import org.w3c.dom.Element;
import javax.xml.bind.JAXBElement;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
。。。
/**
* 故障告警信息数量
*/
public long getActiveAlarmsCount() {
long count = 0L;
try {
AlarmRetrieval alarmRetrieval = WebServiceUtil.getWebService("AlarmRetrieval", AlarmRetrieval.class);
GetActiveAlarmsCountRequest request = new GetActiveAlarmsCountRequest();
GetActiveAlarmsCountResponse response = alarmRetrieval.getActiveAlarmsCount(WebServiceUtil.getHeader(), request);
count = response.getActiveAlarmCount();
} catch (GetActiveAlarmsCountException e) {
e.printStackTrace();
System.err.println("failed getActiveAlarmsCount");
} catch (Exception ex) {
System.err.println(String.valueOf(ex.getCause()));
}
return count;
}
对应的NCE XML接口说明
三、总结
NCE的XML接口感觉就是异常繁琐,数据类型非常多,传参、返回值都非常复杂。如果是http接口的话,参数全部是json,结构简单明了。而且,利用XML传数据,少量数据还好,大批量数据就不行,XML相比json,实在笨重,传输量太大了。
华为这个nce软件,叫网管软件,它接管了整个局域网,然后在上面做各种逻辑划分和管理。不过,它加载需要一段时间。比如我们项目,设备通电以后,20分钟后网络才能访问。再上面做一些更改,比如用户解锁,要约2个小时才生效。之前遇到明明用户已经解锁了,还是不能登录,正疑惑的时候,忽然就可以了。这时已经过去了2个小时。