RMI & classpath & rmic.exe

Status
Nicht offen für weitere Antworten.
G

Guest

Gast
ich krieg das einfache HelloWorld nicht zum laufen laufen :(
folgender Code liegt vor:
Code:
//Hello.java
package RMI;
import java.rmi.*;
public interface Hello extends Remote {
	public String sayHello() throws java.rmi.RemoteException;
}
Code:
//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);
		}
	}
}
Code:
//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

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

Dante

Bekanntes Mitglied
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?!
 
G

Guest

Gast
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

Bekanntes Mitglied
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
Code:
		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
Code:
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.
 
G

Guest

Gast
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?
 

Gumble

Bekanntes Mitglied
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:
Code:
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

Bekanntes Mitglied
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

Bekanntes Mitglied
hmm, also jetzt häng ich an der Zeile mit dem rebind:
Code:
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)
at java.lang.SecurityManager.checkConnect(SecurityManager.java:1026)
at java.net.Socket.connect(Socket.java:446)
at java.net.Socket.connect(Socket.java:402)
at java.net.Socket.<init>(Socket.java:309)
at java.net.Socket.<init>(Socket.java:124)
at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(RMIDirectSocketFactory.java:22)
at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(RMIMasterSocketFactory.java:128)
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:562)
at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:185)
at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:171)
at sun.rmi.server.UnicastRef.newCall(UnicastRef.java:313)
at sun.rmi.registry.RegistryImpl_Stub.rebind(Unknown Source)
at java.rmi.Naming.rebind(Naming.java:160)
at RMI.HelloImpl.main(HelloImpl.java:17)
Exception in thread "main"
 

Dante

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

Code:
grant {

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

        "connect,accept";

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

Gumble

Bekanntes Mitglied
scheint ne Policysache sein. Hab davon leider auch recht wenig Ahnung
Hab jetzt ein java.policy file erstellt:
Code:
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

Bekanntes Mitglied
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

Bekanntes Mitglied
war ja klar, dass noch nicht alles geht :( Jetzt hängt der Client.
Hab eine client.policy erstellt:
Code:
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

Bekanntes Mitglied
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:
Code:
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

Bekanntes Mitglied
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

Bekanntes Mitglied
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

Bekanntes Mitglied
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

Bekanntes Mitglied
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

Bekanntes Mitglied
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

Bekanntes Mitglied
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

Bekanntes Mitglied
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.
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen


Oben