Java Language
リモートメソッド呼び出し(RMI)
サーチ…
備考
RMIには、クライアント、サーバー、および共有リモート・インタフェースの3つのコンポーネントが必要です。共有リモート・インターフェースは、サーバーが実装しなければならないメソッドを指定することによって、クライアント - サーバー契約を定義します。インタフェースは、メソッドを実装できるようにサーバから見える必要があります。サーバが提供するメソッド(「サービス」)を知るために、インタフェースをクライアントに見えるようにする必要があります。
リモートインタフェースを実装するオブジェクトはすべて、サーバーの役割を果たすことを目的としています。したがって、サーバがクライアント内のメソッドも呼び出すことができるクライアント - サーバ関係は、実際にはサーバ - サーバ関係です。これは、サーバーが「クライアント」をコールバックできるので、 コールバックと呼ばれます 。これを念頭に置いて、そのように機能するサーバーに対して指定クライアントを使用することは許容されます 。
共有リモート・インターフェースは、 Remote
拡張する任意のインターフェースです。サーバーとして機能するオブジェクトは、次の処理を実行します。
-
Remote
を実装するUnicastRemoteObject
を拡張することにより、明示的または暗黙的に共有リモートインタフェースを実装します。 -
UnicastRemoteObject
拡張する場合は暗黙的に、UnicastRemoteObject#exportObject
に渡すことで明示的にエクスポートされUnicastRemoteObject#exportObject
。 - 直接のいずれかを介して、レジストリにバインドさ
Registry
または間接的に通過Naming
。これは、最初の通信を確立するために必要なのは、さらなるスタブがRMIを介して直接渡されるためです。
プロジェクトセットアップでは、クライアントプロジェクトとサーバープロジェクトは完全に無関係ですが、それぞれがビルドパスに共有プロジェクトを指定しています。共有プロジェクトには、リモートインタフェースが含まれています。
クライアント - サーバ:あるJVMのメソッドを別のJVMから呼び出す
共有リモートインタフェース:
package remote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RemoteServer extends Remote {
int stringToInt(String string) throws RemoteException;
}
共有リモートインタフェースを実装しているサーバ:
package server;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import remote.RemoteServer;
public class Server implements RemoteServer {
@Override
public int stringToInt(String string) throws RemoteException {
System.out.println("Server received: \"" + string + "\"");
return Integer.parseInt(string);
}
public static void main(String[] args) {
try {
Registry reg = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
Server server = new Server();
UnicastRemoteObject.exportObject(server, Registry.REGISTRY_PORT);
reg.rebind("ServerName", server);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
サーバー上のメソッドを呼び出すクライアント(リモート):
package client;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import remote.RemoteServer;
public class Client {
static RemoteServer server;
public static void main(String[] args) {
try {
Registry reg = LocateRegistry.getRegistry();
server = (RemoteServer) reg.lookup("ServerName");
} catch (RemoteException | NotBoundException e) {
e.printStackTrace();
}
Client client = new Client();
client.callServer();
}
void callServer() {
try {
int i = server.stringToInt("120");
System.out.println("Client received: " + i);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
出力:
受信したサーバー: "120"
受け取ったクライアント:120
コールバック: "クライアント"のメソッドを呼び出す
概要
この例では、2つのクライアントがサーバーを介して互いに情報を送信します。 1つのクライアントは、第2のクライアントに中継される番号をサーバに送信する。第2のクライアントは、その番号を半分にして、サーバを介して第1のクライアントに送り返す。最初のクライアントも同様です。サーバは、クライアントから返された数値が10未満のときに通信を停止します。サーバからクライアントへの戻り値(文字列表現に変換された数)は、プロセスをバックトラックします。
- ログインサーバーは自身をレジストリにバインドします。
- クライアントはログインサーバーを検索し、その情報で
login
メソッドを呼び出しlogin
。次に:- ログインサーバーはクライアント情報を保管します。これには、コールバックメソッドを持つクライアントのスタブが含まれます。
- ログインサーバーは、サーバースタブ(「接続」または「セッション」)を作成し、クライアントに保管して戻します。これには、
logout
メソッド(この例では使用されていない)を含むメソッドを含むサーバーのスタブが含まれます。
- クライアントは、受信者クライアントの名前と
int
passInt
してサーバーのpassInt
を呼び出します。 - サーバーは、その
int
受信側クライアントのhalf
を呼び出します。これは、サーバーによって停止されるまで、前後の(呼び出しとコールバック)通信を開始します。
共有リモートインタフェース
ログインサーバー:
package callbackRemote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RemoteLogin extends Remote {
RemoteConnection login(String name, RemoteClient client) throws RemoteException;
}
サーバー:
package callbackRemote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RemoteConnection extends Remote {
void logout() throws RemoteException;
String passInt(String name, int i) throws RemoteException;
}
クライアント:
package callbackRemote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RemoteClient extends Remote {
void half(int i) throws RemoteException;
}
実装
ログインサーバー:
package callbackServer;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Map;
import callbackRemote.RemoteClient;
import callbackRemote.RemoteConnection;
import callbackRemote.RemoteLogin;
public class LoginServer implements RemoteLogin {
static Map<String, RemoteClient> clients = new HashMap<>();
@Override
public RemoteConnection login(String name, RemoteClient client) {
Connection connection = new Connection(name, client);
clients.put(name, client);
System.out.println(name + " logged in");
return connection;
}
public static void main(String[] args) {
try {
Registry reg = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
LoginServer server = new LoginServer();
UnicastRemoteObject.exportObject(server, Registry.REGISTRY_PORT);
reg.rebind("LoginServerName", server);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
サーバー:
package callbackServer;
import java.rmi.NoSuchObjectException;
import java.rmi.RemoteException;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.server.Unreferenced;
import callbackRemote.RemoteClient;
import callbackRemote.RemoteConnection;
public class Connection implements RemoteConnection, Unreferenced {
RemoteClient client;
String name;
public Connection(String name, RemoteClient client) {
this.client = client;
this.name = name;
try {
UnicastRemoteObject.exportObject(this, Registry.REGISTRY_PORT);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void unreferenced() {
try {
UnicastRemoteObject.unexportObject(this, true);
} catch (NoSuchObjectException e) {
e.printStackTrace();
}
}
@Override
public void logout() {
try {
UnicastRemoteObject.unexportObject(this, true);
} catch (NoSuchObjectException e) {
e.printStackTrace();
}
}
@Override
public String passInt(String recipient, int i) {
System.out.println("Server received from " + name + ":" + i);
if (i < 10)
return String.valueOf(i);
RemoteClient client = LoginServer.clients.get(recipient);
try {
client.half(i);
} catch (RemoteException e) {
e.printStackTrace();
}
return String.valueOf(i);
}
}
クライアント:
package callbackClient;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import callbackRemote.RemoteClient;
import callbackRemote.RemoteConnection;
import callbackRemote.RemoteLogin;
public class Client implements RemoteClient {
RemoteConnection connection;
String name, target;
Client(String name, String target) {
this.name = name;
this.target = target;
}
public static void main(String[] args) {
Client client = new Client(args[0], args[1]);
try {
Registry reg = LocateRegistry.getRegistry();
RemoteLogin login = (RemoteLogin) reg.lookup("LoginServerName");
UnicastRemoteObject.exportObject(client, Integer.parseInt(args[2]));
client.connection = login.login(client.name, client);
} catch (RemoteException | NotBoundException e) {
e.printStackTrace();
}
if ("Client1".equals(client.name)) {
try {
client.connection.passInt(client.target, 120);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void half(int i) throws RemoteException {
String result = connection.passInt(target, i / 2);
System.out.println(name + " received: \"" + result + "\"");
}
}
例を実行する:
- ログインサーバーを実行します。
- 引数
Client2 Client1 1097
してクライアントを実行します。 - 引数
Client1 Client2 1098
してクライアントを実行します。
3つのJVMがあるため、出力は3つのコンソールに表示されます。ここで彼らは一塊になっています:
Client2がログインしました
Client1がログインしました
クライアント1から受信したサーバー:120
クライアント2から受信したサーバー:60
クライアント1から受信したサーバー:30
クライアント2から受信したサーバー:15
Client1から受信したサーバー:7
受信したClient1:「7」
受信したClient2: "15"
受信したClient1: "30"
受信したClient2: "60"
クライアントとサーバーの実装による単純なRMIの例
これは、5つのJavaクラスと2つのパッケージ( サーバーとクライアント)を持つ単純なRMIの例です 。
サーバーパッケージ
PersonListInterface.java
public interface PersonListInterface extends Remote
{
/**
* This interface is used by both client and server
* @return List of Persons
* @throws RemoteException
*/
ArrayList<String> getPersonList() throws RemoteException;
}
PersonListImplementation.java
public class PersonListImplementation
extends UnicastRemoteObject
implements PersonListInterface
{
private static final long serialVersionUID = 1L;
// standard constructor needs to be available
public PersonListImplementation() throws RemoteException
{}
/**
* Implementation of "PersonListInterface"
* @throws RemoteException
*/
@Override
public ArrayList<String> getPersonList() throws RemoteException
{
ArrayList<String> personList = new ArrayList<String>();
personList.add("Peter Pan");
personList.add("Pippi Langstrumpf");
// add your name here :)
return personList;
}
}
Server.java
public class Server {
/**
* Register servicer to the known public methods
*/
private static void createServer() {
try {
// Register registry with standard port 1099
LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
System.out.println("Server : Registry created.");
// Register PersonList to registry
Naming.rebind("PersonList", new PersonListImplementation());
System.out.println("Server : PersonList registered");
} catch (final IOException e) {
e.printStackTrace();
}
}
public static void main(final String[] args) {
createServer();
}
}
クライアントパッケージ
PersonListLocal.java
public class PersonListLocal {
private static PersonListLocal instance;
private PersonListInterface personList;
/**
* Create a singleton instance
*/
private PersonListLocal() {
try {
// Lookup to the local running server with port 1099
final Registry registry = LocateRegistry.getRegistry("localhost",
Registry.REGISTRY_PORT);
// Lookup to the registered "PersonList"
personList = (PersonListInterface) registry.lookup("PersonList");
} catch (final RemoteException e) {
e.printStackTrace();
} catch (final NotBoundException e) {
e.printStackTrace();
}
}
public static PersonListLocal getInstance() {
if (instance == null) {
instance = new PersonListLocal();
}
return instance;
}
/**
* Returns the servers PersonList
*/
public ArrayList<String> getPersonList() {
if (instance != null) {
try {
return personList.getPersonList();
} catch (final RemoteException e) {
e.printStackTrace();
}
}
return new ArrayList<>();
}
}
PersonTest.java
public class PersonTest
{
public static void main(String[] args)
{
// get (local) PersonList
ArrayList<String> personList = PersonListLocal.getInstance().getPersonList();
// print all persons
for(String person : personList)
{
System.out.println(person);
}
}
}
アプリケーションのテスト
- Server.javaのmainメソッドを起動します。出力:
Server : Registry created.
Server : PersonList registered
- PersonTest.javaのmainメソッドを起動します。出力:
Peter Pan
Pippi Langstrumpf