Pokrocile aspekty komponentovych technologii
Transkript
Pokrocile aspekty komponentovych technologii
Západočeská univerzita v Plzni Fakulta aplikovaných věd Katedra informatiky a výpočetní techniky Diplomová práce Pokročilé aspekty komponentových modelů Plzeň, 2009 Vojtěch Liška Orig. zadání 1 Prohlašuji, že jsem diplomovou práci vypracoval samostatně a výhradně s použitím citovaných pramenů. V Plzni dne 18. května 2009 Vojtěch Liška 2 Abstrakt Disciplína softwarového inženýrství založeného na komponentách je přístup, ve kterém jsou systémy stavěny z již dříve existujících softwarových komponent. Takto složené systémy mohou být charakterizovány jejich funkcionalitou, ale také jejich mimofunkčními charakteristikami. Cílem této diplomové práce je navrhnout a implementovat rozšíření existujícího experimentálního komponentového modelu CoSi o pokročilé aspekty komponentových technologií - mimofunkční charakteristiky a balíky komponent. V první části se práce zabývá obecnými principy komponentových technologií, popisem vlastností komponent, příklady komponentových modelů a problematikou popisu mimofunkčních charakteristik komponent. Druhá část práce se věnuje návrhu, specifikaci a implementaci rozšíření komponentového modelu CoSi o mimofunkční charakteristiky a balíky komponent. Abstract Component-based software engineering is an approach where systems are built from preexisting software components. These systems are described by their functionality, but also by their non-functional characteristics. The goal of this thesis is to desing and implement an extension of the CoSi component model that covers advanced aspects of the component models - extra-functional properties and component packages. In the first part of the thesis we describe the basic component technology principles. We also describe components and their specification, present several existing component models and discuss extra-functional properties specification. The second part of the thesis describes the design, specification and implementation of the mentioned CoSi component model extension. 3 Obsah 1 Úvod 7 2 Obecně o komponentových technologiích 8 2.1 Úvod do komponentových technologií . . . . . . . . . . . . . . . . . . . . . . . 2.2 Základní principy komponentových technologií 8 . . . . . . . . . . . . . . . . . 11 2.2.1 Komponenty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.2.2 Rozhraní komponenty . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.2.3 Kontrakty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.2.4 Příklad komponentového systému . . . . . . . . . . . . . . . . . . . . . 14 3 Komponentové modely 16 3.1 O komponentových modelech . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.2 EJB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.3 OSGi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.3.1 OSGi Service Platform . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 SaveCCM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3.4.1 Úvod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3.4.2 Architektonické elementy . . . . . . . . . . . . . . . . . . . . . . . . . 23 3.4 3.5 PicoContainer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Specifikace mimofunkčních charakteristik 25 27 4.1 Definice EFP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 4.2 Přístupy k EFP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 4.2.1 NoFun 28 4.2.2 Component Quality Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 CoSi - návrh rozšíření komponentového modelu 5.1 28 31 Obecně o CoSi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 5.1.1 Meta-model CoSi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 5.1.2 Obsah komponenty . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 4 Obsah 5.2 Mimofunkční charakteristiky . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 5.2.1 Obecné principy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 5.2.2 Definice EFP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 5.2.3 Registr EFP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 5.2.4 Popis EFP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 5.2.5 Porovnávání EFP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 5.3 Balíky komponent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 5.4 Další rozšíření modelu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 5.4.1 Poskytované a vyžadované balíky Java tříd . . . . . . . . . . . . . . . 38 5.4.2 Dependency injection . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 6 Specifikace CoSi 6.1 41 Komponentový model CoSi . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 6.1.1 Obsah komponenty - podrobněji . . . . . . . . . . . . . . . . . . . . . 42 6.1.2 Specifikace komponenty - soubor manifest.mf . . . . . . . . . . . . . . 45 6.1.3 Registr mimofunkčních charakteristik . . . . . . . . . . . . . . . . . . 50 6.1.4 Distribuční forma komponenty . . . . . . . . . . . . . . . . . . . . . . 51 6.1.5 Balík komponent . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 6.1.6 Specifikace balíku komponent - soubor manifest.mf . . . . . . . . . . . 52 6.1.7 Distribuční forma balíku komponent . . . . . . . . . . . . . . . . . . . 54 Životní cyklus komponenty a balíku komponent . . . . . . . . . . . . . . . . . 55 6.2.1 Proces vyhodnocení komponenty . . . . . . . . . . . . . . . . . . . . . 57 6.2.2 Rozhraní komponenty a balíku komponent . . . . . . . . . . . . . . . . 59 Kontejner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 6.3.1 Rozhraní kontejneru . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 6.3.2 Základní služby kontejneru . . . . . . . . . . . . . . . . . . . . . . . . 63 6.4 Class loading architektura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 6.5 Rozdíly proti ostatním specifikacím . . . . . . . . . . . . . . . . . . . . . . . . 67 6.2 6.3 7 Implementace změn 7.1 7.2 7.3 69 Jádro kontejneru . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 7.1.1 Architektura jádra kontejneru . . . . . . . . . . . . . . . . . . . . . . . 69 Implementace služeb . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 7.2.1 Služba ExtrafuncRegistryService . . . . . . . . . . . . . . . . . . . . . 73 7.2.2 Služba SimpleShell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Ukázkové scénáře . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 7.3.1 Ukázkový scénář - meteorologická stanice . . . . . . . . . . . . . . . . 75 7.3.2 Ukázkový scénář - SaveCCM/CoSi transformace . . . . . . . . . . . . . 76 5 Obsah 8 Závěr 80 Seznam zkratek 81 Literatura 82 Přílohy i 6 1 Úvod Během posledních deseti let se komponentové technologie přesunuly z oblasti čistě vědeckého výzkumu do praxe a staly se součástí konstrukce moderních softwarových systémů. Softwarový průmysl tak převzal myšlenku běžnou v jiných oborech – skládání složitých systému z jednodušších a menších již dříve existujících funkčních celků – komponent. Proces skládání komponent umožňuje rychlejší konstrukci výsledného systému a snížení celkových nákladů. Takto složené systémy mohou být charakterizovány jejich funkcionalitou (co systém dělá) a jejich mimofunkčními charakteristikami (jak se systém chová s ohledem na pozorovatelné vlastnosti jako je výkon, spolehlivost, atd.). Tyto charakteristiky jsou často opomíjeny, ačkoliv mohou mít vážný dopad na důležitá rozhodnutí v oblasti architektury a návrhu systému. Následující text nejprve seznámí s obecnými principy komponentových technologií (kapitola 2), tedy tím, co jsou to komponenty, k čemu slouží a jakou hrají roli v procesu vytváření softwarových celků založených na komponentách. Navazující kapitola („Komponentové modely“) uvede do problematiky komponentových modelů a krátce představí několik jejich existujících zástupců. Teoretická část diplomové práce je zakončena kapitolou 4, která se blíže věnuje existujícímu výzkumu v oblasti popisu mimofunkčních charakteristik komponent. Cílem práce je analyzovat, kriticky zhodnotit existující implementaci komponentového modelu CoSi a navrhnout rozšíření modelu o pokročilé aspekty komponentových technologií – mimofunkční charakteristiky a balíky komponent. Praktickou část práce tedy tvoří návrh rozšíření modelu o tyto pokročilé aspekty (kapitola 5), specifikace upraveného komponentového modelu (kapitola 6) a v neposlední řadě implementace navržených změn do existující implementace CoSi (kapitola 7) a ověření jejich funkčnosti na ukázkových příkladech. 7 2 Obecně o komponentových technologiích Tato kapitola pojednává o komponentových technologiích a o softwarové disciplíně, která se komponentovými technologiemi zabývá - softwarovém inženýrství založeném na komponentách (dále jen CBSE1 ). Kapitola z velké části čerpá z článku [3] a úvodních kapitol knihy [8]. Kniha se zabývá tvorbou spolehlivých systémů založených na komponentách a je velmi dobrým úvodem do komponentových technologií. 2.1 Úvod do komponentových technologií V posledních několika desetiletích jsme svědky rozmachu informačních technologií a použití software v průmyslu, obchodu, správě, výzkumu a v každodenním životě. Software už není používán jen okrajově v čistě technických systémech. Místo toho se stává nejdůležitějším faktorem v mnoha odvětvích trhu. Tento trend zvyšuje požadavky na softwarové produkty, zejména požadavky jako je robustnost, spolehlivost, použitelnost, přizpůsobitelnost spolu s jednoduchou instalací a nasazením do provozu. Výsledkem toho je stále se zvyšující velikost a složitost softwarových produktů. Hlavní výzvou dnešních softwarových vývojářů je poradit si s touto složitostí a umět se rychle přizpůsobit změnám. V tradičním pojetí byl software vyvíjen se zaměřením na dodání a vývoj jednoho konkrétního systému a na to, jak je dodržen rozpočet na vývoj a konečný termín dodání. Při tom se nemyslelo na další uplatnění vyvinutého software a na vyhlídky na jeho evoluci. Tento přístup často vedl ke zvýšeným nákladům na provoz a údržbu systému a k nesplnění požadavků na kvalitu systému. Z těchto problémů vzešla základní myšlenka o znovupoužitelnosti software. Jeden z přístupů, který tuto myšlenku vzal za svou, je vývoj založený na komponentách (dále 1 Component based software engineering 8 Úvod do komponentových technologií Obrázek 2.1: Návrhový vzor založený na komponentách (převzato z [3]) jen CBD), kde systémy vyvíjíme skládáním již hotových komponent místo psaní systémů od začátku. Systémy založené na komponentách jsou výsledkem přijmutí strategie návrhu založené na komponentách. Termínem strategie návrhu je myšleno něco velmi podobného k vysokoúrovňovému návrhovému vzoru, který je popsaný typy komponent v systému a vzory jejich interakce [4]. Technologie softwarových komponent zahrnuje koncepty, které podporují a odráží tento návrhový vzor (viz obrázek 2.1) CBD vyžaduje systematický přístup pracující s výhodami softwarových kompoent a zároveň snižující rizika jejich použití na minimum. Tento přístup popisuje disciplína CBSE. CBSE je oblast softwarového inženýrství zaměřená na vývoj softwarových komponent a systémů zkonstruovaných z těchto komponent. Poskytuje metody a nástroje na podporu různých aspektů CBD2 . Komponenta (v obrázku 2.1 pod číslem 1) v CBD je softwarová implementace, která může být spuštěna na fyzickém či logickém zařízení [3]. Komponenta implementuje jedno nebo více rozhraní (2). Tato rozhraní jsou s komponentou pevně spjata a popi2V některých případech jsou pojmy CBD a CBSE používány jako synonyma. V této diplomové práci je CBSE použito ve smyslu systematického a disciplinovaného přístupu k CBD. 9 Úvod do komponentových technologií sují component obligations3 , které jsou často popisovány jako kontrakty (3). Kontrakty zaručují, že se komponenty vyvinuté nezávisle na sobě řídí určitými pravidly. Mohou spolu interagovat a výsledek této interakce je předvídatelný. Kontrakty také zaručují, že komponenty je možné nasadit (deploy) (4) do standardních sestavovacích (build-time environment) a běhových prostředí (run-time environment). Systém založený na komponentách je založený na malém počtu odlišných typů komponent. Každý z těchto typů zastává specializovanou roli v systému (5) a je popsaný rozhraním. Množina typů, jejich rozhraní a specifikace povolených vzorů interakce mezi typy komponent dohromady tvoří komponentový model (6). Komponentový framework (7) pak poskytuje různorodé služby pro podporu a vynucení komponentového modelu. Komponentové frameworky jsou v mnoha ohledech podobné specializovaným operačním systémům, přestože operují na mnohem vyšších úrovních abstrakce. CBD přináší mnoho výhod do vývoje software. Jsou to například: ∙ nezávislá rozšíření ∙ snížený čas na dodání systému na trh ∙ efektivnější kontrola nad rozsahem systému, která vede ke snazšímu vývoji a údržbě systému ∙ znovupoužitelnost komponent v dalších systémech CBD ale nepřináší jen výhody. Jsou zde i nevýhody a rizika, která mohou ohrozit úspěch nasazení tohoto přístupu [8]: ∙ Není vždy zřejmé, co je možné použít znovu a co ne. Požadavky na komponenty nejsou vždy známé předem a jsou často nepřesně nebo nedostatečně definovány. ∙ Vývoj znovupoužitelných komponent klade zvýšené nároky na čas a peníze. ∙ Pro splnění podmínky znovupoužitelnosti musí být komponenty dostatečně obecné, rozšiřitelné a přizpůsobitelné, což může vést ke komplikovanější použitelnosti a zvýšeným nárokům na výpočetní výkon. ∙ Údržba komponent může být velmi drahá, protože komponenty musí reagovat na různé požadavky od různých aplikací z různých prostředí a s různými požadavky na spolehlivost. 3 závazky nebo také povinnosti komponenty 10 Základní principy komponentových technologií 2.2 Základní principy komponentových technologií Základním principem CBSE je koncept komponenty. Další termíny jako rozhraní, kontrakt nebo framework jsou úzce spjaty s CBSE právě přes koncept komponenty. Tato sekce dává přehled o konceptu komponenty v CBSE. Druhá část této sekce popisuje koncept rozhraní a kontraktů a diskutuje problematiku těchto konceptů a jejich vzájemné vztahy. 2.2.1 Komponenty Protože komponenty jsou srdcem komponentových technologií, potřebujeme přesně definovat, co to je komponenta, abychom mohli porozumět základním principům komponentových technologií. Ačkoliv koncept a myšlenka komponent jako menších funkčních celků, ze kterých se skládá systém, by se mohla zdát jasná a intuitivní, neexistuje jednoznačná všeobecně přijímaná definice komponenty. Szyperski [19] definuje komponentu výčtem charakteristických vlastností komponenty: A software component is a unit of composition with contractually specified interfaces and context dependencies only. A software component can be deployed independently and is subject to composition by third parties. Aby mohla být komponenta nezávisle nasazena, je nutné jasné odlišení komponenty od jejího prostředí a od ostatních komponent. Komponenty lze skládat a komunikují s prostředím a ostatními komponentami přes rozhraní podléhající kontraktu. Tato rozhraní jsou jasně definována a jejich implementace je zapouzdřena v komponentě a není přímo přístupná z prostředí komponenty. To dělá z komponenty jednotku nezávislého nasazení. Motivace pro nezávislé nasazení je zřejmá – použití komponenty by nemělo záviset na informacích a nástrojích, které má pouze poskytovatel komponenty. Pokud je to splněno, je možné, aby se systém skládal z komponent z různých nezávislých zdrojů. Crnkovič [8] se zabývá i dalšími definicemi komponent a snaží se najít společný jmenovatel pro komponenty. Aby bylo možné úplně popsat a zajistit její korektní integraci, správu a aktualizaci, měla by se podle něj komponenta skládat z následujících elementů: ∙ Množiny rozhraní poskytovaných prostředím nebo požadovaných od prostředí. ∙ Vykonatelného kódu, který může být spřažený s kódem jiných komponent přes rozhraní. 11 Základní principy komponentových technologií Dále uvádí, že pro zlepšení kvality komponenty by měla specifikace komponenty obsahovat následující elementy: ∙ Specifikaci vyžadovaných nebo poskytovaných mimofunkčních charakteristik (dále jen EFP - extra-functional properties4 ). ∙ Validační kód potvrzující navrhované propojení s jinou komponentou. ∙ Dodatečné informace jako např. informace o designu, dokumenty související se splněním specifikací požadavků na komponentu nebo příklady použití. 2.2.2 Rozhraní komponenty Rozhraní jsou přístupové body ke komponentám. Definují způsoby, jakými se mohou klienti komponenty napojit na služby poskytované komponentou. Pokud komponenta poskytuje více služeb, které odpovídají více přístupovým bodům ke komponentě, měla by mít i více rozhraní. Je důležité upozornit na to, že rozhraní sama o sobě neposkytují žádnou implementaci. Pouze vyjmenovávají seznam operací, které může komponenta vykonávat. Jak bylo již napsáno výše, je důležité striktně oddělovat rozhraní od implementace. Toto oddělení zajišťuje, že [8]: 1. implementace komponenty může být změněna bez zásahu do rozhraní, takže se nemusí systém, ve kterém je komponenta nasazena, znovu sestavovat 2. nová rozhraní a implementace mohou být přidávána bez zásahu do stávajících rozhraní komponenty, což zajistí lepší přizpůsobivost Identifikujeme dva typy rozhraní (viz obrázek 2.2 na následující straně; šipka označuje směr volání mezi komponentami): 1. poskytovaná (exported/provided interface) - představuje služby, které komponenta poskytuje prostředí 2. vyžadovaná (imported/required interface) - definuje, jaké služby komponenta vyžaduje od prostředí 4 Tyto charakteristiky jsou také známé jako kvalitativní charakteristiky (quality attributes). Je to např. výkon, spolehlivost, přesnost, bezpečnost, atd. 12 Základní principy komponentových technologií Obrázek 2.2: Rozhraní komponenty Specifikace rozhraní v současných technologiích mají vážné nedostatky. Jedním z těchto nedostatků je zaměření jazyků popisujících rozhraní5 na syntaktickou stránku popisu rozhraní jako např. vstupy a výstupy, která ale neříká nic o sémantice operací (tzn. o tom, co komponenta opravdu dělá). Rozhraní také neumožňují uvádět kontextové závislosti komponent. Další nevýhodou je to, že rozhraními můžeme popsat pouze funkční charakteristiky komponent, ale už ne EFP. Tyto nevýhody se snaží řešit nástroj, který má za cíl přesně specifikovat chování komponent. 2.2.3 Kontrakty Kontrakty poskytují přesnější specifikaci chování komponenty. Ačkoliv by se mohlo zdát, že rozhraní a kontrakty jsou velmi podobné, jedná se o rozdílné koncepty. Na rozdíl od rozhraní, které popisuje operace komponent, kontrakty specifikují chování komponent a interakci mezi různými komponentami. Podle Meyera [18] kontrakt specifikuje seznam globálních neměnných vlastností spravovaných komponentou (invariant). Pro každou operaci komponenty pak obsahuje seznam podmínek, které musí klient splnit (precondition) a seznam podmínek, které se komponenta zavazuje splnit (postcondition). Komponenta pak může spoléhat na to, že všechny preconditions jsou splněny před zavoláním její operace. Před návratem z operace musí komponenta splnit to, k čemu se zavázala a klient na to může spoléhat. Kontrakty je ale možné chápat šíře. V takovém případě kromě specifikace chování jedné komponenty umožňují specifikovat vzájemné chování mezi skupinou komponent. V této souvislosti uvádějí autoři [8]: ∙ participující komponenty, ∙ roli každé z participujících komponent, ∙ invariant, 5 např. IDL - interface definition language 13 Základní principy komponentových technologií ∙ specifikaci metod, které vytvářejí instanci kontraktu. Hierarchicky je možné kontrakty rozčlenit do čtyř úrovní [5]: 1. Syntaktická úroveň. Specifikuje operace, které může komponenta vykonávat spolu s vstupně/výstupními parametry, které komponenta vyžaduje. 2. Úroveň týkající se chování. Specifikuje předchozí a následné podmínky pro operace. 3. Synchronizační úroveň. Specifikuje synchronizaci mezi různými službami a voláními metod. 4. Úroveň kvality služeb (QoS - Quality of Service). Specifikuje EFP. 2.2.4 Příklad komponentového systému Abychom ukázali výhody komponentových technologií obsahuje tato sekce příklad komponentového systému. Jedná se o jednoduchou hypotetickou meteorologickou stanici. Stanice se skládá z několika spolupracujících komponent (viz obrázek 2.3): ∙ Senzory (WindspeedSensor, TemperatureSensor) - komponenty, které mají za úkol dodávat v pravidelných intervalech data (například o teplotě, tlaku, rychlosti větru nebo vlhkosti). ∙ Registr senzorů (SensorRegistry) - komponenta, která má za úkol vést evidenci všech senzorů v systému. Senzory se do komponenty registrují. ∙ Měřící stanice (MeasuringStation) - komponenta, která pracuje s daty ze senzorů a zpracovává je (například je může zobrazovat nebo dělat statistickou analýzu dat). Komponenta používá registr senzorů pro získání těchto dat. Výhody komponentového přístupu v tomto příkladu jsou zřejmé. Měřící stanice se nestará o to, jaké senzory má k dispozici. Pouze se podívá do registru senzorů (použije službu z rozhraní registru), vybere si některé (nebo všechny) a zpracuje jejich data. Registr nemusí vědět vůbec nic o měřicí stanici ani o senzorech, které uchovává. Pouze poskytuje služby, které umožní senzorům se zaregistrovat. Stejně tak senzory (může jich být libovolné množství) nemusí vědět vůbec nic o tom, co se bude dít s jejich daty, pouze musí data poskytovat v nějakém předem dohodnutém formátu (určeném rozhraním komponenty). 14 Základní principy komponentových technologií Obrázek 2.3: UML komponentový diagram meteorologické stanice 15 3 Komponentové modely Následující kapitola dává přehled o tom, co je to komponentový model a jaké jsou základní charakteristiky každého komponentového modelu. Dále se kapitola zaměřuje na několik zástupců komponentových modelů používaných v různých průmyslových odvětvích (EJB, OSGi) nebo pro účely výzkumu (SaveCCM) a na zástupce kontejneru používajícího návrhový vzor Inversion of Control (IoC) - PicoContainer. Není účelem této práce poskytnout dokonalý popis těchto komponentových modelů. Na následujících stránkách je spíše přehled těchto technologií. 3.1 O komponentových modelech V předchozí kapitole jsme se věnovali komponentám a jejich popisu, ale aby mohly komponenty vůbec existovat a komunikovat spolu, musí existovat jasná pravidla, kterými se komponenty musí řídit. Tato pravidla definuje komponentový model, který umožňuje komponentám vzájemně interagovat a poskytovat si služby. Komponentový model tvoří množina typů komponent, jejich rozhraní a specifikace povolených vzorů interakce mezi typy komponent. K důležitým vlastnostem komponentových modelů patří, že předepisují a vynucují [3]: ∙ Typy komponent. Komponentový model vyžaduje, aby každá komponenta implementovala jedno nebo více rozhraní. Typ komponenty může pak být definovaný jako skupina rozhraní implementovaných komponentou. Různé typy komponent mohou zastávat různé role v systému a mohou být obsaženy v různých schématech interakce (viz dále). ∙ Schémata interakce komponent. Komponentové modely zpravidla předepisují, jaké je možné použít komunikační protokoly nebo jak je v modelu dosaženo požadované kvality služeb (jako jsou zabezpečení nebo transakce). Dále mohou popisovat, jak komponenty komunikují mezi sebou nebo s komponentovým frameworkem. 16 EJB Tato interakční schémata mohou být společná pro všechny typy komponent nebo naopak specifická jen pro určité typy komponent. ∙ Navázání zdrojů. Proces skládání komponent je věcí navázání komponent na jeden nebo více zdrojů. . Zdrojem je myšlena služba poskytovaná frameworkem nebo jinou komponentou, která je nasazena ve frameworku. Komponentový model popisuje, jaké zdroje jsou dostupné pro komponenty a jak se mohou komponenty na zdroje navázat. Komponentový model dále obvykle definuje mechanismus, jak vypadá distribuční forma komponenty a jak je možné komponenty přizpůsobovat a nasadit. To, že komponenty striktně dodržují komponentový model, odlišuje komponenty (ve smyslu v jakém používáme tento termín) od jiných softwarových entit (např. od balíků software) [3]. Jen dodržovat pravidla předepsaná komponentovým modelem ale nestačí. Potřebujeme ještě prostředí, ve kterém komponenty poběží a které zajistí prostředky pro komunikaci komponent. Toto prostředí nazýváme komponentový framework. Komponentový framework si můžeme představit jako operační systém pro komponenty. Komponenty pak mají ve frameworku podobný stav jako aplikace v operačním systému. Narozdíl od operačních systémů mají frameworky zpravidla menší rozsah a jsou specializovány na poskytování služeb pro interakci mezi komponentami.1 Komponentové modely svým způsobem definují architekturu výsledného systému. To může limitovat flexibilitu systému, ale na druhou stranu to zrychluje jeho vývoj. 3.2 EJB Enterprise JavaBeans (EJB) je architektura, vyvinutá firmou Sun Microsystems, která má usnadnit vývojářům vytváření rozsáhlých distribuovaných komponentových systémů v Javě. V současné době existuje verze 3.0 [10], která proti předchozím verzím zjednodušuje vývoj takových systémů a je ve světě Javy velmi populární. EJB specifikace definuje několik základních typů komponent - bean. Jsou to serverside komponenty, skládající se z jednoho nebo více objektů, nasaditelné v distribuovaném prostředí. Rozlišujeme session beans a message driven beans. K těmto komponentám přistupují klienti prostřednictvím rozhraní komponenty, které může být lokální (local 1 Ohledně komponentových modelů a frameworků existují v literatuře různé termíny (např. component kit [9]). V této práci je komponentovým frameworkem myšlena implementace služeb, které podporují a vynucují komponentový model (tzv. podpůrná infrastruktura pro komponenty). 17 OSGi interface) nebo vzdálené (remote interface). Rozhraní musí splňovat požadavky dané EJB specifikací, aby bylo možné komponenty spravovat uvnitř EJB kontejneru2 . Ke komunikaci s těmito komponentami přes remote interface se používá RMI3 . Navzdory tomu, že se EJB zaměřuje hlavně na funkcionalitu systémů, definuje model EJB několik charakteristik, které by se daly považovat za mimofunkční podle definice uvedené v sekci 4.1. Model definuje následující EFP: ∙ Locality - globální charakteristika komponenty, která říká, zda jsou její operace přístupné vzdáleně nebo pouze klienty stejného kontejneru. ∙ State - session bean může být buď stateless (bezstavová), nebo stateful (stavová). ∙ Transaction demarcation - pro operaci beany definuje očekávanou úroveň podpory transakcí v rozsahu od never (nikdy) do required and mandatory (vyžadovaná, povinná), kde klient musí poskytnout pro operaci transakční kontext. ∙ Security - zahrnuje definici role klienta v přístupu k operacím beany. Beans může také běžet pod jinou identitou než původní požadavek. Mimofunkční charakteristiky v EJB jsou popsány buď XML syntaxí v deployment deskriptoru beany, nebo specifikací, která je založená na Java anotacích. 3.3 OSGi Tato sekce podává zkrácený přehled komponentové architektury OSGi4 . Více informací je možné nalézt přímo ve specifikaci OSGi [20, 21]. OSGi AllianceTM je nezávislá, nezisková organizace vytvářející a propagující otevřené specifikace v oblasti distribuovaných prostředí. Tyto specifikace definují OSGi Service Platform. Ta se skládá z definice OSGi frameworku a množiny základních služeb. OSGi framework je postavený na Javě (Java slouží jako běhové prostředí) a jeho cílová platforma zahrnuje set-top-boxy, modemy, spotřební elektroniku, osobní počítače, automobily, mobilní telefony, atd. Ačkoli od vydání první verze specifikace již uplynulo skoro deset let, největší rozmach nastal pro OSGi až v posledních několika letech použitím této technologie nejen v embedded zařízeních5 , ale také v desktopových aplikacích 2 EJB kontejner je aplikační server určený pro nasazení a běh bean. Method Invocation - Java implementace vzdáleného volání metod. 4 Open Services Gateway initiative 5 vestavěná nebo zabudovaná (většínou jednoúčelová) zařízení 3 Remote 18 OSGi a aplikačních serverech. V současné době již existuje celá řada frameworků implementujících OSGi. Jsou to např. IBM Websphere, JBoss, ServiceMix, SpringSource a v neposlední řadě robustní vývojový nástroj Eclipse IDE. 3.3.1 OSGi Service Platform OSGi představuje konfigurovatelnou platformu vybudovanou nad JVM6 , která poskytuje prostředí pro běh modulárních aplikací. Specifikace OSGi definuje komponentový model. Jádrem OSGi specifikace je framework, který implementuje tento komponentový model. Funkcionalita frameworku je rozdělena do několika základních vrstev, které řeší základní požadavky na komponentový model: ∙ Vrstva komponent (modulů) - Definuje, co je to komponenta a zabývá se problematikou validace, distribuce a nasazení komponent. ∙ Vrstva životního cyklu - Definuje životní cyklus komponent. ∙ Vrstva služeb - Zabývá se službami poskytovanými komponentami a komponentám. Nepovinně může framework implementující OSGi ještě obsahovat vrstvu zabezpečení. Vrstva komponent Komponenta v OSGi se nazývá bundle. Je představována jedním JAR7 souborem, který je zároveň jednotkou modularizace a nasazení. Tento soubor obsahuje Java třídy a zdroje (např. obrázky, HTML soubory, soubory nápovědy, atd.), které dohromady s třídami poskytují nějakou funkčnost uživateli komponenty. Kromě toho JAR soubor obsahuje manifest, který popisuje obsah souboru a poskytuje informace o komponentě. V manifestu jsou uvedeny informace o všem, co komponenta poskytuje okolí a všechno, co od okolí vyžaduje, aby byla správně nainstalována a aktivována. V prostředí OSGi je komponenta brána jako černá skříňka. To znamená, že nás nezajímá její implementace a přistupujeme k jejím službám přes rozhraní. Komponenty mezi sebou mohou sdílet Java balíčky (packages), které obsahují. Komponenta může poskytovat (export) svůj balíček nebo využívat (import) balíček jiné komponenty. Soubor manifestu 6 Java Virtual Machine ARchive - formát souboru, ve kterém jsou nejčastěji distribuovány Java třídy a k nim přiřazená meta-data 7 Java 19 OSGi je povinnou součástí každé komponenty a mimo jiné umožňuje frameworku a ostatním komponentám zjišťovat informace o konkrétní komponentě. Vrstva životního cyklu Komponenta představuje JAR soubor spuštěný v OSGi frameworku. Tato vrstva definuje operace nad životním cyklem komponenty - jak je možné komponentu nainstalovat, spustit, aktualizovat a odinstalovat. Správa životního cyklu je jednou z silných stránek OSGi. Všechny vyjmenované operace je možné nad komponentami provádět dynamicky za běhu frameworku. Komponenta v OSGi se vždy nachází v jednom z definovaných stavů (viz obrázek 3.1). Základními komponentami v OSGi jsou ty, které mohou být nastartovány. Aby mohla být komponenta nastartována, musí v jedné ze tříd implementovat rozhraní BundleActivator. Tato třída musí být také identifikována v manifestu komponenty jako aktivátor komponenty. Rozhraní BundleActivator obsahuje metody start a stop, které kontejner volá, aby komponentu spustil popřípadě zastavil. Metoda start spustí všechna potřebná vlákna pro běh komponenty, metoda stop pak musí provést úklid po komponentě a ze systému všechna tato vlákna odstranit. Komponenta nemusí nutně obsahovat aktivátor. V takovém případě není komponenta spustitelná a kontejner se o její spuštění ani nepokouší. Komponenty bez aktivátoru mohou být použity pouze jako exportéři tříd nebo rozhraní. Framework definuje následující stavy: ∙ INSTALLED - Komponenta je úspěšně nainstalována do systému. Znamená to, že kontejner přečetl její JAR soubor a zkontroloval obsah komponenty (všechny povinné položky). ∙ RESOLVED - Všechny Java třídy, které komponenta potřebuje, jsou k dispozici. Tento stav indikuje, že komponenta je buď připravena k nastartování, nebo byla zastavena. ∙ STARTING - Kontejner zavolal metodu BundleActivator.start a ta právě běží. ∙ ACTIVE - Komponenta byla úspěšně nastartována a běží. V tomto stavu se komponenta nachází po úspěšném návratu z metody BundleActivator.start. ∙ STOPPING - Komponenta je právě zastavována. Kontejner zavolal metodu BundleActivator.stop a ta právě běží. 20 OSGi Obrázek 3.1: Stavový diagram životního cyklu komponenty (převzato z [20]) ∙ UNINSTALLED - Komponenta byla odinstalována ze systému. Z tohoto stavu není možné komponentu přesunout do jiného stavu. Vrstva služeb Vrstva služeb definuje dynamický spolupracující model silně integrovaný s vrstvou životního cyklu komponent. Jedná se o model publikuj-najdi-navaž, kde poskytovatelé služeb zveřejní (publikují) popis nabízených služeb, spotřebitelé služeb pak hledají služby, které potřebují a po nalezení se k nim naváží (připojí). V OSGi jsou komponenty vystavěny okolo množiny spolupracujících služeb přístupných z registru služeb. Služba v kontextu OSGi definovaná rozhraním služby a implementovaná objektem služby. Rozhraní služby by mělo obsahovat co nejméně implementačních detailů. Objekt služby, což je obyčejná Java třída implementující rozhraní služby, je vlastněný komponentou a běží uvnitř komponenty. Tato komponenta musí registrovat objekt služby v registru služeb, aby byla funkcionalita objektu přístupná ostatním komponentám ve frameworku. Závislosti mezi komponentami, které vlastní služby, a těmi, které je používají, jsou spravovány kontejnerem. Framework se například postará o odregistrování všech služeb používaných komponentou při jejím zastavení. Registr služeb umožňuje spotřebitelům služeb nacházet služby dotazy formulovanými 21 SaveCCM v syntaxi LDAP8 . Framework také zavádí mechanismus zpráv, který umožňuje službám sledovat změny, které ve frameworku nastávají (např. zaregistrování nové služby nebo odinstalování služby). 3.4 SaveCCM 3.4.1 Úvod SaveCCM (The SaveComp Component Model) je komponentový jazyk určený pro modelování embedded zařízení s důrazem na bezpečnost a předvídatelnost [13]. Aplikační doménou SaveCCM je automobilový průmysl. Byl vyvinut na Mälardalen University ve Švédsku v rámci projektu SAVE [14]. SaveCCM je založeno na syntaxi XML (popis SaveCCM XML syntaxe naleznete v příloze A v [13]) a pro grafickou notaci je použita upravená podmnožina UML2 komponentového diagramu (obrázek 3.2). Sémantika je formálně definovaná jako transformace skládající se ze dvou kroků. Prvním krokem je transformace z jazyka SaveCCM do jazyka SaveCCM Core a druhým pak transformace z SaveCCM Core do timed automata9 s úkoly. V této sekci je stručně popsán pouze komponentový model SaveCCM, který nás zajímá. SaveCCM je limitováno na analýzu real-time charakteristik a závislostí ve své aplikační doméně. Je určený pro modelování automobilových systémů s použitím CBD přístupu a s důrazem kladeným na předvídatelnost a analyzovatelnost. Systémy jsou v SaveCCM budovány ze vzájemně propojených elementů s definovanými rozhraními, které se skládají z vstupních a výstupních portů. Model je založený na vzoru řídícího toku (control flow) a striktně odděluje datový tok od řídícího. Datový tok je zachycen spojeními mezi datovými porty, řídící pak spojeními mezi trigger porty. Existuje ještě třetí typ portu, který kombinuje funkcionalitu obou předchozích a nazývá se kombinovaný port. Oddělení datového a řídícího toku dovoluje vykonávat jak periodické tak událostmi řízené aktivity (exekuce může být spuštěna hodinami nebo externími událostmi). SaveCCM má zabudovanou podporu pro EFP - s každou komponentou můžou být svázány charakteristiky jakosti (quality attributes), které poskytují potřebná data pro analýzu komponent. Zajímavé je, že kromě jména a typu mohou charakteristiky obsahovat 8 Lightweight Directory Access Protocol (finite) automata [1] je labelled transition system model pro komponenty v real-time systémech. Používá se k modelování chování real-time systémů v čase. 9 timed 22 SaveCCM Obrázek 3.2: Grafická notace SaveCCM (převzato z [13]) ještě hodnotu důvěryhodnosti charakteristiky (credibility). 3.4.2 Architektonické elementy Hlavními architektonickými elementy SaveCCM jsou komponenty, assemblies (viz dále) a switches (přepínače). Rozhraní všech těchto typů je definováno jako množina portů představující bod interakce mezi elementem a jeho okolím. Systémy se skládají z těchto elementů propojováním jejich portů. Komponenty Komponenta je hlavní architektonický element v SaveCCM. Obsahuje vstupní a výstupní porty spolu s množinou charakteristik jakosti (které představují EFP komponenty). Těmito charakteristikami mohou být například odhady spolehlivosti nebo modely bezpečnosti. Používají se pro analýzu, extrakci a syntézu modelu. Funkcionalita jedné komponenty je typicky implementována jedinou vstupní funkcí napsanou v jazyce C, ale je možné používat i složitější komponenty, které popisují několik úkolů. Závislosti mezi komponentami je možné vyjadřovat pouze propojením portů. Životní cyklus komponenty začíná ve stavu inactive, ve kterém komponenta zůstane, dokud nejsou aktivovány všechny její trigger porty. Po aktivaci těchto portů se přesune do stavu executing. Tento stav se skládá z několika fází. První fází je read, kdy jsou uloženy všechny hodnoty ze vstupních datových portů do vnitřních proměnných. Následuje fáze execution, ve které jsou vykonány operace nad přečtenými daty a po- 23 SaveCCM Obrázek 3.3: Komponenty Clock a Delay (převzato z [13]) slední fáze write zapíše výstup těchto operací do výstupních portů a aktivuje všechny výstupní trigger porty. Po té se komponenta vrací do stavu inactive. Tato striktní sémantika (přečíst-vykonat-zapsat) zajišťuje nezávislost vykonání komponenty na jakékoliv souběžné aktivitě [13]. Hodiny a zpoždění V SaveCCM jsou k dispozici dvě speciální komponenty určené pro ovlivnění časování triggerů (spouštěčů) - Clock (hodiny) a Delay (zpoždění). Komponenta Clock je generátor triggerů a její jediný port je výstupní trigger port. Má parametry T (perioda) a J (jitter - odchylka od periodického signálu). Nová perioda začíná každých T časových jednotek a trigger je vygenerován během J časových jednotek po začátku této periody. Komponenta Delay má jeden vstupní trigger port a jeden výstupní trigger port. Dva parametry D (delay - zpoždění) a P (precision - přesnost) jsou pak použity k pozdržení trigger signálu o D až D+P časových jednotek. Kompozitní komponenty Kompozitní komponenta je speciální typ komponenty, jejíž chování je popsáno propojením komponent namísto vstupní funkce. Kompozitní komponenta je sama komponentou. To znamená, že je dodržena striktní sémantika přečíst-vykonat-zapsat a platí stejná pravidla pro její životní cyklus. Přepínače Přepínače (switches) slouží k změně zapojení komponent. Obsahují množinu mapování ze vstupních portů na výstupní. Každému z těchto propojení je přiřazen logický výraz, jehož proměnnými jsou hodnoty vstupních portů přepínače. Tento výraz vyhodnocuje, zda je dané mapování aktivní. Přepínače přeposílají data na výstupní porty ihned po jejich obdržení a na rozdíl od komponent nejsou aktivovány. 24 PicoContainer Assemblies Assemblies jsou prostředkem k zapouzdření, pojmenování a skrytí vnitřní struktury propojení více komponent. Stejně jako přepínače nejsou nijak aktivovány a rovnou přeposílají data ze vstupních portů na výstupní. Porty a konexe Jak je uvedeno výše, SaveCCM rozlišuje mezi vstupními a výstupními a mezi datovými a trigger porty. Všechny porty mají své jméno, datové pak navíc typ a nepovinně ještě počáteční hodnotu. Externí port je pak port, který má navíc popisek mapující tento port na nějakou externí entitu a který není propojený s ostatními porty. Porty jsou vzájemně propojeny jedním ze dvou typů konexí (connections). Prvním typem jsou immediate connections, které představují bezztrátový přenos dat z jednoho portu na druhý. Druhým typem jsou pak complex connections, které představují přenos dat (nebo řízení) skrz kanál s možnou ztrátou nebo zpožděním přenášených informací. 3.5 PicoContainer PicoContainer [15] je open-source light-weight kontejner založený na návrhovém vzoru Inversion of Control (dále jen IoC), používající princip Dependency Injection (dále jen DI)[11]. Projekt začal v roce 2003 a dal vzniknout principu constructor injection autowiring10 . Mezi hlavní přednosti tohoto kontejneru patří jeho velikost (jádro má pouze 260kB), podpora různých typů DI připojitelnost kontejneru k projektu (embeddability). Kromě constuctor injection podporuje PicoContainer ještě různé jiné typy DI. Jedním z těchto typů je setter injection auto-wiring. Setter injection je typ DI, kde kontejner předává komponentě její závistlosti za použití tzv. setterů po instancování komponenty. Setter je metoda s přesně definovaným jménem a jediným parametrem, který představuje injektovaný objekt. V ostatních metodách třídy pak můžeme použít tyto objekty, aniž bychom je ručně instancovali. Příklad na setter injection (převzato z [15]): p u b l i c c l a s s Apple { p r i v a t e Orange o r a n g e ; private Pear pear ; 10 Varianta DI, kde komponenty dostávají všechny závislosti přes svůj konstruktor 25 PicoContainer p r i v a t e Banana b a n a n a ; p u b l i c s e t O r a n g e ( Orange o r a n g e ) { t h i s . orange = orange ; } public s e t P e a r ( Pear pear ) { this . pear = pear ; } p u b l i c s e t B a n a n a ( Banana b a n a n a ) { t h i s . banana = banana ; } public void i n i t i a l i z e ( ) { / / a l l s e t X X X s a r e now done } / / o t h e r methods } 26 4 Specifikace mimofunkčních charakteristik Většina výzkumu v oblasti softwarových komponent se zaměřuje na popis funkčních aspektů komponent (jako např. specifikace komponenty, testů, atd.) [2]. Tato kapitola se blíže věnuje popisu charakteristik komponent, které nepopisují funkční aspekty komponent, ale to, jak se systém chová s ohledem na pozorovatelné charakteristiky. Tyto charakteristiky jsou například výkon, znovupoužitelnost nebo spolehlivost. 4.1 Definice EFP Na mimofunkční charakteristiky je možné nahlížet z různých pohledů. Jen samotný termín extra-functional property nemá standardizovanou definici. Nejčastěji používaná synonyma pro EFP jsou „kvalitativní atributy, charakteristiky nebo faktory“ [16]. Pro účely této práce je použita definice mimofunkčních charakteristik z [16]: Mimofunkční charakteristika softwarového modulu je taková charakteristika, která: (1) je důležitá pro klienty nebo dokonce koncové uživatele modulu a (2) není vyvolatelnou funkcionalitou nebo chováním. 4.2 Přístupy k EFP Tato sekce popisuje dva různé publikované přístupy k EFP ve vztahu k CBSE. Záměrem není popsat všechny dostupné studie o EFP, ale pouze ukázat některé hlavní přístupy ke specifikaci EFP. 27 Přístupy k EFP 4.2.1 NoFun Jazyk NoFun [12] se zaměřuje na popis mimofunkčních informací v oblasti komponentového programování a jejich vazbu na softwarové moduly. Identifikuje tři typy těchto informací: Non-functional attribute, Non-functional behaviour a Non-functional requirement 1 (viz dále). Non-functional Attributes Jedná se o jakékoliv atributy softwarového celku, které slouží k jeho popsání nebo vyhodnocení. Tyto atributy jsou součástí domény, která definuje množinu platných hodnot a operací. Autoři identifikují domény jako standardní datové typy Boolean, Integer, Real, String nebo Enumeration. Atributy mohou být základní nebo odvozené. Odvozené atributy jsou odvozené ze základních. Dále mají obor působnosti, který určuje, v jakých komponentách je atribut použit. Každý atribut může být vázán k celé komponentě nebo k jednotlivé operaci. Non-functional Behaviour V jazyce NoFun je oddělena aplikace EFP na určitou implementaci komponenty od jejich definice. Non-functional behaviour je jakékoliv přidělení hodnot k non-functional attributes, které jsou použity v implementované komponentě. Non-functional Requirements Toto se uplatní v případě skládání komponent. Pokud jedna komponenta importuje druhou, musí si vybrat správnou implementaci importované komponenty. Specifikuje nonfunctional requirements, což je podmnožina non-functional behaviour, které od importované komponenty požaduje. Podle shody non-functional requirements s non-functional behaviour pak importující komponenta vybere vyhovující implementaci importované komponenty. 4.2.2 Component Quality Model I když je nadefinováno, jak mimofunkční charakteristiky popsat, stále ještě zbývá definovat, jaké konkrétní mimofunkční charakteristiky lze rozeznávat. Autoři práce [2] kategorizují atributy jakosti, které je možné použít jako EFP. Vycházejí při tom z normy 1 Mimofunkční charakteristika, chování a požadavek 28 Přístupy k EFP ISO/IEC 91262 , kterou upravili pro potřeby CBD. Výsledný model jakosti komponenty (dále jen CQM - Component Quality Model) se skládá z následujících charakteristik: ∙ Funkcionalita (Functionality): schopnost komponenty poskytovat vyžadované služby ∙ Spolehlivost (Reliability): schopnost komponenty udržet určitou úroveň výkonu ∙ Použitelnost (Usability): vyjadřuje, jak je komponenta pochopitelná, naučitelná, použitelná, konfigurovatelná a vykonavatelná ∙ Efektivnost (Efficiency): schopnost komponenty poskytnout požadovanou výkonnost v závislosti na objemu použitých zdrojů ∙ Udržovatelnost (Maintainability): vyjadřuje, jak možné komponentu modifikovat ∙ Přenositelnost (Portability): vyjadřuje, jak je komponenta schopná přenosu z jednoho prostředí do druhého ∙ Prodejnost (Marketability): vyjadřuje tržní charakteristiky komponenty Charakteristiky se dále dělí na sub-charakteristiky související s běhovým prostředím komponenty (runtime) a s jejím životním cyklem (life-cycle). CQM popisuje jakostní atributy pro měření sub-charakteristik komponent. Každý z těchto atributů definuje metriku, která popisuje metodu měření a jednotky. CQM popisuje následující druhy metrik: ∙ Presence (P): Identifikuje přítomnost atributu v komponentě. Skládá se z logické hodnoty (boolean) a textového řetězce (string). Logická hodnota indikuje přítomnost atributu a textová pak popisuje, jak je atribut implementovaný komponentou (pokud je přítomný). ∙ IValues (IV): Indikuje přesné hodnoty. Je popsána jako číselná proměnná (integer) a textový řetězec (string), který určuje jednotku proměnné (např. kb, MHz, atd.). ∙ Ratio (R): Tato metrika se používá k vyjádření percentuální hodnoty. Je popsána jako číselná proměnná (integer) s hodnotami mezi 0 a 100. 2 Jedná se o obecný standardizovaný model jakosti software, který může být aplikován na jakýkoliv softwarový produkt. 29 Přístupy k EFP Kromě uvedených charakteristik definují autoři ještě dodatečné informace, které se skládají z technických informací (verze komponenty, programovací jazyk, atd.) a organizačních informací (CMMI úroveň a reputace organizace). Tyto informace jsou důležité pro vývojáře, aby dokázali analyzovat současný stav komponenty, a pro také pro zákazníky komponenty (například komponenta vyvinutá organizací s CMMI úrovní 5 bude pravděpodobně spolehlivější, než komponenta vyvinutá neznámou firmou). K popisu informací se používá textový řetězec. 30 5 CoSi - návrh rozšíření komponentového modelu Záměrem této diplomové práce je seznámit se s komponentovým modelem CoSi1 , vyvinutým v rámci diplomové práce „Implementace jednoduchého komponentového modelu“ [22] Břetislavem Wajtrem a Ing. Přemyslem Bradou, Msc., Ph.D. a navrhnout a implementovat rozšíření tohoto komponentového modelu o popis mimofunkčních charakteristik komponent a o balíky komponent. Hlavním úkolem CoSi je napomáhat při výzkumu verzování a nahraditelnosti komponent. Přidání EFP do CoSi umožní napomáhat i ve výzkumu nahraditelnosti komponent na základě těchto charakteristik. Balíky komponent pak mají v tomto kontextu za úkol zjednodušit nasazení sdružováním spolu souvisejících komponent do větších celků. V kapitole 6 je popsána specifikace CoSi v její aktuální podobě (verze 2.0) se zakomponovanými rozšířeními komponentového modelu. Tato specifikace předepisuje, co musí implementace komponentového frameworku obsahovat, aby byla považována za implementaci CoSi a aby bylo možné v této implementaci použít komponenty vyvinuté pro CoSi. Nová verze specifikace vznikala zároveň s návrhem rozšíření popsaných v této práci. Abychom oddělili mou práci od práce p. Brady a p. Wajtra, uvedeme zde kroky, ze kterých se skládala realizace této práce: ∙ důkladné seznámení se se specifikací a implementací komponentového modelu CoSi včetně jeho kritické analýzy (tato část byla velmi důležitá pro korektní návrh změn v implementaci), ∙ návrh rozšíření modelu o EFP, ∙ návrh rozšíření modelu o balíky komponent, ∙ návrh změn referenční implementace CoSi pro podporu těchto rozšíření, 1 Components simplified 31 Obecně o CoSi ∙ implementace změn a ukázkových scénářů pro otestování nově implementovaných rozšíření. 5.1 Obecně o CoSi CoSi je obecně použitelný komponentový framework založený na Javě. Jako zdroj inspirace pro CoSi sloužil komponentový model OSGi [20]. Následující sekce popisuje meta-model a termíny používané v CoSi. 5.1.1 Meta-model CoSi Komponentový model CoSi se skládá z entit vyobrazených na obrázku 5.1. Entity označené stereotypem "new" jsou ty, které byly přidány v CoSi verze 2 v rámci této diplomové práce. Základem je komponenta2 , která má svého poskytovatele, jméno a verzi. Tyto tři parametry jednoznačně identifikují komponentu. Komponenta je nasazena do kontejneru, který tvoří běhové prostředí pro komponenty. Komponenta dále může poskytovat nebo vyžadovat vlastností (features), které mají jméno, typ a další nepovinné atributy jako například verzi. Různé vlastnosti komponenty, které mají stejný typ, jsou rozlišeny jejich jmény. Meta-model CoSi definuje tyto čtyři druhy vlastností: Služba (service) je specifikována rozhraním v Javě a implementuje funkčnost. Poskytované služby jsou registrovány v kontejneru. Ten dále zprostředkovává navázání poskytovaných služeb na komponenty, které služby vyžadují. Toto navázání je realizované objekty typu odkaz na službu. Typ (type) odkazuje na třídu nebo rozhraní jazyka. Typy poskytované komponentami jsou přístupné komponentám, které dané typy vyžadují přes class loader (viz sekce 6.4) exportující komponenty. Události (events)3 umožňují vyměňování zpráv mezi komponentami. Kontejner poskytuje službu zajišťující příjem a doručování zpráv komponentám. Události mají typ a jméno, což umožňuje komponentám specifikovat filtry, které říkají, jaké události bude komponenta poslouchat. Doručovat zprávy v CoSi je možné synchronně i asynchronně. 2v CoSi komponenty nazýváme také bundle 32 Obecně o CoSi Obrázek 5.1: Meta-model CoSi Atributy (attributes) jsou typované hodnoty, které může komponenta číst nebo zapisovat. Mohou být pouze pro čtení (read-only) nebo pro čtení i zápis (read-write). Atributy jsou přístupné přes registr atributů udržovaný kontejnerem. 5.1.2 Obsah komponenty V následujícím textu je popsána distribuční forma komponenty a způsoby komunikace komponenty s kontejnerem a s ostatními komponentami. Komponenta je v CoSi softwarový celek distribuovaný ve formě jednoho JAR archivu, který obsahuje vše, co komponenta k běhu potřebuje. Je v něm uložen přeložený kód komponenty, všechna rozhraní a zdroje, které komponenta využívá. Zdroji mohou být například knihovny třetích stran, multimediální soubory (ikony, obrázky), textové soubory, apod. 33 Mimofunkční charakteristiky Komponenty v CoSi se pro okolí chovají jako černá skříňka (black-box). Znamená to, že pokud komponenta nespecifikuje v manifestu komponenty jinak, nemůže využívat žádné zdroje, které nejsou obsaženy v jejím JAR archivu. Stejně tak nikdo mimo komponentu nemůže využívat žádné zdroje této komponenty, pokud nejsou nadefinované jako exportované. Ale neplatí to jen pro zdroje komponenty. Také implementace komponenty je před jejím okolím skryta. Kontejner a ostatní komponenty mají možnost zjistit, co komponenta dělá (přes rozhraní komponenty), ale už ne jakým způsobem. Manifest komponenty je textový soubor, který používá ke specifikaci informací o komponentě hlavičky (headers). Těmito informacemi jsou např. název komponenty, verze, závislosti, class path, atd. Během instalace komponenty do systému čte tento soubor, a na základě takto získaných metadat integruje komponentu do systému. Soubor manifestu obsahuje mimo jiné informace o: ∙ aktivátoru komponenty - třída, kterou kontejner používá ke spuštění a zastavení komponenty; ∙ službách - poskytované a vyžadované služby (rozhraní); ∙ typech - komponenta může poskytovat některé z jejích tříd ostatním komponentám nebo využívat třídy jiných komponent; ∙ událostech - komponenta specifikuje události, které bude odesílat nebo umět přijímat; ∙ atributech - komponenta může nastavit systémovou proměnnou (atribut), kterou mohou ostatní komponenty číst. Jako výsledek této DP framework CoSi od verze 2.0 umožňuje jednoúrovňové skládání komponent do balíků komponent (viz sekce 5.3) za účelem jednoduššího nasazení většího počtu komponent. Nejedná se o skládání ve smyslu hierarchického komponentového modelu, kdy výsledkem složení více komponent je také komponenta, ale o zabalení více komponent tvořících jeden funkční celek, do balíku komponent. Tento balík komponent je pak možné do systému nainstalovat jako celek, čímž se nainstalují jednotlivé komponenty v něm obsažené. Komponenty pak vystupují v systému samostatně. 5.2 Mimofunkční charakteristiky Specifikace EFP pro CoSi vznikala na Katedře informatiky a výpočetní techniky Fakulty aplikovaných věd Západočeské univerzity v Plzni současně s touto diplomovou prácí a 34 Mimofunkční charakteristiky její základní koncepty jsou popsány v [16]. V této části jsou popsána výsledná rozšíření CoSi v rámci diplomové práce. 5.2.1 Obecné principy Jedním z hlavních cílů diplomové práce je rozšíření existujícího CoSi frameworku o možnost rozšíření popisu komponent a jejich služeb o mimofunkční charakteristiky. Jak bylo ukázáno v kapitole 4, koncept EFP není zatím ustálený a neexistují jednotné definice a návody, jak EFP začlenit do modelu. Protože jednou z hlavních předností CoSi je jednoduchost, nesmí zavedení EFP do modelu vnést složité nebo těžko pochopitelné postupy. Další z předností modelu je obecnost - protože je model CoSi primárně používaný při výzkumu nahraditelnosti komponent, měly by být výsledné EFP schopné zachytit co nejširší rozsah charakteristik používaných v různých doménách od real-time embedded systémů (což je doména například SaveCCM - viz sekce 3.4) až po charakteristiky rozsáhlých enterprise systémů (což je doména EJB - viz sekce 3.2). Z použití při výzkumu nahraditelnosti také vyplývá, že by EFP měly být strojově čitelné a analyzovatelné, aby bylo možné proces výběru vhodných komponent na základě EFP automatizovat. Strojová čitelnost je také důležitá pro verifikaci EFP. 5.2.2 Definice EFP Model EFP pro CoSi vychází z konceptů uvedených v kapitole 4. Navržené mimofunkční charakteristiky tak přibližně odpovídají jakostním atributům v CQM [2] nebo non-functional attributes v jazyce NoFun [12]. Na rozdíl od NoFun současný model EFP v CoSi obsahuje pouze charakteristiky odpovídající základním charakteristikám, ale s odvozenými charakteristikami se počítá až v další verzi modelu (viz [16]). Následováním myšlenky jednoduchosti byla definována mimofunkční charakteristika jako dvojice (jméno, typ). Jméno značí název charakteristiky (například response_time) a typ pak možné hodnoty, které popisují charakteristiku. K popisu je možné použít skalární typy (boolean, integer, float, token a string) nebo komplexní typy (enumeration, interval a map). Skalární typy boolean, integer a float slouží k popisu měřitelných hodnot charakteristik. Typ token odpovídá identifikátoru může vyjadřovat jednoduché vlastnosti komponenty jako například typ zabezpečení (ssl). Typ enumeration je výčtem skalárních typů stejné hodnoty a může popisovat například to, jaké komponenta podporuje ope- 35 Mimofunkční charakteristiky rační systémy (windows, unix, macos). Interval je použitelný například pro specifikaci minimální požadované paměti (memory > 10) a poslední typ map, který je definovaný jako dvojice (identifikátor, hodnota), může vyjadřovat například náročnost komponenty na systémové zdroje (disk.space = 10, memory = 50). Podrobněji jsou tyto typy popsány ve specifikaci CoSi v sekci 6.1.2. 5.2.3 Registr EFP Definice EFP sice stačí k popisu charakteristiky, ale je žádoucí, aby se stejnými charakteristikami bylo nakládáno stejně. Představme si charakteristiku popisující použitý algoritmus šifrování dat, která může nabývat hodnot rsa, des, 3des, blowfish, seal a rc4. Mechanismus, který umožní specifikovat, jakých hodnot může konkrétní charakteristika nabývat, se v CoSi nazývá registr mimofunkčních charakteristik. Tento registr obsahuje informace o charakteristice uvedené v sekci 5.2.2 (jméno, typ) spolu s dodatečnými informacemi, jako jsou konkrétní hodnoty, kterých může charakteristika nabývat. Použití takové charakteristiky při specifikaci komponenty vyžaduje uvést pouze její jméno, což je v souladu s požadavkem na jednoduchost. Zjednodušeně by se dalo říci, že registr obsahuje kompletní definice všech použitelných typů charakteristik. Syntax a použití registru je více popsáno v sekci 6.1.3. 5.2.4 Popis EFP Stejně jako NoFun umožňuje CoSi specifikovat EFP pro jednotlivé komponenty nebo pouze pro jejich část (v současné verzi CoSi je možné specifikovat EFP pro služby komponenty, ale v budoucích verzích CoSi se počítá i s možností specifikovat EFP pro další vlastnosti komponenty). Protože CoSi používá pro popis komponent soubor manifestu komponenty (viz sekce 6.1.2), bylo logickým krokem začlenit popis EFP přímo do tohoto souboru. To, jestli je charakteristika svázána s celou komponentou nebo s konkretní službou, nazýváme kontext charakteristiky. Pro charakteristiky svázané s konkrétní službou rozeznáváme vyžadované EFP a poskytované EFP. Dále potřebujeme znát název a hodnotu charakteristiky, pro které platí následující pravidla: 1. Název charakteristiky se musí shodovat s jedním z názvů obsažených v registru EFP (viz výše). 36 Balíky komponent 2. Konkrétní hodnota charakteristiky musí být stejného typu, jaký je pro danou charakteristiku definován v registru, a zároveň musí nabývat pouze hodnot definovaných na stejném místě. 3. Hodnota poskytovaných EFP může být vyjádřena pouze operátorem „rovná se“ (například memory = 10) a hodnota vyžadovaných EFP může být vyjádřena navíc operátory porovnání (<, >, ≤, ≥). Charakteristika splňující body uvedené výše se nazývá validní. Příklady a syntax popisu EFP v manifestu komponenty jsou popsány v definici hlavičky Bundle-Extrafunc sekce 6.1.2. 5.2.5 Porovnávání EFP Jedním ze způsobů, jak v CoSi skládat komponenty, je specifikování poskytovaných a vyžadovaných a služeb (viz sekce 6.1.1). Služby jsou následně navazovány podle jejich verze, jména, atd. S uvedením EFP do CoSi přibyla povinnost zabývat se při navazování služeb i těmito charakteristikami. Aby bylo možné vyhodnotit, zda poskytované EFP jedné služby vyhovují vyžadovaným EFP druhé služby (a tím je možné služby navázat), je nutné definovat proces porovnávání mimofunkčních charakteristik. Na vyžadované EFP se můžeme dívat jako na množinu vlastností, které musí služba splnit. Proces porovnávání EFP použije operátor svázaný s hodnotou vyžadované EFP na hodnotu poskytované EFP. Pokud je výsledek operace vyhodnocen jako pravda, lze dané služby navázat. 5.3 Balíky komponent V případě návrhu rozšíření CoSi o balík komponent (v rámci diplomové práce) se nabízelo několik možností, jak k problematice přistupovat. Jednou z možností bylo přeměnit CoSi v hierarchický komponentový model (jako je například SOFA [7]) a zacházet s balíky komponent stejně jako s komponentami. Druhou možností bylo adoptovat princip použitý například v SaveCCM (viz sekce 3.4) – zacházet s balíky komponent jako s jednotkou deploymentu, bez většího runtime významu balíků. V rámci zachování jednoduchosti komponentového modelu CoSi byla vybrána druhá možnost. Balíky komponent nazýváme (stejně jako v SaveCCM) assemblies4 a mají následující 4 jeden balík komponent se nazývá assembly 37 Další rozšíření modelu vlastnosti: ∙ umožňují sdružovat komponenty do celků ∙ umožňují specifikovat vazby mezi komponentami obsaženými uvnitř balíku ∙ jsou ploché - mohou obsahovat pouze komponenty, ne balíky komponent ∙ jsou jednotkou deploymentu, instalace a odinstalace ∙ umožňují specifikovat pořadí deploymentu komponent obsažených v balíku ∙ nejsou s nimi svázány mimofunkční charakteristiky ∙ je povoleno specifikovat a navazovat vazby mezi komponentami uvnitř a vně balíku Podrobněji jsou balíky komponent popsány v částech 6.1 a 6.2. 5.4 Další rozšíření modelu 5.4.1 Poskytované a vyžadované balíky Java tříd Kromě mimofunkčních charakteristik a balíku komponent byl model CoSi v rámci práce rozšířen ještě o možnost specifikování poskytovaných (vyžadovaných) balíků (viz sekce 6.1.2). V tomto kontextu balíky nejsou balíky komponent, ale klasické Java packages. Rozšíření má primárně za úkol zjednodušit a zkrátit zápis poskytovaných typů u komponent. Poskytnutí balíku komponenty odpovídá exportování všech veřejných tříd v daném balíku, které nejsou exportovány jiným způsobem (například jako služby). Vyžadování balíku v CoSi znamená, že komponenta předpokládá, že jsou k dispozici veškeré třídy tohoto balíku. Toto rozšíření ale není doporučeno používat, protože by mohlo vést k chybám za běhu kontejneru. Představme si scénář, kdy autor komponenty vyžaduje nějaký konkrétní balík. V době vytváření komponenty má k dispozici komponentu exportující tento balík a používá třídy z tohoto balíku. Autorova komponenta je následně nasazena do systému, ve kterém je jiná verze komponenty exportující balík s pozměněnými (nebo chybějícími) třídami, než se kterou počítal autor při vytváření své komponenty. Pokud autor specifikuje pouze importování balíku bez udané verze, toto navázání může způsobit chyby za běhu komponenty (například nenalezení třídy z balíku). Pokud chce autor komponenty i přesto použít importování balíku, měl by vždy uvést verzi importovaného balíku. Toto chování ale není vynucené kontejnerem ani specifikací. 38 Další rozšíření modelu 5.4.2 Dependency injection Při návrhu balíku komponent vyvstala otázka, jak vyjádřit propojení komponent uvnitř balíku a zároveň jak zamezit, aby se komponenty propojily s jinými komponentami, než bylo autorem balíku zamýšleno (například s komponentami již přítomnými v kontejneru). K tomuto účelu byl v CoSi přijat princip dependency injection (DI), konkrétně forma DI setter injection (viz sekce 3.5), používaná hlavně v kontejneru Spring nebo PicoContainer. Jedná se o injektování instance služby použitím přesně definovaného setteru - metody, která má jako jediný parametr rozhraní služby a ve svém těle pouze uloží instanci rozhraní služby do předem připravené proměnné. Každá komponenta v CoSi může u poskytované služby uvést také její jméno (atribut name). Pokud komponenta vyžaduje nějakou službu (a zná předem její konkrétní jméno), je obvyklé, že bude také potřebovat instanci této služby. V původní verzi CoSi musel autor komponenty v jejím kódu zavolat metodu kontejneru pro získání instance této služby a až pak mohl službu používat. Zavedením atributu autowire u pojmenovaných služeb dává kontejner možnost autorovi komponenty napsat pouze kód setteru této služby a o více se nestarat. Kontejner před spuštěním komponenty injektuje instanci této služby do předem připravené proměnné této komponenty zavoláním setteru. Předpoklady pro použití DI pro navázání dvou služeb komponent jsou následující: ∙ komponenta poskytující službu musí mít v manifestu služby v hlavičce ProvideServices vyplněný atribut name ∙ komponenta vyžadující službu musí mít v manifestu služby v hlavičce RequireServices vyplněný atribut name a zároveň musí mít u služby uvedený atribut autowire=“true“ ∙ komponenta vyžadující službu musí v aktivátoru komponenty implementovat metodu setteru služby Příklad hlavičky Require-Services pro vyžadovanou službu registry: Require - S e r v i c e s : cz . zcu . fav . kiv . s e n s o r r e g i s t r y . S e n s o r R e g i s t r y ; name =" r e g i s t r y "; a u t o w i r e =" true " Setter služby Setter služby má jediný parametr - rozhraní injektované služby. Kontejner zavolá tento setter po vytvoření instance aktivátoru komponenty a před spuštěním komponenty. Název 39 Další rozšíření modelu setteru služby vychází z parametru name a musí mít přesně definované jméno, aby kontejner věděl, jakou metodu má pro injektování dané služby použít. Jméno setteru se skládá ze slova „inject“ následovaného názvem služby (atribut name), u kterého se změní počáteční písmeno na velké (pokud již velké nebylo). Pro službu s name=“registry“ by kód setteru vypadal následovně: private SensorRegistry service ; public void i n j e c t R e g i s t r y ( S e n s o r R e g i s t r y s e r v i c e ) { this . service = service ; } Pro použití setteru může aktivátor komponenty obsahovat privátní proměnnou, do které se instance služby uloží. V tomto příkladu je to proměnná service. Je i možné nechat tělo této metody prázdné a nijak s instancí služby nepracovat. Pokud je však uvedený atribut autowire=“true“, musí aktivátor komponenty metodu obsahovat. 40 6 Specifikace CoSi Specifikace CoSi je formální specifikací pro implementaci obecně použitelného Java frameworku, jehož hlavním cílem je umožnit vývoj a běh aplikací založených na komponentách. Vychází ze specifikace OSGi (viz sekce 3.3), přebírá z ní některé prvky a některé zcela vypouští. V této kapitole je popsána část specifikace CoSi ve verzi 2.0, která je výsledkem rozšíření navržených a implementovaných v této diplomové práci. Předchozí verze specifikace nalezneme v [6] a [22]. Specifikace se skládá z popisu: ∙ komponent - co je to komponenta, co obsahuje, jak napsat komponentu a jak může komponenta komunikovat s ostatními komponentami. ∙ kontejneru - kontejner zajišťuje běhové prostředí pro všechny komponenty. Do kontejneru jsou komponenty nahrávány a probíhá tam jejich životní cyklus. V kontejneru jsou všechny dostupné informace o komponentách. Komponenty zde mohou také získávat odkazy na služby. ∙ základních služeb - implementace kontejneru musí poskytovat tři základní služby. Tyto služby zajišťují jednoduché API kontejneru, komunikaci komponent na základě zasílání zpráv a možnost použit mimofunkční charakteristiky při popisu komponent. Uvedené části spolu se souborem pravidel uvedeným dále v této kapitole představují framework. Framework umožňuje vytváření aplikací založených skládání nezávislých komponent a poskytuje prostředky pro jejich instalaci, provázání, spuštění a odinstalaci. Všechny tyto úkony lze provádět za běhu systému (dynamicky) bez nutnosti systém rekompilovat nebo zastavovat. 6.1 Komponentový model CoSi Tato sekce popisuje specifikaci jádra CoSi - komponentového modelu. Dává přehled o komponentách a jejich popisu, třídách, které obsahují, jejích meta-datech a distribuční 41 Komponentový model CoSi formě. Také se zaměřuje na balíky komponent. Součástí specifikace komponentového modelu je i popis meta-modelu CoSi (viz sekce 5.1.1) a popis obsahu komponenty (viz sekce 5.1.2). 6.1.1 Obsah komponenty - podrobněji Tato sekce je pokračováním sekce 5.1.2. Specifikuje podrobněji vlastnosti komponenty. Aktivátor komponenty Na rozdíl od OSGi je každá komponenta v CoSi je považována za spustitelnou (runnable). To ukládá komponentě povinnost v jedné ze svých tříd implementovat rozhraní BundleControl (viz sekce 6.3.1), které obsahuje metody start() a stop(). Právě těmito metodami kontejner startuje a zastavuje všechny komponenty integrované do systému. To, které třída implementuje rozhraní BundleControl, je specifikováno v povinné hlavičce manifestu Control-Class. Kontejner nenainstaluje komponentu, která neobsahuje některou z povinných hlaviček. Služby Jedním ze způsobů komunikace komponent v CoSi jsou služby. Služba je část komponenty, která je zveřejněna okolí a poskytuje funkčnost. Systém služeb je úzce provázaný z životním cyklem komponent (viz sekce 6.2). O správu služeb se stará registr služeb, do kterého se služby registrují ve formě rozhraní, pod kterým vystupuje Java objekt služby. Komponenty mohou do toho registru registrovat své služby nebo hledat služby obsažené v registru, které chtějí používat. Služba je v CoSi definována jejím rozhraním a implementována jako instance služby. Instance služby je vlastněna a běží uvnitř komponenty, která službu zaregistrovala. Aby bylo možné instanci služby používat, musí být zaregistrována v registru služeb. Tím je zajištěno, že sdílení služeb je kontrolováno kontejnerem. Kontejner také spravuje závislosti mezi komponentami poskytujícími služby a komponentami používajícími služby. Kontejner například zajišťuje, že komponenta nebude zastavena, pokud jiné komponenty využívají její služby. Stejně tak nebude komponenta spuštěna, pokud vyžaduje nějakou službu, ale tato služba není přítomná v registru služeb. 42 Komponentový model CoSi ServiceReference Každá služba má v registru služeb přiřazený objekt typu ServiceReference, který obsahuje vlastnosti služby a její další meta-data. Komponenty používají tyto objekty pro zjištění informací o registrovaných službách nebo k tomu, aby vybraly nejvhodnější službu pro jejich potřeby. Tímto způsobem mohou komponenty zjišťovat informace o službách, aniž by se vytvářely vazby mezi komponentami. To je vhodné, pokud komponenta potřebuje informace o službě, ne však její instanci. Pokud komponenta přece jen potřebuje instanci služby, může o ni požádat kontejner zavoláním metody BundleContext.getService(ServiceReference). Rozhraní služby Rozhraní služby je specifikace veřejných metod této služby. Programátoři vytvářejí instance služeb z objektů implementujících rozhraní služby a registrují tyto objekty v registru služeb. Metody instance služby jsou pak přístupné voláním metod rozhraní služby. Ostatní komponenty mají přístup pouze k tomuto rozhraní, typ instance služby je před nimi skrytý. Poskytované (exportované) služby musí být uvedeny v manifestu komponenty (viz sekce 6.1.2) v hlavičce Provide-Services, vyžadované (importované) služby pak v hlavičce Require-Services. Typy Složitější služby často musejí spolu s typem rozhraní exportovat i další doplňkové typy. Představme si, že exportujeme službu, která získává data z databáze. Ty ale mají svůj speciální formát (speciálního typ). Tento typ je proto také součástí služby a je nutné ho exportovat, aby bylo možné službu využívat. Exportované typy jsou specifikovány v hlavičce manifestu Provide-Types, importované pak v hlavičce Require-Types. Kontejner zajistí, že komponenta nebude nainstalována, pokud v systému nejsou přítomny typy, které komponenta importuje. Události Dalším typem komunikace mezi komponentami je systém událostí (zpráv). Framework poskytuje standardní službu MessageService, která se o posílání a přijímání zpráv stará. Zpráva je Java objekt dědící od abstraktní třídy Message, kterou poskytuje kontejner. Každá komponenta, která chce využívat systém zpráv, musí importovat typ Message a službu MessageService. Zpráva je identifikována svým jménem, což je jednoduchý 43 Komponentový model CoSi řetězec, který zprávu zároveň popisuje, a typem. Typ zprávy je jméno třídy komponenty implementující třídu Message. Dále zpráva obsahuje vlastnosti - soubor párů klíčhodnota, který upřesňuje informace o zprávě. Aby mohla komponenta odeslat zprávu, musí uvést její typ a jméno v hlavičce manifestu Generate-Events, vytvořit za běhu instanci zprávy, získat instanci služby MessageService a následně poslat zprávu metodou sendMessage() nebo postMessage(). Metoda sendMessage() je blokující (synchronní) - volání metody neskončí, dokud všichni příjemci zprávu neobdrží. Metoda postMessage() je pak asynchronní - volání metody pouze předá zprávu službě MessageService a ihned potom dochází k návratu z volání metody. Služba MessageService zajistí, že zpráva dorazí ke všem příjemcům. K přijímání zpráv musí komponenta zaregistrovat do služby MessageService příjemce zprávy. Příjemce je třída komponenty, která implementuje rozhraní MessageConsumer poskytované kontejnerem. Rozhraní obsahuje metodu recieveMessage(), která je volána kontejnerem při odesílání zpráv a metodu getAcceptedMessage(), ve které programátor specifikuje jména zpráv, které má příjemce dostávat. V hlavičce manifestu Consume-Events pak komponenta specifikuje (uvedením jména a typu zprávy), jaké zprávy je schopná přijímat. Atributy Posledním typem komunikace mezi komponentami jsou atributy. Atribut je libovolná hodnota identifikovaná řetězcem - klíčem a může být buď poskytován (exportován), nebo vyžadován (importován). Existuje stejný vztah mezi vyžadovanými a poskytovanými atributy jako mezi vyžadovanými a poskytovanými službami - kontejner nedovolí instalaci komponenty, která importuje určitý atribut, pokud v systému neexistuje komponenta, která daný atribut exportuje. Rozdíl mezi zprávami a atributy je ten, že zprávy představují událost v čase, zatímco atributy představují hodnoty čtené komponentami. Atributy můžeme použít k dynamické konfiguraci aplikace. Za běhu systému je mohou komponenty číst a podle toho reagovat. Atributy se v čase mohou měnit, takže by komponenta neměla spoléhat na jediné přečtení atributu, ale měla by ho přečíst pokaždé, když s ním chce pracovat. V kontejneru je k dispozici seznam všech atributů a je přístupný komponentám přes třídu BundleContext. Tato třída je předávána aktivátoru komponenty a pro 44 Komponentový model CoSi práci s atributy nabízí metody setAttributeValue(String, Object) a getAttributeValue(String). Pouze komponenta, která exportuje atribut (uvádí ho v hlavičce manifestu Provide-Attribute) má právo do atributu zapisovat metodou setAttributeValue(). Číst atribut a získat jeho hodnotu metodou getAttributeValue() může jen komponenta, která atribut importuje (uvádí ho v hlavičce manifestu Require-Attributes). 6.1.2 Specifikace komponenty - soubor manifest.mf V této sekci popíšeme obecné principy hlaviček manifestu komponenty a více se zaměříme na nově přidané nebo upravené hlavičky. Úplný popis ostatních hlaviček manifestu nalezneme v příloze 8. Manifest komponenty se skládá z většího počtu hlaviček. Každá hlavička má svoje jméno a hodnotu, oddělené dvojtečkou, a případně ještě atributy (nepovinné, není-li uvedeno jinak). Příklad hlavičky: Bundle - Name : E x t r a f u n c R e g i s t r y B u n d l e „Bundle-Name“ je název hlavičky, „ExtrafuncRegistryBundle“ je pak hodnota hlavičky. V hodnotách hlaviček se často objevují definice verzí nebo intervalu verzí. Syntax těchto definicí je přejatý z OSGi [20]. Zadefinujeme, jak se elementy verzí zapisují v manifestu: Version Verze se skládá z hlavního (major), vedlejšího (minor) a doplňujícího čísla (micro). Tato čísla jsou oddělena tečkou. Volitelnou čtvrtou částí je popis verze (qualifier) - textový doplněk verze oddělený uvedený za doplňujícím číslem a oddělený od něho tečkou. Verze nesmí obsahovat žádné tzv. bílé znaky1 . Implicitní hodnota verze je 0.0.0. Pro porovnání dvou verzí se používá standardní metoda Javy String.compareTo(), takže například výraz „1.1.0.build-05“ < „1.1.0.build-3“ je vyhodnocený jako „true“. Version Range Version range (výraz version-range) představuje interval verzí. Může se vyskytnout například v hlavičkách vyžadovaných typů nebo služeb. Pokud je výraz version-range zapsaný jako pouze jedna verze, je interpretovaný jako rozsah verzí [verze, ∞). Pokud 1 bílé znaky jsou například mezera, tabulátor, atd. 45 Komponentový model CoSi není rozsah specifikován, je použita implicitní hodnota 0, která je namapována na rozsah [0.0.0,∞). Element version-range musí být uzavřen do uvozovek, protože obsahuje čárku, která by mohla kolidovat s ostatními definicemi. Příklady (hodnota - význam): [1.2.3 , [1.2.3 , (1.2.3 , (1.2.3 , 1 . 2 .3 4.5.6) 4.5.6] 4.5.6) 4.5.6] 1.2.3 1.2.3 1.2.3 1.2.3 1.2.3 <= x < 4.5.6 <= x <= 4.5.6 < x < 4.5.6 < x <= 4.5.6 <= x Následující text popisuje hlavičky, které se mohou vyskytnout v manifestu komponenty a jsou pozměněny od předchozí verze CoSi nebo jsou nově přidány. Hlavičky Control-Class, Bundle-Name, Bundle-Version, Cosi-Version, Bundle-Description, Bundle-Provides, Bundle-Classpath, Generate-Events, Consume-Events, Provide-Attributes a Require-Attributes jsou podrobně popsány v příloze 8. Require-Services Definice vyžadovaných (importovaných) služeb. Je možné specifikovat další parametry, které jsou použity při hledání vyžadované služby. Podporovány jsou atributy: ∙ versionrange - Rozmezí verzí, které musí mít exportované rozhraní. Implicitní hodnota atributu je [0.0.0,∞). Syntax odpovídá výrazu version-range . ∙ name - Jméno exportující služby. Pokud je v systému více služeb implementujících stejné rozhraní, jsou rozlišeny tímto jménem. ∙ bundle-name - Jméno exportující komponenty. ∙ bundle-provider - Jméno výrobce exportující komponenty. ∙ bundle-versionrange - Rozmezí verzí, které musí mít exportující komponenta, aby byla vybrána. Implicitní hodnota je [0.0.0,∞). ∙ autowire - Může nabývat hodnot „true“ nebo „false“. Určuje, zda se má automaticky injektovat objekt instance služby do předpřipravené proměnné uvnitř aktivátoru komponenty (viz sekce 5.4.2). Pokud je atribut autowire přítomný, musí být vyplněn také atribut name. 46 Komponentový model CoSi Příklad definice: Require - S e r v i c e s : cz . zcu . fav . kiv . c a l c o u t p u t . C a l c O u t p u t P o r t s ; name =" c a l c o u t "; a u t o w i r e =" true " , cz . zcu . fav . kiv . u p d a t e s t a t e . U p d a t e S t a t e P o r t s Provide-Services Definice poskytovaných (exportovaných) služeb. Je možné specifikovat další parametry pro poskytovanou službu. Tyto parametry ale nejsou povinné. ∙ version - Verze poskytované služby. ∙ name - Jméno poskytované služby. Pokud je specifikováno, nesmí kolidovat se jménem jiné služby implementujícím stejné rozhraní. Jméno služby je nutné specifikovat, pokud chceme, aby bylo možné v komponentách importujících službu použít dependency injection (viz sekce 5.4.2). Framework přiřadí každé poskytované službě následující atributy (které se nesmí objevit definici poskytované služby): ∙ bundle-name - Jméno exportující komponenty. ∙ bundle-version - Verze komponenty poskytující službu. Příklad definice: Provide - S e r v i c e s : cz . zcu . fav . kiv . t e s t b u n d l e 1 . T e s t S e r v i c e ; v e r s i o n = 1 . 2 . 3 ; name =" T e s t S e r v i c e " Provide-Types Specifikuje typy exportované komponentou. Překrývá hlavičku Provide-Packages. Definice exportovaných typů má stejný syntax jako definice exportovaných služeb. Rozdílem je, že typy jsou třídy, které exportující komponenta chce zpřístupnit ostatním komponentám, ale nemohou být interpretovány jako služby. 47 Komponentový model CoSi Require-Types Specifikuje typy importované komponentou. Překrývá hlavičku Require-Packages. Definice importovaných typů má stejný syntax jako definice importovaných služeb. Rozdílem je, že typy jsou třídy, které exportující komponenta chce zpřístupnit ostatním komponentám, ale nemohou být interpretovány jako služby. Pokud je komponenta poskytující importovaný typ v systému, je tento typ přidán do class path importující komponenty. Provide-Packages Specifikuje, jaké balíky tříd jsou exportovány komponentou. Znamená to, že všechny veřejné třídy obsažené v komponentě v balících definovaných v této hlavičce jsou zpřístupněny pro ostatní komponenty, pokud již nejsou zpřístupněny v hlavičkách ProvideTypes nebo Provide-Services. Parametry, které mohou být specifikovány pro poskytované balíky: version - Nepovinný parametr. Specifikuje verzi balíku poskytovaného komponentou. Require-Packages Specifikuje, jaké balíky jsou importovány komponentou (viz sekce 5.4.1). Sémantika hlavičky je taková, že všechny veřejné třídy obsažené v uvedených balících jsou požadovány komponentou, pokud nejsou explicitně uvedeny v hlavičce Require-Types nebo Require-Services. Tyto balíky musí být explicitně exportovány nějakou z komponent v systému. Parametry, které mohou být specifikovány pro požadované balíky: versionrange - Nepovinný parametr. Specifikuje rozmezí verzí importovaného balíku. Implicitní hodnota je [0.0.0,∞). Bundle-Extrafunc Od verze 2 umožňuje CoSi specifikovat mimofunkční charakteristiky (EFP) pro komponentu nebo pro některou z jejích vlastností (viz sekce 5.1.1). Hlavička pro EFP platné pro celou komponentu má následující syntax: Bundle-ExtraFunc: <name>=<value>, ... Pro EFP platné pro jednu vlastnost je použit syntax OSGi atributů: <manifest-header>: <object>;extrafunc=(<name>=<value>, ...) 48 Komponentový model CoSi Typ boolean integer float token string Formát hodnot true|Y, false|N 32-bitový integer se znaménkem 64-bitový IEEE formát float identifikátor s pomlčkami a podtržítky textový řetězec v dvojitých uvozovkách Poznámka Příklady hodnot Y -20471 je vyžadována desetinná tečka Viz 1.3.2 v [20] 0.57 25.3 embedded firstline "That’s all, folks“ Tabulka 6.1: Typy EFP - skalární Typ enum<type> Formát hodnot { value [, value, . . . ] } Poznámka Hodnoty musí být stejného skalárního typu. map { identifier: value, . . . Hodnoty nemusí být } tejného skalárního typu. interval<type> (valmin ,[valmean ],valmax ] Hodnoty musí být stejného typu. Je použita OSGi notace pro interval. Příklady hodnot { start, stop, unknown } { memory: low, security: C2 } (0.5,1.0] [small, large, xxlarge] Tabulka 6.2: Typy EFP - komplexní Příklady: Bundle - E x t r a F u n c : m e m o r y =32 , s e c u r i t y ={ basic , d i g e s t } , gc - model =" eager v3 " , p r e c i s i o n ={ n o r m a l :"0.01" , high : " 0 . 0 0 0 1 " } Export - S e r v i c e s : cz . zcu . fav . kiv . s e n s o r r e g i s t r y . S e n s o r R e g i s t r y ; e x t r a f u n c =( s a m p l i n g r a t e <20 , precision = normal ) Import - S e r v i c e s : com . acme . a c t u a t o r . T e m p A c t u a t o r ; e x t r a f u n c =( refresh < = 4 0 0 ) , com . other . comm . 49 Komponentový model CoSi S e c u r e D a t a T r a n s f e r e r ; v e r s i o n ="[1.0 , 2.3) "; e x t r a f u n c =( s e c u r i t y . method ,{ ssl , tls }) , perf . d e l i v e r y _ t i m e <=10) Typy EFP dělíme na skalární a komplexní. Všechny možné typy EFP jsou popsány v tabulkách 6.1 a 6.2. Všechny tyto hlavičky a atributy předpokládají, že existuje registr EFP (viz sekce 6.1.3). Extrafunc-Catalog Tato hlavička specifikuje umístění (URI) katalogu registu mimofunkčních charakteristik (viz sekce 6.1.3). V současné podobě má hlavička pouze informační hodnotu. V další verzi CoSi se však počítá s tím, že bude framework tento registr z daného umístění stahovat. Příklad: ExtraFunc - C a t a l o g : http :// s e r v i c e s . kiv . zcu . cz / cosi / e x t r a f u n c / v1 / Další pravidla pro soubor manifest.mf ∙ Manifest může obsahovat i další definice neuvedené ve specifikaci. Tyto definice jsou načteny, ale pouze jako řetězce bez dalšího významu (nejsou interpretovány frameworkem). Hodnoty definic jsou přístupné v meta-datech balíku. 6.1.3 Registr mimofunkčních charakteristik Pro podporu výzkumu v oblasti mimofunkčních charakteristik byl kontejner rozšířen o jednoduchý registr, který obsahuje kompletní definice všech typů použitelných mimofunkčních charakteristik. Soubor registru používá následující syntax: # c o m m e n t line < prop - def > := < name > : < type > [; < constraint >] < type > ::= see Bundle - E x t r a f u n c h e a d e r - EFP t ypes < c o nstraint > ::= < interval - constr > | < set - constr > | < map - constr > < interval - constr > ::= " <" < value > , < value > " >" < set - constr > ::= "{" < value [ , ...] "}" 50 Komponentový model CoSi < map - constr > ::= "{" < name > : < value > [ , ...] "}" Registr je v CoSi přístupný prostřednictvím standardní služby ExtrafuncRegistryService (viz sekce 6.3.2). Implicitní registr EFP je umístěn v textovém souboru extrafuncregisty v balíku cz.zcu.fav.kiv.bdcontainer.extrafuncregistry service.impl. Je možné použít i jiný soubor registru. Ve frameworku je pro to k dispozici metoda služby ExtrafuncRegistryService loadRegistryFromURL(URL). Příklad souboru registru: s e c u r i t y : enum ;{ ssl , tsl , none } p r e c i s i o n : map ;{ low , normal , high } r e s p o n s e : i n t e r v a l ;{ slow , average , fast } s a m p l i n g r a t e : i n t e g e r ; <1 ,1000 > gc - model : s t r i n g ; <" A " ," Z " > gs - model : s t r i n g ;{" one " ," two " ," three "} 6.1.4 Distribuční forma komponenty Komponenta je distribuována jako jediný JAR soubor obsahující vše potřebné pro její běh. Framework nedovoluje komponentě využívat zdroje odjinud, než z JAR archivu komponenty. Archiv komponenty musí obsahovat adresář META-INF obsahující soubor manifest.mf s informacemi o komponentě. Také musí obsahovat další povinné adresáře (viz obrázek 6.1; nepovinné adresáře jsou odděleny světlejší barvou). Kontejner kontroluje přítomnost těchto adresářů během instalace komponenty a nedovolí její zavedení do systému, pokud nejsou adresáře nalezeny. Archiv obsahuje následující adresáře: ∙ Adresář bin/ - povinný adresář, který obsahuje přeložený kód komponenty (popř. zdrojové soubory jazyka Groovy určené k interpretování). Je implicitně zahrnut do class path komponenty. ∙ Adresář lib/ - povinný adresář, který obsahuje knihovny potřebné ke správnému běhu komponenty. Tyto knihovny mohou být uloženy jako JAR archivy. Knihovny, které chce komponenta využívat, musí být uvedeny v class path komponenty. ∙ Adresář optional/ - nepovinný adresář. Může obsahovat v podstatě cokoliv. 51 Komponentový model CoSi Obrázek 6.1: Struktura archivu komponenty ∙ Adresář src/ - nepovinný adresář. Obsahuje zdrojové soubory komponenty, pokud je dodavatel chce poskytnout. ∙ Adresář doc/ - nepovinný adresář. Obsahuje dokumentaci komponenty. 6.1.5 Balík komponent CoSi (od verze 2) umožňuje sdružovat komponenty do balíků komponent (assemblies). Balíky jsou jednotkou instalace, odinstalace a nasazení. Sdružují více komponent, které se mohou vzájemně doplňovat a poskytovat tak funkcionalitu jako celek. Usnadňuje také zavedení komponent do systému. Balík komponent obsahuje pouze komponenty a soubor manifestu. 6.1.6 Specifikace balíku komponent - soubor manifest.mf V této sekci popíšeme manifest balíku komponent. Soubor manifestu se skládá z hlaviček. Každá hlavička má své jméno a hodnotu, oddělené dvojtečkou. Definice verzí u hodnot hlaviček mají stejný syntax jako definice verzí u komponent (viz sekce 6.1.2). Následující text popisuje hlavičky, které se mohou vyskytnout v manifestu balíku komponent. 52 Komponentový model CoSi Assembly-Name Specifikuje jméno balíku komponent. Assembly-Version Specifikuje verzi balíku komponent. Pokud dva balíky komponent obsahují různé verze stejných komponent, musí se verze těchto balíků lišit. Příklad: Assembly - V e r s i o n : 1 . 2 . 3 . build -6 Assembly-Provider Specifikuje poskytovatele balíku komponent. Assembly-Description Specifikuje krátký textový popis obsahu balíku. Assembly-Bundles Obsahuje jména komponent, které jsou obsažené v balíku komponent. Má následující syntax: Assembly - B u n d l e s : bundle - name , bundle - name , ... Pro hodnoty této hlavičky platí následující pravidla: ∙ bundle-name je jméno komponenty odpovídající hodnotě hlavičky manifestu komponenty Bundle-Name, ∙ bundle-name musí odkazovat na komponentu fyzicky přítomnou v balíku komponent. Pokud není komponenta přítomná v balíku, je tento záznam ignorován, i když je balík se stejným názvem přítomen v systému. ∙ Komponenty uvnitř balíku jsou při instalaci vyhodnocovány v pořadí uvedeném v této hlavičce. Příklad: 53 Komponentový model CoSi Assembly - B u n d l e s : S a v e C C M C o r e , U p d a t e S t a t e , C alcOutput , S p e e d C o n t r o l l e r , D i s t a n c e G e n e r a t o r , Printer , A C C A p p Assembly-Wiring V balíku komponent je možné explicitně nadefinovat provázání mezi službami komponent (atribut autowire u služeb). Toto provázání je definováno v hlavičce AssemblyWiring. Provázání existuje proto, aby bylo zajištěno správné navázání služby komponenty uvnitř balíku komponent na službu komponenty uvnitř balíku a ne na službu jiné komponenty stejného jména, která je přítomná v kontejneru. Hlavička má následující syntax: Assembly - W i r i n g : bundle - name . e l e m e n t to bundle name . element , bundle - name . e l e m e n t to bundle name . element , ... Platí následující pravidla: ∙ bundle-name odpovídá jménu komponenty a musí být zastoupeno také v hlavičce Assembly-Bundles ∙ element odpovídá jménu služby (atributu name u služby v sekci ProvidedServices nebo Required-Services) ∙ jedna strana provázání musí být exportovaná a druhá strana importovaná služba. Není explicitně uvedeno pořadí, ve kterém by měly být služby uvedeny, ale je doporučeno uvádět služby v pořadí „importovaná to exportovaná“. ∙ importovaná služba musí mít nastavený atribut autowired=“true“ Pokud některé z těchto pravidel není splněno, kontejner příslušné spojení nevytvoří. 6.1.7 Distribuční forma balíku komponent Balík komponent je distribuován ve formě jednoho JAR archivu obsahujícího: ∙ Adresář META-INF se souborem manifest.mf, který obsahuje informace o balíku komponent. 54 Životní cyklus komponenty a balíku komponent ∙ JAR archivy s komponentami přímo v kořenovém adresáři balíku. Pokud není soubor manifestu kontejnerem nalezen, není balík komponent zaveden do systému. 6.2 Životní cyklus komponenty a balíku komponent Životní cyklus komponenty v CoSi vychází z životního cyklu komponenty v OSGi. Je to proces od počátku existence komponenty v systému do jejího odstranění. Proces začíná nainstalováním komponenty. Pokračuje ověřením jejích závislostí a korektnosti. Tento proces se nazývá vyhodnocení komponenty. Po úspěšném vyhodnocení může být komponenta spuštěna. Není-li jí dále potřeba, může být zastavena nebo odinstalována ze systému. Komponenta také může být deaktivována - uvedena do stavu těsně po nainstalování. Rozeznáváme následující stavy (viz obrázek 6.2): ∙ INSTALLED - komponenta byla úspěšně nainstalována nebo deaktivována. Deaktivace se provádí například při odinstalaci balíku komponent ze systému nebo pokud potřebujeme komponentu přesunout z aktivního stavu do stavu před vyhodnocením. ∙ RESOLVED - komponenta je vyhodnocena - všechny závislosti vyžadované komponentou jsou přítomny v systému. V tomto stavu se nachází komponenta, která je zastavena nebo připravena na nastartování. ∙ STARTING - komponenta je startována. Byla vyvolána metoda start() rozhraní BundleControl a volání této metody nebylo ještě ukončeno. ∙ STARTED - komponenta byla úspěšně nastartována a běží. ∙ STOPPING - komponenta je zastavována. Byla vyvolána metoda stop() rozhraní BundleControl a volání této metody nebylo ještě ukončeno. ∙ UNINSTALLED - komponenta byla odinstalována ze systému. Tento stav je konečný - nemůže z něj být přesunuta do jiného stavu. Pro balík komponent rozeznáváme pouze dva stavy (viz obrázek 6.3): 55 Životní cyklus komponenty a balíku komponent Obrázek 6.2: Životní cyklus komponenty v CoSi ∙ INSTALLED - balík komponent byl nainstalován. Byla úspěšně vyvolána metoda installAssembly() rozhraní Assembly. To, že je balík komponent ve stavu INSTALLED neznamená, že byly do systému úspěšně nainstalovány veškeré komponenty, které balík obsahuje. Znamená to pouze, že byl úspěšně načten JAR soubor balíku aa přečten jeho manifest. To znamená, že balík je syntakticky v pořádku a může následovat vyhodnocování jeho komponent. ∙ UNINSTALLED - v tomto stavu se může balík komponent nacházet, pokud 1. byl balík načten do systému, ale ještě nebyla vyvolána metoda installAssembly() rozhraní Assembly 2. volání této metody skončilo s chybou (nelze přečíst JAR soubor balíku) 3. byl balík komponent odinstalován ze systému. V případě odinstalování balíku je stav UNINSTALLED konečný. 56 Životní cyklus komponenty a balíku komponent Obrázek 6.3: Životní cyklus balíku komponent v CoSi 6.2.1 Proces vyhodnocení komponenty Prvním krokem při instalaci komponenty je její vyhodnocení 2 . Vyhodnocení komponenty je proces, ve kterém se pro jednotlivé importy (tedy požadované typy, atributy, služby a zprávy) komponenty hledají odpovídající exportéři. Komponenty mohou na své importy ukládat omezení (constraints) ve formě například specifické verze, jména služby nebo přesného názvu poskytující komponenty. Pokud není v systému nalezena komponenta, která exportuje odpovídající položku, není komponenta vyhodnocena a zůstává ve stavu INSTALLED. Je možné se později pokusit o nové vyhodnocení komponenty, například pokud do systému přidáme další komponenty. Komponenta musí být vyhodnocena před spuštěním jakéhokoliv jiného kódu. Kontejner během vyhodnocování kontroluje: 1. jestli je komponenta ve stavu INSTALLED nebo RESOLVED. Komponenta tedy musí být korektně nainstalována, aby mohla být vyhodnocena. Již vyhodnocená komponenta může být znovu vyhodnocena. 2. jestli se v kontejneru nacházejí poskytovatelé pro všechny importované služby. Během vyhodnocování je pouze zkontrolována přítomnost služeb (balíků) v systému, nevytvářejí se žádné vazby na poskytovatele. V této části se zároveň se kontrolují EFP navázané na služby. Pokud je nalezen exportér služby, ale daná služba nesplňuje požadavky na EFP, kontejner podle konfigurace buď zaloguje chybu a dále pokračuje ve vyhodnocování, nebo zaloguje chybu a s vyhodnocováním skončí. 2v angl. jazyce je vyhodnocení komponenty označováno jako bundle resolve 57 Životní cyklus komponenty a balíku komponent 3. jestli se v kontejneru nacházejí poskytovatelé pro všechny importované typy. Stejně jako u služeb je během vyhodnocování pouze zkontrolována přítomnost typů v systému, nevytvářejí se žádné vazby na poskytovatele. 4. jestli se v kontejneru nacházejí poskytovatelé pro všechny importované balíky. Stejně jako u služeb je během vyhodnocováni pouze zkontrolována přítomnost balíků v systému, nevytvářejí se žádné vazby na poskytovatele. 5. jestli se v kontejneru nacházejí poskytovatelé pro všechny importované atributy. Stejně jako u služeb je během vyhodnocování pouze zkontrolována přítomnost atributů v systému, nevytvářejí se žádné vazby na poskytovatele. 6. jestli se v kontejneru nacházejí poskytovatelé pro všechny importované zprávy. Stejně jako u služeb je během vyhodnocování pouze zkontrolována přítomnost atributů v systému, nevytvářejí se žádné vazby na poskytovatele. 7. jestli jsou korektně exportovány poskytované atributy (jestli jsou komponentou exportovány typy atributů). Toto pravidlo může být porušeno, pokud komponenta typ atributu importuje. Také není nutné exportovat typy, které jsou součástí Boot class path (viz sekce 6.4). 8. jestli jsou korektně exportovány poskytované zprávy. Pro typy zpráv platí stejná pravidla jako pro typy atributů. 9. pokud byla komponenta před vyhodnocením ve stavu RESOLVED, tímto krokem vyhodnocení končí. Pokud byly všechny předešlé kroky úspěšně vykonány, je komponenta přesunuta do stavu RESOLVED. Pokud alespoň jeden z těchto kroků selhal, komponenta se vrátí do stavu INSTALLED a kontejner se musí vrátit do stavu, ve kterém byl před prvním (úspěšným) vyhodnocováním této komponenty. Pokud byla komponenta ve stavu INSTALLED, pokračuje proces vyhodnocování následujícími kroky: 10. Všechny zdroje komponenty (typy, služby, atributy nebo zprávy) jsou zaregistrovány do systému. 11. Komponenta je navázána na své poskytovatele. Na rozdíl od OSGi neexistuje v CoSi k tomuto účely žádný speciální objekt (wire), takže navázání je možné implementovat jako seznam exportů a importů. 12. Je vytvořena instance aktivátoru komponenty. 58 Životní cyklus komponenty a balíku komponent Pokud jsou i tyto kroky úspěšné, je komponenta přesunuta do stavu RESOLVED a je připravena ke spuštění. Během kontroly služeb, atributů, zpráv a typů musí být brány v potaz omezení uložená na tyto vlastnosti, aby byla komponenta navázána na správné poskytovatele. 6.2.2 Rozhraní komponenty a balíku komponent CoSi definuje několik rozhraní úzce spjatých s komponentou, jejími vlastnostmi a s balíkem komponent. Jsou to rozhraní: ∙ Bundle - reprezentuje komponentu a poskytuje metody životního cyklu komponenty. ∙ BundleMetaData - poskytuje přístup k informacím v manifestu komponenty. ∙ ServiceReference - reprezentuje poskytovanou službu. ∙ ModuleClassLoader - obecné rozhraní pro class loader použitelný v CoSi kontejneru. ∙ Assembly - reprezentuje balík komponent ve frameworku a poskytuje metody životního cyklu balíku komponent. ∙ AssemblyMetaData - poskytuje přístup k informacím v manifestu balíku komponent. Rozhraní ServiceReference a ModuleClassLoader se od první verze CoSi nezměnila. Změny rozhraní Bundle a BundleMetaData jsou minimální. Rozhraní Assembly a AssemblyMetaData jsou v CoSi nově od verze 2. V této části popíšeme pouze rozhraní Assembly, podrobný popis všech rozhraní nalezneme v dokumentaci zdrojových kódů na přiloženém CD a v [22]. Rozhraní Assembly Objekt typu Assembly je abstrakcí balíku komponent uvnitř kontejneru. Každý balík komponent má přidělený právě jeden objekt tohoto typu. Definice rozhraní: 59 Životní cyklus komponenty a balíku komponent p u b l i c c l a s s Assembly { p u b l i c s t a t i c f i n a l i n t INSTALLED = 0 ; p u b l i c s t a t i c f i n a l i n t UNINSTALLED = 1 ; public boolean i n s t a l l A s s e m b l y ( ) ; public boolean u n i n s t a l l A s s e m b l y ( ) ; public void s t a r t A s s e m b l y ( ) ; public boolean stopAssembly ( ) ; public int getId ( ) ; public void s e t I d ( i n t id ) ; p u b l i c S t r i n g getName ( ) ; public List < Integer > getBundleIDs ( ) ; public boolean c a n E s t a b l i s h W i r e ( S t r i n g serviceNameAttribute , S t r i n g serviceNameValue ) ; public int g e t S t a t e ( ) ; public boolean i s I n s t a l l e d ( ) ; public AssemblyMetadata getAssemblyMetadata ( ) ; } Popis jednotlivých složek rozhraní: Konstanty INSTALLED a UNINSTALLED představují stavy balíku komponent (viz výše). public boolean i n s t a l l A s s e m b l y ( ) ; public boolean u n i n s t a l l A s s e m b l y ( ) ; Tyto metody kontrolují životní cyklus balíku komponent. public boolean stopAssembly ( ) ; public void s t a r t A s s e m b l y ( ) ; Pomocné metody pro nastartování a deaktivování všech komponent uvnitř balíku. Tyto metody neovlivní životní cyklus balíku komponent. public int g e t S t a t e ( ) ; public boolean i s I n s t a l l e d ( ) ; Metody pro zjištění stavu balíku komponent. public int getId ( ) ; public void s e t I d ( i n t id ) Vrátí (nastaví) jednoznačný identifikátor balíku komponent uvnitř kontejneru. 60 Kontejner p u b l i c S t r i n g getName ( ) ; Vrátí jméno archivu balíku komponent. public List < Integer > getBundleIDs ( ) ; Vrátí seznam identifikátorů komponent nainstalovaných v kontejneru patřících tomuto balíku. public boolean c a n E s t a b l i s h W i r e ( S t r i n g s e r v i c e N a m e A t t r i b u t e , S t r i n g serviceNameValue ) ; Vrátí true pokud je možné vytvořit spojení mezi dvěma službami uvedenými v manifestu balíku komponent v hlavičce Assembly-Wiring. public AssemblyMetadata getAssemblyMetadata ( ) ; Vrátí zpracovaná meta-data balíku komponent. 6.3 Kontejner Kontejner je část CoSi zodpovědná za: ∙ korektní nahrávání komponent do systému, obstarávání jejich životního cyklu (viz sekce 6.2) ∙ udržování registru služeb poskytovaných komponentami ∙ udržování seznamu a hodnot atributů poskytovaných komponentami ∙ poskytování informací o komponentách (například o jejich stavu, závislostech nebo metadatech) ∙ poskytování služby pro ovládání životního cyklu komponent ∙ poskytování služby pro posílání a přijímání zpráv ∙ poskytování služby pro podporu EFP komponent a vlastností komponent Kontejner je běhovým prostředím pro komponenty. Sám o sobě nemá žádnou funkčnost, ale umožňuje komponentám interagovat a poskytovat funkčnost okolí. Pokud komponenta A poskytuje typ, atribut nebo zprávu a v systému existuje komponenta B importující jeden z těchto elementů, musí být komponenta B nejdříve ukončena a odinstalována, aby bylo možné odinstalovat komponentu A. 61 Kontejner Tímto návrhovým omezením je zajištěno korektní běhové prostředí pro komponenty. Na rozdíl od OSGi totiž CoSi negeneruje zprávy oznamující odinstalaci služeb ostatním komponentám. Je zajištěno, že dokud komponenta běží, všechny vyžadované služby, na které komponenta získala odkaz během svého běhu, jsou přítomné v systému, dokud není na komponentě vyvolána metoda stop(). 6.3.1 Rozhraní kontejneru Ačkoliv jsou komponenty v CoSi považovány za černou skříňku (okolí nemá žádné informace o komponentě kromě toho, jaké položky exportuje), je nutné, aby mohly komponenty komunikovat s frameworkem a framework s komponentami. Tato funkcionalita je zajištěna dvěma rozhraními kontejneru. Prvním rozhraním je BundleControl, které musí být implementováno každou komponentou a je jím kontrolován životní cyklus komponenty. Druhým je rozhraní BundleContext. To je předáno každé instanci komponenty a poskytuje základní informace o prostředí komponenty. Rozhraní BundleControl Rozhraní BundleControl musí být implementováno v jedné ze tříd každé komponenty, aby mohl kontejner komponentu spustit či zastavit. Během procesu vyhodnocení komponenty kontejner vytvoří instanci třídy, která je uvedena v hlavičce manifestu komponenty Control-Class, a zkontroluje, zda třída korektně implementuje rozhraní BundleControl. Pokud je na této instanci úspěšně vyvolána metoda start(), je zajištěno, že metoda stop() bude vyvolána při zastavování komponenty na stejné instanci třídy BundleControl. Kontejner zajistí, že obě operace budou správně započaty a pokud nastane výjimka během startování nebo zastavování komponenty, bude odchycena, aby nebyly ostatní komponenty touto výjimkou ovlivněny. Kontejner zaloguje tyto výjimky, vrátí komponentu do stavu RESOLVED a odebere služby a atributy registrované komponentou ze systému. V metodě start() by komponenty měly získat odkazy na služby jiných komponent nebo zaregistrovat své služby a atributy do systému. Metoda stop() je pak místem, kde komponenta musí odregistrovat všechny své služby, atributy a zprávy. Definici rozhraní BundleControl a podrobný popis metod rozhraní nalezneme v javadoc dokumentaci zdrojového kódu. 62 Kontejner Rozhraní BundleContext Objekt kontextu komponenty slouží ke komunikaci komponenty s frameworkem, přístupu k frameworku a k přístupu ke službám registrovaným uvnitř frameworku. Komponenty registrují svoje služby a získávají služby jiných komponent právě přes toto rozhraní. Kromě toho umožňuje rozhraní BundleContext: ∙ registraci a získávání služeb ∙ získávání metadat komponent ∙ používání standardního vstupu a výstupu poskytovaného frameworkem ∙ zapisovat a číst atributy Instance rozhraní je komponentě předána v metodě start(BundleContext) rozhraní BundleControl. Ta stejná instance je pak předána komponentě asociované s tímto kontextem v metodě stop(BundleContext). Kontext komponenty by měl být používán pouze s komponentou, která je k němu asociována a neměl by být sdílený mezi komponentami. Instance kontextu je platná pouze během aktivního stavu komponenty (tzn. jen ve stavech STARTING, STARTED s STOPPING) a nemůže být použita, pokud je komponenta ve stavu jiném. Pokud je komponenta zastavena a znovu spuštěna, je s ní asociována nová instance kontextu. Definici rozhraní BundleContext a podrobný popis metod rozhraní nalezneme v javadoc dokumentaci zdrojového kódu. 6.3.2 Základní služby kontejneru Kontejner poskytuje komponentám základní API ve formě následujících služeb: ∙ SystemService je služba používaná komponentami ke kontrole životních cyklů jiných komponent a zjišťování informací o vnitřním stavu kontejneru. ∙ MessageService je služba, kterou mohou komponenty použít k zasílání a přijímání zpráv. ∙ ExtrafuncRegistryService je služba poskytující možnost používat EFP v popisu komponent. 63 Kontejner Tyto tři služby jsou vždy dostupné všem balíkům v kontejneru a jsou to první tři služby, které kontejner ihned po svém startu instaluje do systému. Služby jsou v kontejneru dostupné v podobě komponent "systembundle", "messagebundle" a "extrafuncregistrybundle". Tyto komponenty nemohou být zastaveny ostatními komponentami. SystemService Framework musí obsahovat implementaci, která registruje službu SystemService hned po startu frameworku. Služba umožňuje komponentám základní přístup k systému, instalaci a kontrolu životního cyklu ostatních komponent nebo získávat informace o závislostech komponent. Instance této služby je vždy přítomná v systému jako komponenta s id 0, je nastartována přede všemi ostatními komponentami a její hlavní funkcionalitou je delegace požadavků na služby jádra frameworku. Detailní popis metod služby se nachází v javadoc dokumentaci zdrojových kódů kontejneru. MessageService Framework musí obsahovat komponentu registrující MessageService při svém startu. Tato komponenta dostane id 1 a musí být nainstalována do systému hned po instalaci SystemService (před instalací ostatních komponent). Když chce komponenta vytvářet událost (posílat zprávu), implementuje v jedné ze svých tříd abstraktní třídu Message. Tento objekt je pak odeslán službě MessageService metodou sendMessage() nebo postMessage() a doručen všem komponentám schopným přijmout danou událost. Pokud chce komponenta zachytit událost (přijmout zprávu), musí implementovat rozhraní MessageConsumer a registrovat tuto implementaci ve službě MessageService metodou registerMessageConsumer(). Služba pak doručí událost tomuto objektu. Služba kontroluje, zda může komponenta přijmout či odeslat zprávu. Informace uvedené ve zprávě musí odpovídat informacím v manifestu komponent účastnících se odesílání nebo příjmu zpráv. Před odesláním zprávy je také kontrolováno, zda je odesílající komponenta v aktivním stavu. Pokud není, musí služba ze systému odstranit všechny implementace rozhraní MessageConsumer zaregistrované odesílající komponentou a zpráva není doručena příjemcům. Pokud totiž nastane při ukončování komponenty výjimka a kvůli tomu se korektně neodregistrují příjemci zpráv generovaných ukončovanou 64 Kontejner komponentou, musí to služba MessageService rozpoznat a musí odregistrovat všechny tyto příjemce. Detailní popis metod služby se nachází v [22] a v javadoc dokumentaci zdrojových kódů kontejneru. ExtrafuncRegistryService Framework musí obsahovat komponentu registrující ExtrafuncRegistrySerice při svém startu. Tato komponenta dostane od kontejneru id 2 a musí být nainstalována po komponentě MessageService a před ostatními komponentami. Tato služba poskytuje funkcionalitu spojenou s EFP. Poskytuje možnost kontrolovat validitu EFP komponenty nebo služby komponenty oproti registru EFP a možnost ověřit, zda EFP jedné komponenty (služby) poskytují všechny EFP požadované jinou komponentou (službou). Rozhraní služby je definované následovně: public interface ExtrafuncRegistryService { / * * E v a l u a t e s u b s t i t u t a b i l i t y o f two p r o p e r t i e s . * / public boolean checkExtraFuncMatch ( ExtraFunc lhsExtraFunc , ExtraFunc rhsExtraFunc ) ; /* * Verify property correctness . */ public boolean checkExtraFuncValid ( ExtraFunc e x t r a f u n c ) ; / * * I n i t i a l i z e r e g i s t r y from a d e f i n i t i o n f i l e . * / p u b l i c v o i d l o a d R e g i s t r y F r o m U R L (URL u r l ) throws E x c e p t i o n ; } Popis jednotlivých metod: public boolean checkExtraFuncMatch ( ExtraFunc lhsExtraFunc , ExtraFunc rhsExtraFunc ) ; Vrací true, pokud lhsExtraFunc (vyžadované EFP) jsou podmnožinou rhsExtraFunc (poskytované EFP). To znamená, že poskytované EFP jedné komponenty (služby) vyhovují požadovaným EFP jiné komponenty (služby). Implementuje proces porovnávání mimofunkčních charakteristik (viz sekce 5.2.5). public boolean checkExtraFuncValid ( ExtraFunc e x t r a f u n c ) ; 65 Class loading architektura Zkontroluje validitu EFP komponenty (služby) oproti registru EFP. Například kontroluje, zda mají EFP správné typy definované v registru. p u b l i c v o i d l o a d R e g i s t r y F r o m U R L (URL u r l ) throws E x c e p t i o n ; Načte registr EFP (viz sekce 6.1.3) z URL. Používá se v případě, kdy implicitní registr EFP nevyhovuje požadavkům aplikace. 6.4 Class loading architektura Architektura nahrávání tříd v CoSi je zjednodušenou verzí této architektury v OSGi. Všechny komponenty jsou instalovány a sdílí jeden virtuální stoj (VM). Uvnitř tohoto vituálního stroje mohou komponenty skrývat své balíky a třídy před ostatními nebo je naopak sdílet. Klíčovým mechanismem při skrývání a sdílení balíků je Java class loader, který načítá třídy z podmnožiny prostoru balíků za použití předem definovaných pravidel. CoSi framework musí implementovat speciální class loader řídící se pravidly popsanými níže. Každá komponenta má vlastní class loader, který se stará o nahrávání zdrojů. Aby mohly komponenty mezi sebou sdílet zdroje, je potřeba vytvořit síť vzájemně propojených class loaderů poskytujících si zdroje podle jasných pravidel. Class loader komponenty může nahrávat zdroje z následujících lokací: ∙ Boot class path - Class path dostupná pro všechny komponenty bez rozdílu. Obsahuje základní balíky potřebné k běhu programů napsaných v Javě nebo Groovy. Je důležité, aby tyto balíky byly poskytovány pouze frameworkem. Komponenta může předpokládat, že tyto zdroje jsou jí dostupné a nesmí je přidávat do své class path. ∙ Framework class path - Class path poskytovaná frameworkem obsahující základní množinu rozhraní a typů potřebných k vytvoření komponenty. Tyto typy a rozhraní jsou automaticky přidány do class path komponenty. ∙ Bundle class path - Komponenty mohou definovat vlastní class path v manifestu komponenty. Tato class path může odkazovat pouze na zdroje uvnitř komponenty a je platná pouze pro danou komponentu. Prostor tříd (class space) jsou všechny třídy dostupné z class loaderu dané komponenty. Pro komponentu tedy prostor tříd tvoří: 66 Rozdíly proti ostatním specifikacím ∙ Rodičovský class loader (obyčejně třídy z boot class path, framework class path) ∙ Importované balíky a typy ∙ Class path komponenty Prostor tříd jednoho balíku musí být konzistentní - nesmí obsahovat dvě třídy se stejným plně kvalifikovaným jménem. 6.5 Rozdíly proti ostatním specifikacím V této sekci popíšeme rozdíly proti dvěma existujícím specifikacím CoSi [6, 22]. Proti CoSi verze 1 [22] specifikace v této diplomové práci obsahuje: ∙ Možnost specifikovat EFP pro komponenty a jejich vlastnosti ∙ Balíky komponent ∙ Změněný meta-model CoSi (doplněný o balíky komponent a EFP) ∙ Podporu specifikace poskytovaných (vyžadovaných) balíků ∙ Upravený životní cyklus ∙ Upravený proces vyhodnocování komponenty Technická zpráva [6] „The CoSi Component Model“ specifikuje cílový stav budoucí podoby CoSi, a z velké části vychází z rozšíření implementovaných v této diplomové práci. Specifikace CoSi obsažená ve zprávě je však upravena s ohledem na další výzkum na katedře a implementace těchto rozšíření je mimo rozsah diplomové práce. Tato technická zpráva navíc oproti rozšířením implementovaným v diplomové práci specifikuje: ∙ Zprávy o událostech životního cyklu – specifikace kontejneru je doplněna o možnost zasílání zpráv o stavu životního cyklu komponent ∙ Doplněný životní cyklus komponenty – životní cyklus komponenty byl doplněný o stav UPDATING ∙ Adresář imports – specifikace ve zprávě předepisuje adresář imports/, ve kterém se uchovávají třídy importované komponentou 67 Rozdíly proti ostatním specifikacím ∙ Jinou sémantiku hlavičky Extrafunc-Catalog – tato hlavička specifikuje umístění katalogu registru EFP. V technické zprávě je přítomnost hlavičky podmínkou pro práci s EFP – pokud není přítomná, jsou EFP zahozeny. Technická zpráva naopak nespecifikuje podporu balíků komponent. 68 7 Implementace změn Součástí této práce je implementace změn do existujícího kontejneru specifikace CoSi. V této kapitole jsou popsány změny v architektuře kontejneru provedené v rámci práce, doplňková služba pro mimofunkční charakteristiky a dva ukázkové scénáře, na kterých je ověřena funkčnost implementovaných změn. Tato implementace vychází z návrhu změn specifikace, popsaných v kapitolách 5 a 6. Proto se tato kapitola podrobněji nevěnuje implementačním detailům jednotlivých tříd. 7.1 Jádro kontejneru Jádro kontejneru bylo od první verze CoSi pozměněno. Byla přidána podpora balíků komponent, dependency injection a podpora mimofunkčních charakteristik. Mechanismus class loaderů byl upraven pro podporu nahrávání komponent z URL a nahrávání knihoven z JAR souborů až do druhé úrovně. Také byla přidána možnost specifikovat exportované nebo importované balíky v popisu komponenty. Kromě toho byly provedeny drobné změny v implementaci, které ale nemají větší dopad na architekturu ani funkčnost, proto zde nebudou popsány. 7.1.1 Architektura jádra kontejneru Jádro kontejneru se skládá z hlavních tříd, které zajišťují funkčnost kontejneru a z vedlejších, které zajišťují podporu tříd hlavních. Vedlejší třídy jsou z hlediska architektury nezajímavé, proto zde budou popsány pouze ty hlavní. Jedná se o třídy: ∙ BDContainer – představuje samotný kontejner ∙ DeployableManager – obsahuje seznam nainstalovaných komponent a balíků komponent 69 Jádro kontejneru ∙ ApplicationContext – obsahuje informace o nainstalovaných komponentách, jejich službách, zprávách, atd. Dále obsahuje registr služeb a metody pro práci s ním. ∙ BundleMetadata, AssemblyMetadata – obsahuje všechna meta-data komponenty (balíku komponent), včetně EFP. Architektura jádra je vyobrazena na obrázku 7.1. Třídy v levém slouci představují nově přidané do jádra CoSi, v prostředním sloupci jsou třídy, ve kterých byla provedena změna a poslední skupina tříd (v pravém sloupci) jsou třídy původní implementace. Jak je vidět, změny v implementaci jsou rozprostřeny přes celé jádro kontejneru. BDContainer Jedná se o hlavní třídu, reprezentující samotný kontejner. Nachází se v balíčku cz.zcu. fav.kiv.bdcontainer a je zodpovědná za start, zastavení, inicializaci a konfiguraci kontejneru (metodami start() a stop()) a za nainstalovnání a spuštění základních systémových komponent (metoda processSystemBundles()). Navíc oproti specifikaci umožňuje tato implementace CoSi specifikovat konfigurační soubor kontejneru (deploy.config), ve kterém se nachází seznam komponent a balíků komponent, které mají být při startu kontejneru automaticky nainstalovány a spuštěny. Implementace také definuje konfigurační soubor cosi.config, který obsahuje konfiguraci základních proměnných kontejneru jako je například jeho verze, chování při vyhodnocování EFP, cesta k souboru deploy.config nebo adresář, ze kterého má kontejner nahrávat komponenty. DeployableManager Tato třída přebírá funci třídy BundleManager z předchozí verze CoSi a doplňuje ji o podporu balíků komponent. Obsahuje seznam nainstalovaných komponent reprezenovaných instancemi třídy BundleImpl implementující rozhraní Bundle a seznam nainstalovaných balíků komponent, reprezentovaných instancemi třídy AssemblyImpl implementující rozhraní Assembly. Třída poskytuje metody na práci s životním cyklem komponent a balíků komponent. 70 Jádro kontejneru Obrázek 7.1: Architektura jádra kontejneru 71 Jádro kontejneru ApplicationContext Třída představuje dynamický pohled na kontejner. Obsahuje informace o poskytovaných a vyžadovaných balících, typech, službách, atributech a zprávách jednotlivých nainstalovaných (běžících) komponent. Dále obsahuje metody pro usnadnění vyhledávání v těchto informacích a registr služeb a atributy komponent. Registr služeb je přístupný komponentě přes objekt typu BundleContext. Komponenty přes tento objekt registrují instanci služby, která je spolu s jejím popisem (instance třídy ServiceReferenceImpl) uložena ve tříde ApplicationContext. Proti CoSi verze 1 byla tato třída doplněna o podporu poskytovaných a vyžadovaných (java) balíků. Podrobný popis metod a proměnných třídy je k dispozici v javadoc dokumentaci zdrojového kódu na přiloženém CD. GClassLoader Důležitou součástí implementace kontejneru CoSi je implementace class loaderu (viz sekce 6.4), který je schopný pracovat se soubory napsanými jak v jazyce Java, tak v jazyce Groovy. Specifikace CoSi předepisuje, aby takový class loader implementovat rozhraní ModuleClassLoader. V této implementaci CoSi je rozhraní implementováno třídou GClassLoader v balíčku bdcontainer.moduleclassloader. Do CoSi přibyla možnost distribuovat komponenty ve formě balíkudruh komponent. Protože balík komponent je fyzicky reprezentován JAR archivem, který obsahuje JAR archivy komponent, bylo nutné změnit implementaci třídy GClassLoader. Původní implementace class loaderu měla možnost odkazovat se na JAR archivy uvnitř JAR kořenového JAR archivu. To ale pro implementaci balíků komponent nastačilo, protože by nebylo možné odkazovat na JAR archivy knihoven uvnitř komponent. Proto musela být do implementace class loaderu přidána podpora pro odkazování na JAR archivy ve druhé úrovni zanoření (na JAR archivy knihoven uvnitř archivu komponent, které jsou uvnitř archivu balíku komponent). Implementační detaily a podrobný popis nalezneme v javadoc dokumentaci k výše uvedenému balíčku a také přímo ve zdrojovém kódu. 72 Implementace služeb 7.2 Implementace služeb 7.2.1 Služba ExtrafuncRegistryService Služba pro podporu mimofunkčních charakteristik (viz sekce 6.3.2) je do kontejneru nainstalována jako třetí v pořadí (v metodě processSystemBundles() třídy BDContainer), hned po dokončení instalace služby MessageService. Systémová komponenta obsahující tuto službu je nainstalována do kontejneru s identifikačním číslem 2. Kód implementující službu se nachází v distribučním archivu kontejneru v balíku cz.zcu.fav.kiv.bdcontainer.extrafuncregistryservice. Podbalíček impl obsahuje aktivátor komponenty (Activator), implementaci služby a implicitní registr mimofunkčních charakteristik (soubor extrafuncregistry). V pod-balíčku manifest se nachází manifest této komponenty. Na rozdíl od ostatních systémových služeb, které pouze delegují volání do kontejneru, služba ExtrafuncRegistryService přímo implementuje poskytovanou funkčnost. Při startu služby se načte implicitní registr mimofunkčních charakteristik ze souboru extrafuncregistry a zkontroluje se validita všech položek, které obsahuje. Pokud implicitní registr pod danou aplikaci nevyhovuje, je možné ho metodou loadRegistryFromURL přepsat. Funkcionalita této služby je podrobněji popsána ve specifikaci CoSi v sekci 6.3.2. Metoda checkExtraFuncMatch je použita také v jádře kontejneru při vyhodnocování komponenty. Změnou hodnoty EXTRAFUNC_ERROR_BEHAVIOUR v konfiguračním souboru cosi.config lze ovlivnit, jak se bude kontejner chovat při vyhodnocování komponenty (viz sekce 6.2.1) v závistlosti na návratové hodnotě této metody. Jsou možné dvě hodnoty této konstanty: ∙ log - V případě, že metoda vrátí "false", zobrazí kontejner (zaloguje) chybovou hlášku a pokračuje ve vyhodnocování. ∙ stop - V případě, že metoda vrátí "false", zobrazí kontejner chybovou hlášku a ukončí vyhodnocování právě vyhodnocované komponenty s chybou. 7.2.2 Služba SimpleShell Tato služba existuje v CoSi již od verze 1. Jedná se o základní službu pro interakci s kontejnerem, implementovanou komponentou simpleshell.jar. K interakci slouží jednoduchý 73 Implementace služeb příkazový řádek (shell). Standardní příkazy zahrnují ovládání životního cyklu komponent a zjišťování informací o stavu kontejneru a systému. Kromě toho poskytuje komponenta službu pro registraci nových příkazů. Komponenty v systému tak mohou službu použít pro implementaci jednoduchého uživatelského rozhraní. Služba je podrobně popsána v [22]. Od původní verze byly upraveny následující příkazy: ∙ interfaces – nově výpis poskytovaných a vyžadovaných služeb zobrazuje také hodnotu atributu name a autowire ∙ ps – vypíše všechny komponenty a nově také balíky komponent v systému Také byly přidány nové příkazy: ∙ extrafunc <id> [-r|-p] – vytiskne na obrazovku vyžadované (–r) nebo poskytované (–p) EFP služeb komponenty <id> ∙ astart <id>, astop <id> – spustí nebo zastaví (pokud je to možné) všechny komponenty obsažené v balíku komponent <id> ∙ ainstall <id>, auninstall <id> – nainstaluje/odinstaluje balík komponent <id> ze systému. Při odinstalaci se pokouší o zastavení a odinstalování všech komponent obsažených v balíku. ∙ aheaders <id> – vytiskne hlavičky obsažené v manifestu balíku komponent <id> ∙ abundles <id> – vytiskne názvy a id komponent obsažených v balíku komponent <id> ∙ deactivate <id> – uvede komponentu <id> do stavu INSTALLED Implementace služby se nachází v balíku cz.zcu.fav.simpleshell. Služba je kompletně napsána v Groovy. 74 Ukázkové scénáře 7.3 Ukázkové scénáře 7.3.1 Ukázkový scénář - meteorologická stanice Tento ukázkový scénář má za úkol otestovat použití mimofunkčních charakteristik v popisu služeb komponent. Jako základ použijeme příklad uvedený v sekci 2.2.4. Jedná se o jednoduchou meteorologickou stanici, zastoupenou komponentami (viz obrázek 2.3): ∙ SensorRegistry - registr meteorologických senzorů. Senzory se registrují do SensorRegistry, aby mohly být použity v meteorologické stanici. Komponenta obsahuje služby SensorRegistry a SensorData v balíku cz.zcu.fav.kiv.sensorregistry. ∙ MeasuringStation - samotná stanice. Čte údaje ze senzorů registrovaných do SensorRegistry (přes službu SensorData) a registruje nové příkazy do shellu na spuštění tohoto příkladu. ∙ WindSpeedSensor - senzor poskytující údaje o rychlostech větru. Tyto údaje jsou pouze náhodně generovaná čísla. ∙ TemperatureSensor - senzor poskytující údaje o teplotě. Tyto údaje jsou pouze náhodně generovaná čísla. WindSpeedSensor a TemperatureSensor jsou zástupci senzorů. Senzory se registrují do registru senzorů (SensorRegistry) a může jich být libovolné množství. V tomto příkladu jsou použity dva senzory WindSpeedSensor a dva senzory TemperatureSensor. Služba SensorRegistry má v manifestu komponenty SensorRegistry následující definici: Provide - S e r v i c e s : cz . zcu . fav . kiv . s e n s o r r e g i s t r y . S e n s o r R e g i s t r y ; name =" r e g i s t r y "; e x t r a f u n c =( s a m p l i n g r a t e = <10;30 > , s e n s o r t y p e ={ windspeed , t e m p e r a t u r e }) Popis mimofunkčních charakteristik: ∙ samplingrate - podporované rychlosti vzorkování senzorů ∙ sensortype - typy senzorů, které se mohou registrovat do registru senzorů 75 Ukázkové scénáře Tyto mimofunkční charakteristiky jsou samozřejmě smyšlené a nemají žádný vliv na funkci komponent, ale pro účel úlohy dostačují. Všechny senzory importují službu SensorRegistry a mají v hlavičce Require-Services u služby uvedené požadované mimofunční charakteristiky. Vždy jeden z dvojice senzorů stejného typu má „správné“ hodnoty (požaduje mimofunkční charakteristiky, které služba SensorRegistry poskytuje) a jeden „špatné“. Podle nastavení kontejneru (viz 7.2.1) buď selže, nebo neselže vyhodnocení komponent, které mají „špatné“ požadované mimofunkční charakteristiky. Po nastartování všech komponent tak registr senzorů obsahuje 2 (4) senzory a je možné příklad spustit. O spuštění příkladu se stará komponenta MeasuringStation, která registruje do shellu příkaz measure. Tento příkaz může být 1. bez parametrů - pro všechny senzory vypíše na standardní výstup hodnoty jimi generované 2. s parametrem min|max|avg - pro všechny senzory vypíše na standardní výstup kontejneru minimální (maximální, průměrnou) hodnotu z hodnot vygenerovaných daným senzorem Příklad výstupu: > m e a s u r e avg T e m p e r a t u r e -0 ,6 d e g r e e C e l s i u s W i n d S p e e d 26 ,1 m / s 7.3.2 Ukázkový scénář - SaveCCM/CoSi transformace Tento ukázkový scénář se zaměřuje na použití balíku komponent. Vychází z projektu Transformation from SaveCCM to CoSi [17], který prozkoumává možnost transformace mezi komponentovým modelem SaveCCM (viz sekce 3.4) a CoSi. Výsledkem tohoto projektu je skupina komponent, které představují jádro SaveCCM v CoSi a návod jak konstruovat systém propojených CoSi komponent odpovídající systému navrženému v SaveCCM. Součástí návodu je popis, jak se architektonické elementy SaveCCM mapují na architektonické elementy CoSi. Není úkolem práce popsat tuto transformaci. Pouze uvedeme základní prvky mapování architektonických prvků komponentových modelů, aby bylo možné lépe pochopit tento ukázkový scénář. V následujícím mapování je na prvním místě vždy prvek z SaveCCM a následně jeho ekvivalent v CoSi. 76 Ukázkové scénáře ∙ datový port se mapuje na atribut komponenty s definovaným typem a trigger port na služby komponenty s definovaným jménem a typem ∙ kombinovaný port se mapuje jako dvojice datového a trigger portu se stejným jménem ∙ konexe se nemapuje na architektonický element CoSi - je reprezentova objektem vytvářeným za běhu ∙ komponenta se mapuje na CoSi komponentu s předem definovaným chováním a množinou rozhraní ∙ quality attributes komponenty jsou reprezentovány EFP komponenty (viz hlavička Bundle-Extrafunc v sekci 6.1.2) ∙ hodiny a zpoždění jsou reprezentovány knihovními třídami ∙ přepínač je mapován na CoSi komponentu s předem definovaným chováním a množinou rozhraní ∙ assembly se také mapuje na CoSi komponentu s předem definovaným chováním V době, kdy vznikala transformace, ještě nebyl komponentový model CoSi rozšířen o balíky komponent. Proto je mapování neobsahuje. Představme si hypotetický systém navržený v SaveCCM – adaptivní tempomat (The Adaptive Cruise Controller - dále jen ACC). Jedná se o systém propojených komponent, který má za úkol z údajů o vzdálenosti automobilu od automobilu před ním a z aktuální rychlosti vypočítat rychlost, kterou by měl automobil jet, aby dodržel bezpečnou vzdálenost od vpředu jedoucího automobilu. Rychlost se počítá a upravuje několikrát za sekundu tak, aby bylo možné předejít srážce. Tento systém namodelovaný v SaveCCM (obrázek 7.2) předeveme do CoSi. Transformovaný systém obsahuje následující CoSi komponenty: ∙ Speed Controller – reprezentace SaveCCM assembly obsahující komponenty Calc Output a Update State – Calc Output – simuluje výpočet výsledné rychlosti – Update State – uchovává stav poslední rychlosti, aby bylo možné zjistit, zda má automobil zpomalit nebo zrychlit 77 Ukázkové scénáře ∙ Distance Generator – simuluje prostředí (senzory automobilu) generováním náhodných dat na výstupech (portech) ∙ Clock1 – hodiny – generují trigger signály na výstupu ∙ Printer – komponenta pouze tiskne na obrazovku výstup z komponenty Speed Controller ∙ ACCApp – komponenta zapouzdřující aplikaci. Jedná se o top-level SaveCCM assembly. Registruje do shellu CoSi příkazy pro otestování aplikace (viz dále). Má stejné jméno jako výsledný balík komponent. Dále obsahuje pomocnou komponentu SaveCCMCore nutnou pro nasazení a spuštění systému. Pro otestování funkčnosti balíku komponent byly všechny komponenty ACC uloženy do jednoho CoSi assembly s názvem ACCApp a nasazeny do systému. Import správných služeb trigger portů importujícími komponentami byl zajištěn atributem autowire. Například komponenta Calc Output exportuje trigger port in1 a komponenta Speed Controller ho importuje. V manifestu balíku komponent ACCApp se proto objeví následující konstrukce: Assembly - W i r i n g : C a l c O u t p u t . in1 to S p e e d C o n t r o l l e r . in1 Tento systém komponent registruje do shellu CoSi dva příkazy: 1. accapp start <ms> – Spustí hodiny Clock1, které začnou generovat trigger signály. 2. accapp createXML <filename> – vytvoří SaveCCM XML deskriptor systému (viz [17]). Příklad výstupu příkazu accapp start 500: P r i n t e r c o m p o n e n t - port v a l u e s : controlIn : Max . speed : 50 C u r r e n t speed : 46 Distance : 4 U p d a t e State : 0 printerIn : s p e e d i n g up V rámci projektu [17] bylo ověřeno, že výsledný systém funguje a generuje správné SaveCCM XML. Správně se naváží všechny závislosti a nainstalují se komponenty do systému. Funguje i odinstalování balíku komponent ze systému. 78 Ukázkové scénáře Obrázek 7.2: ACC systém v SaveCCM 79 8 Závěr Hlavním cílem této diplomové práce bylo navrhnout a implementovat rozšíření komponentového modelu CoSi o mimofunkční charakteristiky a balíky komponent. Při realizaci práce jsem nastudoval obecné principy komponentových technologií a zjistil jsem, co jsou to softwarové komponenty, k čemu slouží a jak se popisují jejich vlastnosti. Prozkoumal a vyhodnotil jsem několik obecně přijímaných řešení pro popis mimofunkčních charakteristik komponent. Největší výzvou pro mě bylo proniknutí do světa komponentových technologií, o kterých jsem měl před realizací práce jen velmi omezené znalosti. Po seznámení se s existujícími komponentovými modely (OSGi, SaveCCM, EJB) a důkladné analýze specifikace a implementace experimentálního komponentového modelu CoSi jsem navrhl jeho rozšíření o pokročilé aspekty komponentových modelů. Model jsem rozšířil o možnost specifikace mimofunkčních charakteristik komponent a o balíky komponent. Daná rozšíření jsem implementoval a ověřil jejich funkčnost na příkladových scénářích. Jsem přesvědčen, že rozšíření komponentového modelu CoSi poskytne další vědecké příležitosti v oblasti výzkumu nahraditelnosti komponent a popisu mimofunkčních charakteristik komponent. Do budoucna je možné rozšířit popis mimofunkčních charakteristik komponent o odvozené charakteristiky (podle [12, 16]). Ty by byly vyjádřeny jednoduchým jazykem a automaticky počítány ze základních. Dalším možným rozšířením je změna přístupu k balíku komponent. Mohli bychom balík komponent považovat také za komponentu a tím transformovat CoSi na plně hierarchický komponentový model. 80 Seznam zkratek API Application Programming Interface ACC The Adaptive Cruise Contoller CoSi Components Simplified CBD Component Based Development CBSE Component Based Software Engineering CQM Component Quality Model EJB Enterprise JavaBeans EFP Extra-Functional Properties. IDL Interface Definition Language IoC Inversion of Control LDAP Lightweight Directory Access Protocol JAR Java Archive soubor OSGi Open Services Gateway initiative QoS Quality of Service SaveCCM The SaveComp Component Model UML Unified Modelling Language XML eXtensible Markup Language 81 Literatura [1] ALUR, R. – DILL, D. L. A Theory of Timed Automata. Theoretical Computer Science. 1994, 126, s. 183–235. [2] ALVARO, A. – ALMEIDA, E. – MEIRA, S. A software component quality model: A preliminary evaluation. In Proceedings of the 32nd EUROMICRO Conference on Software Engineering and Advanced Applications (EUROMICRO?06), s. 28–37, 2006. [3] BACHMANN, F. et al. Volume II: Technical concepts of component-based software engineering. Carnegie Mellon University, Software Engineering Institute, 2000. [4] BASS, L. – CLEMENTS, P. – KAZMAN, R. Software architecture in practice. Addison-Wesley Professional, 2003. [5] BEUGNARD, A. et al. Making components contract aware. Computer. 1999, 32, 7, s. 38–45. [6] BRADA, P. – LISKA, V. – WAJTR, B. The CoSi Component Model. Technical report, University of West Bohemia Department of Computer Science and Engineering, July 2008. [7] BUREŠ, T. – HNTYNKAL, P. – PLÁŠIL, F. SOFA 2.0: Balancing advanced features in a hierarchical component model. In Proc. of SERA, 2006. [8] CRNKOVIC, I. – LARSON, M. Building reliable component-based software systems. Artech House, 2002. [9] DESMOND FRANCIS, D. – WILLS, A. Objects, components, and frameworks with UML: the catalysis approach. Addison-Wesley, 1999. [10] EJB Specification. Version 3.0. Sun Microsystems. 2006. 82 Literatura [11] FOWLER, M. Inversion of Control Containers and the Dependency Injection pattern, 1 2004. Dostupné z: http://www.martinfowler.com/articles/ injection.html. [12] FRANCH, X. Systematic formulation of non-functional characteristics of software. In Requirements Engineering, 1998. Proceedings. 1998 Third International Conference on, s. 174–181, 1998. [13] HÅKANSSON, J. et al. The SaveCCM Language Reference Manual. Technical report, Dept. of Computer Science and Electronics, Mälardalen University, Sweden, 2007. [14] HANSSON, H. et al. SaveCCM - A Component Model for Safety-Critical RealTime Systems. In EUROMICRO ’04: Proceedings of the 30th EUROMICRO Conference, s. 627–635, Washington, DC, USA, 2004. IEEE Computer Society. doi: http://dx.doi.org/10.1109/EUROMICRO.2004.72. ISBN 0-7695-2199-1. [15] HELLESOY, A. – TIRSEN, J. PicoContainer homepage, 2008. Dostupné z: http: //www.picocontainer.org/. [16] JEŽEK, K. – BRADA, P. – ROHLÍK, O. Towards a Unified and Portable Descriptor of Extra-functional Properties for Reusable Software Components. Unpublished draft paper, Department of Computer Science and Engineering, University of West Bohemia, Pilsen, Czech Republic, 2008. Submitted to 6th International Workshop on Formal Engineering approaches to Software Components and Architectures, Satellite event of ETAPS, to be held on 28th March 2009, York, UK. [17] LIŠKA, V. Transformation from SaveCCM to CoSi. CDT412 - Software Engineering Project Report, School of Innovation, Design and Engineering, Mälardalen University, Västeras, Sweden, December 2008. [18] MEYER, B. Applying ’design by contract’. Computer. 1992, 25, 10, s. 40–51. [19] SZYPERSKI, C. Component Software - Beyond Object-Oriented Programming, 1998. [20] The OSGi Alliance. OSGi Service Platform Core Specification, Release 4.1. Technical report, 2007. 83 Literatura [21] The OSGi Alliance. OSGi Service Platform Compendium Specification, Release 4.1. Technical report, 2007. [22] WAJTR, B. Implementace jednoduchého komponentového modelu. Master’s thesis, Západočeská univerzita v Plzni, Fakulta aplikovaných věd, 2007. 84 Přílohy Specifikace komponenty - popis nezměněných hlaviček V této příloze jsou popsány hlavičky manifestu komponenty, které nebyly změněny v této verzi CoSi a jsou tak shodné s [6, 22]. Gramatika použitá pro definici hodnot hlaviček je dostupná v příloze A v [22]. Control-Class Povinná hlavička. Specifikuje aktivátor komponenty - jméno třídy, která implementuje rozhraní BundleControl. Příklad: Control - Class : cz . zcu . fav . kiv . b d c o n t a i n e r . e x t r a f u n c r e g i s t r y s e r v i c e . impl . A c t i v a t o r Bundle-Name Povinná hlavička. Reprezentuje jméno komponenty. Příklad: Bundle - Name : E x t r a f u n c R e g i s t r y B u n d l e Bundle-Version Reprezentuje verzi komponenty. Příklad: Bundle - V e r s i o n : 1 . 2 . 3 . snapshot -10 i Cosi-Version Verze CoSi specifikace, pro kterou byla komponenta navržena. Cosi - V e r s i o n ::= n u m b e r Pro tuto verzi specifikace musí být toto číslo větší nebo rovno 2. Bundle-Description Krátký popis použití a účelu komponenty. Bundle-Provider Jméno poskytovatele komponenty. Bundle-Classpath Definuje seznam jar souborů uvnitř archivu komponenty, kde budou hledány přeložené třídy, zdrojové soubory nebo jiné zdroje potřebné pro běh komponenty. Položky seznamu jsou odděleny čárkou. Tečka (’.’) reprezentuje kořenový adresář jar archivu komponenty a je implicitní hodnotou této hlavičky (kořenový adresář je vždy zahrnutý do class path). Příklad: Bundle - C l a s s p a t h : .; lib / log4j -1.3 alpha -8. jar Generate-Events Specifikuje události (zprávy) generované komponentou. Spolu s definicí jména musí být uvedený typ zprávy. Tento typ je specifikován plným kvalifikovaným jménem v hodnotě povinného atributu type. Typ události není automaticky exportován kontejnerem, je tedy nutné ho uvést v hlavičce Provide-Types. Parametry, které mohou být specifikovány pro událost: version - Nepovinný parametr. Verze poskytovatele události. Implicitní hodnota je 0. Consume-Events Specifikuje událost vyžadované (importované) komponentou. Stejně jako v předchozí hlavičce musí být v hodnotě atributu type uvedeno plně kvalifikované jméno typu (třídy) ii události. Pokud komponenta daný typ neobsahuje (tzn. ve většině případů), musí tento typ události importovat hlavičce manifestu Require-Types. Parametry, které mohou být pro vyžadovanou událost uvedeny: versionrange - Nepovinný parametr. Specifikuje rozmezí verzí importované události. Implicitní hodnota parametru je [0.0.0,∞). Provide-Attributes Specifikuje atributy poskytované komponentou. Povinné parametry jsou jméno atributu name a jeho typ type. Typ atributu je uveden plně kvalifikovaným jménem třídy a musí být importován nebo exportován komponentou, aby mohla být komponenta správně zpracována kontejnerem. Parametry, které mohou být specifikovány pro poskytovaný atribut: version - Nepovinný parametr. Verze poskytovaného atributu. Implicitní hodnota je 0. Require-Attributes Specifikuje atributy vyžadované komponentou. Povinné parametry jsou jméno atributu name a jeho typ type. Typ atributu je uveden plně kvalifikovaným jménem třídy a musí být importován komponentou (uveden v hlavičce Require-Types), aby mohla být komponenta správně zpracována kontejnerem. Parametry, které mohou být specifikovány pro vyžadovaný atribut: versionrange - Nepovinný parametr. Specifikuje možné rozmezí verzí importovaného atributu. Implicitní hodnota je [0.0.0,∞). iii