Refleksion i Java. 8. juli 2003



Relaterede dokumenter
Hvad er Objekter - Programmering

METODER ARV KLASSER. Grundlæggende programmering Lektion 5

Ugeseddel 4 1. marts - 8. marts

AAU, Programmering i Java Intern skriftlig prøve 18. maj 2007

Hvad er et distribueret objekt? Plan Objekter, objektreferencer, metoder, parameteroverførsel. Objekter: notation

class Time { int hours, min; } } Time t1; // Erklær variabel af type Time class Time1 { public static void main(string[] args) { Time t1; t1.

Test af It-komponent

Klasser og objekter. (Afsnit i manualen)

Eksempel: Skat i år 2000

Kursusgang 11. Oversigt: Sidste kursusgang Værktøjer til udvikling og implementering af HCI-design Oversigt over Java Swing

Abstrakte datatyper C#-version

Polymorfi. Arv (inheritance) Abstrakte klasser, substitutionsprincippet, overriding, statisk og dynamisk type. Coercion

University of Southern Denmark Syddansk Universitet. DM503 Forelæsning 11

Software Construction 1 semester (SWC) Spørgsmål 1

Jacob Nordfalk. Ingeniørhøjskolen i København. Nykøbing F itvisioncenter 24. februar 2004

Løsning af møntproblemet

Programmering 1999 KVL Side 5-4. Klassen Time: metoder. Metoder i objektet giver mulighed for at ændre tilstanden, eller kigge på tilstanden.

DM507 Algoritmer og datastrukturer

Tree klassen fra sidste forelæsning

Singleton pattern i Java

DANMARKS TEKNISKE UNIVERSITET

University of Southern Denmark Syddansk Universitet. DM502 Forelæsning 2

Software Construction 1. semester (SWC) januar 2014 Spørgsmål 1

Kursus i OOP og Java. Kursus i Objektorienteret programmering i Java

RMI introduktion. Denne artikel beskriver Java RMI (Remtote Method Invocation).

Forelæsning Uge 2 Torsdag

Grundlæggende Programmering ITU, Efterår Skriftlig eksamen i Grundlæggende Programmering

DM507 Algoritmer og datastrukturer

SWC Elementer i klassedefinition

Administration af subsites BRUGERVEJLEDNING FOR ADMINISTRATOREN

b) Udvid din implementation af forme til at understøtte.equals. To objekter af samme form er ens hvis de har samme værdier i felterne.

4 Basal Objekt-orienteret Programmering I.

Objektorienteret Programmering

Videregående programmering i Java

Civilingeniøreksamen januar Skriftelig prøve den 12. januar 2001 Kursusnummer 49104

Objektorienteret design med arv og polymorfi:

Videregående Programmering for Diplom-E Noter

DM507 Algoritmer og datastrukturer

Videregående Programmering Obligatorisk opgave - 3. semester, efterår 2004

Skriftlig eksamen i Datalogi

Threads i Java. Denne artikel giver en introduktion til threads i Java. Den beskriver hvad tråde er og forklarer hvordan de bruges i Java

Forelæsning Uge 2 Torsdag

DM507 Algoritmer og datastrukturer

Objektorienterede metoder

IDAP manual Emission

Forelæsning Uge 2 Torsdag

DM507 Algoritmer og datastrukturer

University of Southern Denmark Syddansk Universitet. DM502 Forelæsning 3

Objects First with Java A Practical Introduction Using BlueJ

DM507 Algoritmer og datastrukturer

Metaklasser i Smalltalk.

Kursusgang 12. Oversigt: Sidste kursusgang Layout-manager Event-håndtering. Design af brugerflader 12.1

DM502. Peter Schneider-Kamp

SWC eksamens-spørgsmål. Oversigt

DM507 Algoritmer og datastrukturer

dintprog Manual Revision: 1241 August 24, 2010 I Introduktion 3 1 Notation 3 II Begreber 4 2 Grundbegreber om programmering 4

3 Algebraisk Specifikation af Abstrakte Datatyper.

Datalogi OB, Efterår 2002 OH er, forelæsning 3/ forstå datastrukturer og algoritmer (teoretisk forståelse og intuition)

Forelæsning Uge 6 Mandag

Klasser og Objekter i Python. Uge 46 Learning Python: kap 15-16,

30 Objekt-orienteret Programmering i Andre Sprog.

Anvendelse af metoder - Programmering

import java.awt.event.*; import java.awt.*; Container <- Panel <- Applet Component <- Button <- Checkbox <- ScrollPane <- Label

class subklasse-navn extends superklasse-navn { } NorwaySpruce har superklassen Spruce, som igen har superklassen Tree.

Dvs. at give dit program muligheden for at loade og bruge plugins som andre har lavet.

CodeDOM - Dynamisk generering og kompilering af kode

