隨筆 - 42  文章 - 71  trackbacks - 0
          <2008年7月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          常用鏈接

          留言簿

          隨筆檔案

          文章分類

          文章檔案

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          AXIS 1.4 自定義序列化/反序列化類

          Technorati 標簽: axis,customized,serializer,deserializer,web service

          現在在SOA被大肆鼓吹的時代,再加上確實企業級應用平臺中不同系統間的整合越來越多,所以Web Service的地位日益上升。雖然效率上會有所折扣,但是畢竟是一個標準,而且主流的編程語言本身或者加上一些框架都支持Web Service,無論是Server端還是Client端。記得在2001年底就做過一個Java Web Service Client調用Delphi Web Service Server,但是現在后悔的是當時對于Web Service沒有認真學習,只是停留在淺嘗輒止的地步,正是應了古人的那句:“書到用時方覺少”啊。

          現在的項目就是遇到這個情況,大量的跨系統的通訊都是通過Web Service。偏偏有些服務端編寫的比較奇怪,而AXIS生成的客戶端又更加的奇怪。遇到的問題是這樣的,服務端返回的數據中,有幾個類型為xsd:dateTime,AXIS映射到Java的類是java.util.Calendar。偏偏服務器有時候有的字段是不會帶有任何數據的,有的字段是一個字符"T"(因為Web Service中傳輸dateTime類型的數據字符串格式為yyyy-MM-dd'T'hh:mm:ss.SSS,例如2008-07-24T23:49:15.000),也就是應該生成對應Java的null對象。可能是考慮到和.NET客戶端的兼容性吧(.NET不熟悉,但是為了解決這個問題查看一些資料,都是說對于dateTime類型,如果是空,會在.NET的客戶端出現問題),AXIS在處理無數據的dateTime類型的節點時,就粗暴的報錯了。沒辦法,服務器端是沒有辦法改程序了,只好在客戶端下手了,反正這幾個字段對于我的客戶端不是很重要,只要不要因為這幾個特殊的數據影響了其它數據。

          第一個反應就是修改AXIS的代碼,但是這種方案是不到萬不得已是不能用的,太危險了。然后就看AXIS的文檔以及API,看到有typeMapping的配置項,但是對于其中的各個屬性又沒有詳細闡述,網上的例子大部分都是針對服務器端的。經過詢問其它同事,他們也遇到了類似的問題,他們定了自己的序列化/反序列化類,然后在AXIS產生的客戶端Stub代碼中以服務命名的方法,例如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 {
              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("    }

          這里,通過調用org.apache.axis.client.Call對象的registerTypeMapping方法來插入自定義的Serializer和Deserializer。(以上代碼感謝我不認識的那位同事無私的奉獻和幫助)

          但是這種方法有一個問題就是如果重新生成了客戶端代碼,需要在Stub類中插入,也是比較具有破壞性的。我就覺得總有一個方法是比較優雅的解決這個問題的,然后順著這個思路繼續往下找,發現可以在Service locator類,也即extends了org.apache.axis.client.Service的那個類來獲取到TypeMappingRegistery,于是,不再修改Stub類,在調用者代碼中,生成locator對象之后,調用注冊TypeMapping的方法:

          LabService locator = new LabServiceLocator();
                  TypeMappingRegistry tmr = locator.getTypeMappingRegistry();
                  TypeMapping tm = tmr.getDefaultTypeMapping();

                  tm.register(java.util.Date.class, new QName(
                                  "
                                  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(
                                          "
          通過測試,發現此方法可行,基本上比較好了,但是如果這么多的Service每次調用都干這么一件事,也是很麻煩的,而且,如果以后又有其它的自定義Serializer和Deserizlizer,還得修改代碼。于是乎繼續尋找更好的解決辦法。仔細閱讀AXIS的文檔,發現還有client-config.wsdd可用。在AXIS的代碼中,

          org/apache/axis/client/client-config.wsdd

          給出了一個樣本,但是是基本的配置,copy這個樣本到你的工程的source文件夾,不需要包即可,只要AXIS在運行的時候,能夠在classes目錄找到這個文件就可以。

          以下是配置文件及相關類的代碼:

          client-config.wsdd:(為了確保安全,把java.util.Date和java.util.Calendar都注冊了, 如果服務器端沒有強制指定encodingStyle,就把encodingStyle屬性設置為"",不知道為什么,改天抽時間再研究:P)

          ----------------------------------------------------------------------------------

          <deployment xmlns="
              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>

          ----------------------------------------------------------------------------------

          在client-config.wsdd樣本的基礎上修改的。

          languageSpecificType="java:java.util.Date"以及languageSpecificType="java:java.util.Calendar"表明映射到Java中哪種類型的數據要求使用自定義的Serializer和Deserializer。注意寫法,前面有name space "java",

          qname就是指返回的XML文件中的節點類型,對于dateTime類型的全稱就是xsd:dateTime,其中xsd=serializer和deserializer節點是指向你的自定義serializer和deserializer的工廠類,而不是serializer和deserializer類本身,這個要注意。

          由于不需要序列化的自定義,所以一開始我用的AXIS原有的CalendarSerializerFactory,但是發現有問題,參考CustomizedCalendarSerializerFactory中create方法的注釋不分。所以后來還是加上了自定義的Serializer,但是很簡單了(注意繼承的父類):

          CustomizedCalendarSerializer.java:

          ----------------------------------------------------------------------------------

          package lab.serviceclient.mis;

          import org.apache.axis.encoding.ser.CalendarSerializer;

          public class CustomizedCalendarSerializer extends CalendarSerializer {

              private static final long serialVersionUID = 1L;

          }

          ----------------------------------------------------------------------------------

          CustomizedCalendarSerializerFactory.java:

          ----------------------------------------------------------------------------------

          package lab.serviceclient.mis;

          import javax.xml.namespace.QName;

          import org.apache.axis.encoding.ser.BaseSerializerFactory;

          public class CustomizedCalendarSerializerFactory extends BaseSerializerFactory {

              private static final long serialVersionUID = 1L;

              public CustomizedCalendarSerializerFactory(Class javaType, QName xmlType) {
                  super(CustomizedCalendarSerializer.class, xmlType, javaType);
              }

              // 這個static的create方法是必須的。如果使用前面介紹的編程注冊TypeMapping的方式,就不需要這個create方法;如果是定義在client-config.wsdd文件中,

              //AXIS在初始化的時候,org.apache.axis.deployment.wsdd.WSDDDeployment.deployMapping方法會調用factory的create方法,如果沒有這個方法,就不能注冊成功

              // 對于Deserializer也是一樣的
              public static CustomizedCalendarSerializerFactory create(Class javaType, QName xmlType) {
                  return new CustomizedCalendarSerializerFactory(javaType, xmlType);
              }

          }

          ----------------------------------------------------------------------------------

          CustomizedCalendarDeserializer.java

          ----------------------------------------------------------------------------------

          package lab.serviceclient.mis;

          import javax.xml.namespace.QName;

          import org.apache.axis.encoding.ser.CalendarDeserializer;

          public class CustomizedCalendarDeserializer extends CalendarDeserializer {

              private static final long serialVersionUID = 1L;

              public CustomizedCalendarDeserializer(Class javaType, QName xmlType) {
                  super(javaType, xmlType);
              }
              public Object makeValue(String source) {
                  System.out.println("========= This is the Customized Calendar Deserializer ========="); //為了測試是否到達了自定義的類
                  if ( source == null || source.length() == 0 || "T".equals(source)) return null;
                  return super.makeValue(source);
              }
          }

          ----------------------------------------------------------------------------------

          CustomizedCalendarDeserializerFactory.java

          ----------------------------------------------------------------------------------

          package lab.serviceclient.mis;

          import javax.xml.namespace.QName;

          import org.apache.axis.encoding.ser.BaseDeserializerFactory;
          import org.apache.axis.encoding.ser.CalendarDeserializer;

          public class CustomizedCalendarDeserializerFactory extends BaseDeserializerFactory {
              private static final long serialVersionUID = 1L;

              public CustomizedCalendarDeserializerFactory(Class javaType, QName xmlType) {
                  super(CustomizedCalendarDeserializer.class, xmlType, javaType);  
              }
              public static CustomizedCalendarDeserializerFactory create(Class javaType, QName xmlType) {
                  return new CustomizedCalendarDeserializerFactory(javaType, xmlType);
              }
          }

          ----------------------------------------------------------------------------------

          有了這個方法,就可以不擔心服務器端返回奇怪的數據了。


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 体育| 双流县| 利津县| 京山县| 东方市| 贞丰县| 龙海市| 和田县| 木兰县| 仁布县| 民和| 安宁市| 台北市| 郑州市| 汉中市| 文化| 大厂| 涟水县| 台北市| 宜兰市| 淳化县| 华坪县| 吉首市| 绿春县| 乐昌市| 枞阳县| 萝北县| 南岸区| 安平县| 青河县| 额济纳旗| 临泽县| 梁平县| 宁夏| 当涂县| 寻乌县| 杂多县| 东海县| 会同县| 富宁县| 多伦县|