最近做的一个项目,需要读取华为一个叫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不需安装,解压即用。
cd D:\soft\develop\wsdl\apache-cxf-3.5.9\binwsdl2java -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、基本工具
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 patternprivate static CommunicationPatternType patternType = CommunicationPatternType.SIMPLE_RESPONSE;// the type of communication styleprivate static CommunicationStyleType styleType = CommunicationStyleType.RPC;// the Web Service URLprivate static String strURL = "http://10.0.2.18:9997/";public void setStrUrl(String value) {WebServiceUtil.strURL = value;}// userNameprivate static String strUser = "BXuser";public void setStrUser(String value) {WebServiceUtil.strUser = value;}// passwordprivate static String strPassword = "Changeme_147";public void setStrPassword(String value) {WebServiceUtil.strPassword = value;}// MDprivate static String strMD = "Huawei/NCE";@Value("${data-oc.md}")public void setStrMD(String value) {WebServiceUtil.strMD = value;}public static String getStrMD() {return WebServiceUtil.strMD;}@Resourceprivate OtnService otnService;@PostConstructpublic 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代码的。
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个小时。