このチュートリアルでは、おなじみの「Hello World」プログラムの分散システム版を、JavaのRMI (Remote Method Invocation、リモート・メソッド呼び出し)をInternet Inter-ORB Protocol (IIOP)経由で使用して作成する手順を説明します。RMI-IIOPは、CORBA (Common Object Request Broker Architecture)機能を、ほかの多くのプログラミング言語およびプラットフォームに対するJava接続性に追加します。RMI-IIOPにより、Web対応の分散JavaリモートManagement Groupが可能になります。ランタイム・コンポーネントには、IIOP通信を使った分散コンピューティング用のJava ORBが含まれています。
RMI-IIOPは、IIOPをベースとなるトランスポートとして使用してRMIインタフェースをプログラミングしたいJavaプログラマ向けです。RMI-IIOPはさまざまな言語で実装されるCORBAオブジェクトとの相互運用性を提供しますが、リモート・インタフェースをあらかじめJava RMIインタフェースとして定義しておく必要があります。EJBのリモート・オブジェクト・モデルはRMIベースなので、Enterprise JavaBeans (EJB)を使うプログラマには特に有用です。
分散アプリケーションを作成するためのもう1つの選択肢として、Java IDLがあります。Java IDLは、CORBAインタフェース定義言語(IDL)で定義されたインタフェースに基づいてJavaプログラミング言語でプログラムを記述したいCORBAプログラマ向けです。これは「通常どおりの」CORBAプログラミングで、C++やCOBOLのようなほかの言語とまったく同じ方法でJavaをサポートしています。
ここで例として紹介する分散型の「Hello World」プログラムでは、クライアント・アプリケーションからIIOP経由でサーバー(そのクライアント・アプリケーションのダウンロード元のホスト上で稼働している)に対してリモート・メソッド呼出しを行います。このクライアント・アプリケーションを実行すると、「Hello World!」が表示されます。
このチュートリアルの構成は、次のとおりです。
このセクションで行うタスクは3つあります。
HelloInterface.java
- リモート・インタフェースHelloImpl.java
- HelloInterface
を実装するリモート・オブジェクトの実装HelloServer.java
- リモート・オブジェクト実装のインスタンスを作成し、そのインスタンスをネーム・サービスの名前にバインドする、RMIサーバーHelloClient.java
- リモート・メソッドsayHello()
を呼び出すクライアント・アプリケーションRemote
インタフェースを実装するクラスのインスタンスです。作成するリモート・インタフェースでは、ほかのマシンから呼び出したい各メソッドを宣言します。リモート・インタフェースには、次の特性があります。
public
として宣言する必要があります。そうしないと、クライアントがリモート・インタフェースと同じパッケージ内にある場合を除いて、リモート・インタフェースを実装しているリモート・オブジェクトをクライアントがロードしようとした時点でエラーが発生します。java.rmi.Remote
インタフェースを拡張します。throws
節内でjava.rmi.RemoteException
(またはRemoteException
のスーパー・クラス)を宣言する必要があります。HelloImpl
)ではなく、リモート・インタフェースの型 (たとえば、HelloInterface
)として宣言する必要があります。この例では、すべてのソース・ファイルを同じディレクトリ(たとえば、$HOME/mysrc/RMIHelloPOA
)内に作成します。リモート・インタフェースHelloInterface
のインタフェース定義は、次のとおりです。このインタフェースには、ただ1つのメソッドsayHello
が含まれています。
//HelloInterface.java import java.rmi.Remote; public interface HelloInterface extends java.rmi.Remote { public void sayHello() throws java.rmi.RemoteException; }
java.rmi.RemoteException
をスローすることによって報告します。
リモート・オブジェクトの実装クラスHelloImpl.java
は、少なくとも次の条件を満たしていなければなりません。
HelloImpl.java:
のソースは次のとおりです。そのあと、上記の各ステップについて説明します。
//HelloImpl.java import javax.rmi.PortableRemoteObject; public class HelloImpl extends PortableRemoteObject implements HelloInterface { public HelloImpl() throws java.rmi.RemoteException { super(); // invoke rmi linking and remote object initialization } public void sayHello() throws java.rmi.RemoteException { System.out.println( "It works! Hello World!!" ); } }
Javaプログラミング言語では、あるインタフェースを実装することをクラスが宣言すると、そのクラスとコンパイラの間で契約が結ばれます。この契約によって、そのクラスは、そのインタフェース内で宣言された各メソッド・シグネチャに対して、メソッドの本体(つまり定義)を提供することを約束します。インタフェースのメソッドは、暗黙のうちにpublic
およびabstract
として宣言されているため、実装クラスでその契約が果たされない場合、そのクラスは定義に基づきabstract
になります。そのクラスがabstract
として宣言されていない場合は、コンパイラによってその事実が指摘されます。
この例では、実装クラスはHelloImpl
です。このクラスは、どのリモート・インタフェースを実装するのかを宣言します。HelloImpl
クラスの宣言は、次のとおりです。
public class HelloImpl extends PortableRemoteObject implements HelloInterface{便宜上、実装クラスはリモート・クラスを拡張できます。この例では、リモート・クラスは
javax.rmi.PortableRemoteObject
です。PortableRemoteObject
を拡張していることにより、HelloImpl
クラスを、通信にIIOPベースのトランスポートを使うリモート・オブジェクトを作成するために利用できます。
さらに、リモート・オブジェクトのインスタンスは「エクスポート」される必要があります。リモート・オブジェクトをエクスポートすると、そのオブジェクトは、匿名ポート上でリモート・オブジェクトへの着呼を監視することによって、着信したリモート・メソッド要求を受け入れることができるようになります。javax.rmi.PortableRemoteObject
を拡張すると、そのクラスは作成時に自動的にエクスポートされます。
オブジェクトのエクスポートは、java.rmi.RemoteException
をスローする可能性があるため、コンストラクタがほかに何も行わない場合でも、RemoteException
をスローするコンストラクタを定義する必要があります。コンストラクタを定義しなかった場合は、javac
は、次のエラー・メッセージを生成します。
HelloImpl.java:3: unreported exception java.rmi.RemoteException; must be caught or declared to be thrown. public class HelloImpl extends PortableRemoteObject implements HelloInterface{ ^ 1 error復習: リモート・オブジェクトの実装クラスが行う必要のある事柄は、次のとおりです。
java.rmi.RemoteException
をスローするようにコンストラクタを宣言するHelloImpl
クラスのコンストラクタは、次のとおりです。
public HelloImpl() throws java.rmi.RemoteException { super(); }次の点に注意してください。
super
メソッド呼出しは、リモート・オブジェクトをエクスポートするjavax.rmi.PortableRemoteObject
の引数なしのコンストラクタを呼び出します。java.rmi.RemoteException
をスローする必要があります。java.rmi.RemoteException
は、実行時例外ではなく、チェック例外です。
スーパー・クラスの引数なしのコンストラクタsuper()
への呼出しは、省略したとしてもデフォルトで発生しますが、この例では、クラスの前にスーパー・クラスが構築されることを明確にするために、この呼出しを省略せずに記述しました。
sayHello()
メソッドの実装例は、次のようになります。この例では、呼出し側に「It works! Hello World!」という文字列が返されます。
public void sayHello() throws java.rmi.RemoteException { System.out.println( "It works! Hello World!!" ); }リモート・メソッドに渡す引数、またはリモート・メソッドからの戻り値は、Javaプラットフォーム用のどのデータ型であってもかまいません。さらに、インタフェース
java.io.Serializable
を実装したオブジェクトであれば、オブジェクト型であってもかまいません。java.lang
およびjava.util
内のコア・クラスの大部分は、Serializable
インタフェースを実装しています。RMIでは:
static
またはtransient
とマークされたもの以外は、オブジェクトのすべてのデータ・メンバー(またはフィールド)がコピーされます。rmic
を使ってスタブおよびスケルトンを生成する」で説明する。サーバー・クラスは、リモート・オブジェクト実装のインスタンスを生成し、そのインスタンスをネーム・サービスの名前にバインドするmain
メソッドを持ちます。このmain
メソッドを含むクラスは、実装クラスそのものである場合も、まったく別のクラスである場合もあります。
この例では、main
メソッドはHelloServer.java
の一部として含まれており、次の処理を実行します。
HelloServer.java:
のソースは次のとおりです。そのあと、上記の各ステップについて説明します。
//HelloServer.java
import javax.naming.InitialContext;
import javax.naming.Context;
import javax.rmi.PortableRemoteObject ;
//Please note that internal Oracle APIs
//may change in future releases.
import com.sun.corba.se.internal.POA.POAORB;
import org.omg.PortableServer.*;
import java.util.*;
import org.omg.CORBA.*;
import javax.rmi.CORBA.Stub;
import javax.rmi.CORBA.Util;
public class HelloServer {
public HelloServer(String[] args) {
try {
Properties p = System.getProperties();
// add runtime properties here
//Please note that the name of the servertool
//class may change in future releases.
p.put("org.omg.CORBA.ORBClass",
"com.sun.corba.se.internal.POA.POAORB");
p.put("org.omg.CORBA.ORBSingletonClass",
"com.sun.corba.se.internal.corba.ORBSingleton");
ORB orb = ORB.init( args, p );
POA rootPOA = (POA)orb.resolve_initial_references("RootPOA");
// STEP 1: Create a POA with the appropriate policies
Policy[] tpolicy = new Policy[3];
tpolicy[0] = rootPOA.create_lifespan_policy(
LifespanPolicyValue.TRANSIENT );
tpolicy[1] = rootPOA.create_request_processing_policy(
RequestProcessingPolicyValue.USE_ACTIVE_OBJECT_MAP_ONLY );
tpolicy[2] = rootPOA.create_servant_retention_policy(
ServantRetentionPolicyValue.RETAIN);
POA tPOA = rootPOA.create_POA("MyTransientPOA", null, tpolicy);
// STEP 2: Activate the POA Manager, otherwise all calls to the
// servant hang because, by default, POAManager will be in the
// HOLD state.
tPOA.the_POAManager().activate();
// STEP 3: Instantiate the Servant and activate the Tie, If the
// POA policy is USE_ACTIVE_OBJECT_MAP_ONLY
HelloImpl helloImpl = new HelloImpl();
_HelloImpl_Tie tie = (_HelloImpl_Tie)Util.getTie( helloImpl );
String helloId = "hello";
byte[] id = helloId.getBytes();
tPOA.activate_object_with_id( id, tie );
// STEP 4: Publish the object reference using the same object id
// used to activate the Tie object.
Context initialNamingContext = new InitialContext();
initialNamingContext.rebind("HelloService",
tPOA.create_reference_with_id(id,
tie._all_interfaces(tPOA,id)[0]) );
System.out.println("Hello Server: Ready...");
// STEP 5: Get ready to accept requests from the client
orb.run();
}
catch (Exception e) {
System.out.println("Problem running HelloServer: " + e);
e.printStackTrace();
}
}
public static void main(String args[]) {
new HelloServer( args );
}
}
main
メソッドでは、まず、適切なポリシーを持つPortable Object Adapter (POA)を作成する必要があります。たとえば、
Policy[] tpolicy = new Policy[3]; tpolicy[0] = rootPOA.create_lifespan_policy( LifespanPolicyValue.TRANSIENT ); tpolicy[1] = rootPOA.create_request_processing_policy( RequestProcessingPolicyValue.USE_ACTIVE_OBJECT_MAP_ONLY ); tpolicy[2] = rootPOA.create_servant_retention_policy( ServantRetentionPolicyValue.RETAIN); POA tPOA = rootPOA.create_POA("MyTransientPOA", null, tpolicy);
Portable Object Adaptor (POA)は、複数のORB実装で使用できるオブジェクト・アダプタを提供するために設計されていて、異なるベンダーの実装に対応する場合も最低限の書直しで済むようになっています。
POAは、少なくともクライアントの立場からは持続オブジェクトが可能になるようにしています。つまり、サーバーが物理的に何度再起動されても、またはさまざまなオブジェクト実装による実装が行われても、クライアントに関係していればこれらの持続オブジェクトは常に存在し、格納されたデータ値は保守されています。
POAを利用すると、オブジェクトの実装者は、ずっと多くの制御が可能になります。以前は、オブジェクトを実装することは、メソッドの要求に応答して実行されるコードだけの責任でした。今では、それに加えて、オブジェクトの実装者が、オブジェクトの識別、状態、記憶領域、およびライフ・サイクルをもっと制御することができます。
この例では、次のようなポリシー値を設定しています。
LifespanPolicyValue
には、次の値を指定できます。
TRANSIENT
- そのPOAで実装されたオブジェクトは、それらが最初に作成されるPOAインスタンスよりも長く存続することはできません。PERSISTENT
- そのPOAで実装されたオブジェクトは、それらが最初に作成されるプロセスよりも長く存続できます。RequestProcessingPolicyValue
には、次の値を指定できます。
USE_ACTIVE_OBJECT_MAP_ONLY
- オブジェクトIDがアクティブ・オブジェクト・マップ内で見つからない場合、クライアントにOBJECT_NOT_EXIST
例外が返されます。RETAIN
ポリシーも必須です。USE_DEFAULT_SERVANT
- オブジェクトIDがアクティブ・オブジェクト・マップ内で見つからない場合、またはNON_RETAIN
ポリシーが存在する場合、set_servant
オペレーションを使ってデフォルト・サーバントがPOAに登録されていれば、要求はデフォルト・サーバントにディスパッチされます。USE_SERVANT_MANAGER
- オブジェクトIDがアクティブ・オブジェクト・マップ内に見つからない場合か、NON_RETAIN
ポリシーが存在する場合、set_servant_manager
操作を使ってサーバント・マネージャがPOAに登録されているなら、サーバント・マネージャがサーバントを検索するか、例外を発生させます。ServantRetentionPolicyValue
には、次の値を指定できます。
RETAIN
- そのPOAがアクティブなサーバントをActive Object Mapに保存することを示します。POAの作成時にServantRetentionPolicy
が指定されていない場合、デフォルトはRETAIN
となります。NON_RETAIN
- サーバントがPOAによって保存されないことを示します。POAポリシーの詳細は、CORBA/IIOP 2.3.1仕様(http://www.omg.org/cgi-bin/doc?formal/99-10-07)の第11章「Portable Object Adapter」を参照してください。
各POAオブジェクトには、POAManager
オブジェクトが関連付けられています。POAマネージャには、1つまたは複数のPOAオブジェクトを関連付けることができます。POAマネージャは、関連付けられているPOAの処理状態をカプセル化します。このステップでは、POAマネージャを起動します。このステップを実行しないと、POAマネージャはデフォルトではHOLD
状態になっているため、Servant
に対するすべての呼出しがハング・アップしてしまいます。
tPOA.the_POAManager().activate();
main
メソッドでは、リモート・オブジェクト実装のインスタンス(つまりサーバント)を作成する必要があります。たとえば、
HelloImpl helloImpl = new HelloImpl();コンストラクタはリモート・オブジェクトをエクスポートします。これは、リモート・オブジェクトが作成された時点で、そのリモート・オブジェクトは着呼を受け入れる準備ができていることを意味します。
RMI-IIOPテクノロジを使用している実装は、その実装をインタフェースに関連付けるために委譲(「Tieモデル」と呼ばれる)を使用します。上記のように、実装のインスタンスを作成したときは、そのインスタンスをCORBAインタフェースに関連付けるためにTieオブジェクトを作成する必要もあります。次のコード行は、Tieを起動するためのものです(ただし、POAポリシーがUSE_ACTIVE_OBJECT_MAP_ONLY
の場合のみ)。
_HelloImpl_Tie tie = (_HelloImpl_Tie)Util.getTie( helloImpl ); String helloId = "hello"; byte[] id = helloId.getBytes(); tPOA.activate_object_with_id( id, tie );
リモート・オブジェクトがサーバーに登録されたあとは、呼出し側は、オブジェクトを名前で検索し(ネーム・サービスを利用する)、リモート・オブジェクトへの参照を取得してはじめて、そのオブジェクトのメソッドをリモートから呼び出せるようになります。この例では、Object Request Broker Daemon (orbd) (Solaris、LinuxまたはMac OS X用またはWindows用)を使用しています。orbdは、ブートストラップ・サービス、一時ネーム・サービス、持続ネーム・サービスおよびサーバー・マネージャが入っているデーモン・プロセスです。
たとえば、次のコードは、「HelloService」という名前をリモート・オブジェクトへの参照にバインドします。
Context initialNamingContext = new InitialContext(); initialNamingContext.rebind("HelloService", tPOA.create_reference_with_id(id, tie._all_interfaces(tPOA,id)[0]) ); System.out.println("Hello Server: Ready...");
rebind
メソッド呼出しの引数については、次の点に注意してください。
"HelloService"
は、バインドするリモート・オブジェクトの名前を表すjava.lang.String
ですtPOA.create_reference_with_id(id, tie._all_interfaces(tPOA,id)[0]
は、バインドするリモート・オブジェクトのオブジェクトIDですorb.run();
この例のクライアント・アプリケーションは、リモートからsayHello
メソッドを呼び出して、クライアント・アプリケーションが実行されたときに「Hello World!」という文字列を表示します。クライアント・アプリケーションのコードは、次のとおりです。
//HelloClient.java import java.rmi.RemoteException; import java.net.MalformedURLException; import java.rmi.NotBoundException; import javax.rmi.*; import java.util.Vector; import javax.naming.NamingException; import javax.naming.InitialContext; import javax.naming.Context; public class HelloClient { public static void main( String args[] ) { Context ic; Object objref; HelloInterface hi; try { ic = new InitialContext(); } catch (NamingException e) { System.out.println("failed to obtain context" + e); e.printStackTrace(); return; } // STEP 1: Get the Object reference from the Name Service // using JNDI call. try { objref = ic.lookup("HelloService"); System.out.println("Client: Obtained a ref. to Hello server."); } catch (NamingException e) { System.out.println("failed to lookup object reference"); e.printStackTrace(); return; } // STEP 2: Narrow the object reference to the concrete type and // invoke the method. try { hi = (HelloInterface) PortableRemoteObject.narrow( objref, HelloInterface.class); hi.sayHello(); } catch (ClassCastException e) { System.out.println("narrow failed"); e.printStackTrace(); return; } catch( Exception e ) { System.err.println( "Exception " + e + "Caught" ); e.printStackTrace( ); return; } } }
まず、クライアント・アプリケーションは、リモート・オブジェクト実装(「HelloService」として通知されている)への参照を、Java Naming and Directory Interface (JNDI)呼出しを使用してネーム・サービスから取得します。Naming.rebind
メソッドと同様に、Naming.lookup
メソッドは、検索するオブジェクトの名前を表すjava.lang.String
値を引数として取ります。検索したいオブジェクトの名前をNaming.lookup()に提供すると、その名前にバインドされたオブジェクトが返されます。
_HelloImpl_Stub
インスタンスを返すlookup
メソッドは、リモート・オブジェクト(HelloImpl
)のスタブ・インスタンスを受け取り、スタブ・クラス(_HelloImpl_Stub
)をロードするNaming.lookup
は、呼出し側(HelloClient
)にスタブを返すsayHello()
を呼び出して、コマンド行に「It works! Hello World!!」という文字列を表示させる。HelloInterface.java
には、リモート・インタフェースのソース・コードが入っていますHelloImpl.java
には、リモート・オブジェクト実装のソース・コードが入っていますHelloServer.java
には、サーバーのソース・コードが入っていますHelloClient.java
には、クライアント・アプリケーションのソース・コードが入っていますHelloImpl.java
をコンパイルして、rmic
を実行するのに必要な.class
ファイルを作成します。次に、rmic
コンパイラを実行して、スタブとスケルトンを作成します。スタブとは、リモート・オブジェクトのクライアント側のプロキシのことで、RMI-IIOP呼出しをサーバー側のディスパッチャに転送します。続いて、ディスパッチャは、その呼出しを実際のリモート・オブジェクト実装に転送します。最後に、残りの.java
ソース・ファイルをコンパイルして、.class
ファイルを作成します。
このセクションで実行するタスクは次のとおりです。
スタブ・ファイルとスケルトン・ファイルを作成するには、リモート・オブジェクト実装の入ったコンパイル済みクラス・ファイルの完全指定パッケージ名について、rmic
コンパイラを実行する必要があります。この例では、リモート・オブジェクト実装の入ったファイルはHelloImpl.java
です。スタブとスケルトンを生成するためには、まず、次のようにしてHelloImpl.java
をコンパイルする必要があります。
javac -d . -classpath . HelloImpl.java
「-d .
」オプションは、生成されたファイルを、コンパイラを実行しているのと同じディレクトリに置くことを示します。「-classpath .
」オプションは、HelloImpl.java
が依存しているファイルが、このディレクトリ内にあることを示します。
rmic
を使ってスタブおよびスケルトンを生成するrmic
コンパイラを、-poa -iiop
オプションを指定して実行します。rmic -poa -iiop
コマンドは、引数に1つ以上のクラス名をとり、_MyImpl_Tie.class
および_MyInterface_Stub.class
という形式のクラス・ファイルを生成します。この例では、リモート実装ファイルHelloImpl.class
のクラス名を渡します。
rmic
のオプションの詳細は、Solaris、LinuxまたはMac OS X用rmic
のマニュアル・ページまたはMicrosoft Windows用rmic
のマニュアル・ページを参照してください。
HelloImpl
リモート・オブジェクト実装のスタブおよびスケルトンを作成するには、次のようにrmic
を実行します。
rmic -poa -iiop HelloImpl
上記のコマンドによって、次のファイルが作成されます。
_HelloInterface_Stub.class
- クライアント・スタブ_HelloImpl_Tie.class
- サーバー・スケルトンソース・ファイルをコンパイルするには、次のjavac
コマンドを実行します。
javac -d . -classpath . HelloInterface.java HelloServer.java HelloClient.java
このコマンドにより、HelloInterface.class
、HelloServer.class
、およびHelloClient.class
の各クラス・ファイルが作成されます。これらのファイルはそれぞれ、リモート・インタフェース、サーバー、そしてクライアント・アプリケーションです。javac
のオプションの詳細は、Solaris、LinuxまたはMac OS X用javac
のマニュアル・ページまたはMicrosoft Windows用javac
のマニュアル・ページを参照してください。
orbd
)を使用します。これには、一時ネーム・サービスと持続ネーム・サービスの両方が組み込まれており、JDKをダウンロードすれば入手できます。
呼出し側(クライアント、ピア、またはクライアント・アプリケーション)がリモート・オブジェクトのメソッドを呼び出すには、呼出し側はまずリモート・オブジェクトへの参照を取得する必要があります。
リモート・オブジェクトがサーバーに登録されると、呼出し側は、そのオブジェクトを名前によって検索して、リモート・オブジェクトへの参照を取得できます。そうすれば、そのオブジェクトのメソッドをリモートから呼び出せます。
ネーム・サービスを起動するには、コマンド行からorbd
を実行します。このコマンドからは何の出力もありません。通常、バックグラウンドで実行されます。orbd
ツールの詳細は、orbdのマニュアル・ページ(Solaris、LinuxまたはMac OS X用またはWindows用)を参照してください。
この例の場合、Solarisオペレーティング・システムでは次のコマンドを実行します。
orbd -ORBInitialPort 1060&
Microsoft Windowsオペレーティング・システムでは、次のコマンドを実行します。
start orbd -ORBInitialPort 1060
orbd
を実行するポートを指定する必要があります。この例でポートとして1060
が選ばれているのは、Solarisオペレーティング・システムでは、プロセスを1024より下のポートで開始するユーザーはルートになる必要があるからです。
リモート・インタフェースを変更したり、変更または追加されたリモート・インタフェースをリモート・オブジェクトの実装で使用する場合は、必ずサーバーをいったん停止してから再起動する必要があります。そうしないと、ネーム・サービスでバインドされるオブジェクト参照の型が、変更されたクラスと一致しなくなります。
端末ウィンドウをもう1つ開き、この例のソース・ファイルが入っているディレクトリに移ります。クライアントを実行するための下記のコマンドは、読みやすくするために複数行に分けてありますが、実際にコマンドを入力するときには改行を入れないでください。次のコマンドは、HelloServer
サーバーを起動する方法を示しています。もちろん、orbd
ツールを起動するときに1060以外のポートやlocalhost以外のホストを使用した場合には、下記のコマンドの該当する値を、orbd
を起動するときに使用した実際の値で置き換えてください。
java -classpath . -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory -Djava.naming.provider.url=iiop://localhost:1060 HelloServer
java
のオプションの詳細は、Solaris、LinuxまたはMac OS X用java
のマニュアル・ページまたはMicrosoft Windows用java
のマニュアル・ページを参照してください。
出力は、次のようになります。
Hello Server: Ready ...
orbd
ツールを起動するときに1060以外のポートやlocalhost以外のホストを使用した場合には、下記のコマンドの該当する値を、orbd
を起動するときに使用した実際の値で置き換えてください。
java -classpath . -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory -Djava.naming.provider.url=iiop://localhost:1060 HelloClientクライアント・アプリケーションを実行すると、次のような出力が端末ウィンドウまたはコマンド・プロンプト・ウィンドウに表示されます。
Client: Obtained a ref. to Hello server.
サーバー・ウィンドウが次のメッセージを返します。
It works! Hello World!!
これでチュートリアルを終わります。さらに複雑なアプリケーションの作成に進む場合は、次に挙げる情報が役に立ちます。