



<html>
<head>
  <title>javabog.dk -  - RMI - objekter over netv&aelig;rk</title>
  <link rev="stylesheet" type="text/css" href="../typografi.css">
  <meta name="description" content="Lærebog i Java. Af Jacob Nordfalk. Udkommet hos Forlaget Globe">
  <meta name="keywords" content="designmønster, programmering, OOP, objekter, klasser, objektorienteret programmering, Java, JSP, lærebog, UML, IT">
</head>
<body bgcolor="#ffffff">



<a href='http://javabog.dk/'>javabog.dk</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel18.jsp'>&lt;&lt; forrige</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='indhold.jsp'>indhold</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel20.jsp'>n&aelig;ste &gt;&gt;</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kode/'>programeksempler</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='../index_OOP.html'>om bogen</a>

<H1 CLASS="western" STYLE="">19 <a name='afsn19'></a>RMI -
objekter over netv&aelig;rk</H1>
<P LANG="da-DK" CLASS="kapiteloversigt-western">Indhold:</P>
<UL>
  <LI VALUE=1><P LANG="da-DK" CLASS="kapiteloversigt-western">Forst&aring;
  principperne i RMI</P>
  <LI><P LANG="da-DK" CLASS="kapiteloversigt-western">Kalde metoder i
  fjerne objekter</P>
</UL>
<P LANG="da-DK" CLASS="kapiteloversigt-western">Kapitlet foruds&aelig;ttes
ikke i resten af bogen.</P>
<P LANG="da-DK" CLASS="kapiteloversigt-western">Foruds&aelig;tter
<a href='kapitel12.jsp'>kapitel 12</a>, Interfaces, <a href='kapitel18.jsp'>kapitel 18</a>, Serialisering og kendskab til
netv&aelig;rk.</P>


<P LANG="da-DK" CLASS="western" STYLE="">Med
RMI (Remote Method Invocation) kan man arbejde med objekter, der
eksisterer i en anden Java virtuel maskine (ofte p&aring; en anden
fysisk maskine), <I>som om de var lokale objekter</I>.</P>
<H2 CLASS="western">19.1 <a name='afsn19.1'></a>Principper</SPAN></H2>
<P LANG="da-DK" CLASS="western">Herunder er tegnet, hvad der sker,
n&aring;r en klient p&aring; maskine A laver et kald til et
serverobjekt (v&aelig;rts-objekt), der er i maskine B.</P>
<P LANG="da-DK" CLASS="western" ALIGN=CENTER><IMG SRC="bog20_html_716317b1.gif" NAME="Objekt1" ALIGN=MIDDLE></P>
<P LANG="da-DK" CLASS="western">Serverobjektet findes slet ikke p&aring;
maskine A, i stedet er der en s&aring;kaldt <I>RMI-stub</I>, der
repr&aelig;senterer det. N&aring;r der sker et kald til
RMI-stubben p&aring; maskine A, s&oslash;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.</P>
<P LANG="da-DK" CLASS="western">Denne proces foreg&aring;r helt
automatisk og er usynlig for klienten s&aring;vel som serverobjektet.
</P>
<P LANG="da-DK" CLASS="western">RMI benytter serialisering til at
transportere parametre og returv&aelig;rdi mellem maskinerne, s&aring;
man skal huske, at alle objekter, der sendes over netv&aelig;rket,
skal implementere Serializable-interfacet og at variabler, der
ikke skal overf&oslash;res, skal m&aelig;rkes med n&oslash;gleordet
transient.</P>
<P LANG="da-DK" CLASS="western">Der skal v&aelig;re defineret et
interface (kaldet fjerninterfacet) til de metoder p&aring;
serverobjektet, som skal v&aelig;re tilg&aelig;ngelige for klienten.
Serverobjekt skal implementere dette interface.</P>
<H2 CLASS="western">19.2 <a name='afsn19.2'></a>I praksis</SPAN></H2>
<P LANG="da-DK" CLASS="western">Lad os forestille os, at serveren har
et konto-objekt, hvor man kan overf&oslash;re penge, sp&oslash;rge om
saldo og f&aring; bev&aelig;gelserne. Disse metoder skal v&aelig;re
tilg&aelig;ngelige over netv&aelig;rket, s&aring; vi definerer et
fjerninterface til kontoen (her kaldt KontoI):</P>
<PRE CLASS="kode-western">import java.util.ArrayList;

