# RMI & classpath & rmic.exe



## Guest (25. Jun 2004)

ich krieg das einfache HelloWorld nicht zum laufen laufen 
folgender Code liegt vor:

```
//Hello.java
package RMI;
import java.rmi.*;
public interface Hello extends Remote {
	public String sayHello() throws java.rmi.RemoteException;
}
```


```
//HelloImpl.java
package RMI;
import java.rmi.*;
import java.rmi.server.*;
import java.net.*;
public class HelloImpl extends UnicastRemoteObject implements Hello {
	public HelloImpl() throws RemoteException {
		super();
	}
	public String sayHello() throws RemoteException {
		return "Hello, World!";
	}
	public static void main(String args[]) {
		try {
			HelloImpl h = new HelloImpl();

			Naming.rebind("rmi://localhost/hello", h);
			System.out.println("Hello Server ready.");
		} catch (RemoteException re) {
			System.out.println("Exception in HelloImpl.main: " + re);
		} catch (MalformedURLException e) {
			System.out.println("MalformedURLException in HelloImpl.main: " + e);
		}
	}
}
```


```
//HelloClient.java
package RMI;
import java.rmi.*;
public class HelloClient {
	public static void main(String args[]) {
		System.setSecurityManager(new RMISecurityManager());
		try {
			Hello h = (Hello) Naming.lookup("hello");
			String message = h.sayHello();
			System.out.println("HelloClient: " + message);
		} catch (Exception e) {
			System.out.println("Exception in main: " + e);
		}
	}
}
```
Zum Programmieren verwende ich Eclipse 3 und das SDK 1.4.2_03. Nachdem die classfiles erzeugt wurden, möchte ich das Skel und Stub mit dem RMI-Compiler erzeugen, doch das klappt net. 





> E:\Programme\Java\j2sdk1.4.2_03\bin>rmic -classpath E:/Programme/eclipse/workspace/Communikation/RMI/ RMI.HelloImpl
> error: Class RMI.HelloImpl not found.
> 1 error


Dachte vielleicht es läge am CLASSPATH, aber da ist mittlerweile alles mögliche schon drin:





> CLASSPATH=.;E:/Programme/Java/jini2_0_002/lib/jini-core.jar;E:/Programme/Java/jini2_0_002/lib/sun-util.jar;E:/Programme/Java/jini2_0_002/lib/reggie.ja
> r;E:/Programme/Java/jini2_0_002/lib/reggie_dl.jar;E:/Programme/Java/jini2_0_002/lib/jini-ext.jar;E:\Programme\Java\j2re1.4.2_03\lib;E:\Programme\Java\
> j2sdk1.4.2_03\bin;



Kann mir jemand helfen? Ich glaub das liegt am Path, denn der Code müsste stimmen (hab ich von ner Seite abegetippt) - hatte schonmal Probleme damit, als ich JINI testen wollte (ging natürlich auch nicht)
Danke schonmal!


----------



## Gumble (25. Jun 2004)

ups, hab mich vergessen einzuloggen. Jetzt krieg ich auch wenigstens eine Emailbenachrichtigung bei Antwort (hoffentlich)


----------



## Dante (25. Jun 2004)

Ich hab mich heute auch etwas mit dem Compiler rumgeärgert. Ich denke du hast den Classpath falsch gesetzt, oderliegt deine .class wirklich in:

E:/Programme/eclipse/workspace/Communikation/RMI/RMI/Hello.impl ?

Der Classpath sollte ja auf das Verzeichnis zeigen in dem eine Package-Hierarchie beginnt, ich nehme mal an, das

E:/Programme/eclipse/workspace/Communikation/RMI/Hello.impl 

besser wäre?!


----------



## Guest (26. Jun 2004)

jaa. das wars! man, damit hab ich mich über 2 Stunden rumgeärgert. Großes Dank.
Leider klappts immer noch nicht. Den Stub und den Skeleton konnte ich jetzt erzeugen:


> E:\Programme\Java\j2sdk1.4.2_03\bin>rmic -classpath E:/Programme/eclipse/workspace/Communikation RMI.HelloImpl -d E:/Programme/eclipse/workspace/Commu
> nikation/


danach hab ich den rmiregistery.exe gestartet. Doch nun gibts folgenden Fehler beim Server:


