# rmi und download von klassen



## igor99 (1. Feb 2006)

hallo

ich habe eine kleine rmi-application programmiert und möchte, wenn irgendwie möglich, die gemeinsamen klassen im paket _model _nicht auf beiden seiten (server- und clientseite) extra ablegen bzw. verwalten. ich hätte sowohl einen ftp- als auch einen web-server zum ablegen von gemeinsamen klassen. meine frage ist: *ist das überhaupt möglich*? und wenn ja, könnte ich auch mit file-protokol soche dateien downloaden (achtung: server und client laufen auf unterschiedlichen systemen).

danke


----------



## Bleiglanz (1. Feb 2006)

ftp wohl nicht, http funktioniert aber

=> Codebase

http://java.sun.com/j2se/1.4.2/docs/guide/rmi/codebase.html


----------



## Guest (1. Feb 2006)

Bleiglanz hat gesagt.:
			
		

> ftp wohl nicht, http funktioniert aber
> 
> => Codebase
> 
> http://java.sun.com/j2se/1.4.2/docs/guide/rmi/codebase.html



vielen dank. ich habe diesen artikel schon gelesen, bin aber nicht schlauer geworden. ausserdem habe ich auch dies hier gelesen: 

http://labs.cs.utt.ro/doc/java_tutor/rmi/running.html

 ich benuzte *tomcat *, port *8080 *und habe im verzeichnis *webapps/root/* das verzeichnis *export/classes *angelegt, in dem das paket _model _mit den zugehörigen klassen abgelegt wurde. *tomcat *läuft und es kann wohl nicht schief gehen ...

auf der serverseite habe ich folgende *policy.txt* angelegt:


```
grant 
{
    permission java.net.SocketPermission "*:1024-65535", 		
        "connect,accept";
    permission java.net.SocketPermission "*:8080", "connect";
};
```

und server wird wie folgt gestartet (datei run_server.bat):

```
@echo off

REM CLASSPATH setzen
set CLASSPATH=.;lib\log4j.jar;lib\postgresql-8.0-313.jdbc3.jar

REM Server starten
java -Djava.rmi.server.codebase=http://localhost:8080/export/classes/ -Djava.security.policy=policy.txt server.Server

pause
```

auf der clientseite habe ich das paket *model* entfernt (ansonsten funktioniert es einwandfrei). 

- ich starte rmiregistry: start rmiregistry
- ich starte server: run_server.bat

alles im butter. server läuft auf der ip-adresse *137.181.183.110*

ich starte client:

```
java client.Client 137.181.183.110
```

nun bekomme ich meldung, dass die klasse aus dem paket _model _nicht gefunden wird. 

*was mach ich falsch?*


----------



## Bleiglanz (2. Feb 2006)

> vielen dank. ich habe diesen artikel schon gelesen, bin aber nicht schlauer geworden. ausserdem habe ich auch dies hier gelesen:
> 
> http://labs.cs.utt.ro/doc/java_tutor/rmi/running.html


Wirklich

auch den Abschnitt "Starting the Client"?


----------



## Guest (2. Feb 2006)

> Wirklich
> 
> auch den Abschnitt "Starting the Client"?



ja, auch diesen abschnitt habe ich gelesen. aber wenn du schon so fragst,  denke ich, dass in diesem abschnitt etwas steht, was ich übersehen habe. stimmt das?

ich habe mittlerweile weiter probiert und habe noch logging von rmiregistry eingeschaltet. nun, habe ich immer am ende eine kommische meldung:


```
FEINER: RMI RenewClean-[137.181.183.110:1135]: class "java.rmi.server.UID" found
via codebase, defined by null
```

ich gebe hier noch die logg-messages an. vielleicht sieht man da etwas, was ich nicht gesehen habe.


```
02.02.2006 12:23:32 sun.rmi.server.LoaderHandler loadClass                        
FEIN: RMI RenewClean-[137.181.183.110:1135]: name = "java.rmi.dgc.Lease", codebas  
e = "http://localhost:8080/export/classes/"                                       
02.02.2006 12:23:32 sun.rmi.server.LoaderHandler loadClass                        
FEINER: RMI RenewClean-[137.181.183.110:1135]: (thread context class loader: sun.  
misc.Launcher$AppClassLoader@133056f)                                             
02.02.2006 12:23:32 sun.rmi.server.LoaderHandler loadClass                        
FEINER: RMI RenewClean-[137.181.183.110:1135]: class "java.rmi.dgc.Lease" found v  
ia codebase, defined by null                                                      
02.02.2006 12:23:32 sun.rmi.server.LoaderHandler loadClass                        
FEIN: RMI RenewClean-[137.181.183.110:1135]: name = "java.rmi.dgc.VMID", codebase  
 = "http://localhost:8080/export/classes/"                                        
02.02.2006 12:23:32 sun.rmi.server.LoaderHandler loadClass                        
FEINER: RMI RenewClean-[137.181.183.110:1135]: (thread context class loader: sun.  
misc.Launcher$AppClassLoader@133056f)                                             
02.02.2006 12:23:32 sun.rmi.server.LoaderHandler loadClass                        
FEINER: RMI RenewClean-[137.181.183.110:1135]: class "java.rmi.dgc.VMID" found vi  
a codebase, defined by null                                                       
02.02.2006 12:23:32 sun.rmi.server.LoaderHandler loadClass                        
FEIN: RMI RenewClean-[137.181.183.110:1135]: name = "[B", codebase = ""            
02.02.2006 12:23:32 sun.rmi.server.LoaderHandler loadClass                        
FEINER: RMI RenewClean-[137.181.183.110:1135]: (thread context class loader: sun.  
misc.Launcher$AppClassLoader@133056f)                                             
02.02.2006 12:23:32 sun.rmi.server.LoaderHandler loadClass                        
FEINER: RMI RenewClean-[137.181.183.110:1135]: class "[B" found via codebase, def  
ined by null                                                                      
02.02.2006 12:23:32 sun.rmi.server.LoaderHandler loadClass                        
FEIN: RMI RenewClean-[137.181.183.110:1135]: name = "java.rmi.server.UID", codeba  
se = "http://localhost:8080/export/classes/"                                      
02.02.2006 12:23:32 sun.rmi.server.LoaderHandler loadClass                        
FEINER: RMI RenewClean-[137.181.183.110:1135]: (thread context class loader: sun.  
misc.Launcher$AppClassLoader@133056f)                                             
02.02.2006 12:23:32 sun.rmi.server.LoaderHandler loadClass                        
FEINER: RMI RenewClean-[137.181.183.110:1135]: class "java.rmi.server.UID" found
```

was bedeutet das: *found via codebase, defined by null*


----------



## Bleiglanz (2. Feb 2006)

hast du schon mal versucht, auch dem Client die codebase mitzuteilen (-D)


----------



## Guest (2. Feb 2006)

nein, habe ich nicht. und, was soll ich im sagen? das gleiche (bzw. fast das gleiche) wie dem server: 

```
-Djava.rmi.server.codebase=http://137.181.183.110:8080/export/classes/
```
du meinst, der klassenlader sollte dann selbst nach den klassen suchen gehen? 

vilen dank für den tipp. ich werde es morgen probieren.


----------



## Guest (3. Feb 2006)

nun, jetzt habe ich es probiert, funktioniert aber nicht. 

ich habe jetzt aber eine einfache, grundsätzlihce frage: wie funktioniert dynamisches downloaden von klassen in java?

dazu habe ich folgendes beispiel erstellt: klasse *demo.Punkt* und klasse *Main*. 

klasse *demo.Punkt*:

```
package demo;

public class Punkt
{
    private int x;
    private int y;

    public Punkt(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    // set und get methoden ...

 
    public String toString()
    {
        return "x = " + x + ", y = " + y;
    }

}
```

klasse *Main*:

```
import java.rmi.RMISecurityManager;

import demo.Punkt;

public class Main
{
    public static void main(String[] args)
    {     
        if (System.getSecurityManager() == null)
        {
            System.setSecurityManager(new RMISecurityManager());
        }

        Punkt p = new Punkt(10, 20);

        System.out.println(p);
    }
}
```

habe die klasse demo.Punkt (bzw. das verzeichnis _demo_) auf dem webserver *<ROOT>/export/classes/* abgelegt, policy-datei auf der clientseite definiert und probiert, die klasse Main auszuführen:



```
java -Djava.rmi.codebase=http://server:8080/export/classes/ -Djava.security.policy=policy.txt Main
```

meldung: 
	
	
	
	





```
Exception in thread "main" java.lang.NoClassDefFoundError: demo/Punkt
```

der nächste versuch: paket bzw. verzeichnis *demo *local im verzeichnis *c:\temp* abgelegt und den aufruf wie folgt gemacht:

```
java -Djava.rmi.codebase=file:/C:\Temp\  -Djava.security.policy=policy.txt Main
```

und die gleiche fehlermeldung.

nun, wie macht man das? wenn ich mit diesem einfachen beispiel das nicht fertig bringe, dann habe ich wohl ein verständnisproblem, oder was? muss ich da noch mit klassenloader spielen bzw. etwas überschreiben? denn, ich habe gelesen, dass *codebase *eigentlich nichts anderes als ein "globaler" *CLASSPATH *ist? wie bringe ich den klassenloader so weit, dass er im *codebase *sucht? 

danke für jeden tipp.


----------



## Guest (3. Feb 2006)

meine natürlich 


```
-Djava.rmi.server.codebase ...
```


----------



## halber (5. Feb 2006)

Schau mal hier:
Client/Server-Programmierung
vielleicht hilft dir davon was.


----------



## igor99 (7. Feb 2006)

nun habe ich endlich die Klasse *demo.Punkt* von meinem Web-Server downloaden können:


```
public class Main
{
    public static void main(String[] args)
    {
        try
        {
            URL url = new URL("http://192.168.1.33:8080/export/classes/");
            URL[] urls = { url };
            URLClassLoader loader = new URLClassLoader(urls);

            // Klasse 'demo.Punkt' wird geladen
            Class<?> cls = Class.forName("demo.Punkt", true, loader);

            if (cls != null)
            {
                // Argumentenliste (int, int)
                Class[] argClass = new Class[] { int.class, int.class };
                // Konstruktor erzeugen
                Constructor<?> c = cls.getConstructor(argClass);
                // Eine Instanz erzeugen
                Object obj = c.newInstance(12, 18);
                // Ausgabe (toString der Klasse 'demo.Punkt')
                System.out.println(obj);

                // Methoden 'setX' und 'setY' holen
                Method setX = cls.getMethod("setX", int.class);
                Method setY = cls.getMethod("setY", int.class);

                // Falls vorhanden, aufrufen
                if (setX != null && setY != null)
                {
                    setX.invoke(obj, 52);
                    setY.invoke(obj, 62);
                }

                // Ausgabe (toString der Klasse 'demo.Punkt')
                System.out.println(obj);
            }
        }
        catch (Exception e1)
        {
            e1.printStackTrace();
        }
    }
}
```

macht man das etwa so? oder, kann man einfacher / eleganter / besser machen?


----------



## igor99 (8. Feb 2006)

habe ein interessantes artikel zum dynamischen downloaden von klassen gefunden:

http://www.eli.sdsu.edu/courses/spring99/cs696/notes/ddc/ddc.html

das artikel ist zwar relativ alt, sollte aber immer noch grösstenteils "giltig" sein. leider, ist meine hoffunung, schlauer zu werden, nicht in erfüllung gegangen. meine frage bleibt weiter: _wozu dient *codebase*_???

ich habe mit einem einfachen beispiel (unabhängig von RMI) herausfinden probiert, wie das dynamische laden von klassen funktioniert. dazu habe ich folgende klassen definiert:


```
package hsw.fhw.calc;

public abstract class Calculator
{
    public abstract double add(double a, double b);
    public abstract double sub(double a, double b);
    public abstract double mul(double a, double b);
    public abstract double div(double a, double b);
    public abstract double getMax(double ... array);
    public abstract double getMin(double ... array);
}
```


```
package hsw.fhw.calc.model;

import java.util.Arrays;

import hsw.fhw.calc.Calculator;

public class CalculatorImpl extends Calculator
{

    @Override
    public double add(double a, double b)
    {
        Printer.print("Addition von " + a + " und " + b + ":");
        return a + b;
    }

    @Override
    public double sub(double a, double b)
    {
        Printer.print("Subtrachtion von " + a + " und " + b + ":");
        return a-b;
    }

    @Override
    public double mul(double a, double b)
    {
        Printer.print("Multiplikation von " + a + " und " + b + ":");
        return a*b;
    }

    @Override
    public double div(double a, double b)
    {
        Printer.print("Division von " + a + " und " + b + ":");
        return a/b;
    }

    @Override
    public double getMax(double... array)
    {
        Printer.print("Maximum:");
        Arrays.sort(array);
        return array[array.length-1];
    }

    @Override
    public double getMin(double... array)
    {
        Printer.print("Minimum:");
        Arrays.sort(array);
        return array[0];
    }
   
}
```


```
package hsw.fhw.calc.model;

public class Printer
{
    public static void print(String msg)
    {
        System.out.println(msg);
    }
}
```


```
package hsw.fhw.calc.test;

import hsw.fhw.calc.Calculator;

import java.net.URL;
import java.net.URLClassLoader;

public class Main
{
    public static void main(String[] args)
    {       
        try
        {
            URL url = new URL("http://192.168.1.33:8080/export/classes/");
            URL[] urls = { url };
            URLClassLoader loader = new URLClassLoader(urls);

            // Klasse 'hsw.fhw.calc.model.CalculatorImpl' wird geladen
            Class<?> cls = Class.forName("hsw.fhw.calc.model.CalculatorImpl", true, loader);

            if (cls != null)
            {
                Calculator calc = (Calculator)cls.newInstance();
                
                double a = 2.5;
                double b = 2;
                
                double summe = calc.add(a, b);
                double differenz = calc.sub(a, b);
                double produkt = calc.mul(a, b);
                double quotient = calc.div(a, b);
                
                System.out.println("Summe:    " + summe);
                System.out.println("Differnz: " + differenz);
                System.out.println("Produkt:  " + produkt);
                System.out.println("Quotient: " + quotient);                            
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }        
    }

}
```

auf der client-seite befindet sich nur die klasse _Main _und das interface _Calculator_. das paket _model _ (mit _CalculatorImpl _und _Printer_) befindet sich auf dem web-server im verzeichnis *<root>/export/classes*. das ganze programm wird wei folgt gestartet:


```
java hsw.fhw.calc.test.Main
```

und, die ausführung funktioniert. dadurch, dass ich das interface _Calculator _auf der client-seite habe, kann ich das casten von _Object_ in _Calculator_ realisieren und damit alle methoden direkt (ohne reflection-gebrauch) einsetzten. 

so weit, so gut. aber, zurück zu *codebase*. ich gehe davon aus, dass man *codebase* auch gebrauchen kann. nun wie?

ich habe jetzt die klasse _Printer_ in das verzeichnis *<root>/public* auf dem web-server verschoben, mit dem ziel, diese lokation mit *codebase *anzugeben. der aufruf sah wie folgt aus:


```
java -Djava.rmi.server.codebase=http://192.168.1.33:8080/public/ hsw.fhz.calc.test.Main
```

ergebnis: klasse Printer kann nicht gefunden werden.

der nächste versuch: ich benutze noch *useCodebaseOnly=true*


```
java -Djava.rmi.server.useCodebaseOnly=true -Djava.rmi.server.codebase=http://192.168.1.33:8080/public/ hsw.fhz.calc.test.Main
```

ergebnis: klasse Printer kann nicht gefunden werden.

schlussfolgerung: mein versuch ist gescheitert!!!

im nächsten schritt habe ich die lokation der klasse _Printer_ im code direkt angegeben:



```
URL[] urls = { new URL("http://192.168.1.33:8080/export/classes/"), new URL("http://192.168.1.33:8080/public/") };
            URLClassLoader loader = new URLClassLoader(urls);
```

das programm wird wie folgt aufgerufen:


```
java hsw.fhw.calc.test.Main
```

ergebnis: es funktioniert.

obwohl ich klasse _Printer_ nicht explizit lade, wird das laden dynamisch realisiert, sobald die klasse benötigt wird. 

auf meine frage, *wozu codebase dient*, habe ich keine antwort gefunden. kann mir jemand aus meinem albtraum helfen?


----------



## Guest (12. Feb 2006)

Müsste eigentlich funktionieren. Mit *codebase * sollte, nach meinem bescheidenen Wissen, der URLClassLoader automatisch erzeugt und zum Laden von Klassen von der angegebenen URL eingesetzt werden. So etwa wie mit SecurityManger, der von der Commandozeile erzeugt wird, wobei ihm die Policy-Datei mitgegeben wird.


----------

