[ Java & CORBAページへ戻る ]

リモートに存在するオブジェクトへメッセージを送る側をクライアント、メッセージを受け付けて処理を実行する側をサーバと呼んできました。しかし、オブジェクト指向プログラミングでは、オブジェクトの間に一方的なクライアント/サーバ関係にあるだけでなく、相互にメッセージを送りあうことがよくあります。手続き的なプログラミングモデルでは、「コールバック」と呼ばれているものです。オブジェクト指向プログラミングでは、コールバックはごく当たり前に記述できるのですが、CORBAでは若干の記述が必要になります。

ここでは、簡単なIDLのClockインタフェースを持つCORBAオブジェクトをサーバとして提供するプログラムと、このClockオブジェクトに対してポーリングによる時刻取得を行うクライアント、コールバックによってClockオブジェクトからメッセージを受け取るクライアントの両方を作成していきながら、コールバック/プログラミングを見ていきます。


IDLの作成

IDLインタフェースの記述−Clock.idl−

簡単なクロックは、5つのオペレーションを定義します。また、クロックに対してコールバックされるインタフェースとしてクロックリスナーを定義します。Javaのイベント機構をちょっともじったものですが、リスナーメソッドの引数をイベント型にはせずに直接基本型を渡すようにしています。

Clock.idl
#ifndef _Clock_IDL_
#define _Clock_IDL_

#pragma prefix "idl.learn.torutk.java_conf.gr.jp"

module clock_ex1
{
    interface ClockListener
    {
        void timeChanged(in long hour,
                         in long minute,
                         in long second);
    };

    interface Clock
    {
        long getHour();
        long getMinute();
        long getSecond();
        void addClockListener(
            in ClockListener listener
        );
        void removeClockListener(
            in ClockListener listener
        );
    };
};
#endif

このファイルを、開発ディレクトリ(D:\develop\ex3)に置きます。

開発ディレクトリの内容(1)
d:\develop\ex3\
Clock.idl

IDLコンパイラの実行

IDLコンパイラを実行します。

IDLコンパイラ実行
D:\develop\ex3>idl2java Clock.idl
OpenORB IDL Compiler / (c) 2000-2001 Exolab.org
compile : Clock.idl

D:\develop\ex3>

IDLコンパイラ実行後の開発ディレクトリの内容は以下のとおりです。generatedというディレクトリの下に、パッケージ名に対応するディレクトリが作成され、Javaのソースファイルが生成されます。

開発ディレクトリの内容(2)
d:\develop\ex3\
Clock.idl
d:\develop\ex3\generated\jp\gr\java_conf\torutk\learn\idl\clock_ex1\
Clock.java
ClockHelper.java
ClockHolder.java
ClockOperations.java
ClockPOA.java POA継承方式で使用
ClockPOATie.java POA委譲方式で使用
_ClockStub.java
ClockListener.java
ClockListenerHelper.java
ClockListenerHolder.java
ClockListenerOperations.java
ClockListenerPOA.java POA継承方式で使用
ClockListenerPOATie.java POA委譲方式で使用
_ClockListenerStub.java

IDLを実装するクラスーサーバントー

Clockサーバント

IDLに記述したClockインタフェースを実現するクラス(サーバント)としてEasyClock.javaを記述します。IDLを実装するには、次の2つの方法があります。

  1. POAの継承方式(Inheritance-Based Implementation)
  2. POAの委譲方式(Delegation-Based Implementation)

今回は、2.のPOA委譲方式を使ってみます。

POA委譲方式 - EasyClock.java -

POA委譲方式で実装する場合は、IDLで定義したオペレーションのJavaマッピングであるinterface ClockOperationsを実装します。

EasyClock.java
package jp.gr.java_conf.torutk.learn.idl.clock_ex1;

import org.omg.CORBA.Any;
import java.util.List;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;

public class EasyClock implements ClockOperations {
    private int hour = 0;
    private int minute = 0;
    private int second = 0;
    private boolean isChanged = false;
    private List listeners = new ArrayList();

    public EasyClock() {
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
                public void run() {
                    tick();
                    notifyListeners();
                }
            }, 10000, 1000);
    }
    private final synchronized void tick() {
        second++;
        if (second >= 60) {
            second = 0;
            minute++;
        }
        if (minute >= 60) {
            minute = 0;
            hour++;
        }
        if (hour >= 24) {
            hour = 0;
        }
        setChanged();
    }
    private final void setChanged() {
        isChanged = true;
    }   
    private final void clearChanged() {
        isChanged = false;
    }
    private final boolean hasChanged() {
        return isChanged;
    }
    private final void notifyListeners() {
        if (!hasChanged()) {
            return;
        }
        Object[] listenersToNotify = null;
        synchronized(this) {
            listenersToNotify = listeners.toArray();
        }
        for (int i=0; i<listenersToNotify.length; i++) {
            ClockListener listener =
                (ClockListener)listenersToNotify[i];
            listener.clockChanged(getHour(),
                                  getMinute(),
                                  getSecond());
        }
    }
    public final synchronized int getSecond() {
        return second;
    }
    public final synchronized int getMinute() {
        return minute;
    }
    public final synchronized int getHour() {
        return hour;
    }
    public void addClockListener(ClockListener listener) {
        listeners.add(listener);
    }
    public void removeClockListener(ClockListener listener) {
        listeners.remove(listener);
    }
}// EasyClock