Indledning. Hvorfor det forholder sig sådan har jeg en masse idéer om, men det bliver for meget at komme ind på her. God fornøjelse med læsningen.

DM507 Algoritmer og datastrukturer

Introduction til.net remoting i C#

Skriftlig eksamen i Datalogi

Lektion 6. Grundlæggende programmering i VR

Online billede filtrering

ITJAV2 Obligatorisk opgave Portering af KVM til RTKernel

Andreas Lauge V. Hansen klasse 3.3t Roskilde HTX

Rename og redefine. Abstrakte klasser. Dynamisk binding.

Tabelbegrebet. Klassediagrammer (III) Oversigt. Anvendelse af Tabeller. Tabeller og qualified associations

Afsnittet er temmelig teoretisk. Er du mere til det praktiske, går du blot til det næste afsnit.

Moduler i Standard ML

Forelæsning Uge 5 Mandag

Design by Contract Bertrand Meyer Design and Programming by Contract. Oversigt. Prædikater

Martin Geisler. Uge 49, 2001

Skriftlig eksamen i Datalogi

Table of Contents. Prøveværktøj

Forelæsning Uge 3 Mandag

Forelæsning Uge 12 Torsdag

Aftenskole i programmering sæson Core Data del 2. Sæson 2-13

Opret prøve og tilpas dit fronter-rum Spørgsmålstyper og justering Oversigt over spørgsmålstyper...20 Justering af spørgsmål og sider...

Forelæsning Uge 12 Mandag

Exceptions i Delphi. Try except

Introduktion til ActionScript

Udvidelse og specialisering. Klassehierarkier. Nedarvningsterminologi. Interfaces. Statiske og dynamiske typer. Polymorfi. Abstrakte klasser.

Singleton pattern i C#

Modern Concurrency Abstractions for C#

14.1 Internationale programmer

Datalogi OB, Efterår 2002 OH er, forelæsning 10/ Klasser og nedarvning

I denne artikel vil vi bruge en User klasse som vi så vil gruppere på forskellige måder.

Bemærk! Et PHP script har kun brug for at forbinde én gang til databaseserveren. Det kan så sagtens udføre flere kommandoer vha. denne forbindelse.

UML til kravspecificering

SmartFraming Et vindue til nationale sundhedssystemer. Version 3.0

Forelæsning Uge 3 Torsdag

Transkript:

Refleksion i Java Udarbejdet af: Jesper Tejlgaard Pedersen Anders Baumann Tine Thorn IT-højskolen i København 4-ugersprojekt F2001 Vejleder: Kasper Østerbye 8. juli 2003 1

Indhold 1 Forord 3 2 Indledning 4 2.1 Problemstilling........................... 4 3 Refleksionsbegreber 4 3.1 Refleksion.............................. 4 3.2 Reifikation.............................. 5 3.3 Introspektion............................ 5 3.4 Manipulation............................ 5 3.5 Strukturel refleksion......................... 5 3.6 Opførselsrefleksion......................... 5 3.7 Kompileringstids-, load-tids- og køretidsrefleksion........ 6 3.7.1 Kompileringstidsrefleksion................. 6 3.7.2 Load-tidsrefleksion..................... 6 3.7.3 Køretidsrefleksion..................... 6 3.8 Baseniveau og metaniveau..................... 6 3.8.1 Baseniveau......................... 6 3.8.2 Metaniveau......................... 7 3.8.3 Metaobjekter og metaklasser................ 7 3.8.4 Metaobjektprotokol (MOP)................ 8 3.9 Opsamling på refleksionsbegreber................. 8 4 Refleksion i Java 8 4.1 Strukturel refleksion......................... 9 4.2 Klasse-loader............................ 9 4.3 Dynamiske proxy klasser...................... 9 4.4 Opsamling på Java s refleksion................... 12 5 Objekt Browser 12 5.1 Formål................................ 12 5.2 Design................................ 13 5.3 Implementation........................... 13 5.4 Test af systemet........................... 15 5.5 Brugervejledning.......................... 16 5.6 Opsamling på det praktiske arbejde................. 16 2

6 Refleksion i MetaJava 16 6.1 Design af MetaJava......................... 16 6.1.1 Event-modellen....................... 17 6.1.2 Forbindelse af metaniveau og baseniveau......... 17 6.1.3 Metaniveauinterface og event-generering......... 19 6.2 Implementation........................... 21 6.2.1 Skyggeklasser....................... 22 6.3 Anvendelse............................. 23 6.4 Performance og pladsforbrug.................... 24 6.5 Opsamling på MetaJava s refleksion................ 25 7 Refleksion i OpenJava 25 7.1 Design af OpenJava s MOP..................... 25 7.1.1 Oversættelse og metaobjekter............... 26 7.1.2 API............................. 28 7.2 Anvendelse............................. 30 7.3 Opsamling på OpenJava s refleksion................ 32 8 Refleksion i Javassist 33 8.1 Design................................ 33 8.2 Implementation........................... 33 8.3 Anvendelse............................. 34 8.4 Opsamling på Javassist s refleksion................. 35 9 Diskussion 35 9.1 Analyse af skema.......................... 35 9.2 Sammenligning af de enkelte sprog................. 36 9.3 Analyse af performance....................... 37 10 Konklusion 38 A Appendix: Klassediagrammer 40 B Appendix: Kildekode 41 B.1 Pakken app.browser......................... 41 B.2 Pakken app.gui........................... 56 B.3 Pakken test.browser......................... 73 3

