一個簡單的CORBA例子


因為對CORBA分析的需要,這里寫一個簡單的CORBA例子。從JDK1.2開始,JDK中集成了ORB的實現,本例子使用了JDK1.7,對於JDK1.2+應該都沒有問題。這個例子實現一個簡單的加減乘除的功能的計算器,客戶端將參數和請求的方法名傳送到服務端,服務端處理這個請求並將結果返回給客戶端。

我們知道不同編程語言中的類型的表達,內存模型是不一樣的,為此CORBA發明了一套中間描述語言IDL,不同語言平台的ORB實現負責將IDL中的類型映射到本地類型中。因此IDL是我們編寫CORBA程序的出發點,首先,我們用IDL來描述我們的接口/對象:

 1 module com{
 2     module bes{
 3         module corba{
 4             module test{
 5                 interface Calc{
 6                     void add(in long a,in long b,out long c);
 7                     void sub(in long a,in long b,out long c);
 8                     void multi(in long a,in long b,out long c);
 9                     void div(in long a,in long b,out long c);
11                 };
12             };
13         };
14     };
15 };

當然接口Calc中的方法的返回值不一定為void,這里將返回值放到了out類型的參數c中,方法可以帶有多個out類型的參數。然后我們用idlj工具(jdk自帶)將Calc.idl轉換為對應java的描述,並生成Stub和POA等類:

idlj給我們生成很多文件,首先我們來看一下UML圖:

上面的圖不涉及工具類CalcHelper和CalcHolder,這兩個類的作用在后而闡述。

package com.bes.corba.test;


/**
* com/bes/corba/test/_CalcStub.java .
* 由IDL-to-Java 編譯器 (可移植), 版本 "3.2"生成
* 從Hello.idl
* 2016年2月15日 星期一 下午09時08分34秒 CST
*/

public class _CalcStub extends org.omg.CORBA.portable.ObjectImpl implements com.bes.corba.test.Calc
{

  public void add (int a, int b, org.omg.CORBA.IntHolder c)
  {
            org.omg.CORBA.portable.InputStream $in = null;
            try {
                org.omg.CORBA.portable.OutputStream $out = _request ("add", true);
                $out.write_long (a);
                $out.write_long (b);
                $in = _invoke ($out);
                c.value = $in.read_long ();
                return;
            } catch (org.omg.CORBA.portable.ApplicationException $ex) {
                $in = $ex.getInputStream ();
                String _id = $ex.getId ();
                throw new org.omg.CORBA.MARSHAL (_id);
            } catch (org.omg.CORBA.portable.RemarshalException $rm) {
                add (a, b, c        );
            } finally {
                _releaseReply ($in);
            }
  } // add

  public void sub (int a, int b, org.omg.CORBA.IntHolder c)
  {
            org.omg.CORBA.portable.InputStream $in = null;
            try {
                org.omg.CORBA.portable.OutputStream $out = _request ("sub", true);
                $out.write_long (a);
                $out.write_long (b);
                $in = _invoke ($out);
                c.value = $in.read_long ();
                return;
            } catch (org.omg.CORBA.portable.ApplicationException $ex) {
                $in = $ex.getInputStream ();
                String _id = $ex.getId ();
                throw new org.omg.CORBA.MARSHAL (_id);
            } catch (org.omg.CORBA.portable.RemarshalException $rm) {
                sub (a, b, c        );
            } finally {
                _releaseReply ($in);
            }
  } // sub

  public void multi (int a, int b, org.omg.CORBA.IntHolder c)
  {
            org.omg.CORBA.portable.InputStream $in = null;
            try {
                org.omg.CORBA.portable.OutputStream $out = _request ("multi", true);
                $out.write_long (a);
                $out.write_long (b);
                $in = _invoke ($out);
                c.value = $in.read_long ();
                return;
            } catch (org.omg.CORBA.portable.ApplicationException $ex) {
                $in = $ex.getInputStream ();
                String _id = $ex.getId ();
                throw new org.omg.CORBA.MARSHAL (_id);
            } catch (org.omg.CORBA.portable.RemarshalException $rm) {
                multi (a, b, c        );
            } finally {
                _releaseReply ($in);
            }
  } // multi

  public void div (int a, int b, org.omg.CORBA.IntHolder c)
  {
            org.omg.CORBA.portable.InputStream $in = null;
            try {
                org.omg.CORBA.portable.OutputStream $out = _request ("div", true);
                $out.write_long (a);
                $out.write_long (b);
                $in = _invoke ($out);
                c.value = $in.read_long ();
                return;
            } catch (org.omg.CORBA.portable.ApplicationException $ex) {
                $in = $ex.getInputStream ();
                String _id = $ex.getId ();
                throw new org.omg.CORBA.MARSHAL (_id);
            } catch (org.omg.CORBA.portable.RemarshalException $rm) {
                div (a, b, c        );
            } finally {
                _releaseReply ($in);
            }
  } // div

  // Type-specific CORBA::Object operations
  private static String[] __ids = {
    "IDL:com/bes/corba/test/Calc:1.0"};

  public String[] _ids ()
  {
    return (String[])__ids.clone ();
  }

  private void readObject (java.io.ObjectInputStream s) throws java.io.IOException
  {
     String str = s.readUTF ();
     String[] args = null;
     java.util.Properties props = null;
     org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init (args, props);
   try {
     org.omg.CORBA.Object obj = orb.string_to_object (str);
     org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl) obj)._get_delegate ();
     _set_delegate (delegate);
   } finally {
     orb.destroy() ;
   }
  }

  private void writeObject (java.io.ObjectOutputStream s) throws java.io.IOException
  {
     String[] args = null;
     java.util.Properties props = null;
     org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init (args, props);
   try {
     String str = orb.object_to_string (this);
     s.writeUTF (str);
   } finally {
     orb.destroy() ;
   }
  }
} // class _CalcStub

_CalcStub是存根類,有過遠程調用編程經驗的讀者應該對這個詞比較熟悉,它是遠程對象在本地的一個代理(Proxy)。從業務划分角度來說,CORBA這些底層的東西不應該太多地污染到我們的應用,比如這個_CalcStub是我們不希望在業務代碼中出現的,我們只需要看到我們需要的Calc。客戶端從ORB中拿到的Calc接口實現其實是一個_CalcStub,客戶程序對Calc接口中的方法進行調用時,_CalcStub將相應方法的調用轉發到服務端,然后將服務器的響應返回給客戶端,從而成功從欺騙客戶端程序。

_CalcStub繼承了ObjectImpl類,這使得_CalcStub能夠關聯到ORB環境中,從而完成遠程調用。

idlj工具並沒有直接在Calc.java中定義idlj方法的java語言描述,而是在CalcOperation.java中。Calc接口繼承了IDLEntity,org.omg.CORBA.Object和CalcOperation三個接口:

IDLEntity是一個標記接口,表明Calc接口是一種IDL接口,這個與org.omg.CORBA.Object有點相類似,但實現了org.omg.CORBA.Object接口的對象不一定是IDL描述的,因此這里單獨把IDLEntity拎出來。

org.omg.CORBA.Object接口定義了一些CORBA相關的方法,因為客戶端所使用的是Calc,參數傳遞到ORB中語義上也是 Calc類型,當然我們不能將非IDL的對象傳遞到ORB中,ORB無法完成那樣子的操作;Calc接口繼承 org.omg.CORBA.Object(ORB層面使用的是org.omg.CORBA.Object),那就意味了通過編譯器來保證類型安全(避免 強制轉換,ClassCastException之類的異常)。

package com.bes.corba.test;


/**
* com/bes/corba/test/CalcOperations.java .
* 由IDL-to-Java 編譯器 (可移植), 版本 "3.2"生成
* 從Hello.idl
* 2016年2月15日 星期一 下午09時08分34秒 CST
*/

public interface CalcOperations 
{
  void add (int a, int b, org.omg.CORBA.IntHolder c);
  void sub (int a, int b, org.omg.CORBA.IntHolder c);
  void multi (int a, int b, org.omg.CORBA.IntHolder c);
  void div (int a, int b, org.omg.CORBA.IntHolder c);
} // interface CalcOperations

CalcOperation是相對應的IDL映射。

package com.bes.corba.test;


/**
* com/bes/corba/test/CalcPOA.java .
* 由IDL-to-Java 編譯器 (可移植), 版本 "3.2"生成
* 從Hello.idl
* 2016年2月15日 星期一 下午09時08分34秒 CST
*/

public abstract class CalcPOA extends org.omg.PortableServer.Servant
 implements com.bes.corba.test.CalcOperations, org.omg.CORBA.portable.InvokeHandler
{

  // Constructors

  private static java.util.Hashtable _methods = new java.util.Hashtable ();
  static
  {
    _methods.put ("add", new java.lang.Integer (0));
    _methods.put ("sub", new java.lang.Integer (1));
    _methods.put ("multi", new java.lang.Integer (2));
    _methods.put ("div", new java.lang.Integer (3));
  }

  public org.omg.CORBA.portable.OutputStream _invoke (String $method,
                                org.omg.CORBA.portable.InputStream in,
                                org.omg.CORBA.portable.ResponseHandler $rh)
  {
    org.omg.CORBA.portable.OutputStream out = null;
    java.lang.Integer __method = (java.lang.Integer)_methods.get ($method);
    if (__method == null)
      throw new org.omg.CORBA.BAD_OPERATION (0, org.omg.CORBA.CompletionStatus.COMPLETED_MAYBE);

    switch (__method.intValue ())
    {
       case 0:  // com/bes/corba/test/Calc/add
       {
         int a = in.read_long ();
         int b = in.read_long ();
         org.omg.CORBA.IntHolder c = new org.omg.CORBA.IntHolder ();
         this.add (a, b, c);
         out = $rh.createReply();
         out.write_long (c.value);
         break;
       }

       case 1:  // com/bes/corba/test/Calc/sub
       {
         int a = in.read_long ();
         int b = in.read_long ();
         org.omg.CORBA.IntHolder c = new org.omg.CORBA.IntHolder ();
         this.sub (a, b, c);
         out = $rh.createReply();
         out.write_long (c.value);
         break;
       }

       case 2:  // com/bes/corba/test/Calc/multi
       {
         int a = in.read_long ();
         int b = in.read_long ();
         org.omg.CORBA.IntHolder c = new org.omg.CORBA.IntHolder ();
         this.multi (a, b, c);
         out = $rh.createReply();
         out.write_long (c.value);
         break;
       }

       case 3:  // com/bes/corba/test/Calc/div
       {
         int a = in.read_long ();
         int b = in.read_long ();
         org.omg.CORBA.IntHolder c = new org.omg.CORBA.IntHolder ();
         this.div (a, b, c);
         out = $rh.createReply();
         out.write_long (c.value);
         break;
       }

       default:
         throw new org.omg.CORBA.BAD_OPERATION (0, org.omg.CORBA.CompletionStatus.COMPLETED_MAYBE);
    }

    return out;
  } // _invoke

  // Type-specific CORBA::Object operations
  private static String[] __ids = {
    "IDL:com/bes/corba/test/Calc:1.0"};

  public String[] _all_interfaces (org.omg.PortableServer.POA poa, byte[] objectId)
  {
    return (String[])__ids.clone ();
  }

  public Calc _this() 
  {
    return CalcHelper.narrow(
    super._this_object());
  }

  public Calc _this(org.omg.CORBA.ORB orb) 
  {
    return CalcHelper.narrow(
    super._this_object(orb));
  }


} // class CalcPOA

CalcPOA:它工作在服務端,POA是Portable Object Adapter的縮寫,這里Adapter(適配器)的語義是指適配相應的編程語言中的對象(比如在java中那意思就是指 Java對象的適配器),適配器的作用有三點:

1:接受客戶端發過來的調用請求,反序列化(Unmarshalling)參數,方法名等,然后將請求分發給對應的Servant。POA和Servant之間的關系如下圖。

2:將對象引用(Object Reference)和相應的Servant起來(可以看到Servant._object_id方法),比如我們在EJB中的有狀態會話Bean。

3:負責Servant的生命周期管理(如創建,鈍化,銷毀等),這里又讓我聯想到了EJB的生命周期。到這里這里我們可以清楚Home接口存在的理由。

好吧,我們要實現的例子確實很簡單,在這個例子中,讀者只需要了解到第一點即可(CORBA水很深,很容易死里面去的)。idlj為我們生成的POA中,集Servant,CalcOperation和InvocationHandler於一身,有越殂代皰的嫌疑,當然這並不影響程序的正常執行,當然如果服務端比較關注2,3兩點的話,自己實現POA還是很有必要的,但這已經超出了本文的范圍。

