2025-05-24

引言:

    最近比較搓,忙得沒空寫寫博客,回想一下又好像沒忙什麼事。得反省一下瞭,當然此是後話。

    本文就Zeroc Ice方法返回復雜類的對象(return by-value, not by-reff),做以簡單說明。之所以有這篇文章,隻因筆者發現網上流傳的中文文章中有這麼個空白,英文的也沒個直接的說明。

    此文用BBCode編寫。

內容:

    一、ICE方法返回對象的實現
    二、機制的簡要說明
    三、一個Exception的解決
    四、資源信息

正文:

一、ICE方法返回對象的實現。

    1,模型設計。

 

    如上圖“class_diagram.JPG”所示,Bond(債券)為JavaBean,MainOperator(主操作者)有一個方法“Bond getBean(String beanUID)”返回一個JavaBean。

    2,具體實現。(各代碼所在文件名,請參看首註釋中的“file”註釋)

    A)slice定義

Java代碼 
/*
 * file:    BondDef.ice
 * by:      zhaoningbo
 * date:    2011-07-25 15:51
 */ 
#ifndef BEAN_BOND_DEF 
#define BEAN_BOND_DEF 
module com{ 
    module number{ 
        module bean{ 
             
            // 債券Bean 
            class Bond{ 
             
                // Files 
                string bName; 
                string bCode; 
                 
                // Methods 
                string getbName(); 
                void setbName(string bName); 
                 
                string getbCode(); 
                void setbCode(string bCode); 
                 
            }; 
         
        }; 
    }; 
}; 
#endif 


Java代碼 
/*
 * file:    MainOperatorDef.ice
 * by:      zhaoningbo
 * date:    2011-07-25 16:02
 */ 
#ifndef OPERATOR_MAINOPERATOR_DEF 
#define OPERATOR_MAINOPERATOR_DEF 
module com{ 
    module number{ 
     
        // 預定義 
        module bean{ 
            class Bond; 
        }; 
     
        module operator{ 
         
            // 總執行者 
            interface MainOperator{ 
                // 獲取Bond對象 
                idempotent com::number::bean::Bond getBean(string beanUID); 
            }; 
        }; 
    }; 
}; 
#endif 


    B)slice2java生成ice的java接口類集

    C)編寫服務方

    (i)實現Bond。因為Bond是個抽象類,需要給定一個實現BondI。

Java代碼 
/*
 * file:    BondI.java
 */ 
package com.number.bond; 
 
import java.io.Serializable; 
import Ice.Current; 
import com.number.bean.Bond; 
 
/**
 * 自定義債券Bean
 * 註:實現Bond時,直接實現Override即可,無需添加其他的類元素。
 * @author zhnb
 *
 */ 
public class BondI extends Bond implements Serializable { 
     
    private static final long serialVersionUID = 8758902536680272427L; 
     
    @Override 
    public String getbCode(Current current) { 
        return this.bCode; 
    } 
 
    @Override 
    public String getbName(Current current) { 
        return this.bName; 
    } 
 
    @Override 
    public void setbCode(String bCode, Current current) { 
        this.bCode = bCode; 
    } 
 
    @Override 
    public void setbName(String bName, Current current) { 
        this.bName = bName; 
    } 
 


    (ii)加個dao層數據提供者(僅圖好看)

Java代碼 
/*
 * file:    BondLCData.java
 */ 
package com.number.dao; 
 
import java.io.Serializable; 
import com.number.bond.BondI; 
 
/**
 * 數據提供類
 * @author zhnb
 *
 */ 
public class BondLCData implements Serializable { 
 
    private static final long serialVersionUID = -5413237344986060553L; 
 
    // 單值 
    public static BondI BONDLC_DATA_SINGLE = null; 
    static{ 
        BondI bondI= new BondI(); 
        bondI.setbCode("600006"); 
        bondI.setbName("青島啤酒"); 
         
        BONDLC_DATA_SINGLE = bondI; 
    } 
     


    (iii)實現操作者業務類

Java代碼 
/*
 * file:    MainOperatorI.java
 */ 
package com.number.operator; 
 
import java.io.Serializable; 
import Ice.Current; 
import com.number.bean.Bond; 
import com.number.dao.BondLCData; 
 
/**
 * 主操作業務類
 * @author zhnb
 *
 */ 
public class MainOperatorI extends _MainOperatorDisp implements Serializable { 
 
    private static final long serialVersionUID = 1017768576442347413L; 
 
    @Override 
    public Bond getBean(String beanUID, Current current) { 
         
        // 獲取一個BondLC對象 
        Bond bond = BondLCData.BONDLC_DATA_SINGLE; 
         
        return bond; 
    } 
 


    (ix)發佈業務類,註冊到服務

Java代碼 
/*
 * file:    MainOperatorServer.java
 */ 
package com.number.operator; 
 
import java.io.Serializable; 
import Ice.ObjectAdapter; 
 
/**
 * 主操作服務發佈者
 * @author zhnb
 *
 */ 
public class MainOperatorServer implements Serializable { 
 
