Backgrond
由于官方不再维护IBMJDK1.6,公司内部有一套十多年前的构建系统,需要从IBMJDK1.6升级到OpenJDK1.8.其中有一个功能是HTMLbuild,会根据定义文件生成大量的HTML文件以及文件清单.但是在迁移之后,发现某些情况下,会报buildfailure的异常.
Difficulties
如下图所示,构建系统本身只提供了构建的flow,而具体的构建逻辑是由开发者自己定义脚本来实现的.由于构建脚本本身逻辑复杂,并且已经很多年没有做更改了,难以测试.所以迁移的方案是仅更改运行环境,对开发者自定义的脚本尽量不做更改.但是这就意味着构建程序和脚本之间都是跨进程调用,而且中间可能跨了不止一个进程,导致直接在如果在脚本执行中出现问题,不能直接打断点进行调试.
image.png
\2.HTMLbuild是分为不同的国家.Failure仅固定出现在其中某几个国家的build过程中.
Troubleshooting
log中的报错信息如下,可以看到因为有一个Fatal的报错导致build失败,但是Fatal的原因从日志中无法看出.报错的地方是启动了一个java进程去直接运行一个开发者提供的jar包,因为又是跨多个进程调用,无法直接打断点.所以第一步需要做的是,找出执行该进程时的参数以及环境变量,配好环境后,单独运行改jar包debug
[exec][java][FATAL]caughtexception:null[exec][java][INFO]+++++Logerrorcounts:[exec][java]FATAL:1logged,outof1call(s).[exec][java]ERROR:0logged,outof0call(s).[exec][java]WARN:logged,outofcall(s).[exec][java]INFO:logged,outofcall(s).[exec][java]CONFIG:0logged,outof0call(s).[exec][java]DEBUG:0logged,outofcall(s).[exec][java][cms:INFO]Main.exit:exitcode=1[exec][java][cms:INFO]Exitcode:0=Success,1=Error,2=Warning[exec][java]JavaResult:1
配置完成后,找到报错的位置,可以得到真实的失败原因如下.去对应的路径下查找,的确没有该文件,但是却有一个类似的文件:error-browsers.html%C2%A0
java.io.FileNotFoundException:/Users/***/help/error-browsers.html(Nosuchfileordirectory)
找到对应的定义文件,里面对于该文件的定义如下.可以发现,在标签结束的地方有一个空格.再结合上面看到的文件名,可以发现这是一个utf-8种特殊的空格,感兴趣的同学可以自行搜索下半角非中断字符,在这里就过多介绍了.
urlhelp/sell/photo-error-browsers.html/url
FileNotFoundException就是由这个特殊字符引起的.OpenJDK1.8在生成xml文件时,会用到StreamResult,当StreamResult接收的是文件对象时,会将文件的pathescape,如果文件名中含有特殊字符,就会造成原本文件对象中的path和StreamResult中的路径不一致,所以即使在文件生成后,使用原本的文件对象也无法找到对应的文件.下面是一个sample,模拟文件路径中有特殊字符的情况.同时也将JDK中关于StreamResult的做了对比.
importjavax.xml.transform.stream.StreamResult;importjava.io.File;importjava.nio.charset.StandardCharsets;publicclasstest{publicstaticvoidmain(String[]args){Stringfilename="/Users/***/Documents/error-browsers.html"+newString(newbyte[]{-62,-96},StandardCharsets.UTF_8);FileoutputFile=newFile(filename);StreamResults1=newStreamResult(filename);StreamResults2=newStreamResult(outputFile);System.out.println(outputFile.getPath());System.out.println(s1.getSystemId());System.out.println(s2.getSystemId());}}
OpenJDK1.8
publicStreamResult(Filef){//convertfiletoappropriateURI,f.toURI().toASCIIString()//convertstheURItostringasperrulespecifiedin//RFC,setSystemId(f.toURI().toASCIIString());}
IBMJDK1.6
publicStreamResult(Filef){this.setSystemId(f);}
Conclusion
在迁移过程中,经常会碰见各式各样奇怪的问题,需要保持刨根问底的精神,其实最后发现,即使是由JDK行为变化而产生的报错,实际上也并不复杂.