Java

RMI

RMIとは?

例えば小型デバイスと高性能なPCがあったとします。小型デバイスからデータを送り、PCでそれを処理して小型デバイスに処理内容を返すという仕組みを作りたい場合に、PC上のオブジェクトへの参照を小型デバイスで取得するということができればすごく便利です。

通常オブジェクトは、同じヒープ領域にないと参照ができません。そのため異なるデバイスのオブジェクトを参照するためには、ServerSocketクラスを用いればなんとかできますが、仕組み(コード)がとても複雑になるために難しいです。

ところがなんと!!!

javaには、その複雑になる仕組みがあらかじめ用意されています。

それが、、RMI(Remote Method Invocation)です。

RMIの構造

異なるコンピュータにおいて、オブジェクトは同じヒープ領域にないと参照ができません。なので、ヘルパー(RMIではスタブとスケルトンと言います)というオブジェクトを異なるコンピュータ同士におくことによって、同一のヒープ領域にあるように振る舞うようにします。もちろんヘルパーはClient objectとServer objectはそのものではないです。

RMIアプリケーション(サーバー部分)の作成手順

リモートインターフェースを作成する

リモートインターフェースというのは、クライアントがリモートで呼び出せるメソッドを宣言したインターフェースのことです。このインターフェースは、「サーバーオブジェクト(スタブオブジェクト)への参照」を格納する変数の型として使用できます。スタブとサーバークラスは、このインターフェースを実装します。

import java.rmi.*;

public interface ServiceServer extends Remote {
    Object[] getServiceList() throws RemoteException;
    Service getService(Object serviceKey) throws RemoteException;
}

リモートインターフェースのサブクラス(サーバークラス)を作成する

クライアントが呼び出すリモートメソッドを含む「本物」のサーバークラスを作成します。クライアントが求める処理は最終的には、このクラスのメソッドによってなされることになります。

import java.rmi.*;
import java.util.*;
import java.rmi.server.*;
import java.security.Provider.Service;

public class ServiceServerImpl extends UnicastRemoteObject implements ServiceServer {
    HashMap serviceList;

    public ServiceServerImpl() throws RemoteException {
        setUpServices();
    }

    private void setUpServices() {
        serviceList = new HashMap();
        serviceList.put("Dice Rolling Service", new DiceService());
        // serviceList.put("Day of the Week Service", new DayOfTheWeekService());
        // serviceList.put("Visual Music Service", new MiniMusicService());
    }

    public Object[] getServiceList() {
        System.out.println("in remote");
        return serviceList.keySet().toArray();
    }

    public Service getService(Object serviceKey) throws RemoteException {
        Service theService = (Service) serviceList.get(serviceKey);
        return theService;
    }

    public static void main(String[] args) {
        try {
            Naming.rebind("ServiceServer", new ServiceServerImpl());
        } catch (Exception ex) {
            System.out.println("Remote service is running");
        }

    }
}
class DiceService implements Service {

    JLabel label;
    JComboBox numOfDice;

    public JPanel getGuiPanel() {
        JPanel panel = new JPanel();
        JButton button = new JButton("Roll `em!");
        String[] choices = { "1", "2", "3", "4", "5", "6" };
        numOfDice = new JComboBox(choices);
        label = new JLabel("dice values here");
        button.addActionListener(new RollEmListener());
        panel.add(numOfDice);
        panel.add(button);
        panel.add(label);
        return panel;
    }

    public class RollEmListener implements ActionListener {
        public void actionPerformed(ActionEvent ev) {
            String diceOutput = "";
            String selection = (String) numOfDice.getSelectedItem();
            int numOfDiceToRoll = Integer.parseInt(selection);
            for (int i = 0; i < numOfDiceToRoll; i++) {
                int r = (int) ((Math.random() * 6) + 1);
                diceOutput += (" " + r);
            }
            label.setText(diceOutput);
        }
    }
}

 

スタブとスケルトンをrmicツールを使用して作成する

スタブとスケルトンは、プログラマが自作する必要はありません。(それどころかスタブ、スケルトンを構成するクラスのソースコードさえみる必要がない)いずれもrmicというツールを使って自動的に生成します。

%rmic ServiceServerImpl

RMIレジストリを起動する

RMIレジストリはいわば、「電話帳」みたいなものです。スタブとスケルトンを検索するために使います。

%rmiregistry

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

サーバークラスのクラスファイルを実行すると、サーバークラスがインスタンス化され、RMIレジストリに登録されます。RMIレジストリに登録されたサーバーオブジェクトは、クライアントに利用できるようになります。

%java ServiceServerImpl

RMIアプリケーション(クライアント部分)の作成

クライアント部分においては、同じヒープ上にあるように扱えます。

import java.awt.*;
import javax.swing.*;
import java.rmi.*;
import java.awt.event.*;

public class ServiceBrowser {

    JPanel mainPanel;
    JComboBox serviceList;
    ServiceServer server;

    public void buildGUI() {
        JFrame frame = new JFrame("RMI Browser");
        mainPanel = new JPanel();
        frame.getContentPane().add(BorderLayout.CENTER, mainPanel);
        Object[] services = getServicesList();
        serviceList = new JComboBox(services);
        frame.getContentPane().add(BorderLayout.NORTH, serviceList);
        serviceList.addActionListener(new MyListListener());

        frame.setSize(500, 500);
        frame.setVisible(true);
    }

    void loadService(Object serviceSelection) {
        try {
            Service svc = server.getService(serviceSelection);

            mainPanel.removeAll();
            mainPanel.add(svc.getGuiPanel());
            mainPanel.validate();
            mainPanel.repaint();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    Object[] getServicesList() {
        Object obj = null;
        Object[] services = null;

        try {
            obj = Naming.lookup("rmi://127.0.0.1/ServiceServer");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        server = (ServiceServer) obj;

        try {
            services = server.getServiceList();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return services;
    }

    class MyListListener implements ActionListener {
        public void actionPerformed(ActionEvent ev) {
            Object selection = serviceList.getSelectedItem();
            loadService(selection);
        }
    }

    public static void main(String[] args) {
        new ServiceBrowser().buildGUI();
    }
}
%java ServiceBrowser

 参考サイト

http://ash.jp/java/rmi.html