同樣地ORB的東西不應該玷污到我們服務端的業務邏輯 ,Servant和InvocationHandler將POA關聯到ORB中去。注意Servant和InvocationHandler是兩接口是分開的,這一點還不是太清楚(也許是為了特性的划分吧)。

 

package com.bes.corba.test;


/**
* com/bes/corba/test/CalcHelper.java .
* 由IDL-to-Java 編譯器 (可移植), 版本 "3.2"生成
* 從Hello.idl
* 2016年2月15日 星期一 下午09時08分34秒 CST
*/

abstract public class CalcHelper
{
  private static String  _id = "IDL:com/bes/corba/test/Calc:1.0";

  public static void insert (org.omg.CORBA.Any a, com.bes.corba.test.Calc that)
  {
    org.omg.CORBA.portable.OutputStream out = a.create_output_stream ();
    a.type (type ());
    write (out, that);
    a.read_value (out.create_input_stream (), type ());
  }

  public static com.bes.corba.test.Calc extract (org.omg.CORBA.Any a)
  {
    return read (a.create_input_stream ());
  }

  private static org.omg.CORBA.TypeCode __typeCode = null;
  synchronized public static org.omg.CORBA.TypeCode type ()
  {
    if (__typeCode == null)
    {
      __typeCode = org.omg.CORBA.ORB.init ().create_interface_tc (com.bes.corba.test.CalcHelper.id (), "Calc");
    }
    return __typeCode;
  }

  public static String id ()
  {
    return _id;
  }

  public static com.bes.corba.test.Calc read (org.omg.CORBA.portable.InputStream istream)
  {
    return narrow (istream.read_Object (_CalcStub.class));
  }

  public static void write (org.omg.CORBA.portable.OutputStream ostream, com.bes.corba.test.Calc value)
  {
    ostream.write_Object ((org.omg.CORBA.Object) value);
  }

  public static com.bes.corba.test.Calc narrow (org.omg.CORBA.Object obj)
  {
    if (obj == null)
      return null;
    else if (obj instanceof com.bes.corba.test.Calc)
      return (com.bes.corba.test.Calc)obj;
    else if (!obj._is_a (id ()))
      throw new org.omg.CORBA.BAD_PARAM ();
    else
    {
      org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate ();
      com.bes.corba.test._CalcStub stub = new com.bes.corba.test._CalcStub ();
      stub._set_delegate(delegate);
      return stub;
    }
  }

  public static com.bes.corba.test.Calc unchecked_narrow (org.omg.CORBA.Object obj)
  {
    if (obj == null)
      return null;
    else if (obj instanceof com.bes.corba.test.Calc)
      return (com.bes.corba.test.Calc)obj;
    else
    {
      org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate ();
      com.bes.corba.test._CalcStub stub = new com.bes.corba.test._CalcStub ();
      stub._set_delegate(delegate);
      return stub;
    }
  }

}

CalcHelper:從名字上可以看出,它是一個工具類,它的職責在有:

1、通過提供narrow方法和RepositoryId等信息來實現類型安全。

2、與Any類型之間的進行轉換。

3、從InputStream中讀出Calc(對象引用),將Calc寫出到OutputStream中。

我們的例子中將只用到第一點。

 

package com.bes.corba.test;

/**
* com/bes/corba/test/CalcHolder.java .
* 由IDL-to-Java 編譯器 (可移植), 版本 "3.2"生成
* 從Hello.idl
* 2016年2月15日 星期一 下午09時08分34秒 CST
*/

public final class CalcHolder implements org.omg.CORBA.portable.Streamable
{
  public com.bes.corba.test.Calc value = null;

  public CalcHolder ()
  {
  }

  public CalcHolder (com.bes.corba.test.Calc initialValue)
  {
    value = initialValue;
  }

  public void _read (org.omg.CORBA.portable.InputStream i)
  {
    value = com.bes.corba.test.CalcHelper.read (i);
  }

  public void _write (org.omg.CORBA.portable.OutputStream o)
  {
    com.bes.corba.test.CalcHelper.write (o, value);
  }

  public org.omg.CORBA.TypeCode _type ()
  {
    return com.bes.corba.test.CalcHelper.type ();
  }

}

CalcHolder:這個類在Calc被作為out或者inout類型的參數傳遞時候被使用,如果我們在另一個IDL方法中使用Calc作為參數,那么生成的代碼將會是這樣子的:

