Java-rmi-quick-guide
Java RMI-はじめに
RMIは Remote Method Invocation の略です。 これは、あるシステム(JVM)に存在するオブジェクトが別のJVMで実行されているオブジェクトにアクセスしたり呼び出したりできるようにするメカニズムです。
RMIは、分散アプリケーションの構築に使用されます。 Javaプログラム間のリモート通信を提供します。 パッケージ java.rmi で提供されます。
RMIアプリケーションのアーキテクチャ
RMIアプリケーションでは、* serverプログラム*(サーバーに常駐)と* clientプログラム*(クライアントに常駐)の2つのプログラムを作成します。
- サーバープログラム内で、リモートオブジェクトが作成され、そのオブジェクトの参照がクライアントで利用可能になります(レジストリを使用)。
- クライアントプログラムは、サーバー上のリモートオブジェクトを要求し、そのメソッドを呼び出そうとします。
次の図は、RMIアプリケーションのアーキテクチャを示しています。
このアーキテクチャのコンポーネントについて説明しましょう。
- トランスポート層-この層はクライアントとサーバーを接続します。 既存の接続を管理し、新しい接続を設定します。
- スタブ-スタブは、クライアントでのリモートオブジェクトの表現(プロキシ)です。 クライアントシステムに存在します。クライアントプログラムのゲートウェイとして機能します。
- スケルトン-これはサーバー側にあるオブジェクトです。 *スタブ*はこのスケルトンと通信して、リモートオブジェクトにリクエストを渡します。
- * RRL(Remote Reference Layer)*-クライアントがリモートオブジェクトに対して行った参照を管理するレイヤーです。
RMIアプリケーションの動作
次のポイントは、RMIアプリケーションがどのように機能するかをまとめています-
- クライアントがリモートオブジェクトを呼び出すと、スタブが受信し、最終的にこのリクエストをRRLに渡します。
- クライアント側のRRLは、リクエストを受信すると、オブジェクト remoteRef の* invoke()*というメソッドを呼び出します。 サーバー側のRRLに要求を渡します。
- サーバー側のRRLはリクエストをSkeleton(サーバー上のプロキシ)に渡し、Skeletonは最終的にサーバー上の必要なオブジェクトを呼び出します。
- 結果はクライアントに戻されます。
マーシャリングとアンマーシャリング
クライアントがリモートオブジェクトのパラメーターを受け入れるメソッドを呼び出すたびに、パラメーターはネットワークに送信される前にメッセージにバンドルされます。 これらのパラメーターは、プリミティブ型またはオブジェクトの場合があります。 プリミティブ型の場合、パラメーターがまとめられ、ヘッダーが付加されます。 パラメータがオブジェクトの場合、それらはシリアル化されます。 このプロセスは*マーシャリング*として知られています。
サーバー側では、パックされたパラメーターがアンバンドルされ、必要なメソッドが呼び出されます。 このプロセスは「アンマーシャリング」として知られています。
RMIレジストリ
RMIレジストリは、すべてのサーバーオブジェクトが配置される名前空間です。 サーバーがオブジェクトを作成するたびに、このオブジェクトをRMIregistryに登録します(* bind()または reBind()*メソッドを使用)。 これらは、*バインド名*と呼ばれる一意の名前を使用して登録されます。
リモートオブジェクトを呼び出すには、クライアントはそのオブジェクトの参照を必要とします。 その時点で、クライアントはバインド名を使用して(* lookup()*メソッドを使用して)レジストリからオブジェクトをフェッチします。
次の図は、プロセス全体を説明します-
RMIの目標
RMIの目標は次のとおりです-
- アプリケーションの複雑さを最小限に抑えるため。
- 型の安全性を保つため。
- 分散ガベージコレクション。
- ローカルオブジェクトとリモートオブジェクトの操作の違いを最小限に抑えます。
Java RMIアプリケーション
RMI Javaアプリケーションを作成するには、以下に示す手順に従う必要があります-
- リモートインターフェイスを定義する
- 実装クラス(リモートオブジェクト)を開発する
- サーバープログラムを開発する
- クライアントプログラムを開発する
- アプリケーションをコンパイルする
- アプリケーションを実行する
リモートインターフェイスの定義
リモートインターフェイスは、特定のリモートオブジェクトのすべてのメソッドの説明を提供します。 クライアントはこのリモートインターフェイスと通信します。
リモートインターフェイスを作成するには-
- パッケージに属する事前定義されたインターフェース Remote を拡張するインターフェースを作成します。
- このインターフェイスでクライアントが呼び出すことができるすべてのビジネスメソッドを宣言します。
- リモート呼び出し中にネットワークの問題が発生する可能性があるため、 RemoteException という名前の例外が発生する場合があります。投げて。
以下は、リモートインターフェイスの例です。 ここでは、 Hello という名前のインターフェイスを定義し、* printMsg()*というメソッドを持っています。
import java.rmi.Remote;
import java.rmi.RemoteException;
//Creating Remote interface for our application
public interface Hello extends Remote {
void printMsg() throws RemoteException;
}
実装クラスの開発(リモートオブジェクト)
前の手順で作成したリモートインターフェイスを実装する必要があります。 (実装クラスを個別に記述するか、サーバープログラムにこのインターフェイスを直接実装させることができます。)
実装クラスを開発するには-
- 前の手順で作成したインターフェイスを実装します。
- リモートインターフェイスのすべての抽象メソッドに実装を提供します。
以下は実装クラスです。 ここでは、 ImplExample という名前のクラスを作成し、前の手順で作成したインターフェイス Hello を実装し、メッセージを出力するこのメソッドに body を提供しました。
//Implementing the remote interface
public class ImplExample implements Hello {
//Implementing the interface method
public void printMsg() {
System.out.println("This is an example RMI program");
}
}
サーバープログラムの開発
RMIサーバープログラムは、リモートインターフェイスを実装するか、実装クラスを拡張する必要があります。 ここで、リモートオブジェクトを作成し、それを RMIregistry にバインドする必要があります。
サーバープログラムを開発するには-
- リモートオブジェクトを呼び出すクライアントクラスを作成します。
- 以下に示すように実装クラスをインスタンス化することにより、リモートオブジェクトを作成します。
- パッケージ java.rmi.server に属する UnicastRemoteObject という名前のクラスのメソッド* exportObject()*を使用して、リモートオブジェクトをエクスポートします。
- パッケージ java.rmi.registry に属する LocateRegistry クラスの* getRegistry()*メソッドを使用して、RMIレジストリを取得します。
- Registry という名前のクラスの* bind()*メソッドを使用して、作成されたリモートオブジェクトをレジストリにバインドします。 このメソッドには、バインド名とエクスポートされたオブジェクトを表す文字列をパラメーターとして渡します。
以下は、RMIサーバープログラムの例です。
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Server extends ImplExample {
public Server() {}
public static void main(String args[]) {
try {
//Instantiating the implementation class
ImplExample obj = new ImplExample();
//Exporting the object of implementation class
//(here we are exporting the remote object to the stub)
Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);
//Binding the remote object (stub) in the registry
Registry registry = LocateRegistry.getRegistry();
registry.bind("Hello", stub);
System.err.println("Server ready");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
}
クライアントプログラムの開発
その中にクライアントプログラムを記述し、リモートオブジェクトをフェッチし、このオブジェクトを使用して必要なメソッドを呼び出します。
クライアントプログラムを開発するには-
- リモートオブジェクトを呼び出すつもりのクライアントクラスを作成します。
- パッケージ java.rmi.registry に属する LocateRegistry クラスの* getRegistry()*メソッドを使用して、RMIレジストリを取得します。
- パッケージ java.rmi.registry に属するクラス Registry のメソッド* lookup()*を使用して、レジストリからオブジェクトを取得します。 +このメソッドには、バインド名を表す文字列値をパラメーターとして渡す必要があります。 これにより、リモートオブジェクトが返されます。
- lookup()は、リモートタイプのオブジェクトを返し、それをHelloタイプにダウンキャストします。
- 最後に、取得したリモートオブジェクトを使用して必要なメソッドを呼び出します。
次に、RMIクライアントプログラムの例を示します。
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Client {
private Client() {}
public static void main(String[] args) {
try {
//Getting the registry
Registry registry = LocateRegistry.getRegistry(null);
//Looking up the registry for the remote object
Hello stub = (Hello) registry.lookup("Hello");
//Calling the remote method using the obtained object
stub.printMsg();
//System.out.println("Remote method invoked");
} catch (Exception e) {
System.err.println("Client exception: " + e.toString());
e.printStackTrace();
}
}
}
アプリケーションのコンパイル
アプリケーションをコンパイルするには-
- リモートインターフェイスをコンパイルします。
- 実装クラスをコンパイルします。
- サーバープログラムをコンパイルします。 *クライアントプログラムをコンパイルします。
Or,
すべてのプログラムを保存したフォルダーを開き、以下に示すようにすべてのJavaファイルをコンパイルします。
Javac* .java
アプリケーションの実行
ステップ1 *-次のコマンドを使用して *rmi レジストリを起動します。
start rmiregistry
これにより、以下に示すように、別のウィンドウで rmi レジストリが開始されます。
- ステップ2 *-以下に示すようにサーバークラスファイルを実行します。
Java Server
- ステップ3 *-以下に示すようにクライアントクラスファイルを実行します。
java Client
検証-クライアントを起動するとすぐに、サーバーに次の出力が表示されます。
Java RMI-GUIアプリケーション
前の章では、サンプルのRMIアプリケーションを作成しました。 この章では、クライアントがGUIウィンドウ(JavaFX)を表示するメソッドを呼び出すRMIアプリケーションを作成する方法について説明します。
リモートインターフェイスの定義
ここでは、 Hello という名前のリモートインターフェイスと、* animation()*という名前のメソッドを定義しています。
import java.rmi.Remote;
import java.rmi.RemoteException;
//Creating Remote interface for our application
public interface Hello extends Remote {
void animation() throws RemoteException;
}
実装クラスの開発
このアプリケーションの実装クラス(リモートオブジェクト)では、JavaFXを使用して、GUIコンテンツを表示するウィンドウを作成しようとしています。
import javafx.animation.RotateTransition;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;
//Implementing the remote interface
public class FxSample extends Application implements Hello {
@Override
public void start(Stage stage) {
//Drawing a Box
Box box = new Box();
//Setting the properties of the Box
box.setWidth(150.0);
box.setHeight(150.0);
box.setDepth(100.0);
//Setting the position of the box
box.setTranslateX(350);
box.setTranslateY(150);
box.setTranslateZ(50);
//Setting the text
Text text = new Text(
"Type any letter to rotate the box, and click on the box to stop the rotation");
//Setting the font of the text
text.setFont(Font.font(null, FontWeight.BOLD, 15));
//Setting the color of the text
text.setFill(Color.CRIMSON);
//Setting the position of the text
text.setX(20);
text.setY(50);
//Setting the material of the box
PhongMaterial material = new PhongMaterial();
material.setDiffuseColor(Color.DARKSLATEBLUE);
//Setting the diffuse color material to box
box.setMaterial(material);
//Setting the rotation animation to the box
RotateTransition rotateTransition = new RotateTransition();
//Setting the duration for the transition
rotateTransition.setDuration(Duration.millis(1000));
//Setting the node for the transition
rotateTransition.setNode(box);
//Setting the axis of the rotation
rotateTransition.setAxis(Rotate.Y_AXIS);
//Setting the angle of the rotation
rotateTransition.setByAngle(360);
//Setting the cycle count for the transition
rotateTransition.setCycleCount(50);
//Setting auto reverse value to false
rotateTransition.setAutoReverse(false);
//Creating a text filed
TextField textField = new TextField();
//Setting the position of the text field
textField.setLayoutX(50);
textField.setLayoutY(100);
//Handling the key typed event
EventHandler<KeyEvent> eventHandlerTextField = new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event) {
//Playing the animation
rotateTransition.play();
}
};
//Adding an event handler to the text feld
textField.addEventHandler(KeyEvent.KEY_TYPED, eventHandlerTextField);
//Handling the mouse clicked event(on box)
EventHandler<javafx.scene.input.MouseEvent> eventHandlerBox =
new EventHandler<javafx.scene.input.MouseEvent>() {
@Override
public void handle(javafx.scene.input.MouseEvent e) {
rotateTransition.stop();
}
};
//Adding the event handler to the box
box.addEventHandler(javafx.scene.input.MouseEvent.MOUSE_CLICKED, eventHandlerBox);
//Creating a Group object
Group root = new Group(box, textField, text);
//Creating a scene object
Scene scene = new Scene(root, 600, 300);
//Setting camera
PerspectiveCamera camera = new PerspectiveCamera(false);
camera.setTranslateX(0);
camera.setTranslateY(0);
camera.setTranslateZ(0);
scene.setCamera(camera);
//Setting title to the Stage
stage.setTitle("Event Handlers Example");
//Adding scene to the stage
stage.setScene(scene);
//Displaying the contents of the stage
stage.show();
}
//Implementing the interface method
public void animation() {
launch();
}
}
サーバープログラム
RMIサーバープログラムは、リモートインターフェイスを実装するか、実装クラスを拡張する必要があります。 ここで、リモートオブジェクトを作成し、それを RMIregistry にバインドする必要があります。
以下は、このアプリケーションのサーバープログラムです。 ここでは、上記で作成したクラスを拡張し、リモートオブジェクトを作成し、バインド名 hello でRMIレジストリに登録します。
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Server extends FxSample {
public Server() {}
public static void main(String args[]) {
try {
//Instantiating the implementation class
FxSample obj = new FxSample();
//Exporting the object of implementation class
//(here we are exporting the remote object to the stub)
Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);
//Binding the remote object (stub) in the registry
Registry registry = LocateRegistry.getRegistry();
registry.bind("Hello", stub);
System.err.println("Server ready");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
}
クライアントプログラム
以下は、このアプリケーションのクライアントプログラムです。 ここでは、リモートオブジェクトを取得し、* animation()*という名前のメソッドを呼び出しています。
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Client {
private Client() {}
public static void main(String[] args) {
try {
//Getting the registry
Registry registry = LocateRegistry.getRegistry(null);
//Looking up the registry for the remote object
Hello stub = (Hello) registry.lookup("Hello");
//Calling the remote method using the obtained object
stub.animation();
System.out.println("Remote method invoked");
} catch (Exception e) {
System.err.println("Client exception: " + e.toString());
e.printStackTrace();
}
}
}
サンプルを実行する手順
RMIの例を実行する手順は次のとおりです。
- ステップ1 *-すべてのプログラムを保存したフォルダーを開き、以下に示すようにすべてのJavaファイルをコンパイルします。
Javac *.java
ステップ2 *-次のコマンドを使用して *rmi レジストリを起動します。
start rmiregistry
これにより、以下に示すように、別のウィンドウで rmi レジストリが開始されます。
- ステップ3 *-以下に示すようにサーバークラスファイルを実行します。
Java Server
- ステップ4 *-以下に示すようにクライアントクラスファイルを実行します。
java Client
検証-クライアントを起動するとすぐに、サーバーに次の出力が表示されます。
Java RMI-データベースアプリケーション
前の章では、クライアントがGUIウィンドウ(JavaFX)を表示するメソッドを呼び出すサンプルRMIアプリケーションを作成しました。
この章では、クライアントプログラムがサーバー上にあるMySQLデータベースのテーブルのレコードを取得する方法を確認する例を取り上げます。
以下に示すように、データベース details に student_data という名前のテーブルがあると仮定します。
+----+--------+--------+------------+---------------------+
| ID | NAME | BRANCH | PERCENTAGE | EMAIL |
+----+--------+--------+------------+---------------------+
| 1 | Ram | IT | 85 | [email protected] |
| 2 | Rahim | EEE | 95 | [email protected] |
| 3 | Robert | ECE | 90 | [email protected] |
+----+--------+--------+------------+---------------------+
ユーザーの名前が myuser で、パスワードが password であると仮定します。
学生クラスの作成
以下に示すように、 setter および getter メソッドで Student クラスを作成します。
public class Student implements java.io.Serializable {
private int id, percent;
private String name, branch, email;
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getBranch() {
return branch;
}
public int getPercent() {
return percent;
}
public String getEmail() {
return email;
}
public void setID(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setBranch(String branch) {
this.branch = branch;
}
public void setPercent(int percent) {
this.percent = percent;
}
public void setEmail(String email) {
this.email = email;
}
}
リモートインターフェイスの定義
リモートインターフェイスを定義します。 ここでは、* getStudents()という名前のメソッドを含む *Hello という名前のリモートインターフェイスを定義しています。 このメソッドは、クラス Student のオブジェクトを含むリストを返します。
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.*;
//Creating Remote interface for our application
public interface Hello extends Remote {
public List<Student> getStudents() throws Exception;
}
実装クラスの開発
クラスを作成し、上記で作成した* interface。*を実装します
ここでは、 Remote interface の* getStudents()メソッドを実装しています。 このメソッドを呼び出すと、 *student_data という名前のテーブルのレコードが取得されます。 setterメソッドを使用してこれらの値をStudentクラスに設定し、リストオブジェクトに追加して、そのリストを返します。
import java.sql.*;
import java.util.*;
//Implementing the remote interface
public class ImplExample implements Hello {
//Implementing the interface method
public List<Student> getStudents() throws Exception {
List<Student> list = new ArrayList<Student>();
//JDBC driver name and database URL
String JDBC_DRIVER = "com.mysql.jdbc.Driver";
String DB_URL = "jdbc:mysql://localhost:3306/details";
//Database credentials
String USER = "myuser";
String PASS = "password";
Connection conn = null;
Statement stmt = null;
//Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");
//Open a connection
System.out.println("Connecting to a selected database...");
conn = DriverManager.getConnection(DB_URL, USER, PASS);
System.out.println("Connected database successfully...");
//Execute a query
System.out.println("Creating statement...");
stmt = conn.createStatement();
String sql = "SELECT * FROM student_data";
ResultSet rs = stmt.executeQuery(sql);
//Extract data from result set
while(rs.next()) {
//Retrieve by column name
int id = rs.getInt("id");
String name = rs.getString("name");
String branch = rs.getString("branch");
int percent = rs.getInt("percentage");
String email = rs.getString("email");
//Setting the values
Student student = new Student();
student.setID(id);
student.setName(name);
student.setBranch(branch);
student.setPercent(percent);
student.setEmail(email);
list.add(student);
}
rs.close();
return list;
}
}
サーバープログラム
RMIサーバープログラムは、リモートインターフェイスを実装するか、実装クラスを拡張する必要があります。 ここで、リモートオブジェクトを作成し、それを* RMIレジストリ*にバインドする必要があります。
以下は、このアプリケーションのサーバープログラムです。 ここでは、上記で作成したクラスを拡張し、リモートオブジェクトを作成し、バインド名 hello でRMIレジストリに登録します。
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Server extends ImplExample {
public Server() {}
public static void main(String args[]) {
try {
//Instantiating the implementation class
ImplExample obj = new ImplExample();
//Exporting the object of implementation class (
here we are exporting the remote object to the stub)
Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);
//Binding the remote object (stub) in the registry
Registry registry = LocateRegistry.getRegistry();
registry.bind("Hello", stub);
System.err.println("Server ready");
} catch (Exception e) {
System.err.println("Server exception: " + e.toString());
e.printStackTrace();
}
}
}
クライアントプログラム
以下は、このアプリケーションのクライアントプログラムです。 ここでは、リモートオブジェクトを取得し、* getStudents()*という名前のメソッドを呼び出しています。 リストオブジェクトからテーブルのレコードを取得して表示します。
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.*;
public class Client {
private Client() {}
public static void main(String[] args)throws Exception {
try {
//Getting the registry
Registry registry = LocateRegistry.getRegistry(null);
//Looking up the registry for the remote object
Hello stub = (Hello) registry.lookup("Hello");
//Calling the remote method using the obtained object
List<Student> list = (List)stub.getStudents();
for (Student s:list)v {
//System.out.println("bc "+s.getBranch());
System.out.println("ID: " + s.getId());
System.out.println("name: " + s.getName());
System.out.println("branch: " + s.getBranch());
System.out.println("percent: " + s.getPercent());
System.out.println("email: " + s.getEmail());
}
//System.out.println(list);
} catch (Exception e) {
System.err.println("Client exception: " + e.toString());
e.printStackTrace();
}
}
}
サンプルを実行する手順
RMIの例を実行する手順は次のとおりです。
- ステップ1 *-すべてのプログラムを保存したフォルダーを開き、以下に示すようにすべてのJavaファイルをコンパイルします。
Javac *.java
ステップ2 *-次のコマンドを使用して *rmi レジストリを起動します。
start rmiregistry
これにより、以下に示すように、別のウィンドウで rmi レジストリが開始されます。
- ステップ3 *-以下に示すようにサーバークラスファイルを実行します。
Java Server
- ステップ4 *-以下に示すようにクライアントクラスファイルを実行します。
java Client