Faculteit Toegepaste Wetenschappen Vakgroep Informatietechnologie Voorzitter: Prof. Dr. Ir. P. Lagasse Een OSGi-compatibele implementatie van een Java Resource Monitor voor TabletPC door Bruno Van Den Bossche Promotor: Prof. Dr. Ir. F. Gielen Scriptiebegeleider: N. Goeminne Scriptie ingediend tot het behalen van de academische graad van licenciaat in de informatica Academiejaar 2003–2004 Faculteit Toegepaste Wetenschappen Vakgroep Informatietechnologie Voorzitter: Prof. Dr. Ir. P. Lagasse Een OSGi-compatibele implementatie van een Java Resource Monitor voor TabletPC door Bruno Van Den Bossche Promotor: Prof. Dr. Ir. F. Gielen Scriptiebegeleider: N. Goeminne Scriptie ingediend tot het behalen van de academische graad van licenciaat in de informatica Academiejaar 2003–2004 Een OSGi-compatibele implementatie van een Java Resource Monitor voor TabletPC door Bruno Van Den Bossche Academiejaar 2003–2004 Promotor: Prof. Dr. Ir. F. Gielen Scriptiebegeleider: N. Goeminne Faculteit Toegepaste Wetenschappen Universiteit Gent Vakgroep Informatietechnologie Voorzitter: Prof. Dr. Ir. P. Lagasse Samenvatting Adaptieve software kan zich aanpassen aan de omgeving waarbinnen deze gebruikt wordt. Het bekomen van informatie over de omgeving impliceert het gebruik van heel specifieke programmabibliotheken en functies. Het gevolg hiervan is dat de adaptieve software erg platformspecifiek dreigt te worden. De adaptiviteit van de software wordt hierdoor beperkt. De oplossing die in deze thesis uitgewerkt wordt, is in een platformonafhankelijke bibliotheek te voorzien die alle noodzakelijke gegevens over het gebruikte platform op een eenduidige manier beschikbaar maakt. Deze bibliotheek is in Java geschreven, mits het gebruik van de Java Native Interface, en wordt verpakt in een OSGi-bundel. OSGi is een framework-specificatie die toelaat om op een heel eenvoudige manier software te installeren, updaten en te verwijderen. Wat het dus uitermate geschikt maakt om als basis voor adaptieve software te gebruiken. Voor deze thesis werd een TabletPC met het besturingsysteem WindowsXP Tablet Edition als referentieplatform gebruikt. Trefwoorden OSGi, Java, J2ME, JNI, JMF, resource monitoring, adaptieve software. Toelating tot bruikleen “De auteur geeft de toelating deze scriptie voor consultatie beschikbaar te stellen en delen van de scriptie te kopiëren voor persoonlijk gebruik. Elk ander gebruik valt onder de beperkingen van het auteursrecht, in het bijzonder met betrekking tot de verplichting de bron uitdrukkelijk te vermelden bij het aanhalen van resultaten uit deze scriptie.” Bruno Van Den Bossche, mei 2004 Dankwoord Een scriptie schrijven is iets dat tijdens je hele studentenloopbaan steeds ver weg lijkt, maar plots zit je in het laatste jaar en sta je er voor. Het is een hele opgave, één waar je vol enthousiasme aan werkt, maar ook af en toe vervloekt. Gelukkig sta je nooit alleen en daarom zou ik graag van deze gelegenheid gebruik maken om een aantal mensen te bedanken. Zo is er professor Frank Gielen, die als promotor dit alles heeft mogelijk gemaakt. Nico Goeminne als onze dagelijkse begeleider, die niet alleen zijn persoonlijke TabletPC beschikbaar stelde, maar ons ook met raad en daad bijstond tijdens het hele proces en dit met een ontembaar enthousiasme. Ik heb het hier over ’ons’ want een belangrijk deel van deze scriptie is in samenwerking met Koen Van Boxstael verwezenlijkt. Deze samenwerking heeft zeker bijgedragen tot de kwaliteit van het werk dat nu voor u ligt, en met twee weet je echt altijd meer dan alleen. Vandaar ook een welgemeende ’Bedankt Koen’. Verder wil ik ook nog mijn vriendin Cindy en vrienden Christophe, Inge, Bart, Raf en Joachim vermelden omdat ze me mijn verhaal lieten doen en voor de nodige ontspanning zorgden tijdens dit toch wel drukke thesisjaar. Tot slot wil ik ook mijn ouders bedanken om er altijd voor me te zijn. Bruno Van Den Bossche, mei 2004 INHOUDSOPGAVE i Inhoudsopgave 1 Een nieuwe softwarewereld 1.1 Vereisten . . . . . . . . . . . . . . . . 1.1.1 Software . . . . . . . . . . . . . 1.1.2 Platform . . . . . . . . . . . . . 1.2 Voordelen . . . . . . . . . . . . . . . . 1.2.1 Ontwikkeling en onderhoud . . 1.2.2 Gebruik . . . . . . . . . . . . . 1.2.3 Economisch . . . . . . . . . . . 1.3 Problemen en oplossingen . . . . . . . 1.3.1 Software is platformafhankelijk 1.3.2 Installatie en deı̈nstallatie . . . 1.4 Doelstelling . . . . . . . . . . . . . . . 1.5 Overzicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2 2 2 2 3 4 4 5 5 6 6 7 2 Onderzoek 2.1 OSGi . . . . . . . . . . . . . . . . . . . . 2.1.1 Wat is OSGi? . . . . . . . . . . . 2.1.2 Hoe werkt OSGi? . . . . . . . . . 2.1.3 Voordelen van OSGi . . . . . . . 2.1.4 OSGi implementaties . . . . . . . 2.1.5 OSGi en deze thesis . . . . . . . 2.2 J2ME . . . . . . . . . . . . . . . . . . . 2.2.1 Wat is J2ME? . . . . . . . . . . 2.2.2 Configuraties . . . . . . . . . . . 2.2.3 Profielen . . . . . . . . . . . . . . 2.2.4 J2ME en deze thesis . . . . . . . 2.3 Java Native Interface . . . . . . . . . . . 2.3.1 Wat is de Java Native Interface? 2.3.2 Werking van JNI . . . . . . . . . 2.3.3 JNI en deze thesis . . . . . . . . 2.4 Doelplatformen . . . . . . . . . . . . . . 2.5 Java Community Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 8 8 9 11 12 13 13 13 15 16 16 17 17 18 18 19 19 3 De API specificatie 3.1 Resources API . . . 3.1.1 Resources . . 3.1.2 Factorymodel 3.2 Monitoring API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 23 23 25 27 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . INHOUDSOPGAVE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 29 31 32 34 39 4 De implementatie 4.1 Algemene Structuur . . . . . . . . . . . . . . . . . . 4.1.1 Scheiding tussen specificatie en implementatie 4.1.2 Platformonafhankelijke code . . . . . . . . . . 4.1.3 Platformafhankelijke code . . . . . . . . . . . 4.2 Implementatie van een Resource: RAM . . . . . . . 4.2.1 Platformonafhankelijke code . . . . . . . . . . 4.2.2 Platformafhankelijke code . . . . . . . . . . . 4.3 Implementatie van de Monitor . . . . . . . . . . . . 4.3.1 MonitorService . . . . . . . . . . . . . . . . 4.4 Integratie met het OSGi-framework . . . . . . . . . . 4.4.1 Bundels . . . . . . . . . . . . . . . . . . . . . 4.5 Problemen en opmerkingen . . . . . . . . . . . . . . 4.5.1 Speciale resources . . . . . . . . . . . . . . . 4.5.2 Het wiel opnieuw uitvinden . . . . . . . . . . 4.5.3 Niet geı̈mplementeerde resources . . . . . . . 4.5.4 Theorie vs. praktijk . . . . . . . . . . . . . . 4.6 Besluit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 40 41 41 42 42 44 45 47 47 47 48 49 49 49 50 50 53 5 Toepassing 5.1 Resourcemonitor . . . . . . . . 5.2 MediaPlayer . . . . . . . . . . . 5.2.1 Gebruikte resources . . 5.2.2 Instellingen . . . . . . . 5.3 Java Media Framework . . . . . 5.3.1 Installatie . . . . . . . . 5.3.2 Mogelijkheden . . . . . 5.3.3 Alternatieven . . . . . . 5.4 Architectuur . . . . . . . . . . 5.4.1 MultiSourcePlayer . . 5.4.2 Grafische user interface 5.5 OSGi-compatibel . . . . . . . . 5.5.1 Activator . . . . . . . . 5.5.2 Instellingen . . . . . . . 5.6 Besluit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 55 56 56 57 58 59 59 59 60 60 61 61 62 62 63 3.3 3.4 3.2.1 Resourcemonitors . 3.2.2 Monitorservice . . Gebruik van de API . . . 3.3.1 Resources API . . 3.3.2 Monitoring API . . Besluit . . . . . . . . . . . ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Besluit 64 A Een A.1 A.2 A.3 66 67 67 67 overzicht van OSGi-implementaties Java Embedded Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . OSCAR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Knopflerfish . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . INHOUDSOPGAVE iii A.4 Service Management Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . A.5 Overzicht . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 68 B Ledenlijst OSGi 70 C Beknopt overzicht van de API 72 D Beknopt overzicht van de implementatie 74 E API specificatie changelog 75 F Howto: gebruik van de API F.1 Opmerkingen . . . . . . . . . . . F.2 Gebruik van resources en OSGi . F.2.1 Broncode . . . . . . . . . F.2.2 Compileren . . . . . . . . F.2.3 Inpakken in een JAR-file F.3 Gebruik van een default Monitor F.3.1 Code . . . . . . . . . . . . F.3.2 Compileren . . . . . . . . F.3.3 Inpakken in een JAR-file F.4 Een BatterijMonitor . . . . . . . F.4.1 Code . . . . . . . . . . . . G Handleiding MediaPlayer G.1 Input . . . . . . . . . . . G.2 Startscherm . . . . . . . G.3 Preferences . . . . . . . G.3.1 Algemeen . . . . G.3.2 Bestandskeuze . G.3.3 Schermgrootte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 77 77 78 79 79 80 80 82 82 83 83 . . . . . . 85 85 85 86 86 87 87 H Tools 89 I 90 cd-rom Bibliografie 91 LIJST VAN FIGUREN iv Lijst van figuren 2.1 2.2 2.3 2.4 Structuur van het service platform . . . . . . . . . . . . . . . . . . . . . . . . . . Schematisch overzicht van de beschikbare Java-platformen en hun onderlinge verhouding. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Schematisch overzicht van de J2ME configuraties en profielen. . . . . . . . . . . . JNI vormt een brug tussen een native taal en Java. . . . . . . . . . . . . . . . . . 14 15 17 3.1 3.2 3.3 3.4 3.5 (a) Request-response model, (b) Event model De hiërarchie van de resources . . . . . . . . . De factoryklasse SystemResourceFactory . De hiërarchie van de monitors . . . . . . . . . Gebruik van het monitorgedeelte van de API . . . . . 22 23 26 28 35 4.1 4.2 De verschillende lagen in de implementatie. . . . . . . . . . . . . . . . . . . . . . (a) Het eenvoudigste geval met een één op één relatie, (b) Een meer complexe structuur van geheugens en storage-devices. . . . . . . . . . . . . . . . . . . . . . 42 5.1 5.2 5.3 (a) Monitor van het CPU-gebruik, (b) Monitor van het RAM-gebruik. . . . . . . De mediaplayer in actie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . De implementatie-klasse van de mediaplayer met de belangrijkste methoden. . . 55 57 61 G.1 G.2 G.3 G.4 Startscherm van de MediaPlayer . . . . . . . . . Instellen welke acties mogen ondernomen worden. Instellingen voor de bestandswissel. . . . . . . . . Instellingen voor de schermwissel. . . . . . . . . . 86 86 87 87 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 43 Opmerkingen aan de lezer Tijdens het schrijven van deze thesis werden een aantal conventies aangehouden om de leesbaarheid te verhogen. Een belangrijk deel van het werk bestond immers uit het ontwikkelen van software en bijgevolg duikt in de tekst ook hier en daar een verwijzing naar de broncode of een integraal code-voorbeeldje op. Wanneer er in de doorlopende tekst een verwijzing naar code opduikt, of er klassenamen gebruikt worden zal dit als volgt aangeduid zijn: De klasse VoorbeeldKlasse laat de gebruiker toe om via de methode voorbeeldMethode() aan te tonen hoe code in doorlopende tekst wordt weergegeven. Wanneer het om een integraal code-voorbeeld gaat wordt dit als volgt aangegeven. /**Dit is Java-code**/ public void eenMethode() { System.out.println("Hello World"); } Af en toe wordt er ook de nadruk gelegd of dient er extra aandacht besteed te worden aan een bepaalde opmerking, en dat gebeurt dan op deze manier. EEN NIEUWE SOFTWAREWERELD 1 Hoofdstuk 1 Een nieuwe softwarewereld Software is alomtegenwoordig. De tijd dat computers hele kamers of zelfs verdiepingen in beslag namen is reeds lang voorbij. Steeds meer wordt elk elektronisch apparaat ook een (kleine) computer. Natuurlijk bestaan de mainframes nog steeds en ook het rijk van de desktopcomputers is nog lang niet uit. Ondertussen zijn ook de PDA’s en GSM’s met extra functionaliteit reeds immens populair. Maar daar eindigt het niet. De toekomst lonkt met wasmachines die automatisch het juiste programma kiezen, ijskasten die zelf bestellingen plaatsen wanneer de melk op is en wagens die intelligent gaan meesturen en remmen. Deze explosie van ’nieuwe’ apparaten heeft als gevolg dat er een even grote, zo niet grotere explosie van software ontstaan is. Elk toestel heeft immers eigen specifieke software nodig. Er is echter ook de wens om dezelfde software op al deze verschillende toestellen te gebruiken. Hoe vaak controleert men immers niet of men e-mail ontvangen heeft? Waarom zou men telkens weer naar een vaste computer moeten lopen enkel en alleen om na te gaan of er nieuwe berichten zijn. Zou het niet veel makkelijker zijn als we dit vanop de geı̈ntegreerde display van onze ijskast kunnen doen, of met behulp van onze PDA die we toch overal bij hebben? Wat we dus eigenlijk willen bereiken is universele software die op een hele reeks van apparaten kan gebruikt worden, zichzelf kan aanpassen aan de eigenschappen van de apparaten en dit zonder enige aanpassing of niet triviale tussenkomst van de gebruiker. Het hoeft ook niet te eindigen bij aanpassingen aan de statische eigenschappen van de apparaten. Wanneer we het grappige filmpje dat we als e-mail attachment gekregen hebben op onze ijskast bekijken, willen we uiteraard niet dat dit de normale werking van de ijskast gaat beı̈nvloeden omdat alle systeemresources opgeslorpt worden. We wensen dus ook software die zich dynamisch 1.1 Vereisten 2 kan aanpassen aan de omstandigheden waarin het toestel zich bevindt. 1.1 Vereisten Het idee van deze universele en adaptieve software is natuurlijk wel heel mooi, maar het stelt ook heel wat extra eisen aan de software en aan het onderliggende platform. 1.1.1 Software Aangezien we willen dat de software op eender welk apparaat gebruikt kan worden, dient deze software platformonafhankelijk te zijn. Daarnaast moet bij het ontwerpen van de software ook rekening gehouden worden met de capaciteiten van de mogelijke doelplatformen, bv. het al dan niet aanwezig zijn van een netwerkverbinding en de bandbreedte ervan. In de software moeten natuurlijk ook de nodige mechanismen ingebouwd zitten zodat deze zich dynamisch kan aanpassen. 1.1.2 Platform De eisen van platformonafhankelijkheid en kennis van de capaciteiten van het gebruikte platform lijken tegenstrijdig. Het is niet mogelijk om dit op een triviale manier te realiseren. Daarom is er nood aan een algemene API die op een uniforme manier toelaat om de platformspecifieke gegevens op te vragen. Eens we de platformonafhankelijke adaptieve software hebben, zouden we deze uiteraard graag gebruiken. Maar vaak is het installeren en weer verwijderen van software niet altijd even eenvoudig. Laat staan dat het altijd automatisch kan. Daarom stellen we aan het platform nog de bijkomende eis, dat het installeren en weer verwijderen van software op een heel eenvoudige manier kan gebeuren. 1.2 Voordelen De voorgestelde scenario’s uit de inleiding klinken dan misschien wel futuristisch, deze toekomst is niet meer zo veraf. Bovendien heeft dit concept nog heel wat meer toepassingsgebieden en 1.2 Voordelen 3 misschien meer concrete voordelen. Zowel naar de gebruiker als ontwikkelaar van de software toe. We zetten hier de voordelen netjes op een rij. 1.2.1 Ontwikkeling en onderhoud Eerst en vooral zijn er natuurlijk de voordelen op het gebied van de ontwikkeling en het onderhoud van de software. Ontwikkelen van de software Write once... Een populair concept, dat vooral in de open source wereld gepromoot wordt, is ’write once, compile everywhere’, wat dus wil zeggen dat men software éénmaal goed schrijft en men vervolgens de broncode overal kan compileren tot een uitvoerbaar programma. Uiteraard blijven we ook dit concept hanteren. Compile once... Om onze doelstellingen te bereiken, dienen we echter nog een stap verder te gaan: de code slechts éénmaal compileren. Dit concept wordt ook vandaag al toegepast en gebruikt, namelijk in de wereld van de virtuele machines, zoals gebruikt bij Java en .NET. Op deze manier wordt de gecompileerde code dan uitgevoerd door de virtuele machine, onafhankelijk van het platform waarop deze virtuele machine zelf draait. Run everywhere... (really!) Het vorige punt doet misschien vermoeden dat de software reeds op alle mogelijke platformen waarvoor de virtuele machine bestaat kan uitgevoerd worden. Dit is tot op zekere hoogte waar, maar de software zal zich niet aanpassen aan het toestel waarop deze gebruikt wordt. Of de software gaat uit van bepaalde onderstellingen over de hardware, wat deze ongeschikt maakt voor toestellen die hier niet aan voldoen. Testen en debuggen Aangezien er maar één software-versie bestaat, moet er uiteraard maar één versie getest en gedebugged worden. Het is niet nodig dat de software op alle mogelijke doelapparaten apart dient getest te worden. In belangrjke mate zal het testwerk op een standaardplatform kunnen uitgevoerd worden, en dient slechts in de eindfase getest te worden met specifieke apparaten. Dit zal ook de ontwikkelingscyclus versnellen. 1.2 Voordelen 4 Onderhoud Het onderhoud van de code wordt beperkt tot één versie, wijzigingen dienen slechts éénmaal gemaakt te worden. Hetzelfde geldt voor uitbreidingen. De ondersteuning van nieuwe platformen zal zelfs geen extra ingrepen vereisen. Handleiding De handleiding voor de software dient slechts éénmaal geschreven te worden. Hoewel de software er misschien anders kan uitzien op de verschillende platformen gaat het immers echt om hetzelfde pakket, dat bijgevolg één handleiding nodig heeft. 1.2.2 Gebruik Wat betreft gebruiksgemak maken we eveneens een sprong voorwaarts. De eindgebruiker dient immers slechts éénmaal het gebruik van het pakket aan te leren. Ook hier is de opmerking terecht dat het pakket er op verschillende platformen anders kan uitzien, maar de gebruikte concepten en manier van werken blijven hetzelfde. Iets wat in het geval van totaal verschillende pakketten niet noodzakelijk het geval zal zijn. 1.2.3 Economisch De economische voordelen zijn onrechtstreekse gevolgen van de vorige. Aangezien er slechts één versie van de software dient ontwikkeld te worden, zijn er uiteraard minder kosten. De ontwikkelaars dienen niet meer naast elkaar aan hetzelfde probleem te werken op de verschillende platformen of moeten hun aandacht niet meer over verschillende platformen verdelen. Er hoeft slechts éénmaal een handleiding geschreven te worden en het pakket kan in één variant verspreid worden. Ook voor de eindgebruiker zal de software goedkoper zijn aangezien de aankoop van één versie zal volstaan om op alle apparaten te gebruiken. Dit waar voordien voor bijna elk apparaat een andere versie diende aangeschaft te worden. 1.3 Problemen en oplossingen 1.3 5 Problemen en oplossingen Bij het zoeken naar een omgeving en ontwikkelen van software om de aangehaalde voordelen waar te maken, stuiten we natuurlijk op een aantal problemen. 1.3.1 Software is platformafhankelijk Kijken we naar de bestaande software, dan merken we op dat deze grotendeels platformafhankelijk is. Dit houdt in dat je een bepaald softwarepakket niet zonder meer op meerdere verschillende toestellen kan installeren en gebruiken. Het platformonafhankelijk zijn van software kan zich op verschillende niveau’s manifesteren. Broncode Eerst en vooral is de broncode niet altijd zonder meer porteerbaar naar andere platformen. Dit kan doordat er onzorgvuldig geprogrammeerd werd, maar veel vaker door het gebruik van platformafhankelijke bibliotheken of soms gewoon omdat de taal en compiler zelf enkel beschikbaar zijn voor een beperkt aantal platformen. De oplossing voor dit probleem is relatief eenvoudig en bestaat erin een taal en compiler te kiezen die beschikbaar zijn voor alle platformen en zich te beperken tot bibliotheken beschikbaar voor alle platformen. Uitvoerbare code Het kiezen van een goede taal is echter niet voldoende. In de compilatiestap wordt de broncode immers omgezet naar uitvoerbare code. Typisch is dit assemblercode voor een specifieke combinatie van processorarchitectuur en besturingssysteem. Concreet zal de platformonafhankelijke broncode omgezet worden naar platformafhankelijk uitvoerbare code. Dit brengt ons nog niet ver genoeg, aangezien de uitvoerbare bestanden dan nog steeds verschillend zijn voor alle platformen. Om dit probleem op te lossen kan er gebruik gemaakt worden van platformen zoals Java en .NET. Hier wordt de broncode immers omgezet naar een platformonafhankelijke bytecode. Deze bytecode wordt vervolgens uitgevoerd door een virtuele machine. De virtuele machine dient dan 1.4 Doelstelling 6 éénmaal geı̈mplementeerd te worden voor alle platformen, en daarna kan deze op alle platformen waarvoor de virtuele machine bestaat uitgevoerd worden. Veronderstellingen over het gebruikte platform Vaak wordt software reeds van bij de eerste stap in het ontwikkelingsproces voor een specifiek doelplatform ontwikkeld. Het gevolg hiervan is dat heel wat van de ontwerpbeslissingen uitgaan van een zekere voorkennis van dit platform, bv. de geheugencapaciteit of rekenkracht. Dit probleem gaat heel wat dieper dan de keuze van taal of virtuele machine en is achteraf ook niet zo eenvoudig op te lossen. Gedurende heel het ontwikkelingsproces dient er nauwlettend in de gaten gehouden te worden welke ontwerpbeslissingen mogelijk zijn. In het ideale geval kan met at runtime nagaan wat wel en niet kan. 1.3.2 Installatie en deı̈nstallatie Het installeren en deı̈nstalleren van software is niet altijd een eenvoudige taak. Het is belangrijk dat dit proces zo eenvoudig mogelijk is. Zeker indien we willen evolueren naar situaties waar de software ook zichzelf moet kunnen installeren en deı̈nstalleren. Om dit te kunnen verwezenlijken is het gebruik van een framework zoals OSGi ideaal. Dit laat toe het installatie- en deı̈nstallatieproces te beschouwen als het eenvoudig in- en uitpluggen van een stopcontact. 1.4 Doelstelling Tot zover zijn er een aantal eisen geformuleerd die vervuld dienen te worden, evenals een aantal problemen met suggesties tot oplossingen. Het doel van deze thesis bestaat erin: • Een OSGi-compatibele Java API op te bouwen die toelaat om gegevens over het platform op te vragen. Deze thesis richt zich vooral naar de mobiele en daardoor vaak meer beperkte apparaten. Dit moet weerspiegeld worden in de API doordat deze geen te hoge systeemeisen mag stellen. 1.5 Overzicht 7 • In een implementatie van deze API te voorzien. • Een toepassing te schrijven die gebruik maakt van deze API om de mogelijkheden te demonstreren. 1.5 Overzicht In hoofdstuk 2 zullen eerst alle technologieën overlopen en besproken worden om daarna in hoofdstuk 3 een API op te stellen en in hoofdstuk 4 een concrete implementatie te realiseren. Tenslotte zal in hoofdstuk 5 een applicatie ontwikkeld worden die de mogelijkheden demonstreert. Het opstellen van de API gebeurde in samenwerking met Koen Van Boxstael. Aangezien de onderzoeksfase en de ontwikkeling van de API gemeenschappelijk waren, werden hoofdstuk 2 en 3 samen geschreven. Zo ook appendices A, C en E. ONDERZOEK 8 Hoofdstuk 2 Onderzoek Dit hoofdstuk geeft een overzicht van de technologie en het gedane opzoekingswerk nodig voor het verwezenlijken van deze thesis. De belangrijkste technologieën die een groot deel van de zoektocht uitmaakten, zijn OSGi (zie punt 2.1), J2ME (zie punt 2.2) en JNI (zie punt 2.3). Verder is er ook onderzocht wat de mogelijkheden van de doelplatformen zijn, en welke native ondersteuning er beschikbaar is om informatie over het systeem op te vragen (zie punt 2.4). Ten slotte vonden we het ook nuttig eens na te gaan hoe een standaardisatieproces van een Java specificatie verloopt in het Java Community Process (zie punt 2.5). 2.1 2.1.1 OSGi Wat is OSGi? OSGi staat voor Open Services Gateway Initiative. Het is een organisatie die open specificaties maakt voor het leveren van services over een netwerk aan thuisgebruikers, bedrijven, auto’s en vele andere omgevingen. Het biedt aan serviceproviders, ontwikkelaars en software-ontwerpers een gemeenschappelijk raamwerk voor het ontwikkelen en ontplooien van services [1]. Alle apparaten waarop een implementatie van de OSGi specificaties aanwezig is, kunnen hierbij gebruikt worden. Daarenboven gebruikt de specificatie Java als taal, wat ervoor zorgt dat services kunnen ontwikkeld worden op een platformonafhankelijke manier. 2.1 OSGi 9 De eerste specificatie van OSGi in mei 2000 was vooral gericht op het gebruik van het framework op home network gateways. Dit zijn apparaatjes, zoals set-top boxes en modems, die verbonden zijn met een netwerk en diensten kunnen aanbieden aan thuisgebruikers. De tweede en derde release van de specificatie breiden de eerste uit en hebben van OSGi een platform gemaakt dat voor veel meer apparaten bedoeld is. Tot de mogelijkheden behoren nu ook o.a. PC’s, mobiele telefoons, allerhande consumer electronics zoals PDA’s, mobiele toepassingen in auto’s . . . Eén van de scenario’s waarvoor het OSGi framework ontworpen is, is een netwerk beheerd door een operator waarop een groot aantal service platforms is aangesloten. Deze kunnen services aanbieden van verschillende serviceproviders. Dit is echter slechts één mogelijk scenario want de specificatie laat vele andere mogelijkheden open. Een voorbeeld hiervan is Eclipse1 dat sinds versie 3.0 M5 gebruik maakt van OSGi om het gebruik van plugins mogelijk te maken. 2.1.2 Hoe werkt OSGi? Het service platform bestaat uit een Java Virtuele Machine (JVM), een implementatie van het OSGi framework en bundels die erop draaien. Een overzicht van de structuur is te zien in figuur 2.1. Het besturingssysteem en de hardware zijn uiteraard de onderste laag. Voor de werking van OSGi is deze schakel echter niet zo belangrijk. Voor het geheel van de thesis uiteraard wel, aangezien hier alle systeeminformatie dient opgevraagd te worden. Dit komt uitgebreider aan bod in punt 2.3. De Java Virtual Machine zorgt voor een platformonafhankelijke basis waarbinnen het OSGi framework zich nestelt. Een uitgebreide bespreking van de eisen gesteld aan de JVM volgt in punt 2.2. Het OSGi framework verzorgt dan een gestandaardiseerde omgeving waarbinnen bundels kunnen geı̈nstalleerd worden. Bundels kunnen dynamisch geladen, gestart, gestopt, geüpdatet en gedeı̈nstalleerd worden door het framework. 1 een Java IDE, zie http://www.eclipse.org en http://www.aqute.biz/eclipse.html voor meer informatie. 2.1 OSGi 10 Monitor ... Resources log servlet http bundels OSGi framework Java Virtual Machine OS & Hardware Figuur 2.1: Structuur van het service platform Een bundel bestaat uit alle Java klassen en andere resources die nodig zijn om een bepaalde functie aan te bieden aan de gebruiker of om services aan te bieden aan andere bundels. Concreet zijn het archiefbestanden, JAR of Java ARchive. Het archief bevat een standaard manifest[2] met enkele specifieke OSGi-headers. Dit manifest is een tekstbestand waarin informatie staat over de bundel die het framework nodig heeft, zoals een beschrijving van de bundel, de packages die het nodig heeft, de packages die het zelf ter beschikking stelt en een aanduiding welke klasse gebruikt moet worden als activator. De activator is een klasse met een start() en een stop() methode, die aangeroepen worden bij het starten resp. stoppen van de bundel. Op deze manier kan een bundel geı̈nitialiseerd worden en kan hij zijn resources opruimen bij het stoppen. Na de start van de bundel zijn de services en de geëxporteerde packages beschikbaar voor andere bundels. Het framework regelt zelf alle afhankelijkheden tussen bundels. In figuur 2.1 worden enkele standaardbundels getoond en op hetzelfde niveau de 2 nieuwe bundels die het resultaat zijn van deze thesis. De uitwerking hiervan volgt in de latere hoofdstukken. Gedurende zijn levensloop kan een bundel zich in één van de volgende toestanden bevinden: INSTALLED De bundel is succesvol geı̈nstalleerd en de eventuele native libraries zijn aanwe- 2.1 OSGi 11 zig. RESOLVED Alle klassen vereist door de bundel zijn aanwezig. De bundel is ofwel klaar om gestart te worden of is reeds gestopt. Packages die de bundel exporteert worden beschikbaar gemaakt voor andere bundels wanneer hij voor de eerste keer in deze toestand terechtkomt. STARTING De bundel werd gestart, maar het startproces is nog niet beëindigd. STOPPING De bundel werd gestopt, maar het stopproces is nog niet beëindigd. ACTIVE De bundel werd succesvol gestart en is momenteel actief. De services die hij heeft geregistreerd zijn beschikbaar voor andere bundels. UNINSTALLED De bundel werd gedeı̈nstalleerd. Dit is een eindtoestand en de bundel kan niet meer van toestand veranderen. Het installeren, starten, stoppen en deı̈nstalleren van een bundel kan door rechtstreeks commando’s in te geven2 in het OSGi framework of vanuit andere bundels. Deze manier van werken laat bijvoorbeeld ook toe om bundels vanop afstand te installeren door middel van een beheerdersbundel, bijvoorbeeld onder de vorm van een webinterface. De overgang tussen de verschillende toestanden kan een aantal events veroorzaken. Zo zal het installeren van een bundel een BundleEvent veroorzaken, hetzelfde gebeurt voor de andere toestandsovergangen. Een BundleEvent is eigenlijk een event afgeleid van een FrameworkEvent. Dit kan bijvoorbeeld voorkomen na een fout in het framework, het updaten van packages of gewoon om aan te geven dat het framework klaar is met opstarten. Verder is er ook nog het ServiceEvent. Indien een bundel een aantal services beschikbaar stelt, zal bij het stoppen van de bundel een ServiceEvent gegenereerd worden dat aangeeft dat de service gestopt wordt. Dit laat toe dat bundels die gebruik maken van deze service hun resources kunnen vrijgeven zodat deze service kan beëindigd worden. Analoog worden er ook events gegenereerd bij het registreren, wijzigen. . . van services. 2.1.3 Voordelen van OSGi De voordelen van het OSGi-framework zijn legio. Enkele van de meest markante zijn: 2 Eigenlijk is de console waar deze commando’s kunnen ingegeven worden niets meer dan een bundel die textuele commando’s vertaalt naar de juiste methode-oproepen in het framework. 2.1 OSGi 12 Platformonafhankelijk Het OSGi-framework maakt gebruik van het Java-platform, dat zelf al platformonafhankelijk is. Bijgevolg kan het framework op elk apparaat met Javaondersteuning3 geı̈nstalleerd en gebruikt worden. Bovendien hoeven bundels maar één keer geschreven te worden in Java, en niet specifiek voor elk type of merk van het doelapparaat. Eenvoudige (de-)installatie van bundels Het installeren van bundels kan over het netwerk of van een gegevensdrager. Bovendien is er geen installatie-procedure nodig. De bundel kan onmiddellijk ingeplugged en gestart worden. Net als de installatie is ook het verwijderen van bundels heel eenvoudig. Alles kan als het ware gebeuren met een eenvoudige druk op de knop. Een bijkomend voordeel is dat er geen bestanden of referenties in het besturingssysteem achterblijven, want het is er immers volledig onafhankelijk van. Open De OSGi-specificatie is open, onafhankelijk van een bepaald bedrijf of platform. Ook heel algemeen en dus onafhankelijk van het scenario waarvoor het gebruikt zal worden, iedereen mag en kan een implementatie maken zolang deze maar voldoet aan de specificatie. Het ontwikkelen van de specificatie gebeurt door de OSGi-leden, een volledige lijst is terug te vinden als bijlage. 2.1.4 OSGi implementaties De OSGi Alliance zorgt voor het ontwikkelen en de specificatie van het services platform. Ze levert zelf echter geen implementatie. Dit wordt overgelaten aan derden. Een aantal van de momenteel bestaande implementaties zijn: • Java Embedded Server 2.0 (SUN) [3] • mBedded Server (Procyst) [4] • aveLink Embedded Gateway (Atinav) [5] • Oscar (Open Source Software) [6] • KnopflerFish (Open Source Software) [7] Gebaseerd op het Gatespace GDSP OSGi framework [8] • IBM Service Management Framework (IBM) [9] 3 Elk apparaat dat voldoet aan de CDC/Foundation profile, zie pagina 16 2.2 J2ME 13 Als referentie gateway zullen zowel KnopflerFish als SMF gebruikt worden. Een vergelijking van een aantal van deze implementaties is als bijlage toegevoegd. 2.1.5 OSGi en deze thesis Het uiteindelijke doel van ons werk bestaat erin een OSGi-bundel te creëren die onze packages exporteert, en aldus andere bundels toelaat er gebruik van te maken. Het framework dat we bouwen maakt gebruik van de Java Native Interface (zie punt 2.3). Het OSGi-framework laat dit op zijn beurt toe door de native libraries te includeren in de bundels en het bundelmanifest te voorzien van de Bundle-NativeCode header. In deze header kan gespecificeerd worden waar de native library voor elk ondersteund besturingssyteem en elke ondersteunde architectuur te vinden zijn. Afhankelijk van het gebruikte systeem zal het OSGiframework dan zelf uitzoeken welke native library geschikt is. Op die manier is het dus niet nodig om voor elk platform een aparte bundel te creëren. 2.2 2.2.1 J2ME Wat is J2ME? De Java 2 Micro Edition is een sterk geoptimaliseerde Java runtime omgeving, met als doelstelling het gebruik in elektronische gebruiksapparaten. J2ME behelst heel de ruimte van gebruiksapparaten, gaande van smartcards of beepers tot en met geavanceerde apparaten zoals een set-top box. Na het ontstaan van Java werd het platform alsmaar verder uitgebreid om de taal zo aantrekkelijk mogelijk te maken voor de commerciële eindgebruikers. De mogelijkheden en grootte van het Java-platform namen alsmaar toe. Ten tijde van de eerste commerciële release was er al de noodzaak tot opsplitsing. Er is het J2SE platform, Java 2 Standard Edtion, met de basisfunctionaliteit. Optionele packages kunnen hieraan toegevoegd worden om aan specifieke eisen te voldoen. Er was ook een platform gericht naar server- en bedrijfstoepassingen, J2EE, Java 2 Enterprise Edition. In figuur 2.2 worden de verhoudingen tussen de verschillende platformen grafisch voorgesteld. Merk hierbij op dat J2ME niet enkel een subset is van de andere platformen. 2.2 J2ME 14 Figuur 2.2: Schematisch overzicht van de beschikbare Java-platformen en hun onderlinge verhouding. Gedurende die ontwikkeling van Java werden de systeemvereisten groter met elke release. De vereisten van het Java-platform waren veel te groot geworden voor gebruik in consumentenelektronica, ironisch genoeg net dat waar Java oorspronkelijk voor ontwikkeld was (al heette het toen nog Oak). Als antwoord hierop creëerde Sun een aantal gereduceerde Java-omgevingen4 , elk gericht op een specifiek marktsegment, en allen gebaseerd op JDK 1.1, de voorganger van Java 2. Dit is waar J2ME, gebaseerd op Java 2, om de hoek komt kijken. Uiteindelijk zal het de bedoeling zijn dat het alle voorgaande JDK 1.1-producten vervangt. De doelstellingen van J2ME zijn heel uitgebreid, en de verschillende toepassingsgebieden hebben heel verschillende en specifieke eisen. Het is bijgevolg bijna vanzelfsprekend, dat J2ME zelf weer opgesplitst wordt in een aantal specificaties, elk met een eigen verzameling doelstellingen en toepassingsmogelijkheden, geschikt voor een bepaalde klasse van consumentenelektronica. Zo’n subset van de volledige Java programmeeromgeving voor een specifiek toestel wordt gedefinieerd door één of meerdere profielen (profiles) die de basiseigenschappen van een configuratie (configuration) uitbreiden. Elk profiel stelt extra eisen aan het toestel, waardoor het gamma van geschikte toestellen kleiner wordt naarmate het gebruik van bijkomende profielen. Een schematisch overzicht van de verschillende profielen en configuraties is terug te vinden in figuur 2.3. 4 JavaCard, EmbeddedJava, PersonalJava 2.2 J2ME 15 Figuur 2.3: Schematisch overzicht van de J2ME configuraties en profielen. 2.2.2 Configuraties De configuraties worden opgesplitst aan de hand van een aantal eigenschappen zoals • De types en hoeveelheid van het beschikbaar geheugen. • Het type van de processor en zijn snelheid. • Het type netwerkconnectie beschikbaar voor het toestel. Een configuratie representeert een minimaal platform en definieert geen optionele features. Dit om ontwikkelaars toe te laten om hun toepassingen zo platformonafhankelijk mogelijk te maken. Momenteel zijn er 2 configuraties gedefinieerd: Connected Limited Device Configuration (CLDC) richt zich tot de low-end toestellen zoals gsm’s of pda’s met een beschikbaar geheugen van om en bij de 512KiB. Omwille van deze reden is CLDC dan ook nauw verbonden met wireless Java, dat als doel heeft om gsm toestellen toe te laten, kleine Java-applicaties (MIDlets) te laten downloaden en uitvoeren. [10, 11] Connected Device Configuration (CDC) richt zich tot alle toestellen die tussen de CLDC configuratie liggen en volwaardige desktopsystemen. Deze toestellen hebben doorgaans een grotere geheugencapaciteit (2MiB of meer) en krachtigere processors, waardoor ze bijgevolg een meer volledige Java-omgeving kunnen ondersteunen. [12] 2.2 J2ME 16 Elke configuratie bestaat uit een Java virtual machine en een collectie Java basisklassen die in een programmeeromgeving voorzien. De beperkingen van de doeltoestellen maken het onmogelijk om het volledige Java platform te ondersteunen. Om die reden zijn de J2ME Virtual Machines vaak gespecificeerd in termen van die delen van de Java Virtual Machine [13] en de Java Language Specification [14] die niet ondersteund hoeven te worden. Zo specificeert de CLDC-specificatie dat de Virtual Machine de types float en double niet moet ondersteunen, aangezien de onderliggende hardware vaak geen floating point berekeningen ondersteunt. 2.2.3 Profielen Een profiel breidt een configuratie uit met additionele klassen die features toevoegen specifiek aan bepaalde apparaten. Mobile Information Device Profile (MIDP) is gespecificeerd bovenop de CLDC configuratie en is ontworpen voor mobiele telefoons en eenvoudige PDA’s. Het biedt een API voor user interfaces, netwerkverbindingen, dataopslag en applicatiebeheer. Foundation Profile (FP) is het eenvoudigste profiel bovenop de CDC configuratie. Het is ontworpen voor embedded implementaties zonder user interface. De CDC profielen zijn gelaagd, en bovenop het het FP profiel kunnen dus ook nog andere profielen toegevoegd worden. Personal Basis Profile (PBP) is een subset van het PP met ondersteuning voor netwerkconnecties en een beperkte grafische user interface. PBP is gelaagd op het FP. Personal Profile (PP) is gericht op toestellen die een volledige GUI- en applet-support vereisen, zoals PDA’s, communicator-type toestellen en gameconsoles. Het biedt de volledige AWT-toolkit en laat toe om web-based applets uit te voeren. Het Personal Profile vervangt de PersonalJava technologie. PP is gelaagd op het FP. 2.2.4 J2ME en deze thesis Het doel van deze thesis is een framework te creëren voor het toegankelijk maken en het monitoren van hardware resources op een beperkt mobiel toestel, meer bepaald op een OSGi-gateway. Uiteraard trachten we geen extra eisen wat betreft systeemvereisten op te leggen dan diegene, reeds opgelegd door het OSGi-framework. Het gebruik van de Java Native Interface (JNI) is 2.3 Java Native Interface 17 essentieel. Het moet immers mogelijk zijn om rechtstreeks te interageren met het onderliggende besturingssysteem om toegang te krijgen tot de systemresources. JNI is echter niet beschikbaar in de CDLC-configuratie, maar wel in de CDC-configuratie. Daarom zal er met het CDC/Foundation Profile gewerkt worden. 2.3 2.3.1 Java Native Interface Wat is de Java Native Interface? De Java Native Interface (JNI) [15] is een onderdeel van het Java platform dat een brug vormt tussen Java en andere programmeertalen zoals C of C++ (zie figuur 2.4). Men noemt deze nietJavacode meestal met de Engelstalige benaming native code. JNI maakt het mogelijk om native code geschreven in C/C++ aan te roepen vanuit een Java programma. Dit kan bestaande code zijn die men wil hergebruiken, maar kan ook nieuwe code zijn. Langs de andere kant biedt JNI ook een mogelijkheid om Java code en een Java Virtual Machine (JVM) aan te roepen vanuit programma’s geschreven in C of C++. Java Native Functions Libraries JNI Exceptions Classes VM Figuur 2.4: JNI vormt een brug tussen een native taal en Java. Java is ontworpen om platformonafhankelijk te zijn. Soms kan het echter nodig zijn om toegang te hebben tot mogelijkheden die niet ondersteund worden door de Java API en wel specifiek zijn voor de hardware en het besturingssysteem waarop de JVM draait. Het kan ook nodig zijn om voor zeer rekenintensieve bewerkingen een stukje supergeoptimaliseerde native code te 2.3 Java Native Interface 18 gebruiken. Dit is waarom JNI toegevoegd werd aan het Java platform. JNI heeft ook nadelen. Ten eerste, Java code die gebruik maakt van native code verliest haar platformonafhankelijkheid. Alleen op het platform waarvoor de native code geschreven is zal het programma nog werken. Ten tweede, verliest de programmeur de veiligheid van de Java taal. Java is fundamenteel verschillend van C/C++. De programmeur moet zeer nauwlettend de omzetting maken tussen Java datatypes en C datatypes en moet zelf instaan voor het geheugengebruik in de native code. 2.3.2 Werking van JNI De Java taal heeft een keyword native om aan te duiden dat een methode een native implementatie heeft. Deze methode zal ook een lege implementatie hebben in de Javacode. De JVM zal bij de uitvoering van een methode die native als modifier heeft de implementatie niet in het classbestand zoeken, maar in een bibliotheek van native code. De bibliotheek die moet gebruikt worden, wordt in een Java klasse ingeladen via een oproep van de API methode System.loadLibrary(). 2.3.3 JNI en deze thesis In onze thesis maken we een API om informatie op te vragen over systeem resources. Verschillende resources zijn systeem-specifiek en zijn ontoegankelijk via de Java API. Voor deze zal het nodig zijn om API’s te gebruiken van het onderliggende besturingssysteem of van andere bibliotheken die niet in Java maar in C/C++ geschreven zijn. JNI zal dus onontbeerlijk zijn om systeeminformatie beschikbaar te stellen aan Java applicaties. Het komt er dus eigenlijk op neer dat we een Java API zullen definiëren bovenop een bestaande API. Strikt genomen is het dus eigenlijk niet nodig om dit werk te leveren. De platformspecifieke API’s zijn echter zeer verschillend waardoor het niet mogelijk is om op een algemene wijze toegang te krijgen tot de systemresources. Willen we platformonafhankelijke software creëren die gebruik maakt van de specifieke eigenschappen van het platform, dan is er hoge nood aan een generieke API die deze resources beschikbaar maakt, op éénzelfde manier voor alle platformen. 2.4 Doelplatformen 2.4 19 Doelplatformen Uiteraard kan zo goed als elk platform gebruikt worden. De belangrijkste vereiste is de beschikbaarheid van een Java Virtuele Machine. Verder moet deze minstens J2ME CDC/Foundation Profile ondersteunen om gebruik te kunnen maken van de API. Daarnaast moet er natuurlijk ook de mogelijkheid bestaan om de gegevens die we willen beschikbaar maken te kunnen opvragen. Het zal daarom niet mogelijk zijn op elk platform elke resource te ondersteunen. Deze thesis richt zich vooral op mobiele en daardoor vaak ook beperktere toestellen. Laat dit nu ook net die groep van apparaten zijn waar het opvragen van de resources niet altijd triviaal is. Enkele van de mogelijke doelplatformen zijn: • iPAQ met PocketPC als besturingssysteem. De API’s van deze platformen kunnen gevonden worden op websites van HP [16] en Microsoft [17]. • Het besturingssysteem Symbian [18] dat op diverse mobiele telefoons te vinden is. • TabletPC met WindowsXP Tablet Edition als besturingssysteem [17]. Deze thesis zal specifiek het platform TabletPC met WindowsXP Tablet Edition als doelplatform gebruiken. 2.5 Intermezzo: Java Community Process Het Java Community Process [19] is de manier waarop alle nieuwe Java technologieën ontwikkeld worden. Iedereen kan hieraan deelnemen, zowel experts, bedrijven en het publiek. Het is het proces waarmee alle standaarden gemaakt worden die met Java te maken hebben. Het proces verloopt in grote lijnen als volgt. Initiation Een lid van JCP doet een voorstel voor een nieuwe specificatie of de wijziging van een bestaande. Zo’n voorstel heet in het jargon een JSR (Java Specification Request). Er wordt dan gestemd of het voorstel aanvaard wordt. Community Draft Wanneer de JSR aanvaard wordt, begint een groep van experts aan een eerste versie van de specificatie. Deze wordt nagekeken door de andere leden van de JCP community die feedback geven. Uiteindelijk wordt er gestemd over deze versie van de specificatie. 2.5 Java Community Process 20 Public Draft en Final Release De specificatie wordt opengesteld voor het grote publiek. Iedereen kan ze bekijken en er feedback over terugsturen naar de makers van de specificatie. Hiermee rekening houdend, wordt er een definitieve versie van de specificatie gemaakt die voor een laatste keer onderworpen wordt aan een stemming. Na een positieve stemming is de specificatie een nieuwe Java standaard. Bij het opstellen en de uitwerking van de API werd gebruik gemaakt van een vereenvoudigde en ingekorte versie van dit proces. DE API SPECIFICATIE 21 Hoofdstuk 3 De API specificatie De bedoeling van deze thesis is het beschikbaar maken van systeem resources in Java, opdat applicaties hiervan gebruik kunnen maken om zichzelf aan te passen aan de beschikbaarheid van bepaalde resources. We hebben een API gedefinieerd die dit mogelijk maakt. De API specificatie moet geı̈mplementeerd worden op ieder platform waarop we hem wensen te gebruiken. De implementatie zal dus platformspecifiek zijn. Vanuit het standpunt van de applicatieprogrammeur echter, zal code die gebruik maakt van de API volledig platformonafhankelijk zijn. Alle platformspecifieke code zit verborgen in de implementatie. De API zelf is geschreven in Java en ziet er dus op alle platformen hetzelfde uit. Naast het beschikbaar maken van resources is er een onderdeel van de API dat instaat voor monitoring. Dit is een framework dat event-gebaseerd is. De gebruiker kan beslissen welke resource hij wil laten monitoren en wanneer hij op de hoogte gebracht wil worden van een toestandswijziging van de resource d.m.v. een event. Het is belangrijk op te merken dat deze API enkel monitoring 1 toelaat en geen driving 2 . De toegang tot de resources is bijgevolg read-only, het is mogelijk om met behulp van deze API de eigenschappen van de beschikbare resources te raadplegen maar niet om ze ook te wijzigen. Doelstellingen De doelstellingen die we in gedachten hielden bij het definiëren van de API zijn dat de API o.a. de volgende eigenschappen heeft: 1 2 Het continu bevragen van een bepaalde eigenschap Het aansturen van hardware DE API SPECIFICATIE 22 Systeem resources beschikbaar op twee manieren Het opvragen moet kunnen gebeuren via een request-response model (figuur 3.1a) en via een event model (figuur 3.1b). userprogram resource getInformation() userprogram monitor service register() information resourceHasChanged() (a) (b) Figuur 3.1: (a) Request-response model, (b) Event model OSGi compatibel Het is in de eerste plaats de bedoeling dat de API kan gebruikt worden in een OSGi omgeving. Hij moet zonder wijzigingen ook gebruikt kunnen worden in een gewone J2SE of J2ME applicatie. Uitbreidbaar De API moet voldoende generieke klassen of interfaces bevatten die kunnen dienen als beginpunt voor verdere uitbreiding. Bovendien is het wenselijk dat het monitoren zo algemeen mogelijk is, zodat dit kan gebeuren onafhankelijk van welke resource er precies gemonitored wordt. Gemakkelijk in gebruik Het bevragen van resources moet eenvoudig zijn. Het moet mogelijk zijn met een beperkt aantal methode-aanroepen en de klassen, interfaces en methoden moeten voor de hand liggende namen hebben. We willen er ook voor zorgen dat alle courante resources zoals geheugen of netwerkbandbreedte standaard voorzien zijn in de API. In de volgende delen van dit hoofdstuk wordt besproken wat de structuur van de API is, waarom deze zo is en hoe men de API kan gebruiken. 3.1 Resources API 3.1 23 Resources API Het package dat de resources bevat heet resources. Het is opgebouwd uit een structuur van resources en een factory-klasse om de resources te creëren. De gedefinieerde resources zijn allemaal interfaces en de eigenlijke implementaties zijn verborgen voor de gebruiker. 3.1.1 Resources Resource De basisinterface Resource is de wortel van de boom die alle resources beschrijft, zie figuur 3.2. Alle andere resources zijn een uitbreiding van Resource of van een andere afgeleide interface. «interface» Resource +getName() +getVendor() +getVersion() +getProperty(in name) +getProperties() «interface» StaticResource «interface» Display +getSize() +getColorDepth() +isGraphical() «interface» DynamicResource +getTotalCapacity() +getAvailableCapacity() +getUsage() «interface» Keyboard +hasNavigationInput() +hasNumericalInput() +hasTextInput() «interface» Memory +isReadOnly() +isVolatile() «interface» CPU +getNumberOfProcessors() «interface» NetworkConnection +isConnected() «interface» Power +isBatteryPowered() «interface» PointingDevice +getNumberOfButtons() «interface» Storage +isRemovable() Figuur 3.2: De hiërarchie van de resources Deze paragraaf geeft een algemeen overzicht van de API, voor een gedetailleerde en uitgebreide specificatie verwijzen we u graag door naar de javadoc-pagina’s te vinden op de CD-ROM. Resource definieert enkele standaard methoden zoals • public String getName() 3.1 Resources API 24 • public String getVendor() • public String getVersion() • public String getProperty(String key) • public Properties getProperties() StaticResource Sommige resources zijn enkel statisch en wijzigen niet of nauwelijks over de tijd. Er valt te denken aan de eigenschappen van een monitor of invoerapparaat zoals een toetsenbord. Daartoe is de interface StaticResource, die Resource uitbreidt, gedefinieerd. Enkele belangrijke resources zijn standaard voorzien: • Display • Keyboard • PointingDevice De interface StaticResource biedt geen extra algemene methoden aan en fungeert als een tagging-interface. Dit wil zeggen dat deze alle statische resources onder zich groepeert, dit om een duidelijk onderscheid te kunnen maken met de dynamische resources. DynamicResource Andere resources hebben een veel meer dynamisch karakter en zeer snel wijzigende eigenschappen en hebben een bepaald gebruik. Om hieraan tegemoet te komen is de interface DynamicResource gedefinieerd. Zo’n dynamische resource zou bv. een processor of geheugen kunnen representeren. De eigenschappen die typisch belangrijk zijn bij zo’n resource zijn de mogelijke capaciteit en het huidige gebruik ervan. Het is dus belangrijk dat deze eigenschappen op een eenvoudige en uniforme manier kunnen opgevraagd worden via een vaste set methoden: • public long getAvailableCapacity() • public long getTotalCapacity() • public double getUsage() 3.1 Resources API 25 getAvailableCapacity() en getTotalCapacity() geven de absolute waarden terug, dit waar getUsage() een procentuele weergave van het resourcegebruik teruggeeft. De methoden die de capaciteit opvragen geven een long terug. Dit werd zo gekozen omdat bijvoorbeeld de capaciteit van de meeste moderne harde schijven in bytes al lang niet meer kan voorgesteld worden met een 32-bits int. De maximaal voorstelbare waarde is met een int maar 2 GiB. Uiteraard is het belangrijk om een aantal veel voorkomende dynamische resources standaard te voorzien. De meest voor de hand liggende zijn natuurlijk de processor en het geheugen. Aangezien deze API zich vooral toespitst op draagbare devices is ook de capaciteit van de batterij en de eventuele netwerkverbinding belangrijk. De standaard voorziene interfaces zijn: • CPU • Memory – Storage • NetworkConnection • Power Zoals te zien in deze opsomming heeft Memory ook nog een subinterface Storage. Deze extra interface is toegevoegd om een onderscheid te kunnen maken tussen de verschillende types intern en extern geheugen zoals daar zijn RAM, ROM, harde schijven . . . Op de meeste platformen komt Memory overeen met volatiel intern geheugen en Storage met niet-volatiel, al dan niet extern, geheugen. Het onderscheid is echter niet altijd zo eenvoudig. Zo bestaan er PDA’s waarbij het RAM-geheugen bewaard blijft bij het uitschakelen en enkel gewist wordt door een harde reset. In zo’n situaties is het aan de fabrikant om te beslissen of het geheugen onder Memory of Storage moet geplaatst worden. 3.1.2 Factorymodel SystemResourceFactory De hiërarchie van resources is opgebouwd uit interfaces, dit opdat de gebruiker niet afhankelijk zou worden van één bepaalde implementatie. De eigenlijke implementatie van die interfaces is 3.1 Resources API 26 bovendien verborgen voor de gebruikers. Het moet uiteraard wel mogelijk zijn om instanties van deze klassen te creëren. Het model dat dit alles toelaat is de Factory. De klasse SystemResourceFactory representeert de Factory van deze API (figuur 3.3). Deze klasse laat toe instanties te creëren van klassen die de resource-interfaces implementeren. Daartoe zijn er een aantal statische methoden voorzien zoals • public static Memory getRAM() • public static CPU getCPU() • public static Keyboard getKeyboard() • ... Meer details zijn terug te vinden in de javadoc-pagina’s. SystemResourceFactory +getCPU() : CPU +getDisplay() : Display +getKeyboard() : Keyboard +getNetworkConnection(in name : String) : NetworkConnection +getNetworkConnectionNames() : String[] +getPointingDevice() : PointingDevice +getPower() : Power +getRAM() : Memory +getROM() : Storage +getStorage(in name : String) : Storage +getStorageNames() : String[] +getVirtualMachineMemory() : Memory Figuur 3.3: De factoryklasse SystemResourceFactory Via deze methoden kan de gebruiker alle courante resources op een eenvoudige en gecentraliseerde manier opvragen. Excepties Omdat er soms weleens dingen kunnen mislopen worden er enkele excepties gedefinieerd. Ze zijn afgeleid van een gemeenschappelijke klasse ResourceException. Wanneer een resource opgevraagd wordt die niet bestaat op het apparaat in kwestie, wordt er een ResourceNotFoundException opgeworpen. Dat kan bijvoorbeeld gebeuren wanneer de methode getDisplay() 3.2 Monitoring API 27 aangeroepen wordt op een apparaat zonder scherm. Een andere mogelijkheid is dat de gevraagde resource misschien wel bestaat, maar om de ene of de andere reden niet ondersteund wordt door de implementatie van de API. In dat geval wordt een ResourceNotSupportedException opgeworpen. 3.2 Monitoring API Het toegankelijk maken van resources is één zaak, maar dat alleen is niet voldoende. Er is nog een model nodig dat toelaat dat je automatisch op de hoogte gebracht wordt van wijzigingen in de resources. Bijvoorbeeld een toenemend of afnemend gebruik, of gewoon het beschikbaar worden van een resource. Dit is waar de monitor komt bij kijken. Een monitor is een klasse die een bepaalde resource zal observeren. Het is dus niet verwonderlijk dat een monitor een Resource als argument heeft bij de constructor. Aan de monitor kan dan gevraagd worden of er een wijziging is opgetreden bij de resource. Als deze wijziging belangrijk genoeg is zal een event gegenereerd worden. Om dit alles te automatiseren is er een service-interface gedefinieerd. Deze interface en de monitorklassen vormen samen het package resources.util.monitor. 3.2.1 Resourcemonitors Er zijn 3 belangrijke types basisresources nl. Resource, StaticResource en DynamicResource. Aangezien StaticResource eigenlijk enkel fungeert als tagging-interface zijn er maar 2 belangrijke types monitors nl. ResourceMonitor en DynamicResourceMonitor. Het zou de dingen enkel maar nutteloos complexer maken om ook een StaticResourceMonitor te creëren aangezien deze geen extra voordelen of restricties met zich meebrengt. Strikt genomen zou het niet nodig zijn om een onderscheid te maken tussen deze monitors, maar het wel doen brengt enkele interessante voordelen met zich mee. De gewone resourcemonitor is immers vooral bedoeld om de statische eigenschappen van resources te monitoren. Dit betekent eventuele wijzigingen in de properties, bv. de resolutie van een display. Het zijn eigenschappen die kunnen veranderen maar dit niet vaak doen. Het is belangrijk op te merken dat elke resource, ook een dynamische, altijd een aantal statische eigenschappen zal hebben. De dynamische monitor daarentegen is gericht op het observeren van zeer snel wijzigende resources. Doorgaans heeft het melden van wijzigingen van deze resources een veel hogere prioriteit. Dankzij het 3.2 Monitoring API 28 onderscheid wordt het dan ook een stuk eenvoudiger om met die verschillende noden om te gaan. Het voorkomt ook dat men de dynamische eigenschappen van een statische resource zou proberen te monitoren. Een dynamische resourcemonitor is uiteraard ook gewoon een resourcemonitor en dus wordt DynamicResourceMonitor afgeleid van ResourceMonitor. Er zijn standaard een aantal alge- mene monitors voorzien, zoals te zien in figuur 3.4. ResourceMonitor «interface» MonitorService +addMonitor(in monitor : ResourceMonitor) +removeMonitor(in monitor : ResourceMonitor) * +minimumInterval +priority #resource +monitor() * * «interface» ResourceListener +eventOccurred(in event : ResourceEvent) «implementation class» MonitorServiceImplementation ResourceEvent +source +message +data 1 PropertyChangeMonitor DynamicResourceMonitor +property UsageThresholdMonitor CapacityThresholdMonitor UsageChangeMonitor CapacityChangeMonitor +threshold +threshold +minChange +minChange Figuur 3.4: De hiërarchie van de monitors ResourceMonitor en DynamicResourceMonitor zijn 2 abstracte klassen. Ze kunnen dus niet geı̈nstantieerd worden. De implementatie dient door de gebruiker vervolledigd te worden. Er is één abstracte methode: public abstract void monitor(). Dit is de methode die moet opgeroepen worden om na te gaan of er wijzigingen in de resource zijn of niet. Dit heeft als doel de monitors zo generiek, maar vooral zo veelzijdig mogelijk te maken. Deze werkwijze laat de gebruiker immers toe om zelf te definiëren wat als wijziging wordt beschouwd. Voor de ene toepassing zijn wijzigingen in het gebruik van minder dan 15 % misschien niet belangrijk, waar voor anderen een wijziging van 1 % al belangrijk is. Ook wat betreft de efficiëntie is dit een belangrijke eigenschap. Er moet immers enkel een event gegenereerd worden wanneer dit echt nodig is. Concreet wordt er dus binnen de monitor()-methode beslist wanneer de fireEvent()-methode wordt aangeroepen. Deze aanpak is zeer gelijkaardig aan het implemen- teren van de service() methode van een servlet in een afgeleide klasse van de abstracte klasse 3.2 Monitoring API 29 GenericServlet, of van de doGet() methode in een HttpServlet 3 . Events en listeners Het monitoren is een event-gebaseerd systeem. Het is dus nodig om een event en een listener te voorzien. Deze zijn terug te vinden onder de namen ResourceEvent en ResourceListener. Een resource-event bevat een verwijzing naar de event-genererende klasse en kan ook voorzien worden van een extra boodschap of waarde, zoals het gebruik op dat moment. De bijhorende listener-interface voorziet een methode public void eventOccurred(ResourceEvent e) die zal aangeroepen worden bij een event. Voorgedefinieerde monitors Om tegemoet te komen aan de gebruikers zijn er een aantal veelgebruikte en belangrijke monitors voorzien. UsageChangeMonitor en CapacityChangeMonitor zullen bij elke voldoende grote wijziging een event genereren. Wat als voldoende grote wijziging beschouwd wordt, is een attribuut dat kan ingesteld worden. UsageThresholdMonitor en CapacityThresholdMonitor zullen een event genereren wan- neer een bepaalde drempelwaarde van het gebruik overschreden wordt. Ook hier is de drempel een aanpasbaar attribuut. PropertyChangeMonitor zal een event genereren wanneer een resource-property wijzigt. Zoals blijkt uit de opsomming zijn er twee types Change- en ThresholdMonitors. Het enige verschil is dat de UsageMonitor een procentuele waarde zal observeren, en de CapacityMonitor de absolute waarde. Deze laatste laat dus toe om heel nauwkeurig en zonder eventuele afrondingsfouten te werken. Maar deze vraagt dan ook iets meer accounting van de gebruiker zelf. 3.2.2 Monitorservice Het is uiteraard niet handig om telkens zelf de monitor()-methode aan te roepen. Daarom werd er een interface MonitorService gecreëerd. Een implementatie van deze service zal op 3 Meer over servlets op http://java.sun.com/products/servlet/docs.html 3.2 Monitoring API 30 geregelde tijdstippen de monitor() methode aanroepen. Monitors kunnen toegevoegd en verwijderd worden. Van zodra een monitor toegevoegd wordt begint het monitorproces, tot hij weer verwijderd wordt. Er kan ook een prioriteit ingesteld worden opdat, indien dat nodig zou blijken, belangrijkere monitors voorrang zouden krijgen op de rest. Om een vloed aan gegenereerde events te voorkomen kan bij een monitor een tijdsinterval ingesteld worden waarbinnen maximaal één event kan gegenereerd worden. Dit resultaat wordt verkregen door de service de monitor()-methode slechts één keer per interval te laten aanroepen. Eventuele wijzigingen in de resource die weer ongedaan gemaakt werden door tegenovergestelde wijzigingen binnen dit interval worden dus niet gedetecteerd, en bijgevolg wordt er ook geen event gegenereerd. Het belangrijkste voordeel aan deze manier van werken is dat eventuele events binnen het interval niet hoeven gebufferd te worden, en er ook geen events gegenereerd worden die op het moment van ontvangst vals lijken te zijn. Een aandachtspunt voor de programmeur: het is belangrijk te weten dat de voorgedefinieerde monitorklassen geen voorzieningen bieden om randgevallen op te vangen. Hiermee wordt bedoeld dat wanneer een gemonitorde eigenschap schommelt rond de drempel van een thresholdmonitor er voortdurend events zullen gegenereerd worden, terwijl dat misschien niet gewenst is. Zo kan een thresholdmonitor, die een event genereert telkens het geheugengebruik de grens van 90 % overschrijdt, er toe leiden dat een maximaal aantal events gegenereerd zal worden indien het geheugengebruik ook effectief rond de 90 % schommelt. Dit kan belangrijke gevolgen hebben indien de listener van deze monitor reken- of geheugenintensieve operaties zou starten. Er moet immers voor gezorgd worden dat het afhandelen van events in de listener een kortstondige operatie is, opdat de monitor niet geblokkeerd raakt. Om randsituaties zoals dit drempelvoorbeeld te voorkomen moeten de nodige voorzieningen genomen worden in de listener, of reeds in de monitor()-methode van de desbetreffende monitor. Dit kan door overerving van een bestaande monitor en het overriden van de methode, of door een eigen aangepaste monitor te schrijven die geen overvloed van events genereert bij het schommelen rond een drempelwaarde. 3.3 Gebruik van de API 31 Waarom monitors? Men zou zich kunnen afvragen waarom de monitorklassen nodig zijn. Hetzelfde zou bereikt kunnen worden met enkel een monitorservice en listeners. De monitorservice zou dan voortdurend events genereren bij iedere verandering van een resource en de listeners vangen al deze events op. In dit scenario moeten de listeners dus zelf maar beslissen of een event voor hen belangrijk is of niet. Het nadeel hiervan is dus dat de monitorlogica (beslissen welke events belangrijk zijn) terechtkomt tussen de actielogica die zegt wat er moet gebeuren als er daadwerkelijk op een belangrijk event moet gereageerd worden. Onze oplossing hiervoor is om de logica die beslist of een event belangrijk is in een aparte monitorklasse onder te brengen. In de listeners staat nu enkel wat echt belangrijk is, nl. wat er moet gebeuren als reactie op een event. Wanneer de listener een event ontvangt van een monitor, weet die immers al dat het om een belangrijke wijziging gaat die om reactie vraagt, omdat dit zo beslist werd door de monitor. Naast het feit dat dit overzichtelijker is, heeft dit nog enkele voordelen. Er worden enkel events gegenereerd wanneer het echt nodig is. Hierdoor wordt de voortdurende creatie van kort levende eventobjecten wat beperkt, wat de efficiëntie ten goede komt. Voor de programmeur is het ook voordelig, want voor veel voorkomende soorten monitoring moet hij niet eens meer de monitorlogica schrijven. Die zit al in de voorgedefinieerde monitorklassen. Ten slotte kan ook opgemerkt worden dat dit monitormodel algemeen genoeg is om ook voor andere doeleinden dan hardwaremonitoring gebruikt te worden. Het monitoren bestaat erin op geregelde tijdstippen de methode monitor() te laten aanroepen. Wat in die methode staat kan vrij gekozen worden en hoeft dus in principe niks te maken te hebben met resources. Uiteraard is het monitoren van hardwareresources wel het hoofddoel. 3.3 Gebruik van de API Er zijn twee belangrijke toepassingen van de API. Hij kan gewoon gebruikt worden als een pure resource monitor die eventueel kan gebruikt worden om een waarschuwing te geven bij het overschrijden van een drempelwaarde of om aan load balancing te doen. Een andere toepassing, die veel krachtiger is, is adaptieve software. De software past zichzelf aan zijn omgeving aan zonder expliciete menselijke interventie. Men kan dit op twee manieren interpreteren. Ofwel 3.3 Gebruik van de API 32 blijft de software op dezelfde plaats en verandert de omgeving, ofwel verplaatst de software zich waardoor uiteraard ook de omgeving zal veranderen. Men kan vele toepassingen bedenken van adaptieve software. Een voorbeeld is een browser die alleen de tekst downloadt maar niet alle multimediale toeters en bellen, als er te weinig bandbreedte voorhanden is. Een programma dat zijn grafische interface zelf aanpast aan de beschikbare schermruimte is een ander voorbeeld. In het ideale geval zou een zelfde programma dan kunnen draaien op een 21 inch scherm, maar ook op het kleine schermpje van een GSM. Verdere toepassingen kunnen zijn: een programma dat zware berekeningen en grafisch geweld achterwege laat wanneer de batterij bijna leeg is, videostromen die overschakelen naar lagere resolutie of framerate bij kleinere bandbreedte . . . De volgende twee delen tonen aan de hand van enkele voorbeeldjes hoe de API gebruikt kan worden. 3.3.1 Resources API Met dit onderdeel van de API kan een applicatie nodige informatie over de hardwareomgeving te weten komen. Dit zal vooral gebruikt kunnen worden in programma’s die verplaatst worden naar een ander apparaat. Op basis van de opgevraagde informatie kan een programma zichzelf op de ene of de andere manier configureren om optimaal te draaien gegeven de beschikbare resources. Het toegangspunt tot de resources is de klasse SystemResourceFactory. De statische methoden van deze klasse geven objecten terug die de resources voorstellen. Dit voorbeeld toont hoe we het geheugen beschikbaar tot de JVM kunnen opvragen. Memory jvmMem = SystemResourceFactory.getVirtualMachineMemory(); Indien een bepaalde resource niet beschikbaar is op het gebruikte platform, wordt bij de creatie van die resource in de SystemResourceFactory een ResourceNotFoundException opgeworpen. De methoden getCPU(), getPower(), getRAM() en getVirtualMachineMemory() doen dit echter niet aangezien deze minimumvereisten zijn voor het CDC/Foundation Profile. Een typische situatie waar een exception zal opgeworpen worden is een apparaat zonder scherm. Wanneer het voor de implementatie onmogelijk is om de gevraagde informatie te verschaffen, wordt er een ResourceNotSupportedException opgeworpen. Het kan zijn dat die resource wel bestaat, maar het is onmogelijk er iets over te weten te komen. 3.3 Gebruik van de API 33 try { Display display = SystemResourceFactory.getDisplay(); ... } catch (ResourceNotFoundException e) { System.err.println("This device has no display."); } catch (ResourceNotSupportedException e) { System.err.println("This device may or may not have a display, but we cannot get information about the display."); } Wanneer er geen exception opgeworpen is, heeft het teruggegeven object verschillende methoden die toegang geven tot informatie over de resource in kwestie. De waarden teruggegeven door deze methoden zijn altijd de meest recente. Het resource-object moet dus maar één keer opgevraagd worden bij de SystemResourceFactory en kan voor de rest van het programma gebruikt worden. De totale capaciteit en het aantal bytes in het geheugen dat nog beschikbaar is tot de virtuele machine, kan eenvoudig opgevraagd worden. Memory jvmMem = SystemResourceFactory.getVirtualMachineMemory(); long total = jvmMem.getTotalCapacity(); long available = jvmMem.getAvailableCapacity(); Alle resource types hebben ook (quasi-)statische eigenschappen die kunnen opgevraagd worden. Ze kunnen allemaal tegelijk verkregen worden met de methode getProperties(). Deze geeft een object terug dat alle eigenschappen bevat. Properties properties = jvmMem.getProperties(); String[] keys = properties.getKeys(); for (int i = 0; i < keys.length; i++) { String key = keys[i]; System.out.println(key + ": " + properties.getProperty(key)); } Deze klasse Properties heet voluit resources.util.Properties en is niet de java.util.Properties uit de standaard bibliotheek van Java. Deze laatste bevat te veel methoden die in deze context niet nodig zijn. Daarom is er een aparte en zeer compacte Properties-klasse voorzien. 3.3 Gebruik van de API 34 Eén enkele eigenschap kan ook opgevraagd worden met de methode getProperty(String key). Memory rom; try { rom = SystemResourceFactory.getROM(); String readOnly = rom.getProperty("readOnly"); if (readOnly.equals("true")) { System.out.println("ROM is read-only"); } } catch (ResourceNotFoundException e) { } catch (ResourceNotSupportedException e) { } 3.3.2 Monitoring API Dit deel van de API biedt een raamwerk om resources te monitoren. De werkwijze om een resource te monitoren bestaat uit drie stappen die geı̈llustreerd worden in figuur 3.5: 1. Maak een instantie van een klasse die afgeleid is van de abstracte klasse ResourceMonitor. 2. Voeg listeners toe aan deze monitor. 3. Voeg deze monitor toe aan de service MonitorService die ervoor zorgt dat het monitoren begint. Een monitor aanmaken Een monitor bevat alle gegevens om een resource te monitoren. Alle monitors zijn afgeleiden van de abstracte klasse ResourceMonitor of van een abstracte subklasse DynamicResourceMonitor. Ze zijn abstract omdat één methode nog niet geı̈mplementeerd is, nl. monitor(). In deze methode moet geschreven worden wat er zal gebeuren tijdens één monitorcyclus. De programmeur heeft twee methoden ter beschikking die gebruikt moeten worden in de methode monitor: een methode om toegang te krijgen tot de resource die gemonitord moet worden, getResource() (of getDynamicResource() voor dynamische resources), en een methode die 3.3 Gebruik van de API 35 userprogram :MonitorService new monitor:ResourceMonitor 1. new 2. aListener:ResourceListener addListener(aListener) addMonitor(monitor) 3. loop if monitor() eventOccurred() Figuur 3.5: Gebruik van het monitorgedeelte van de API moet aangeroepen worden wanneer er reden is om een event te genereren, fireEvent(). Dit alles maakt het mogelijk om zelf monitors te definiëren en dit voor alle mogelijke resourcetypes. Het volgende voorbeeld toont een implementatie van een door de gebruiker gemaakte monitor. Deze eenvoudige monitor vuurt events af wanneer het gebruik van de dynamische resource in een bepaald interval komt of dit interval verlaat. import resources.DynamicResource; import resources.util.monitor.DynamicResourceMonitor; public class IntervalMonitor extends DynamicResourceMonitor { private double from, to; 3.3 Gebruik van de API 36 private double previous; public IntervalMonitor(DynamicResource res, double from, double to) { super(res); this.from = from; this.to = to; previous = res.getUsage(); } public void monitor() { double current = getDynamicResource().getUsage(); if ((current >= from && current <= to) && (previous < from || previous > to)) { fireEvent("in", new Double(current)); } else if ((previous >= from && previous <= to) && (current < from || current > to)) { fireEvent("out", new Double(current)); } previous = current; } } In de plaats van deze klassen zelf te schrijven kan een programmeur het zichzelf ook gemakkelijk maken door klassen te gebruiken die de monitor-methode al implementeren en die voorzien zijn in de API. Een voorbeeld hiervan is UsageThresholdMonitor, een monitor die events stuurt wanneer het gebruik van een dynamische resource een bepaalde drempelwaarde overschrijdt. Het volgende voorbeeld maakt zo’n monitor aan. Hij zal het virtuele-machinegeheugen monitoren en events genereren wanneer het geheugengebruik 90 % overschrijdt. Memory jvmMem = SystemResourceFactory.getVirtualMachineMemory(); UsageThresholdMonitor monitor = new UsageThresholdMonitor(jvmMem, 90); De monitors hebben nog andere attributen zoals de prioriteit en het minimum interval tussen twee monitorcycli. Al deze attributen hebben een defaultwaarde die, indien gewenst, expliciet kan gewijzigd worden. Voor meer informatie hierover wordt verwezen naar de API specificatie op de cd-rom. Indien deze defaultwaarden voldoen, is bovenstaande alles wat nodig is om een 3.3 Gebruik van de API 37 monitor te creëren. Listeners toevoegen aan de monitor Listeners zijn klassen die de events ontvangen die gegenereerd worden door een monitor en een actie ondernemen wanneer ze een event ontvangen. Ze moeten de interface ResourceListener implementeren. Dit bestaat erin dat de listenerklasse een publieke methode eventOccurred(ResourceEvent) moet hebben waarin staat wat er moet gebeuren bij het ontvangen van een event. Dit is de plaats waar de actie zich afspeelt. Het is hier dat software adaptief kan gemaakt worden door gepast te reageren op wijzigingen in de omgeving. Aansluitend bij het vorige voorbeeld maken we een listener die telkens wanneer hij een event ontvangt gewoon een boodschap op het scherm toont. ResourceListener listener = new ResourceListener() { public void eventOccurred(ResourceEvent e) { System.out.println("Threshold crossed."); System.out.println("Threshold is " + ((UsageThresholdMonitor)e. getMonitor()).getThreshold()); System.out.println("Current value is " + (Double)e.getData()); System.out.println("Message is " + e.getMessage()); } }; monitor.addListener(listener); Merk op dat een event drie gegevens bevat: de monitor die het event verstuurd heeft, een boodschap en een Object, typisch een Double of Integer, dat wat extra informatie kan meegeven, zoals de gemeten waarde die het event veroorzaakte. De monitor toevoegen aan de service Om te beginnen monitoren moet de aangemaakte monitor alleen nog toegevoegd worden aan de service MonitorService. In een OSGi-omgeving zal dit daadwerkelijk een service zijn die MonitorService als interface heeft. De service zal volledig het monitoren verzorgen door herhaaldelijk de monitor-methode aan te roepen en zal het beheer van alle monitors op zich nemen. We voegen de monitor uit het voorbeeld toe aan de service. 3.3 Gebruik van de API 38 //In OSGi bundel eerst het service-object opvragen via de BundleContext ctxt ServiceReference sref = ctxt.getServiceReference("resources.util.monitor. MonitorService"); MonitorService service = (MonitorService) ctxt.getService(sref); service.addMonitor(monitor); In een gewone J2ME of J2SE omgeving, zonder het OSGi-framework, kan de service als volgt verkregen worden. MonitorService service = MonitorServiceFactory.getMonitorService(); service.addMonitor(monitor); Hier eindigt het werk van de programmeur. Het monitoren is gestart, events zullen worden verstuurd en het programma zal acties ondernemen bij het ontvangen van events. Het enige wat nog moet gebeuren is de monitors terug verwijderen uit de service wanneer ze niet meer nodig zijn. Daarvoor bestaat er een methode removeMonitor(). 3.4 Besluit 3.4 39 Besluit De gespecificeerde API maakt het mogelijk toegang te krijgen tot alle courante systeem resources in Java, meer bepaald in een OSGi-omgeving. Bovendien biedt het een framework aan om aan resource monitoring te doen. De API is uitbreidbaar want nieuwe resources kunnen afgeleid worden van generieke resource-interfaces. Ook kan de gebruiker zelf nieuwe monitors aanmaken door af te leiden van abstracte monitorklassen. Deze API maakt het mogelijk om op een relatief eenvoudige en platformonafhankelijke wijze adaptieve software te ontwikkelen. DE IMPLEMENTATIE 40 Hoofdstuk 4 De implementatie Na het opstellen van de API dient deze uiteraard geı̈mplementeerd te worden. Een geslaagde implementatie zal aan alle eisen van de specificatie voldoen en ook geen beperkingen opleggen die niet expliciet in de specificatie opgenomen zijn. Deze specifieke implementatie zal zich richten tot het platform TabletPC + WindowsXP Tablet Edition [17]. Wegens de sterke gelijkenis van het doelplatform en de gemiddelde hedendaagse PC, zal deze implementatie zonder aanpassingen, ook werken op een gelijkaardig desktopplatform. Een belangrijk aandachtspunt is, dat de implementatie zonder noemenswaardige problemen moet kunnen uitgebreid worden om meerdere doelplatformen te ondersteunen. In een eerste paragraaf zal op de algemene structuur van de implementatie van de resources ingegaan worden, en later zal aan de hand van een voorbeeld, meer gedetailleerd, de concrete implementatie van een resource besproken worden. Naast de eigenlijke resources definieert de API ook nog een monitor-framework. Uiteraard dient ook dit geı̈mplementeerd te worden. Dit deel van de API zal geen native code bevatten, en is bijgevolg zonder enig probleem overdraagbaar naar andere platformen. 4.1 Algemene Structuur De structuur van de implementatie is reeds voor een groot deel door de specificatie bepaald, maar er blijft nog een zekere vrijheid over. De mogelijke keuzes en hun motivatie komen hier aan bod. 4.1 Algemene Structuur 4.1.1 41 Scheiding tussen specificatie en implementatie De specificatie definieert hoe de publieke packages en klassen zich zullen gedragen. Dit is een stabiel geheel dat niet meer kan en mag wijzigen. Het is daarom aangewezen om er ook tijdens de implementatie voor te zorgen dat dit publieke deel stabiel blijft en niet, of zo weinig mogelijk dient gewijzigd te worden. Dit wordt zeker belangrijker naarmate men meerdere platformen wil ondersteunen met één enkele implementatie. Uiteraard kunnen eventuele toekomstige releases van deze specificatie wel nog wijzigingen of uitbreidingen toevoegen. Verschillende implementaties moeten zich echter houden aan de specificaties van de API. Het is daarom ook wenselijk dat dit in de code naar voor komt. Aangezien de specificatie voor het overgrote deel uit interfaces bestaat, en wegens het gebruikte factory-designpattern lijkt een gescheiden structuur zelfs vanzelfsprekend. Er is voor gekozen om alle implementatie-specifieke klassen in een aparte package-hiërarchie te plaatsen. De publieke interfaces en klassen, zoals aangeduid in de specificatie zijn bevat in de resources.* hiërarchie en de toegevoegde implementatie-specifieke klassen zijn in de be.ugent.ibcn.* hiërarchie1 bevat. Dit laat een optimale scheiding tussen implementatie en specificatie toe. Het resultaat van deze extra gelaagdheid is dat met minimale moeite de bestaande implementatie kan vervangen worden door een andere. Bovendien voorkomt het ook een aantal praktische problemen ivm. de naamgeving van klassen. Het is immers niet nodig om voor elke resource een nieuwe naam te gaan verzinnen omdat deze al zou ingenomen zijn door de interface. Dezelfde techniek wordt bv. ook toegepast door Sun in de implementatie van de eigen Java Runtime en heeft reeds zijn degelijkheid bewezen. Naast de scheiding tussen de specificatie en de implementatie is er ook nog de scheiding tussen het platformonafhankelijke en het platformafhankelijke deel van de code. Zoals weergegeven in figuur 4.1. Ook hier is weer een extra verdeling in lagen doorgevoerd wat toelaat om de implementatie zo algemeen mogelijk te houden, zoals later zal blijken. 4.1.2 Platformonafhankelijke code Alle Java-code van de implementatie is platformonafhankelijk. Niet omdat het om Java-code gaat, maar omdat in deze code geen veronderstellingen gemaakt zijn over het onderliggende 1 Het is in Java de gewoonte om eigen packages te laten beginnen met de omgekeerde domeinnaam van de producent. De domeinnaam van de vakgroep leek mij hiervoor een goede keuze. 4.2 Implementatie van een Resource: RAM 42 Specificatie Implementatie platformonafhankelijk Implementatie platformafhankelijk Figuur 4.1: De verschillende lagen in de implementatie. platform. In het package be.ugent.ibcn.resources is voor elke resource die de SystemResourceFactory kan teruggeven een implementatie voorzien. Dit houdt in dat er een klasse bestaat die de nodige interfaces implementeert en in de nodige native calls voorziet. 4.1.3 Platformafhankelijke code De platformafhankelijke code bestaat uit een aantal shared libraries die in de nodige methoden voorzien om de informatie, niet rechtstreeks beschikbaar in Java, aan te leveren. Het is belangrijk dat ook op dit native niveau de mogelijkheid geboden wordt om te bepalen of een bepaalde resource ondersteund wordt of niet. Stel dat dit reeds in de Java-code beslist is, dan wordt de portabiliteit van de code beperkt. Het is immers perfect mogelijk dat op een bepaald platform bepaalde informatie wel op te vragen is en dat dit op een ander platform niet mogelijk is. Bv. het opvragen van het cpu-gebruik op TabletPC en PocketPC. Het ondersteunen van meerdere platformen wordt op deze manier gewoon een kwestie van voorzien in de nodige native libraries. 4.2 Implementatie van een Resource: RAM In de vorige paragraaf werd een algemene uiteenzetting gegeven over hoe de implementatie gestructureerd is. Hier volgt nu een meer gedetailleerde uitwerking voor één resource. Het doel hiervan is een duidelijk beeld te scheppen van de implementatie, het rechtstreekse verband met de interfaces uit de specificatie en met de eigenlijke hardware. 4.2 Implementatie van een Resource: RAM 43 In de API zijn een aantal resources gespecificeerd zoals de processor, het ramgeheugen, de opslagmogelijkheden enz. In het eenvoudigste geval is er een één op één relatie tussen enerzijds de hardware en de in de API gedefiniëerde resources en anderzijds de in de API gedefiniëerde resources en de implementatieklassen. Een voorbeeld hiervan is de muis, waarvan er doorgaans één op het apparaat beschikbaar is. Zie figuur 4.2a. Een meer complex geval hebben we bij het geheugen. Er zijn immers meerdere types van geheugen zoals het RAM en ROM, maar ook de vaste schijven, schijfstations met verwisselbare schijven, memory-disks enz. Dit komt ook tot uiting in de hiërarchie van interfaces waar Storage de interface Memory uitbreidt. Storage wordt op zijn beurt dan weer gebruikt om zowel het ROM-geheugen als de vaste schrijven voor te stellen. Maar beiden vereisen een verschillende implementatie. Zie figuur 4.2b. ROM RAM vaste schijf interface Memory muis interface Storage interface PointingDevice implementatie RAM implementatie ROM implementatie PointingDevice (a) implementatie schijf (b) Figuur 4.2: (a) Het eenvoudigste geval met een één op één relatie, (b) Een meer complexe structuur van geheugens en storage-devices. Concreet komt het er op neer dat de vereenvoudiging door de specificatie van de interfaces niet 4.2 Implementatie van een Resource: RAM 44 kan doorgetrokken worden op het niveau van de implementatie. Maar dit is helemaal niet erg. Conceptueel zullen we deze verschillende resources immers net op dezelfde manier gebruiken. Indien we de vrije ruimte op een storage-device wensen te kennen, willen we dit device op dezelfde manier kunnen benaderen onafhankelijk van het specifieke type van het device. Het zijn immers enkel de interfaces die van nut zijn voor de eindgebruiker van de API. Deze zijn net zo gedefinieerd zodat er geen onderscheid dient gemaakt te worden. Om de gedachten te vestigen zal in wat volgt de implementatie van het RAM-geheugen overlopen en aangevuld worden met de nodige annotaties om het geheel te verduidelijken. Een overzicht van alle implementatieklassen is te vinden in bijlage D. 4.2.1 Platformonafhankelijke code Een instantie die het RAM-geheugen representeert kan opgevraagd worden met de methodeoproep getRAM() uit de SystemResourceFactory. Deze methode heeft als return-waarde een object van het type Memory. De klasse RAMMemory implementeert de methoden gedefinieerd door de interface Memory, en indien mogelijk worden deze direct gemapt naar native calls. De implementatie ziet er dan als volgt uit: package be.ugent.ibcn.resources; import resources.ResourceNotSupportedException; public class RAMMemory extends DynamicResource implements resources.Memory { static { System.loadLibrary("rammemory"); } Eerst en vooral komen de package-definitie en de klasse-definitie. De klasse RAMMemory breidt een abstracte klasse DynamicResource uit en implementeert de interface Memory. Analoog aan de interfaces-hiërarchie in de specificatie is er een abstracte klasse Resource, StaticResource en DynamicResource voorzien. Dit laat toe om een aantal generieke methoden, gelijk voor alle interfaces op één enkele plaats te implementeren. De native library waarvan gebruik gemaakt wordt door deze klasse heet rammemory.dll en moet geladen worden via het bovenstaande static-blok.2 2 Het is niet nodig om voor- of achtervoegsels mee te geven, de Java Virtual Machine zorgt hier zelf voor 4.2 Implementatie van een Resource: RAM 45 public RAMMemory() throws ResourceNotSupportedException { super(); if (!isSupported()) { throw new ResourceNotSupportedException("RAMMemory is not supported on this platform."); } setName("RAMMemory"); } public native boolean isSupported(); Vervolgens komt de constructor, deze kan een ResourceNotSupportedException opwerpen indien het platform waar de code wordt op uitgevoerd niet ondersteund wordt. Om te beslissen of deze Exception dient opgeworpen te worden wordt gebruikt gemaakt van de native methode isSupported(). Zoals reeds aangegeven in punt 4.1.3 is het belangrijk dat deze beslissing op het native niveau genomen wordt om de porteerbaarheid van de code te kunnen garanderen. Tenslotte volgen alle methoden gedefinieerd door de geı̈mplementeerde interfaces: public native long getAvailableCapacity(); public native long getTotalCapacity(); public native double getUsage(); public native boolean isReadOnly(); public native boolean isVolatile(); } Ook hier is het belangrijk dat zo veel mogelijk informatie op het native niveau opgevraagd wordt. Indien men meer dan één platform wil ondersteunen, wordt dit beperkt tot het toevoegen van de nodige bibliotheken voor dat platform. De verleiding is immers groot om een methode als isVolatile() steeds true te laten teruggeven, reeds in de Java-code. Maar er bestaan pda’s waar dit niet altijd het geval is. 4.2.2 Platformafhankelijke code De native methoden uit de Java-klasse moeten vervolgens ook geprogrammeerd worden. Typisch gebeurt dit in C of C++, maar theoretisch kan het ook in andere programmeertalen. Zelf heb naargelang het gebruikte platform. bv. *.dll voor Windows en lib*.so voor Unix. 4.2 Implementatie van een Resource: RAM 46 ik voor C gekozen. De broncode ziet er dan als volgt uit: #include <windows.h> #include "be_ugent_ibcn_resources_RAMMemory.h" Eerst volgen de vereiste include-statements. Er is de header-file windows.h, nodig om informatie over het geheugen op te vragen. En ook de header-file be ugent ibcn resources RAMMemory.h die de functie-definities van de native methoden beschrijft. Deze header-file kan automatisch gegenereerd worden met behulp van de executable ’javah’. Een complete bespreking en tutorial over de werking en het gebruik van JNI is terug te vinden op de site van Sun [15]. Wat volgt in de source-file is een uitwerking van alle methodes: JNIEXPORT jboolean JNICALL Java_be_ugent_ibcn_resources_RAMMemory_isSupported (JNIEnv * env, jobject obj) { jboolean result = 1; return result; } Er is de methode die aangeeft of een resource ondersteund is op het platform. Deze doet niets meer dan het teruggeven van true of false. Het RAM is ondersteund op het doelplatform en dus wordt er true teruggegeven. JNIEXPORT jlong JNICALL Java_be_ugent_ibcn_resources_RAMMemory_getTotalCapacity (JNIEnv * env, jobject o) { MEMORYSTATUS memstat; GlobalMemoryStatus(&memstat); return memstat.dwTotalPhys; } Hetzelfde gebeurt voor alle andere methoden gedefinieerd in de gegenereerde header-file. Bovenstaande methode vraagt de totale hoeveelheid geheugen op en geeft dit resultaat terug. 4.3 Implementatie van de Monitor 4.3 47 Implementatie van de Monitor Eigenlijk is het monitor-framework niets meer dan een standaard meegeleverde toepassingsapplicatie die gebruik maakt van de nieuwe mogelijkheden, geboden door de geı̈mplementeerde resources. Bovendien kan het helemaal in Java geschreven worden en is het dus per definitie overdraagbaar naar andere platformen zonder enige aanpassing. Alle noodzakelijke klassen voor de Monitor zijn gegroepeerd in het resources.util.monitorpackage. Centraal in dit package staan de interface MonitorService en de factory-klasse MonitorServiceFactory. Daarnaast zijn een aantal default monitors meegeleverd. Allen met een eenvoudige vanzelfsprekende implementatie. De code is terug te vinden in appendix I. 4.3.1 MonitorService Een klasse die wat meer aandacht verdient is de implementatie van de monitorservice. In het package be.ugent.ibcn.resources.util.monitor is een implementatie van deze interface voorzien. Deze klasse implementeert de interface Runnable aangezien deze in een onafhankelijk thread moet kunnen lopen. Zelf houdt deze klasse een lijst van threads bij die elk een Monitor bevatten. Elk van deze threads slaapt gedurende een aantal milliseconden, zoals gedefinieerd met de methode setMinimumInterval( int interval). Bij het wakker worden wordt de monitor()-methode aangeroepen en valt de tijd opnieuw in slaap voor een aantal milliseconden. Elk van deze threads behoren tot een ThreadGroup beheert door de implementatie van MonitorService en hun prioriteit wordt in- gesteld volgens de prioriteit van de monitors. Dit volstaat voor een stand-alone gebruik van de API. 4.4 Integratie met het OSGi-framework Tot nu is enkel de implementatie van de API aan bod gekomen, zonder expliciet rekening te houden met de OSGi-omgeving waarin deze implementatie zal ingebed worden. 4.4 Integratie met het OSGi-framework 4.4.1 48 Bundels Zoals reeds aan bod gekomen is in het eerste hoofdstuk, werkt het OSGi- framework met bundels welke eenvoudig kunnen in- en uitgeplugd worden. Aangezien de API en ook de implementatie in twee delen kan opgesplitst worden, is het ook logisch deze zelfde scheiding door te voeren in het samenstellen van de bundels. Dit vergroot de vrijheid van de gebruiker en vereenvoudigt ook het onderhoud van de code. Indien er ondersteuning voor nieuwe platformen toegevoegd wordt, moet enkel de resources-bundel aangepast worden. Resources Een eerste bundel zal alle klassen uit de packages resources, resources.util en be.ugent.ibcn.resources bevatten. Concreet wil dit zeggen alle packages die gebruik maken van de JNI-interface. De Manifest-file van deze bundel zal dus moeten voorzien in een Bundle-NativeCode-header die alle native libraries vermeld en voor welke platformen deze geschikt zijn. Verder worden ook de te exporteren packages beschreven. In deze header kunnen meerdere libraries voor meerdere platformen opgegeven worden. De OSGi-omgeving zal dan zelf beslissen welke de correcte libraries zijn en deze laden. Dit is waarom het zo belangrijk is dat de Java-klassen en de native libraries zo goed gescheiden zijn. Het enige doel van deze bundel is het exporteren van de nodige packages en deze toegankelijk te maken voor andere bundels die er wensen gebruik van te maken. Monitor De bundel die de monitor bevat zal de packages resources.util.monitor en be.ugent.ibcn.resouces.util.monitor bevatten. Uiteraard maakt deze bundel gebruik van de resources bevat in de Resources-bundel en zal de packages door deze bundel geëxporteerd dan ook importeren. Dit gebeurt door middel van de Import-Package-header. Uiteraard zal deze bundel ook zijn eigen publieke packages exporteren zodat deze kunnen gebruikt worden door adaptieve software-bundels. De monitor-bundel is zelf ook een stuk software die een eigen thread opstart waarbij monitors kunnen geregistreerd worden. Daartoe dient er in de Manifest-file van de bundel een 4.5 Problemen en opmerkingen 49 Bundle-Activator-header gedefinieerd te zijn welke verwijst naar de Activator-klasse van de bundel. De implementatie van deze Activator zal toelaten dat de MonitorService gestart wordt en dat de monitors kunnen geregistreerd worden. 4.5 4.5.1 Problemen en opmerkingen Speciale resources Bij de algemene bespreking over de implementatie van de resources zijn we ervan uitgegaan dat alle data over een resource rechtstreeks kan opgevraagd worden. Dit is echter niet altijd het geval. Zo zijn er een aantal resources zoals de processor en de netwerkverbinding waarbij het gebruik op een bepaald moment in de tijd niet eenduidig te definiëren is. Het gebruik bij dit type resources wordt gedefinieerd in functie van een (kleine) tijdspanne bv. het aantal verstuurde bytes per seconde, in het geval van de netwerkverbinding. De verdere beschrijving verloopt aan de hand van de netwerkverbinding. Het gaat hier dus eigenlijk om statistische gegevens dewelke niet altijd rechtstreeks toegankelijk zijn. Dit vereist immers een zekere ondersteuning van het onderliggende platform. Wat vaak wel beschikbaar is, is het totaal aantal verstuurde bytes sinds het opstarten van de machine. Daarom vereist de implementatie van deze resources extra inspanning. De oplossing waarvoor gekozen is, bestaat er in deze resources te implementeren als een Thread. Bij constructie wordt deze gestart, en van dat moment af zal deze op vaste tijdsintervallen het totale aantal verwerkte bytes opvragen, en aan de hand van de vorige gemeten waarde een gemiddeld gebruik per tijdseenheid berekenen. Deze gemiddelde waarde is dan de waarde die teruggegeven wordt. 4.5.2 Het wiel opnieuw uitvinden Deze implementatie dient te voldoen aan de eisen gesteld door het J2ME CDC/Foundation profile. Zoals reeds eerder vermeld, is deze specificatie gebaseerd op de J2SE 1.2 API Specificatie. Ondertussen heeft de ontwikkeling van het Java-platform niet stilgestaan en is op het moment van schrijven reeds de J2SE 1.5.0 Beta 1 API Specificatie beschikbaar. Een aantal van de uitbreidingen van de specificatie blijken ook voor onze Resource API relevant te zijn. 4.5 Problemen en opmerkingen 50 Zo is het sinds versie 1.4 mogelijk om rechtstreeks aan de Runtime het aantal beschikbare processors op te vragen. Eveneens sinds versie 1.4 is de klasse java.net.NetworkInterface toegevoegd welke de implementatie van de netwerk-resource aanzienlijk kan vereenvoudigen. En tenslotte is er ook een klasse java.awt.GraphicsDevice die sinds versie 1.4 alle informatie aanbiedt vereist door onze API. In vorige versies van de specificatie waren de cruciale methoden hiervoor nog niet voorzien. Dat een aantal van de geı̈mplementeerde resources ook rechtstreeks vanuit de JVM zullen beschikbaar worden is echter helemaal geen probleem. Integendeel, indien deze klassen op termijn ook in de J2ME configuraties zouden beschikbaar worden, vereenvoudigt dit enkel de implementatie. En ook het nut van deze opgestelde API zal niet verdwijnen. Deze API blijft immers een duidelijk overzicht bieden van de toegankelijke resources en vormt een centraal aanspreekpunt. Bovendien is er ook nog de krachtige en flexibele monitoring API die zijn nut blijft behouden. 4.5.3 Niet geı̈mplementeerde resources Er zijn bij het implementeren van de gedefinieerde resources geen noemenswaardige problemen opgetreden. Eén resource werd niet geı̈mplementeerd, namelijk het ROM. In het geval van de TabletPC is dit het BIOS. Deze informatie is op dit platform zeer ontoegankelijk en bovendien heel apparaat-specifiek. Deze resource is op dit platform echter niet zo belangrijk waardoor de impact van dit gebrek heel beperkt blijft. Er is ook één resource die niet helemaal voldoet aan de specificatie, de NetworkConnection zal niet altijd een correct resultaat geven bij een oproep van de methode isConnected(). Strikt genomen dient deze methode na te gaan of de netwerkverbinding actief is, de implementatie zal echter aangeven of de interface werkt of niet. Alle andere methoden, zoals het meten van de bandbreedte, werken naar behoren. 4.5.4 Theorie vs. praktijk Verder zijn er nog een heleboel zaken die principieel heel mooi zijn, maar de praktijk voldoet niet altijd aan de eisen gesteld door de theorie. Of het is gewoon niet efficiënt om het op die manier te doen. 4.5 Problemen en opmerkingen 51 Beperkingen van de OSGi-omgeving Zoals reeds eerder vermeld biedt de OSGi-omgeving de mogelijkheid om dynamisch de juiste native libraries te selecteren voor het juiste platform. In de praktijk blijkt deze mogelijkheid echter nog niet fijnmazig genoeg geı̈mplementeerd te zijn. Momenteel kan er geselecteerd worden op basis van: • processor-type • besturingssysteem • versienummer van het besturingssysteem • taal Wegens de erg specifieke informatie die dient opgevraagd te worden, blijkt deze informatie niet altijd voldoende. De verscheidenheid in mobiele devices is enorm. Hoewel vaak dezelfde besturingssystemen teruggevonden worden, hebben deze vaak uitbreidingen, specifiek voor één type toestel of één bepaald merk. Meestal blijkt nu net in deze uitbreidingen de informatie te zitten nodig voor onze API. Een voorbeeld hiervan is de iPAQ PocketPC, met een merkspecifieke API. De specifieke implementatie voor TabletPC in combinatie met WindowsXP Tablet Edition heeft als voordeel dat gebruik kan gemaakt worden van de Windows-API die zo goed als helemaal gelijk is voor alle versies van WindowsXP. Efficiëntie Het is natuurlijk heel mooi dat de OSGi-omgeving dynamisch kan beslissen welke native libraries dienen gebruikt te worden. Maar het is niet erg efficiënt dat een bundel die een tiental platformen ondersteund, en dus een veelvoud native libraries bevat, zou gebruikt worden op een klein mobiel toestel met beperkte opslag. In heel veel gevallen zal deze bundel éénmaal in een OSGi-omgeving geı̈nstalleerd worden. Net zoals je éénmaal kiest welke OSGi-omgeving, en Java Virual Machine installeert. Vanuit hetzelfde oogpunt kan het in sommige gevallen beter zijn het aantal native calls te beperken en hier en daar iets op het Java-niveau af te handelen. Dit vormt een kleine beperking op de automatische porteerbaarheid van de implementatie maar heel waarschijnlijk is het nog altijd 4.5 Problemen en opmerkingen 52 mogelijk een heleboel gelijkaardige platformen te ondersteunen, door enkel de native libraries aan te passen. Een reële situatie zou bv. ook kunnen zijn dat de makers van een bepaald apparaat zelf in een implementatie voorzien. De kans dat deze implementatie meerdere apparaten (van andere fabrikanten) zal ondersteunen lijkt ook in dat geval behoorlijk klein. 4.6 Besluit 4.6 53 Besluit Er is gekozen voor een implementatie die duidelijk opgesplitst is in verschillende onderdelen, die zo porteerbaar mogelijk gehouden zijn. Enkel de native libraries zullen moeten herschreven en hercompileerd worden om meerdere platformen te ondersteunen. Dit was een bewuste keuze om een maximale algemeenheid en portabiliteit te garanderen. Het blijkt echter ook dat dit geen zaligmakende keuze is. Er zijn voldoende argumenten om een andere weg in te slaan en de implementatie specifieker te maken voor één apparaat of een groep van apparaten. De TabletPC is echter een voldoende krachtig platform met voldoende schijfen geheugenruimte zodat de efficiëntie een minder bepalende factor is dan op meer beperkte apparaten. Het is ook een vrij algemeen platform, waardoor het kiezen voor een algemene implementatie geen probleem vormt. Er is geen juiste of foute keuze, maar het belangrijkste is dat de implementatie voldoet aan de eisen gesteld door de API. Het einderesultaat is een concrete implementatie van de opgestelde API die voldoet aan alle eisen en geen beperkingen oplegt. De implementatie is conform met het J2ME CDC/Foundation profile waarmee de voorop gestelde eisen vervuld zijn. TOEPASSING 54 Hoofdstuk 5 Toepassing Tot nu toe is er een API-specificatie opgesteld die het mogelijk maakt om informatie over de systeemresources op te vragen en deze automatisch te later monitoren. Met een implementatie die helemaal OSGi-compatibel is en werkt op een Java platform dat voldoet aan de eisen gesteld door het CDC/Foundation profile hebben we alle ingrediënten om de applicaties geschetst in het inleidende hoofdstuk ook echt te verwezenlijken. Dankzij de Java Virtual Machine kunnen we onze code éénmaal schrijven, éénmaal compileren en vervolgens overal uitvoeren. De OSGi-omgeving laat ons toe de software heel eenvoudig te installeren, te upgraden of te verwijderen. En dankzij onze API kunnen we onze software at runtime aanpassen aan de statische en dynamische eigenschappen van het apparaat. Bij wijze van demonstratie zijn er een aantal demo-toepassingen ontworpen. In een eerste fase is er een heel eenvoudige resourcemonitor ontworpen om de implementatie te kunnen te testen. Daarna is er aan een meer representatieve toepassing gewerkt, namelijk een mediaplayer. Deze mediaplayer zal zich dynamisch kunnen aanpassen aan de beschikbare processorkracht. Indien de processor te zwaar belast wordt, wat zou leiden tot een slechte weergave van de video, kan de grootte van het scherm aangepast worden, of kan er overgeschakeld worden naar een ander bronbestand. De grootte van het scherm heeft een invloed daar het herschalen van de media extra rekenkracht kost. Ook het gebruikte bronbestand kan een invloed hebben naargelang de gebruikte codec, de compressiegraad van de media en de algemene kwaliteit. Men kan ook om heel andere redenen rekening houden met het processorgebruik, nl. het extra stroomverbuik dat een zwaar belaste processer met zich meebrengt. Bij toestellen die met 5.1 Resourcemonitor 55 batterijen werken is dit zeker een must. Uiteraard zal deze demo-applicatie hogere eisen stellen dan het CDC/Foundation profile waaraan de API-implementatie zich houdt. Dit profiel vereist immers niet eens dat het gebruikte toestel een monitor heeft. Een videoplayer voor zo’n platform implementeren zou bijgevolg nogal nutteloos zijn. Bovendien vereist het afspelen en decoderen van multimediabestanden een behoorlijke rekenkracht. Een TabletPC is hierop voorzien, een PDA of GSM echter (nog) niet. Dit demonstreert ook onmiddellijk dat het geen enkel probleem is om deze API op een ruim aanbod aan toestellen te gebruiken. Hoewel ze met beperkte apparaten in het achterhoofd is ontwikkeld vormt dit geen rem wat betreft de inzetbaarheid op meer geavanceerde toestellen. 5.1 Resourcemonitor Een eerste demo-toepassing is een eenvoudige resourcemonitor. Deze geeft onder de vorm van staafdiagrammen het gebruik van de laatste seconden weer. In figuur 5.1 zijn enkele screenshots te zien van de monitor in actie. (a) (b) Figuur 5.1: (a) Monitor van het CPU-gebruik, (b) Monitor van het RAM-gebruik. 5.2 MediaPlayer 5.2 56 MediaPlayer De eigenlijke demotoepassing is een mediaplayer. Wat deze player anders maakt dan bestaande players is dat deze rekening zal houden met de eigenschappen van het platform waarop hij gebruikt wordt. Allereerst zal de grootte van het scherm bepaald worden en kan aan de hand hiervan de displaygrootte van het videobestand bij het starten aangepast worden. Maar ook tijdens het afspelen van de video kan de schermgrootte dynamisch aangepast worden zonder tussnkomst van de kijker. Deze dynamische aanpassingen worden veroorzaakt door het gebruik van de processor. Indien het processorgebruik boven een bepaalde kantelwaarde stijgt zal de schermgrootte kleiner gemaakt worden, en omgekeerd. Een meer ingrijpende wijziging is het kiezen van het bronbestand op basis van het processorgebruik. Bij het overschrijden van de kantelwaarde zal dan een ander bronbestand gebruikt worden. Het is de moeite waard om dergelijke aanpassingen op basis van het processorgebruik te doen want: • Het is aangenamer om een video in lagere kwaliteit of op een kleiner scherm te bekijken in plaats van in alle pracht en glorie maar met schokjes • We kunnen nog steeds de video bekijken wanneer er in de achtergrond andere programma’s rekenwerk aan het verrichten zijn, ook al is het dan tegen een lagere kwaliteit. (Om de andere programma’s niet te veel te vertragen.) • We kunnen het processorgebruik aanwenden om de batterij te sparen, want een zwaar belaste processor verbruikt meer. • Sommige toestellen zorgen ervoor dat de processorsnelheid teruggeschakeld wordt bij lagere belasting of het niet aangesloten zijn op de netspanning. Ook dit komt de levensduur van de batterij ten goede. 5.2.1 Gebruikte resources Om de vermelde functionaliteit te verwezenlijken zijn 2 resources van onze API nodig. 5.2 MediaPlayer 57 Statisch: Display Eerst en vooral is er een scherm vereist, en daarnaast is de resolutie van het scherm belangrijk. Dit om te kunnen bepalen tegen welke ratio het beeld moet geschaald worden, opdat het het volledige scherm zou innemen, of slechts een deel ervan. Dynamisch: CPU Naast het scherm speelt de processor de belangrijkste rol bij het afspelen van videobestanden. De belasting van de processor wordt bepaald door een aantal factoren. Eerst en vooral is er de gebruikte videocodec. Codecs die een hogere compressie aanbieden vereisen meer rekenkracht dan codecs die nauwelijks of geen compressie gebruiken. Een tweede factor is de resolutie en bitrate, al dan niet variabel, van het media-bestand. Als derde factor is er dan nog de herschaling van de weergave. Indien een video niet in de oorspronkelijke verhoudingen of resolutie weergegeven wordt dient deze uitgerokken of ingekrompen te worden. Deze bewerking vergt op zich ook weer extra rekenkracht. Doorgaans vraagt het inkrimpen van het media-bestand veel minder resources dan het uitrekken ervan. Figuur 5.2: De mediaplayer in actie 5.2.2 Instellingen De door de gebruiker instelbare eigenschappen van de player zijn de volgende: CPU threshold bronbestand De waarde van het CPU gebruik vanaf waar dient overgeschakeld te worden naar het andere bronbestand. Bij het overschrijden van een hogere naar 5.3 Java Media Framework 58 lagere waarde zal overgeschakeld worden naar het bestand met lagere kwaliteit. In het andere geval naar een bestand met hogere kwaliteit. CPU threshold schermgrootte De waarde van het CPU gebruik vanaf waar dient overgeschakeld te worden naar een andere schermgrootte. Bij het overschrijden van een hogere naar lagere waarde zal overgeschakeld worden naar een kleinere schermgrootte. In het andere geval naar een grotere schermgrootte. De schermgroottes die gebruikt worden zijn ook door de gebruiker instelbaar, en indien gewenst wordt de player telkens gecentreerd op het scherm bij elke wijziging. Minimaal wijzigingsinterval voor het bronbestand De tijd die dient te verstrijken vooraleer een wijziging van het bronbestand mag plaatsvinden. Dit is nodig opdat de wijzigingen elkaar niet te snel opvolgen en om te voorkomen dat een korte piek of daling een wijziging zouden veroorzaken. Minimaal wijzigingsinterval voor de schermgrootte De tijd die dient te verstrijken vooraleer een wijziging van de schermgrootte mag plaatsvinden. Wel of niet wijzigingen doorvoeren Dit laat toe in te stellen welke wijzigingen plaats mogen hebben. Zo kunnen enkel wijzigingen van het bronbestand of van de schermgrootte toegelaten worden. Zoals uit bovenstaand lijstje blijkt, kunnen er 2 thresholds voor het CPU-gebruik ingesteld worden. Deze twee waarden kunnen volledig vrij ingesteld worden en zullen de volgorde bepalen van de kwaliteitsvermindering. Er zijn slechts twee waarden instelbaar, want indien er meer mogelijkheden zouden zijn zal dit tot een vaak wijzigende player leiden. En dit gaat de kijkervaring eerder negatief beı̈nvloeden in plaats van positief, wat toch wel de bedoeling is. Een handleiding van de mediaplayer is terug te vinden in appendix G. 5.3 Java Media Framework Het afspelen van multimediabestanden zoals video is in Java niet zonder meer mogelijk. Er bestaat echter een optioneel framework, het Java Media Framweork (JMF) wat een uitbreiding is op het standaard J2SE platform, dat dit alles wel mogelijk maakt. Er volgt nu een lijstje met aandachtspunten in verband met het gebruik van het JMF, met daarna een kort overzicht van de alternatieven. 5.3 Java Media Framework 5.3.1 59 Installatie De installatie van het JMF is helemaal geautomatiseerd en hier valt eigenlijk niets speciaal over te zeggen. Het is echter belangrijk te weten dat de packages beschikbaar gesteld door het framework apart in het classpath dienen opgenomen te worden. Indien men dus het JMF in combinatie met een OSGi-platform wenst te gebruiken dient men ervoor te zorgen dat dit ook effectief zo is bij het starten van het OSGi-framework. 5.3.2 Mogelijkheden Het JMF maakt gebruik van platformafhankelijke codecs, beschikbaar voor het gebruikte platform. Er zijn versies beschikbaar voor Windows, Linux en Solaris. Daarnaast is er ook een platformonafhankelijke versie helemaal in Java. Deze versie is echter enkele grootte-ordes trager dan de platformspecifieke versies. Het aantal ondersteunde codecs is jammer genoeg niet erg uitgebreid, dit is voor een deel te wijten aan licentieperikelen. Maar er bestaan ook codecs die achteraf kunnen geı̈nstalleerd worden. Zo biedt IBM een mpeg4-codec aan1 . 5.3.3 Alternatieven Vooraleer het JMF te kiezen als basis voor deze mediaplayer werden ook een aantal andere opties overwogen. Deze maakten allen gebruik van een bestaande player. Een eerste mogelijkheid was om de Quicktime player van Apple te gebruiken, deze heeft een Java-interface waardoor deze heel eenvoudig vanuit Java kan gebruikt worden of in een Javaapplicatie kan geı̈ntegreerd worden. Verder kon ook nog Windows Media Player (of eender welke andere player) gebruikt worden. Het nadeel van deze optie is dat deze niet rechtstreeks vanuit Java kan aangesproken worden. Het zou dus nodig geweest zijn om zelf een Java-interface te definiren die via JNI zou toelaten om deze player te besturen vanuit de applicatie. De keuze is gevallen op JMF omdat op deze manier het project helemaal in Java kan ontwikkeld worden en omdat dit een grotere vrijheid laat wat betreft ontwerp van de applicatie. Bovendien 1 http://www.alphaworks.ibm.com/tech/mpeg-4 5.4 Architectuur 60 is JMF beschikbaar voor alle platformen waarvoor J2SE beschikbaar is. Al dan niet onder de vorm van een tragere Java-implementatie. 5.4 Architectuur De algemene architectuur van de mediaplayer zal hier geschetst worden, met de nadruk op het gebruik van de resource- en monitor API. De volledige broncode voorzien van commentaar is te vinden in appendix I. De code van de player is ondergebracht in 3 packages: dynamicmedia, dynamicmedia.player en dynamicmedia.player.util. waarbij deze respectievelijk de grafische user interface (GUI), de eigenlijke player en de hulpklassen voor de player en GUI bevatten. De centrale klasse in het ontwerp is de eigenlijke player, MultiSourcePlayer. 5.4.1 MultiSourcePlayer De playerklasse is opgevat als een black box waar een aantal instellingen van kunnen aangepast worden en die al het werk intern afhandelt. Om de gebruikers van deze klasse op de hoogte te stellen van eventuele wijzigingen van de player zal deze events genereren. De GUI, als gebruiker van de klasse, zal zich dan als luisteraar bij de player registreren. In figuur 5.3 vinden we een overzicht van de belangrijkste methoden van de player terug. Deze omvatten een aantal getters en setters om de instellingen aan te passen en ook de methoden getVideoScreen() en getController(). Analoog aan de methoden voorzien door de interface Player in het JMF. Intern maakt de player gebruik van twee UsageThresholdMonitors om het CPU-gebruik in de gaten te houden. Wanneer deze een event genereren, zal de player na de ingestelde wachttijd controleren of de overschrijding van de threshold blijvend is. Indien dit het geval is, wordt er een event gegenereerd. Wanneer beide thresholds naar boven overschreden worden, zal de player na het verloop van de wachttijd van de monitor met de laagste threshold een eerste event genereren. Pas dan begint de tweede wachttijd. Op deze manier kunnen de twee kwaliteitswijzigingen nooit samen plaats hebben en gebeuren de wijzigingen altijd in de juiste volgorde. Het voorkomt bovendien dat we 5.5 OSGi-compatibel 61 MultiSourcePlayer +getVideoScreen() +getController() +getPreferredScreenSize() +setQualityControl() +getQualityControl() +setScreenControl() +getScreenControl() +setQualityThreshold() +getQualityThreshold() +setScreenThreshold() +getScreenThreshold() +setScreenTime() +getScreenTime() +setQualityTime() +getQualityTime() +addListener() +addMediaFile() Figuur 5.3: De implementatie-klasse van de mediaplayer met de belangrijkste methoden. twee wijzigingen doorvoeren, waar één zou volstaan. Hetzelfde geldt uiteraard ook indien beide thresholds naar beneden overschreden worden. 5.4.2 Grafische user interface De grafische user interface zal een instantie van de player aanmaken en zich als luisteraar registreren. De events waarop de GUI zal reageren zijn ofwel een wijziging van het bronbestand ofwel een wijziging van de schermgrootte. In het eerste geval zal met de methode getVisualScreen() de display van het nieuwe bronbestand opgevraagd worden om de bestaande display te vervangen. Indien het gaat om een wijziging van grootte, zal aan de hand van de event bepaald worden of een grotere of kleinere weergave gewenst is. Vervolgens wordt de gewenste grootte berekend in functie van de ingestelde verhouding en de grootte van het computerscherm, waarna deze nieuwe grootte ook effectief ingesteld wordt. 5.5 OSGi-compatibel De player wordt verondersteld te werken binnen een OSGi-omgeving en dus dienen een aantal maatregelen genomen te worden. Dit opdat de klassen kunnen verpakt worden in een bundel die in eender welke OSGi-omgeving kan ingeplugd worden waarbinnen ook al de resource API geı̈nstalleerd is. 5.5 OSGi-compatibel 5.5.1 62 Activator Elke bundel heeft een Activator nodig, deze zal ook onmiddellijk als main klasse fungeren opdat de player ook alleenstaand kan gebruikt worden. In deze Activator zal bij het starten eerst informatie over de display opgevraagd worden. Indien er geen display aanwezig is zal de bundel het start-proces onmiddellijk beëindigen en zich weer afsluiten. Hetzelfde zal gebeuren indien de display niet grafisch is. Indien er een geschikte display aanwezig is op het apparaat zal er een referentie naar de MonitorService opgevraagd worden aan de OSGi-omgeving. De player maakt intern immers gebruik van Monitors, en deze dienen geregistreerd te worden bij deze service. Indien de player buiten een OSGi-omgeving gestart wordt zal er een MonitorService gecreëerd worden met behulp van de MonitorServiceFactory, die speciaal voor dat geval voorzien is. Eens ook dit verwezenlijkt is wordt de player gestart en wacht deze op interactie van de gebruiker. 5.5.2 Instellingen De voorkeuren die de gebruiker kan instellen dienen bewaard te worden, zodat bij elke herstart van de player deze niet telkens opnieuw moeten ingegeven worden. Het OSGi-framework schermt de bundels echter een beetje af van het onderliggende platform en het opslaan van deze instellingen vraagt dus een beetje extra werk. Het OSGi-framework laat ons toe met gewone bestanden te werken, net zoals we dit in een gewone Java-applicatie zouden doen, maar i.p.v. rechtstreeks het bestand aan te maken of te openen dient dit via de BundleContext te gebeuren. Het creëren of openen van een bestand gaat dan als volgt: File f = bc.getDataFile(file) Waar bc de BundleContext is, en file een String met de bestandsnaam. Indien het bestand niet kan geopend of gecreëerd worden zal deze methode null teruggeven. Eens deze File verkregen is, verloopt het gebruik van het bestand net zoals in een stand-alone applicatie. 5.6 Besluit 5.6 63 Besluit Als eindresultaat van dit hoofdstuk hebben we een mediaplayer gerealiseerd die gebruik maakt van de resources- en monitor-API, en aldus de werking en het nut ervan demonstreert. De player kan op meerdere apparaten gebruikt worden en zal zich naargelang het CPU-gebruik anders gedragen. De implementatie van deze applicatie zou zonder de opgestelde API niet mogelijk geweest zijn, tenzij door zelf via de nodige native methoden alle informatie op te vragen. Dus hoewel het gebruik van de API een beetje extra programmeerwerk vroeg, blijft dit toch een veel eenvoudigere oplossing dan het alternatief en alles zelf te gaan doen. Bovendien zorgt het gebruik van de API ervoor dat de applicatie zelf platformonafhankelijk blijft. BESLUIT 64 Hoofdstuk 6 Besluit Het belangrijkste doel van deze thesis was het opstellen van een duidelijke API die toelaat om meer informatie over het gebruikte platform te verkrijgen. Zowel wat betreft statische als zeer dynamische resources zoals het scherm, het geheugen, de processor . . . Daarnaast is er ook een structuur voorzien die het eenvoudig maakt om de resources te gaan monitoren. Daardoor wordt het mogelijk waarschuwingen te krijgen wanneer er bepaalde gedragingen in de resources optreden, bijvoorbeeld het gebruik overschrijdt een vooraf ingestelde waarde. Deze specificatie werd daarna ook succesvol geı̈mplementeerd op de TabletPC met WindowsXP Tablet Edition als besturingssysteem. Deze implementatie voldoet aan de eisen gesteld door de specificatie en is beschikbaar als OSGi-bundel. Parallel met deze thesis werd ook een implementatie voor PocketPC voorzien. [20] Dankzij deze API en bijhorende implementaties liggen de poorten naar platformonafhankelijke adaptieve software open. Het is nu mogelijk software te schrijven die op een variëteit van toestellen werkt en zich dynamisch aanpast aan de mogelijkheden ervan. Het is niet meer nodig een keuze te maken om ofwel platformonafhankelijke software, ofwel adaptieve software te ontwikkelen. Ook de ontwikkeling en het onderhoud van de software wordt eenvoudiger daar er geen parallelle versies meer moeten onderhouden worden. Eén enkele versie volstaat voor alle platformen. De voordelen manifesteren zich ook op het gebruikersniveau, want niet enkel kan je altijd en overal hetzelfde softwarepakket gebruiken, het volstaat ook om één versie aan te schaffen. Een demonstratie van de mogelijkheden is gegeven door het ontwikkelen van een mediaplayer die BESLUIT 65 naargelang het processorgebruik de kwaliteit van de weergave aanpast door de schermgrootte te wijzigen of een minder belastend bronbestand te kiezen. Toekomstig werk Natuurlijk is er altijd ruimte voor uitbreiding en verbetering. Dit bleek al tijdens de ontwikkeling en implementatie van de API. Af en toe dook reeds een onvoorzien probleem op door beperkingen van het doelplatform of omwille van een kleine onvolkomenheid in de specificatie. (Zie appendix E voor de wijzigingen die om deze reden zijn gemaakt.) Indien er nog verborgen problemen zijn met de specificatie, is de kans groot dat deze aan het licht zullen komen bij bijkomende implementaties voor andere platformen. Het ondersteunen van meerdere platformen is ook de meest voor de hand liggende uitbreiding. Eén van de meest belangrijke doelstellingen is echter ook de API gebruiken. Dit is immers de meest adequate manier om te ontdekken of aan alle noden voldaan is. Het is immers onmogelijk om reeds bij de ontwikkeling alle mogelijke gebruiken te voorzien. Ook de mediaplayer kan nog verder uitgebreid en verbeterd worden. Een voor de hand liggende stap zal erin bestaan om naast lokale bestanden ook streaming media te ondersteunen. In dat geval kan naast het processorgebruik ook rekening gehouden worden met de beschikbare bandbreedte. Het Java Media Framework biedt reeds ondersteuning voor streaming media, doch zal deze uitbreiding speciale aandacht vereisen. Het wijzigen van bronstream zal immers veel minder snel kunnen dan het wijzigen van bronbestand. Om toch nog een vloeiende weergave te kunnen behouden zal er moeten gebruik gemaakt worden van een buffer. EEN OVERZICHT VAN OSGI-IMPLEMENTATIES 66 Bijlage A Een overzicht van OSGi-implementaties De OSGi Alliance biedt een framework aan voor het verdelen en beheren van applicaties en services naar allerlei apparaten in een netwerk, wat niet wegneemt dat het framework ook perfect bruikbaar is op stand-alone apparaten. De OSGi Alliance geeft echter enkel een specificatie en de implementatie wordt overgelaten aan derden. Hier volgt een overzicht van enkele implementaties die we getest hebben. Allereerst moet opgemerkt worden dat er nogal wat verschillen zijn tussen de verschillende implementaties. Uiteraard is dit geen probleem, zolang ze maar voldoen aan de opgelegde specificatie. Het is echter hier dat het soms misloopt. Niet alle implementaties houden zich voor 100 % aan de specificatie of implementeren deze reeds volledig. Een voorbeeld dat opviel, was dat SMF verwacht dat er in de manifestheader Bundle-NativeCode aanhalingstekens staan rond de naam van het besturingssysteem wanneer daarin spaties voorkomen. Wanneer de aanhalingstekens er wel staan, werkt de bundel dan weer niet in JES of OSCAR. KnopflerFish ondersteunt beide versies van de header. Deze verwarring wordt waarschijnlijk veroorzaakt doordat de OSGi-specificatie [1, sec. 4.6] hier wat afwijkt van de bestaande Manifest-conventies voor Jar-files1 . Deze zijn op hun beurt geı̈nspireerd op RFC8222 . In de OSGi-specificatie [1, sec. 4.6] kan de waarde van een parameter een token of een quoted string te zijn. In de bestaande Manifest-conventies wordt echter geen gebruik gemaakt van quoted strings, waar dit in 1 2 http://java.sun.com/j2se/1.4.2/docs/guide/jar/jar.html#JAR%20Manifest http://www.faqs.org/rfcs/rfc822.html A.1 Java Embedded Server 67 RFC822 wel het geval is. Het probleem blijkt dus te ontstaan doordat Oscar en JES de naam van het besturingssysteem als een token zien, waar SMF dit als een quoted string ziet en ze niet beiden ondersteunen zoals het eigenlijk hoort. Enkel KnopflerFish kan overweg met beide interpretaties. A.1 Java Embedded Server JES 2.0 is de implementatie van Sun. Ze schiet tekort op enkele punten. Zo is ze niet in staat om goed om te gaan met native code. Zo werken enkel bundels die maar van één native bibliotheek gebruik maken. Bovendien werkt het updatemechanisme niet voor bundels met native code. Toch heeft JES ook zijn goede kanten. JES is voorzien van een zeer handige webinterface om het framework te beheren, het remote management panel. Via deze webpagina kunnen bundels geı̈nstalleerd en gedeı̈nstalleerd worden met enkele muisklikken. Dit is veel productiever en overzichtelijker dan commando’s typen in een tekstinterface zoals alle implementaties die aanbieden. Het beheren kan zelfs vanop afstand gebeuren. Je hoeft immers alleen maar naar de administratiepagina surfen op het IP-adres van het apparaat. A.2 OSCAR OSCAR 0.9.4pre1 is een open-sourceproject. Het voordeel hiervan is dat het mogelijk is zelf dingen aan te passen of toe te voegen, of in ieder geval bekijken hoe de implementatie in elkaar zit. Het belangrijkste nadeel is dat OSCAR niet voor 100 % voldoet aan de standaard. Ook hier is de ondersteuning voor native code niet perfect, alhoewel het meestal wel lukt. Ook het specifiëren van meerdere beturingssystemen en/of architecturen werkt net zoals in JES niet. Toch nog een pluspunt: ook OSCAR heeft optioneel een (heel beperkte) grafische interface in Swing. A.3 Knopflerfish Knopflerfish 1.0.2 is eveneens een open-sourceproject. Dit is zeker het meest gebruiksvriendelijke pakket. Standaard start het framework op in de desktop. Dat is een grafische interface geschreven in Swing waarin de bundels kunnen beheerd worden. Het nadeel hiervan is dat de A.4 Service Management Framework 68 desktop niet beschikbaar is in J2ME, omdat Swing daar niet bestaat. Er is geen remote management mogelijk met de desktop. Er wordt wel een bundel aangeboden waarmee telnettoegeng mogelijk zou moeten zijn, maar die werkt niet naar behoren. Nog een vervelende bug is dat het deı̈nstalleren van bundels niet altijd lukt. Langs de andere kant, Knopflerfish implementeert de OSGi-specificatie praktisch volledig. De recentste versie van Knopflerfish is 1.3.0. Deze kwam echter te laat uit om nog uitvoerig te gebruiken en te testen. Deze versie implementeert nu Release 3 van de OSGi-specificatie. De gebruikersinterface is nog beter en de remote toegang via telnet is gerepareerd. Nadat we de bug gemeld hadden i.v.m. het deı̈nstalleren van bundels werd ook dit foutje verbeterd. Remote management is nu mogelijk met een optionele bundel. A.4 Service Management Framework SMF 3.5.2 is de implementatie van IBM. Zoals verwacht mag worden van een commerciële implementatie werkt alles naar behoren. Het enige wat soms niet werkt is het updaten van bundels die een service registreren. Er worden ook heel veel bundels meegeleverd bij het framework. Zo heeft SMF naast een console ook een mooie webinterface die vergelijkbaar is met die van JES. Dit is de enige van deze vier implementaties die zonder problemen kan overgedragen worden op J2ME. Het is verwonderlijk dat enkel SMF specifiek voor beperkte (mobiele) apparaten gemaakt is, terwijl dat toch vooral de doelapparaten van OSGi zijn. A.5 Overzicht Aangezien de verschillende implementaties de verschillende management applicaties (uiteraard) voorzien onder de vorm van bundels is het mogelijk om deze onderling uit te wisselen. Zo is het bv. perfect mogelijk om de Remote Management interface van JES te installeren en gebruiken op alle andere implementaties. A.5 Overzicht 69 JES OSCAR Knopflerfish SMF 2.0 0.9.4 1.0.2 1.3.0 3.5.2 Implementeert OSGi Release 1 2-3 2 3 3 Voldoet goed aan specificatie ✘ ✘ ✔ ✔ ✔ Console interface ✔ ✔ ✔ ✔ ✔ ✔✔ ✔ ✔✔ ✔✔ ✔✔ Remote management ✔ ✘ ✘ ✔ ✔ Open-source ✘ ✔ ✔ ✔ ✘ Goed geschikt voor J2ME ✘ ✘ ✔ ✔ ✔✔ Geteste versie Grafische interface Tabel A.1: Overzicht van OSGi-implementaties LEDENLIJST OSGI Bijlage B Ledenlijst OSGi 4DHomeNet, Inc. http://www.4dhome.net/ Alpine Electronics Europe Gmbh http://www.alpine-europe.com/ AMI-C http://www.ami-c.org/ Atinav Inc. http://www.atinav.com/ Belgacom http://www.belgacom.be/ BMW http://www.bmw.com/ Cablevision Systems http://www.cablevision.com/ Deutsche Telekom http://www.telekom3.de/ Echelon Corporation http://www.echelon.com/ Electricite de France (EDF) http://www.edf.com/ Espial Group, Inc. http://www.espial.com/ ETRI http://www.etri.re.kr/ France Telecom http://www.francetelecom.fr/ Fraunhofer Gesellschaft http://www.fraunhofer.de/ Gatespace Telematics AB http://www.gatespacetelematics.com/ IBM Corporation http://www.ibm.com/ Insignia Solutions http://www.insignia.com/ Institute for Infocomm Research http://www.i2r.org.sg/ KDDI RenD Laboratories Inc. http://www.kddlabs.co.jp/ Mitsubishi Electric Corporation http://www.mitsubishielectric.com/ 70 LEDENLIJST OSGI Motorola, Inc. http://www.motorola.com/ Nokia http://www.nokia.com/ NTT http://www.ntt.co.jp/ Object XP AG http://www.objectxp.com/ Oracle Corporation http://www.oracle.com/ Panasonic Technologies, Inc. http://www.research.panasonic.com/ Philips http://www.philips.com/ ProSyst Software AG http://www.prosyst.com/ Robert Bosch Gmbh http://www.bosch.com/ Samsung Electronics Co. http://www.samsung.com/ Sharp Corporation http://www.sharp.co.jp/ Siemens http://www.siemens.com/ Sun Microsystems http://www.sun.com/ Telcordia Technologies http://www.telcordia.com/ Telefonica I+D http://www.tid.es/ TeliaSonera http://www.teliasonera.com/ Texas Instruments, Inc. http://www.ti.com/ Toshiba Corporation http://www.toshiba.co.jp/ Een actuele lijst is steeds terug te vinden op http://www.osgi.org/about/members.asp 71 BEKNOPT OVERZICHT VAN DE API Bijlage C Beknopt overzicht van de API Package resources Klassen resources.SystemResourceFactory java.lang.Throwable java.lang.Exception resources.ResourceException resources.ResourceNotFoundException resources.ResourceNotSupportedException Interfaces resources.Resource resources.DynamicResource resources.CPU resources.Memory resources.Storage resources.NetworkConnection resources.Power resources.StaticResource resources.Display resources.Keyboard resources.PointingDevice Package resources.util Klassen resources.util.Dimension resources.util.Properties 72 BEKNOPT OVERZICHT VAN DE API Package resources.util.monitor Klassen java.util.EventObject resources.util.monitor.ResourceEvent resources.util.monitor.MonitorServiceFactory resources.util.monitor.ResourceMonitor resources.util.monitor.DynamicResourceMonitor resources.util.monitor.CapacityChangeMonitor resources.util.monitor.CapacityThresholdMonitor resources.util.monitor.UsageChangeMonitor resources.util.monitor.UsageThresholdMonitor resources.util.monitor.PropertyChangeMonitor Interfaces java.util.EventListener resources.util.monitor.ResourceListener resources.util.monitor.MonitorService 73 BEKNOPT OVERZICHT VAN DE IMPLEMENTATIE Bijlage D Beknopt overzicht van de implementatie Package be.ugent.resources Klassen be.ugent.ibcn.resources.Resource be.ugent.ibcn.resources.DynamicResource be.ugent.ibcn.resources.CPU be.ugent.ibcn.resources.DiskStorage be.ugent.ibcn.resources.NetworkConnection be.ugent.ibcn.resources.Power be.ugent.ibcn.resources.RAMMemory be.ugent.ibcn.resources.ROMStorage be.ugent.ibcn.resources.VirtualMachineMemory be.ugent.ibcn.resources.StaticResource be.ugent.ibcn.resources.Display be.ugent.ibcn.resources.Keyboard be.ugent.ibcn.resources.PointingDevice Package be.ugent.resources.util.monitor Klassen be.ugent.ibcn.resources.util.monitor.MonitorServiceImpl be.ugent.ibcn.resources.util.monitor.ServiceActivator 74 API SPECIFICATIE CHANGELOG 75 Bijlage E API specificatie changelog In de tijd tussen de eerste versie van de API-specificatie en de definitieve versie die in deze thesis voorgesteld wordt, zijn enkele wijzigingen gebeurd. De ingrepen waren nooit drastisch, maar kwamen voort uit noden of bedenkingen die pas aan het licht kwamen tijdens het implementeren van de API of tijdens het testen ervan. Hier volgt een kort overzicht van wat er veranderd werd en waarom. Naast de definitieve versie kunnen ook de oudere versies teruggevonden worden op de CD-ROM. Versie oktober 2003 • De eerste afgewerkte versie. Deze werd gebruikt als vertrektpunt van de implementatie. Versie maart 2004 • Een nieuw type exceptie toegevoegd in package resources: ResourceNotSupported. Deze exceptie kan opgeworpen worden door de methoden uit SystemResourceFactory wanneer een resource opgvraagd wordt dat niet ondersteund wordt door de implementatie. Tijdens het implementeren van de API voor de PocketPC bleek dat de beperkte API van Windows CE/PocketPC niet alle functionaliteit toelaat die nodig is voor een volledige implementatie. Zo is het bijvoorbeeld onmogelijk om gegevens over de CPU op te vragen. Om dit op te vangen werd een extra exceptie toegevoegd, om het onderscheid te kunnen maken tussen een onbestaande resource en een ongeı̈mplementeerde resource. API SPECIFICATIE CHANGELOG 76 Versie april 2004 • Methoden verwijderd uit SystemResourceFactory. Deze klasse bevatte voor elke getXXX()-methode een methode met dezelfde signatuur op een extra argument na. Dit argument was van het type Class. Deze klasse werd dan door de methode geı̈nstantieerd. De bedoeling was het dynamisch uitbreidbaar maken van de API. Deze methoden werden uiteindelijk niet nuttig bevonden. Ze moedigen aan om rechtstreeks met implementatieklassen te werken in plaats van via een interface. Dit komt de uniformiteit niet ten goede. Als men dan toch een eigen implementatie wil gebruiken voor een resource, kan men bovendien evengoed zelf een instantie maken met new in plaats van dat door een methode te laten doen. • Hernoemen van methode getNetworkConnections() naar getNetworkConnectionNames() om consistent te zijn met getStorageNames(). • Definitieve versie. HOWTO: GEBRUIK VAN DE API 77 Bijlage F Howto: gebruik van de API Deze bijlage heeft tot doel een kleine inleiding te geven in het gebruik van de API en de integratie met het OSGi-framework. In hoofdstuk 3 werd reeds door middel van voorbeeldcode het gebruik geschetst. Hier worden enkele volledig uitgewerkte voorbeeldjes beschreven om zo het geheel te verduidelijken. Alle code uit deze appendix is integraal terug te vinden op de bijgevoegde cdrom. F.1 Opmerkingen Alle voorbeeldjes zijn OSGi-bundles. Zoals beschreven in hoofdstuk 2 is daarom het gebruik van een BundleActivator noodzakelijk en dienen de gecompileerde klassen correct verpakt te worden in een JAR-file. Een BundleActivator vereist enkel de implementatie van een start- en stop-methode. Het concrete gebruik zal onmiddellijk duidelijk worden in het eerste voorbeeldje. Vooraleer deze bundels kunnen gebruikt worden, dienen in de OSGi-omgeving reeds de Resourcesen Monitor-bundle geı̈nstalleerd en gestart te zijn. Deze zijn eveneens terug te vinden op de cd-rom. F.2 Gebruik van resources en OSGi Een eerste mogelijkheid die de API ons biedt is het rechtstreeks bevragen van resources. We zullen informatie over het scherm opvragen en dit uitschrijven bij het starten van de bundel. Aangezien dit alles is wat we willen doen volstaat het om enkel een BundleActivator te schrijven. Het is de gewoonte om de activator-klasse van een bundel gewoon Activator te noemen. F.2 Gebruik van resources en OSGi 78 Dit maakt het ook voor andere programmeurs makkelijk om deze klasse terug te vinden. F.2.1 Broncode import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import resources.Display; import resources.SystemResourceFactory; /** * Heel eenvoudige bundle die Informatie over het scherm uitprint. */ public class Activator implements BundleActivator { De eerste twee import-statements zijn nodig omdat we een bundel zullen programmeren, de volgende statements zijn nodig om gebruik te kunnen maken van de API-implementatie. We willen immers de resource Display opvragen en dit gebeurt via de SystemResourceFactory. /** * Start-methode van de bundel. */ public void start(BundleContext ctxt) throws Exception { Display display = SystemResourceFactory.getDisplay(); System.out.println("Display :"); System.out.println("Grootte : "+display.getSize()); System.out.println("Kleurdiepte : "+display.getColorDepth()); System.out.println("Grafisch : "+display.isGraphical()); } Bovenstaande stukje code bevat de start-methode van onze bundel, dit wil zeggen dat dit de code is die uitgevoerd wordt bij het starten van de bundel. Allereerst vragen we aan de SystemResourceFactory een instantie van de Display. Vervolgens schrijven we de grootte en kleurdiepte uit en ook nog of het om een grafische display gaat of niet. Het opvragen van de display genereert mogelijks een exception indien de resource Display niet ondersteund is in de gebruikte implementatie of indien er geen display aanwezig is op het toestel. Merk ook op dat de start-methode mogelijks altijd een exception opgooit, dus het is eigenlijk F.2 Gebruik van resources en OSGi 79 niet nodig om deze exceptions ook daadwerkelijk op te vangen. Door dit niet te doen wordt aan de starter van de bundel onmiddellijk duidelijk gemaakt dat er iets is misgelopen. Dit is zeker belangrijk indien deze starter zelf ook software of een bundle is. Indien het de bedoeling is dat deze bundel enkel door mensen gestopt of gestart wordt, dan is het vaak wel de moeite om deze exceptions op te vangen en ’te vertalen’ naar een, voor mensen, meer begrijpbare boodschap. /** * Stop-methode van de bundel */ public void stop(BundleContext ctxt) throws Exception {} } De stop-methode van een bundel wordt uitgevoerd wanneer deze gestopt wordt, voor deze bundel is het niet nodig deze in te vullen. F.2.2 Compileren In de bovenstaande broncode hebben we gebruik gemaakt van OSGi-specifieke klassen en van de Resource-API klassen. Deze dienen bij het compileren in het classpath aanwezig te zijn. Rekening houdend met de directory-structuur op de cd-rom kan bovenstaande code gecompileerd worden met het volgende commando1 : javac -classpath ../jars/Resources.jar;../jars/osgi.jar *.java F.2.3 Inpakken in een JAR-file OSGi-bundles zijn eigenlijk niets meer dan gewone JAR-files met enkele extra headers in de Manifest. De Manifest voor deze bundel ziet er als volgt uit: Import-Package: resources, resources.util Bundle-Name: DisplayInfo Bundle-Activator: Activator Bundle-Description: Illustreert het gebruik van een Resource. 1 Op Windows-systemen is de delimiter tussen de verschillende paden een ; op Unix-systemen is dit een : F.3 Gebruik van een default Monitor 80 De Import-Package-header geeft aan welke externe packages er gebruikt worden binnen de bundel en de Bundle-Activator-header duidt aan welke klasse de BundleActivator is. Het inpakken in de JAR-file kan dan als volgt: jar -cvfm DisplayInfo.jar manifest.mf *.class Waarbij DisplayInfo.jar de naam van de bundel is en manifest.mf de Manifest-file zoals hoger beschreven. F.3 Gebruik van een default Monitor Het volgende voorbeeldje beschrijft hoe een UsageThresholdMonitor gebruikt wordt samen met de MonitorService zoals deze aangeboden wordt binnen het OSGi-framework. F.3.1 Code //Import-statements zijn weggelaten. /** * Gebruik van een monitor. */ public class Activator implements BundleActivator, ResourceListener { MonitorService ms; UsageThresholdMonitor utm; Opnieuw noemen we onze klasse Activator. Deze klasse zal ook onmiddellijk de listener zijn die naar de MonitorEvents luistert en daarom implementeert ze ook de interface ResourceListener. We voorzien de MonitorService en usageThresholdMonitor. /** * Start-methode van de bundel. */ public void start(BundleContext ctxt) throws Exception { ServiceReference[] ref = null; /* * De monitorService opvragen. F.3 Gebruik van een default Monitor 81 */ try { ref = ctxt.getServiceReferences( "resources.util.monitor.MonitorService", "(description=MonitorService)"); } catch (InvalidSyntaxException e) { System.err.println("syntaxerror : " + e); } if (ref != null) { ms = (MonitorService) ctxt.getService(ref[0]); } else { throw new BundleException("Monitor service not found."); } In het eerste deel van de start-methode vragen we via de BundleContext de MonitorService aan de OSGi-omgeving, indien deze niet gevonden wordt, wordt een BundleException opgeworpen. We hebben de MonitorService immers nodig om onze monitor bij te registreren. /* * De RAM-resource opvragen. */ Memory ram = SystemResourceFactory.getRAM(); utm = new UsageThresholdMonitor(ram,50); utm.addListener(this); ms.addMonitor(utm); } In het tweede deel van de start-methode wordt de resource RAM opgevraagd en construeren we hiermee een UsageThresholdMonitor. We geven als tweede argument de waarde 50 mee aan de constructor, wat wil zeggen dat wanneer het gebruik van 50 % overschreden wordt, ofwel naar boven ofwel naar onder, er een event zal gegenereerd worden. Daarna wordt bij de monitor een luisteraar geregistreerd en wordt de monitor aan de MonitorService toegevoegd. Vanaf dit moment kunnen er events gegenereerd worden. /** * Stop-methode van de bundel. */ F.3 Gebruik van een default Monitor 82 public void stop(BundleContext ctxt) throws Exception { ms.removeMonitor(utm); utm = null; ms = null; } Wanneer de bundle gestopt wordt dienen we de monitor te verwijderen van MonitorService. /** * Listener-methode */ public void eventOccurred(ResourceEvent e) { System.out.println("Usage :"+e.getData()); } } Wanneer er een event plaats heeft zullen we de waarde die meegegeven wordt uitschrijven. F.3.2 Compileren Het compileren verloopt net zoals bij het vorige voorbeeld, enkel wordt hier ook gebruik gemaakt van de monitors, en dienen deze dus ook opgenomen te worden in het classpath: javac -classpath ../jars/Resources.jar;../jars/MonitorService.jar;\\ ../jars/osgi.jar *.java F.3.3 Inpakken in een JAR-file Ook het inpakken in de JAR-file verloop analoog aan het vorige voorbeeld. Hier dient ook nog het monitor-package toegevoegd te worden aan de Import-Package-header. De Manifest ziet er als volgt uit: Import-Package: resources, resources.util, resources.util.monitor Bundle-Name: MonitorUser Bundle-Activator: Activator Bundle-Description: Illustreert het gebruik van een Monitor. F.4 Een BatterijMonitor F.4 83 Een BatterijMonitor Als laatste onderdeel zullen we een eigen monitor schrijven die een event genereert wanneer het toestel overschakelt van batterij naar netspanning en omgekeerd. F.4.1 Code //import-statements zijn weggelaten. /** * Batterijmonitor die een event zal genereren wanneer er wordt overgeschakeld * tussen batterij en netspanning. */ public class PowerMonitor extends ResourceMonitor { boolean prevState; /** * Constructor */ public PowerMonitor(Power power) { super(power); prevState = power.isBatteryPowered(); } We schrijven een eigen monitor door over te erven van de bestaande abstracte klasse ResourceMonitor. Een instantie van de klasse kan enkel geconstrueerd worden met een Power-resource, deze monitor zal immers enkel hiervoor bruikbaar zijn. Aangezien we de overgang willen ontdekken tussen batterij en netspanning of omgekeerd is het belangrijk te weten wat de krachtbron is op moment van constructie. /** * Monitor-methode */ public void monitor() { boolean curState = getPower().isBatteryPowered(); F.4 Een BatterijMonitor 84 /* * indien de huidige status en de vorige status verschillend zijn * wordt er een event gegenereerd met de juiste boodschap */ if(curState != prevState) { String msg = curState ? "batterij" : "netspanning"; prevState = curState; fireEvent(msg); } } De monitor-methode zal door het toevoegen van de monitor aan de MonitorService elk interval aangeroepen worden. In deze methode vragen we opnieuw op wat de huidige krachtbron is en indien dit niet meer dezelfde is als toen we dit de vorige keer hebben opgevraagd, wordt er een event gegenereerd. Hiertoe kunnen we gebruik maken van de fireEvent-methoden al voorzien door de abstracte ResourceMonitor-klasse. Ook alle benodigdheden om luisteraars bij onze monitor te registreren zijn reeds voorzien. /** * De batterij opvragen * @return Power */ protected Power getPower() { return (Power)getResource(); } } Voor het gemak is ook een protected methode getPower() toegevoegd om de Power-resource op te vragen. Op de cd-rom is eveneens een activator die gebruik maakt van deze monitor toegevoegd. HANDLEIDING MEDIAPLAYER 85 Bijlage G Handleiding MediaPlayer Eerst volgt een beschrijving van het formaat van het input-bestand, en vervolgens zullen de instelbare opties overlopen worden. G.1 Input De player neemt twee bestanden als input en deze dienen in een eenvoudig tekstbestandje gespecificeerd te worden. Dit bestand ziet er bv. als volgt uit: Vakantiefilm file:///C:\vakantie\film\strand_high.mpeg file:///C:\vakantie\film\strand_low.mpeg Op de eerste lijn wordt de titel geplaatst, en vervolgens komen de twee volledig gekwalificeerde paden naar de bestanden. Het bestand in de beste kwaliteit dient eerst te komen. G.2 Startscherm Figuur G.1 toont het scherm dat te zien is, net na het starten van de player. De functionaliteit van de knoppen is de volgende: 1. een bronbestand openen; 2. het preferences-venster openen; G.3 Preferences 86 Figuur G.1: Startscherm van de MediaPlayer 3. deze handleiding tonen; 4. een informatie-venstertje tonen; 5. de player afsluiten; G.3 Preferences Het venstertje met de instellingen bestaat uit 3 tabbladen: G.3.1 Algemeen Figuur G.2: Instellen welke acties mogen ondernomen worden. Op het eerste tabblad (zie figuur G.2) kan ingesteld worden welke wijzigingen mogen plaatsgrijpen. Een wijziging van het bronbestand, van de schermgrootte, beiden of geen van beiden. De G.3 Preferences 87 volgende twee tabbladen laten toe deze keuzes in te stellen. G.3.2 Bestandskeuze Figuur G.3: Instellingen voor de bestandswissel. Op het tabblad voor de bestandskeuze (zie figuur G.3) kan ingesteld worden vanaf welke threshold er moet geschakeld worden tussen de verschillende bestanden. Verder is het ook mogelijk in te stellen hoe lang er minstens tussen twee wijzigingen dient gewacht te worden. G.3.3 Schermgrootte Figuur G.4: Instellingen voor de schermwissel. Op het tabblad voor de keuze van de schermgrootte (zie figuur G.4) kan net zoals bij de keuze van het bestand een threshold en minimuminterval ingesteld worden. Daarnaast kan ook de gewenste schermgrootte gekozen worden in functie van de grootte van de fysieke display. Deze waarden zullen gebruikt worden bij het wijzigen van de schermgrootte. Tenslotte kan ook aangegeven worden dat bij het wijzigen van de schermgrootte de mediaplayer dient gecentreerd G.3 Preferences 88 te worden. Indien deze optie niet aangevinkt wordt, zal de linkerbovenhoek gebruikt worden als referentiepunt. TOOLS 89 Bijlage H Tools Eclipse Een IDE, uitermate geschikt voor Java ontwikkeling. http://www.eclipse.org/ Ant Een build-tool om bv het compileren, builden van bundels en genereren van javadoc te automatiseren. http://ant.apache.org/ Microsoft Visio Ontwikkeltool om UML en andere diagrammen mee te maken. http://www.microsoft.com/office/visio/ OpenOffice.org Office suite, gebruikt voor het maken van niet UML figuren. http://www.openoffice.org/ MinGW Een vrije C/C++ compiler voor Windows. http://www.mingw.org/ Microsoft Visual Studio Een IDE, zeer geschikt voor ontwikkeling in Windows. http://msdn.microsoft.com/vstudio/ CVS Een Concurrent Versions System. http://www.cvshome.org/ LATEX Het typesetting systeem waarmee deze tekst gemaakt is. http://www.ctan.org/ CD-ROM 90 Bijlage I cd-rom • Het boek in pdf-formaat • De presentatie zoals deze gegeven werd op de thesisverdediging op 25 mei 2004 • Software – De drie belangrijke releases van de API – De implementatie van de API – De demotoepassing: een MediaPlayer en bijhorende testfilmpjes – Een eenvoudige ResourceMonitor – Een hulpprogramma dat toelaat om de load op de cpu kunstmatig te verhogen of te verlagen – Voorbeeldcode uit hoofdstuk 2 en bijlage F • Een volledig geconfigureerde OSGi-omgeving met alle bundles voorgenstalleerd (gebaseerd op Knopflerfish) BIBLIOGRAFIE 91 Bibliografie [1] OSGi. Osgi service platform release 3. Technical report, The Open Services Gateway Initiative, March 2003. http://www.osgi.org (24 Mei 2004). [2] Jar manifest specificaties. http://java.sun.com/j2se/1.4.2/docs/guide/jar/jar.html#JAR%20Manifest (24 Mei 2004). [3] Java embedded server. http://java.sun.com/jes/ (24 Mei 2004). [4] Prosyst mbedded. http://www.prosyst.com (24 Mei 2004). [5] avelink embedded gateway. http://www.atinav.com/osgi/ (24 Mei 2004). [6] Open source cluster application resources (oscar). http://oscar.sf.net (24 Mei 2004). [7] Knopflerfish osgi. http://www.knopflerfish.org. [8] Gatespace telematics ab. http://www.gatespacetelematics.com/ (24 Mei 2004). [9] Ibm service management framework. http://www-306.ibm.com/software/wireless/smf/index.html (24 Mei 2004). [10] Wireless highlights and news. http://wireless.java.sun.com (24 Mei 2004. [11] Connected limited device configuration. http://java.sun.com/products/cldc (24 Mei 2004). [12] Connected device configuration. http://java.sun.com/products/cdc/ (24 Mei 2004). [13] Tim Lindholm and Frank Yellin. The Java Virtual Machine Specification. Addison-Wesley, second edition, 1999. [14] Bill Joy, Guy Steele, James Gosling, and Gilad Bracha. Java Language specification. Addison-Wesley, second edition, 2000. http://java.sun.com/docs/books/jls/ (24 Mei 2004). BIBLIOGRAFIE 92 [15] Sheng Liang. The Java Native Interface, Programmer’s Guide and Specification. AddisonWesley, 1999. http://java.sun.com/docs/books/jni/ (24 Mei 2004). [16] Developer information for the ipaq handhelds. http://h71018.www7.hp.com/ (24 Mei 2004). [17] Microsoft windows mobile. http://www.microsoft.com/windowsmobile/ (24 Mei 2004). [18] Symbian os - the mobile operating system. http://www.symbian.com (24 Mei 2004). [19] Java community process. http://www.jcp.org (24 Mei 2004). [20] Koen Van Boxstael. Resource monitoring api in java voor adaptieve software. Master’s thesis, Universiteit Gent, 2003-2004. [21] Kim Topley. J2ME™ in a nutshell, A desktop quick reference. O’Reilly, first edition, March 2002. [22] Michael Juntao Yuan. Enterprise J2ME, Developing mobile Java applications. Prentice Hall, 2004. [23] Mikkio Kontio. Mobile Java™ with J2ME™. IT Press, 2002. [24] The independent source for enterprise java. http://www.onjava.com (24 Mei 2004).