> java.rmi.ServerError: Error occurred in server thread; nested exception is:
> java.lang.NoClassDefFoundError: RMI/Hello


an was liegt es diesmal?


----------



## Dante (26. Jun 2004)

Das Problem ist die Codebase, das ganze schaut ja so aus:

Server: Hat das Remote-Interface und die eigentliche Implementierung der Remote-Klasse

Registry: Braucht das Interface und die eigentlicht Implementierung

Client: Hat das Interface und braucht die eigentliche Implementierung.

Um die eigentliche Implementierung der Remote-Klasse nicht an allen drei Punkten abzulegen (was ja bei mehreren Clients und ständigen Updates ein zeitraubender Prozess wäre) kann RMI eine Codebase benutzen, das kann zB. ein Webserver sein. In den Systemproperties kannst du unter java.rmi.server.codebase die URL zu einer Package-Hierarchie (also auch hier wieder aufpassen, das du das richtige root-verzeihnis erwischt) angeben.

Die RMI-Geschichte durchsucht dann, nachdem im lokalen Classpath nichts gefunden wurde die Codebase nach den passenden Klassen.

Diese Codebase sollte für Client und Registry benutzt werden!

Um die Properties zu laden gibt es zwei Möglichkeiten: 

- Über die Shell mit dem Parameter -D mitgeben, oder
- Die Registry aus einem Java-Programm starten und vorher die Properties laden:

Registry.java

```
Properties props = new Properties(System.getProperties());
		try {
			FileInputStream in = new FileInputStream("registry.properties");
			props.load(in);
		} catch (Exception e) {
			e.printStackTrace();
		}
		String port = props.getProperty("registry.port");
		System.setProperties(props);
	
		if (System.getSecurityManager() == null) {
			System.setSecurityManager(new RMISecurityManager());
		}		
		LocateRegistry.createRegistry(Integer.parseInt(port));
```

registry.properties

```
java.rmi.server.codebase=http://server.domain/rmi-codebase/

java.security.policy=java.policy

registry.host= localhost

registry.port= 1099
```


Zu beachten ist, das die Registry nur solange läuft, wie das Programm läuft was sie gestartet hat.


----------



## Guest (27. Jun 2004)

also, ein Stückchen bin ich nun weiter. Allerdings häng ich jetzt an dieser Exception:


> java.rmi.server.ExportException: Port already in use: 1099; nested exception is:
> java.net.BindException: Address already in use: JVM_Bind


Entstehen tut sie in dieser Zeile





> LocateRegistry.createRegistry(Integer.parseInt(port));


Hab schon andere Portnummern ausprobiert - immer dasselbe. Als codebase in dem properties-file hab ich "http://localhost/hello/ " genommen. Ist doch richtig?


----------



## Jaraz (27. Jun 2004)

Hi,

kann es sein das du die Zeile mehrmals aufrufst?

Gruß Jaraz


----------



## Gumble (27. Jun 2004)

Jaraz hat gesagt.:
			
		

> kann es sein das du die Zeile mehrmals aufrufst?


Nein, es hängt bereits beim ersten Aufruf (im Server). Zum Client komm ich noch gar nicht - dadrin starte ich auch einmal den Code den Dante mir gegeben hat.
Mein Server:
	
	
	
	





```
package RMI;

import java.rmi.*;
import java.rmi.server.*;
import java.net.*;
public class HelloImpl extends UnicastRemoteObject implements Hello {
	public HelloImpl() throws RemoteException {
		super();
	}
	public String sayHello() throws RemoteException {
		return "Hello, World!";
	}
	public static void main(String args[]) {
		try {
			Registry.start();
			HelloImpl h = new HelloImpl();
			Naming.rebind("rmi://localhost/hello", h);
			System.out.println("Hello Server ready.");
		} catch (RemoteException re) {
			re.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
	}
}
```
Wobei Registry.start() nur eine static-methode mit den besagten Codezeilen ist.


			
				Dante hat gesagt.:
			
		

> Zu beachten ist, das die Registry nur solange läuft, wie das Programm läuft was sie gestartet hat.


Ich starte die rmiregistry.exe einfach per Hand vor dem Debuggen/Run. Sollte man wohl später im Code aus starten.[/quote]


----------



## Dante (27. Jun 2004)