void test(int a, int b, CalcHolder calc)

CalcHolder負責從InputStream或者OuputStream分別讀出和寫入Calc,從CalcHolder生成的代碼中我們可以看出,CalcHolder的_read和_write方法將相應的操作委托給了CalcHelper。

哆嗦了這么多,是時候拿出我們的客戶端和服務端了。

服務端代碼:

CalculatorImpl是Servant的實現,它繼承了CalcPOA類。

package com.bes.corba.impl;

import org.omg.CORBA.IntHolder;

import com.bes.corba.test.CalcPOA;

public class CalculatorImpl extends CalcPOA{

    @Override
    public void add(int a, int b, IntHolder c) {
        c.value=a+b;
    }

    @Override
    public void sub(int a, int b, IntHolder c) {
        c.value=a-b;
    }

    @Override
    public void multi(int a, int b, IntHolder c) {
        c.value=a*b;
    }

    @Override
    public void div(int a, int b, IntHolder c) {
        c.value=a/b;
    }
}

Server類:

package com.bes.corba.test;

import org.omg.CORBA.ORB;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;

import com.bes.corba.test.Calc;
import com.bes.corba.test.CalcHelper;
import com.bes.corba.impl.CalculatorImpl;

public class Server {
    public static void main(String[] args) throws Exception{
        /*
         * ORB 初始化。
         */
        ORB orb=ORB.init(args,null);
        
        /*
         * 獲取根POA並初始化。
         */
        POA rootPoa=POAHelper.narrow(orb.resolve_initial_references("RootPOA") );
        rootPoa.the_POAManager().activate();
        
        /*
         * 構建一個CalculatorImpl。
         */
        CalculatorImpl calculatorImpl=new CalculatorImpl();
        
        /*
         * 將Servant注冊到RootPOA中,建立Servant到Object Reference的相互映射,
         * 注意這里具體行為跟RootPOA的POA Policy有關。
         */
        org.omg.CORBA.Object ref=rootPoa.servant_to_reference(calculatorImpl);
        Calc iref=CalcHelper.narrow(ref);
        
        /*
         * 獲取命名服務。
         */
        org.omg.CORBA.Object objRef=orb.resolve_initial_references("NameService");
        NamingContextExt ncRef= NamingContextExtHelper.narrow(objRef);
        
        /*
         * 將對象引用以相應的名字發布到命名服務中。
         */
        String name="Calc";
        NameComponent path[] = ncRef.to_name(name);
        ncRef.rebind(path,iref);
        
        System.out.println("Calculator server ready...");
        
        /*
         * 阻塞直到ORB關閉。
         */
        orb.run();
    }
}

客戶端代碼Client

package com.bes.corba.test;

import org.omg.CORBA.IntHolder;
import org.omg.CORBA.ORB;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;

import com.bes.corba.test.Calc;
import com.bes.corba.test.CalcHelper;

public class Client {
    public static void main(String[] args) throws Exception{
        /*
         * ORB 初始化。
         */
        ORB orb=ORB.init(args,null);
        
        /*
         * 獲取命名服務。
         */
        org.omg.CORBA.Object objRef=orb.resolve_initial_references("NameService");
        NamingContextExt ncRef=NamingContextExtHelper.narrow(objRef);
        
        /*
         * 從命名服務中查找相應的對象引用,並進行類型轉型。
         */
        
        String name="Calc";
        Calc calc=CalcHelper.narrow(ncRef.resolve_str(name));
        
        /*
         * 調用對象的方法。
         */
        IntHolder result=new IntHolder();
        
        calc.add(1,2,result);
        System.out.printf("1+2=%d\n",result.value);
    }
}

啟動命名服務,命名服務器不一定運行在對象服務器的進程中,尤其是在一個分布式的環境中,命名服務器與對象服務器通常是一對多的關系。

orbd -ORBInitialPort 1050 -ORBInitialHost localhost&

 啟動服務端:

java HelloServer -ORBInitialPort 1050 -ORBInitialHost localhost

ORBInitialPort和ORBInitialHost參數指定了命名服務器的主機名和端口號。

啟動客戶端:

java com.bes.corba.test.Client -ORBInitialPort 1050 -ORBInitialHost localhost

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM