项目中需要通过java获取exchange的新邮件,使用官方接口包,测试时总是有空指针(java.lang.NullPointerException)异常:
java.lang.NullPointerException
at microsoft.exchange.webservices.data.Strings.<clinit>(Strings.java:197)
at microsoft.exchange.webservices.data.EwsXmlReader.internalReadElement(EwsXmlReader.java:57)
at microsoft.exchange.webservices.data.EwsXmlReader.readStartElement(EwsXmlReader.java:345)
at microsoft.exchange.webservices.data.ServiceRequestBase.readResponse(ServiceRequestBase.java:285)
at microsoft.exchange.webservices.data.SimpleServiceRequestBase.readResponse(SimpleServiceRequestBase.java:126)
at microsoft.exchange.webservices.data.SimpleServiceRequestBase.internalExecute(SimpleServiceRequestBase.java:33)
at microsoft.exchange.webservices.data.MultiResponseServiceRequest.execute(MultiResponseServiceRequest.java:146)
at microsoft.exchange.webservices.data.ExchangeService.bindToFolder(ExchangeService.java:355)
at microsoft.exchange.webservices.data.ExchangeService.bindToFolder(ExchangeService.java:380)
at microsoft.exchange.webservices.data.Folder.bind(Folder.java:53)
at microsoft.exchange.webservices.data.Folder.bind(Folder.java:112)
at com.aires.Test.main(Test.java:28)
Exception in thread "main" java.lang.NullPointerException
at java.util.regex.Matcher.getTextLength(Unknown Source)
at java.util.regex.Matcher.reset(Unknown Source)
at java.util.regex.Matcher.<init>(Unknown Source)
at java.util.regex.Pattern.matcher(Unknown Source)
at java.util.Formatter.parse(Unknown Source)
at java.util.Formatter.format(Unknown Source)
at java.util.Formatter.format(Unknown Source)
at java.lang.String.format(Unknown Source)
at microsoft.exchange.webservices.data.EwsXmlReader.internalReadElement(EwsXmlReader.java:56)
at microsoft.exchange.webservices.data.EwsXmlReader.readStartElement(EwsXmlReader.java:345)
at microsoft.exchange.webservices.data.ServiceRequestBase.readResponse(ServiceRequestBase.java:285)
at microsoft.exchange.webservices.data.SimpleServiceRequestBase.readResponse(SimpleServiceRequestBase.java:126)
at microsoft.exchange.webservices.data.SimpleServiceRequestBase.internalExecute(SimpleServiceRequestBase.java:33)
at microsoft.exchange.webservices.data.MultiResponseServiceRequest.execute(MultiResponseServiceRequest.java:146)
at microsoft.exchange.webservices.data.ExchangeService.bindToFolder(ExchangeService.java:355)
刚开始时没多想,以为接口调用得有问题,后多方查找无果,反编译jar包,终于有所收获,特此记录:
1、第一个异常为缺少配置文件,不知道是不是我拿到的jar包有问题,问题出在microsoft.exchange.webservices.data.Strings,该类内容大概为错误信息常量,其中有一静态块
/* */ static
/* */ {
/* */ try
/* */ {//此处bundle为一Properties对象
/* 223 */ BUNDLE.load(Thread.currentThread().getContextClassLoader().getResource("microsoft/exchange/webservices/data/Strings.properties").openStream());
/* */ }
/* */ catch (Exception e) {
/* 226 */ e.printStackTrace();//此处为上文第一个NullPointerException出处
/* */ }
/* 228 */ loadProperties();
/* */ }
其中loadProperties做的是常量初始化:
private static void loadProperties()
/* */ {
/* 233 */ BothSearchFilterAndQueryStringCannotBeSpecified = BUNDLE.getProperty("BothSearchFilterAndQueryStringCannotBeSpecified");
/* */
/* 236 */ IndexIsOutOfRange = BUNDLE.getProperty("IndexIsOutOfRange");
/* */
/* 239 */ CannotCallConnectDuringLiveConnection = BUNDLE.getProperty("CannotCallConnectDuringLiveConnection");
/* 240 */ InvalidAutodiscoverSmtpAddressesCount = BUNDLE.getProperty("InvalidAutodiscoverSmtpAddressesCount");
/* 241 */ NoSoapOrWsSecurityEndpointAvailable = BUNDLE.getProperty("NoSoapOrWsSecurityEndpointAvailable");
/* 242 */ ObjectDoesNotHaveId = BUNDLE.getProperty("ObjectDoesNotHaveId");
。。。。。
当配置Strings.properties不存在时,抛出第一处异常并继续执行loadProperties(),执行完成后所有字符串“常量”被设置为null(因为此时BUNDLE内容为空)。
2、API包中,大量异常抛出使用了String.foramt方法,并将Strings中的“常量”(此时应为null)作为第一个参数传入,如:
microsoft.exchange.webservices.data.SimpleServiceRequestBase.java
/* */ catch (HTTPException e)
/* */ {
/* */ Object serviceResponse;
/* 175 */ if (e.getMessage() != null) {
/* 176 */ getService().processHttpResponseHeaders(
/* 177 */ TraceFlags.EwsResponseHttpHeaders, response);
/* */ }
/* */
/* 180 */ throw new ServiceRequestException(String.format(
/* 181 */ Strings.ServiceRequestFailed, new Object[] { e.getMessage() }), e);
/* */ }
/* */ catch (IOException e) {
/* 184 */ throw new ServiceRequestException(String.format(
/* 185 */ Strings.ServiceRequestFailed, new Object[] { e.getMessage() }), e);
/* */ }
此处有坑:第一个参数为null时,String.format将抛出NullPointerException,并且该异常将替代实际异常抛出,实际异常被“吃掉”;调用者在使用接口时捕获的是一个无用的空指针异常,然后被各种误导。。。。
3、解决方式:
1)增加对应配置文件;
2)修改Jar包中的异常抛出方式,抛出原异常;
我们项目中未考虑修改原jar包,配置文件内容又多,偷了个懒,简单将反编译的源码放入工程中覆盖jar包中的对应内容并做简单调整,输出实际的异常信息,根据实际异常修改调用方式至成功后,删除了添加到工程中的源码。我们的调整方式比较暴力,直接抛出原异常:
/* 167 */ InputStream responseStream =
/* 168 */ ServiceRequestBase.getResponseStream(response);
System.out.println("-----------响应内容开始-----------------");//此处用于查看接口返回数据
System.out.println("响应内容长度:" + responseStream.available());
BufferedReader br = new BufferedReader(new InputStreamReader(responseStream));
StringBuilder sb = new StringBuilder();
String tmp;
while((tmp = br.readLine()) != null){
sb.append(tmp);
System.out.println(tmp);
}
System.out.println("-----------响应内容结束-----------------");
ByteArrayInputStream bis = new ByteArrayInputStream(sb.toString().getBytes());
/* 169 */ EwsServiceXmlReader ewsXmlReader = new EwsServiceXmlReader(
/* 170 */ bis, getService());
/* 171 */ serviceResponse = readResponse(ewsXmlReader);
/* */ }
/* */ }
/* */ catch (HTTPException e)
/* */ {
/* 175 */ if (e.getMessage() != null) {
/* 176 */ getService().processHttpResponseHeaders(
/* 177 */ TraceFlags.EwsResponseHttpHeaders, response);
/* */ }
/* */
/* 180 */ throw e;//此处修改
/* */ }
/* */ catch (IOException e) {
/* 184 */ throw e;//此处修改
/* */ }
文首的第二个异常为此处:
EwsXmlReader.java
/* */ private void internalReadElement(XmlNamespace xmlNamespace, String localName, XMLNodeType nodeType)
/* */ throws Exception
/* */ {
/* 109 */ if (xmlNamespace == XmlNamespace.NotSpecified) {
/* 110 */ internalReadElement("", localName, nodeType);
/* */ } else {
/* 112 */ read(nodeType);
/* */
/* 114 */ if ((!getLocalName().equals(localName)) ||
/* 115 */ (getNamespaceUri() !=
/* 116 */ EwsUtilities.getNamespaceUri(xmlNamespace)))
/* 117 */ throw new ServiceXmlDeserializationException(
/* 119 */ String.format(
/* 120 */ Strings.UnexpectedElement, new Object[] {
/* 122 */ EwsUtilities.getNamespacePrefix(
/* 123 */ xmlNamespace),
/* 124 */ localName, nodeType.toString(),
/* 125 */ getName(), getNodeType()
/* 126 */ .toString() }));
/* */ }
/* */ }