public interface KontoI extends java.rmi.Remote
{
  public void overf&oslash;rsel(int kroner) throws java.rmi.RemoteException;
  public int saldo()                 throws java.rmi.RemoteException;
  public ArrayList bev&aelig;gelser()      throws java.rmi.RemoteException;
}</PRE><P LANG="da-DK" CLASS="western">
Fjerninterfacet skal arve fra interfacet java.rmi.Remote og alle
metoder skal kunne kaste undtagelsen java.rmi.RemoteException. 
</P>
<H3 CLASS="western">19.2.1 <a name='afsn19.2.1'></a>P&aring; serversiden</H3>
<P LANG="da-DK" CLASS="western">P&aring; 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&aring; Impl (for at
vise, at det er implementationen af fjerninterfacet).</P>
<PRE CLASS="kode-western">import java.util.ArrayList;
import java.rmi.server.UnicastRemoteObject;

public class KontoImpl <B>extends UnicastRemoteObject implements KontoI</B>
{
<B>  public int saldo;</B>
<B>  public ArrayList bev&aelig;gelser;</B>

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

  public void overf&oslash;rsel(int kroner)
  {
    saldo = saldo + kroner;
    String s = &quot;Overf&oslash;rsel p&aring; &quot;+kroner+&quot; kr. Ny saldo er &quot;+saldo+&quot; kr.&quot;;
    bev&aelig;gelser.add(s);
    System.out.println(s);
  }

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

  public ArrayList bev&aelig;gelser()
  {
    System.out.println(&quot;Der sp&oslash;rges p&aring; alle bev&aelig;gelser.&quot;);
    return bev&aelig;gelser;
  }
}</PRE>
<P LANG="da-DK" CLASS="western">Nu skal vi oprette et serverobjekt og
registrere vores tjeneste under et navn i RMI:</P>
<PRE CLASS="kode-western">import java.rmi.Naming;
public class Kontoserver
{
  public static void main(String[] arg) throws Exception
  {
<I>    // Enten: K&oslash;r programmet 'rmiregistry' fra mappen med .class-filerne, eller:</I>
    java.rmi.registry.LocateRegistry.createRegistry(1099); <I>// start i server-JVM</I>

    KontoI <B>k = new KontoImpl()</B>;
<B>    Naming.rebind(&quot;rmi://localhost/kontotjeneste&quot;, k);</B>
    System.out.println(&quot;Kontotjeneste registreret.&quot;);
  }
}</PRE>
<HR>
<PRE CLASS="kode-western">Kontotjeneste registreret.
...</PRE><P LANG="da-DK" CLASS="western">
Programmet afslutter ikke, men venter p&aring;, at noget henvender
sig, for at bruge tjenesten.</P>
<P LANG="da-DK" CLASS="western">For at registreringen kan foreg&aring;,
skal der k&oslash;re en RMI-navnetjeneste, der holder styr p&aring;,
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&aring; port 1099. Det nemmeste er
dog at starte navnetjenesten i samme JVM som serverobjektet k&oslash;rer,
som vist ovenfor<A CLASS="sdfootnoteanc" NAME="sdfootnote1anc" HREF="#sdfootnote1sym"><SUP>1</SUP></A>.</P>

<H3 CLASS="western">19.2.2 <a name='afsn19.2.2'></a>P&aring; klientsiden</H3>
<P LANG="da-DK" CLASS="western">P&aring; klientsiden skal vi sl&aring;
serverobjektet op i RMI-tjenesten og derefter bruge det objekt, vi
f&aring;r retur, som om det var serverobjektet selv (i virkeligheden
er det RMI-stubben):</P>
<PRE CLASS="kode-western">import java.rmi.Naming;

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

