2. Java-interface og RMI.

Like dokumenter
Løsningsskisse, eksamen J2EE og distribuerte systemer 19.mai 2004

1. Tråder og socketprogrammering.

Java RMI (Remote Method Invocation) Gruppe 9: Ivar Steien Rasmussen Tom Anders Dalseng Andreas Petlund

LC191D Videregående programmering Høgskolen i Sør-Trøndelag, Avdeling for informatikk og e-læring. Else Lervik, januar 2012.

Videregående programmering 6

Java RMI. Introduksjon. Markus Foss Hans-Gunnar Vold

Hittil har programmene kommunisert med omverden via tastatur og skjerm Ønskelig at data kan leve fra en kjøring til neste

Kapittel 8: Programutvikling

JSP - 2. Fra sist. Hvordan fungerer web? Tjenerside script HTML. Installasjon av Web-tjener Et enkelt JSP-script. Ønsker dynamiske nettsider:

Kapittel 7: Mer om arv

HØGSKOLEN I SØR-TRØNDELAG

Dagens tema Kapittel 8: Objekter og klasser

Forklaring til programmet AbstraktKontoTest.java med tilhørende filer Konto.java, KredittKonto.java, SpareKonto.java

Repitisjonskurs. Arv, Subklasser og Grensesnitt

En klasse som arver, eller selv deklarerer en abstrakt metode, må deklareres som abstrakt.

Konstruktører. Bruk av konstruktører når vi opererer med "enkle" klasser er ganske ukomplisert. Når vi skriver. skjer følgende:

INF1000: Forelesning 7. Konstruktører Static

UNIVERSITETET I OSLO

INF1000: Forelesning 7

1- og 2-veis Innkapsling Java Stabel Kø Prio-kø Iterator. Enveis- og toveislister Innkapsling («boxing») (Big Java 6.8.5)

Løsningsforslag ukeoppg. 6: 28. sep - 4. okt (INF Høst 2011)

HØGSKOLEN I SØR-TRØNDELAG

INF1000: Forelesning 6. Klasser og objekter del 1

1- og 2-veis Innkapsling Java Stabel Kø Prio-kø Iterator. Enveis- og toveislister Innkapsling («boxing») (Big Java 6.8.5)

OBJEKTER SOM EN PROGRAMMERINGS-TEKNIKK

Kapittel 9: Sortering og søking Kort versjon

Å lese tall fra en fil, klassen Scanner

HØGSKOLEN I SØR-TRØNDELAG

Fra Python til Java, del 2

Klasser skal lages slik at de i minst mulig grad er avhengig av at klienten gjør bestemte ting STOL ALDRI PÅ KLIENTEN!

Argumenter fra kommandolinjen

programeksempel Et større En større problemstilling Plan for forelesingen Problemstillingen (en tekstfil) inneholdt ordet "TGA"

UNIVERSITETET I OSLO

Forkurs INF1010. Dag 2. Andreas Færøvig Olsen Gard Inge Rosvold Institutt for Informatikk, 14.

Klassen javax.swing.joptionpane

2 Om statiske variable/konstanter og statiske metoder.

EKSAMEN 6108/6108N PROGRAMMERING I JAVA Alt trykt og skriftlig materiale.

Kapittel 5. Din første klasse

Stein Gjessing, Institutt for informatikk, Universitetet i Oslo

Forelesning inf Java 1

Fra Python til Java. En introduksjon til programmeringsspråkenes verden. Dag Langmyhr

Innhold. Forord Det første programmet Variabler, tilordninger og uttrykk Innlesing og utskrift...49

Oversikt. INF1000 Uke 1 time 2. Repetisjon - Introduksjon. Repetisjon - Program

Hva er en metode. Hva skjer når vi kaller en metode

i=0 Repetisjon: arrayer Forelesning inf Java 4 Repetisjon: nesting av løkker Repetisjon: nesting av løkker 0*0 0*2 0*3 0*1 0*4

Forelesning inf Java 4

INF1010 våren 2019 Onsdag 30. januar. Mer om unntak i Java (med litt repetisjon av I/O først)

INF 1010, vår 2005 Løsningsforslag uke 11

HØGSKOLEN I SØR-TRØNDELAG

Stein Gjessing. Institutt for informatikk. Universitetet i Oslo. Institutt for informatikk

IN1010. Fra Python til Java. En introduksjon til programmeringsspråkenes verden Dag Langmyhr

UNIVERSITETET I OSLO Det matematisk-naturvitenskapelige fakultet

Gjennomgang av eksamen H99

INF1000 EKSTRATILBUD. Stoff fra uke 1-5 (6) 3. oktober 2012 Siri Moe Jensen

IN1010 V19, Obligatorisk oppgave 2

