Hej! Idag ska vi övervÀga ett ganska intressant Àmne: Java RMI. Detta stÄr för Remote Method Invocation. Du kan anvÀnda RMI för att tillÄta tvÄ program att kommunicera med varandra, Àven om de finns pÄ olika datorer. LÄter det coolt? :) Och det Àr inte sÄ svÄrt att göra! I dagens lektion kommer vi att analysera delarna av RMI-interaktionen och ta reda pÄ hur man konfigurerar den. Det första vi behöver Àr en klient och en server. Vi behöver egentligen inte dyka djupt in i datorterminologi. NÀr det kommer till RMI Àr det bara tvÄ program. En av dem kommer att inkludera ett objekt, och den andra kommer att anropa metoder för det objektet. Anropa metoder för ett objekt som finns i ett annat program - nu Àr det nÄgot vi inte har gjort Àn! Det Àr dags att prova! :) För att undvika att fastna, lÄt' s hÄlla vÄrt program enkelt. I allmÀnhet utför en server vissa berÀkningar som begÀrs av en klient. Och sÄ kommer det att vara med oss. VÄr server kommer att vara ett enkelt kalkylatorprogram. Det kommer bara att ha en metod:multiplicera() . Den kommer att multiplicera tvÄ siffror som skickas till den av klientprogrammet och sedan returnera resultatet. Först och frÀmst behöver vi ett grÀnssnitt:

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Calculator extends Remote {

   int multiply(int x, int y) throws RemoteException;
}
Varför behöver vi ett grÀnssnitt? Eftersom RMI förlitar sig pÄ att skapa proxyservrar, som du studerade i tidigare lektioner . Som du sÀkert kommer ihÄg arbetar vi med proxyservrar genom grÀnssnitt, inte klasser. Det finns 2 viktiga krav för vÄrt grÀnssnitt!
  1. Det mÄste utöka fjÀrrgrÀnssnittet.
  2. Alla dess metoder mÄste ge ett RemoteException (IDE kommer inte att göra detta automatiskt - du mÄste lÀgga till detta manuellt!).
Nu mĂ„ste vi skapa en serverklass som implementerar vĂ„rt Calculator- grĂ€nssnitt. RMI i praktiken - 2Även hĂ€r Ă€r allt ganska enkelt:

import java.rmi.RemoteException;

public class RemoteCalculationServer implements Calculator {

   @Override
   public int multiply(int x, int y) throws RemoteException {
       return x*y;
   }

}
Det finns egentligen inget att kommentera hÀr :) Nu mÄste vi skriva ett serverprogram som ska konfigurera och köra vÄrt kalkylatorobjekt. Det kommer att se ut sÄ hÀr:

import java.rmi.AlreadyBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

public class ServerMain {

   public static final String UNIQUE_BINDING_NAME = "server.calculator";

   public static void main(String[] args) throws RemoteException, AlreadyBoundException, InterruptedException {

       final RemoteCalculationServer server = new RemoteCalculationServer();

       final Registry registry = LocateRegistry.createRegistry(2732);

       Remote stub = UnicastRemoteObject.exportObject(server, 0);
       registry.bind(UNIQUE_BINDING_NAME, stub);

       Thread.sleep(Integer.MAX_VALUE);

   }
}
LÄt oss ta reda pÄ det hÀr :) PÄ den första raden deklarerar vi nÄgon strÀngvariabel:

public static final String UNIQUE_BINDING_NAME = "server.calculator";
Denna strÀng Àr fjÀrrobjektets unika namn. VÄrt klientprogram anvÀnder detta namn för att hitta vÄr server: du kommer att se detta senare. DÀrefter skapar vi vÄrt kalkylatorobjekt:

final RemoteCalculationServer server = new RemoteCalculationServer();
Allt Àr klart hÀr. Vad som kommer hÀrnÀst Àr mer intressant:

final Registry registry = LocateRegistry.createRegistry(2732);
Detta registerobjekt Ă€r ett register över fjĂ€rrobjekt. Det hĂ€r Ă€r objekt som andra program kan komma Ă„t pĂ„ distans :) Vi skickade numret 2732 till metoden LocateRegistry.createRegistry() . Detta Ă€r portnumret — ett unikt nummer som andra program kommer att anvĂ€nda för att hitta vĂ„rt objektregister (igen, du kommer att se detta nedan). GĂ„r rakt fram... LĂ„t oss se vad som hĂ€nder i nĂ€sta rad:

