Java IDL: コールバックオブジェクトを使用する例

クライアントプログラムでは、サーバー側で起こった変更や更新への対応が必要になることがあります。たとえば、株価サーバー上で株価が更新されるたびに、クライアントのグラフ表示プログラムやスプレッドシートプログラムでも更新を行いたいといった場合などです。このような場合、クライアントには 2 つのオプションがあります。

このドキュメントの例では、クライアントプログラムからサーバーにコールバックオブジェクトを渡す方法と、その後にサーバーからクライアントに変更を通知する方法について説明します。ここでは、単純なアプリケーションを拡張したコードを紹介します。アプリケーションを簡略化した箇所については、コード内にコメントを記載します。

このドキュメントで紹介するコードは、次のようなものです。

サンプルのコンパイルと実行の手順についても説明します。

コールバックの例: 中級レベル

IDL ファイルの記述

このアプリケーション例で使用する callback.idl ファイルは、次のようなものです。

interface Listener {
        void message(in string msg);
};

interface MessageServer {
        void register(in Listener lt);
};

サーバーコードの記述

このアプリケーション例で使用する Server.java ファイルは、次のようなものです。

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

public class Server {

    public static void main(String[] args) {
        try {
            //create and initialize the ORB
            Properties props = System.getProperties();
            props.put("org.omg.CORBA.ORBInitialPort", "1050");
            //Replace MyHost with the name of the host on which you are running the server
            props.put("org.omg.CORBA.ORBInitialHost", "<MyHost>");
            ORB orb = ORB.init(args, props);
            System.out.println("Initialized ORB");

            //Instantiate Servant and create reference
            POA rootPOA = POAHelper.narrow(
                orb.resolve_initial_references("RootPOA"));
            MessageServerImpl msImpl = new MessageServerImpl();
            rootPOA.activate_object(msImpl);
            MessageServer msRef = MessageServerHelper.narrow(
                rootPOA.servant_to_reference(msImpl));

            //Bind reference with NameService
            NamingContext namingContext = NamingContextHelper.narrow(
                orb.resolve_initial_references("NameService"));
            System.out.println("Resolved NameService");
            NameComponent[] nc = { new NameComponent("MessageServer", "") };
            namingContext.rebind(nc, msRef);

            //Activate rootpoa
            rootPOA.the_POAManager().activate();

            //Start readthread and wait for incoming requests
            System.out.println("Server ready and running ....");
            
            //REMOVE THE NEXT LINE FOR THE SIMPLER EXAMPLE
            msImpl.startReadThread();
            
            orb.run();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

MessageServer 実装の記述

このファイルのコードでは、新しいクライアントを登録し、メッセージを受け付けたあと、登録されたクライアントにそのメッセージを中継します。このアプリケーション例で使用する MessageServerImpl.java ファイルは、次のようなものです。

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Vector;
import java.util.Iterator;

public class MessageServerImpl extends MessageServerPOA {

    private Vector clients = new Vector();
    private ReadThread rt = null;

    public MessageServerImpl() {
        rt = new ReadThread(this);
    }

    public void register(Listener lt) {
        clients.add(lt);
    }

    public void startReadThread() {
        rt.start();
    }

    public void message(String msg) {
        Iterator it = clients.iterator();
        while (it.hasNext()) {
            Listener lt = (Listener) it.next();
            lt.message(msg);
            //FOR THE SIMPLER EXAMPLE, ADD A SIMPLE
            //MESSAGE TO BE CALLED BACK, FOR EXAMPLE,
            //SLEEP FOR 30 SECONDS, THEN SEND THE TIME
        }
    }
}

//EXCLUDE THIS CLASS FOR THE SIMPLER EXAMPLE
class ReadThread extends Thread {

    MessageServerImpl msImpl = null;

    public ReadThread(MessageServerImpl msImpl) {
        this.msImpl = msImpl;
    }

    public void run() {
        BufferedReader br = new BufferedReader(
            new InputStreamReader(System.in));

        try {
            for (;;) {
                System.out.print("message > ");
                String msg = br.readLine();
                msImpl.message(msg);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

クライアントコードの記述

このアプリケーション例で使用する Client.java ファイルは、次のようなものです。

import java.util.Properties;
import org.omg.CORBA.ORB;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContext;
import org.omg.CosNaming.NamingContextHelper;

public class Client {

    public static void main(String[] args) {
        try {
        
            //initialize orb
            Properties props = System.getProperties();
            props.put("org.omg.CORBA.ORBInitialPort", "1050");
            //Replace MyHost with the name of the host on which you are running the server
            props.put("org.omg.CORBA.ORBInitialHost", "<MyHost>");
            ORB orb = ORB.init(args, props);
            System.out.println("Initialized ORB");

            //Instantiate Servant and create reference
            POA rootPOA = POAHelper.narrow(
                orb.resolve_initial_references("RootPOA"));
            ListenerImpl listener  = new ListenerImpl();
            rootPOA.activate_object(listener);
            Listener ref = ListenerHelper.narrow(
                rootPOA.servant_to_reference(listener));

            //Resolve MessageServer
            MessageServer msgServer = MessageServerHelper.narrow(
                orb.string_to_object("corbaname:iiop:1.2@localhost:1050#MessageServer"));

            //Register listener reference (callback object) with MessageServer
            msgServer.register(ref);
            System.out.println("Listener registered with MessageServer");

            //Activate rootpoa
            rootPOA.the_POAManager().activate();

            //Wait for messages
            System.out.println("Wait for incoming messages");
            orb.run();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Listener 実装の記述

Listener は、サーバーからのメッセージ受信を識別すると、そのメッセージをクライアントに表示します。このアプリケーション例で使用する ListenerImpl.java ファイルは、次のようなものです。

public class ListenerImpl extends ListenerPOA {

    public void message(String msg) {
        System.out.println("Message from server : " + msg);
    }
}

サンプルのコンパイル方法と実行方法についての説明

このアプリケーション例を実行するには、次の手順を SERVER マシンで実行します。
  1. 例を以前に実行したことがある場合は、生成された既存のファイルを削除します。UNIX オペレーティング環境では、次のようなコマンドを使用します。
    rm -rf ./classes ./orb.db
    
  2. スタブとスケルトンを生成します。まず、スタブとスケルトンを置くディレクトリを作成し、次に、idlj コンパイラを実行してスタブとスケルトンを生成します。コマンドは次のようになります。
    mkdir -p ./classes
    <path_to_java>/bin/idlj -fall -td ./classes callback.idl
    
  3. 次のようにして、.java ファイルをコンパイルします。
    <path_to_java>/bin/javac -classpath ./classes -d ./classes *.java
    
  4. 次のようにして、orbd ネームサービスを開始します。
    <path_to_java>/bin/orbd -ORBInitialPort 1050 -ORBInitialHost <host_name> &
    
  5. 次のようにして、サーバーを実行します。
    <path_to_java>/bin/java -classpath ./classes Server -ORBInitialPort 1050
    

サーバーを開始すると、端末ウィンドウに次のような出力が表示されます。

Initialized ORB
Resolved NameService
Server ready and running ....
message > 

このプロンプトを使用してクライアントにメッセージを送信することになりますが、その前に、クライアントアプリケーションを開始する必要があります。CLIENT 端末で次の手順を実行して、クライアントアプリケーションを実行します。クライアントコードの properties セクションで、サーバーが実行されているホスト名を使用したことを確認してください。

<path_to_java>/bin/java -classpath ./classes Client -ORBInitialPort 1050 

クライアントアプリケーションからの出力が、次のような形式で表示されます。

Initialized ORB
Listener registered with MessageServer
Wait for incoming messages

コールバックを使用した機能を表示するには、サーバー端末のプロンプトにデータを入力します。そうすると、そのデータがすべてのクライアント端末に表示されます。通知の内容には、クライアントが関心を持つものを何でも含めることができます。

一例として、フットボールの試合のスコアが変化したときに、クライアントに通知してみます。

message > Niners TD. Niners 7 - Giants 0
message > Giants TD. Niners 7 - Giants 7
message > Niners TD. Niners 14 - Giants 7

上記のようなメッセージが送信されると、クライアントに次のように表示されます。

Wait for incoming messages
Message from server : Niners TD. Niners 7 - Giants 0
Message from server : Giants TD. Niners 7 - Giants 7
Message from server : Niners TD. Niners 14 - Giants 7

Copyright © 1993, 2013, Oracle and/or its affiliates. All rights reserved.