Wenn du ein Objekt an der Registrierung bekannt machst, dann bleibt das da bis entweder die Registrierung beendet wird oder das Objekt dort explizit wieder "gelöscht" (unbound) wird.

Bist du sicher das nur eine Registry läuft? Wenn du die mit locateRegistry... aus deinem Java-Code startest, musst du die exe nicht mehr benutzen, die machen ja genau dasselbe.


----------



## Gumble (27. Jun 2004)

hmm, also jetzt häng ich an der Zeile mit dem rebind:
	
	
	
	





```
Naming.rebind("rmi://localhost/hello", h);
```
Hier gibts einen fetten Fehler so das sogar eine Meldung aufpoppt das die JVM abgeschmiert ist. (Fatal exception occured. Programm will exit)
Hier der StackTrace:





> java.security.AccessControlException: access denied (java.net.SocketPermission 127.0.0.1:1099 connect,resolve)
> at java.security.AccessControlContext.checkPermission(AccessControlContext.java:269)
> at java.security.AccessController.checkPermission(AccessController.java:401)
> at java.lang.SecurityManager.checkPermission(SecurityManager.java:524)
> ...


----------



## Dante (27. Jun 2004)

Dein Security-Manager verbietet die Verbindung zur Registrierung. Du musst eine entsprechende Policy erstellen:


```
grant {

    permission java.net.SocketPermission "*:1024-65535",

        "connect,accept";

    permission java.net.SocketPermission "*:80", "connect";
    
};
```


----------



## Gumble (27. Jun 2004)

scheint ne Policysache sein. Hab davon leider auch recht wenig Ahnung
Hab jetzt ein java.policy file erstellt:

```
grant codebase "file:/E:/Programme/eclipse/workspace/Communikation/RMI" {
     permission java.net.SocketPermission "localhost:1024-65535",
"accept,listen,connect,resolve";};
```
Und starte das Programm jetzt über die Konsole





> java -Djava.security.policy=java.policy RMI.HelloImpl


doch der Fehler bleibt.


----------



## Gumble (27. Jun 2004)

Danke für die rasche Antwort. Hab ich erstma glatt überlesen 
Das Problem lag daran, dass das Policy-file mit dem java und class files im RMI verzeichnis lag und nicht in dem Projektroot von dem ich java aus aufrufe!
Also der Server läuft schonmal. jetzt teste ich mal weiter! mein erstes Erfolgserlebnis  Danke schonmal!!!


----------



## Gumble (27. Jun 2004)

war ja klar, dass noch nicht alles geht  Jetzt hängt der Client.
Hab eine client.policy erstellt:
	
	
	
	





```
grant { 
    permission java.net.SocketPermission "*:1024-65535", "connect,accept, resolve"; 
    permission java.net.SocketPermission "*:80", "connect";     
};
```
doch nun krieg ich folgende Exception:
_java.rmi.NotBoundException:hello_
Interessant ist, wenn ich aus dem Sternchen "localhost" in dem policyfile mache, krieg ich diese Exception:
_java.security.AccessControlException: access denied (java.net.SocketPermission 192.168.1.13:1099 connect,resolve)_
Das ist die IP meiner einen Netzwerkkarte...


----------



## Gumble (27. Jun 2004)

ok ich habs:
1. Client darf nicht Registry.start(); starten (-> exception)
2. Hello h = (Hello) Naming.lookup("rmi/hello"); //Das RMI nicht vergessen
EDIT:
Anscheinend doch OHNE die package-struktur, also nur "hello"!
3. tipp: sich mal die Services anzeigen lassen:
	
	
	
	





```
String [] l = Naming.list("rmi://localhost/hello");
			for(int i=0;i<l.length;i++)
				System.out.println("Available service : ["+l[i]+"]");
```
Danke Dante für deine Geduld. Deine Hilfe hat mir echt geholfen. Hoffe das läuft jetzt auch auf verschiedenen Rechnern. Vor allem wenn man die IP des Servers nicht kennt. Vielleicht hast du ja da noch ein paar Tipps auf Lager


----------



## Dante (27. Jun 2004)

Kein Problem, ich hab da gerade für nen Praktikum an der Uni ne Aufgabe machen müssen und weiss, daß das ne ziemlich fitzelige Sache ist, wenn man es das erste mal machen muss 