HØGSKOLEN I SØR-TRØNDELAG

Velkommen til. INF våren 2016

UNIVERSITETET I OSLO

Eksekveringsrekkefølgen (del 1) Oppgave 1. Eksekveringsrekkefølgen (del 2) Kommentar til oppgave 1. } // class Bolighus

EKSAMENSFORSIDE Skriftlig eksamen med tilsyn

Array&ArrayList Lagring Liste Klasseparametre Arrayliste Testing Lenkelister

INF1010 våren 2008 Uke 4, 22. januar Arv og subklasser

Del 3: Evaluere uttrykk

Eksamen. Objektorientert Programmering IGR 1372

AVDELING FOR INGENIØRUTDANNING EKSAMENSOPPGAVE

INF Uke 10. Ukesoppgaver oktober 2012

Introduksjon til objektorientert programmering

INF106 Objektorientert programmering

Mål med kurset. Java i INF Dagens tema. GUI med Swing. Dokumentasjon

Obligatorisk oppgave 4: Lege/Resept

LC191D/LO191D Videregående programmering mai 2010

INF1010, 22. mai Prøveeksamen (Eksamen 12. juni 2012) Stein Gjessing Inst. for Informatikk Universitetet i Oslo

HØGSKOLEN I SØR-TRØNDELAG

J2EE. CMP Entity Beans, Transaksjoner, JSP

IN1010 våren 2018 Tirsdag 15. mai. Repetisjon av subklasser og tråder. Stein Gjessing Institutt for informatikk Universitetet i Oslo

Innhold uke 4. INF 1000 høsten 2011 Uke 4: 13. september. Deklarasjon av peker og opprettelse av arrayobjektet. Representasjon av array i Java

INF1010 våren 2018 tirsdag 23. januar

Hva er verdien til variabelen j etter at følgende kode er utført? int i, j; i = 5; j = 10; while ( i < j ) { i = i + 2; j = j - 1; }

IN1010. Fra Python til Java. En introduksjon til programmeringsspråkenes verden Dag Langmyhr

Programmeringsspråket C

INF våren 2017

INF1010 våren 2017 Onsdag 25. januar. Litt om unntak i Java

INF1010, 21. februar Om å gå gjennom egne beholdere (iteratorer) Stein Gjessing Inst. for Informatikk Universitetet i Oslo

To måter å programmere på. Java 12. Programmering med objekter. Statisk programmering

Kapittel 9: Sortering og søking Kort versjon

INF1010 våren Arv og subklasser - del 2

EKSAMEN I FAG TDT4100 Objekt-orientert programmering. Fredag 3. juni 2005 KL

UNIVERSITETET I OSLO

INF1000 : Forelesning 4

Kapittel 1. Datamaskiner og programmeringsspråk. 1.1 Programmering

Eksamen Objektorientert Programmering 2012

. Ved sensur vl1 ahe bokstaverte deloppgaver (a, b, c,...) telle like mye.

IN 211 Programmeringsspråk. Java. på 20 enkle ark. spesielt for de som kan. Simula. (og gjerne litt C) Ark 1 av 20

Kapittel 15: Grafiske brukergrensesnitt: Enkel GUI. Del I

HØGSKOLEN I SØR-TRØNDELAG

Leksjon 7. Filer og unntak

Objektorientert Programmering Ekstraordinær eksamen 2014

Sortering med Comparable og Comparator

i=0 i=1 Repetisjon: nesting av løkker INF1000 : Forelesning 4 Repetisjon: nesting av løkker Repetisjon: nesting av løkker j=0 j=1 j=2 j=3 j=4

Læreboken på 45 minutter

Transkript:

Avdeling for informatikk og e-læring, Høgskolen i Sør-Trøndelag 2. Java-interface og RMI. Else Lervik 17.09.2008 Lærestoffet er utviklet for faget LO346D Java EE og distribuerte systemer 2. Java-interface og RMI. Resymé: Dette er en stor og arbeidskrevende leksjon. Hovedtemaet er RMI som er Java sin teknologi for å lage distribuerte objekter. RMI er også temaet for neste leksjon. Men først må vi bruke litt tid på emnet Java-interface som er mer eller mindre kjent fra tidligere. I programmering av distribuerte systemer er det å kunne lage egne interface helt nødvendig. Fra og med kapittel er vi ved leksjonens hovedtema. Vi skal lage klient- og tjenerprogram der objekter kommuniserer med hverandre. Dette er helt sentralt for forståelsen av resten av kurset. Innhold 2. JAVA-INTERFACE OG RMI....1 MERK 1 JAVA-INTERFACE...2 Oppgave med løsning bakerst i leksjonen...5 REMOTE METHOD INVOCATION (RMI)...5 Oppgave med løsning bakerst i leksjonen...10 Å LAGE FJERNOBJEKTER...10 Oppgave med løsning bakerst i leksjonen...11 OVERFØRING AV ARGUMENTER OG RETURVERDIER I RMI...11 LØSNING TIL SMÅOPPGAVENE FORAN...12 Aktuelle referanser til læreboka: Lervik & Havdal: Programmering i Java, 1. utgave: kap. 12.10 og 19.2-19.3 Lervik & Havdal: Programmering i Java, 2./3. utgave: kap. 10.10 og 19.2-19.3 NB! Uansett hvilken utgave du har av boka, du skal bruke eksemplene som hører til 3.utgave, se http://www2.tisip.no/boker/java/ Merk Leksjonen inneholder noen mindre oppgaver der du inviteres til å gjøre små utvidelser og forandringer i de eksemplene som gjennomgås. Løsninger til oppgavene finnes bakerst i leksjonen. De skal derfor ikke leveres inn, men er kun tatt med for å hjelpe deg til å jobbe mer aktivt med stoffet. Det er viktig at du gjør disse oppgavene, spesielt de som har med RMI å gjøre.