    private static final long serialVersionUID = -691557224337330222L; 
 
    public static void main(String[] args) { 
 
        // 0, 聲明執行狀態 
        int status = 0; 
        Ice.Communicator ic = null; 
 
        try { 
 
            // 1, 初始化環境 
            // 加載屬性文件 
            ic = Ice.Util.initialize(); 
 
            // 2, 初始化Adapter 
 
            String name = "MainOperatorServer"; 
            String endpoints = "default -h 127.0.0.1 -p 9999"; 
            ObjectAdapter objAdapter = ic.createObjectAdapterWithEndpoints( 
                    name, endpoints); 
 
            // 3, 創建伺服者 
            Ice.Object servant = new MainOperatorI(); 
 
            // 4, 添加伺服者至適配器 
            objAdapter.add(servant, Ice.Util.stringToIdentity("MainOperatorUID")); 
 
            // 5, 激活 
            objAdapter.activate(); 
 
System.out.println("<<MainOperatorUID started>>"); 
            // 6, 等待關閉 
            ic.waitForShutdown(); 
 
        } catch (Exception e) { 
            e.printStackTrace(); 
            status = 1; 
        } finally { 
            if (ic != null) { 
                ic.destroy(); 
            } 
            System.exit(status); 
        } 
 
    } 


    以上類中MainOperatorI主是個普通接口的實現方式,很簡單。BondI是個類的實現方式,需要留意。

    D)編寫客戶方

    (i)編寫請求者

Java代碼 
/*
 * file:    MainOperatorClient.java
 */ 
package com.number.operator; 
 
import java.io.Serializable; 
import Ice.ObjectPrx; 
import com.number.bean.Bond; 
import com.number.bond.ObjectFactory4Bond; 
import com.number.except.UGenericException; 
 
/**
 * 請求數據者(通用接口方式)
 * @author zhnb
 *
 */ 
public class MainOperatorClient implements Serializable { 
 
    private static final long serialVersionUID = -3207025201067021445L; 
 
    /**
     * 獲取債券對象
     * @param bondUID   債券標志
     * @return
     */ 
    public Bond getBean(String bondUID){ 
         
        Bond bond = null; 
         
        try { 
            // 獲取代理 
            MainOperatorPrx mainOperatorPrx = this.getOwnPrx(); 
 
/*
            // 添加自定義類
            Ice.ObjectFactory factory = new ObjectFactory4Bond();
            this.ic.addObjectFactory(factory, com.number.bond.BondI.ice_staticId());
 */             
            bond = mainOperatorPrx.getBean("anyThingAsArg"); 
             
        } catch (UGenericException e) { 
            e.printStackTrace(); 
        } 
         
        return bond; 
    } 
     
    // =========以<下>為私有方法,提供ICE支撐。========= 
    // 獲取服務端提供的代理 
    private MainOperatorPrx mainOperatorPrx = null; 
 
    // Ice通訊員(為回收資源時,方便自動回收) 
    private Ice.Communicator ic = null; 
 
    // GC回收時,自動銷毀Ice.Communicator。 
    @Override 
    protected void finalize() throws Throwable { 
        if (this.ic != null) { 
            ic.destroy(); 
        } 
        super.finalize(); 
    } 
 
    /**
     * 獲取代理
     * 
     * @return 本類的代理
     */ 
    private MainOperatorPrx getOwnPrx() throws UGenericException { 
 
        // 代理為空時,自動獲取代理。 
        if (this.mainOperatorPrx == null) { 
            // 環境為空時,初始化環境 
            if (this.ic == null) { 
                // 1, 初始化環境 
                ic = Ice.Util.initialize(); 
            } 
            // 2, 創建代理基類對象 
            String str = "MainOperatorUID:default -h 127.0.0.1 -p 9999"; 
             
            ObjectPrx objPrx = this.ic.stringToProxy(str); 
            // 3, 獲取代理 
            this.mainOperatorPrx = MainOperatorPrxHelper.checkedCast(objPrx); 
 
            // 4, 測試是否可用,不可用時拋出異常。 
            if (this.mainOperatorPrx == null) { 
                throw new UGenericException(str + ", request proxy faild."); 
            } 
        } 
        return this.mainOperatorPrx; 
    } 
    // =========以<上>為私有方法,提供ICE支撐。========= 


    (ii)為客戶端寫個手工測試類

Java代碼 
/*
 * file:    StartAllClient.java
 */ 
package com.number.start; 
 
import java.io.Serializable; 
import com.number.bean.Bond; 
import com.number.operator.MainOperatorClient; 
 
/**
 * 啟動使用者
 * @author zhnb
 *
 */ 
public class StartAllClient implements Serializable { 
 
    private static final long serialVersionUID = -6282697303788648813L; 
 
