在对新一代的Jaxp做了基本学习以后,那么对于axis2如何处理SOAP消息有了基本的了解,在跟踪了代码调试以后,发现问题主要是出在axis2的rampart模块的Axis2Util类,其中的两个方法getDocumentFromSOAPEnvelope(SOAPEnvelope env,boolean useDoom)和getSOAPEnvelopeFromDOMDocument(SOAPEnvelope env,boolean useDoom)。在有WS-Security和没有的不同情况下,传入的参数useDoom为true和false,导致走了两个不同的解析流程。当useDoom为true的时候,axis2通过SOAPEnvelope对象和axis2的Streaming parser来解析和构建Dom Document。当useDoom为false的时候,首先将SOAPEnvelope对象读入字节数组流,然后在根据Stax工厂生成实例,并且构造出StAXSOAPModelBuilder,然后返回通过StAXSOAPModelBuilder获得的Dom Document对象。
察看了一下调用者传入参数的地方,其实是通过MsgContext的参数配置来确定采取什么策略,因此只需要将axis2.xml中配置增加一个parameter,设置useDoom为true即可。或者就是做一个handle或者phase在Inflow和outflow中配置这个参数即可。
搞了那么久也就是修改一个配置,如果光从结果看,花了两天时间真是比较浪费,但是如果从过程来看,那么这两天时间所学到的那还是比较值的。
由于第二天是周日,问题解决了也就没有再继续细究。但是周日早晨晨跑的时候,给自己列了三个疑问,首先为什么走系统获取的Stax会有问题,再则如果我用sun的jaxp实现来替换是否能够解决此问题而不需要配置useDoom。useDoom两种处理模式究竟有什么区别。
问题的再次细究
周一上班的时候还是记得周日早晨提出的三个问题,因此就仔细的再分析了一下这三个问题。
首先是采取sun的jaxp替换,这个实现在sun的jwsdp中已经包含了,替换以后然后强制在jre的配置文件中指定使用,出现了异常,看来直接使用还不行,需要针对一些参数作配置,特别是对namingspace的解析,同时也没有花更多时间去细研究。
再则,仔细回想了一下我在定位这个问题的时候做的实验,我曾经试图将中文先用Base64编码,然后服务端接收到以后回传,客户端再用Base64解码,没有任何问题。有时候换一个中文或者中文前后有字母数字,也可以正常处理,同时在跟踪代码过程中看过SOAP消息中的内容,内容是乱码。这让我有点启发,例如我输入参数为“岑文初”每次始终都会出错,如果输入为“岑文”,就没有问题,看了看内存内的变量,发现,原来如果是“岑文初”的时候SOAP消息中的标签封闭被破坏,如果是“岑文”,虽然是乱码,但是没有破坏标签的封闭。
仔细看了看上次提到的两个流程,其实两个流程除了parser不同以外,对于SOAPEnvelope的处理也是不一样的,走UseDoom的是直接将分析好的Dom对象返回,不做附加的处理,只是根据Envelope生成了SOAP的解析器以及配置了Stax的Cursor的两个接口实现类。不走UseDoom的情况则是完全将SOAPEnvelope再次序列化并且通过外部的Stax实现来解析和处理,但是问题就出在对象到字节流的序列化过程,默认的是使用了SOAP规定的utf-8编码方式,因此在这个过程中有些中文的内容就破坏了SOAP的消息包XML的标签合法性,导致外部解析器分析出现问题。如果将传入和传出的中文都编码成utf-8没有任何问题。
问题总结就是其实根源在于对于内容中的中文字符编码时采用Utf-8破坏了xml的封闭性,而我开始采用的useDoom正好规避了这个过程,也就自然通过了。但是就其设计本身来说,rampart应该是赞同使用useDoom为false的方式,这才是真正的Stax的模式,同时有很好扩展性。另一方面个人觉得类似于这种抽象工厂机制来说,最好不要在系统变量或者jre中强制指定,这样会导致一些意想不到的问题,虽然是规范,但是细节实现毕竟有差异,因此一些特殊的开源框架的一些莫名其妙的xml解析问题也常常由于这些引起。
几点感悟
灵活的SPI(Service Provider Interface)模式是当前框架设计以及底层设计的必要特质,开放才会发展得更好。
灵活是把双刃剑,在遇到一些灵活的框架设计时,首先必须了解其原理和结构,然后根据实践来验证问题的缘由。
抽象工厂还是有适用场景的,类似于
Jaxp
和
SCA
等框架的实现,抽象工厂以及利用
Jar
的
Meta-INF/services
来载入
SPI
的实现是
IOC
的一种很好的补充。