# RMI - Ein Objekt für Alle?



## Markus09 (14. Mrz 2006)

Hallo!

Ich gerade dabei eine RMI Anwendung zu schreiben. Dabei habe ich mir gedacht, dass es doch möglich sein muss ein faches "MessageBoard" zu entwickeln. D.h. der RMI Server speichert alle Daten und die Clients stellen sie dar und können selbst Daten hinzufügen.

Das ganze habe ich für einen Client schon geschafft. Blos wie stelle ich es an, dass beide Clients die gleichen Daten haben?

Ich hab etwas mit UnicastRemoteObject probiert, aber es wollte nicht funktionieren.

Weiß hier jemand eine Lösung?

Markus


Der Sourcecode:


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

public interface MsgBoardInterface extends Remote
{
	public ArrayList getMessages()	throws RemoteException;
	public void addMessage(String msg) throws RemoteException;
	
	public void addListener(MsgBoardListener mbl) throws RemoteException;
	public void removeListener(MsgBoardListener mbl) throws RemoteException;
}
```


```
public interface MsgBoardListener
{
	public void msgAdded(String msg);
}
```


```
import java.util.ArrayList;
import java.io.Serializable;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;

public class MsgBoardObject extends UnicastRemoteObject implements MsgBoardInterface, Serializable
{
	private ArrayList<String> al;
	
	private ArrayList<MsgBoardListener> alListeners;
	
	MsgBoardObject() throws RemoteException
	{
		try
		{
			al = new ArrayList<String>();
			alListeners = new ArrayList<MsgBoardListener>();
		}
		catch(Exception e)
		{
		}
		
	}
	
	public void addMessage(String msg) throws RemoteException
	{		
		al.add(msg);
		callListeners(msg);
	}
	
	public ArrayList getMessages() throws RemoteException
	{
		return al;
	}
	
	public void addListener(MsgBoardListener mbl) throws RemoteException
	{
		alListeners.add(mbl);
	}
	
	public void removeListener(MsgBoardListener mbl) throws RemoteException
	{
		alListeners.remove(mbl);
	}
	
	private void callListeners(String msg) throws RemoteException
	{
		for(int i=0; i<alListeners.size(); i++)
		{
			((MsgBoardListener)(alListeners.get(i))).msgAdded(msg);
		}
	}
}
```


```
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.RemoteException;

import java.rmi.Naming;

public class MessageBoardServer
{
	public static void main(String[] args)
	{
		// lokalen Server starten
		try
		{
			LocateRegistry.createRegistry(Registry.REGISTRY_PORT);

			MsgBoardObject mbo = new MsgBoardObject();
			Naming.rebind("rmi://localhost/MsgBoardObject", mbo);
			
			System.out.println("MsgBoardObject bound");
			while(true) Thread.sleep(300);
		}
		catch(Exception e)
		{
			System.out.println(e);
		}
	}
}
```


```
import java.rmi.Naming;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.io.Serializable;

public class MessageBoardClient implements MsgBoardListener, ActionListener, Serializable
{
	private static boolean done;
	
	private static MsgBoardInterface mbo;
	
	private static JFrame frame;
	private static JList entries;
	private static JTextField text;
	
	private static Vector<String> data;
	
	public static void main(String[] args)
	{
		done = false;
		
		MessageBoardClient listener = new MessageBoardClient();
		
		data = new Vector<String>();
		
		try
		{
			mbo = (MsgBoardInterface) Naming.lookup("rmi://localhost/MsgBoardObject");
			
			mbo.addListener(listener);
		}
		catch(Exception e)
		{
			System.out.println(e);
		}
		
		frame = new JFrame("MessageBoardClient");	
		frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
		frame.setLayout(new GridLayout(3,1));
		
		JButton buttonAdd = new JButton("Hinzufügen");
		buttonAdd.addActionListener(listener);
		
		entries = new JList();
		JScrollPane jsp = new JScrollPane(entries);
		
		text = new JTextField();
		
		frame.add(jsp);
		frame.add(text);
		frame.add(buttonAdd);
		
		frame.pack();
		frame.setVisible(true);
	}
	