    System.out.println( &quot;Bev&aelig;gelser er: &quot;+ bev&aelig;gelser );
  }
}</PRE>
<HR>
<PRE CLASS="kode-western">Saldo er: 250
Saldo er: 101
Bev&aelig;gelser er: [Overf&oslash;rsel p&aring; 100 kr. Ny saldo er 200 kr., Overf&oslash;rsel p&aring; 50 kr. Ny saldo er 250 kr., Overf&oslash;rsel p&aring; -200 kr. Ny saldo er 50 kr., Overf&oslash;rsel p&aring; 51 kr. Ny saldo er 101 kr.]</PRE><P LANG="da-DK" CLASS="western">
Sammen med Kontoklient skal ligge fjerninterfacet KontoI.</P>
<P LANG="da-DK" CLASS="western">Mens kontoklienten k&oslash;rer,
kommer der f&oslash;lgende uddata fra Kontoserver:</P>
<PRE CLASS="kode-western">Overf&oslash;rsel p&aring; 100 kr. Ny saldo er 200 kr.
Overf&oslash;rsel p&aring; 50 kr. Ny saldo er 250 kr.
Der sp&oslash;rges om saldoen. Den er 250 kr.
Overf&oslash;rsel p&aring; -200 kr. Ny saldo er 50 kr.
Overf&oslash;rsel p&aring; 51 kr. Ny saldo er 101 kr.
Der sp&oslash;rges om saldoen. Den er 101 kr.
Der sp&oslash;rges p&aring; alle bev&aelig;gelser.</PRE><P LANG="da-DK" CLASS="western">
<IMG SRC="bog20_html_340d52e.gif" NAME="Objekt2" ALIGN=RIGHT>P&aring;
figuren til h&oslash;jre ses de enkelte klassers funktioner. Bem&aelig;rk
at Java automatisk genererer stub- og skel-klasserne. 
</P>
<H2 CLASS="western">19.3 <a name='afsn19.3'></a>Opgaver</SPAN></H2>
<P LANG="da-DK" CLASS="western">Start serveren og k&oslash;r klienten
et par gange.</P>
<H3 CLASS="western">19.3.1 <a name='afsn19.3.1'></a>Server og klient to forskellige steder</H3>
<P LANG="da-DK" CLASS="western">N&aring;r ovenst&aring;ende fungerer,
s&aring; ret i Kontoklient, s&aring;dan at klienten kontakter en
anden maskine. Er v&aelig;rtsmaskinens IP-nummer f.eks. 192.168.1.42,
retter du i Kontoklient til:</P>
<PRE CLASS="kode-western">    KontoI k =(KontoI) Naming.lookup(&quot;rmi://192.168.1.42/kontotjeneste&quot;);</PRE><H3 CLASS="western">
19.3.2 <a name='afsn19.3.2'></a>Starte separat 'rmiregistry'</H3>
<P LANG="da-DK" CLASS="western">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&oslash;res med RMI, s&aring; CLASSPATH skal s&aelig;ttes eller
rmiregistry skal startes fra den samme mappe, som bytekoden findes.
Ligger .class-filerne i C:\jbproject\mitProjekt\classes, kunne du
&aring;bne en DOS-kommandoprompt<A CLASS="sdfootnoteanc" NAME="sdfootnote2anc" HREF="#sdfootnote2sym"><SUP>2</SUP></A>
og skrive: 
</P>
<PRE CLASS="western">  cd C:\jbproject\mitProjekt\classes<BR>  rmiregistry</PRE>
<DIV ID="sdfootnote1">
  <P LANG="da-DK" CLASS="sdfootnote-western"><A CLASS="sdfootnotesym" NAME="sdfootnote1sym" HREF="#sdfootnote1anc">1</A>Vil
  du hellere k&oslash;re rmiregistry i et separat vindue, s&aring; se
  &oslash;velsen sidst i kapitlet.</P>
</DIV>
<DIV ID="sdfootnote2">
  <P LANG="da-DK" CLASS="sdfootnote-western"><A CLASS="sdfootnotesym" NAME="sdfootnote2sym" HREF="#sdfootnote2anc">2</A>I
  Windows f&aring;s en DOS-prompt ved at klikke i menuen Start, v&aelig;lge
  'K&oslash;r...' og skrive 'cmd'.</P>
</DIV>

<a href='http://javabog.dk/'>javabog.dk</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel18.jsp'>&lt;&lt; forrige</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='indhold.jsp'>indhold</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel20.jsp'>n&aelig;ste &gt;&gt;</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kode/'>programeksempler</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='../index_OOP.html'>om bogen</a>
<hr>
<font size=-2>http://javabog.dk/ - <b></b> af Jacob Nordfalk.
<br>
  Licens og kopiering under <a href='http://www.linuxbog.dk/licens.html'>&Aring;ben Dokumentlicens</a> (&Aring;DL)
  hvor intet andet er nævnt (79% af værket).
</font>
<br>
Ønsker du at se de sidste 21% af dette værk (226970 tegn)
skal du købe bogen. Så får du pæne figurer og layout, stikordsregister og en trykt bog med i købet.
<!-- netlæser: Wget/1.10, autoHent: true  -->
     

</body>
</html>