エラー処理はほとんど行っていません。本当は、リスナーへ通知するときに既にリスナーが存在していない場合のエラー処理、removeClockListenerでリスナーが存在しないときの通知等が入っていません。

EasyClock.javaは、開発ディレクトリの下に直接パッケージ名に対応するディレクトリを作成して置くことにします。

開発ディレクトリの内容(3)
d:\develop\ex3\
Clock.idl
d:\develop\ex3\jp\gr\java_conf\torutk\learn\idl\clock_ex1\
EasyClock.java IDLを実装するクラス
d:\develop\ex3\generated\jp\gr\java_conf\torutk\learn\idl\clock_ex1\
        :
        :
IDLコンパイラで生成されたファイル

ClockListenerサーバント

IDLに記述したClockListenerインタフェースを実現するクラス(サーバント)としてEasyClockListener.javaを記述します。今回は、POAの継承方式(Inheritance-Based Implementation)を使用します。

EasyClockListener.java
package jp.gr.java_conf.torutk.learn.idl.clock_ex1;

public class EasyClockListener
    extends ClockListenerPOA
{
    public void clockChanged(
        int hour, int minute, int second
    ) {
        System.out.println(
            "ServerClock=" + hour + ":" +
            minute + ":" + second
        );
    }
}// EasyClockListener

EasyClockListener.javaは、開発ディレクトリの下に直接パッケージ名に対応するディレクトリを作成して置くことにします。

開発ディレクトリの内容(4)
d:\develop\ex3\
Clock.idl
d:\develop\ex3\jp\gr\java_conf\torutk\learn\idl\clock_ex1\
EasyClock.java IDLインタフェース Clockを実装するクラス
EasyClockLisntener.java IDLインタフェース ClockListenerを実装するクラス
d:\develop\ex3\generated\jp\gr\java_conf\torutk\learn\idl\clock_ex1\
        :
        :
IDLコンパイラで生成されたファイル

CORBAオブジェクト起動サーバ

Clockオブジェクトを起動するサーバプログラムです。今回のサーバント(EasyClock)はPOA委譲方式です。

package jp.gr.java_conf.torutk.learn.idl.clock_ex1;

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 org.omg.PortableServer.POAManager;

/**
 * Server.java
 *
 * Created: Sun Oct 28 01:41:59 2001
 *
 * @author <a href="mailto:torutk@alles.or.jp">Toru TAKAHASHI</a>
 * @version
 */

public class Server {
    public static void main(String[] args) {
        // 1. ORBの起動
        ORB orb = ORB.init(args, null);

        // 2. 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);
        }

        // 3. サーバントの作成
        // POATie(委譲による実装方式)の場合は、サーバントの他に、
        // POATieオブジェクトを作成する。
        EasyClock clock = new EasyClock();
        ClockPOATie clockTie = new ClockPOATie(clock);
        // 4. サーバントをPOAに結び付け
        org.omg.CORBA.Object clockRef = null;
        try {
            rootPoa.activate_object(clockTie);
            clockRef = rootPoa.servant_to_reference(clockTie);
        } catch (Exception e) {
            System.out.println("Clockオブジェクト作成でエラー");
            e.printStackTrace();
            System.exit(1);
        }
        
        // 5. ネーミングサービスの取得
        // ルートネーミングコンテクストオブジェクトの取得
        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);
        }
        NamingContextExt rootNameContext =
            NamingContextExtHelper.narrow(objNs);
        if (rootNameContext == null) {
            System.out.println("ルートネーミングコンテクストが空です");
            System.exit(1);
        }
        
        // 6. ネーミングサービスへサーバントのCORBAリファレンス登録
        // リモートクロックオブジェクトのネームコンポーネント作成
        NameComponent[] clockNameComponent = null;
        try {
            clockNameComponent = rootNameContext.to_name("TheClock.Object");
            rootNameContext.rebind(clockNameComponent, clockRef);
            System.out.println("Registered clock object to name service");
        } catch (Exception e) {
            System.out.println("クロックオブジェクトのネーミングサービスへの" +
                               "登録でエラーが発生しました");
            e.printStackTrace();
            System.exit(1);
        }

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

クライアント

普通のクライアント

参考までに、Clockオブジェクトを呼び出すクライアントのコードを載せます。

package jp.gr.java_conf.torutk.learn.idl.clock_ex1;

import org.omg.CORBA.ORB;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.CosNaming.NamingContextPackage.NotFound;

/**
 * Client.java
 *
 * Created: Sat Nov 10 02:09:34 2001
 *
 * @author <a href="mailto:torutk@alles.or.jp">Toru TAKAHASHI</a>
 * @version
 */

public class Client {
    
