Hei! I dag skal vi vurdere et ganske interessant emne: Java RMI. Dette står for Remote Method Invocation. Du kan bruke RMI til å la to programmer kommunisere med hverandre, selv om de er på forskjellige datamaskiner. Høres det kult ut? :) Og det er ikke så vanskelig å gjøre! I dagens leksjon vil vi analysere elementene i RMI-interaksjonen og finne ut hvordan du konfigurerer den. Det første vi trenger er en klient og en server. Vi trenger egentlig ikke å dykke dypt inn i dataterminologi. Når det gjelder RMI, er dette bare to programmer. En av dem vil inkludere et objekt, og den andre vil kalle metoder på det objektet. Kalle metoder for et objekt som eksisterer i et annet program - nå er det noe vi ikke har gjort ennå! Det er på tide å prøve det! :) For å unngå å gå fast, la' s holde programmet vårt enkelt. Generelt utfører en server noen beregninger som er forespurt av en klient. Og slik vil det være med oss. Serveren vår vil være et enkelt kalkulatorprogram. Det vil bare ha én metode:multiplisere() . Den vil multiplisere to tall sendt til den av klientprogrammet, og deretter returnere resultatet. Først av alt trenger vi et grensesnitt:

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

public interface Calculator extends Remote {

   int multiply(int x, int y) throws RemoteException;
}
Hvorfor trenger vi et grensesnitt? Fordi RMI er avhengig av å lage proxyer, som du studerte i tidligere leksjoner . Som du sikkert husker jobber vi med proxyer gjennom grensesnitt, ikke klasser. Det er 2 viktige krav til grensesnittet vårt!
  1. Den må utvide det eksterne grensesnittet.
  2. Alle metodene må gi et RemoteException (IDE-en vil ikke gjøre dette automatisk - du må legge til dette manuelt!).
Nå må vi lage en serverklasse som implementerer kalkulatorgrensesnittet vårt . RMI i praksis - 2Også her er alt ganske enkelt:

import java.rmi.RemoteException;

public class RemoteCalculationServer implements Calculator {

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

}
Det er egentlig ikke noe å kommentere her :) Nå må vi skrive et serverprogram som skal konfigurere og kjøre kalkulatorobjektet vårt. Det vil se slik ut:

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);

   }
}
La oss finne ut av dette :) I den første linjen erklærer vi en strengvariabel:

public static final String UNIQUE_BINDING_NAME = "server.calculator";
Denne strengen er det eksterne objektets unike navn. Vårt klientprogram bruker dette navnet for å finne serveren vår: du vil se dette senere. Deretter lager vi vårt kalkulatorobjekt:

final RemoteCalculationServer server = new RemoteCalculationServer();
Alt er klart her. Det som kommer etterpå er mer interessant:

final Registry registry = LocateRegistry.createRegistry(2732);
Dette registerobjektet er et register over eksterne objekter. Dette er objekter som andre programmer kan få ekstern tilgang :) Vi sendte nummeret 2732 til LocateRegistry.createRegistry()- metoden. Dette er portnummeret – et unikt nummer som andre programmer vil bruke for å finne objektregisteret vårt (igjen, du vil se dette nedenfor). Går rett langs... La oss se hva som skjer i neste linje:

Remote stub = UnicastRemoteObject.exportObject(server, 0);
Vi lager en stubbe i denne linjen. En stump innkapsler hele fjernsamtalen. Du kan vurdere dette som det viktigste elementet i RMI. Hva gjør den?
  1. Den mottar all informasjon om et eksternt anrop av en eller annen metode.
  2. Hvis metoden har parametere, vil stubben deserialisere dem. Vær oppmerksom på dette punktet! Argumentene du sender til de eksternt kalte metodene må kunne serialiseres (de vil tross alt bli overført over nettverket). Dette er ikke noe problem for oss – vi overfører bare tall. Men hvis du overfører objekter, ikke glem dette kravet!
  3. Etter det kaller stubben ønsket metode.
Vi sender kalkulatorserverobjektet vårt til UnicastRemoteObject.exportObject() -metoden. Dette er hvordan vi gjør det mulig å eksternt kalle metodene. Det er bare én ting igjen å gjøre:

registry.bind(UNIQUE_BINDING_NAME, stub);
Vi "registrerer" stubben vår i det eksterne objektregisteret under navnet vi fant opp helt i begynnelsen. Nå vil klienten kunne finne den! Kanskje du la merke til at vi la programmets hovedtråd i dvale på slutten:

Thread.sleep(Integer.MAX_VALUE);
Vi trenger bare at serveren skal kjøre lenge. I IDE vil vi samtidig lansere to main()- metoder: først serverens main()-metode (i ServerMain -klassen, som vi allerede har skrevet), og for det andre klientens main()-metode (i ClientMain - klassen, som vi vil skrive nedenfor). Det er viktig at serverprogrammet ikke avsluttes mens vi starter klienten, så vi legger den bare i dvale lenge. Uansett vil den fortsette å kjøre :) Nå kan vi kjøre serverens main()- metode. La det kjøre og vent på at klientprogrammet kaller en eller annen metode :) La oss nå skrive klientprogrammet! Det vil sende tall til serveren vår for multiplikasjon.

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 hva skjer her? Først må klienten kjenne det unike navnet på objektet hvis metoder den vil kalle eksternt. Følgelig opprettet vi i klientprogrammet den offentlige statiske endelige strengen UNIQUE_BINDING_NAME = "server.kalkulator"; variabel. Deretter, i main()- metoden, får vi tilgang til registeret over eksterne objekter. For å gjøre dette, må vi ringe LocateRegistry.getRegistry()- metoden og sende portnummeret som ble brukt til å opprette registret vårt i ServerMain-programmet (port 2732; dette nummeret er bare et eksempel - du kan prøve å bruke et annet nummer):

final Registry registry = LocateRegistry.getRegistry(2732);
Nå trenger vi bare å hente ønsket objekt fra registeret! Dette er enkelt, fordi vi kjenner det unike navnet!

Calculator calculator = (Calculator) registry.lookup(UNIQUE_BINDING_NAME);
Vær oppmerksom på type støping. Vi caster det mottatte objektet til Calculator- grensesnittet, ikke til RemoteCalculationServer -klassen. Som vi sa i begynnelsen av leksjonen, er RMI avhengig av en proxy, så fjernanrop er kun tilgjengelig for metodene til et grensesnitt, ikke metodene til en klasse. Til slutt kaller vi multiply() -metoden eksternt på objektet vårt og sender resultatet til konsollen.

int multiplyResult = calculator.multiply(20, 30);
System.out.println(multiplyResult);
ServerMain - klassens main()- metode har allerede kjørt i lang tid. Nå er det på tide å kjøre main()- metoden i klientprogrammet ( ClientMain )! Konsoll utgang:

600
Det er det! Programmet vårt (to programmer, faktisk!) gjorde det det skulle :) Har du tid og lyst kan du krydre dette litt. La for eksempel kalkulatoren støtte de fire standard aritmetiske operasjonene, og gi tall ikke som primitive typer, men som CalculationInstance(int x, int y) -objekter. Vi sees i neste leksjon! :)