[ Java & CORBAページへ戻る ]

オブジェクト指向設計を行っていると、CORBAオブジェクトのオペレーション(メソッド)を呼び出す際に、そのオペレーションの戻り値や引数にオブジェクトを使用したくなります。ところが、CORBAでは、引数や戻り値に指定できるのは、CORBA基本型と、IDLで定義した型だけです。IDLでinterface定義した型を引数や戻り値に指定した場合は、CORBAオブジェクト参照情報が渡ります。しかし、オブジェクト参照ではなく、オブジェクト自体を「コピー」してそっくり渡したい場合があります。いってみれば、参照渡しに対する値渡し(Passing Object By Value)です。元々CORBAにはstruct(構造体)が定義されていますが、継承ができない、メソッドを持たない、といったマイナス点が目立ちます。そこでCORBA2.3から追加されたのがOBV(Object By Value)です。

なお、valuetypeで定義された型はCORBAオブジェクトではないので、リモートからそのメソッドを呼ぶことはできません。


問題

単純な例ですが、ある予定(計画)を管理するCORBAオブジェクトSchedulerを考えます。Schedulerオブジェクトはただ1つだけ予定を持っており、オペレーションgetPlan()が呼ばれると予定を返すものとします。予定には、開始時間と終了時間があります。また、時間は(時、分、秒)で表すものとします。この予定はCORBAオブジェクトではなく、受け取ったマシン側にローカルに持ちたいとします。

従来の解法

オペレーションの戻り値は1つしか使用できません。今回の問題のように、複数のデータを返却するには次の方法があります。

  1. CORBA基本型で時間を表し、それをgetPlanの引数にout指定して記述する。
    void getPlan(out long startHour, out long startMinute, out long startSecond,
                 out long endHour, out long endMinute, out long endSecond);
  2. CORBA構造体で予定を表し、それを戻り値に記述する。
    struct Plan
    {
        long startHour;
        long startMinute;
        long startSecond;
        long endHour;
        long endMinute;
        long endSecond;
    };
    :
    Plan getPlan();

1.の方法は、outパラメータを使うためコーディングが面倒になること、引数の数が多い(しかも皆同じ型)ため複雑で間違いやすいメソッドになることが問題として挙げられます。

2.の方法は、1.に比べるとスマートですが、CORBA構造体はメソッドを持たないclassとしてマッピングされます。このclassは、フィールドが全てpublicであり、しかもfinal classとなっています。そのため、オブジェクト指向プログラミングとの相性があまりよくない問題が挙げられます。


OBVによる解法

ローカルでは、オブジェクトへメッセージを送る(メソッドを起動する)際は、その引数や戻り値にオブジェクトを使用することができます。これを、CORBAのリモートオペレーション呼び出しでも使用し、しかもそのオブジェクトをリモート参照ではなく、オブジェクトをそのまま相手にコピーして渡すには、そのオブジェクトをvaluetypeとしてIDLに定義します。

Schedule.idl
#ifndef _schedule_IDL_
#define _schedule_IDL_

module schedule
{
  valuetype Time
  {
    private long hour;
    private long minute;
    private long second;
                
    long getHour();
    long getMinute();
    long getSecond();
  };

  valuetype Plan
  {
    private Time start;
    private Time end;
  };

  interface Scheduler
  {
    Plan getPlan();
  };
};

#endif




時刻(時分秒)情報をvaluetype宣言。
Timeクラスとして定義する。
Timeクラスの属性を定義。
属性にはアクセス修飾子としてpublicか
privateを指定できる。

Timeクラスのオペレーションを定義。




予定(開始時刻,終了時刻)情報をvaluetype宣言。
Planクラスとして定義する。
Planクラスの属性を定義。属性にはvaluetypeを
入れ子に指定してもよい。
また、オペレーションは定義しなくてもよい。

valuetypeはCORBAオブジェクトではないので、
CORBAオブジェクトのオペレーションの引数
または戻り値に指定される。



valuetypeのJavaへのマッピング

valuetype TimeがどのようにJavaにマッピングされるかを見てみます。コードはOpenORB 1.2.0を使った場合のものです。ただしコメント、インデント等は修正しています。

Time.javaのコード
package schedule;

public abstract class Time 
    implements org.omg.CORBA.portable.StreamableValue
{
        protected int hour;
        protected int minute;
        protected int second;

        public abstract int getHour();
        public abstract int getMinute();
        public abstract int getSecond();

        static final String[] _ids_list = {
                "IDL:schedule/Time:1.0"
        };

        public String [] _truncatable_ids() {
                return _ids_list;
        }

        public void _read(org.omg.CORBA.portable.InputStream is) {
                hour = is.read_long();
                minute = is.read_long();
                second = is.read_long();
        }

        public void _write(org.omg.CORBA.portable.OutputStream os) {
                os.write_long(hour);
                os.write_long(minute);
                os.write_long(second);
        }

        public org.omg.CORBA.TypeCode _type() {
                return schedule.TimeHelper.type();
        }
}

valuetypeの属性に指定したものはフィールドに、オペレーションはabstractメソッドにマッピングされています。また、シリアライズ用のメソッド_read、_writeといったものが定義されているのが分かります。OBVでは、オブジェクトの属性をオペレーション呼び出し時にシリアライズして転送します。受け取り手では、シリアライズされてきたデータからオブジェクトを生成する必要があります。このときシリアライズ対象となるのが、valuetypeで属性として定義されているものです。


コード

valuetypeの実装

Time

valuetypeで定義された型は、Javaのabstract classにマッピングされます。したがって、これを継承した実装クラスを定義することになります。実装クラスでは、オペレーションをメソッドとして実装する他、private指定した属性に対するアクセッサメソッドも実装します。

TimeImpl.java
package schedule;
public class TimeImpl extends Time {
    public TimeImpl() {
        hour = 0;
        minute = 0;
        second = 0;
    }
    public TimeImpl(int h, int m, int s) {
        hour = h;
        minute = m;
        second = s;
    }
    public int getHour() {
        return hour;
    }
    public int getMinute() {
        return minute;
    }
    public int getSecond() {
        return second;
    }
    public String toString() {
        return "[TimeImpl]{hour=" + hour +
            ",minute=" + minute + ",second=" +
            second + "}";
    }
}
valuetypeのTimeから生成された
Timeクラスを継承する。

コンストラクタは必要に応じて適宜定義する。








IDLで定義したメソッドの実装を記述する。








toString()は定義しておくと便利です。




Plan

同様にvaluetypeで定義したPersonを実装します。

PlanImpl.java
package schedule;
public class PlanImpl extends Plan {  
    public PlanImpl() {
        start = new TimeImpl(1, 1, 1);
        end = new TimeImpl(2, 2, 2);
    }

    public PlanImpl(Time aStart, Time anEnd) {
        start = aStart;
        end = anEnd;
    }

    public Time getStartTime() {
        return start;
    }

    public Time getEndTime() {
        return end;
    }

    public String toString() {
        return ("[PlanImpl]{start=" +
            start + ":end=" + end + "}");
    }
}

valueファクトリの作成

valuetypeでは、オブジェクトの状態をシリアライズして転送することになります。オブジェクトの受け取り手では、シリアライズされたデータからオブジェクトを生成する必要があります。CORBAでは、この生成のコードを記述してあげる必要があります。簡単なデフォルトのファクトリを使用する場合は、

valueファクトリのクラス名に、<タイプ名>DefalutFactory と命名すれば、ORBへの登録は不要です。それ以外の名前を付けた場合には、ORBオブジェクトに登録しておく必要があります。

Time用valueファクトリの実装

TimeDefaultFactory.java
package schedule;
import org.omg.CORBA.portable.ValueFactory;
import org.omg.CORBA_2_3.portable.InputStream;
import java.io.Serializable;

public class TimeDefaultFactory implements ValueFactory {
    public Serializable read_value(InputStream in) {
        Time time = new TimeImpl();
        in.read_value((Serializable)time);
        return time;
    }
}

上記コードでは、オブジェクトを最初にnewしておき、そこへシリアライズされてきたデータを当てこんでいます。read_valueメソッドは、ORBから呼ばれます。

Plan用valueファクトリの実装

PlanDefaultFactory.java
package schedule;
import org.omg.CORBA.portable.ValueFactory;
import org.omg.CORBA_2_3.portable.InputStream;
import java.io.Serializable;

public class PlanDefaultFactory implements ValueFactory {
    public Serializable read_value(InputStream in) {
        Plan plan = new PlanImpl();
        in.read_value((Serializable)plan);
        return plan;
    }
}

インタフェースSchedulerの実装

IDLのインタフェースの実装は、サーバントとして作成します。ここでは、簡単なPOA継承によって実装しています。

SimpleScheduler.java
package schedule;
public class SimpleScheduler extends SchedulerPOA {
    private Plan myPlan;

    public SimpleScheduler() {
        Time start = new TimeImpl(12, 34, 56);
        Time end = new TimeImpl(23,45,6);
        
        myPlan = new PlanImpl(start, end);
    }

    public Plan getPlan() {
        return myPlan;
    }
}

サーバの実装

Server.java
package schedule;

import org.omg.CORBA.ORB;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContext;
import org.omg.CosNaming.NamingContextHelper;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;
import org.omg.PortableServer.POAManager;

/**
 * Server.java
 *
 * @author <a href="mailto:torutk@alles.or.jp">Toru TAKAHASHI</a>
 * @version
 */

public class Server {
    public static void main(String[] args) {

        ORB orb = ORB.init(args, null);

        // RootPOAの取得
        org.omg.CORBA.Object objPoa = null;
        POA rootPoa = null;
        try {
            objPoa = orb.resolve_initial_references("RootPOA");
            rootPoa = POAHelper.narrow(objPoa);
        } catch (org.omg.CORBA.ORBPackage.InvalidName e) {
            System.out.println("RootPOAの名前が正しくありません");
            System.exit(1);
        }

        if (rootPoa == null) {
            System.out.println("POAのオブジェクト参照が取得できません");
            System.exit(1);
        }

        // Servantの作成
        SimpleScheduler scheduler = new SimpleScheduler();
        org.omg.CORBA.Object schedulerRef = null;
        try {
            rootPoa.activate_object(scheduler);
            schedulerRef = rootPoa.servant_to_reference(scheduler);
        } catch (Exception e) {
            System.out.println("Schedulerオブジェクト作成でエラー");
            e.printStackTrace();
            System.exit(1);
        }
        
        // NamingServiceへの登録
        // ルートネーミングコンテクストオブジェクトの取得
        org.omg.CORBA.Object objNs = null;
        try {
            objNs = orb.resolve_initial_references("NameService");
        } catch (org.omg.CORBA.ORBPackage.InvalidName e) {
            System.out.println("ネーミングサービスの名前が正しくありません");
            e.printStackTrace();
            System.exit(1);
        }
        NamingContext rootNameContext = NamingContextHelper.narrow(objNs);
        if (rootNameContext == null) {
            System.out.println("ルートネーミングコンテクストが空です");
            System.exit(1);
        }
        
        // Schedulerオブジェクトのネームコンポーネント作成
        NameComponent[] schedulerNameComponent = new NameComponent[1];

        schedulerNameComponent[0] = new NameComponent("TheScheduler", "Object");

        try {
            rootNameContext.rebind(schedulerNameComponent, schedulerRef);
            System.out.println("Registered scheduler object to name service");
        } catch (Exception e) {
            System.out.println("Schedulerオブジェクトのネーミングサービスへの登録で
                 エラーが発生しました");
            e.printStackTrace();
            System.exit(1);
        }

        // POAマネージャの活性化
        POAManager poaManager = rootPoa.the_POAManager();
        try {
            poaManager.activate();
            System.out.println("activated POA Manager");
        } catch (org.omg.PortableServer.POAManagerPackage.AdapterInactive e) {
            System.out.println("POAマネージャの活性化に失敗しました");
            e.printStackTrace();
            System.exit(1);
        }

        // クライアントからの要求の待ち合わせ
        System.out.println("Waiting request from client");
        try {
            orb.run();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
} // Server

クライアントの実装

package schedule;

import org.omg.CORBA.ORB;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContext;
import org.omg.CosNaming.NamingContextHelper;

/**
 * Client.java
 *
 * @author <a href="mailto:torutk@alles.or.jp">Toru TAKAHASHI</a>
 * @version
 */
public class Client {
   
    public static void main(String[] args) {
        Scheduler scheduler = null;
        try {
            ORB orb = ORB.init(args, null);
            System.out.println("ORB initialized.");
            org.omg.CORBA.Object objNs =
                orb.resolve_initial_references("NameService");
            NamingContext rootNameContext =
                NamingContextHelper.narrow(objNs);
            System.out.println("NamingContext aquired.");
            NameComponent nc =
                new NameComponent("TheScheduler", "Object");
            NameComponent[] path = {nc};
            scheduler =
                SchedulerHelper.narrow(rootNameContext.resolve(path));
            System.out.println("TheScheduler Object aquired.");
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }

        try {
            Plan plan = scheduler.getPlan();
            System.out.println("Server Plan = " + plan);
            Person person = scheduler.getPerson();
            System.out.println("Server Person = " + person);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
} // Client

This page is written by Toru TAKAHASHI.(torutk@02.246.ne.jp)