    public static void main(String[] args) {
        Clock clock = null;
        try {
            // 1. ORBの起動
            ORB orb = ORB.init(args, null);
            // 2. ネーミングサービスの取得
            org.omg.CORBA.Object objNs =
                orb.resolve_initial_references("NameService");           
            NamingContextExt ncRef =
                NamingContextExtHelper.narrow(objNs);
            // 3. ネーミングサービスからリモートオブジェクト
            // リファレンスを獲得
            String name = "TheClock.Object";
            clock = ClockHelper.narrow(ncRef.resolve_str(name));
        } catch (NotFound e) {
            System.out.println("Remote Object 'TheClock' not found.");
            System.out.println("why = " + e.why.value());
            System.out.println("rest_of_name length = " +
                               e.rest_of_name.length);
            for (int i=0; i<e.rest_of_name.length; i++) {
                System.out.println("rest_of_name[" + i + "] = {" + 
                                   e.rest_of_name[i].id + ", " +
                                   e.rest_of_name[i].kind + "}"
                );
            }
            System.exit(1);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }

        try {
            // 4. リモートオブジェクトのメソッドを呼び出し
            int hour = clock.getHour();
            int minute = clock.getMinute();
            int second = clock.getSecond();
            System.out.println("Server Clock = " + hour + ":" +
                               minute + ":" + second);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
} // Client

コールバック・クライアント

コールバックを受け取るクライアントプログラムの記述です。リモートオブジェクトからメッセージ要求を受け付け、サーバントを呼び出すためには、POAが必要です。そこで、POAの取得、サーバントの作成、POAマネージャの活性化を行います。

package jp.gr.java_conf.torutk.learn.idl.clock_ex1;

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

/**
 * ListenerClient.java
 *
 * Created: Sat Nov 10 02:09:34 2001
 *
 * @author <a href="mailto:torutk@alles.or.jp">Toru TAKAHASHI</a>
 * @version
 */

public class ListenerClient {
    
    public static void main(String[] args) {
        Clock clock = null;
        ORB orb = null;
        try {
            // ORBの起動
            orb = ORB.init(args, null);
            // ネーミングサービスの取得
            org.omg.CORBA.Object objNs =
                orb.resolve_initial_references("NameService");           
            NamingContextExt ncRef = NamingContextExtHelper.narrow(objNs);
            // リモートオブジェクトリファレンス獲得
            String name = "TheClock.Object";
            clock = ClockHelper.narrow(ncRef.resolve_str(name));
        } catch (NotFound e) {
            System.out.println("Remote Object 'TheClock' not found.");
            System.out.println("why = " + e.why.value());
            System.out.println("rest_of_name length = " +
                               e.rest_of_name.length);
            for (int i=0; i<e.rest_of_name.length; i++) {
                System.out.println("rest_of_name[" + i + "] = {" + 
                                   e.rest_of_name[i].id + ", " +
                                   e.rest_of_name[i].kind + "}"
                );
            }
            System.exit(1);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }

        // 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);
        }

        // コールバックオブジェクトのサーバント作成とPOAへの結びつけ
        org.omg.CORBA.Object listenerRef = null;
        try {
            EasyClockListener listener = new EasyClockListener();
            rootPoa.activate_object(listener);
            listenerRef = rootPoa.servant_to_reference(listener);           
        } catch (Exception e) {
            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);
        }
        // コールバックオブジェクトのCORBAリファレンスをリモートオブジェクトへ
        // 渡す。
        try {
            ClockListener ref = ClockListenerHelper.narrow(listenerRef);
            clock.addClockListener(ref);
            // コールバックのリクエストを待つイベントループ
            orb.run();
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
} // ListenerClient

実行

ネーミングサービス

ネーミングサービスの起動については、使用しているCORBA製品によって異なります。OpenORBの提供するネーミングサービスについては、OpenORB TipsページのNaming Service項を参照。


サーバ

サーバプログラムを実行するには、先にネーミングサービスを実行している必要があります。サーバプログラムを実行する際に、先に起動したネーミングサービスのオブジェクトリファレンス情報をコマンドラインオプションで指定します。

ネーミングサービスの指定 -ORBInitRef NameService=<オブジェクトURL>
サーバプログラム実行
D:\develop\ex3\classes>java jp.gr.java_conf.torutk.learn.idl.clock_ex1.Server
 -ORBInitRef NameService=corbaloc:iiop:1.2@nameserver:5555/NameService
Registered clock object to name service
activated POA Manager
Waiting request from client

普通のクライアント

ネーミングサービスにサーバを問合せて参照を取得後、普通にメソッドを呼び出す。

普通のクライアントプログラム実行
D:\develop\ex3\classes>java jp.gr.java_conf.torutk.learn.idl.clock_ex1.Cli
ent -ORBInitRef NameService=corbaloc:iiop:1.2@nameserver:5555/NameService 
Server Clock = 0:1:26

コールバッククライアント

コールバッククライアントプログラム実行
D:\develop\ex3\classes>java jp.gr.java_conf.torutk.learn.idl.clock_ex1.Listener
Client -ORBInitRef NameService=corbaloc:iiop:1.2@nameserver:5555/NameService 
ServerClock=0:2:58
ServerClock=0:2:59
ServerClock=0:3:0
ServerClock=0:3:1

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