2. Java-interface og RMI. side 2 av 14 Java-interface Et interface er en samling metodehoder. En klasse kan implementere interfacet. Dersom noen av metodene ikke implementeres, blir klassen abstrakt. Du er vant med å bruke interface i forbindelse med GUI-programmering: class Knappelytter implements ActionListener {.. public void actionperformed(actionevent hendelse) { guibeholderen.setbackground(color.red); Klassen Knappelytter implementerer den ene metoden som er deklarert i interfacet ActionListener. Dette interfacet ser slik ut: interface ActionListener extends EventListener { void actionperformed(actionevent e) Vi ser at ActionListener er subinterface (jmf. subklasse) til EventListener som er et interface uten metoder. Interfacet fungerer kun som et merke. Alle lytterinterfacene er subinterface til EventListener. Vi vil etter hvert se flere eksempler på at interface kun fungerer som merkelapper. Du er kanskje også vant med å implementere interfacet Comparable slik at objekter kan sorteres med en ferdig sorteringsmetode 1 fra Java-API-et. Interfacet Comparable ser slik ut: interface Comparable { int compareto(object o); Klassen som objektene tilhører må dermed se slik ut: class MinKlasse implements Comparable { public int compareto(object o) {.. I disse eksemplene brukes interface til å standardisere metodehoder slik at klassene passer inn i det rammeverket som Java-API-et utgjør. Hovedhensikten med interface er imidlertid å la det beskrive grensesnittet (!) til et objekt. Klassen vil da beskrive implementasjonen. Dette er god objektorientert tankegang, vi lar omverdenen forholde seg til et veldefinert grensesnitt, mens implementasjonen er til intern bruk for en mindre krets av programmerere. Du har kanskje ikke brukt interface på denne måten hittil, men ved utvikling av større programsystemer er dette måten vi arbeider på. Vi lager moduler (klasser) med veldefinerte grensesnitt, en og samme modul kan ha flere grensesnitt til ulik bruk. Eksempelvis vil en databasemodul ha ett grensesnitt mot sluttbrukerne og et annet mot driftspersonale. 1 For bruk av ferdige sorteringsmetoder, se eventuelt "Programmering i Java" 1.utgave kap. 10.7 eller 2./3.utgave kap. 9.6.

2. Java-interface og RMI. side 3 av 14 Hovedtemaet i dette kurset er distribuerte systemer bygd på en teknologi der objekter kommuniserer med hverandre over nettet. I klientprogrammet har vi et klientobjekt som sender meldinger til et tjenerobjekt som hører hjemme i tjenerprogrammet. Klientprogrammet forholder seg kun til tjenerobjektet sitt grensesnitt, som er beskrevet i et Java-interface. Det er derfor viktig å kunne lage interface til en klasse. Eksempel: class Konto { private long kontonr; private String navn; private double saldo; public Konto(long startkontonr, String startnavn, double startsaldo) { kontonr = startkontonr; navn = startnavn; saldo = startsaldo; public long finnkontonr() { return kontonr; public String finnnavn() { return navn; public double finnsaldo() { return saldo; public void gjørtransaksjon(double beløp) { saldo += beløp; Her er et enkelt testprogram: class KontoTest { public static void main(string[] args) { Konto oleskonto = new KontoImpl(123456676756L, "Ole Olsen", 2300.50); System.out.println("Før endring av dataene: \nkontonr: " + oleskonto.finnkontonr() + ", navn: " + oleskonto.finnnavn() + ", saldo: " + oleskonto.finnsaldo()); oleskonto.gjørtransaksjon(1000); // nå er saldo lik 3300,50 oleskonto.gjørtransaksjon(-500); // nå er saldo lik 2800,50 System.out.println("Etter endring av dataene: \nkontonr: " + oleskonto.finnkontonr() + ", navn: " + oleskonto.finnnavn() + ", saldo: " + oleskonto.finnsaldo());

2. Java-interface og RMI. side 4 av 14 /* Kjøring av programmet: Før endring av dataene: Kontonr: 123456676756, navn: Ole Olsen, saldo: 2300.5 Etter endring av dataene: Kontonr: 123456676756, navn: Ole Olsen, saldo: 2800.5 */ La oss lage et Java-interface til klassen: interface Konto { long finnkontonr(); String finnnavn(); double finnsaldo(); void gjørtransaksjon(double beløp); Interfacet får nå det navnet som vi tidligere har brukt om klassen (vi kunne også brukt et annet navn). Legg merke til at vi har utelatt modifikatoren public på metodehodene. Den er underforstått, og det er derfor anbefalt å ikke ha den med. 2 Konstruktører er aldri med i interface. Klassen som implementerer interfacet får navnet KontoImpl: class KontoImpl implements Konto { private long kontonr; private String navn; private double saldo; public KontoImpl(long startkontonr, String startnavn, double startsaldo) { kontonr = startkontonr; navn = startnavn; saldo = startsaldo; public long finnkontonr() { return kontonr; public String finnnavn() { return navn; public double finnsaldo() { return saldo; public void gjørtransaksjon(double beløp) { saldo += beløp; 2 Dette er en anbefaling i Java Language Specification (http://java.sun.com/docs/books/jls/html/). Personlig syns jeg ikke dette er noen god idé. Du vil lære (og erfare) at metodehodene i klassen som implementerer interfacet må være lik med metodehodene i interfacet. Og i klassen må du ta med public foran, for gjør du ikke det betyr det pakketilgang og det er noe annet. Til tross for denne innvendingen bør en selvfølgelig prøve å følge anbefalingene fra Sun, det sikrer størst mulig gjenkjennelse fra Java-program til Java-program. Det ville bli en uholdbar situasjon om alle skulle følge sin egen måte å gjøre ting på...

2. Java-interface og RMI. side 5 av 14 Merk at her må vi ha med public i metodehodene. (Det var underforstått i interfacet.) Ellers bør en bruke kopier/lim inn i editoren for å sikre eksakt de samme metodehodene som spesifisert i interfacet. Testprogrammet kan være akkurat det samme, med unntak av at vi må bruke KontoImpl i stedet for Konto etter new: Konto oleskonto = new KontoImpl(123456676756L, "Ole Olsen", 2300.50); Referansen på venstre side kan være klassenavnet, eller det kan være navnet på et interface som klassen implementerer. Klassen kan inneholde flere metoder (til intern bruk) enn det interfacet sier. Ved å bruke interface-navnet på venstre side forhindrer vi at disse blir brukt (dersom det skulle være ønskelig). Vi ser at testprogrammet foran ikke er uavhengig av klassen. Vi må bruke klassenavnet etter new for å få laget objekter. Det er mulig å gjøre klienten helt uavhengig av klassen ved å bruke en såkalt objektfabrikk. Dette er en klasse som har som sin eneste oppgave å lage objekter av en annen klasse, i dette tilfelle av klassen KontoImpl. Her er klassen: class Kontofabrikk { public static Konto lagkonto(long startkontonr, String startnavn, double startsaldo) { return new KontoImpl(startKontonr, startnavn, startsaldo); I testprogrammet kan objektet nå opprettes på følgende måte: Konto oleskonto = Kontofabrikk.lagKonto(123456676756L, "Ole Olsen", 2300.50); Denne teknikken brukes mye i Java-API-et, for eksempel metoder for å lage bestemte formater: NumberFormat prosentformat = NumberFormat.getPercentInstance(); NumberFormat pengeformat = NumberFormat.getCurrencyInstance(); Oppgave 1 Oppgave med løsning bakerst i leksjonen Ta utgangspunkt i eksemplet foran uten fabrikkmetode. a) Utvid klassen med en tostring()-metode. b) Lag en metode som finner ut om to kontoer har samme navn. Bruk equalsignorecase() til å sammenligne navnene. Denne metoden skal ikke være synlig for klienter som forholder seg til interfacet. c) Endre testprogrammet slik at både a) og b) blir prøvd ut. Remote Method Invocation (RMI) Figur 1 forsøker å vise hva vi nå skal gjøre. De tre store blå boksene viser programmer som kjører. Programmet KontoKlient kjører på en egen datamaskin. Det samme gjør rmiregistry og KontoTjener. rmiregistry kan nås utenfra på port 1099.

2. Java-interface og RMI. side 6 av 14 KontoTjener er et program som oppretter et konto-objekt og registrerer dette i en navnetjeneste som kalles rmi-registeret. Der får objektet navnet "OleSinKonto". Dermed kan objektet aksesseres utenfra, for eksempel fra programmet KontoKlient. Dette programmet henvender seg til port 1099 på den andre maskinen og ber om en referanse til objektet med navn "OleSinKonto". Deretter kan klienten sende meldinger til objektet. Du skal nå kjøre dette programmet. Kompiler filene i katalogen KontoRMI i eksempelsamlingen til denne leksjonen. >javac *.java port 1099 OleSinKonto Naming.lookup() Internett Internett Naming.rebind() rmiregistry oleskonto 12345667756 Ole Olsen 2000 oleskonto KontoKlient KontoTjener Figur 1: Tjenerprogrammet registrerer et objekt i rmi-registeret. Et klientprogram henvender seg dit for å få tilgang til objektet. rmiregistry er et program som følger med J2SDK. Åpne et konsollvindu og sørg for at du står i katalogen KontoRMI. Start programmet i et eget vindu ved å skrive (MS-DOS): >start rmiregistry 3 Nå starter programmet på port 1099. (Det er også mulig å knytte programmet til en annen port.) Et eget konsollvindu skal åpne seg. Ingen utskrifter kommer der. Men la vinduet være åpent. Deretter starter du programmet KontoTjener: >start java KontoTjener 3 Windows-brukere: Dersom du får feilmeldingen "'rmiregistry' is not recognized as an internal or external command, operable program or batch file." e.l. betyr det at du ikke har katalogen til programmet rmiregistry.exe i miljøvariabelen path. Sannsynligvis får du heller ikke til å kompilere (javac) fra kommandolinjen. (Å kjøre programmer med java går imidlertid vanligvis bra, på grunn av at dette programmet ligger flere steder.) rmiregistry.exe og javac.exe ligger i katalogen bin rett under den katalogen der du har installert J2SDK. Se eventuelt vedlegg A1 i "Programmering i Java".

2. Java-interface og RMI. side 7 av 14 Enda et vindu skal åpne seg. Der skal følgende utskrift komme: RMI-objekt er registrert Start klientprogrammet: >start java KontoKlient Legg inn et par beløp og følg med på ustkriftene i de to siste vinduene. Start klientprogrammet enda en gang. Nå skal du ha i alt fire programmer som kjører. Legg inn beløp hos begge klientene. Observer at endringer som den ene gjør blir registrert hos den andre. Hvordan ser kildekoden ut? Vi kan jo begynne med tjenerprogrammet: import java.rmi.naming; class KontoTjener { public static void main(string[] args) throws Exception { Konto oleskonto = new KontoImpl(123456676756L, "Ole Olsen", 2000); String objektnavn = "OleSinKonto"; Naming.rebind(objektnavn, oleskonto); System.out.println("RMI-objekt er registrert"); javax.swing.joptionpane.showmessagedialog(null, "Trykk OK for å stoppe tjeneren."); Naming.unbind(objektnavn); System.exit(0); KontoImpl er subklasse til en klasse som heter UnicastRemoteObject. Det medfører at et objekt av denne klassen starter i en egen tråd som venter på at klienter skal ta kontakt. Og klientprogrammet ser slik ut: import static javax.swing.joptionpane.*; 4 class KontoKlient { public static void main(string[] args) throws Exception { // Dersom rmi-registeret og tjenerprogrammet kjører på en annen maskin, // må IP-adressen eller // maskinnavnet settes inn i stedet for localhost på neste linje. Konto oleskonto = (Konto) Naming.lookup("rmi://localhost/OleSinKonto"); System.out.println("Kontonr: " + oleskonto.finnkontonr() + ", navn: " + oleskonto.finnnavn() + ", saldo: " + oleskonto.finnsaldo()); String beløplest = showinputdialog("oppgi beløp, avslutt med blank"); 4 Vi bruker klassen JOptionPane til enkel kommunikasjon med brukeren. Dersom du har 1.utgave av "Programmering i Java", er du kanskje vant til å bruke den hjemmelagede klassen JavabokGUI. På inngangssiden til leksjonen finner du et notat som beskriver JOptionPane.

2. Java-interface og RMI. side 8 av 14 while (!beløplest.equals("")) { double beløp = Double.parseDouble(beløpLest); //ingen feilkontroll oleskonto.gjørtransaksjon(beløp); System.out.println("Kontonr: " + oleskonto.finnkontonr() + ", navn: " + oleskonto.finnnavn() + ", saldo: " + oleskonto.finnsaldo()); beløplest = showinputdialog("oppgi beløp, avslutt med blank"); Dette så jo greit ut. Hva med resten? Ja, vi har redigert bittelitegrann på interfacet Konto: interface Konto extends Remote { long finnkontonr() throws RemoteException; String finnnavn() throws RemoteException; double finnsaldo() throws RemoteException; void gjørtransaksjon(double beløp) throws RemoteException; Et interface som beskriver et objekt som skal nås over nettet, må være subinterface til interfacet java.rmi.remote. Og når man kommuniserer over nettet kan det alltid oppstå feil, derfor må hver eneste metode kunne kaste unntaksobjekt av typen java.rmi.remoteexception. Vi har også gjort litt med implementasjonen: import java.rmi.server.*; class KontoImpl extends UnicastRemoteObject implements Konto { private long kontonr; private String navn; private double saldo; public KontoImpl(long startkontonr, String startnavn, double startsaldo) throws RemoteException { kontonr = startkontonr; navn = startnavn; saldo = startsaldo; public synchronized long finnkontonr() throws RemoteException { return kontonr; public synchronized String finnnavn() throws RemoteException { return navn; public synchronized double finnsaldo() throws RemoteException { return saldo; public synchronized void gjørtransaksjon(double beløp) throws RemoteException { saldo = saldo + beløp; System.out.println("Transaksjon: " + beløp);

2. Java-interface og RMI. side 9 av 14 Klassen må være subklasse til java.rmi.server.unicastremoteobject. Metodene må selvfølgelig kunne kaste unntaksobjekt. Og de må synkroniseres. Grunnen til det siste er at flere klienter kan jobbe samtidig med det samme objektet. Men likevel det var ikke så ille, var det vel? Det gjelder å overføre den objektorienterte tankegangen du er vant med, til systemer der objektene kjører på forskjellige maskiner. Ethvert objekt som skal aksesseres over nettet må ha et tilhørende stubbobjekt (stedfortrederobjekt, proxyobjekt). Dette objektet lages automatisk. 5 Metodehodene i denne klassen er de samme som i originalklassen, men innholdet er forskjellig. Metodene sørger for å sende data (argumentene) over til tjenermaskinen. Klienten sender altså en melding til stubbobjektet, som videresender denne til tjeneren. Der er det et tilsvarende program som tar i mot dataene og sørger for at riktig metode blir kalt på det egentlige objektet. Eventuelle returverdier og unntaksobjekt sendes tilbake motsatt vei. Se figuren nedenfor. stubb-objekt (stedfortrederobjekt) mottaker-objekt oleskonto gjørtransaksjon(500) KontoKlient gjørtransaksjon(500) Internett Internett KontoTjener gjørtransaksjon(500) 12345667756 Ole Olsen 2000 oleskonto Figur 2: Klientprogrammet kommuniserer med tjenerobjektet via stedfortrederobjekt. 5 Objektet lages automatisk fra og med versjon J2SE5. I tidligere Java-versjoner måtte vi bruke programmet rmic for å få laget filer med stubbklasser. Dersom du har 1. eller 2.utgave av læreboka, kan du dermed se bort fra det som står om rmic.

2. Java-interface og RMI. side 10 av 14 Oppgave 2 Oppgave med løsning bakerst i leksjonen Utvid Konto og KontoImpl med en tostring()-metode. Du vil finne at du må kalle metoden noe annet, hvorfor? Bruk den nye metoden i klientprogrammet i stedet for utskrift via finnmetoder. Å lage fjernobjekter Et fjernobjekt er et objekt som tilhører en subklasse til UnicastRemoteObject. Et fjernobjekt har fått navnet sitt fordi det er mulig å fjernjobbe med det. Objektet kjører i et tjenerprogram på en maskin som er kjent for de klientene som skal jobbe med objektet. Ofte lages fjernobjektene av tjenerprogrammet. I eksemplet foran laget tjenerprogrammet ett objekt: Konto oleskonto = new KontoImpl(123456676756L, "Ole Olsen", 2000); Men det kan jo hende at klienten vil lage objekter. Et typisk klientprogram i en bank vil være slik at kunden ber om å få opprettet en konto. Klientprogrammet kan ikke utføre setningen foran. Ja, det kan hende det kan, dersom det har tilgang til klassen KontoImpl, men det vil bli helt feil, fordi objektet da vil bo i klientens Java-tolker. I stedet må klienten gi beskjed til tjenerprogrammet om at det blir opprettet et fjernobjekt der. Nå nærmer vi oss fabrikkmetodene nevnt foran. Der hadde vi en klassemetode som utførte setningen over, men det hjelper oss ikke her. Dersom vi bruker en klassemetode, vil vi også nå oppnå at objektet havner i klientens Java-tolker. I stedet må vi lage en objektfabrikk på tjenersiden. Vi lar denne fabrikken være et fjernobjekt og registrerer den i navnetjenesten. Interface og implementasjon: interface Kontofabrikk extends Remote { Konto lagkonto(long startkontonr, String startnavn, double startsaldo) throws RemoteException; import java.rmi.server.*; class KontofabrikkImpl extends UnicastRemoteObject implements Kontofabrikk { public KontofabrikkImpl() throws RemoteException { public Konto lagkonto(long startkontonr, String startnavn, double startsaldo) throws RemoteException { System.out.println("Oppretter ny konto for " + startnavn); return new KontoImpl(startKontonr, startnavn, startsaldo); Merk at vi alltid må lage konstruktør i en klasse av denne typen, også dersom konstruktøren har tom parameterliste og tom kropp. Grunnen til dette er at den må kunne kaste unntaksobjekt.

2. Java-interface og RMI. side 11 av 14 Tjenerprogrammet ser nå slik ut: class KontoTjener { public static void main(string[] args) throws Exception { Kontofabrikk fabrikk = new KontofabrikkImpl(); Naming.rebind("Kontofabrikken", fabrikk); System.out.println("Fabrikkobjekt er registrert i rmi-registeret"); javax.swing.joptionpane.showmessagedialog(null, "Trykk OK for å stoppe tjeneren."); Naming.unbind(objektnavn); System.exit(0); I tillegg kunne vi selvfølgelig latt tjenerprogrammet opprette noen konto-objekter og registrere dem i rmi-registeret. Vi har kun laget en meget enkel klient: class KontoKlient { public static void main(string[] args) throws Exception { Kontofabrikk fabrikk = (Kontofabrikk) Naming.lookup("rmi://localhost/Kontofabrikken"); Konto k1 = fabrikk.lagkonto(12345678l, "Pernille Hansen", 5000); Konto k2 = fabrikk.lagkonto(33345678l, "Åshild Jensen", 6000); k1.gjørtransaksjon(600); k2.gjørtransaksjon(1000); System.out.println("Kontonr: " + k1.finnkontonr() + ", navn: " + k1.finnnavn() + ", saldo: " + k1.finnsaldo()); System.out.println("Kontonr: " + k2.finnkontonr() + ", navn: " + k2.finnnavn() + ", saldo: " + k2.finnsaldo()); Oppgave 3 Oppgave med løsning bakerst i leksjonen Klientprogrammet foran lager to Konto-objekter. Slik systemet er nå, er det ikke mulig for andre klienter å nå disse objektene selv om de kjører på tjenersiden. Hvorfor er det slik? Hva kan vi gjøre for at andre klienter skal kunne nå objektene? Overføring av argumenter og returverdier i RMI Stedfortrederobjekter er viktige når vi jobber med fjernobjekter. Klienten som sender meldinger til objektet kan gjerne befinne seg på en maskin på den andre siden av jordkloden. Stedfortrederobjekter (stubb-objekter, proxyobjekter) gjør slik fjernjobbing mulig. Et stedfortrederobjekt har de samme metodene som det virkelige objektet, men innholdet i metodene er noe helt annet. Metodene i et stedfortrederobjekt sørger for å sende data over nettet slik at metoder på det virkelige objektet blir utført. Se figur 2.

2. Java-interface og RMI. side 12 av 14 I eksemplet foran (kapittel ) har vi ett fjernobjekt. Det kan aksesseres utenfra via rmiregisteret, og det har navnet "OleSinKonto". Metoden lookup() returnerer et stedfortrederobjekt som lar klienten jobbe med det nevnte objektet. Dersom vi lager en metode som tar dette objektet som argument, vil også et stedfortrederobjekt blir overført. Men hva med andre objekter i denne sammenhengen? I en av oppgavene foran laget du en metode som returnerte et strengobjekt. Klassen String er ikke subklasse til UnicastRemoteObject. Dermed får klienten ikke noe stedfortrederobjekt. I stedet får den et ordentlig objekt. Men det er overført over nettet. For at det skal være mulig må klassen være serialisert. Du er vant med å serialisere objekter som skal lagres på fil 6. Men alt som kan lagres på fil, kan også sendes over nettet. En titt i API-dokumentasjonen forteller at String implementerer java.io.serializable, ja, de aller fleste klassene i API-et implementerer dette interfacet. (java.io.serializable er et eksempel på et interface uten metoder, det fungerer kun som en merkelapp, jamfør avsnittet om interface, foran.) En klient som mottar et objekt ved serialisering, får en kopi av originalobjektet. Vi prøver å summere opp: Fjernobjektene er kjernen i det distribuerte systemet vårt. Disse objektene må eksistere der tjenerprogrammet kjører. Klienter får tilgang til disse via stedfortrederobjekter. Det kan hende at metodene som jobber mot fjernobjektene har andre typer objekter som argumenter eller returverdier. Disse objektene må overføres ved serialisering, det vil si at klassene de tilhører må implementere java.io.serializable. Oppgave 1 Revidert interface: Løsning til småoppgavene foran interface Konto { long finnkontonr(); String finnnavn(); double finnsaldo(); void gjørtransaksjon(double beløp); String tostring(); Revidert implementasjon: class KontoImpl implements Konto { som før public String tostring() { return "Kontonr.: " + kontonr + ", navn: " + navn + ", saldo: " + saldo; public boolean sammenavn(kontoimpl konto2) { // ikke med i interfacet return navn.equalsignorecase(konto2.navn); 6 "Programmering i Java", 1.utgave kap. 11.10, 2. og 3.utgave, kap. 12.10.

2. Java-interface og RMI. side 13 av 14 Nytt testprogram: class KontoTest { public static void main(string[] args) { // sammenavn() kun kjent i klassen KontoImpl oleskonto1 = new KontoImpl(123456676756L, "Ole Olsen", 2300.50); KontoImpl oleskonto2 = new KontoImpl(123456676756L, "Ole Olsen", 2300.50); KontoImpl kariskonto1 = new KontoImpl(123456676756L, "Kari Olsen", 2300.50); if (oleskonto1.sammenavn(oleskonto2)) { System.out.println("Oles 1 og 2 har samme navn"); if (!oleskonto1.sammenavn(kariskonto1)) System.out.println("Ikke samme navn"); // prøver tostring() System.out.println(olesKonto1 + "\n" + oleskonto2 + "\n" + kariskonto1); /* Kjøring av programmet: Oles 1 og 2 har samme navn Ikke samme navn Kontonr.: 123456676756, navn: Ole Olsen, saldo: 2300.5 Kontonr.: 123456676756, navn: Ole Olsen, saldo: 2300.5 Kontonr.: 123456676756, navn: Kari Olsen, saldo: 2300.5 */ Det hadde ikke vært nødvendig å deklarere tostring() i interfacet, på grunn av at denne uansett er deklarert i Object. Polymorfien sørger for at riktig utgave blir brukt. Oppgave 2 Metoden tostring() er deklarert i klassen Object. Enhver deklarasjon av denne metoden i en annen klasse vil være en omdefinering av den utgaven som er arvet fra Object. Imidlertid er det ikke tillatt å utvide metodehodet med et sjekket unntak (dvs. unntak som tilhører en subklasse til Exception, men ikke RuntimeException.) Vi kaller derfor metoden for lagutskrift(). Interfacet ser nå slik ut: interface Konto extends Remote { long finnkontonr() throws RemoteException; String finnnavn() throws RemoteException; double finnsaldo() throws RemoteException; void gjørtransaksjon(double beløp) throws RemoteException; String lagutskrift() throws RemoteException; Implementasjonen av metoden: public synchronized String lagutskrift() throws RemoteException { return "Kontonr.: " + kontonr + ", navn: " + navn + ", saldo: " + saldo; Klientprogrammet:

2. Java-interface og RMI. side 14 av 14 import javax.swing.joptionpane; class KontoKlient { public static void main(string[] args) throws Exception { // Dersom rmi-registeret og tjenerprogrammet kjører på en annen maskin, // må IP-adressen eller maskinnavnet settes inn i stedet for // localhost på neste linje. Konto oleskonto = (Konto) Naming.lookup("rmi://localhost/OleSinKonto"); System.out.println(olesKonto.lagUtskrift()); String beløplest = JOptionPane.showInputDialog("Oppgi beløp, avslutt med blank"); while (!beløplest.equals("")) { double beløp = Double.parseDouble(beløpLest); //ingen feilkontroll oleskonto.gjørtransaksjon(beløp); System.out.println(olesKonto.lagUtskrift()); beløplest = JOptionPane.showInputDialog("Oppgi beløp, avslutt med blank"); System.exit(0); Oppgave 3 Det er kun den ene utgaven av klientprogrammet som har referanser til disse objektene. De er derfor ikke tilgjengelig fra andre programmer, selv om objektene eksisterer på tjenersiden. Problemet kan løses ved at metoden lagkonto() registrerer objektene i rmi-registeret. (Det er mulig å få ut alle registrerte navn ved å bruke metoden Naming.list()). En annen løsning er å samle objektene f.eks. i en tabell-liste på tjenersiden. Ved å gi en klient adgang til tabell-listen vil den få tak i objektene ved å løpe gjennom tabell-listen.