	public void msgAdded(String msg)
	{
		data.add(msg);
		
		entries.setListData(data);
	}
	
	public void actionPerformed(ActionEvent e)
	{
		String str = e.getActionCommand();
		
		if(str.equals("Hinzufügen"))
		{
			String msg = text.getText();
			
			try
			{
				mbo.addMessage(msg);
			}
			catch(Exception ex)
			{
				System.out.println("ErrEx:" + ex);
			}
		}	
	}
}
```


----------



## Guest (16. Mrz 2006)

Das ist ein sehr nettes Beispiel. 


```
private void callListeners(String msg) throws RemoteException
   {
      for(int i=0; i<alListeners.size(); i++)
      {
         ((MsgBoardListener)(alListeners.get(i))).msgAdded(msg);
      }
   }
```

Wenn ich den Code richtig verstehe, möchtes du hier die Methode des Clients aufrufen, mit dem ein update gemacht werden soll. Das wird wohl so nicht gehen. Diese Nachricht (msgAdded(msg)) muss über das Netz zum Client gehen. Dein Aufruf geht davon aus, dass es sich um ein lokales Objekt handelt, was nicht der Fall ist. Es würde micht interessieren, ob du da Fehlermeldungen bekommst (Exceptions) und wenn ja, welche?

Ich würde sagen, dass du in der Methode 'addListener' die Registrierung von Clients ganz anders machen musst. Nähmlich so, dass in dieser Methode ein Stub für den Client dem Server zugestellt wird. Das heisst, dass du auch ein Interface definieren musst, in dem die Methoden des Clients, die du auf der Serverseite aufrufen möchtest, deklariert sind. Daraus wird (...) ein Stub erzeugt und bei der Registrierung dem Server zugestellt. Erst dann könntest du die Methode 'msgAdded' aufrufen. Dieser Methodenaufruf wird dann vom Stub verpackt und dem Client zugestellt. Ich glaube, wo was wird (in dieser oder ähnlicher Form) 'callback' genannt.


----------



## Markus09 (16. Mrz 2006)

Also das mit dem Listener geht. Das hab ich wie es da steht ausgeführt (nur ohne "extends UnicastRemoteObject")

Das Problem ist, dass es nicht möglich ist, dass 2 Clients benachrichtigt werden, auch wenn sich beide registriert haben.

Es entsteht also sowas:
Client -->  (A)Server(B)  <-- Client

Was ich aber haben will ist etwa das:

Client -->  (A)Server(A)  <-- Client

Also, dass sich beide Clients auf EINEM RemoteObject anmelden.


----------



## Guest (16. Mrz 2006)

Laut Literatur, muss die entfernte Klasse von UnicastRemoteObject abgeleitet werden.



> Das ist die empfohlene Voergehensweise, die in [Sun/RMISpec 2004] beschrieben ist. Durch die Vererbung wird unsere Klasse _DateImpl_ zu einem entfernten Objekt und kann auf entfernte Mewthodenaufrufe reagieren.
> 
> Quelle: Middleware in Java, Heinzl S., Mathes M.



Da es sich um "soll" handelt, gehe ich davon aus, dass dies nicht die einzige Möglichkeit ist, ein entferntes Obejkt so weit zu bringen, dass es auf entfernte Aufrufe reagiert. 

Könntest du den Code, in dem dies mit einem Client funktioniert, zeigen?


----------



## Guest (16. Mrz 2006)

Habe jetzt deinen Code übernommen. In der Zeile 62 der Klasse 'MessageBoardClient', Methode 'msgAddes' kommt eine NullPointerException! 


```
data.add(msg);
```

Have a look!


----------



## Markus09 (16. Mrz 2006)

Wenn die Zeile:

public class MsgBoardObject extends UnicastRemoteObject implements MsgBoardInterface, Serializable 


in folgende geändert wird funktioniert es:
public class MsgBoardObject implements MsgBoardInterface, Serializable 

(also extends weglassen)


----------