1 Forord Denne projektrapport er udarbejdet i maj 2001 under vejledning af Kasper Østerbye i forbindelse med et fire-ugersprojekt på IT-højskolen i København. Projektet omhandler refleksion i forbindelse med programmeringssprogene Java og OpenJava samt to prototype-implementationer af Java, nemlig MetaJava og Javassist. Endvidere omhandler projektet design og implementation af en objektbrowser, der kan anvendes i forbindelse med debugging. Kildekoden til objektbrowseren kan ses på webadressen: http://www.it-c.dk/ tejl/objektbrowser/src Klassefilerne til objektbrowseren (objectbrowser.jar) ligger på webadressen: http://www.it.c-dk/ tejl/objektbrowser 4

2 Indledning Refleksion er en mekanisme, der gør et program i stand til at se og ændre dels sin egen kode, dels programmeringssprogets syntaks og semantik. Igennem refleksion er et program dermed i stand til at tilpasse sig varierende omgivelser. Dette er en egenskab, som kan anvendes i flere forskellige sammenhænge. Blandt andet kan man benytte refleksion til at få information om objekter, dvs. hvilken klasse objektet er en instans af, hvilke metoder og felter objektet har, samt hvad værdien af disse er osv. Denne information kan eksempelvis benyttes af debuggere og GUIværktøjer. 2.1 Problemstilling Det overordnede formål med dette projekt er, selvstændigt at sætte os ind i emnet refleksion. Dette gøres dels ved et litteraturstudium, dels gennem design og implementering af en simpel objektbrowser. Grundet tidsrammen på fire uger, har vi valgt kun at undersøge refleksion i forbindelse med programmeringssproget Java og forskellige udvidelser af Java (MetaJava og Javassist) samt OpenJava. Eftersom vi ikke havde noget egentligt kendskab til refleksion inden vi påbegyndte dette projekt (projektet bygger ikke videre på et kursusforløb), har vi valgt at fokusere på de procesmæssige aspekter frem for de produktmæssige. Der er derfor lagt stor vægt på at opnå en forståelse af begrebet refleksion frem for den praktiske anvendelse af refleksion. Formålet med at designe og implementere en objektbrowser i Java var udelukkende at stifte bekendtskab med Java s refleksions-api. Det har ikke været et mål at implementere en fuldt ud anvendelig objektbrowser til brug i debuggingsammenhænge. 3 Refleksionsbegreber Formålet med dette afsnit er kort, at introducere de for vores projekt væsentligste begreber vedrørende refleksion. 3.1 Refleksion Refleksion er et systems evne til at ræssonere og handle på baggrund af sig selv, samt kunne tilpasse sig forskellige omstændigheder [1]. Programmet har altså derigennem en indbygget evne til at kunne kigge på, eller ændre på sin egen syntaks, 5

semantik eller implementation [2]. Man taler om forskellige former for refleksion, bla. introspektiv, manipulerende, kompileringstids-, load-tids- og køretidsreflekion. Disse begreber defineres i de nedenstående afsnit. 3.2 Reifikation Reifikation betyder at gøre noget tilgængeligt, som ikke normalt er tilgængeligt i programmet eller er skjult for programmøren [1]. Dvs. reifikation er en proces hvor en del af et program eller sprog, der før ikke var tilgængeligt, nu gøres tilgængeligt, ved at bruge en datastruktur der udtrykkes i sproget selv [2]. Herefter har programmet mulighed for at inspicere sig selv (metainformation er tilgængelig). 3.3 Introspektion Introspektion er en evne til at kunne undersøge egenskaber for datastrukturer i et program, f.eks. klasser [3, 4]. 3.4 Manipulation Manipulation 1 af et program betyder, at programmets opførsel eller struktur ændres. 3.5 Strukturel refleksion Strukturel refleksion er evnen til at se eller ændre de datastrukturer der benyttes af et program [4], dvs. evnen til at se eller ændre det abstrakte syntakstræ. Et eksempel på strukturel refleksion er ændring af definitionen af datastrukturer for klasser og metoder [4]. Man kan også sige, at strukturel refleksion er et sprogs evne til at tilbyde fuldstændig reifikation af det kørende program samt af dets abstrakte datatyper [2]. Strukturel refleksion kan både være introspektiv og manipulerende, og der kan optræde strukturel refleksion i alle faser af eksekveringen af et program. Javassist er et eksempel på et sprog der understøtter strukturel refleksion. 3.6 Opførselsrefleksion Opførselsrefleksion er evnen til at kunne se og/eller ændre et programs opførsel, f.eks. ved at ændre metoders opførsel [4]. Derved kan opførselsrefleksion både være introspektiv og manipulerende. Opførselsrefleksion tillader ikke, i modsætning til strukturel refleksion, ændringer af statiske datastrukturer [4]. 1 Dette er vores oversættelse af det engelske begreb intercession 6

