Technorati 標(biāo)簽: axis,customized,serializer,deserializer,web service
現(xiàn)在在SOA被大肆鼓吹的時(shí)代,再加上確實(shí)企業(yè)級應(yīng)用平臺(tái)中不同系統(tǒng)間的整合越來越多,所以Web Service的地位日益上升。雖然效率上會(huì)有所折扣,但是畢竟是一個(gè)標(biāo)準(zhǔn),而且主流的編程語言本身或者加上一些框架都支持Web Service,無論是Server端還是Client端。記得在2001年底就做過一個(gè)Java Web Service Client調(diào)用Delphi Web Service Server,但是現(xiàn)在后悔的是當(dāng)時(shí)對于Web Service沒有認(rèn)真學(xué)習(xí),只是停留在淺嘗輒止的地步,正是應(yīng)了古人的那句:“書到用時(shí)方覺少”啊。
現(xiàn)在的項(xiàng)目就是遇到這個(gè)情況,大量的跨系統(tǒng)的通訊都是通過Web Service。偏偏有些服務(wù)端編寫的比較奇怪,而AXIS生成的客戶端又更加的奇怪。遇到的問題是這樣的,服務(wù)端返回的數(shù)據(jù)中,有幾個(gè)類型為xsd:dateTime,AXIS映射到Java的類是java.util.Calendar。偏偏服務(wù)器有時(shí)候有的字段是不會(huì)帶有任何數(shù)據(jù)的,有的字段是一個(gè)字符"T"(因?yàn)閃eb Service中傳輸dateTime類型的數(shù)據(jù)字符串格式為yyyy-MM-dd'T'hh:mm:ss.SSS,例如2008-07-24T23:49:15.000),也就是應(yīng)該生成對應(yīng)Java的null對象。可能是考慮到和.NET客戶端的兼容性吧(.NET不熟悉,但是為了解決這個(gè)問題查看一些資料,都是說對于dateTime類型,如果是空,會(huì)在.NET的客戶端出現(xiàn)問題),AXIS在處理無數(shù)據(jù)的dateTime類型的節(jié)點(diǎn)時(shí),就粗暴的報(bào)錯(cuò)了。沒辦法,服務(wù)器端是沒有辦法改程序了,只好在客戶端下手了,反正這幾個(gè)字段對于我的客戶端不是很重要,只要不要因?yàn)檫@幾個(gè)特殊的數(shù)據(jù)影響了其它數(shù)據(jù)。
第一個(gè)反應(yīng)就是修改AXIS的代碼,但是這種方案是不到萬不得已是不能用的,太危險(xiǎn)了。然后就看AXIS的文檔以及API,看到有typeMapping的配置項(xiàng),但是對于其中的各個(gè)屬性又沒有詳細(xì)闡述,網(wǎng)上的例子大部分都是針對服務(wù)器端的。經(jīng)過詢問其它同事,他們也遇到了類似的問題,他們定了自己的序列化/反序列化類,然后在AXIS產(chǎn)生的客戶端Stub代碼中以服務(wù)命名的方法,例如process方法插入自定義的序列化/反序列化類:
public XXResponse process(XXRequest req) throws java.rmi.RemoteException {
if (super.cachedEndpoint == null) {
throw new org.apache.axis.NoEndPointException();
}
org.apache.axis.client.Call _call = createCall();
_call.setOperation(_operations[0]);
_call.setUseSOAPAction(true);
_call.setSOAPActionURI("process");
_call.setEncodingStyle(null);
_call.setProperty(org.apache.axis.client.Call.SEND_TYPE_ATTR,
Boolean.FALSE);
_call.setProperty(org.apache.axis.AxisEngine.PROP_DOMULTIREFS,
Boolean.FALSE);
_call
.setSOAPVersion(org.apache.axis.soap.SOAPConstants.SOAP11_CONSTANTS);
_call.setOperationName(new javax.xml.namespace.QName("", "process"));
WsUtil.prepareCall(_call); // 這一句是用來插入自定義的Serializer和Deserializer
setRequestHeaders(_call);
看看WsUtil的prepareCall如何編寫的:
public class WsUtil { 這里,通過調(diào)用org.apache.axis.client.Call對象的registerTypeMapping方法來插入自定義的Serializer和Deserializer。(以上代碼感謝我不認(rèn)識(shí)的那位同事無私的奉獻(xiàn)和幫助) 但是這種方法有一個(gè)問題就是如果重新生成了客戶端代碼,需要在Stub類中插入,也是比較具有破壞性的。我就覺得總有一個(gè)方法是比較優(yōu)雅的解決這個(gè)問題的,然后順著這個(gè)思路繼續(xù)往下找,發(fā)現(xiàn)可以在Service locator類,也即extends了org.apache.axis.client.Service的那個(gè)類來獲取到TypeMappingRegistery,于是,不再修改Stub類,在調(diào)用者代碼中,生成locator對象之后,調(diào)用注冊TypeMapping的方法: LabService locator = new LabServiceLocator(); tm.register(java.util.Date.class, new QName( org/apache/axis/client/client-config.wsdd 給出了一個(gè)樣本,但是是基本的配置,copy這個(gè)樣本到你的工程的source文件夾,不需要包即可,只要AXIS在運(yùn)行的時(shí)候,能夠在classes目錄找到這個(gè)文件就可以。 以下是配置文件及相關(guān)類的代碼: client-config.wsdd:(為了確保安全,把java.util.Date和java.util.Calendar都注冊了, 如果服務(wù)器端沒有強(qiáng)制指定encodingStyle,就把encodingStyle屬性設(shè)置為"",不知道為什么,改天抽時(shí)間再研究:P) ---------------------------------------------------------------------------------- <deployment xmlns=" ---------------------------------------------------------------------------------- 在client-config.wsdd樣本的基礎(chǔ)上修改的。 languageSpecificType="java:java.util.Date"以及l(fā)anguageSpecificType="java:java.util.Calendar"表明映射到Java中哪種類型的數(shù)據(jù)要求使用自定義的Serializer和Deserializer。注意寫法,前面有name space "java",
public static void prepareCall(org.apache.axis.client.Call _call) {
_call.registerTypeMapping(
java.util.Date.class,
new javax.xml.namespace.QName(" new CalendarSerializerFactory(java.util.Date.class, new javax.xml.namespace.QName( new CalendarDeserializerFactory(java.util.Date.class, new javax.xml.namespace.QName( _call.registerTypeMapping(
java.util.Calendar.class,
new javax.xml.namespace.QName(" new CalendarSerializerFactory(java.util.Date.class,
new javax.xml.namespace.QName( new CalendarDeserializerFactory(java.util.Date.class, new javax.xml.namespace.QName(" }
TypeMappingRegistry tmr = locator.getTypeMappingRegistry();
TypeMapping tm = tmr.getDefaultTypeMapping();
" new CustomizedCalendarSerializerFactory(java.util.Date.class,
new javax.xml.namespace.QName(
" new CustomizedCalendarDeserializerFactory(
java.util.Date.class,
new javax.xml.namespace.QName(
" tm.register(java.util.Calendar.class, new QName(
" new CustomizedCalendarSerializerFactory(java.util.Calendar.class,
new javax.xml.namespace.QName(
" new CustomizedCalendarDeserializerFactory(
java.util.Calendar.class,
new javax.xml.namespace.QName(
"通過測試,發(fā)現(xiàn)此方法可行,基本上比較好了,但是如果這么多的Service每次調(diào)用都干這么一件事,也是很麻煩的,而且,如果以后又有其它的自定義Serializer和Deserizlizer,還得修改代碼。于是乎繼續(xù)尋找更好的解決辦法。仔細(xì)閱讀AXIS的文檔,發(fā)現(xiàn)還有client-config.wsdd可用。在AXIS的代碼中,
xmlns:java="
xmlns:xsd=">
<globalConfiguration>
<parameter name="disablePrettyXML" value="false" />
</globalConfiguration>
<transport name="http"
pivot="java:org.apache.axis.transport.http.HTTPSender" />
<transport name="local"
pivot="java:org.apache.axis.transport.local.LocalSender" />
<transport name="java"
pivot="java:org.apache.axis.transport.java.JavaSender" />
<typeMapping
encodingStyle="
languageSpecificType="java:java.util.Date"
qname="xsd:dateTime" classname="java.util.Date"
serializer="lab.serviceclient.mis.CustomizedCalendarSerializerFactory"
deserializer="lab.serviceclient.mis.CustomizedCalendarDeserializerFactory" />
<typeMapping
encodingStyle="
languageSpecificType="java:java.util.Calendar"
qname="xsd:dateTime" classname="java.util.Calendar"
serializer="lab.serviceclient.mis.CustomizedCalendarSerializerFactory"
deserializer="lab.serviceclient.mis.CustomizedCalendarDeserializerFactory" />
</deployment>