1. Exemplet Echo: Översikt
Nu ska vi skriva ett exempel som tar inmatning från tangentbordet och skickar tillbaka samma text, med x mellan varje bokstav. Exemplet gör samma sak som exemplet echo på sidan om
Sockets . Här ges en sammanfattning på alla steg som man måste göra för att programmet ska fungera.
- Miljö: Jag kör Java SDK 1.5 i detta exempel.
För att ladda hem hela applikationen, klicka på
rmiecho.zip . För att se koden till enskilda klasser, klicka på den klass du vill se:
-
Echo.java : Gränssnittet som det anropade objektet implementerar. Finns på både klient och serversidan.
-
EchoImpl.java : Det objekt som vi vill anropa.
-
EchoClient.java : Klientklassen.
-
EchoServer.java : Serverklassen som bl.a. ser till att registrera EchoImpl så att klassen går att hitta.
2. Exemplet Echo: Serversidan
På serversidan behöver vi göra följande:
- Kompilera gränssnittet Echo.java.
- Kompilera klassen EchoImpl.java.
- Efter kompileringen av EchoImpl.java måste en stubbe och ett skelett skapas med kommandot rmic EchoImpl.
- Kompilera servern EchoServer.java.
- Starta registret med rmiregistry.
- Starta servern med java -Djava.rmi.server.codebase=file:///path_to_classes_directory/ EchoServer .
Vi börjar med att kompilera alla klasserna och skapa stubbarna. Jag lägger alla filerna i mappen /home/olle/java/rmiecho/server/src/, dessutom skapar jag en mapp där klasserna ska ligga /home/olle/java/rmiecho/server/classes/. Öppna ett terminalfönster och skriv alltså följande:
[olle@dev1]$ cd /home/olle/java/rmiecho/server/src
[olle@dev1]$ javac -d ../classes/ Echo.java
[olle@dev1]$ javac -d ../classes/ EchoImpl.java
[olle@dev1]$ javac -d ../classes/ EchoServer.java
[olle@dev1]$ cd ../classes/
[olle@dev1]$ rmic EchoImpl
[olle@dev1]$ ls
Echo.class EchoImpl.class EchoImpl_Stub.class EchoServer.class
|
Vi ser att vi förutom de kompilerade klasserna också har fått en stubbe EchoImpl_Stub.class. Öppna ett nytt terminalfönster och starta registret:
[olle@dev1]$ cd /home/olle/java/rmiecho/server/classes/
[olle@dev1]$ rmiregistry
|
Registertjänsten skapas på servermaskinen. I detta fall startas en process som lyssnar på defaultporten 1099. Man kan specifiera en egen port om man vill. Öppna ett nytt termialföster och starta servern:
[olle@dev1]$ cd /home/olle/java/rmiecho/server/classes/
[olle@dev1]$ java -Djava.rmi.server.codebase=file:///home/olle/java/rmiecho/server/classes/ EchoServer
Echo Server is ready.
|
Sökvägen /home/olle/java/rmiecho/classes/ är sökvägen till den mapp i operativsystemet där de kompilerade klasserna ligger, denna måste du ändra om du har en annan sökväg.
3. Exemplet Echo: Klientsidan
På klientsidan måste följande saker göras:
- Kopiera gränssnittet Echo.java från servern till klienten.
- Kopiera stubben EchoImpl_Stub.class från servern till klienten.
- Skriva och kompilera klassen EchoClient.java.
- Starta klienten med java EchoClient.
Vi skapar mappen /home/olle/java/rmiecho/client/src/ och /home/olle/java/rmiecho/client/classes/. Sen kopierar vi Echo.class och EchoImpl_Stub.class till classes-mappen (där klientens klasser ska ligga):
[olle@dev1]$ cd /home/olle/java/rmiecho/server/classes/
[olle@dev1]$ cp EchoImpl_Stub.class ../../client/classes/
[olle@dev1]$ cd /home/olle/java/rmiecho/server/src/
[olle@dev1]$ cp Echo.java ../../client/src/
|
Vi kompilerar och testkör klienten med följande kommandon:
[olle@dev1]$ cd /home/olle/java/rmiecho/client/src/
[olle@dev1]$ ls
EchoClient.java Echo.java
[olle@dev1]$ javac -d ../classes/ Echo.java EchoClient.java
[olle@dev1]$ cd ../classes/
[olle@dev1]$ ls
Echo.class EchoClient.class EchoImpl_Stub.class
[olle@dev1]$ java EchoClient
Object found!
hej alla
echo: hxexjx xaxlxlxax
ok
echo: oxkx
|
Verkar funka.
4. Echo
Echo ska finnas både på klienten och på servern.
import java.rmi.*;
public interface Echo extends Remote {
public String echo(String s) throws RemoteException;
}
|
Ett krav på gränssnittet är att det ärver av Remote.
5. EchoImpl
EchoImpl är den klass som vi vi ska använda från klienten.
import java.rmi.*;
import java.rmi.server.*;
public class EchoImpl extends UnicastRemoteObject implements EchoInterface{
public Echo() throws RemoteException{
super();
}
public String echo(String s) throws RemoteException {
String res="";
for(int i=0; i<s.length(); i++)
res+=s.charAt(i)+"x";
return res;
}
}
|
Följande delar av EchoImpl är obligatoriska:
- Klassen ärver från UnicastRemoteObject.
- Klassen implementerar EchoInterface.
- Konstruktorn kastar RemoteException.
- Metoden echo() kastar RemoteException.
6. EchoServer
EchoServer är den klass som kör i bakgrunden och tar emot förfrågningar från klienter.
import java.rmi.*;
import java.rmi.server.*;
public class EchoServer{
public static void main (String[] argv) {
try {
Naming.rebind ("Echo", new EchoImpl());
System.out.println ("Echo Server is ready.");
} catch (Exception e) {
System.out.println ("Echo Server failed: " + e);
}
}
}
|
Naming.rebind() utför själva registreringen av klassen i registret. En server kan registrera många klasser, men vi registrerar bara EchoImpl.
7. EchoClient
Med EchoClient testar vi EchoImpl från en annan dator. Är man lite försiktig kan man först testa EchoClient på servern. Då använder man localhost istället för IP-adress.
import java.rmi.*;
import java.rmi.server.*;
import java.io.*;
public class EchoClient{
public static void main (String[] argv) {
try {
// Get Echo
Echo echo = (Echo) Naming.lookup ("rmi://localhost/Echo");
System.out.println("Object found!");
// Use Echo
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
String userInput;
while ((userInput = stdIn.readLine()) != null) {
System.out.println("echo: " + echo.echo(userInput));
}
} catch (Exception e) {
System.out.println ("EchoClient exception: " + e);
}
}
}
|
8. Säkerhet
Det vanligaste problemet med RMI är strul med säkerheten. För att tillåta allt lägger du följande kod överst i main i din klientklass.
System.setSecurityManager (new RMISecurityManager() {
public void checkConnect (String host, int port) {}
public void checkConnect (String host, int port, Object context) {}
});
|
Denna deklarerar en ny RMISecurityManager som tillåter allt. Koden ser lite konstig ut eftersom vi har gjort en anonym inre klass (vilket man inte gör så ofta).
- OBS! Detta är ett fulhack som jag inte vill ta ansvar för. Denna kod sätter SecurityManager ur spel. Använd endast vid test.