MetaJava er et eksempel på et sprog der understøtter opførselsrefleksion. 3.7 Kompileringstids-, load-tids- og køretidsrefleksion Afhængigt af hvornår binding mellem basesystem og metasystem foregår, kan man tale om hhv. kompileringstids-, load-tids- og køretidsrefleksion. Der kan i alle disse tre reflektionsformer foretages både strukturel refleksion og opførselsrefleksion. 3.7.1 Kompileringstidsrefleksion Med kompileringtidsrefleksion er det muligt at udnytte refleksion i et program, når programmet kompileres. OpenJava er et eksempel på et sprog, der understøtter kompileringstidsrefleksion. 3.7.2 Load-tidsrefleksion Ved Load-tidsrefleksion bliver refleksion udført på en klasse, når den loades af klasse-loaderen. [4]. Det er altså i klasse-loaderen at refleksion foretages. Javassist er et eksempel på et sprog, der understøtter load-tidsrefleksion. 3.7.3 Køretidsrefleksion Med køretidsrefleksion er der mulighed for refleksion på køretidspunktet, dvs. mens programmet rent faktisk eksekveres. MetaJava er et eksempel på et sprog, der understøtter køretidssrefleksion. 3.8 Baseniveau og metaniveau I forbindelse med refleksion indføres der ofte et baseniveau og et metaniveau. Disse gør det muligt at adskille funktionel kode fra ikke-funktionel kode. Funktionel kode er den kode, der vedrører operationer, som foregår i basesystemet 2. Ikke-funktionel kode er placeret i metasystemet, og har til formål at kontrollere og holde øje med, hvordan den funktionelle kode bliver udført (jvf. figur 1) [5, 1]. 3.8.1 Baseniveau På baseniveauet findes det program, der skal undersøges eller ændres, hvilket vil sige applikationsprogrammet, hvorpå refleksion skal foretages [3]. 2 Dvs. i det egentlige applikationsprogram 7

Figur 1: Metasystem og basesystem. Figuren viser sammenhængen mellem metasystemet og basesystemet. Frit efter [6]. 3.8.2 Metaniveau På metaniveauet findes den del af koden, der tillader et program at reflektere over sig selv. Det er på metaniveauet, at introspektion såvel som manipulation af programmets strukturelle og opførelsesmæssige egenskaber foretages. For at kunne understøtte refleksive operationer, foretages der på metaniveauet en reificering af programmet på baseniveauet. 3.8.3 Metaobjekter og metaklasser Et metaobjekt er en instans af en metaklasse. Metaobjektet indeholder information om entiter på baseniveauet, og kan kontrollere udførslen af disse baseentiteter [1]. Et eksempel på en metaklasse er Java s klasse Class. 8

3.8.4 Metaobjektprotokol (MOP) En MOP er en protokol, der specificerer et interface til metaniveauet, dvs hvordan metaklasser implementeres, og hvordan metaobjekter tilknyttes baseniveauet (dvs. hvordan der oprettes metaobjekter) [3]. Der er både kompileringstids-, load-tids- og køretids-mop s. Afhængigt af i hvilken fase af programmets eksekvering man er, vil de refleksive beregninger ske på kompileringstidspunktet, load-tidspunktet eller køretidspunktet [3]. OpenJava er en kompileringstids-mop, mens Javassist er en load-tids-mop og MetaJava en køretids-mop. 3.9 Opsamling på refleksionsbegreber Eftersom der er en vis ortogonalitet mellem nogle af de gennemgåede refleksionsbegreber, har vi valgt at systematisere disse i et skema (jvf. nedenstående skema 1). Vi vil undervejs i rapporten placere hvert enkelt programmeringssprog i skemaet, og vil benytte skemaet som udgangspunkt for vores diskussion. Refleksion Kompileringstid Load-tid Køretid Introspektion Opførsel Strukturel Manipulation Opførsel Strukturel Tabel 1: Skema til systematisering af refleksionsbegreberne 4 Refleksion i Java Java er et programmeringssprog der understøtter introspektiv strukturel refleksion på køretid, hvor ved der er mulighed for at få information om et programs datastrukturer under eksekvering af programmet. Det er således muligt at undersøge et objekts tilstand, udføre et metodekald specificeret ved en streng, samt få information om hvilke felter og metoder objektets klasse har. Den strukturelle refleksion beskrives i afsnit 4.1. Udover den introspektive strukturelle reflektion gives der i Java 1.3 mulighed for en begrænset form for opførselsrefleksion. Dette omtales i afsnit 4.3. 9

