javabog.dk  |  << forrige  |  indhold  |  næste >>  |  programeksempler  |  om bogen

19 RMI - objekter over netværk

Indhold:

Kapitlet forudsættes ikke i resten af bogen.

Forudsætter kapitel Fejl: Henvisningskilde ikke fundet, Fejl: Henvisningskilde ikke fundet, kapitel Fejl: Henvisningskilde ikke fundet, Fejl: Henvisningskilde ikke fundet og kendskab til netværk.

Med RMI (Remote Method Invocation) kan man arbejde med objekter, der eksisterer i en anden Java virtuel maskine (ofte på en anden fysisk maskine), som om de var lokale objekter.

19.1 Principper

Herunder er tegnet, hvad der sker, når en klient på maskine A laver et kald til et serverobjekt (værts-objekt), der er i maskine B.

Serverobjektet findes slet ikke på maskine A, i stedet er der en såkaldt RMI-stub, der repræsenterer det. Når der sker et kald til RMI-stubben på maskine A, sørger den for at transportere kaldet og alle parametre til maskine B, hvor serverobjektet bliver kaldt, som om det var et lokalt kald. Serverobjektets svar bliver transporteret tilbage til RMI-stubben, der returnerer det til klienten.

Denne proces foregår helt automatisk og er usynlig for klienten såvel som serverobjektet.

RMI benytter serialisering til at transportere parametre og returværdi mellem maskinerne, så man skal huske, at alle objekter, der sendes over netværket, skal implementere Serializable-interfacet og at variabler, der ikke skal overføres, skal mærkes med nøgleordet transient.

Der skal være defineret et interface (kaldet fjerninterfacet) til de metoder på serverobjektet, som skal være tilgængelige for klienten. Serverobjekt skal implementere dette interface.

19.2 I praksis

Lad os forestille os, at serveren har et konto-objekt, hvor man kan overføre penge, spørge om saldo og få bevægelserne. Disse metoder skal være tilgængelige over netværket, så vi definerer et fjerninterface til kontoen (her kaldt KontoI):

import java.util.ArrayList;

public interface KontoI extends java.rmi.Remote
{
  public void overførsel(int kroner) throws java.rmi.RemoteException;
  public int saldo()                 throws java.rmi.RemoteException;
  public ArrayList bevægelser()      throws java.rmi.RemoteException;
}

Fjerninterfacet skal arve fra interfacet java.rmi.Remote og alle metoder skal kunne kaste java.rmi.RemoteException.

19.2.1 På serversiden

På serversiden skal vi implementere Konto-interfacet og programmere den funktionalitet, der skjuler sig bag det i et serverobjekt, der skal arve fra UnicastRemoteObject. Klassenavnet ender normalt på Impl (for at vise, at det er implementationen af fjerninterfacet).

import java.util.ArrayList;
import java.rmi.server.UnicastRemoteObject;

public class KontoImpl extends UnicastRemoteObject implements KontoI
{
  public int saldo;
  public ArrayList bevægelser;

  public KontoImpl() throws java.rmi.RemoteException 
  {
    // man starter med 100 kroner
    saldo = 100;
    bevægelser = new ArrayList();
  }

  public void overførsel(int kroner)
  {
    saldo = saldo + kroner;
    String s = "Overførsel på "+kroner+" kr. Ny saldo er "+saldo+" kr.";
    bevægelser.add(s);
    System.out.println(s);
  }

  public int saldo()
  {
    System.out.println("Der spørges om saldoen. Den er "+saldo+" kr.");
    return saldo;
  }

  public ArrayList bevægelser()
  {
    System.out.println("Der spørges på alle bevægelser.");
    return bevægelser;
  }
}

Nu skal vi oprette et serverobjekt og registrere vores tjeneste under et navn i RMI:

import java.rmi.Naming;
public class Kontoserver
{
  public static void main(String[] arg) throws Exception
  {
    // Enten: Kør programmet 'rmiregistry' fra mappen med .class-filerne, eller:
    java.rmi.registry.LocateRegistry.createRegistry(1099); // start i server-JVM

    KontoI k = new KontoImpl();
    Naming.rebind("rmi://localhost/kontotjeneste", k);
    System.out.println("Kontotjeneste registreret.");
  }
}