Du musst auf jedenfall eine Adresse des Servers kennen, ob das aber ne IP oder eine Domainname ist, ist dem RMI-Ding egal (das geht eh alles in nen Socket und der löst ja automagisch auf). Du könntest natürlich nen 'Master-Server' aufmachen, der auf Anfrage die Adresse des Servers mitteilen kann... Oder du durchsuchst bestimmte Adressbereiche.. Läuft halt aber alles auf dasselbe hinaus...


----------



## Gumble (27. Jun 2004)

jo, bis man sich durch den Wust des Einbindens und "zum Laufen" kriegen durchbeisst, vergisst man ganz schnell dass die eigentliche Arbeit ja noch ansteht. Will nämlich eine Client/Serveranwendung bauen, bei dem ein oder mehrere interaktiver Tests durchgeführt werden können.
Jedenfalls möchte ich dem Clientuser nicht zu viel aufhalsen wie IP des Servers eingeben. Dachte da entweder an einem Scan-Button der IP-Bereich des local-Net durchforstet oder, imho die bessere da netzfreundlichere Lösung, per Broker. D.h. der Server, bzw bei mehreren Servern Einer, ist irgendwie statisch erreichbar, und an ihm melden sich die Server an. Das würde dann genau die Tests sein, z.b. Mathetest, IQ-Test usw. Leider hab ich nur theoretisch davon Ahnung. Aber vielleicht hat hier ja jemand schonmal sowas gebaut?


----------



## Dante (27. Jun 2004)

Das ist das was ich mit Master-Server meinte, ein Server der immer erreichbar ist und nur sammelt,. was es denn noch für Server gibt (bzw. auch selbst ein eigener Server ist) und die an den Client schickt... da könnte man gleich noch etwas lastausgleich oder ähnliches reinbringen


----------



## Gumble (27. Jun 2004)

puh. das ist mir zu heftig 
ich groupgoogle schon wild umher. CORBA scheint da sowas fertig zu besitzen... Meinst das kann ich dafür hernehmen? Das Problem ist wohl eher es zum laufen zu kriegen  ???:L


----------



## Dante (27. Jun 2004)

Naja, so schwer ist das ja eigentlich nicht.

Probleme gibt es halt bei der Sicherheit, da musst du dir unabhängig hiervon nochmal Gedanken drüber machen.

Aber an sich brauchst du irgendwo einen Master-Server der zwei Sachen macht:

- Neue Server die sich bei ihm melden eintragen.
- Überwachen ob die eingetragenen Server noch aktiv sind.

Ein Client tut dann nix anderes als sich beim Master-Server zu melden, der übergibt eine Adresse an den Client wo er einen Server findet. Mit dem kann er dann reden.

Der Master-Server könnte zB. ne RMI-Registry sein, neue Server binden sich dann in diese und du kannst als Client nachschauen wer so da ist.

Andererseits könnte auch ein Master-Server die Adressen & Ports von mehreren Registrierungen verwalten und diese an Clients verteilen. 

Wie man aber mit RMI nen sinnvolles SIcherheitskonzept umsetzt kann ich dir auch nicht sagen....


----------



## Gumble (27. Jun 2004)

ich glaub mit CORBA hab ich da was verwechselt... hat mich verwirrt weil Broker im Wort steckt (das B). Sollte schon alles über RMI laufen.
Das mit dem RegistryServer hab ich mir auch schon gedacht als ich die Zeile hier eingebunden hab
String [] l = Naming.list("rmi://localhost/hello");
jetzt bräucht ich nur einen statischen nahmen für den RegServer, damit ich die Zeile auch statisch in den Client einbinden kann. Allerdings wie kommt man "darein"? Es gibt keine RMIRegistry-class von denen man ableiten könnte.
Das ganze dann auch Internetfest machen wird wohl ein anderes übel... interessanter Link zu ner usenet group


----------



## Gumble (28. Jun 2004)

so ein letztes Posting für heute - zum Thema Design. Gibts vielleicht gute Skizzen, Diagramme oder Beispielcodes um stimmig die Anwendungen zu konzipieren? 
Oder wie kann der Server dem Client etwas mitteilen? Vielleicht per Polling (client polled nach Ablauf eines Timers eine Methode getMessage() vom Server. Damit könnte man gleich einen Timeout einbauen).
Selber hab ich dies Beispiel bei SUNgefunden. Werd das mal morgen durchmachen.


----------