4.1 Strukturel refleksion Den centrale klasse i Java s standard refleksions-api er Class [7]. En instans af denne klasse repræsenterer eller reflekterer en klasse på køretid. Ved at kalde dens metoder kan man få returneret instanser af klasserne: Constructor, Method, Field og Class. Man kan så bruge disse objekter til bl.a. at få information om klassens konstruktorer, metoder, felter og superklasser. Eksempelvis returnerer metoden getmethods() i Class et array af instanser af klassen Method. På hver af disse instanser kan man efterfølgende kalde diverse metoder, f.eks. getparametertypes(), der returnerer et array af Class, hvor hver instans repræsenterer typen af en inputparameter. Instanser af Class repræsenterer også interfaces, og man kan derfor også hente oplysninger om modifiers, metoder og konstanter til et interface. Med Java s refleksions-api er det også muligt at oprette instanser af en klasse, hvor navnet på klassen først er kendt på køretidspunktet. Dette gøres med den statiske metode Class.forname(String name), der returnerer et objekt af typen Class. Det er også muligt at se og ændre feltværdier samt kalde metoder, hvis navne først er kendt på køretidspunktet. Et eksempel på en faktisk anvendelse af denne funktionalitet kan ses i afsnit 5, hvor vi har implementeret en såkaldt objektbrowser, der gør det muligt for brugeren at se tilstanden af et givent objekt på et hvilket som helst tidspunkt under programafviklingen. Kald af en metode, hvor metodenavnet først er kendt på køretid er bl.a. anvendelig i en debugger, der tillader brugeren at vælge hvilke metoder han/hun vil kalde under selve programudførslen [8]. 4.2 Klasse-loader Java s virtuelle maskine benytter en klasse-loader til at loade.class-filer. Det er i Java muligt at lave sin egen klasse-loader, ved at definere en subklasse af den abstrakte klasse ClassLoader. I denne subklasse er det muligt at ændre et programs opførsel ved at modificere den loadede bytekode. Denne teknik benyttes af Javassist (se afsnit 8). 4.3 Dynamiske proxy klasser I Java 1.3 er det blevet muligt at bruge en tilnærmet form for opførselsrefleksion ved brug af dynamiske proxy klasser. Disse tillader, at et program på køretid implementerer nye interfaces ved at videresende alle metodekald til en instans af klassen InvocationHandler. Dynamiske proxy klasser kan således tilføje ekstra 10

funktionalitet til eksisterende klasser uden at ændre i disse, og de giver derfor programmører mulighed for at tilføje metode-generisk funktionalitet til deres system [9]. Et eksempel på en sådan funktionalitet er logning. I forbindelse med debugging ønsker man ofte at logge en klasses metodekald. Som et eksempel kigger vi på følgende program: interface X public void foo(); public void bar(); class XImpl implements X private int x = 0; private int y = 0; public void foox = y * 2; public void bary = x + 5; Normalt vile man implementere logning ved brug af designmønstret decorator, der gør brug af en såkaldt wrapper-klasse [10]: class XLogger implements X private X _x; public XLogger(X x) _x = x; public void foo()( System.out.println("foo called"); _x.foo(); ) public void bar() System.out.println("bar called"); _x.bar(); Som det ses tilføjer XLogger ekstra funtionalitet til XImpl uden at rette i selve klassen. Denne løsning er dog ikke optimal. For det første er det besværligt at skulle skrive hele klassen XImpl en gang til. For det andet er logning et generisk problem, men det er ovenstående løsning ikke. Hvis vi ønsker at logge en anden klasse Y 11

skal vi således skrive en ny wrapper-klasse for Y. Hvis log-funktionalitet skal være generisk og derfor uafhænging af interfacet X, så skal vi bruge en dynamisk proxy klasse. Vi kan få en instans af en dynamisk proxy klasse, der implementerer et givent antal interfaces ved at kalde den statiske metode: Proxy.newProxyInstance(ClassLoader classloadertouse, Class[] interfacestoimplement, InvocationHandler objtodelegateto) Metoden returnerer en dynamisk proxy klasse, der implementerer de interfaces, der er angivet i parameteren interfacestoimplement. Proxyklassen vil delegere alle metodekald i disse interfaces videre til den associerede InvocationHandlers eneste metode: public Object invoke(object proxy, Method meth, Object[]args) Det første argument i denne metode er den proxy-instans, som metoden blev kaldt på. Det andet argument repræsenterer den kaldte metode og det sidste de argumenter, som blev givet som parametre til metodekaldet. Det er i metoden invoke, at man specificerer man hvad der skal ske ved de enkelte metodekald. For at benytte en dynamiske proxy klasse skal vi have en instans af en klasse, der implementerer InvocationHandler-interfacet, og vores proxyklasse vil så sende metodekald videre til dette objekt. class Logger implements InvocationHandler private Object delegate; public Logger(Object o) delegate = o; public Object invoke(object proxy, Method meth, Object[] args) System.out.println(meth.getName()); // Skriv til log. try return meth.invoke(delegate, args); catch (InvocationTargetException e) throw e.gettargetexception(); public static void main(string[] args) 12

