programmera.net -> java -> normal     för utskrift      info@programmera.net

RMI Echo

1. Exemplet Echo: Översikt
2. Exemplet Echo: Serversidan
3. Exemplet Echo: Klientsidan
4. Echo
5. EchoImpl
6. EchoServer
7. EchoClient
8. Säkerhet

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:

2. Exemplet Echo: Serversidan

På serversidan behöver vi göra följande:

  1. Kompilera gränssnittet Echo.java.
  2. Kompilera klassen EchoImpl.java.
  3. Efter kompileringen av EchoImpl.java måste en stubbe och ett skelett skapas med kommandot rmic EchoImpl.
  4. Kompilera servern EchoServer.java.
  5. Starta registret med rmiregistry.
  6. 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:

  1. Kopiera gränssnittet Echo.java från servern till klienten.
  2. Kopiera stubben EchoImpl_Stub.class från servern till klienten.
  3. Skriva och kompilera klassen EchoClient.java.
  4. 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.