Remote stub = UnicastRemoteObject.exportObject(server, 0);
Vi skapar en stubb i denna linje. En stubb kapslar in hela fjÀrrsamtalet. Du kan betrakta detta som den viktigaste delen av RMI. Vad gör den?
  1. Den tar emot all information om ett fjÀrranrop av nÄgon metod.
  2. Om metoden har parametrar kommer stubben att deserialisera dem. Var uppmĂ€rksam pĂ„ denna punkt! Argumenten som du skickar till de fjĂ€rranropade metoderna mĂ„ste kunna serialiseras (de kommer trots allt att sĂ€ndas över nĂ€tverket). Detta Ă€r inga problem för oss – vi sĂ€nder bara nummer. Men om du sĂ€nder objekt, glöm inte detta krav!
  3. Efter det anropar stubben den önskade metoden.
Vi skickar vÄrt kalkylatorserverobjekt till metoden UnicastRemoteObject.exportObject() . Det Àr sÄ vi gör det möjligt att pÄ distans anropa dess metoder. Det finns bara en sak kvar att göra:

registry.bind(UNIQUE_BINDING_NAME, stub);
Vi "registrerar" vÄr stubb i fjÀrrobjektregistret under det namn vi hittade pÄ i början. Nu kommer kunden att kunna hitta den! Du kanske mÀrkte att vi lade programmets huvudtrÄd i vila i slutet:

Thread.sleep(Integer.MAX_VALUE);
Vi behöver bara servern att köra lÀnge. I IDE kommer vi samtidigt att starta tvÄ main()- metoder: för det första serverns main()-metod (i klassen ServerMain , som vi redan har skrivit), och för det andra, klientens main()-metod (i klassen ClientMain , som vi kommer att skriva nedan). Det Àr viktigt att serverprogrammet inte avslutas medan vi startar klienten, sÄ vi lÀgger den bara i vila lÀnge. Hur som helst kommer den att fortsÀtta köras :) Nu kan vi köra vÄr servers main()- metod. LÄt det köra och vÀnta pÄ att klientprogrammet ska anropa nÄgon metod :) LÄt oss nu skriva klientprogrammet! Det kommer att skicka siffror till vÄr server för multiplikation.

import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class ClientMain {

   public static final String UNIQUE_BINDING_NAME = "server.calculator";

   public static void main(String[] args) throws RemoteException, NotBoundException {

       final Registry registry = LocateRegistry.getRegistry(2732);

       Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);

       int multiplyResult = calculator.multiply(20, 30);

       System.out.println(multiplyResult);
   }
}
Det ser enkelt ut. Men vad hÀnder hÀr? Först mÄste klienten kÀnna till det unika namnet pÄ objektet vars metoder den kommer att anropa pÄ distans. Följaktligen skapade vi i klientprogrammet den offentliga statiska slutstrÀngen UNIQUE_BINDING_NAME = "server.calculator"; variabel. DÀrefter, i main() -metoden, fÄr vi tillgÄng till registret över fjÀrrobjekt. För att göra detta mÄste vi anropa metoden LocateRegistry.getRegistry() och skicka portnumret som anvÀnds för att skapa vÄrt register i ServerMain-programmet (port 2732; detta nummer Àr bara ett exempel - du kan prova att anvÀnda ett annat nummer):

final Registry registry = LocateRegistry.getRegistry(2732);
Nu behöver vi bara hÀmta önskat objekt frÄn registret! Detta Àr enkelt, eftersom vi kÀnner till dess unika namn!

Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);
Var uppmÀrksam pÄ typgjutning. Vi castar det mottagna objektet till Calculator- grÀnssnittet, inte till klassen RemoteCalculationServer . Som vi sa i början av lektionen förlitar sig RMI pÄ en proxy, sÄ fjÀrranrop Àr endast tillgÀngliga för metoderna för ett grÀnssnitt, inte metoderna för en klass. Slutligen anropar vi multiply() -metoden pÄ vÄrt objekt pÄ distans och matar ut resultatet till konsolen.

int multiplyResult = calculator.multiply(20, 30);
System.out.println(multiplyResult);
ServerMain - klassens main()- metod har redan körts lÀnge. Nu Àr det dags att köra metoden main() i klientprogrammet ( ClientMain ) ! KonsolutgÄng:

600
Det Àr allt! VÄrt program (tvÄ program, faktiskt!) gjorde vad det skulle :) Har du tid och lust kan du krydda det hÀr lite. LÄt till exempel rÀknaren stödja de fyra vanliga aritmetiska operationerna och skicka siffror inte som primitiva typer, utan som CalculationInstance(int x, int y) -objekt. Vi ses i nÀsta lektion! :)