X logger = (X) Proxy.newProxyInstance( Thread.currentThread().getContextClassLoader(), new Class[] X.class, new Logger(new XImpl())); I vores hovedprogram opretter vi ved brug af den statiske metode Proxy.newProxyInstance en ny proxy, der implementerer de interfaces, der er angivet som anden parameter. I vores tilfælde drejer det sig om interfacet X. Alle kald af X s metoder vil blive delegeret videre til en instans af Logger, som er tredie parameter. For at invoke i Logger kan delegere det modtaget metodekald videre efter at der er blevet skrevet i loggen, er den nød til at eje en instans af en klasse, der implementere det interface, hvor metodekaldet stammer fra. Derfor tager Logger s konstruktør et objekt af typen Object som inputparameter. I vores tilfælde giver vi en instans af klassen XImpl. Som det ses bruger invoke denne instans som inputparameter i det refleksive metodekald: return meth.invoke(delegate, args); Herved vil metoden meth i objektet delegate blive kaldt med args som parametre. Vi har med ovenstående løsning implementeret en generisk logger, der kan bruges på en hvilken som helt klasse. Dynamiske proxy klasser har dog, ligesom resten af Java s refleksions-api, et vist performance overhead på køretidspunktet. 4.4 Opsamling på Java s refleksion Efter at have gennemgået Java, kan vi konkludere at dette programmeringssprog understøtter følgende refleksive egenskaber (jvf. skema 1 i afsnit 3.9): Java understøtter introspektiv opførselsrefleksion og strukturel refleksion på køretidspunktet, da det er muligt at undersøge metoder, instansvariable, hvilken klasse et objekt er en instans af osv. på køretidspunktet. Endvidere understøtter Java manipulerende opførselsrefleksion på køretidspunktet, idet det vha. dynamiske proxy-klasser er muligt at ændre metoder. 5 Objekt Browser 5.1 Formål For at få praktisk erfaring med Javas refleksions-api har vi valgt at implementere en objektbrowser. Ideen med objektbrowseren er at den skal fungere som et hjælpeprogram til brug ved debugging af andre programmer. Brugeren skal på et 13

hvilket som helst sted i sit program kunne kalde en statisk metode i browseren med et vilkårligt objekt som input, og objektbrowseren skal så vise objektets instanser og værdierne for disse. 5.2 Design På et overordnet plan består vores system af to dele: en præsentationsdel, der står for præsentation af uddata for brugeren og selve beregningsdelen, hvor vi benytter refleksion til at udtrække de relevante oplysninger om inddata. Denne separation af præsentation og model giver os både et pænt og overskueligt design og giver os samtidig en mulighed for at præsentere uddata på flere forskellige måder. Vi har således valgt at implementere en simpel tekstbaseret præsentation hvor uddata bliver skrevet til Shell en, samt en mere avanceret GUI-version, hvor brugeren har følgende muligheder: Et tryk på en reference vil vise tilstanden af objektet, som referencen refererer til. Et tryk på en klasse vil vise information om klassen: Pakkenavn. Statiske felter. Superklasse. Hvilke interfaces klassen implementerer. Metoder. Det er muligt at browse både frem og tilbage mellem uddata. Vores system har kun et vindue, og når brugeren trykker på et link, vil det nye indhold overskrive det aktuelle. Ved tryk på tilbage-knappen vil det forrige indhold blive vist. Alternativ kunne man havde valgt en løsning, hvor hvert klik fra brugeren åbnede et nyt vindue. Vi mener dog, at vores løsning er enklere og mere brugervenlig. Frem- og tilbageknapperne har vi valgt at placere i en toolbar øverst til venstre i browservinduet, ligesom de er i f.eks. Netscape. For at tydeliggøre for brugeren hvilke ord, det er muligt at trykke på, er alle links markeret med en lilla farve. 5.3 Implementation Vi vil i det følgende gennemgå de vigtigste klasser i vores system (se figur 14 og 15 i appendix A): 14

Figur 2: GUI-version af Objectbrowser Figur 3: Shell-version af Objectbrowser ObjectInfo Denne klasse indeholder information om et objekts tilstand. Dens konstruktor tager et objekt af typen Object som input, og udtrækker information om objektets felter og deres værdi ved brug af Java s indbyggede refleksions-api. Til opbevaring af objektets felter anvender ObjectInfo et array af instanser af klassen FieldInfo. 15

ClassInfo Denne klasse indeholder information om en klasse svarende til den information brugeren får præsenteret, når han/hun klikker på et klasse-link. Til opbevaring af information om metoder, kontruktorer og statiske felter bruger ClassInfo instanser af klasserne MethodInfo, ConstructorInfo og StaticFieldInfo. ShellBrowser Det er denne klasse, som brugeren skal benytte, hvis han/hun ønsker at få uddata præsenteret i Shell. Klassen benytter en instans af klassen ShellPrinter til at få data udskrevet på skærmen. GUIBrowser Svarer til ShellBrowser, men her bliver uddata vist i et Swing-vindue. BrowserWindow Denne klasse repræsenterer selve den grafiske udgave af browservinduet, som vi har implementeret i Swing. Vi har valgt at lade klassen være en singleton, da vi vil være sikre på, at det altid er den samme instans af klassen vi får fat på, når vi indsætter ny tekst i browservinduet. ObjectInfoCanvas Denne klasse er ansvarlig for at vise et objekts tilstand i en Swing frame. ClassInfoCanvas Denne klasse står for at vise information om en klasse i en Swing frame. For fuldstændig selv at kunne bestemme browservinduets udseende, har vi valgt at bruge specialiseringer af Swings indbyggede klasser. Vi har således benyttet klasser der nedarver fra JLabel, JButton, JPanel og JFrame. For at vores browser kan give et øjebliksbillede af et objekts tilstand, er vi nødt til at standse det kaldende program, indtil browservinduet bliver lukket. Hvis programmet i stedet fortsatte sin udførsel, ville informationen i browservinduet ikke mere være korrekt, da programmets videre forløb kunne ændre det aktuelle objekts tilstand. For at standse det kaldende program har vi derfor implementeret en klassen WaitMonitor, der har to metoder: rest() og goon(). BrowserWindow kalder rest() lige efter at det har åbnet browservinduet, hvilket medfører at det kaldende programs tråd venter i WaitMonitor indtil vinduet bliver lukket og goon() kaldes. 5.4 Test af systemet Vi har ved brug af JUnit skrevet unit test for alle klasser i pakken app.browser (jvf. figur 14 i appendix A). Dvs. de klasser i vores system, der har med refleksion at gøre. Vi har valgt at benyttet testmetoden fra Extreme Programming, hvor testen for hver klasse skrives før selve klassen. Vi testede vores programmer vha. en 16

dummy-klasse, og vi oplevede ingen særlige testproblemer, der knyttede sig til, at testene blev udført på et refleksivt program. 5.5 Brugervejledning For at benytte vores objektbrowser, skal filen objectbrowser.jar inkluderes i classpath, og brugeren kan så undersøge et givent objekt o ved enten at kalde ShellBrowser.browse(o) eller GUIBrowser.Browse(o), der ligger i pakken app.browser. 5.6 Opsamling på det praktiske arbejde Introspektiv strukturel refleksion er blevet afprøvet i praksis ved at bruge Java s reflektions-api til at designe og implementere en objektbrowser. Den implementerede objektbrowser giver mulighed for, at undersøge et objekts tilstand samt modtage information om objektets klasse, herunder klassens metoder, konstruktorer, statiske felter og superklasse. Derimod er det i vores objektbrowser ikke muligt at ændre strukturen af klassen, eftersom Java ikke understøtter denne form for manipulerende refleksion. 6 Refleksion i MetaJava MetaJava 3 er en prototype-implementation af Java, der bygger på en uvidelse af Java s virtuelle maskine (kaldet MetaJava Virtual Machine; MJVM). Denne udvidede JVM gør MetaJava i stand til at understøtte opførselsrefleksion på køretid. Intentionen med MetaJava var at lave en refleksiv arkitektur til Java, hvor både strukturel refleksion og opførselsrefleksion var understøttet 4 [1]. Dette var årsag til, at der blev introduceret et metasystem, således at det var muligt at adskille funktionel kode fra ikke-funktionel kode (jvf. afsnit 3.8). 6.1 Design af MetaJava Modellen for MetaJava bygger som før nævnt på et basesystem samt et metasystem. Derudover opererer modellen med begreberne event, metaniveauinterface 3 Senere blev MetaJava omdøbt til metaxa, men vi vil i dette projekt benytte det oprindelige navn MetaJava 4 I MetaJava benyttes begrebet strukturel refleksion som synonym for introspektiv strukturel refleksion, dvs. mulighed for at undersøge egenskaber for strukturen at et program, f.eks. struktur af klasser. 17

samt skyggeklasse. I det følgende defineres disse begreber og MetaJava s model beskrives. 6.1.1 Event-modellen Når man ønsker at understøtte refleksion, er det essentielt at kunne overføre kontrol fra basesystemet til metasystemet. Under eksekvering af applikationsprogrammet udføres der i basesystemet forskellige operationer så som metodekald, læsning/skrivning af instansvariable, dannelse af nye objekter, loading af klasser o.s.v. I den virtuelle maskine er der implementeret default-opførsler for hver af disse operationer. En overførsel af kontrol fra basesystem til metasystem betyder, at det er metasystemet, der afgør hvordan operationerne i basesystemet skal udføres (det kan både være en default-opførsel eller en af metasystemet bestemt opførsel). Til at overføre kontrol fra basesystem til metasystem benyttes der i MetaJava en event-model. Hvis et metaobjekt implementerer en given operation 5, og denne operation udføres i basesystemet, vil der opstå en event. Denne event bliver videredelegeret til metasystemet, hvor den vil blive evalueret og håndteret på passende vis (jvf. figur 4) [6, 11]. De forskellige events der eksisterer for de forskellige operationer kan ses i tabel 2. 6.1.2 Forbindelse af metaniveau og baseniveau Måden hvorpå baseniveau og metaniveau knyttes sammen, er ved at tilknytte et metaobjekt til en baseniveauentitet, dvs. en klasse, et objekt eller en reference i basesystemet. Når et metaobjekt er tilknyttet en baseniveauentitet betyder det, at det er dette metaobjekt, der modtager og håndtere alle de events, der vedrører den givne baseniveauentitet (jvf. figur 5) [6, 11, 1]. Metaobjekter kan have forskellige funktioner afhængigt af, hvad de skal bruges til, og hvilken baseniveauentitet de er tilknyttet. Er metaobjektet tilknyttet et baseobjekt, vil funktionen af metaobjektet som regel være at ændre semantikken for dette baseobjekt, og derved gøre objektet refleksivt [6, 11]. Eksempelvis ville metaobjektet kunne reificere alle de metodekald, der stammer fra det specifikke baseobjekt [1]. Ændringer i semantikken for et base-objekt kan opnås ved at modificere objektets klasse (se nedenstående beskrivelse af skyggeklasser). Er metaobjektet tilknyttet en reference, vil det kun være events, der genereres på baggrund af operationer, som bruger denne reference der videredelegeres [1]. Herefter kan metaobjektet på samme måde, som hvis det var tilknyttet et baseobjekt, reificere metodekald, der benytter den givne reference. 5 se afsnit om event-generering 18

Figur 4: Event-model. På baggrund af operationer (f.eks. metode-kald, tilgang til felter, klasse-loading osv.) i basesystemet skabes der events, hvis et metaobjekt implementerer en given operation. Event sendes afsted til metasystemet, hvor det er op til det tilknyttede metaobjekt at evaluere og håndtere denne event. Herved overføres der kontrol fra basesystemet til metasystemet. Endelig vil tilknytning af et metaobjekt til en klasse betyde, at metaobjektet modtager de events, der genereres for alle instanser af denne klasse [11, 1]. Dette betyder, at alle instanser af klassen gøres refleksive [5, 11]. Når et metaobjekt skal knyttes til en baseniveauentitet, kan det benytte en af følgende tre metoder [1]: void attachobject(object o) Knytter metaobjektet til et objekt. Object attachreference(object o) Knytter metaobjektet til en objektrefe- 19

Event enter-method(object, method, arguments) create-object(class) read-field(object, field) write-field(object, field, value) load-class(classname) aquire-objectlock(object) Beskrivelse Metoden method kaldes på objektet object med argumenterne arguments. Der laves en instans af klassen class. Objektet object s felt field læses. Værdien value skrives i objektet object s felt field. Klassen med navnet classname benyttes for første gang og skal derfor loades. Der optages lås på objektet object. Objektet object s lås frigives. release-objectlock(object) Tabel 2: Events. Tabellen viser hvilke events, der kan genereres på baggrund af operationer i basesystemet. rence. I MetaJava er det ikke muligt at ændre semantikken af en reference. Denne metode returnerer i stedet en ny reference, der besidder de refleksive egenskaber. void attachclass(class c) Knytter metaobjektet til en klasse. 6.1.3 Metaniveauinterface og event-generering MetaJava er som sagt en udvidelse af Java s virtuelle maskine, og denne udvidelse består af to dele. Den ene del er et såkaldt metaniveauinterface 6 mens den anden del er et event-genererende lag (jvf. figur 6). For at metaobjekter kan få mulighed for at foretage manipulationer af den virtuelle maskine, eller mulighed for introspektiv strukturel refleksion, har metaobjekterne adgang til en række metoder[1, 12]. Det er disse metoder, der har fået betegnelsen metanivauinterface. Alle metoder i metaniveauinterfacet er implementeret som native metoder, hvilket betyder, at metoderne er processorspecifikke. Oven på metaniveauinterfacet ligger det event-genererende lag (jvf. figur 6). Dette lag består af et sæt af metoder skrevet i Java, der arves af alle metaobjekter 6 Dette er en direkte oversættelse af begrebet meta-level interface (MLI). 20