Recherche…


Remarques

RMI nécessite 3 composants: client, serveur et une interface distante partagée. L'interface distante partagée définit le contrat client-serveur en spécifiant les méthodes qu'un serveur doit implémenter. L'interface doit être visible pour le serveur afin qu'il puisse implémenter les méthodes; l'interface doit être visible pour le client afin qu'il connaisse les méthodes ("services") fournies par le serveur.
Tout objet implémentant une interface distante est destiné à jouer le rôle de serveur. En tant que telle, une relation client-serveur dans laquelle le serveur peut également invoquer des méthodes dans le client est en fait une relation serveur-serveur. Ceci est appelé callback puisque le serveur peut rappeler le "client". Dans cette optique, il est acceptable d'utiliser la désignation client pour les serveurs qui fonctionnent comme tels.

L'interface distante partagée est toute interface d'extension Remote . Un objet qui fonctionne comme un serveur subit ce qui suit:

  1. Implémente l'interface distante partagée, explicitement ou implicitement, en étendant UnicastRemoteObject qui implémente Remote .
  2. Exporté, soit implicitement s'il étend UnicastRemoteObject , soit explicitement en étant transmis à UnicastRemoteObject#exportObject .
  3. Lié à un registre, soit directement par le biais du Registry soit indirectement via Naming . Cela n'est nécessaire que pour établir la communication initiale, car d'autres tronçons peuvent être transmis directement via RMI.

Dans la configuration du projet, les projets client et serveur sont totalement indépendants, mais chacun spécifie un projet partagé dans son chemin de génération. Le projet partagé contient les interfaces distantes.

Client-Server: appel de méthodes dans une JVM à partir d'une autre

L'interface distante partagée:

package remote;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RemoteServer extends Remote {

    int stringToInt(String string) throws RemoteException;
}

Le serveur implémentant l'interface distante partagée:

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();
        }
    }
}

Le client appelant une méthode sur le serveur (à distance):

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();
        }
    }
}

Sortie:

Serveur reçu: "120"
Client reçu: 120

Callback: invoquer des méthodes sur un "client"

Vue d'ensemble

Dans cet exemple, 2 clients envoient des informations entre eux via un serveur. Un client envoie au serveur un numéro qui est transmis au second client. Le second client divise par deux le nombre et le renvoie au premier client via le serveur. Le premier client fait la même chose. Le serveur arrête la communication lorsque le nombre renvoyé par l'un des clients est inférieur à 10. La valeur de retour du serveur aux clients (le numéro converti en représentation sous forme de chaîne) fait ensuite un retour en arrière.

  1. Un serveur de connexion se lie à un registre.
  2. Un client recherche le serveur de connexion et appelle la méthode de login avec ses informations. Alors:
    • Le serveur de connexion stocke les informations client. Il inclut le stub du client avec les méthodes de rappel.
    • Le serveur de connexion crée et renvoie un stub de serveur ("connexion" ou "session") au client à stocker. Il inclut le stub du serveur avec ses méthodes incluant une méthode de logout (inutilisée dans cet exemple).
  3. Un client appelle le passInt du serveur avec le nom du client destinataire et un int .
  4. Le serveur appelle la half sur le client destinataire avec cet int . Cela déclenche une communication de va-et-vient (appels et rappels) jusqu'à son arrêt par le serveur.

Les interfaces distantes partagées

Le serveur de connexion:

package callbackRemote;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RemoteLogin extends Remote {

    RemoteConnection login(String name, RemoteClient client) throws RemoteException;
}

Le serveur:

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;
}

Le client:

package callbackRemote;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RemoteClient extends Remote {

    void half(int i) throws RemoteException;
}

Les implémentations

Le serveur de connexion:

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();
        }
    }
}

Le serveur:

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);
    }
}

Le client:

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 + "\"");
    }
}

Exécuter l'exemple:

  1. Exécutez le serveur de connexion.
  2. Exécutez un client avec les arguments Client2 Client1 1097 .
  3. Exécutez un client avec les arguments Client1 Client2 1098 .

Les sorties apparaîtront dans 3 consoles puisqu'il y a 3 JVM. ici ils sont regroupés:

Client2 connecté
Client1 connecté
Serveur reçu de Client1: 120
Serveur reçu de Client2: 60
Serveur reçu de Client1: 30
Serveur reçu de Client2: 15
Serveur reçu de Client1: 7
Client1 reçu: "7"
Client2 reçu: "15"
Client1 reçu: "30"
Client2 reçu: "60"

Exemple simple de RMI avec implémentation client et serveur

Ceci est un exemple simple de RMI avec cinq classes Java et deux packages, serveur et client .

Package de serveur

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();
    }
}

Package client

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);
        }
    }
}

Testez votre application

  • Démarrez la méthode principale de Server.java. Sortie:
Server : Registry created.
Server : PersonList registered
  • Commencez la méthode principale de PersonTest.java. Sortie:
Peter Pan
Pippi Langstrumpf


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow