Netzwerkkommunikation über Java RMI

Mit Hilfe von RMI (Remote Method Invocation) besteht in der Java Softwareetnwiklung die Möglichkeit, Programme über unterschiedliche Rechner zu verteilen. Für die Nutzung von RMI ist es lediglich notwendig, auf jedem dieser Rechner eine Java Virtuelle Maschine (JVM) auszuführen. Bei der Remote Method Invocation werden entfernte Java Objekte aufgerufen, welche beispielsweise von einem Server erzeugt und verwaltet werden. Die Umsetzung von RMI spiegelt im Ergebnis ein Client-Server Modell wieder.
In diesem Tutorial wird grundlegend gezeigt, wie so ein Client-Server Modell aufgebaut werden kann.

Eclipse Projekt anlegen

Zu Beginn wird ein neues Eclipse Projekt mit dem Namen de.michel.network.rmi angelegt. Für den späteren Verlauf werden zwei Packages mit dem Namen client und server zusätzlich erstellt.

Server Package

Im ersten Schritt werden die Serverfunktionalitäten erstellt. Als Beispiel soll vom Client eine Methode aufgerufen werden, die eine Liste von Personen ausgibt. Hierzu schreiben wir das passende Interface auf dem Server.

PersonListInterface.java

package server;

import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.ArrayList;

public interface PersonListInterface extends Remote
{
	/**
	 * Liefert die Personenliste in einer ArrayList.
	 * Diese Interface wird sowohl vom Server
	 * als auch vom Client angesprochen
	 */
	public ArrayList<String> getPersonList() throws RemoteException;
}

Das wohl wichtigste an diesem Interface ist die Erweiterung von Remote. Schnittstellen mit der Remote Erweiterung geben an, das diese von entfernten Methoden aufgerufen werden können.

PersonListImplementation.java

Dies ist eine einfach Implementierung des zuvor angelegten Interfaces.

package server;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;

public class PersonListImplementation 
	extends UnicastRemoteObject 
	implements PersonListInterface
{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	// Standardkonstruktor muss vorhanden sein
	public PersonListImplementation() throws RemoteException
	{}

	/**
	 * Implementierung des "PersonListInterface"
	 * Clientanfragen greifen über das Interface auf
	 *  diese (entfernte) Methode zu
	 */
	@Override
	public ArrayList<String> getPersonList() throws RemoteException
	{
		ArrayList<String> personList = new ArrayList<String>();
		
		personList.add("Simon Michel");
		personList.add("Peter Pan");
		personList.add("Pippi Langstrumpf");
		
		return personList;
	}
}

Auch in dieser Klasse ist zu beachten, das diese von UnicastRemoteObject erweitert wird. Diese Erweiterung gibt an, das die vorhandenen Objekte remotefähig sind.

Server.java

Zu guter Letzt muss der Server noch lokal erstellt werden.

package server;

import java.io.IOException;
import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Server
{

	/**
	 * Registriert den Server mit dem ihn 
	 * bekannten öffentlichen Methoden
	 */
	private static void createServer()
	{
		try
		{
			// Namensdienst (Registry) mit dem Standardport 1099 registrieren
			LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
			System.out.println("Server : Registry wurde erzeugt.");

			// Personenliste wird in der Registry unter 
			// dem eindeutigen Namen "PersonList" angemeldet
			Naming.rebind("PersonList", new PersonListImplementation());
			System.out.println("Server : Personenliste registriert");

		} catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	public static void main(String[] args)
	{
		createServer();
	}
}

Mit Hilfe von Naming.rebind(..) ist es möglich, Implementierungen unter einem bestimmten Namen in der registry abzulegen. Wird die main() gestartet, gibt die Eclipse Console folgendes aus:

Server : Registry wurde erzeugt.
Server : Personenliste registriert

Die Registrierung ist erfolgreich abgeschlossen und die Personenliste wurde registriert.

Client Package

Im Folgendem soll der Client die eben registrierte Personenliste aufrufen.

PersonListLocal.java

Als erstes wird eine Basisklasse benötigt, welche nach der Personenliste im Netzwerk Ausschau hält.

package client;

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.ArrayList;

import server.PersonListInterface;

public class PersonListLocal
{
	private static PersonListLocal instance;
	private PersonListInterface personList;

	/**
	 * mit Hilfe des Singleton Pattern wird 
	 * lediglich eine Instanz dieser Klasse erzeugt
	 */
	public static PersonListLocal getInstance()
	{
		if (instance == null)
		{
			instance = new PersonListLocal();
		}

		return instance;
	}

	private PersonListLocal()
	{
		try
		{
			// wir schauen uns nach dem lokal laufenden
			// Server mit dem Standardport 1099 um
			// da der Server lokal läuft, würde an 
			// dieser Stelle auch getRegistry() ausreichen
			Registry registry = LocateRegistry.getRegistry("localhost",
					Registry.REGISTRY_PORT);

			// die Personenliste wird über ihren eindeutigen
			// Namen aus der Registry abgerufen
			personList = (PersonListInterface) registry.lookup("PersonList");
		} catch (RemoteException e)
		{
			e.printStackTrace();
		} catch (NotBoundException e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * Liefert die auf dem Server befindliche Personenliste
	 */
	public ArrayList<String> getPersonList()
	{
		if (instance != null)
		{
			try
			{
				// über das Interface wird die Personenliste von dem Server geladen
				return personList.getPersonList();
			} catch (RemoteException e)
			{
				e.printStackTrace();
			}
		}

		// für den (unwahrscheinlichen) Fall das keine Instanz
		// existiert, wird eine leere Liste geliefert
		return new ArrayList<String>();
	}
}

PersonTest.java

Schlussendlich kann der Server Methodenaufruf getestet werden.

package client;

import java.util.ArrayList;

public class PersonTest
{
	public static void main(String[] args)
	{
		// wir holen uns die (lokale) Personenliste
		ArrayList<String> personList = PersonListLocal.getInstance().getPersonList();
		
		// es werden alle Personen ausgegeben
		for(String person : personList)
		{
			System.out.println(person);
		}
	}
}

Die Eclipse Consolse gibt folgendes aus:

Simon Michel
Peter Pan
Pippi Langstrumpf

Diese klein gehaltene Tutorial hat gezeigt, wie entfernte Methoden über ein Netzwerk angesprochen werden können.