Kontotjeneste registreret.
...

Programmet afslutter ikke, men venter på, at noget henvender sig, for at bruge tjenesten.

For at registreringen kan foregå, skal der køre en RMI-navnetjeneste, der holder styr på, hvilke tjenester, der udbydes under hvilke navne og formidler kontakten til dem. Det er et lille program, der hedder rmiregistry og som normalt lytter på port 1099. Det nemmeste er dog at starte navnetjenesten i samme JVM som serverobjektet kører, som vist ovenfor1.

19.2.2 På klientsiden

På klientsiden skal vi slå serverobjektet op i RMI-tjenesten og derefter bruge det objekt, vi får retur, som om det var serverobjektet selv (i virkeligheden er det RMI-stubben):

import java.rmi.Naming;

public class Kontoklient
{
  public static void main(String[] arg) throws Exception
  {
    KontoI k =(KontoI) Naming.lookup("rmi://localhost/kontotjeneste");
    k.overførsel(100);
    k.overførsel(50);
    System.out.println( "Saldo er: "+ k.saldo() );
    k.overførsel(-200);
    k.overførsel(51);
    System.out.println( "Saldo er: "+ k.saldo() );
    java.util.ArrayList bevægelser = k.bevægelser();

    System.out.println( "Bevægelser er: "+ bevægelser );
  }
}

Saldo er: 250
Saldo er: 101
Bevægelser er: [Overførsel på 100 kr. Ny saldo er 200 kr., Overførsel på 50 kr. Ny saldo er 250 kr., Overførsel på -200 kr. Ny saldo er 50 kr., Overførsel på 51 kr. Ny saldo er 101 kr.]

Sammen med Kontoklient skal ligge fjerninterfacet KontoI.

Mens kontoklienten kører, kommer der følgende uddata fra Kontoserver:

Overførsel på 100 kr. Ny saldo er 200 kr.
Overførsel på 50 kr. Ny saldo er 250 kr.
Der spørges om saldoen. Den er 250 kr.
Overførsel på -200 kr. Ny saldo er 50 kr.
Overførsel på 51 kr. Ny saldo er 101 kr.
Der spørges om saldoen. Den er 101 kr.
Der spørges på alle bevægelser.

På figuren til højre ses de enkelte klassers funktioner. Bemærk at Java automatisk genererer stub- og skel-klasserne.

19.3 Opgaver

Start serveren og kør klienten et par gange.

19.3.1 Server og klient to forskellige steder

Når ovenstående fungerer, så ret i Kontoklient, sådan at klienten kontakter en anden maskine. Er værtsmaskinens IP-nummer f.eks. 192.168.1.42, retter du i Kontoklient til:

    KontoI k =(KontoI) Naming.lookup("rmi://192.168.1.42/kontotjeneste");

19.3.2 Starte separat 'rmiregistry'

Fjern kaldet til java.rmi.registry.LocateRegistry.createRegistry(1099) fra Kontoserver og start i stedet rmiregistry i et separat vindue. Husk at det skal kende definitionen af serverobjekter og evt. klasser, der overføres med RMI, så CLASSPATH skal sættes eller rmiregistry skal startes fra den samme mappe, som bytekoden findes. Ligger .class-filerne i C:\jbproject\mitProjekt\classes, kunne du åbne en DOS-kommandoprompt2 og skrive:

  cd C:\jbproject\mitProjekt\classes
rmiregistry

1Vil du hellere køre rmiregistry i et separat vindue, så se øvelsen sidst i kapitlet.

2I Windows fås en DOS-prompt ved at klikke i menuen Start, vælge 'Kør...' og skrive 'cmd'.

javabog.dk  |  << forrige  |  indhold  |  næste >>  |  programeksempler  |  om bogen
http://javabog.dk/ - Forord af Jacob Nordfalk.
Licens og kopiering under Åben Dokumentlicens (ÅDL) hvor intet andet er nævnt (79% af værket).

Ønsker du at se de sidste 21% af dette værk (267325 tegn) skal du købe bogen. Så får du pæne figurer og layout, stikordsregister og en trykt bog med i købet.