    public static void main(String[] args) { 
 
        MainOperatorClient moc = new MainOperatorClient(); 
        Bond bond = moc.getBean("something"); 
         
        StringBuffer info = new StringBuffer(); 
        if (bond == null) { 
            info.append("null"); 
        } else { 
            info.append("Bond@" + bond.hashCode() + ":"); 
             
            info.append("bName=" + bond.bName); 
            info.append(",bCode=" + bond.bCode); 
             
            info.append(":"); 
            info.append("bName=" + bond.getbName()); 
            info.append(",bCode=" + bond.getbCode()); 
        } 
         
        System.out.println(info.toString()); 
        System.exit(0); 
    } 
 


    OK,看樣子寫完瞭,可以跑瞭吧。試個……(提交一下,我去瞅個行號~。=)

念叨著,“先啟服務run 'MainOperatorServer'……再啟客戶run 'StartAllClient'”……

    哦~&*……*出錯瞭!

Java代碼 
Exception in thread "main" Ice.NoObjectFactoryException 
    reason = "" 
    type = "::com::number::bean::Bond" 
    at IceInternal.BasicStream.readObject(BasicStream.java:1444) 


    Why? ?? !? 不是一直這麼個寫法嘛?!

    ——如果是這麼個寫法,我也就不花功夫寫這篇文章瞭。

二、機制的簡要說明

   返回值有兩種方式,一種是Ice最喜歡(也是最推薦的)“引用”方式,另一種是“傳值”方式。在ICE中的含意如下:

   “引用”,即客戶端不會拿到類型實體的副本,隻拿到一個代理,可以抽象成一個遠程指針(C系)或者一個對象引用(J系)。
   “傳值”,跟“引用”相對,即拿到類型實體的副本。
   (此處略去二者特點,即使用范圍,約一千字。)

    因此傳接口的時候,就類似於“遠程過程調用”的感覺,屬於“行為”性。可抽象成一系列的接口,實現C-S間的規范協議。而傳值時,有“序列反序列”的味道,屬於“實體”性。需要傳方有個打包成序列的模板,收方有個解包成對象的模板。回觀上文報錯,釋然瞭。

三、一個Exception的解決

    一個Exception指的是“NoObjectFactoryException”,無對象工廠異常。當客戶方拿到一箱Bond的零件後,他找不到工廠給的對象裝配圖。傻眼瞭的意思。

   人工建圖。沒有拿到模型,但是知道有個“Bond.java”抽象的不能使,那就直接實現一個最基礎的吧。造個BondI當臨時模板使著吧,先!

Java代碼 
/*
 * file:    BondI.java
 */ 
package com.number.bond; 
 
import java.io.Serializable; 
import Ice.Current; 
import com.number.bean.Bond; 
 
/**
 * 自定義債券Bean(LC, 本地類)
 * @author zhnb
 *
 */ 
public class BondI extends Bond implements Serializable { 
     
    private static final long serialVersionUID = 8758902536680272427L; 
     
    // Methods 
    @Override 
    public String getbCode(Current current) { 
        return this.bCode; 
    } 
 
    @Override 
    public String getbName(Current current) { 
        return this.bName; 
    } 
 
    @Override 
    public void setbCode(String bCode, Current current) { 
        this.bCode = bCode; 
    } 
 
    @Override 
    public void setbName(String bName, Current current) { 
        this.bName = bName; 
    } 
 


    建好瞭,怎麼告訴裝配工呢。ICE的裝配工,會看已有的圖紙,也會手機上網去ObjectFactory試著查還沒裝到自己包裡的圖紙。那我們就把裝配圖傳到ObjectFactory上去吧!

    (i)創建一個ObjectFactory規范下的裝配圖

Java代碼 
/*
 * file:    ObjectFactory4Bond.java
 */ 
package com.number.bond; 
 
import Ice.Object; 
import Ice.ObjectFactory; 
 
/**
 * 傳值方式,必須實現一個自定義類工廠。
 * @author zhnb
 *
 */ 
public class ObjectFactory4Bond implements ObjectFactory { 
 
    @Override 
    public Object create(String type) { 
        System.out.println("!!>type=" + type); 
        if (type.equals(com.number.bond.BondI.ice_staticId())) { 
            return new BondI(); 
        } 
        return null; 
    } 
 
    @Override 
    public void destroy() { 
        // TODO Auto-generated method stub 
 
    } 
 


    (ii)拿到這箱Bond前,把裝配圖傳到ObjectFactory上去。

    定位: 正文 | 一、ICE方法返回對象的實現 | 2,具體實現。| D)編寫客戶方
    找到:“MainOperatorClient.java”第34~38行,把註釋部分放出來。

    註釋掉的這兩行代碼,將裝配圖“BondI”放到ObjectFactory上去。以備裝配工查看。

    (iii)再次運行,通過。顯示如下

Java代碼 
!!>type=::com::number::bean::Bond 
Bond@12830537:bName=青島啤酒,bCode=600006:bName=青島啤酒,bCode=600006 


四、資源信息

    你可以在code google上下載到此demo的源代碼,隻需熱身一下你的SVN。

Java代碼 
svn checkout http://number-icedemo-base.googlecode.com/svn/trunk/ number-icedemo-base-read-only 


補充:

    有未說明清楚的問題,歡迎尾隨追貼。~,=

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *