Použití architektury COM v distribuovaných aplikacích
(Diplomová práce)

Copyright © září 2002   Martin Přikryl

Obsah

  1. Zadání diplomové práce
  2. Prohlášení
  3. Anotace
  4. Annotation
  5. 1. Úvod
    1. 1.1 Výběr tématu
    2. 1.2 Obsah práce
    3. 1.3 Cíle práce
    4. 1.4 Nároky na čtenáře
    5. 1.5 Vývojové prostředí
  6. 2. Systém ESA
    1. 2.1 Vyplývající požadavky na architekturu
      1. 2.1.1 Rychlost odezvy
      2. 2.1.2 Obsluha více klientů
      3. 2.1.3 Více-vláknové (multithreading) aplikace
      4. 2.1.4 Řízení zdrojů
      5. 2.1.5 Výpadek spojení
      6. 2.1.6 Přenos velkého množství dat
      7. 2.1.7 Bezpečnost
      8. 2.1.8 Volání klienta (události)
      9. 2.1.9 Propojení sjinými systémy (webový server)
    2. 2.2 Výběr řešené oblasti
      1. 2.2.1 Rychlost odezvy, obsluha více klientů, řízení zdrojů, více-vláknové (multithreading) aplikace
      2. 2.2.2 Výpadek spojení
      3. 2.2.3 Přenos velkého množství dat
      4. 2.2.4 Bezpečnost
      5. 2.2.5 Volání klienta (události)
  7. 3. Analýza a design
    1. 3.1 Architektura
    2. 3.2 Funkce systému
      1. 3.2.1 Určení parametrů
      2. 3.2.2 Ověření identifikační karty
      3. 3.2.3 Souhrnné informace o klíčcích v sadě
      4. 3.2.4 Získání údajů o klíčku
      5. 3.2.5 Získání seznamu klíčků v sadě včetně jejich stavu
      6. 3.2.6 Zjištění, zda určitá osoba má půjčený klíček
      7. 3.2.7 Navržení klíčku k půjčení
      8. 3.2.8 Vypůjčení klíčku
      9. 3.2.9 Vrácení klíčku
  8. 4. Inkrementální vývoj aplikace
    1. 4.1 Databázová vrstva
      1. 4.1.1 Databázový server a vytvoření databáze
      2. 4.1.2 Datový model
      3. 4.1.3 Definování funkcionality na úrovni databázového serveru: uložené procedury
      4. 4.1.4 Chybové kódy
      5. 4.1.5 Přístupová práva
    2. 4.2 Výchozí parametry projektu
    3. 4.3 Vytvoření nového COM serveru v prostředí Delphi
    4. 4.4 Rozhraní aplikačního serveru
    5. 4.5 Přístup serveru k databázi
    6. 4.6 Implementace metod
    7. 4.7 Uživatelské rozhraní serveru
    8. 4.8 Klientská aplikace
    9. 4.9 Dynamické volání
    10. 4.10 Přenos velkého množství dat
    11. 4.11 Události
    12. 4.12 Výjimky
      1. 4.12.1 COM a obsluha chyb
      2. 4.12.2 Obsluha chyb a Delphi
      3. 4.12.3 Využití v aplikaci
    13. 4.13 Co se děje na pozadí
      1. 4.13.1 Inicializace COM
      2. 4.13.2 Rozhraní IUNKNOWN
      3. 4.13.3 Ukazatele na rozhraní a počítání referencí
    14. 4.14 Vzdálený server
    15. 4.15 Bezpečnost
      1. 4.15.1 Úroveň ověřování (authentication)
      2. 4.15.2 Zosobnění (impersonation)
      3. 4.15.3 Přístupová oprávnění (access control)
      4. 4.15.4 Identita serveru (server identity)
      5. 4.15.5 Nástroje pro nastavení bezpečnostních parametrů
      6. 4.15.6 Programové nastavení bezpečnostních parametrů
      7. 4.15.7 Zjištění bezpečnostních údajů klienta
      8. 4.15.8 Fyzické umístnění klienta
      9. 4.15.9 Výjimka "Přístup byl odepřen"
      10. 4.15.10 Časté chyby a problémy
    16. 4.16 Webový klient
      1. 4.16.1 Úpravy serveru
      2. 4.16.2 Změna bezpečnostního nastavení
      3. 4.16.3 Implementace webového klienta
    17. 4.17 Použití COM+
      1. 4.17.1 Aplikace COM+
      2. 4.17.2 Převedení programu Klíčky na aplikaci COM+
    18. 4.18 Řízení zdrojů
      1. 4.18.1 Sdružování COM objektů
      2. 4.18.2 Implementace rozhraní IOBJECTCONTROL a potvrzování transakcí
      3. 4.18.3 Klient
      4. 4.18.4 Sledování sdružených objektů
    19. 4.19 Parametrizování objektu
    20. 4.20 Optimalizace s pomocí sdílených vlastností
      1. 4.20.1 Správce skupin sdílených vlastností
      2. 4.20.2 Použití sdílených vlastností
      3. 4.20.3 Práce s SPM
      4. 4.20.4 Implementace mezipaměti s využitím SPM
    21. 4.21 Výpadek spojení
      1. 4.21.1 MSMQ
      2. 4.21.2 Queued components
      3. 4.21.3 Implementace rozhraní volaného přes frontu
  9. 5. Zátěžové testy
    1. 5.1 Metodika
    2. 5.2 Rychlost vyvolání metody při použití různých technologií
    3. 5.3 Doba potřebná k získání reference na objekt (rozhraní)
    4. 5.4 Srovnání s dynamickým voláním
    5. 5.5 Předávání velkého množství dat
    6. 5.6 Současná obsluha více klientů
  10. 6. Závěr
    1. 6.1 Zhodnocení aplikace vzhledem k požadavkům
      1. 6.1.1 Rychlost odezvy
      2. 6.1.2 Obsluha více klientů
      3. 6.1.3 Více-vláknové (multithreading) aplikace
      4. 6.1.4 Řízení zdrojů
      5. 6.1.5 Výpadek spojení
      6. 6.1.6 Přenos velkého množství dat
      7. 6.1.7 Bezpečnost
      8. 6.1.8 Volání klienta (události)
    2. 6.2 Shrnutí
  11. 7. Literatura

Zadání diplomové práce

Jméno
Martin Přikryl
Obor
informatika
Specializace
informační technologie

Vedoucí katedry Vám ve smyslu nařízení vlády o státních závěrečných zkouškách a státních rigorózních zkouškách určuje tuto diplomovou práci:

Téma
Použití architektury COM v distribuovaných aplikacích
Osnova
  1. Výběr problému, jehož řešení pomocí architektury COM bude cílem práce. Zdůvodnění výběru.
  2. Rozbor technických a funkčních požadavků na architekturu ve vztahu k vybranému problému.
  3. Inkrementální implementace distribuované aplikace řešící vybraný problém. V každém kroku bude k řešení dílčího problému použita jiná vlastnost (funkce) technologie COM resp. navazujících technologií jako MTS, COM+, OLE automation, MSMQ atp.
  4. Zátěžové testy vytvořené aplikace.
  5. Srovnání výchozích požadavků s dosaženými výsledky.
Seznam odborné literatury
  1. Binh Li: COM Concepts and tutorials; http://www.techvanguards.com/com/
  2. MSDN Library: COM+ (Component Services); http://msdn.microsoft.com/library/
  3. Microsoft Component Object Model: http://www.microsoft.com/com/
  4. Borland Delphi 5: Developer's Guide
  5. Gopalan Suresh Raj: A Component Engineering Cornucopia; http://www.execpc.com/~gopalan/
  6. Diskusní skupiny:
    1. news://borland.public.delphi.oleautomation
    2. news://microsoft.public.microsofttransactionserver
    3. news://microsoft.public.msmq
    4. news://microsoft.public.activex
Vedoucí diplomové práce
Ing. Helena Jílková, CSc.
Datum zadání diplomové práce
2002
Termín odevzdání diplomové práce
2002

Prohlášení

Prohlašuji, že jsem diplomovou práci zpracoval samostatně a že jsem uvedl všechny použité prameny a literaturu, ze kterých jsem čerpal.

Anotace

Obsahem práce je praktický popis vývoje distribuovaných aplikací na architektuře COM. Práce je založena na postupném vývoji ukázkové aplikace "Klíčky", které je subsystémem IS Esa, využívaného na počítačových učebnách Vysoké školy ekonomické v Praze. Cílem je zjistit, jestli je možné pomocí funkcí a schopností architektury COM dostát všem nárokům, které vyvíjený informační systém požaduje. Dalším záměrem je nalézt obecné řešení typických problémů většiny distribuovaných systémů.

úvodní části jsou pomocí požadovaných funkcí a chování aplikace "Klíčky" specifikovány nároky na architekturu. Mezi ně patří zejména: dostatečná rychlost odezvy, obsluha více klientů zároveň, více-vláknová (multithreading) obsluha požadavků, řízení zdrojů (databázových spojení...), řešení problému výpadku spojení mezi klientem a serverem, přenos velkého množství dat, zajištění bezpečnosti přenosu dat a autorizace, volání klienta serverem (události), propojení s jinými systémy (webový server).

Ve třetí kapitole je provedena analýza. Nejdůležitější částí je popis funkcí systému, ze kterého bude vycházet definice rozhraní serveru.

Na analýzu navazuje ve čtvrté a nejrozsáhlejší části vlastní vývoj systému. Vývoj je prováděn inkrementálně. V každé části je hledáno řešení jednoho z určených požadavků. Tím se zároveň rozšiřuje systém o novou funkci. Ty jsou vybrány tak, aby byla pro jejich implementaci použita vždy jiná funkce architektury COM. Na jednoduchých (ale funkčních) ukázkách kódu v jazyce Object Pascal vývojového prostředí Borland Delphi 5 je názorně předvedeno použití těchto funkcí. Zároveň jsou vždy zmíněna úskalí s nimi spojená.

Pátá kapitola obsahuje výsledky několika zátěžových testů, které se soustřeďují zejména na rychlost odezvy v nejrůznějších extrémních situacích. Z nich je usuzováno na schopnost architektury splnit výkonnostní požadavky na systém.

závěru jsou opět shrnuty nároky definované na začátku práce. U každého z nich je zhodnoceno, jak úspěšně byl problém vyřešen, případně je poukázáno na problémy, které nejsou pomocí architektury COM řešitelné nebo jejichž řešení je příliš složité.

Annotation

This thesis contains practical description of distributed application development using COM architecture. The thesis is based on incremental development of sample application "Keys", which is subsystem of IS Esa, used on computer labs of University of Economics in Prague. Main goal is to verify that COM architecture is able to fully satisfy all requirements that are claimed by developed information system. Other goal is to find general solution of typical problems connected with distributed systems development.

In the introductory part the requirements for architecture are specified. Description of functions and system behavior is used for specification. Most important requirements are: sufficient response time, concurrent multiple clients handling, multithreaded request handling, governing resources (database connection...), problem of losing connection between client and server, large data transfers, securing data transfers and authentication, calling client by server (events), integration with other systems (web server).

Third chapter contains analysis. The most important part is description of system functions. It will be used as basis for server interface definition.

Development of system connects to analysis in forth chapter. Development is pursued incrementally. In each part solution of one appointed requirements is being found. Alongside, the system is being extended with new function. It is chosen so a sits implementation utilizes new function of COM architecture in each case. Simple but functional sample is given to demonstrate selected function. Object Pascal language of Borland Delphi 5 environment is used. Also common pitfalls connected with described function are shown.

Fifth chapter contains results of several load tests. They are focused primarily on response time in various extreme conditions. From the results, it is considered, if COM architecture can fulfill performance requirements of the system.

In the conclusion, all the requirements for system are summarized. For each of them the solution is evaluated. The problems, which are not solvable by COM, are pointed out.

1. Úvod

1.1 Výběr tématu

Téma distribuovaných komponent jsem si na začátku zvolil čistě ze zájmu o tuto oblast, ve snaze rozšířit své znalosti a tím i možnost svého budoucího uplatnění v profesi programátora. Mou snahou bylo získat a nastudovat co nejvíce materiálů týkajících se praktického využití nejrozšířenějších komponentových architektur, tedy architektury COM od společnosti Microsoft, CORBA sdružení OMG a JAVA/RMI společnosti Sun Microsystems.

Jako zaměstnanec Výpočetního centra Vysoké školy ekonomické v Praze jsem se ve stejné době podílel na analýze a designu nové verze informačního systému ESA, který mimo jiné provádí evidenci identifikačních karet studentů a zaměstnanců školy a zajišťuje mnoho podpůrných funkcí provozu studentských počítačových studoven a učeben. Architektura původní verze systému byla vytvořena bez využití standardizovaných architektur.

Při zběžném srovnání požadavků na inovovanou verzi informačního systému a možností uvedených komponentových architektur jsem si uvědomil, že by bylo vhodné posoudit možnost jejich využití při vývoji systému. Výsledkem této úvahy je, že moje práce je oproti původní volbě daleko více prakticky zaměřená.

Převládající platformou stanic školní počítačové sítě jsou různé verze operačního systému Windows. Případné použití technologie COM, která je většinou verzí tohoto operačního systému nativně podporována, se od začátku zdálo být nejschůdnější variantou. Distribuce implementací architektur CORBA a JAVA/RMI na stovky stanic byla stěží představitelná. CORBA byla vyloučena také pro relativně velké náklady na zakoupení licence. Faktorem, který vyřadil technologii JAVA/RMI, byla mimo jiné nedostatečná znalost jazyka Java.

1.2 Obsah práce

druhé kapitole se stručně zmíním o informačním systému ESA. V krátkém popisu současného stavu se zmíním o jeho funkcích a stávající podobě. Na základě nároků na systém, vyplývajících z jeho funkcí, odvodím technologické požadavky na architekturu. Mojí snahou bude zjistit, zda architektura COM tyto požadavky splňuje a je tedy vhodná pro vývoj systému. Vhodnosti architektury posoudím na příkladu aplikace "Klíčky", která je subsystémem IS ESA. Zároveň uvedu důvody, které mě vedly k výběru právě této aplikace.

Třetí kapitola textu je určena k analýze vybraného subsystému. Zde je důležitá část popisující funkce aplikace. Později se tento popis použije při definici rozhraní aplikačního serveru.

Čtvrtou část textu věnuji vlastnímu vývoji aplikace. Vývoj bude prováděn inkrementálně. V každé podkapitole se pokusím vyřešit některý z technologických požadavků specifikovaných ve druhé kapitole (výjimkou je hned první podkapitola, kde stručně zmíním konfiguraci databáze). Realizované funkce programu se budu snažit vybírat tak, aby jejich vazba k technologickým požadavkům byla přirozená. To znamená, že nechci uvádět pouze anonymní příklady použití zvolené technologie, ale její skutečné využití v praxi tak, aby vznikla smysluplná aplikace.

S pomocí vytvořené aplikace provedu v páté kapitole několik testů ve snaze posoudit, zda je systém výkonnostně schopen splnit požadavky na něj kladené.

Šestou a závěrečnou kapitolu věnuji zhodnocení dosažených výsledků a to zejména v evztahu ke stanoveným požadavkům.

1.3 Cíle práce

Pro tuto práci si kladu dva cíle, jeden specifický a jeden obecnější.

Tím specifickým je již zmíněná snaha posoudit vhodnost použití architektury COM pro vývoj nové verze informačního systému ESA, a to realizací jeho malé části. Mým cílem není vytvořit plnohodnotnou distribuovanou aplikaci. Implementovat budou jen takové funkce, na kterých mohu ukázat nějaké zajímavé využití schopností architektury COM. Naopak některé části systému zmíním jen stručně, například vývoji uživatelského rozhraní se nebudu věnovat vůbec.

Druhým obecnějším cílem je vytipovat a vyřešit některé běžné problémy, které musí řešit každý programátor při vývoji softwaru na architektuře COM. Proto budu (všude, kde to bude možné) hledat obecnější řešení problémů, na které narazím. V některých případech uvedu i několik možných řešení, byť třeba jen slovně bez konkrétních ukázek zdrojového kódu. Budu se také snažit čtenáře odkázat na literaturu popisující téma do větší hloubky.

1.4 Nároky na čtenáře

Při psaní práce předpokládám, že čtenář má již alespoň základní znalosti o architektuře COM. Nebudu tedy popisovat její principy ani vysvětlovat jednoduché pojmy (rozhraní, objekt, vlákno...). Neznalého čtenáře odkazuji na [8], [3], [4].

1.5 Vývojové prostředí

Pro vývoj systému jsem zvolil prostředí Borland Delphi 5, a to přesto, že systém ESA bude vyvíjen v jazyce C++. Důvodem výběru je fakt, že jazyk Pascal je mnohem přehlednější než C++. To umožňuje snazší orientaci v kódu i programátorovi, který tento jazyk nezná.

Syntaxe jazyka Object Pascal je rozšířena o podporu pro rozhraní COM architektury. Prostředí Delphi 5 obsahuje nástroj pro vizuální design komponent a jejich rozhraní. Popis tohoto nástroje není předmětem této práce. Jeho ovládání je dobře popsáno v nápovědě, pro podrobnější popis doporučuji [8].

Object Pascal obsahuje mnoho konstrukcí, které usnadňují vývoj COM komponent. Tato vlastnost velmi urychluje vývoj. Nevýhodou je, že se tím před programátorem skrývá mnoho procesů, které probíhají "na pozadí". Zvláště začátečníky to může vést k špatným návykům, a tím například k neefektivnímu využití zdrojů. Budu se proto snažit na takové skutečnosti poukázat a v některých případech se i využití speciálních jazykových konstrukcí záměrně vyhnu. Výsledné příklady sice budou zdánlivě "zbytečně" složité, na druhou stranu tím vývojářům, pracujícím s jinými jazyky, umožním jejich snazší použití. Navíc v kapitole 4.13 "odhalím" čtenáři některé zmíněné taje skrývající se za zdánlivě jednoduchými příkazy.

2. Systém ESA

Původním účelem systému byla správa identifikačních karet a jejich využití k autorizaci přístupu studentů ke školní počítačové síti. Možnosti autorizace byli dále rozšiřovány a nyní zahrnují i ověřování příslušnosti studenta na výuku (resp. zkoušku konanou na počítačové učebně), omezení strojového času, omezení přihlášení ze zvláštních důvodů (registrace a zápisy) a další. Ověřování autorizace zahrnuje i zaznamenávání přístupu studentů k síti pro potřeby pozdějšího ověřování a pro statistické účely. Mimo tuto základní funkci byly přidány další podpůrné funkce, jako například vydávání uživatelských jmen, zobrazování zpráv na informačních monitorech, vydávání a evidence klíčku od úschovných skříněk ve studovně a mnoho dalších.

ESA má klasickou třívrstvou klient/server architekturu. Skládá se ze dvou databázových serverů, dvou aplikačních serverů a mnoha klientských aplikací.

Databáze pro systém ESA je zajištěna dvěma servery Sybase SQL Anywhere. Jeden z nich slouží jako provozní a obsahuje zejména seznamy studentů, jejich uživatelských jmen a přidělených identifikačních karet, seznam počítačových stanic zapojených do sítě, rozvrhy všech studentů a seznam kurzů včetně umístnění. Mimo tyto základní údaje je obsaženo mnoho nastavení ovlivňujících chod systému. Velká část funkčnosti je obsažena v uložených procedurách této databáze, veškerý přístup do databáze probíhá jejich prostřednictvím, nikdy se nepřistupuje přímo k tabulkám. Druhý server obsahuje archivní databázi, která obsahuje časy a místa přihlášení studentů a použití identifikačních karet. Do této databáze se přesunují data z provozní databáze starší než určené časové období.

Systém ESA obsahuje dva aplikační servery. Oba jsou schopny zastávat stejné funkce. Jeden z nich je vždy nastaven jako primární. Druhý je určen k odpovídání na požadavky, které není časově schopen vykrýt primární server. Také zajišťuje výše popsanou archivaci. Oba servery zajišťují pro klienty přístup k databázi, většinou pouze jako prostředník pro obousměrné předávání dat, někdy zajišťují i dodatečnou funkcionalitu. Dále cyklicky procházejí všechny uživatele přihlášené k síti a ověřují jejich oprávnění k přístupu. Servery jsou naprogramovány v prostředí Borland C++ Builder 5, běží na operačním systému Microsoft Windows NT na stejných stanicích jako databázové servery.

Používané klienty lze rozdělit do dvou kategorií. V jedné jsou klienti, kteří zajišťují provádění kontroly oprávněnosti přístupu při přihlašování, ve druhé klienti zajišťující servisní funkce.

Klienti zajišťující kontrolu oprávněnosti přístupu k počítačové síti (tzv. "brány") jsou jednoduché aplikace, které jsou spouštěny v přihlašovacích skriptech všech uživatelů školní počítačové sítě. Klient odešle informace o uživateli na server, tam se provede autorizační procedura a výsledek se odešle klientu. Ten podle odpovědi buď povolí uživateli přihlášení nebo ne. V některých případech umožní klient uživateli napravit příčinu nepovolení přístupu a zopakuje autorizační proceduru. Tento klient existuje ve dvou verzích, ve verzi pro operační systém Ms-DOS a ve verzi pro Microsoft Windows.

Servisní klienti zajišťují rozhraní pro služby na počítačových učebnách pro přístup k podpůrným funkcím systému ESA. Tyto funkce jsou následující:

Ne všechny tyto funkce jsou přístupné na všech instancích aplikace. Klient si při startu ze serveru zjistí účel, ke kterému je spuštěn a podle toho upraví svoje menu. Přístup k jednotlivým funkcím je zajištěn i na straně serveru.

U servisního klienta je částečně porušena klient/server architektura. Některé funkce serveru jsou na klientu duplikovány, je to proto aby klient mohl nepřetržitě fungovat i v případě výpadku serveru nebo spojení s ním. V průběhu výpadku si klient zaznamenává všechny uskutečněné operace a v okamžiku opětovného navázání spojení vše odešle na server a následně si stáhne aktualizované údaje nezbytné pro jeho další chod. Toto je příčina relativní složitosti klienta.

Klient je naprogramován v prostředí Borland C++ 3.1 a běží na operačním systému Ms-DOS.

Podrobnější popis a zejména přehled historického vývoje systému je popsán v [18].

2.1 Vyplývající požadavky na architekturu

Implementace mnoha z uvedených funkcí informačního systému klade určité nároky na architekturu. V této kapitole se je pokusím stručně shrnout. Při tvorbě ukázkové aplikace v následující kapitole se budu snažit vybírat řešené problémy tak, abych ukázal schopnost (resp. neschopnost) architektury COM tyto nároky splnit.

2.1.1 Rychlost odezvy

Zcela samozřejmým požadavkem při vývoji každého informačního systému je dostatečně rychlá odezva serveru na požadavky klientských aplikací. Průměrná doba odezvy je ovlivněna dvěma faktory, efektivností implementace vlastní aplikační logiky a vlastnostmi použité architektury.

Prvním uvedeným faktorem se v této práci zabývat nebudu. Vlivu architektury COM na rychlost odezvy věnuji kapitolu 5.

V některých částech budu řešit problematiku výběru různých technik řešení problému a to právě s ohledem na výslednou rychlost odezvy. Velká část technologie skrývající se za COM je pro programátora skryta, je tedy často obtížné rozhodnout, který z možných postupů je vhodnější. Tento problém se zvětšuje při použití vysokoúrovňových vývojových prostředí, jako právě Borland Delphi.

2.1.2 Obsluha více klientů

Tato problematika úzce souvisí právě s rychlostí odezvy. Je důležité vědět, jak se aplikační server bude chovat, pokud bude muset ve stejný okamžik obsluhovat požadavky z více klientů.

Na základě logových záznamů současné verze systému ESA jsem se pokusil odhadnout horní hranici počtu požadavků, které se mohou v jeden okamžik "sejít". Existují záznamy o třech typech operací: ověření protažené identifikační karty, kontrola oprávněnosti přístupu k počítačové síti, půjčení a vrácení klíčku. Ostatní operace se nezaznamenávají, ale dá se předpokládat, že uvedené čtyři operace jsou nejčastější a vytvářejí tedy největší zátěž pro server.

Ze záznamů za období 1. leden 2001 - 30. listopad 2001 jsem pro každý ze školních "prostorů" vybral jednu minutu, ve které došlo k největšímu počtu každé z uvedených typů operace.

Tabulka 1. Maximální počet požadavků za jednu minutu
Požadavek Stará budova Žižkov Studijní centrum Jarov Jižní město Ostatní Celkem
Ověření protažené identifikační karty 176 22 5 - 203
Kontrola oprávněnosti přístupu k počítačové síti 144 10 38 40 232
Půjčení klíčku 13 4 4 - 21
Vrácení klíčku 16 16 3 - 35
Celkem 349 52 50 40 491

tabulky vyplývá, že pesimistický odhad maximálního počtu operací za jednu minutu je 491. Lze očekávat, že s růstem počtu funkcí systému se zátěž serveru bude dále zvyšovat. Jak se server bude v takto extrémní situaci chovat, závisí také na způsobu zpracování požadavků. Pokud jsou vyřizovány v jednom vlákně (tedy sériově), bylo by nutné, aby byla doba trvání jedné operace rovna 122 ms (60 s / 491). Protože server musí pro každý požadavek provést relativně náročnou databázovou operaci, není takovýto přístup vhodný. Z této skutečnosti vyplývá další požadavek na architekturu, kterým je podpora více-vláknových aplikací.

2.1.3 Více-vláknové (multithreading) aplikace

Více-vláknová aplikace je taková aplikace, která je schopná provádět několik operací (procesů) současně (paralelně). Architektura COM tento přístup naštěstí podporuje. Při vytváření COM komponenty, vybere její autor jeden z možných vláknových modelů. Každý model má svá pravidla, která musí komponenta dodržovat. Jednotlivými vláknovými modely se budu zabývat v několika částech kapitoly 4 (jejich přehled se stručnou charakteristikou obsahuje Tabulka 4).

Při vývoji více-vláknových aplikací se programátor setká s problémem přístupu ke sdíleným datům. Je zřejmé, že pokud v rámci aplikace běží paralelně několik vláken, snadno se stane, že dvě z nich začnou pracovat se stejnými daty. Nejhorší situace vynikne, pokud obě vlákna začnou zároveň zapisovat na stejnou adresu v paměti. Existuje několik technik řešících tento problém, jako například kritické sekce. Tato problematika sice nesouvisí přímo s architekturou COM, přesto se jí budu muset místy stručně věnovat.

Dalším rysem více-vláknových aplikací je povinnost otevřít nové spojení na databázi pro každé vlákno (pokud vlákno k databázi přistupuje). Protože databázová spojení jsou vzácné zdroje, je nutné se postarat o jejich efektivní využití.

2.1.4 Řízení zdrojů

Každé vlákno, které pracuje s globálním zdrojem (typicky databázovým spojením), tento zdroj blokuje a tím brání ostatním vláknům v jeho použití. Protože počet současných spojení na databázový server je často licenčně omezen (takto je to i v našem případě), není vhodné, aby každé vlákno vytvářelo vlastní spojení k databázi.

Pro efektivní řízení databázových spojení (i jiných zdrojů) lze využít faktu, že komponenta COM běžící na serveru (každá ve svém vlákně) pracuje, jen pokud to klient vyžaduje. A klientské aplikace většinou pouze čekají na "pomalého" uživatele. Je tedy zřejmé, že není nutné, aby vlákno serveru "blokovalo" spojení po celou dobu své existence, stačí aby jej získalo právě na ten okamžik kdy potřebuje provést databázovou operaci.

Jakým způsobem lze vyřešit řízení zdrojů v architektuře COM, si ukážeme v kapitole 4.18.

2.1.5 Výpadek spojení

Při vývoji distribuovaných aplikací, zvláště v případě, kdy klient a server jsou geograficky vzdáleni, nelze pominout problém výpadku spojení. Výpadek může být způsoben chybou na komunikačním kanále, i samotnou serverovou aplikací, např. v důsledku přetížení. Přerušení činnosti některých klientských aplikací systému ESA, by způsobilo nemalé problémy. Je tedy vhodné, aby dokázaly pokračovat v provozu i při ztrátě spojení.

Aby klientská aplikace mohla tento problém uspokojivě řešit, musí dokázat do jisté míry suplovat roli serveru a to i na úrovni datové základny. Zde se objevuje problém synchronizace dat (a nejen jich) po opětovném navázání spojení. Tato problematika je velmi složitá a není mým cílem hledat její nejlepší řešení, to by mohlo být předmětem několika samostatných diplomových prácí. Přesto se ale pokusím v kapitole 4.21 ukázat jedno z řešení, které architektura COM nabízí.

2.1.6 Přenos velkého množství dat

Mnoho z funkcí systému vyžaduje přenos relativně velkého množství dat. Nikdy se nejspíš nebude jednat o objemy řádu megabytů, přesto mě zajímá, jaký vliv bude mít větší velikost parametrů volaných metod na rychlost přenosu a dobu odezvy. Obojí závisí na algoritmu, který převádí hodnoty parametrů na straně serveru do datového proudu, který je přenese přes komunikační kanál a na druhé straně z něj opět "sestrojí" původní hodnoty. Tomuto algoritmu se v terminologii COM říká marshallování [8].

Často také bude třeba přenést pole určitých hodnot (seznam porušení pravidel...). V kapitole 4.10 ukážu, jakým způsobem je nejvhodnější přenášet takováto data.

2.1.7 Bezpečnost

Jednou z nejdůležitějších problematik distribuovaných systémů je zajištění bezpečnosti. Do této problematiky spadá jak zaručení bezpečnosti přenášených dat (vhodným komunikačním protokolem), tak zajištění autorizace klientských aplikací, resp. jejich uživatelů.

Protože mnoho funkcí systému je vázáno na lokalitu, ve které je klientská aplikace spuštěna, je nutné aby server měl možnost zjistit, ze které stanice požadavek přichází. Tato vlastnost by umožnila i provedení autorizace na síti Novell Netware, která se na VŠE používá. A to zjištěním zda uživatel, který je na stanici přihlášen patří mezi osoby oprávněné používat volanou funkci.

2.1.8 Volání klienta (události)

Klasické klient/server aplikace jsou založeny na předpokladu, že komunikaci vždy zahajuje klient a server pouze odpoví na jeho požadavek. V mnoha situacích se však ukazuje, že je vhodné mít i možnost aby server vyvolával určité funkce na klientovi.

Příkladem je upozornění klienta na určitou změnu, například v datech nebo konfiguraci. Server může i po klientovi požadovat provedení nějaké činnosti, třeba zobrazení zprávy uživateli aplikace, nebo vlastní ukončení před vypnutím serveru atd.

Architektura COM specifikuje několik způsobů jak volání klienta řešit. Kombinace těchto postupů s jinými požadavky (zejména více-vláknový server) přináší mnoho problémů. Věnovat se jim budu v kapitole 4.11.

2.1.9 Propojení sjinými systémy (webový server)

V současné době je důležité mít možnost zpřístupnit některé z funkcí systému nejen prostřednictvím dedikovaných klientů, ale i pomocí webového rozhraní. Pro uživatele tím odpadne nutnost mít k dispozici klientskou aplikaci, stačí znát URL pro přístup ke službě. V kapitole 4.16 popíšu jednoduchý příklad, jak služby poskytované COM serverem integrovat s webovým serverem.

2.2 Výběr řešené oblasti

Usoudil jsem, že nejlepším způsobem, jak posoudit vhodnost použití architektury COM pro vývoj informačního systému ESA, je pokusit se implementovat jeho malou ale reprezentativní část. Snažil jsem se najít takový subsystém, kterým bych co nejvíce pokryl požadavky uvedené v předchozí části. Zároveň bylo nutné, aby implementovaná část byla co nejméně závislá na zbytku systému, aby byla výsledkem mého snažení skutečně smysluplná a funkční aplikace.

Volba padla na funkci půjčování klíčků. Už v době její implementace do současné verze systému se jednalo o funkci, která byla do celku přidána jaksi nepatřičně, jen z důvodu nedostupnosti lepšího řešení. Až dodatečně byli nalezeny možnosti jak ji provázat s ostatními subsystémy, například kontrolou oprávněnosti přístupu k počítačové síti. Tyto vazby však nejsou pro vlastní funkčnost důležité.

Aplikace "Klíčky" zahrnuje tyto funkce:

Podrobnosti o těchto funkcích (vstupní a výstupní parametry, možné chybové stavy...) uvedu na začátku kapitoly 3. Jak už jsem se zmínil, budu se při implementaci těchto funkcí snažit pokrýt co nejvíce požadavků uvedených v předešlé části.

2.2.1 Rychlost odezvy, obsluha více klientů, řízení zdrojů, více-vláknové (multithreading) aplikace

Tyto obecné požadavky souvisejí se všemi funkcemi systému a nemá smysl je dále rozebírat ve vztahu k aplikaci "Klíčky".

2.2.2 Výpadek spojení

Přerušení spojení se serverem v případě aplikace "Klíčky" jistě není, z hlediska systému jako celku, fatální. Obsluze však jistě způsobí velké problémy. Ty budou tím větší čím méně údajů o vypůjčených klíčcích bude mít klientská aplikace lokálně uložena. Není těžké si představit situaci, kdy je klient, který zná všechny potřebné údaje (seznam identifikačních karet, informace o již vypůjčených klíčcích...), schopen pokračovat v práci i bez spojení s aplikačním serverem. Musíme se ovšem, alespoň po dobu výpadku, vzdát všech výhod, které klient/server architektura přináší. Jedná se například o rozšířené kontroly navázané na další části informačního systému (spojení s kontrolou oprávněnosti přístupu, on-line ověření identifikační karty oproti primárnímu seznamu...).

Komplikace nastane v okamžiku obnovení spojení. Je vhodné, aby došlo k synchronizaci lokálních dat a dat na serveru, zejména klientská aplikace musí dát serveru "vědět", k jakým změnám po dobu ztráty spojení došlo (které klíčky byli vypůjčeny, které vráceny...).

2.2.3 Přenos velkého množství dat

K simulaci tohoto požadavku využiji funkci zobrazení "seznamu klíčků s informacemi o jejich stavu". Aby klient mohl tento seznam zobrazit, musí ho nejdříve ze serveru získat. Nejedná se o nijak závratný objem dat. Jak jsem již zmínil, tato aplikace nevyžaduje přenos většího množství dat pro žádnou funkci. Přesto jde již o řádově jiný problém, než přenos několika čísel, maximálně řetězců, jako u ostatních úloh typu "ověření identifikační karty".

2.2.4 Bezpečnost

Bezpečnostní hledisko není pro tuto aplikaci nijak důležité. Pokud však technicky vyřeším autorizaci připojení klienta k serveru na základě adresy (např. IP) pracovní stanice, budu znát i jeho fyzické umístnění. Se touto informací mohu rozšířit funkcionalitu serveru o automatické určení sady klíčků, která má být touto instancí klientské aplikace obsluhována.

Možností využití je víc, pokud budu mít možnost odlišit klienty, můžu například zajistit ukládání uživatelských preferencí do databáze nebo obměňovat aplikační logiku podle lokality (změna algoritmu výběru volného klíčku ze sekvenčního na náhodný, atp.).

2.2.5 Volání klienta (události)

Techniku upozorňování klientských aplikací na vzniklé události, lze pro aplikaci "Klíčky" využít zejména pro vyvolání aktualizační procedury v případě změny dat.

Taková potřeba může mít dvě příčiny. Tou první je zásah do dat ze strany administrátora systému (omezení/rozšíření počtu půjčovaných klíčků...). Mohu také požadovat, aby měl systém schopnost bezpečně obsluhovat stejnou sadu klíčků z více stanic (jako tomu je na studijním centru Jarov). Pak je třeba, po každé změně způsobené jedním klientem, uvědomit všechny ostatní stanice, aby mohli aktualizovat svá lokálně uložená data.

3. Analýza a design

V této kapitole budou podrobně popsány všechny požadavky na systém a to tak podrobně, abych již podle nich mohl zahájit vlastní vývoj aplikace. Požadavky na systém jsem rozdělil na část pojednávající o architektuře (počet vrstev a jejich účel) a na část detailně popisující funkce. Všechny tyto požadavky (zejména funkční) lze považovat zadané. Nebudu zde zdůvodňovat, proč byly stanoveny právě takto, mým cílem je pouze najít jejich nejvhodnější implementaci.

3.1 Architektura

Aby bylo dosaženo cíle této práce, je nezbytné, aby architektura aplikace "Klíčky" byla plně identická s architekturou celého informačního systému ESA.

"Klíčky" (resp. IS ESA) je klasická třívrstvá klient/server aplikace. První vrstvou je databáze, ve které jsou soustředěna veškerá data potřebná k provozu. Druhá vrstva je aplikační server, který slouží jako prostředník mezi databází a třetí vrstvou, tedy klientskou aplikací. Server je velmi "tenký", protože většina funkcí je zajištěna již na databázovém serveru prostřednictvím uložených procedur (stored procedures). Klient je program, který umožňuje obsluze využívat služeb systému, tedy správu určité sady klíčků. V IS ESA mají všechny vrstvy samozřejmě mnoho dalších, zde nepopsaných, funkcí.

Obecně mohou být všechny tři vrstvy umístněny na různých stanicích. Přesto lze očekávat, že databázový i aplikační server poběží na jednom vyhrazeném serveru, protože databáze je vyhrazena pouze pro IS ESA a k jejich oddělení tedy není důvod. Naopak u klientských aplikací se zřejmé, že mohou být spuštěny na mnoha stanicích a to ať již vyhrazených přímo k tomu účelu (např. stanice u vchodu do školní studovny...) nebo soukromých stanicích uživatelů systému (osobní počítač vedoucího oddělení správy počítačových učeben...).

Zatímco komunikace databázový server - aplikační server není vzhledem k tématu práce nyní zajímavá, komunikace aplikační server - klient je jejím základem.

3.2 Funkce systému

Převážná část funkcí aplikace "Klíčky", resp. funkcí, které poskytuje aplikační server klientské aplikaci, se přirozeně týká správy klíčků v určité sadě.

Protože většina operací se na klientské aplikaci potvrzuje protažením identifikační karty musí systém umožnit ověření karty a získaní určitých informací o jejím vlastníku. Tato funkce patří logicky do jiné skupiny než funkce pro správu klíčků, protože bude používána i v jiných aplikacích než pro půjčování klíčků. Tato vlastnost bude zohledněna při definování rozhraní (interfaces) aplikačního serveru (viz kapitola 4.4).

Při startu musí klientská aplikaci zjistit několik parametrů, které ovlivňují chování systému a závisí na geografickém umístnění stanice, na které je klientská aplikace spuštěna. V aplikaci "Klíčky" je takovým parametrem typicky sada klíčků, kterou instance obsluhuje. Z hlediska bezpečnosti i řízení distribuované aplikace je vhodné, aby o těchto parametrem rozhodoval aplikační (nebo databázový) server a ne klient. Tím tedy přibývá další funkce (resp. skupina funkcí), kterou klient volá jednorázově při svém startu.

Nyní podrobně rozeberu požadované funkce včetně jejich vstupů, výstupů a možných chybových stavů, které při volání funkce mohou nastat.

3.2.1 Určení parametrů

V této jednoduché aplikaci se určení parametrů redukuje na vrácení názvu obsluhované sady klíčků. Rozhodnutí provádí aplikační server na základně umístnění stanice, na které je spuštěna klientská aplikace, tedy např. podle její IP adresy nebo doménového jména. Server může také klientu odmítnout možnost spravovat jakoukoli sadu klíčků. Je přirozené, že tuto možnost budou mít pouze dedikované stanice.

Vstupní parametry:
žádné
Výstupní parametry:
název spravované sady klíčků
Možné chybové stavy:
stanice není oprávněna spravovat klíčky

3.2.2 Ověření identifikační karty

Pro uskutečnění operací, které jsou vztaženy k určité osobě (např. vypůjčení klíčku), je nutné znát nějaký údaj, který osobu jednoznačně identifikuje (rodné číslo). Aby obsluha klienta nemusela takovýto údaj ručně (z klávesnice) zadávat, provádí se identifikace osoby automaticky protažením její identifikační karty. Přímo na kartě však žádný identifikační údaj obsažený není, údaje o držitelích karet jsou uloženy jen v databázi systému. Je tedy potřeba, aby klient nejdříve tyto informace ze serveru získal na základě čísla identifikační karty získaného buď z čárového kódu nebo z čipu. Vedlejším, ale velmi důležitým, efektem této funkce je ověření platnosti identifikační karty (platnost karty může být již ukončena, karta může být dokonce falešná...).

Vstupní parametry:
číslo identifikační karty
Výstupní parametry:
údaj identifikující držitele karty (rodné číslo)
jméno vlastníka (pouze pro informační účely)
poznámka (např. informace, že držitel karty porušil v minulosti určitá pravidla a v rámci sankce nemá možnost po danou dobu využívat určitých služeb)
Možné chybové stavy:
neznámá identifikační karta
neplatná identifikační karta (karta může být například zablokována z důvodu ztráty, s touto eventualitou naše zjednodušená aplikace nepočítá)
neznámý držitel identifikační karty (při vhodně navrženém datovém modelu by tato situace neměla nastat)

3.2.3 Souhrnné informace o klíčcích v sadě

Tato funkce má zejména informační účel. Umožňuje obsluze získat rychlý přehled o stavu spravované sady klíčků.

Vstupní parametry:
označení sady klíčků
Výstupní parametry:
celkový počet klíčků v sadě
počet vypůjčených klíčků
počet volných klíčků
Možné chybové stavy:
neznámá sada klíčků

3.2.4 Získání údajů o klíčku

Tato funkce umožní obsluze získat detailní informace o jednom konkrétním klíčku v sadě. Mezi tyto údaje patří velikost skříňky a jeho stav (volný, půjčený, rezervace, ztracený). Pokud je klíček půjčený, je nutné znát i osobu, které byl půjčen a kdy byl půjčen. Poslední dvě uvedené informace jsou důležité v případě, že osoba nevrátí klíček do stanovené doby a je tedy nutné přijmout určitá opatření případně dokonce sankce.

Vstupní parametry:
označení sady klíčků
číslo klíčku
Výstupní parametry:
stav klíčku (volný, půjčený, rezervace, ztracený)
údaj identifikující osobu, které byl půjčen (jen pokud je půjčený)
jméno osoby, které byl půjčen (jen pokud je půjčený)
čas, kdy byl půjčen (jen pokud je půjčený)
Možné chybové stavy:
neznámá sada klíčků
klíček s tímto číslem v sadě není
klíček byl půjčen neznámé osobě (při vhodně navrženém datovém modelu by tato situace neměla nastat)

3.2.5 Získání seznamu klíčků v sadě včetně jejich stavu

Pro obsluhu klientské aplikace je mnohdy nezbytné mít možnost vyvolat podrobný seznam všech klíčků v sadě, ne jen jednoho vybraného. Aby klientská aplikace takový seznam získala, musela by předchozí funkci volat mnohokrát pro každý klíček zvlášť. Aby se zabránilo plýtvání zdroji (opakované ověřování mnoha údajů, zbytečné plýtvání kapacitou komunikačního kanálu...), je vhodné, aby aplikační server byl schopen vrátit celý seznam najednou. Od klientské aplikace se očekává, že získaná data zobrazí vhodnou formou uživateli.

Vstupní parametry:
označení sady klíčků
Výstupní parametry:
seznam klíčků, s informacemi shodnými s výstupními parametry předešlé funkce
Možné chybové stavy:
neznámá sada klíčků

3.2.6 Zjištění, zda určitá osoba má půjčený klíček

Po té co se osoba identifikuje svojí kartou následují logicky dva možné kroky: půjčení nebo vrácení klíčku (viz následující funkce). O tom, kterým z kroků se má pokračovat může rozhodnout obsluha podle žádosti osoby. Může o tom také rozhodnout klientská aplikace podle toho zda již má osoba nějaký klíček (následuje jeho vrácení) nebo nemá (následuje půjčení). Zvlášť v běžné situaci, kdy není možné, aby jedna osoba měla vypůjčeno několik klíčků, je tento postup rozumný.

Vstupní parametry:
označení sady klíčků
údaj identifikující osobu (pravděpodobně získaný ověřením identifikační karty)
Výstupní parametry:
má půjčený klíček (ano/ne)
číslo půjčeného klíčku (pokud má nějaký půjčený)
Možné chybové stavy:
neznámá sada klíčků
neznámá osoba

3.2.7 Navržení klíčku k půjčení

Po zvolení funkce půjčení klíčku (ať již rozhodnutím obsluhy nebo automaticky) je třeba určit, který klíček má být půjčen. Stejně jako předchozí rozhodnutí může být tato operace provedena ručně nebo automaticky. Při automatickém rozhodnutí (voláním této funkce) musí klientská aplikace určit jaké velikosti má být skříňka, ke které klíček patří. Postup je stanoven tak, že když již není volná skříňka požadované velikosti je navržena skříňka větší (pokud je taková k dispozici), opačně to neplatí. Klient dále předává identifikační údaj osoby, které bude klíček (po potvrzení obsluhou, viz další funkce) půjčen. Tento údaj sice nemá v současnosti vliv na číslo navrženého klíčku (to se může později změnit), server má však možnost již nyní dát klientovi vědět, že dané osobě není možné klíček půjčit. Tím se předejde pozdějším komplikacím.

Vstupní parametry:
označení sady klíčků
údaj identifikující osobu (pravděpodobně získaný ověřením identifikační karty)
číslo požadované velikosti skříňky
Výstupní parametry:
navržené číslo klíčku
velikost skříňky navrženého klíčku (může být větší nebo rovna požadované velikosti)
Možné chybové stavy:
neznámá osoba
osoba má již půjčený klíček (i v jiné sadě)
neznámá sada klíčků
neexistuje žádná skříňka požadované velikosti
není volný žádný klíček od skříňky požadované (nebo větší) velikosti

3.2.8 Vypůjčení klíčku

Po té co je potvrzena totožnost osoby a je i rozhodnuto o čísle klíčku, který jí má být půjčen, je možné přistoupit k závěrečné fázi, tedy k potvrzení půjčení klíčku osobě. Klient musí počítat i se situací, že požadovaný klíček byl mezitím (od jeho navržení) vypůjčen na jiné stanici.

Vstupní parametry:
označení sady klíčků
údaj identifikující osobu (pravděpodobně získaný ověřením identifikační karty)
číslo klíčku (získané voláním předchozí funkce)
Výstupní parametry:
žádné
Možné chybové stavy:
neznámá osoba
osoba má již půjčený klíček (i v jiné sadě)
neznámá sada klíčků
požadovaný klíček neexistuje
požadovaný klíček není volný

3.2.9 Vrácení klíčku

Poslední požadovanou funkcí je vrácení vypůjčeného klíčku. Protože vycházím z podmínky, že každá osoba může mít půjčený jen jeden klíček, je jediným potřebným parametrem této funkce identifikační údaj osoby. Druhý parametr, tedy označení sady, je přidán jako dodatečná kontrola.

Vstupní parametry:
označení sady klíčků
údaj identifikující osobu (získaný z identifikační karty)
Výstupní parametry:
žádné
Možné chybové stavy:
neznámá osoba
neznámá sada klíčků
osoba nemá půjčený klíček z uvedené sady

4. Inkrementální vývoj aplikace

4.1 Databázová vrstva

V této kapitole se budu velmi stručně věnovat popisu databázové vrstvy systému. Tato vrstva není pro tuto práci příliš důležitá. Ale jak jsem již zmínil, je v uložených procedurách databáze soustředěna velká část funkcionality IS, proto není možné se jejímu popisu vyhnout.

4.1.1 Databázový server a vytvoření databáze

Pro databázi "Klíčky" i budoucí informační systém ESA budu využívat databázový server Sybase Adaptive Server Anywhere 8. Pro databázový server jsem vyhradil minimálně 5120 KB vyrovnávací paměti.

Pro projekt jsem vytvořil novou databázi s názvem "KLICKY". Z parametrů, které se zadávají při vytváření databáze považuji za důležité zmínit, že databáze nepoužívá transakční log, velikost databázové stránky jsem nastavil na 2048 bajtů a řazení se provádí podle kódové stránky 852 (Latin 2).

4.1.2 Datový model

Protože testovací aplikace je velmi jednoduchá, není ani datový model příliš rozsáhlý a komplikovaný. Obsahuje těchto šest entit:

OSOBA
Tabulka OSOBA obsahuje seznam osob. O každém jedinci jsou v testovací databázi dostupné jen tři údaje: rodné číslo, jméno a poznámku. V budoucím IS ESA bude údajů mnohem víc, ale pro náš jednoduchý příklad nejsou nutné.
IDKARTA
Každá identifikační karta je přiřazena k určité osobě. Jeden člověk, však může být držitelem několika karet. Karty v aplikaci "Klíčky" slouží jen jako prostředek k ověření identity, systém tedy neřeší jejich výrobu, ani aktualizaci seznamu v databázi.
KLICEK_SADA, KLICEK_STAV
Tyto tabulky jsou "číselníky" obsahující seznam sad a stavů klíčků, tedy hodnot, kterých mohou nabývat odpovídající atributy entit KLICEK a KLICEK_LOG. Obsah obou tabulek je v čase neměnný (s výjimkou přidání další sady klíčků při rozšíření systému).
KLICEK
KLICEK je nejdůležitější entita modelu. Obsahuje záznam pro každý klíček ve všech sadách obsluhovaných systémem. Pro provoz je podstatná hodnota atributu KLICEK_STAV, nabývající některé z hodnot ze stejnojmenné tabulky. Možné hodnoty jsou: volný, půjčený, rezervovaný, ztracený. Za běžného chodu systému se mění jen hodnota právě tohoto atributu.
KLICEK_LOG
Každá operace půjčení resp. vrácení klíčku se zaznamenává do této tabulky. Při půjčení se přidá nový záznam a vyplní se atributy KLICEK_SADA, KLICEK, PUJCENO a RC. Atribut VRACENO zůstává nenastavený až do okamžiku vrácení. Tím vzniká určitá duplicita informace s hodnotou atributu stav entity KLICEK. Například při vrácení klíčku musí být vždy změněna resp. nastavena hodnota obou zmíněných atributů. Tato situace by měla být ošetřena v databázi buď pomocí triggerů nebo na úrovni uložených procedur (tento postup použiji).
Obrázek 1. Fyzický datový model databáze "KLICKY"
Obrázek 1. Fyzický datový model databáze "KLICKY" (5 kB)

Datový model jsem vytvořil v CASE nástroji Power Designer, který podle modelu vygeneruje skript v jazyce SQL. Vytvoření skript jsem spustil na již dříve připravené databázi. Do vytvořených tabulek jsem příkazy jazyka SQL vložil testovací záznamy.

4.1.3 Definování funkcionality na úrovni databázového serveru: uložené procedury

Jak jsem již zmínit dříve, velká část funkcionality aplikace je implementována již na úrovni databázové vrstvy v uložených procedurách. V poslední části o databázi se budu věnovat právě jim, a to nikoli celému popisu jejich tvorby, ale hlavně jejich rozhraní (vstupním a výstupním parametrům), které potřebujeme znát pro vývoj dalších vrstev systému.

Každá z funkcí popsaných v předešlé části je realizována právě jednou uloženou procedurou. Tento fakt usnadní následující popis, protože nebude nutné zdůrazňovat jestli "funkcí" myslím databázovou funkci (uloženou proceduru) nebo funkce aplikační serveru resp. klientské aplikace.

Procedury jsem vytvořil jazykem SQL resp. jeho rozšířením Watcom-SQL, používaném v databázích Sybase. K vytvoření procedury slouží příkaz CREATE PROCEDURE. Jako příklad uvádím kód pro vytvoření funkce KLICEK_VRACENI.

Ukázka 1. Uložená procedura KLICEK_VRACENI
create procedure
dba.klicek_vraceni(in sada char(20),in osoba char(10),out vysledek integer)
begin
  declare cislo_sady integer;
  declare cislo_klicku integer;
  if not exists(select * from osoba where rc = osoba) then
    set vysledek=5
  else
    set cislo_sady=null;
    select klicek_sada into cislo_sady from klicek_sada where nazev = sada;
    if cislo_sady is null then
      set vysledek=3
    else
      set cislo_klicku=null;
      select max(klicek) into cislo_klicku from klicek_log where
        rc = osoba and klicek_sada = cislo_sady and vraceno is null;
      if cislo_klicku is null then
        set vysledek=10
      else
        update klicek_log set vraceno = now(*)
          where klicek_sada = cislo_sady and rc = osoba;
        update klicek set klicek_stav = 0 where klicek_sada = cislo_sady and
          klicek = cislo_klicku;
        set vysledek=0
      end if
    end if
  end if
end

Vytvořené uložené procedury jsou přiřazené k funkcím IS takto:

Tabulka 2. Názvy a parametry uložených procedur
Funkce IS Název uložené procedury Parametry uložené procedury
Ověření identifikační karty IDKARTA_OVERENI IN KARTA_CISLO CHAR(7), OUT KARTA_PLATNOST INTEGER, OUT VLASTNIK CHAR(10), OUT VLASTNIK_JMENO VARCHAR(50), OUT VLASTNIK_POZNAMKA VARCHAR(200)
Souhrnné informace o klíčcích v sadě KLICKY_V_SADE IN SADA CHAR(20), OUT VYSLEDEK INTEGER, OUT CELKEM INTEGER, OUT PUJCENE INTEGER, OUT VOLNE INTEGER
Získání údajů o klíčku KLICEK_STAV IN SADA CHAR(20), IN CISLO_KLICKU INTEGER, OUT VYSLEDEK INTEGER, OUT STAV INTEGER, OUT STAV_POPIS CHAR(30), OUT VLASTNIK CHAR(10), OUT VLASTNIK_JMENO CHAR(50), OUT PUJCEN_OD TIMESTAMP
Zjištění zda určitá osoba má půjčený klíček VLASTNI_KLICEK IN OSOBA CHAR(10), IN KLICEK_ZE_SADY CHAR(20), OUT VYSLEDEK INTEGER, OUT PUJCENY_KLICEK INTEGER
Navržení klíčku k půjčení KLICEK_NAVRHNI IN SADA CHAR(20), IN OSOBA CHAR(10), IN POZADOVANA_VELIKOST INTEGER, OUT VYSLEDEK INTEGER, OUT NAVRZENY_KLICEK INTEGER, OUT VELIKOST_KLICKU INTEGER
Vypůjčení klíčku KLICEK_VYPUJCENI IN SADA CHAR(20), IN OSOBA CHAR(10), IN POZADOVANY_KLICEK INTEGER, OUT VYSLEDEK INTEGER
Vrácení klíčku KLICEK_VRACENI IN SADA CHAR(20), IN OSOBA CHAR(10), OUT VYSLEDEK INTEGER

4.1.4 Chybové kódy

Všechny definované procedury indikují hodnotou výstupního parametru VYSLEDEK, jestli byla požadovaná funkce úspěšně dokončena (hodnota nula) nebo došlo k chybě (nenulová hodnota). Z hodnoty parametru při chybě může volající aplikace zjistit, k jaké chybě došlo. Aby se daly kontroly chyb při volání všech procedur zobecnit, jsou návratové chybové kódy sjednoceny. Chybové kódy negenerované v databázi budou mít čísla 100 a výše (nejsou v následujícím seznamu).

Tabulka 3. Chybové kódy při volání uložených procedur
Kód Chyba
1 stanice není oprávněna spravovat klíčky
2 neznámá identifikační karta
3 neznámá sada klíčků
4 klíček s tímto číslem v sadě není
5 neznámá osoba
6 osoba má již půjčený klíček (i v jiné sadě)
7 neexistuje žádná skříňka požadované velikosti
8 není volný žádný klíček od skříňky požadované (nebo větší) velikosti
9 požadovaný klíček není volný
10 osoba nemá půjčený klíček z uvedené sady

4.1.5 Přístupová práva

Aplikační server se musí pro připojení autorizovat, proto jsem na databázi vytvořil uživatelské konto "Server". Tento uživatel nemá práva pro vytváření databázových objektů (ani jejich rušení a změnu). Nemá ani oprávnění k přístupu k tabulkám databáze. Jediný způsob, jak může s databází pracovat, je voláním uložených procedur. To proto, že právě procedury jsou navrženy tak, aby jejich volání převedlo data z jednoho konzistentního stavu do jiného. Server má práva na spouštění všech procedur, které byly uvedeny v předchozí části.

4.2 Výchozí parametry projektu

V této kapitole se poprvé konečně dostávám k technologii COM. Vývoj systému začnu serverovou částí. Mimo vlastní implementace funkcí je třeba zvolit typ serveru, vláknový model, podporu více instancí objektu v serveru a nadefinovat rozhraní (interfaces), která server implementuje. Předpokládám, že čtenář je již s těmito základními pojmy architektury COM seznámen, nebudu je tedy objasňovat. Pouze vyberu vhodné parametry pro vyvíjenou aplikaci. Je možné, že se konfigurace, kterou zvolím pro počáteční fázi, později změní, s tím jak se budou zvyšovat nároky na systém. Mohlo by tedy být vhodné zvolit již na začátku dostatečně obecné řešení, který všechny požadavky splní. To by ale učinilo aplikaci již zpočátku příliš komplikovanou. Protože jsem se rozhodl pro cestu postupného rozšiřování aplikace, musím přijmout fakt, že bude možná nutné později začít znovu "od nuly". Samozřejmě, že již napsaný kód bude možné znovu použít.

COM (resp. DCOM) zná tři typy serveru: lokální in-process server (DLL knihovna), lokální out-of-process server (spustitelný soubor) a vzdálený server (spustitelný soubor na jiné stanici) [3]. Protože náš systém je distribuovaný, jedinou možnou volbou je třetí možnost, tedy vzdálený server. Ten musí být, podle požadavků DCOM implementován spustitelnou EXE aplikací. Pokud bychom chtěli použít DLL knihovnu, je nutné ji spustit v rámci tzv. surrogate-procesu. K tomuto účelu je standardně k dispozici DLLHOST.EXE. Server musí být implementován jako DLL knihovna také pokud má být používán v rámci Microsoft Transaction Serveru (MTS), resp. v COM+. Této problematice bude věnována kapitola 4.17.

Obrázek 2. Schéma architektury COM/DCOM
Obrázek 2. Schéma architektury COM/DCOM (7 kB)

Mezi čtyřmi vláknovými modely (single, apartment, free a both) volím model apartment jako kompromis mezi jednoduchostí implementace (na rozdíl od modelu free) a žádné podpory více-vláknových aplikací modelu single. Výběr modelu apartment znamená, že veškerá lokální data COM objektů jsou z hlediska vláken bezpečná. To neplatí pro globální data (globální proměnné).

Tabulka 4. Přehled vláknových modelů COM [7]
Vláknový model Popis Výhody a nevýhody
Single Žádná podpora pro vlákna. Požadavky klientů jsou vyřizovány sériově. Server není více-vláknový (snadná implementace). Malý výkon.
Apartment Metody objektu mohou být volány jen z vlákna, ve kterém byl objekt vytvořen. Každý objekt může být volán z jiného vlákna. Data instance objektu jsou bezpečná, což neplatí pro globální data. Snadná implementace objektu. Zvýšení výkonu.
Free Klienti mohou volat libovolnou metodu libovolného objektu v jakýkoli čas z libovolného vlákna. Přístup k lokálním i globálním datům musí být zajištěn kritickou sekcí (nebo jiným způsobem). Implementace objektu je obtížná. Vysoký výkon.
Both Objekt může obsluhovat klienty s modelem Apartment i Free. Spojuje výhody modelů Apartment a Free.

Třetím požadovaným parametrem je možnost vytvoření více instancí COM objektu. V opačném případě by se pro každou klientskou aplikaci spouštěl samostatný server (ve smyslu programu) s jednou instancí COM objektu.

Poslední vlastností, která je pro komunikaci s klientem nejdůležitější, jsou definice rozhraní, která server (resp. COM objekt) implementuje. Jak jsem již dříve uvedl, rozdělil jsem funkce systému do tří skupin. Ve dvou z nich je jen po jedné funkci (určení parametrů resp. ověření identifikační karty), hlavní (třetí) skupina obsahuje všechny funkce související s klíčky. Toto rozdělení se může zdát v jednoduchém systému zbytečné. Pokud by ale server poskytoval více funkcí, jako např. budoucí IS ESA, je definování více rozhraní výhodné. Pokud bude v budoucnu nutné změnit například parametry jedné z metod se současným zachováním zpětné kompatibility, je nutné nadefinovat novou verzi celého rozhraní obsahujícího změněnou metodu [3]. Čím méně metod je v každém rozhraní, tím menší změny bude nutné provést na obou úrovních aplikace.

4.3 Vytvoření nového COM serveru v prostředí Delphi

Vytvoření COM serveru zahájíme otevřením nového projektu aplikace. Do prázdného projektu přidáme COM objekt volbou File/New/ActiveX/COM Object. Zobrazí se průvodce vytvořením COM objektu. Je potřeba zadat jméno objektu ("Esa") a vyplnění údajů vyplývajících z parametrů určených v předešlé kapitole. Tedy nastavit Instancing na Multiple instance a Threading model na Apartment. Delphi vygenerují základní kód COM objektu, vloží do projektu knihovnu typů (type library) a otevřou její editor. Delphi také sami generují identifikátory GUID resp. IID. Do inicializační části programu se také doplní kód, který vytvoří class factory pro objekt. Class factory je reprezentovaná třídou TTYPEDCOMOBJECTFACTORY (resp. TAUTOOBJECTFACTORY).

4.4 Rozhraní aplikačního serveru

Náš projekt bude obsahovat jen jeden základní COM objekt, který však bude implementovat několik rozhraní. Každé rozhraní obsahuje metody příslušné do určité logické skupiny. Tyto skupiny a metody jsem již popisoval dříve. Nyní jen obecnému popisu dáme formální úpravu a to jazykem IDL (pomocí editoru knihovny typů) [8].

V první fázi v knihovně typů definujeme zmíněný objekt se jménem ESA a jeho tři rozhraní odpovídající skupinám funkcí: IESAIDKARTA, IESAKLICEK, IESANASTAVENI (názvy rozhraní dle konvence začínají znakem I). Do každého rozhraní doplníme metody. Jejich parametry jsou zřejmé z IDL kódu (viz Ukázka 2). Metody lze doplnit buď pomocí grafického rozhraní editoru knihovny typů nebo zapsáním kódu v jazyce IDL na záložce "Text". Kód rozhraní lze také zobrazit v ekvivalentní podobě v jazyce Object Pascal (metu Tools, volba Environment Options, záložka Type Library). Pro prvotní seznámení s jazykem IDL lze použít volby "Display update before refreshing", která způsobí, že před uložením knihovny typů se zobrazí okno, ve kterém je podrobně rozepsáno, jak se jednotlivé změny provedené grafickým rozhraním promítnou do kódu IDL.

Ukázka 2. Části kódu definice rozhraní objektu Esa (IDL)
...
library EsaServer
{
  ...
   interface IEsaIdkarta: IUnknown
  {
    [id(0x00000001)]
    HRESULT _stdcall IdkartaOvereni([in] BSTR KartaCislo, [out] long *KartaPlatnost,
      [out] BSTR *Vlastnik, [out] BSTR *VlastnikJmeno, [out] BSTR *VlastnikPoznamka );
  };
  ...
   interface IEsaKlicek: IUnknown
  {
    [id(0x00000001)]
    HRESULT _stdcall VlastniKlicek([in] BSTR Osoba, [in] BSTR KlicekZeSady,
      [out] long * Vysledek, [out] long * PujcenyKlicek );
    [id(0x00000002)]
    HRESULT _stdcall KlickyVSade([in] BSTR Sada, [out] long * Vysledek,
      [out] long * Celkem, [out] long * Pujcene, [out] long * Volne );
    [id(0x00000003)]
    HRESULT _stdcall KlicekNavrhni([in] BSTR Sada, [in] BSTR Osoba,
      [in] long PozadovanaVelikost, [out] long * Vysledek, [out] long * NavrzenyKlicek,
      [out] long * VelikostKlicku );
    [id(0x00000004)]
    HRESULT _stdcall KlicekVypujceni([in] BSTR Sada, [in] BSTR Osoba,
      [in] long PozadovanyKlicek, [out] long * Vysledek );
    [id(0x00000005)]
    HRESULT _stdcall KlicekVraceni([in] BSTR Sada, [in] BSTR Osoba,
      [out] long * Vysledek );
  };
  ...
   interface IEsaNastaveni: IUnknown
  {
    [id(0x00000001)]
    HRESULT _stdcall SpravovanaSadaKlicku([out] long * Vysledek, [out] BSTR * Sada );
  };
  ...
  coclass Esa
  {
    [default] interface IEsaIdkarta;
    interface IEsaKlicek;
    interface IEsaNastaveni;
  };
};

V tomto okamžiku považuji za nutné upozornit na fakt, že právě definované rozhraní budu v průběhu práce měnit, tak jak budu do projektu zapojovat nové poznatky. Tento přístup ale odporuje principu architektury COM, která předpokládá, že rozhraní jsou v čase neměnná. Při správném postupu bych již nyní měl rozhraní navrhnout tak, aby vyhovovalo všem budoucím požadavkům. Tím bych porušil přístup, který jsem si stanovil v úvodu práce, tedy inkrementální vývoj, s postupným využíváním nových poznatků. Problém se týká například využití výjimek místo parametru VYSLEDEK, použití rozhraní IDISPATCH a dual interfaces, atp.

Na druhou stranu již nyní udělám ústupek, který umožní pozdějšímu použití funkce object pooling v COM+. Nepoužiji totiž žádné vlastnosti, přestože to COM (i jeho implementace v Delphi) umožňuje. Důvodem je, že object pooling vyžaduje, aby byl objekt bezstavový, tedy aby si neuchovával žádné informace o svém stavu. Nejzřejmějším projevem stavových informací jsou právě vlastnosti.

Po nadefinování rozhraní COM objektu a jeho uložení vytvoří Delphi soubor .TLB, který obsahuje knihovnu typů a kostru programového kódu objektu, reprezentovaného potomkem třídy TTYPEDCOMOBJECT. Třída obsahuje prázdné implementace metod všech rozhraní objektu. Povšimněte si, že typy parametrů nejsou typy jazyka IDL, ale jejich ekvivalenty v jazyce Object Pascal. Další zajímavostí je konvence volání použitá u všech metod, tedy SAFECALL. Jejím hlavním účelem je předávání výjimek ze serveru na klienta. Budu se jí věnovat v kapitole 4.12.2.

Ukázka 3. Rozhraní třídy implementující metody rozhraní COM objektu
type
  TEsa = class(TTypedComObject, IEsa, IEsaIdkarta, IEsaKlicek, IEsaNastaveni)
  protected
    procedure IdkartaOvereni(const KartaCislo: WideString;
      out KartaPlatnost: Integer; out Vlastnik, VlastnikJmeno,
      VlastnikPoznamka: WideString); safecall;
    procedure KlicekVraceni(const Sada, Osoba: WideString;
      out Vysledek: Integer); safecall;
    procedure KlicekVypujceni(const Sada, Osoba: WideString;
      PozadovanyKlicek: Integer; out Vysledek: Integer); safecall;
    { dalsi metody vypusteny } ...
  end;

4.5 Přístup serveru k databázi

Přístupu k databázi z aplikačního serveru se budu věnovat jen velmi krátce. Z hlediska tématu práce není zajímavý. Zmíním se jen o dvou problémech. Prvním je problematika přístupu k databázi z vícevláknové aplikace (kterým náš program je, protože každá instance COM objektu může běžet ve vlastním vlákně), druhým je optimalizace mnohonásobného volání téže uložené procedury.

Jak jsem již v úvodu zmínil, každé vlákno, které přistupuje do databáze musí mít otevřeno vlastní databázové spojení. Kdyby tomu tak nebylo, hrozilo by, že dojde k současnému zápisu do stejných dat, což může vést k porušení transakčního přístupu a následně dat. Delphi tuto problematiku řeší koncepcí seancí (sessions). Každá seance sdružuje několik spojení (i do různých databází), které jsou vzájemně izolovány. K ošetření současného přístupu dochází buď na úrovni databáze nebo používaného middlewaru (např. BDE). K reprezentaci seance slouží třída (vizuální komponenta) TSESSION. Každý databázový objekt (tabulka, uložená procedura...) musí být asociován s jednou instancí této třídy. Pokud programátor žádnou instanci nespecifikuje, naváže se objekt na výchozí seanci (default session). Každý objekt TSESSION musí mít jméno jedinečné v rámci celé aplikace. Pokud je instancí předem nedefinovaný počet a zejména pokud jsou vytvářeny dynamicky za běhu programu, je vhodné použít automatické pojmenovávání, ke kterému slouží vlastnost AUTOSESSIONNAME [5].

Přístup k databázi jsem v naší aplikaci vyřešil takto:

K problematice optimalizace opakovaného volání uložených procedur jen zmíním, že třída TSTOREDPROC má metodu PREPARE. Tato metoda provede jednorázově optimalizaci parametrů procedury, která by jinak musela probíhat opakovaně před každým spuštěním.

4.6 Implementace metod

Jak již bylo napsáno, je velká část funkcionality systému obsažena již v uložených procedurách. COM objekt ESA většinou jen zprostředkuje požadavek klienta databázi a vrátí mu její odpověď. Složitější metody budu přidávat až v dalších kapitolách. Ze začátku také budu vycházet z předpokladu, že není možné předávat výjimky ze serveru na klienta, protože COM výjimky nepodporuje. Každá metoda tedy musí zajistit, aby ji žádná výjimka neopustila a předat chybový stav výstupním parametrem VYSLEDEK. Předpoklad nepoužitelnosti výjimek opustím v kapitole 4.12.

Ukázka 4. Příklad implementace metody COM objektu (KLICKYVSADE)
procedure TEsa.KlickyVSade(const Sada: WideString; out Vysledek, Celkem,
  Pujcene, Volne: Integer);
begin
  try
    { komponenta na datovem modulu reprezentujici ulozenou proceduru }
    with DataModule.KlickyVSadeProc do
    begin
      ParamByName('sada').Value := Sada; { vstupni parametr }
      ExecProc(); { volani ulozene procedury }
      Vysledek := ParamByName('vysledek').Value; { vystupni parametry }
      Celkem := ParamByName('celkem').Value;
      Pujcene := ParamByName('pujcene').Value;
      Volne := ParamByName('volne').Value;
    end;
  except
    Vysledek := 100; { obecna chyba databaze }
  end;
end;

4.7 Uživatelské rozhraní serveru

Po naprogramování všech metod podle příkladu (Ukázka 4) a zkompilování je server v hrubé podobě připraven. Abychom měli i nějakou uživatelskou odezvu, vytvoříme jednoduché uživatelské rozhraní, které bude zobrazovat počet připojených klientů (resp. počet instancí COM objektu) a počet volání metod. Nejdůležitější funkcí bude zobrazování chybových zpráv, aby měl administrátor systému přehled o jeho chodu.

Obrázek 3. Jednoduché uživatelské rozhraní serveru
Obrázek 3. Jednoduché uživatelské rozhraní serveru (4 kB)

Na vytvoření okna není nic složitého. Komplikace se objeví, když si uvědomíme, že uživatelské rozhraní aplikace obsluhuje jiné vlákno než v jakém běží instance COM objektu. Tímto vláknem je hlavní vlákno aplikace. Pokud by dva klienti zavolali ve stejný okamžik určitou metodu, mohlo by při pokusu o aktualizace počítadla dojít k současnému zápisu do stejného paměťového prostoru. Výsledky mohou být fatální.

Je několik způsobů jak se s tímto problémem vypořádat. Jedním z těch jednoduchých, i když málo efektivních jsou kritické sekce. K jejich obsluze je v knihovnách Delphi definována třída TCRITICALSECTION [5]. Operačním systémem je zaručeno, že ze všech částí kódu programu, které jsou umístněny mezi volání jejích metod ENTER a LEAVE, bude prováděna jen jedna, bez ohledu na fakt, že každý kód je prováděn v jiném vlákně.

Pro obsluhu uživatelského rozhraní jsem vytvořil třídu TROZHRANI s metodami KLIENTPRIPOJEN, KLIENTODPOJEN, METODAZAVOLANA a PRIDEJHLASENI. Instance třídy je vytvořena při startu serveru z jeho hlavního vlákna. Ve svém konstruktoru si objekt zaregistruje vlastní kritickou sekci, kterou použije na zabránění současného volání všech svých metod. Metody jsou volány z vláken instancí COM objektu. KLIENTPRIPOJEN resp. KLIENTODPOJEN jsou volány z konstruktoru resp. destruktoru objektu, METODAZAVOLANA z každé metody rozhraní objektu a PRIDEJHLASENI ze sekcí obsluhy výjimek.

Ukázka 5. Použití kritické sekce
procedure TRozhrani.MetodaZavolana;
begin
  FCriticalSection.Enter; { vstup do kriticke sekce }
  Inc(FPocetVolani);
  ServerForm.VolaniLabel.Caption := IntToStr(FPocetVolani);
  FCriticalSection.Leave; { konec kriticke sekce }
end;
Ukázka 6. Aktualizace uživatelského rozhraní z jiného vlákna
procedure TEsa.KlickyVSade(const Sada: WideString; out Vysledek, Celkem,
  Pujcene, Volne: Integer);
begin
  try
    RozhraniServeru.MetodaZavolana;
    ... { volani ulozene procedury na databazi }
  except
    on E: Exception do
      RozhraniServeru.PridejHlaseni('TEsa.KlickyVSade: ' + E.Message);
  end;
end;

4.8 Klientská aplikace

Nyní když již máme připravenu jednoduchou verzi serveru, přistoupíme k vytvoření klientské aplikace. V porovnání se serverem je práce na klientovi velmi málo. Po vytvoření jednoduchého uživatelského rozhraní (viz Obrázek 4), je třeba pouze vytvořit instanci COM objektu (tím i navázat spojení se serverem) a podle požadavků uživatele volat jeho metody.

Aby kompilátor znal rozhraní serveru je potřeba klauzulí USES vložit automaticky generovanou knihovnu typů. Příslušný soubor má jméno projektu serveru, rozšířené o koncovku TLB. V našem případě tedy ESASERVER_TLB. Pro vytvoření instance objektu ESA se volá metoda CREATE (resp. CREATEREMOTE) automaticky generované třídy COESA. Tyto metody interně volají API funkce COCREATEINSTANCE resp. COCREATEINSTANCEEX. Pro získání reference na jiné rozhraní než IESAIDKARTA (výchozí rozhraní), lze v jazyce Object Pascal použít standardní klíčové slovo AS (viz Ukázka 7 a kapitola 4.13).

Obrázek 4. Uživatelské rozhraní klientské aplikace
Obrázek 4. Uživatelské rozhraní klientské aplikace (3 kB)
Ukázka 7.Vytvoření instance objektu a volání metod
EsaServer := CoEsa.Create;
EsaNastaveni := EsaServer as IEsaNastaveni;
EsaNastaveni.SpravovanaSadaKlicku(Vysledek, SadaKlicku);
if Vysledek <> 0 then raise
  Exception.Create('Nepodařilo se zjistit jakou sadu má klient spravovat.');

Před spuštěním klientské aplikace je nutné zaregistrovat knihovnu typů serveru. Pokud máme k dispozici spustitelný soubor je nejjednodušším způsobem spuštění serveru s parametrem /REGSERVER [8].

Protože vytvářím instanci objektu konstruktorem CREATE (resp. funkcí COCREATEINSTANCE), bude server implicitně hledán na stanici, na které spuštěn klient. Aby mohlo být vytvořeno spojení se vzdáleným serverem musí být volán konstruktor CREATEREMOTE s adresou serveru nebo se musí na klientské stanici příslušným nástrojem nastavit, kde se má server objektu ESA hledat. Druhý způsob má výhodu ve větší transparentnosti, umístnění serveru není nijak omezeno implementací klienta. Na druhou stranu to přináší komplikovanější instalaci nastanici. Další podrobnosti o této problematice budou popsány v kapitole 4.14.

4.9 Dynamické volání

Způsob práce s COM objektem popsaný v předešlé kapitole, vyžaduje, jak bylo zmíněno, zaregistrování knihovny typů. Toto omezení není pro aplikaci "Klíčky" nijak významné. Při provozovaní některých dalších funkcí systému ESA, by to však přineslo mnoho komplikací. Příkladem je ověření oprávněnosti přístupu. Příslušný klient je spouštěn prakticky na každé z několika tisíc stanic zapojených do školní sítě. Instalace a konfigurace každé z nich je těžko představitelná.

Pro takové případy je vhodné uvažovat o použití technologie, která se obecně dá nazvat dynamické volání. V kontextu architektury COM se jedná o technologii OLE Automation a speciální typ COM objektů, zvaný Automation objects. Tyto objekty implementují rozhraní IDISPATCH. Přes toto rozhraní lze volat metody speciálního typu rozhraní dispinteface. Protože IDISPATCH je standardní rozhraní, je zaregistrované na všech systémech, kde je nainstalován COM, není tedy třeba již nic registrovat. Je možné definovat i tzv. duální rozhraní, která musí být odvozena z IDISPATCH. Ta jsou dostupná oběma způsoby, tento přístup budu dále používat.

Při použití dynamického volání nemusí klient znát (a ani nezná) rozhraní serveru (tj. ani jména metod a jejich parametry). Nezná ho dokonce ani kompilátor, nemůžeme tedy použít třídu TESA. Místo třídy se použije proměnná typu VARIANT. Volané metody se při kompilaci nekontrolují (ani názvy ani parametry), k vyhodnocení dochází až za běhu programu voláním metod GETIDSOFNAMES a INVOKE z rozhraní IDISPATCH [1]. Tím se práce s Automation objects podobá programování ve skriptovacích jazycích typu VBScript, kvůli kterým byly původně zavedeny [3].

Aby mohl náš objekt ESA implementovat duální rozhraní a tím mohly být jeho metody volány dynamicky, musí podporovat rozhraní IDISPATCH. Toto rozhraní nemusíme programovat sami, Delphi nám dávají k dispozici třídu TAUTOOBJECT, která implementuje všechny čtyři metody tohoto rozhraní a kterou můžeme použít jako předka třídy TESA místo TTYPEDCOMOBJECT. Delphi automaticky použijí TAUTOOBJECT pokud COM objekt vytvoříme průvodcem pro Automation object. Obdobně zaměníme třídu class factory z TTYPEDCOMOBJECTFACTORY na TAUTOOBJECTFACTORY. Po změně předka třídy TESA změníme i předka všech rozhraní z IUNKNOWN na IDISPATCH.

Nyní již můžeme přistoupit ke klientské části. K získání reference na objekt použijeme funkci CREATECOMOBJECT (resp. CREATEREMOTECOMOBJECT), která opět interně volá API funkci COCREATEINSTANCE (resp. COCREATEINSTANCEEX). K určení požadovaného objektu slouží identifikátor GUID. Potřebujeme referenci na rozhraní IDISPATCH (žádné z našich rozhraní IESA... klient nezná). Po té co ji získáme můžeme již volat metody duálních rozhraní.

Ukázka 8. Volání metod pomocí rozhraní IDISPATCH
const { GUID objektu }
  CLASS_Esa: TGUID = '{FDB5F8FD-1135-4E8C-B328-6DF851F93390}';
var Esa: Variant;
    Vysledek: Integer;
    Vlastnik, Jmeno, Poznamka: WideString;
begin
  Esa := CreateComObject(CLASS_Esa) as IDispatch;
  { dynamicke volani, kompilator neoveruje existenci metody a jeji parametry }
  Remote.IdkartaOvereni(Karta, Vysledek, Vlastnik, Jmeno, Poznamka);
  ...
  Remote := Unassigned;
end;

K použití rozhraní IDISPATCH se ještě vrátím v kapitole 4.16, kde budeme náš objekt volat ve webové aplikaci z VB skriptu.

4.10 Přenos velkého množství dat

V seznamu požadovaných funkcí systému (kapitola 3.2) je možnost zobrazení seznamu klíčků včetně jejich stavu. Je zřejmé, že v rozhraní IESAKLICKY nejsou metody, které by to umožňovali. Zatím máme jen možnost zjistit počet klíčků v sadě metodou KLICKYVSADE.

Při návrhu vhodného způsobu získávání dat o klíčcích by se mi, z pohledu programátora zvyklého na objektový přístup (zvláště když COM je také objektová architektura), líbila představa klíčku jako samostatného objektu. Klíček by měl vlastnosti (properties) STAV, VLASTNIK, PUJCENOD... resp. metody vracející tyto hodnoty. Třída ESA by měla pouze metodu, která by podle indexu vrátila referenci na rozhraní zpřístupňující tyto vlastnosti (resp. metody). Jak by rozhraní objektu Klíček mohlo vypadat je zřejmé z ukázky.

Ukázka 9. Příklad rozhraní objektu KLICEK (vlastnosti jsou pouze pro čtení)
interface IKlicek: IUnknown
{
  [propget, id(0x00000003)]
  HRESULT _stdcall Stav([out, retval] long * Value );
  [propget, id(0x00000006)]
  HRESULT _stdcall Vlastnik([out, retval] long * Value );
  [propget, id(0x00000008)]
  HRESULT _stdcall PujcenOd([out, retval] DATE * Value );
};

S takto nadefinovaným rozhraním a objektem KLICEK a metodou vracející referenci na rozhraní by byla práce se seznamem klíčků na klientovi velmi jednoduchá.

Ukázka 10. Klíček jako samostatný objekt
EsaKlicek.KlickyVSade(Sada, Vysledek, Celkem, Pujcene, Volne);
for Klicek := 1 to Celkem do
  { pro kazdy klicek vlozime jeden radek do seznamu }
  with EsaKlicek.Klicek(Klicek), KlickyListView.Items.Add do
  begin
     Caption := IntToStr(Klicek);
     if Stav = skVolny then SubItems.Add('Volný')
       else ... { pridani dalsich vlastnosti }
  end;

Takto navržené rozhraní je sice poměrně komplikované na implementaci, ale jak už je jednou hotové, přináší programátorovi klientské aplikace velmi pohodlný přístup k seznamu klíčků. Je také vzhledem ke svému zjevně objektovému přístupu velmi snadné na pochopení i pro jiné programátory, kteří neznají vnitřní implementaci serveru. To samo o sobě je cílem architektury COM. Tento přístup se velmi blíží například koncepci práce s objekty OLE Automation v Microsoft Office.

Tato koncepce má jednu zásadní nevýhodu, která téměř znemožní její použití. Programátor, který nemá praxi v práci s COM si nemusí ihned uvědomit, že vpřípadě distribuované aplikace "Klíčky" by každé volání metody KLICEK a každý přístup k vlastnosti rozhraní IKLICEK vyžadovalo komunikaci po síti mezi klientem a serverem. Tím by nároky na systém neúměrně vzrostly [1].

Z popsaného důvodu musím ustoupit od své představy (je však třeba připustit, že v případě lokální, nedistribuované, aplikace jakou je například Microsoft Word by uvedený přístup byl přesto vhodný). Návrh rozhraní musím nyní nekoncepčně přizpůsobit požadavkům klienta. Se znalostí jeho požadovaných funkcí vím, že pracuje buď pouze s jedním klíčkem nebo se všemi. Je tedy vhodné přidat do rozhraní dvě odpovídající funkce. První, tedy KLICEKSTAV, bude volat již připravenou uloženou proceduru KLICEK_STAV. Na ní není nic nového ani zajímavého. Naopak nový bude způsob vracení seznamu klíčků z druhé metody, tedy KLICEKSEZNAM.

Ukázka 11. Rozšíření rozhraní IESAKLICEK o metody KLICEKSTAV a KLICEKSEZNAM
interface IEsaKlicek: IUnknown
{ ...
  HRESULT _stdcall KlicekStav([in] BSTR Sada, [in] long CisloKlicku,
    [out] long * Vysledek, [out] long * Stav, [out] BSTR * StavPopis,
    [out] BSTR * Vlastnik, [out] BSTR * VlastnikJmeno, [out] DATE * PujcenOd );
  HRESULT _stdcall KlicekSeznam([in] BSTR Sada, [out] long * Vysledek,
    [out] VARIANT * SeznamKlicku );
... }

COM mezi podporovanými typy definuje i typ VARIANT ( v Delphi je reprezentovaný typem OLEVARIANT), který může nabývat hodnot mnoha typů včetně pole hodnot. Všechny hodnoty pole musejí být stejného typu, kterým může být opět i typ VARIANT. Delphi definují funkci VARARRAYCREATE, která vytvoří pole požadované velikosti s elementy určitého typu.

Ukázka 12. Vytvoření pole typu VARIANT se seznamem všech klíčků v sadě
procedure TEsa.KlicekSeznam(const Sada: WideString; out Vysledek: Integer;
  out SeznamKlicku: OleVariant);
begin
  ...
  SeznamKlicku := VarArrayCreate([1, Pocet], varVariant);
  for Klicek := 1 to Pocet do
    with FDataModule.KlicekStavProc do
    begin
      ParamByName('sada').Value := Sada;
      ParamByName('cislo_klicku').Value := Klicek;
      ExecProc();
      SeznamKlicku[Klicek] := VarArrayOf([Klicek,
        ParamByName('vysledek').Value, ParamByName('stav').Value,
        ParamByName('stav_popis').Value, ParamByName('vlastnik').Value,
        ParamByName('vlastnik_jmeno').Value, ParamByName('pujcen_od').Value]);
    end;
  ...
end;

Sestavené dvourozměrné dynamické pole (první rozměr je číslo klíčku, druhý jsou jednotlivé údaje o klíčku) se odešle na klienta. V naší jednoduché aplikaci se získané informace pouze zobrazí uživateli v seznamu.

Obrázek 5. Okno se seznamem klíčků
Obrázek 5. Okno se seznamem klíčků (4 kB)

4.11 Události

V této kapitole se již konečně dostávám k poměrně pokročilému a zajímavému tématu. Tím jsou zpětná volání, jinak řečeno události. Pro názornost uvedu konkrétní příklad. V aplikaci klíčky je možné, že stejnou sadu klíčků obsluhuje více stanic. Důvodem může být například rozložení zátěže obsluhy v hodně frekventovaných šatnách. V okně klientské aplikace se zobrazuje aktuální přehled volných resp. půjčených klíčků, který se aktualizuje po každé operaci (půjčení, vrácení). K aktualizaci samozřejmě dojde pouze pokud se operace uskuteční na stejné stanici. Aby mohlo dojít k obnovení ukazatelů i při půjčení resp. vrácení klíčku z jiné stanice, musí server uvědomit všechny připojené klienty.

Aby mohl server upozornit klienta na určitou událost, musí se role klienta a serveru otočit. Klient se stane serverem serveru. Pokud chceme toto umožnit, musí klient dát serveru k dispozici rozhraní, prostřednictvím kterého bude server klienta volat. Není důvod, aby toto rozhraní nebylo opět rozhraní COM. To znamená, že klientská aplikace může definovat vlastní COM objekt (v tomto kontextu zvaný event sink), který implementuje toto rozhraní.

Ukázka 13. Rozhraní klientské aplikace pro zpětná volání
interface IEsaKlicekKlient: IUnknown
{
  [id(0x00000001)]
  HRESULT _stdcall KlicekZmena( void );
};
Obrázek 6. Nástin koncepce událostí [1]
Obrázek 6. Nástin koncepce událostí  (2 kB)

Práce s událostmi v COM má jednoduchý koncept. Klient se v okamžiku, kdy získá potřebu být informován o určité události, zaregistruje. Server si drží seznam zaregistrovaných klientů. Když dojde k události (v našem případě typicky voláním metody COM objektu jedním z klientů), server (v kontextu COM událostí se server nazývá event source) v cyklu zavolá metodu události z rozhraní všech zaregistrovaných klientů.

Protože server nevytváří instanci klientského COM objektu, nemusí být klientská aplikace na serveru registrována (ani by to nebylo možné). Server pouze při registrování získá referenci na rozhraní již existujícího objektu. Obě aplikace potřebují znát jen definici rozhraní.

Protože události jsou využívány velmi často, definuje COM standardní mechanizmus na jejich obsluhu. Ten je nazýván connection point a reprezentuje jednu událost, k jejímuž odběru může být přihlášeno více klientů. Connection point je definován rozhraním ICONNECTIONPOINT, které má dvě metody: ADVICE a UNADVICE. Předáním svého rozhraní metodě ADVICE se klient zaregistruje k odběru události a zároveň získá unikátní identifikátor, zvaný koláček (cookie). Koláček se později použije k odhlášení, voláním metody UNADVICE.

Pokud objekt definuje více událostí, je lepší implementovat rozhraní ICONNECTIONPOINTCONTAINER, které slouží jako "zásobník" událostí. Klient referencí na toto rozhraní získá pomocí jeho metody FINDCONNECTIONPOINT přístup k rozhraním ICONNECTIONPOINT všech událostí, které serverový objekt implementuje. Událost (resp. connection point) se stejně jako vše v architektuře COM identifikuje svým GUID.

Nyní se dostávám k závažnému rozporu. Systém, tak jak je teď navržen, předpokládá, že každý klient komunikuje s vlastní instancí serverového objektu. A každý serverový objekt, by měl vlastní seznam klientů (vlastní rozhraní ICONNECTIONPOINT), ve kterém by byl právě pouze ten jeden klient. Jsou dvě zřejmá řešení. První je upravení koncepce systému tak, aby serverový objekt byl jen jeden. Druhou možností je vyřešit sdílení seznamu klientů mezi serverovými objekty.

Proti prvnímu řešení stojí fakt, že při zachování možnosti současného volání metody objektu více klienty (z různých vláken) by server musel používat vláknový model free. Jeho implementace je relativně obtížná. Dále by bylo nutné navrhnout vhodný mechanizmus, kterým by klientské aplikace získávali referenci na stejný objekt. Při použití lokálního serveru k tomu lze jednoduše využít API funkcí REGISTERACTIVEOBJECT a GETACTIVEOBJECT. V distribuované aplikaci to ovšem tak jednoduché není. Řešením by mohl být například další serverový objekt, který by "běžel" na stejné stanici a zprostředkovával by vzdáleným klientům referenci na skutečný objekt ESA. Tato varianta se však již velmi podobá druhému zmíněnému řešení, ale stále vyžaduje vláknový model free.

Druhou možností řešení je vytvořit objekt, který bude obsluhovat pouze události. Takový objekt mimo rozhraní ICONNECTIONPOINTCONTAINER implementuje již jen rozhraní s metodami, které v cyklu zavolají klienty "odebírající" určitou událost. Výše definované rozhraní IESAKLICEKKLIENT je přidáno do definice objektu ESAUDALOSTI s atributem SOURCE, což značí, že objekt rozhraní neimplementuje, ale naopak volá.

Ukázka 14. Rozhraní serveru událostí (ESAUDALOSTI)
interface IEsaUdalosti: IDispatch
{
  [id(0x00000001)]
  HRESULT _stdcall KlicekZmena( void );
};
...
coclass EsaUdalosti
{
  [default] interface IEsaUdalosti;
  [default, source] interface IEsaKlicekKlient;
};

Aby byl klient "odstíněn" od faktu, že tento druhý objekt existuje, lze využít principu delegace [8]. Rozhraní ICONNECTIONPOINTCONTAINER budou implementovat oba objekty (ESA i ESAUDALOSTI), ale implementace v objektu ESA bude spočívat v delegování volání na "server událostí". Klient tedy nemusí sám získávat referenci na objekt ESAUDALOSTI, dokonce ani nemusí vědět o jeho existenci. Implementaci rozhraní ICONNECTIONPOINTCONTAINER nám Delphi usnadňují standardní třídou TCONNECTIONPOINTS. Tu opět můžeme využít pro delegování, tentokrát již v objektu ESAUDALOSTI. Pro delegování rozhraní na jiný objekt se v jazyce Object Pascal používá klíčové slovo IMPLEMENTS jako atribut vlastnosti (PROPERTY).

Ukázka 15. Delegování rozhraní ICONNECTIONPOINTCONTAINER na sdílený objekt
type
  TEsa = class(TTypedComObject, IEsa, IEsaIdkarta, IEsaKlicek, IEsaNastaveni,
    IConnectionPointContainer)
  private
    FConnectionPoints: IConnectionPointContainer;
    FServerUdalosti: IEsaUdalosti;
  protected
    property ConnectionPoints: IConnectionPointContainer
      read FConnectionPoints implements IConnectionPointContainer;
  end;
...
procedure TEsa.Initialize;
begin
  ...
  { zkusime najit jiz exitujici instaci objektu, jinak vytvorime vlastni }
  if Succeeded(GetActiveObject(CLASS_EsaUdalosti, nil, AServerUdalosti)) then
    FServerUdalosti := AServerUdalosti as IEsaUdalosti
      else FServerUdalosti := CoEsaUdalosti.Create;
  { ziskame referenci na rozhrani pro delegaci }
  FConnectionPoints := FServerUdalosti as IConnectionPointContainer;
  ...
end;
Ukázka 16. Vytvoření connection point a registrace instance objektu ESAUDALOSTI
procedure TEsaUdalosti.Initialize;
begin
  inherited;
  OleCheck(RegisterActiveObject(Self as IUnknown, CLASS_EsaUdalosti,
    ActiveObject_Weak, FROTCookie));
  FConnectionPoints := TConnectionPoints.Create(Self);
  FKlickyConnectionPoint := FConnectionPoints.CreateConnectionPoint(
    IID_IEsaKlicekKlient, ckMulti, EventConnect)
end;

Server událostí se při svém prvním vytvoření zaregistruje již popsanou funkcí REGISTERACTIVEOBJECT a každý další vytvořený objekt ESA získá jeho rozhraní voláním funkce GETACTIVEOBJECT [1]. To samozřejmě předpokládá implementaci obou objektů na stejné stanici.

Klientská aplikace po vytvoření instance serverového objektu, získá také referenci na jeho rozhraní ICONNECTIONPOINTCONTAINER (aniž by věděla, že je implementované jiným objektem). Metodou FINDCONNECTIONPOINT získá rozhraní ICONNECTIONPOINT požadované události. Samozřejmostí je znalost GUID této události. Posledním krokem je zaregistrování se k odběru události předáním reference na rozhraní IESAKLICEKKLIENT klientského objektu voláním metody ADVISE.

Je třeba si uvědomit, že standardní rozhraní ICONNECTIONPOINTCONTANER i ICONNECTIONPOINT používají konvenci volání STDCALL. Je tedy vhodné ověřit úspěšnost volání předáním návratové hodnoty metod funkci OLECHECK. Více o problematice konvencí STDCALL a SAFECALL je v kapitole 4.12.

Ukázka 17. Registrace klienta k "odběru" události
type
  TKlickyEventsSink = class(TInterfacedObject, IEsaKlicekKlient)
    function KlicekZmena: HResult; stdcall;
  end;
...
FEsaKlickySink := TKlickyEventsSink.Create;
Udalosti := (EsaServer as IConnectionPointContainer);
OleCheck(Udalosti.FindConnectionPoint(
  IID_IEsaKlicekKlient, ConnectionPoint));
OleCheck(ConnectionPoint.Advise(FEsaKlickySink, FCookieUdalosti));

Objekt ESA po každé změně v databázi klíčků, tedy po volání metod KLICEKPUJCENI a KLICEKVRACENI, zavolá metodu KLICKYZMENA z rozhraní IESAUDALOSTI. Tato metoda zavolá v cyklu všechny zaregistrované klientské objekty. Toto je kritická fáze popisované koncepce událostí. Protože server se tímto stává klientem, čeká na dokončení volání metody. Pokud se vykonávání metody na klientovi (resp. teď serveru) z jakéhokoliv důvodu pozdrží, zastaví se zároveň server. Toto zablokování se může lavinovitě šířit celým systémem. Je tedy třeba důsledně dbát na vhodnou implementaci volané metody klienta.

Ukázka 18. Volání zaregistrovaných klientů
procedure TEsaUdalosti.KlickyZmena;
var
  Index: Integer;
begin
  with FKlickyConnectionPoint.SinkList do
    for Index := 0 to Count-1 do
      if Assigned(Items[Index]) then IUnknown(Items[Index]).KlicekZmena;
end;
Obrázek 7. Architektura systému událostí aplikace "Klíčky"
Obrázek 7. Architektura systému událostí aplikace "Klíčky" (4 kB)

4.12 Výjimky

Až dosud jsme k zjišťování úspěšnosti provedení volaných metod používali výstupní parametr VYSLEDEK. Každý programátor používající některý z moderních vývojových nástrojů je zvyklý pro kontrolu chyb využívat výjimky (exceptions). Architektura COM výjimky jako takové nepodporuje. Přesto, pokud zkusíte v implementaci metody na serveru výjimku vyvolat a na klientu odchytit, zjistíte, že to funguje. Jak to tedy ve skutečnosti je?

4.12.1 COM a obsluha chyb

COM definuje sadu rozhraní a API funkcí, sloužících pro obsluhu chyb a jejich předávání z volaného objektu. Mezi zmiňovaná rozhraní patří: IERRORINFO, ICREATEERRORINFO a ISUPPORTERRORINFO.

COM objekt, který podporuje obsluhu chyb, musí implementovat rozhraní ISUPPORTERRORINFO. Pokud během vykonávání některé z jeho metod dojde k chybě, objekt by měl provést následující kroky [3]:

Obrázek 8. Předání informací o chybě [3]
Obrázek 8. Předání informací o chybě  (2 kB)

Program, který metodu zavolal provede v podstatě opačné kroky ke zjištění úspěšnosti operace a případných parametrů chyby, ke které došlo:

Obrázek 9. Získání informací o chybě [3]
Obrázek 9. Získání informací o chybě  (3 kB)

4.12.2 Obsluha chyb a Delphi

Jak jsem již uvedl, pokud je jak klient tak server vytvořen v prostředí Delphi, funguje předávání výjimek, aniž bych musel implementovat oba výše uvedené postupy. Delphi to udělají za mne pokud jako konvenci volání (calling convetion) všech metod COM objektu určím safecall [1]. Protože to je výchozí konvence, nemusím ve skutečnosti měnit nic. Konvence safecall je z hlediska předávání parametrů ekvivalentní konvenci stdcall.

Pokud ovšem bude klientská aplikace vytvářena nástrojem, který nepodporuje popsanou obsluhu chyb, musíme si pomoci sami. Řešení uvedu na příkladu. Budu předpokládat, že metody rozhraní IESANASTAVENI mají konvenci volání stdcall:

Ukázka 19. Rozhraní IESANASTAVENI s konvencí volání stdcall
type
  IEsaNastaveni = interface(IDispatch)
    ['{99BF3F79-8CCE-4ABD-98AF-90E3E8DFEA18}']
    function SpravovanaSadaKlicku(out Vysledek: Integer;
      out Sada: WideString): HRESULT; stdcall;
  end;

Metoda SPRAVOVANASADAKLICKU je nyní funkce se standardní návratovou hodnotou HRESULT. Dle konvence mají metody při úspěšném volání vrátit hodnotu S_OK, v opačném případě hodnotu indikující typ chyby. Konvenci safecall, tak jak je realizována v knihovnách Delphi, si lze představit jako funkci, které je předána hodnota HRESULT každé volané metody. Pokud se hodnota liší od S_OK, funkce provede veškeré kroky popsané v předešlé části. Po zjištění všech dostupných údajů o chybě funkce vyvolá výjimku EOLEEXCEPTION. Je tedy zřejmé, že nedojde k zachování třídy objektu výjimky (i když metoda vyvolá chybu EABORT, klient vždy zachytí EOLEEXCEPTION). K rozlišení třídy bylo možné využít různé návratové hodnoty HRESULT. Podrobnosti postupu jsou uvedeny v komentářích následující ukázky.

Ukázka 20. Funkce obsluhující chyby při volání metody COM objektu
procedure Kontrola(Vysledek: HResult);
var
  KontextNapovedy: Integer;
  Popis, Zdroj, SouborNapovedy: WideString;
  ErrorInfo: IErrorInfo;
begin
  { je navratova hodnota ruzna od S_OK? }
  if Failed(Vysledek) then
  begin
    { ziskame referenci na chybovy objekt }
    if GetErrorInfo(0, ErrorInfo) <> S_OK then
      KontextNapovedy := 0
    else
      with ErrorInfo do
      begin
        { zjistime parametry chyby }
        GetSource(Zdroj);
        GetDescription(Popis);
        GetHelpFile(SouborNapovedy);
        GetHelpContext(KontextNapovedy);
      end;
    { vyvolame vyjimku, vzdy EOleException }
    raise EOleException.Create(Popis, Vysledek, Zdroj,
      SouborNapovedy, KontextNapovedy);
  end;
end;

{ priklad pouziti, metoda SpravovanaSadaKlicku ma konvenci volani stdcall! }
Kontrola(EsaNastaveni.SpravovanaSadaKlicku(Vysledek, Sada));

Ani o obsluhu chyb na druhé straně (na straně serveru) se programátor nemusí starat. Všechny standardní třídy reprezentující COM objekt, jako jsou TTYPEDCOMOBJECT, TAUTOOBJECT a TMTSAUTOOBJECT (dvě poslední budou popsány později) jsou odvozeny z TCOMOBJECT. Tato třída již mimo jiné implementuje zmíněné rozhraní ISUPPORTERRORINFO. Kompilátor zároveň implicitně "zaobalí" implementaci všech metod objektu s konvencí volání SAFECALL do bloku TRY ... EXCEPT, který zajistí, že všechny výjimky vyvolané v metodách budou odchyceny, zpracovány a předány klientovi standardním postupem, uvedeným výše. Programátor může podle potřeby postup upravit vlastní virtuální metodou SAFECALLEXCEPTION, které je volána právě při vzniku výjimky.

4.12.3 Využití v aplikaci

Se znalostí principu obsluhy výjimek nyní mohu změnit implementaci metod, tak abych nových znalostí využil. Všechny metody COM objektu budou nyní místo vracení kódu výstupním parametrem VYSLEDEK vyvolávat odpovídající výjimku. Při zpracování chyb navrácených z uložených procedur databáze lze k mapování chybových zpráv využít tabulku 3. Lze předpokládat, že při neúspěchu volání většiny funkcí nebudou klientské aplikace analyzovat příčinu neúspěchu, ale pouze zobrazí chybové hlášení. Proto povede přesunutí vyvolání výjimky (a tím i mapování chybového kódu na chybové hlášení) k značnému zkrácení a zpřehlednění kódu. Ostatně stejně jako běžné použití výjimek i v nedistribuované aplikaci.

Zavedením výjimek pozbývá výstupní parametr VYSLEDEK smysl. Je tedy třeba předefinovat rozhraní objektu. Potřebná změna je zřejmá, nebudu ji zde uvádět. V dalších částech textu budu již předpokládat, že metody tento parametr již nemají.

Protože serverová i klientské aplikace budou převážně vytvořeny v prostředí Delphi, jsou výhody využití výjimek nesporné. Pokud by však byli klienti vyvíjeni v jiném prostředí může se ukázat použití číselného kódu jako jednodušší, ale dostatečné řešení.

4.13 Co se děje na pozadí

V předchozí kapitole jsem se dotkl tématu skryté podpory, kterou Delphi programátorům při vývoji COM objektů poskytují. Mnoha implementačními detaily architektury COM se většinou programátor nemusí zabývat. V okamžiku, kdy se při vývoji začneme potýkat s nepochopitelnými chybami, je výhodou, když víme co program dělá jaksi navíc mimo kódu, který jsme sami napsali. Proto tuto kapitolu věnuji tématu "co se děje na pozadí".

Vedlejším důvodem, proč se tímto námětem zabývám, je závazek, který jsem dal na začátku práce. Slíbil jsem, že se budu snažit nezaměřit tuto práci jednostranně na Delphi.

4.13.1 Inicializace COM

Před jakýmkoli voláním funkcí COM je nutné provést inicializaci COM runtime voláním API funkce COINITIALIZE (resp. COINITIALIZEEX). Před ukončením aplikace by měl program zavolat funkci COUNITIALIZE, ze stejného vlákna. Inicializace COM runtime se musí provést pro každé vlákno zvlášť. Delphi provedou inicializaci automaticky (pouze pro hlavní vlákno!) z metody TAPPLICATION.INITIALIZE, která je přidána jako první příkaz do projektového souboru [1]. Standardně se volá funkce COINITIALIZE. Pokud je třeba volat funkci COINITIALIZEEX, s parametrem určujícím vláknový model aplikace, je třeba na začátku programu nastavit globální proměnnou COINITFLAGS.

Ukázka 21. Inicializace COM
uses ComObj;
begin
  CoInitFlags := COINIT_MULTITHREAD;
  Application.Initialize;
  ...
end.

4.13.2 Rozhraní IUNKNOWN

Jedním ze základních principů architektury COM je, že každý objekt musí implementovat minimálně rozhraní IUNKNOWN. Přesto jsem ho zatím nikdy implementovat nemusel, je to proto, že třídy zajišťující základní funkcionalitu COM toto rozhraní (a mnohá další) již samy o sobě implementují. Takovou třídou je například TCOMOBJECT a třídy z ní odvozené (TAUTOOBJECT...)

Často potřebujeme v klientské aplikaci vytvořit objekt, jehož jediným účelem je implementace rozhraní zpětného volání (callback interface). Nejčastější použití je volání událostí (viz Ukázka 13). V takovém případě není nutné aby třída reprezentovala plnohodnotný COM objekt. Nemusí být zaregistrována, nemusí mít vlastní class factory, atp. Pro takové speciální případy nám Delphi dávají k dispozici třídu TINTERFACEDOBJECT, která implementuje pouze rozhraní IUNKNOWN a její tři metody QUERYINTERFACE, ADDREF a RELEASE [1].

Místo vytvoření ad hoc třídy, můžeme využít libovolné komponenty (tedy i formuláře), protože třída TCOMPONENT zmíněné tři metody implementuje také. Neimplementuje však rozhraní IUNKNOWN, musíme si ho sami přidat do seznamu rozhraní.

4.13.3 Ukazatele na rozhraní a počítání referencí

Účelem základního rozhraní IUNKNOWN, je mimo jiné počítání referencí. Pokaždé, když klient získá referenci na COM objekt, musí o tom objekt voláním metody ADDREF rozhraní IUNKNOWN uvědomit. V okamžiku, kdy již aplikaci referenci nepotřebuje, volá naopak metody RELEASE. Běžná implementace metod ADDREF a RELEASE pouze zvyšuje resp. snižuje interní počítadlo. Při jeho poklesu na nulu, objekt sám sebe zruší. Při práci s referencemi na rozhraní za nás Delphi volání obou metod provádí automaticky [1].

Druhou funkcí IUNKNOWN je získávání referencí na další rozhraní objektu. K tomu slouží metoda QUERYINTERFACE. V prostředí Delphi však programátor nemusí volat tuto metodu, stačí použít standardní klíčové slovo AS, které běžně slouží k přetypování ukazatele na objekt. Kompilátor volání QUERYINTERFACE automaticky dosadí.

Byť se to tak nemusí na první pohled zdát, šetří tímto Delphi programátorovi mnoho práce. Zřejmé je to z následujících dvou ukázek. V první je kód, který programátor napíše. Druhá ukazuje, co se ve skutečnosti děje, tedy co kompilátor k napsanému kódu sám doplní.

Ukázka 22. Počítání referencí (původní kód)
var
  Esa, Esa2: IUnknown;
  EsaIdkarta: IEsaIdkarta;
begin
  Esa := CoEsa.Create; { vytvoreni objektu }
  Esa2 := Esa; { druha reference na objekt }
  EsaIdkarta := Esa as IEsaIdkarta; { reference na rozhrani IEsaIdkarta }
  Esa := nil; { uvolneni reference (jen jedne!) na objekt }
end;

Je třeba si uvědomit, že i kopírováním první reference z proměnné ESA do proměnné ESA2 vzniká nová reference, je tedy třeba zavolat metodu ADDREF. Z toho ale také vyplývá, že přiřazením hodnoty NIL do ESA, nebude objekt zrušen, protože na něj existují ještě další dvě reference v proměnných ESA2 a ESAIDKARTA. Delphi ovšem automaticky uvolní referenci v okamžiku, kdy proměnná opouští blok programu, pro který byla definována. Nulování proměnné ESA je tedy zbytečné, protože by k němu došlo stejně.

Ukázka 23. Počítání referencí (kód rozšířený o části generované kompilátorem)
procedure NulujRozhrani(var Rozhrani: Pointer);
begin
  if Rozhrani <> nil then
  begin
    IUnknown(Rozhrani)._Release;
    Rozhrani := nil;
  end;
end;

procedure RozhraniKopiruj(var Cil: Pointer; const Zdroj: Pointer);
begin
  if Cil <> nil then IUnknown(Cil)._Release;
  Cil := Zdroj;
  if Cil <> nil then IUnknown(Cil)._AddRef;
end;

procedure RozhraniPreved(var Cil: Pointer; const Zdroj: Pointer; IID: TGUID);
begin
  if Cil <> nil then IUnknown(Cil)._Release;
  if Zdroj <> nil then
  begin
    if IUnknown(Zdroj).QueryInterface(IID, Cil) <> S_OK then
      raise EIntfCastError.Create('Požadované rozhraní není implementováno.');
  end
    else Cil := nil;
end;

var
  Esa, Esa2: IUnknown;
  EsaIdkarta: IEsaIdkarta;
begin
  Esa := nil; { vsechny promenne typu rozhrani se na zacatku nuluji }
  Esa2 := nil;
  EsaIdkarta := nil;

  Esa := CoEsa.Create;
  RozhraniKopiruj(Pointer(Esa2), Pointer(Esa));
  RozhraniPreved(Pointer(EsaIdkarta), Pointer(Esa), IID_IEsaIdkarta);

  NulujRozhrani(Pointer(Esa)); { Esa := nil; viz predesla ukazka }
  NulujRozhrani(Pointer(Esa)); { automaticky pridane nulovani }
  NulujRozhrani(Pointer(Esa2));
  NulujRozhrani(Pointer(EsaIdkarta));
end;

4.14 Vzdálený server

Všechny předešlé ukázky ignorovaly fakt, že server a klient nebudou v reálném provozu spuštěni na stejné stanici. V této krátké kapitole si ukážeme, jak zajistit vzdálenou komunikaci mezi klientem a serverem pomocí architektury COM.

Pro získání reference na rozhraní serveru jsme dosud používali metodu CREATE vygenerované třídy COESA. Jak jsem se již zmínil, tato třída definuje i metodu CREATEREMOTE s parametrem MACHINENAME. Pomocí ní lze získat referenci i na vzdálený server. Jako parametr lze předat adresu počítače v libovolném formátu podporovaném operačním systémem (IP adresa, doménové jméno, jméno stanice v síti Microsoft). Metoda CREATEREMOTE volá interně API funkce COCREATEINSTANCEEX.

Při použití dynamického volání nám Delphi dávají k dispozici funkci CREATEREMOTECOMOBJECT.

Další (a možná i lepší) možností jak určit umístnění serveru je použití nástroje DCOMCNFG, který je součástí operačního systému. Po jeho spuštění se zobrazí seznam objektů, v něm nalezneme objekt ESA podle textu uvedeném v položce "Help string" v knihovně typů. Zobrazíme vlastnosti objektu stejnojmenným tlačítkem. Na záložce "Umístnění" nyní můžeme určit, kde se má server spouštět. Implicitně je zvolena možnost "Spouštět aplikaci v tomto počítači". Pokud chceme, aby se server spouštěl na jiné stanici, zvolíme možnost "Spouštět aplikaci v následujícím počítači" a zadáme adresu stanice. Je také možné zvolit obě možnosti, pak bude server spuštěn na vzdálené stanici pouze, pokud ho nebude možné spustit lokálně.

Další možnosti a funkce nástroje DCOMCNFG budou popsány mimo jiné v kapitole 4.15.5.

Obrázek 10. Umístnění COM objektu v nástroji DCOMCNFG
Obrázek 10. Umístnění COM objektu v nástroji DCOMCNFG (5 kB)

Při použití tohoto způsobu určení místa spouštění serveru můžeme v klientské aplikaci ponechat metodu CREATE. Výhodou je, že umístnění serveru není vázáno na kód klienta. Je tím zachována lokační transparentnost. Na druhou stranu se tím mírně komplikuje instalace klienta, nestačí již pouze "nakopírovat" program a zaregistrovat knihovnu typů. Zvláště výrazná je tato nevýhoda při použití dynamického volání, kdy není potřeba ani registrovat knihovnu typů.

4.15 Bezpečnost

Pro distribuované systémy je velmi důležité zajištění bezpečnosti. To má dva aspekty. Prvním je zabezpečení přenášených dat a druhým ověření identity komunikujících stran (nejčastěji klienta, ale i serveru).

Nastavení úrovně zabezpečení lze, podobně jako určení umístnění serveru v předchozí kapitole, zajistit buď již v kódu programu (jak serveru tak klienta) nebo opět pomocí nástroje DCOMCNFG. Nejprve je potřeba se seznámit s pojmy, které specifikace COM používá při definování zabezpečení.

4.15.1 Úroveň ověřování (authentication)

Úroveň ověřování určuje, jak a kdy se provádí ověřování identity klienta (nebo serveru). COM definuje několik úrovní ověření, její výběr je výsledkem "vyjednávání" mezi klientem a serverem. Obě strany navrhnou požadovanou úroveň a z nich se vybere ta vyšší. Pokud ji jedna ze stran (častěji klient) neumožňuje, ověření automaticky selže. Tím je zaručeno, že žádné ověřování neproběhne na nižší úrovni, než na jakou je nastaven server. Zároveň může klient požadovat i větší zabezpečení. Lze provést globální výchozí nastavení pro všechny COM objekty nebo jednotlivě pro každý zvlášť.

Tabulka 5. Úrovně ověřování
Úroveň Popis
Žádné (None) Neprobíhá žádné ověřování
Připojení (Connect) Ověření proběhne pouze jednou při prvním připojení
Volání (Call) Ověření proběhne při každém volání
Paket (Packet) Jako "Volání", navíc se zjišťuje jestli všechna odeslaná data byla doručena
Integrita paketu (Packet integrity) Jako "Paket", navíc se ověřuje jestli odeslaná data nebyla během přenosu změněna
Utajení paketu (Packet privacy) Jako "Integrita paketu", navíc jsou všechna data šifrována

4.15.2 Zosobnění (impersonation)

COM umožňuje, aby server převzal identitu klienta a tím i oprávnění, která klient má (resp. uživatel jeho jménem klientská aplikace vystupuje). Tato schopnost se nazývá zosobnění (impersonation). Podle nastavené úrovně zosobnění (impersonation level) může server přistupovat ke zdrojům (jak lokálním, tak vzdáleným), ke kterým může přistupovat klient. To může znamenat jak zúžení tak rozšíření možností. Aby mohlo dojít k zosobnění, musí proběhnout ověřování alespoň na úrovni "Připojení".

Tabulka 6. Úrovně zosobnění
Úroveň Popis
Identita (Identify) Server zná klientova oprávnění, ale zosobnění neprobíhá
Zosobnění (Impersonate) Probíhá zosobnění, ale s omezeními. Pokud server a klient nejsou na stejné stanici, může server jménem klienta přistupovat jen ke zdrojům na své stanici.
Delegace (Delegate) Server může přistupovat jménem klienta i ke zdrojům na jiných stanicích.

4.15.3 Přístupová oprávnění (access control)

Pokud již server ví, kdo klient je, může povolit nebo odmítnout přístup ke službám, které poskytuje. Oprávnění lze nastavit jak na úrovni celého objektu, tak jednotlivě podle metod rozhraní. Nastavení oprávnění k metodám lze provést buď programově pomocí API funkcí nebo je nutné využít rozšířené koncepce přístupových oprávnění architektury COM+. Rozšíření spočívá v možnosti definovat role a přiřazovat jim oprávnění.

Oprávnění lze také nastavovat zvlášť pro spouštění (launch permissions) a volání (access permissions). Tím lze povolit volání objektu širšímu výběru klientů, ale jen pro případ, že vzdálený server již běží. Jiná skupina (užší) klientů pak má právo server spustit, pokud při pokusu o připojení ještě neběží.

Někdy můžeme chtít umožnit volání metod objektu i klientům u kterých není možné ověřit identitu. Typickým příkladem je využití komponenty v Internetové aplikaci v ASP (Active Server Pages) skriptu například z IIS (Internet Information Server). Pro tyto účely je nutné přidat do seznamu oprávnění speciálního uživatele network.

4.15.4 Identita serveru (server identity)

U serverové aplikace musí být už v okamžiku spuštění určeno jaká má oprávnění. Jde o práva aplikace jako celku, nejedná se tedy o zosobnění, které je vztažené k určitému volání metody objektu. COM definuje tři možné nastavení identity serveru.

První je interaktivní uživatel (interactive user), což znamená, že server má práva uživatele, který je přihlášen na stanici, kde je serverová aplikace spuštěna. Z hlediska bezpečnosti není tato varianta vhodná, využije se však při vývoji. Jako jediná totiž umožňuje zobrazení uživatelského rozhraní aplikace, čímž poskytne zpětnou vazbu vývojáři.

Další volbou je spouštějící uživatel (launching user). Ta způsobí, že server převezme oprávnění klienta. Zde je jistá podobnost se zosobněním. Rozdíl spočívá v tom, že pro každého klienta se vytvoří vlastní instance serverové aplikace. To proto, že práva se přiřazují aplikaci a ta nemůže mít nastavena práva více uživatelů (více klientů).

Pro ostrý provoz je nevhodnější třetí možnost, která umožní přiřadit k aplikaci konkrétního uživatele a tím i přesné vymezení práv (vytvořením uživatele určeného pouze pro server, jen s nezbytnými právy).

4.15.5 Nástroje pro nastavení bezpečnostních parametrů

Většinu uvedených parametrů bezpečnosti lze určit pomocí nástroje DCOMCNFG. Třetí nástroj použitelný k administraci COM objektů je OleView, tuto aplikaci lze zdarma stáhnout z Internetových stránek společnosti Microsoft [8]. Pomocí něj lze, mimo některých rozšířených nastavení, prohlížet rozhraní a knihovny typů všech zaregistrovaných objektů. Pro administraci objektů COM+, o kterých se zmíním později, není možné v plné šíři upokojivě použít DCOMCNFG. Je vhodné použít speciální konfigurační program služby Component Services.

Použití aplikace DCOMCNFG jsem se již dotkl v kapitole 4.14. Na tomto místě jen poznamenám, že k nastavení bezpečnostních parametrů slouží záložky "Zabezpečení" a "Identita" v okně vlastností COM objektu.

Obrázek 11. Nastavení identity serveru nástrojem DCOMCNFG
Obrázek 11. Nastavení identity serveru nástrojem DCOMCNFG (4 kB)

4.15.6 Programové nastavení bezpečnostních parametrů

Pro nastavení parametrů je možné mimo určených nástrojů použít i volání API funkcí. Výhody a nevýhody z toho plynoucí jsou podobné jako při určování umístnění serveru (viz kapitola 4.14).

Nejjednodušším způsobem jak nastavit oprávnění je použít funkci COINITIALIZESECURITY. Respektive, tato funkce se volá vždy. Pokud není volána přímo z programu, zavolá ji COM za nás při získání první reference na libovolný objekt implementovaný v aplikaci. Jako parametry se použijí údaje obsažené v registrech operačního systému (tedy parametry určené již zmíněnými nástroji). Funkcí lze provést například tato nastavení: úroveň ověřování, úroveň zosobnění, přístupová oprávnění, výběr ověřovací služby (Kerberos, Snego...) a další. Volání musí proběhnout až po inicializaci funkcí COINITIALIZE, ale před vytvořením reference na libovolné rozhraní. Delphi však provádí obě tyto operace již v metodě TAPPLICATION.INITIALIZE. Funkci COINITIALIZE však můžeme zavolat ještě jednou před metodou INITIALIZE, pokud ji spárujeme s odpovídajícím voláním COUNINITIALIZE.

Ukázka 24. Jednoduché použití COINITILIZESECURITY
OleCheck(CoInitialize(nil));
OleCheck (CoInitializeSecurity(nil, -1, nil, nil, 1 { RPC_C_AUTHN_LEVEL_NONE},
  1 { RPC_C_IMP_LEVEL_ANONYMOUS }, nil, 0 { EOAC_NONE }, nil));
Application.Initialize;
try
  Application.Run;
finally
  CoUninitialize;
end;

4.15.7 Zjištění bezpečnostních údajů klienta

Pokud nám nestačí nastavování oprávnění na úrovni objektů nebo metod (s použitím COM+), ale potřebujeme práva odlišit například podle parametrů metody, nevystačíme s dostupnými nástroji. K řešení tohoto problému je nutné provést již v implementaci metod. Příkladem použití je v naší aplikaci rozhodnutí o spravované sadě klíčků podle uživatele, který volá metodu SPRAVOVANASADAKLICKU z rozhraní IESANASTAVENI.

COM poskytuje serveru možnost získat informace i klientovi prostřednictvím rozhraní ISERVERSECURITY. Aplikace získá referenci na toto rozhraní voláním API funkce COGETCALLCONTEXT [1]. Důležité je si uvědomit, že reference je platná pouze pro jedno volání.

Rozhraní ISERVERSECURITY obsahuje čtyři metody. Tři z nich jsou určeny ke kontrole zosobnění, pomocí nich může server aktivovat zosobnění programově, například jen pro určitou část kódu, kdy se přistupuje k určitým zdrojům. Tyto metody jsou: IMPERSONATECLIENT (aktivace zosobnění), REVERTTOSELF (deaktivace zosobnění) a ISIMPERSONATING (zjištění stavu zosobnění).

Nás však nyní zajímá poslední, čtvrtá metoda QUERYBLANKET. Její parametry se velmi podobají parametrům API funkce COINITIALIZESECURITY, ale jsou výstupní. Navíc má i parametr PRINCIPALNAME. Principal je entita, která metodu zavolala. Tedy typicky uživatel, přihlášený ke stanici. Hodnota má několik definovaných formátů, podle zvolené ověřovací služby. Pro zjednodušení budeme předpokládat jen nejjednodušší variantu, tedy že formát je "stanice/uživatel".

Pro usnadnění použití metody QUERYBLANKET je nám k dispozici API funkce COQUERYCLIENTBLANKET, která má stejné parametry [3]. Funkce získá referenci na ISERVERSECURITY voláním funkce COGETCALLCONTEXT, zavolá metodu QUERYBLANKET a nakonec uvolní získanou referenci. S její pomocí již implementace metody SPRAVOVANASADAKLICKU není obtížná.

Ukázka 25. Jednoduché použití funkce COQUERYCLIENTBLANKET k identifikaci klienta
procedure TEsa.SpravovanaSadaKlicku(out Sada: WideString);
var PrincipalName: PWideChar;
begin
  ...
  Vysledek := 0;
  OleCheck(CoQueryClientBlanket(nil, nil, nil, 0, 0, @PName, 0));
  if PName = 'KNIHOVNA_RECEPCE/satna' then Sada := 'Žižkov'
    else
  if PrincipalName = 'JAROV_RECEPCE/skrinky' then Sada := 'Jarov'
    else raise Exception.Create('Stanice není oprávněna spravovat klíčky');
  ...
end;

4.15.8 Fyzické umístnění klienta

Aby ověřování popsané v předchozí kapitole fungovalo, je nutné aby klientská i serverová stanice sdílely seznam uživatelů, například tím, že jsou zařazeny do stejné domény. Tyto podmínky nelze v prostředí počítačové sítě VŠE zajistit, protože se nepoužívá Microsoft Network, ale Novell Netware.

Alternativou, i když ne tak flexibilní, může být alespoň pro rozhodování o obsluhované sadě klíčků umístnění klienta. Polohu lze určit například podle IP adresy, čísla ethkarty, doménového jména apod.

Ani tento postup však není možný. Jak jsem již zmínil, jedním z principů architektury COM je transparentnost umístnění (location transparency). To znamená, že ani server ani klient neví, kde je druhá strana fyzicky umístněna. Samozřejmě s výjimkou případu, kdy klient volá funkci COCREATEINSTANCEEX s adresou serveru (i když i v tomto případě může vzdálená stanice delegovat volání na libovolný další server). Proto neexistuje žádná API funkce ani standardní rozhraní, které by nám umožnilo zjistit adresu klienta.

Jednou z možností je spolehnout se na klienta a nechat ho zavolat metodu, kterou serveru sdělí "kde je". Pro zachování bezstavového přístupu by bylo nutné přidat takový parametr ke všem metodám všech rozhraní. Z hlediska bezpečnosti je však tato varianta naprosto nedostačující. Nikdy si nemůžeme být jisti, jestli náš objekt nevolá někdo s "nekalými úmysly".

Možností by bylo rozšířit tento postup o vlastní autorizaci, založenou například na algoritmu MD5. Mimo parametru s adresou by se vyžadoval další se zašifrovanou adresou rozšířenou o tajný klíč, který by znali jen naši klienti (ve smyslu aplikace) a server. Server by s přijatou adresou provedl stejný postup a výsledek by porovnal s hodnotou druhého parametru. Tento způsob je jen jedním z mnoha možných, ale stejně jako všechny přináší velké dodatečné náklady. Zvláště pro bezstavové komponenty by bylo přidání dvou dalších parametrů ke každé metodě velmi neefektivní (a neelegantní).

[19] zmiňuje další řešení s využitím volitelných částí hlavičky paketu protokolu ORPC (object remote procedure call) a standardních systémových rozhraní ICHANNELHOOK a IRCPCHANNELBUFFER. Tím lze předávání adresy s každým voláním automatizovat a zároveň se vyhneme nutnosti přidávat parametry do definicí metod. Řešení je relativně komplikované a svým zaměřením vybočuje z tématu mé práce, proto ho zde nebudu uvádět.

4.15.9 Výjimka "Přístup byl odepřen"

Často můžeme potřebovat dokázat zjistit, že důvodem neúspěchu získání reference na rozhraní jsou nedostatečná práva (nebo jiná bezpečnostní omezení). Můžeme například chtít zobrazit uživateli návod, jak si zažádat o povolení k přístupu k aplikaci (serveru). Funkce COCREATEINSTANCE, vrací chybový kód jako svůj výsledek pomocí typu HRESULT. Tato 32-bitová hodnota obsahuje ve svých dolních 16 bitech kód chyby, ke které došlo [1]. Chyba "Přístup byl odepřen" (Access denied) má kód 5 (konstanta ERROR_ACCESS_DENIED). Delphi na základě výsledku HRESULT generují výjimku EOLESYSERROR, která je velmi podobná již zmíněné EOLEEXCEPTION používané v souvislosti s konvencí volání stdcall. Kód HRESULT je obsažen v její vlastnosti ERRORCODE.

Ukázka 26. Odchycení výjimky "Přístup byl odepřen"
try
  EsaServer := CoEsa.Create;
except
  on E: EOleSysError do
  begin
    if E.ErrorCode and $FF = ERROR_ACCESS_DENIED then JakZiskatPristPrava;
    raise;
  end;
end;

4.15.10 Časté chyby a problémy

Při vývoji a testování aplikace klíčky jsem narazil na mnoho problémů a chybových hlášení, se kterými jsem si často nevěděl rady. Je pravděpodobné, že se stejnými problémy se bude zpočátku potýkat každý vývojář bez hlubších znalostí architektury COM. Proto zde uvádím seznam nejčastějších problému, jejich příčin a řešení. Protože všechny nějak souvisí s bezpečnostními parametry a ověřováním oprávněnosti přístupu, uvádím přehled v této kapitole.

Nelze se připojit k serveru, pokud je server v jiné doméně, pracovní skupině apod.
Aby autorizace fungovala správně musí být server i klient ve stejné doméně (resp. pracovní skupině). Důvodem je fakt, že uživatelská jména (i skupiny uživatelů) jsou platná jen v rámci domény (resp. pracovní skupiny) a to i když je uživatelské jméno v druhé doméně stejné. Jednou z možností, jak umožnit připojení klienta z jiné domény, je přidat uživatele network do seznamu oprávněných uživatelů [2]. Tím ovšem umožníme přístup prakticky komukoli.
Chyba "Interface not supported"
Nejtypičtější příčinou této chyby je, že knihovna typů serveru není zaregistrována buď na klientské stanici (častěji) nebo na serverové stanici.
Problém lze často také vyřešit přidáním jednoho z následujících uživatelů do seznamu oprávněných uživatelů: network, interactive user nebo system.
Chyba se dále objevuje pokud je nastaveno zosobnění spouštějící uživatel (launching user), na serverové i klientské stanici je přihlášen uživatel administrator a každý má jiné heslo. Protože server za těchto okolností "jedná" jménem klienta, autorizace samozřejmě selže. Pokud není možné změnit uživatele nebo heslo, lze problém vyřešit změnou zosobnění na konkrétního uživatele (volba následující uživatel v DCOMCNFG) a to na uživatele administrator (s heslem, který má na serverové stanici).
Chyba "Přístup byl odepřen"
O této chybě jsem se již zmiňoval, jedná se o jednu z nejčastěji hlášených. V první řadě je nutné zkontrolovat seznam oprávněných uživatelů. Pokud je vybrána možnost "Používat vlastní přístupová oprávnění" (custom access permissions) a seznam je prázdný, je přirozené, že se nikdo nemůže k serveru přihlásit. Je třeba se ujistit, jménem jakého uživatele se klient k serveru připojuje a toho přidat do seznamu (s volbou "povolit přístup").
Pokud je objekt volaný z IIS (Internet Information Server), je nutné do seznamu přidat uživatele network (to bylo již zmíněno v kapitole 4.15.3) [2].
Stejně jako u chyby "Interface not supported" je vhodné zjistit, jestli vzhledem k požadovanému nastavení není potřeba přidat do seznamu některého z uživatelů network, interactive user nebo system.
Chyba "Nebyla volána procedura COINITIALIZE"
O tomto problému jsem se již také zmiňoval, tedy znovu a stručně: API funkce COINITIALIZE musí být volána z každého vlákna před všemi dalšími funkcemi COM. Delphi volají tuto funkci sami z metody TAPPLICATION.INITIALIZE. Toto volání platí ovšem pouze pro hlavní vlákno aplikace, pro všechna další ho musí programátor zajistit sám [1].
Chyba "Katastrofické selhání"
Nejčastějším důvodem této chyby je vyvolání výjimky z metody, která používá konvenci volání stdcall. V takových metodách musí být výjimka odchycena, aby neopustila hranici procesů.
Nelze získat referenci na rozhraní vzdáleného serveru
Aby bylo možné využívat distribuovaný COM (DCOM), musí být povolen na obou stanicích. DCOM se aktivuje v nástroji DCOMCNFG volbou "Povolit používání modelu DCOM na tomto počítači" ("Enable distributed COM on this computer") na záložce "Výchozí vlastnosti" ("Default properties").

4.16 Webový klient

V této části věnuji několik stránek popisu implementace klienta v prostředí ASP (Active Server Pages). Původním záměrem této kapitoly bylo prakticky demonstrovat univerzálnost architektury COM a její nezávislost na vývojovém prostředí. Ve skutečnosti jsem ale zjistil, že její implementace v prostředí ASP není úplná (a z principu asi ani být nemůže). Důsledkem je, že objekt ESA, tak jak je nyní navržen nemůže být ve webové aplikaci použit. Existují proto dva zásadní důvody:

Nejdříve popíši řešení druhého obecnějšího problému. Nejjednodušší, ale zároveň neelegantní, způsob jak problém obejít je definovat nové rozhraní (typu dispinteface), které bude shrnovat veškerou funkcionalitu ostatních rozhraní, resp. alespoň takovou funkcionalitu, jakou vyžaduje webový klient. Toto rozhraní bude nastaveno jako výchozí. Neelegantnost řešení spočívá v tom, že nás nutí vytvořit speciální rozhraní určené jednomu typu klientů, které nijak nerozšiřuje možnosti serveru.

Podmínkou funkčnosti řešení je, aby žádný klient, který nepoužívá dynamické volání, nevolal metody nově definovaného rozhraní (nazvěme ho IESADISPATCH). Proč tomu tak nesmí být? Příčina spočívá v principu neměnnosti rozhraní. Časem budeme jistě chtít rozšířit funkcionalitu objektu ESA. Pokud bychom nepoužili dynamické volání, nadefinovali bychom nové rozhraní, které by novou funkcionalitu zpřístupnilo světu. Ovšem, aby nové metody mohl využít i webový klient, musíme je přidat i do výchozího rozhraní typu dispinteface. Tím by ale došlo ke změně již dříve definovaného rozhraní. Klienti, kteří nepoužívají dynamické volání, ale metodu early-binding, jsou závislí na znalosti pořadí metod v rozhraní. Změnou rozhraní může dojít ke změně pořadí. Potom by již existující klienti začali volat nesprávné metody. Tento problém webový klient mít nebude, protože při volání metod pomocí IDISPATCH dochází k vázání pomocí názvu metody (late-binding), který se nezmění. [15] uvádí další způsoby jak obejít tento problém.

Omezení typu výstupních parametrů (první problém) nás nutí předefinovat typy všech výstupních parametrů metod nového rozhraní IESADISPATCH na VARIANT (OLEVARIANT). Typy vstupních parametrů mohou zůstat zachovány.

4.16.1 Úpravy serveru

Upravíme tedy náš objekt tak, aby byl použitelný i z prostředí ASP. Úprava spočívá v přidání nového rozhraní, které splňuje podmínky naznačené v předchozích odstavcích. Do rozhraní nepřidáme všechny metody, ale jen ty, které využijeme v jednoduchém webovém klientovi. Jak již bylo vysvětleno, pozdější přidání dalších metod je možné.

Ukázka 27. Výchozí rozhraní IESADISPATCH s výstupními parametry typu VARIANT
...
interface IEsaDispatch: IDispatch
{
  [id(0x00000001)]
  HRESULT _stdcall SpravovanaSadaKlicku([out] VARIANT * Sada );
  [id(0x00000002)]
  HRESULT _stdcall KlickyVSade([in] BSTR Sada, [out] VARIANT * Celkem,
     [out] VARIANT * Pujcene, [out] VARIANT * Volne );
  [id(0x00000003)]
  HRESULT _stdcall KlicekSeznam([in] BSTR Sada, [out] VARIANT * SeznamKlicky );

...
coclass Esa
{
  [default] interface IEsaDispatch;
  interface IEsaIdkarta;
  interface IEsaKlicek;
  interface IEsaNastaveni;
};
...

Protože chci, aby se metody rozhraní IESADISPATCH jmenovaly stejně jako jim odpovídající metody v původních rozhraních, vznikne zřejmě konflikt ve jménech metod na objektu TESA. Nebude totiž použita již existující implementace metod, protože parametry metod v původních a novém rozhraní se liší v typu výstupních parametrů. Musí být tedy implementovány nové metody se stejným jménem. Delphi (resp. jazyk Object Pascal) tuto možnost nevylučují, stačí u definice obou metod použít klíčové slovo OVERLOAD. Bohužel editor knihovny typů s touto situací nepočítá a bude nám soustavně dříve nadefinovanou metodu umazávat. Změny tedy musíme provést "ručně" přímo v kódu.

Ukázka 28. Metody stejného jména
procedure SpravovanaSadaKlicku(out Sada: WideString); overload; safecall;
procedure SpravovanaSadaKlicku(out Sada: OleVariant); overload; safecall;

Všechny tři nově přidané metody nebudou provádět nic nového, pouze zavolají již existující stejnojmenné metody s generickými typy výstupních parametrů. Čtenář si jistě všiml, že metody již nemají výstupní parametr VYSLEDEK, jehož funkci převzalo vyvolávání výjimek (viz kapitola 4.12).

Ukázka 29. Implementace metod rozhraní IESADISPATCH
procedure TEsa.SpravovanaSadaKlicku(out Sada: OleVariant);
var ASada: WideString;
begin
  SpravovanaSadaKlicku(ASada);
  Sada := ASada;
end;

4.16.2 Změna bezpečnostního nastavení

Jak jsem již několikrát napsal, při přístupu ke COM objektu z webové aplikace a při použití jednoduchého bezpečnostního modelu, je nutné přidat do seznamu oprávněných uživatelů speciálního uživatele network. Je zřejmé, že z hlediska bezpečnosti není tato varianta vhodná, umožní totiž přístup k objektu komukoli.

Pokud jako webový server používáme IIS (Internet Information Server), je možné pro odlišení požadavků ze serveru od ostatních požadavků z vnějšku použít uživatelská jména IUSR_XXX a IWAN_XXX (kde XXX je jméno stanice).

Při použití rozšířeného bezpečnostního modelu COM+ je možné definovat přístupová oprávnění na úrovni rozhraní a metod. Mohli bychom tedy přístup "ze sítě" povolit jen k rozhraní IESADISPATCH, které zatím zpřístupňuje jen metody sloužící "pro čtení".

Existuje mnoho způsobů jak bezpečnostní problém řešit. Nejjednodušší situace nastane v případě, že v systému používáme výhradně "prvky" firmy Microsoft, tedy např. IIS a Microsoft Network. Při práci v heterogenním prostředí, jakým je právě síť VŠE, je řešení komplikovanější.

4.16.3 Implementace webového klienta

Klient bude implementován v prostředí IIS v jazyce Visual Basic Script. Jeho funkcionalita bude velmi omezená (účelem je pouze demonstrovat použití COM objektu z jiného prostředí). Web se skládá pouze ze dvou stránek, kde první obsahuje seznam klíčků v jedné sadě s odkazem na podrobnější údaje o klíčků na další stránce. Informace jsou tedy pouze pro čtení, metody rozhraní IESADISPATCH, tak jsme ho definovali, ani nic jiného neumožňují.

Obrázek 12. Rozhraní webového klienta
Obrázek 12. Rozhraní webového klienta (8 kB)

K použití COM objektu ve VB skriptu pouze poznamenám, že reference na objekt se získá voláním metody CREATEOBJECT objektu SERVER. Jediným parametrem je identifikátor objektu. Tím tentokrát není GUID, ale tzv. ProgID. Tento údaj se skládá z názvu knihovny typů a tečky následované názvem objektu. Pro náš objekt je to tedy ESASERVER.ESA.

Ukázka 30. Použití objektu ESA ve Visual Basic skriptu
<%@ Language=VBScript %><%
option explicit

dim Esa
set Esa = Server.CreateObject("ESAServer.Esa")
dim Sada, Celkem, Pujcene, Volne, Seznam

' Zjisteny spravovane sady klicku
Esa.SpravovanaSadaKlicku Sada
' Zjisteni poctu klicku v sade
Esa.KlickyVSade Sada, Celkem, Pujcene, Volne
' Seznam klicku
Esa.KlicekSeznam Sada, Seznam

Response.Write _
  "<html><head><title>Klíčky</title></head><body>" & vbNewLine & _
  "<h1>Sada klíčků '" & Sada & "'</h1>" & vbNewLine & _
  "<table border=1>" & vbNewLine & _
  "<tr><th>Klíček</th><th>Stav</th><th>Vlastník</th></tr>" & vbNewLine

dim Klicek, Vlastnik

for Klicek = 1 to Celkem
  if Seznam(Klicek)(2) = 1 then
    Vlastnik = Seznam(Klicek)(5)
  else
    Vlastnik = "není"
  end if
  Response.Write "<tr><td><a href=""klicek.asp?klicek=" & Klicek & _
    """>" & Klicek & "</a></td><td>" & Seznam(Klicek)(3) & _
    "</td><td>" & Vlastnik & "</td></tr>" & vbNewLine
next

Response.Write "</table></body></html>"
%>

4.17 Použití COM+

Nyní již konečně přistoupíme k velmi pokročilým tématům. Zároveň začneme ve větší míře využívat vlastností a funkcí COM+, tedy rozšíření původní architektury COM. Je tedy na místě se zmínit, co to vlastně COM+ je. Tímto názvem se označuje souhrn několika komponent, které jsou součástí operačního systému Windows 2000/XP. Mezi ně patří COM, DCOM, MTS (Microsoft Transaction Server), MSMQ (Microsoft Message Queue) a další [4].

Ne všechny funkce COM+ si můžeme dovolit v naší aplikaci využít. Velká část stanic, na kterých bude systém ESA provozován, nedisponuje operačním systémem Windows 2000/XP. Lze předpokládat, že bude k dispozici alespoň pro stanice, kde poběží serverová část. Můžeme tedy využít jen ty nové funkce, které jsou volány na serverové vrstvě a jsou pro klientské aplikace absolutně transparentní.

4.17.1 Aplikace COM+

Pojem aplikace v kontextu architektury COM+ neznamená spustitelný soubor. Myslí se jím skupina komponent, které dohromady plní určitou funkci. Aplikace se považuje za celek, lze s ní tak nakládat při administraci, nastavování přístupových práv a podobně. Podobným ale starším pojmem je balíček v MTS, který byl používán v prostředí Windows NT. Balíčky je možné do COM+ importovat, čímž se stanou plnohodnotnou aplikací COM+. Aplikace je tvořena jednou nebo několika dynamickými knihovnami (DLL), které obsahují COM objekty.

Ke správě aplikací a objektů COM+ slouží nástroj Služba Component Services, kterou lze vyvolat z Ovládacích panelů Windows 2000/XP. O jeho funkcích se zmíním v dále v této kapitole.

4.17.2 Převedení programu Klíčky na aplikaci COM+

Jak již bylo zmíněno, je aplikace COM+ tvořena dynamickými knihovnami. Musíme tedy implementaci objektu ESA přesunout ze spustitelné EXE aplikace do DLL knihovny. Převod není složitý, stačí vytvořit nový projekt Active X Library a přidat do něj knihovnu typů původní aplikace a vlastní implementaci objektu ESA (včetně datového modulu). Po zkompilování projektu se vytvoří soubor s příponou DLL.

Dalším krokem je vytvoření aplikace COM+, do které následně přidáme objekt ESA. V tom nám pomůže nástroj Služba Component Services. V jeho navigačním stromě nalezneme položku Aplikace COM+ a v menu Akce zvolíme volbu Nový/Aplikace. Pomocí Průvodce instalací aplikace modelu COM vytvoříme novou serverovou aplikaci se jménem ESA. Do aplikace přidáme novou komponentu (opět v menu Akce, tentokrát položky Komponenty), kterou je objekt ESA v DLL knihovně.

Obrázek 13. Nová COM+ aplikace "Klíčky"
Obrázek 13. Nová COM+ aplikace "Klíčky" (23 kB)

4.18 Řízení zdrojů

4.18.1 Sdružování COM objektů

První a velmi užitečnou funkcí COM+, kterou využijeme v projektu je sdružování objektů (object pooling). Sdružování znamená, že několik instancí stejného objektu je sdruženo v poolu. Pokaždé, když klient chce vytvořit novou instanci objektu, zkusí COM+ nalézt volnou instanci v poolu. Za volnou se nepovažuje jen instance, na kterou nemá žádný klient referenci, ale i instance, která právě nevykonává žádnou metodu. To znamená, že jedna instance může obsluhovat několik klientů a zároveň každé volání metody jedním klientem může obsloužit jiná instance. Tento proces však musí být pro klientskou aplikaci transparentní. Aby to mohlo být zajištěno, jsou na sdružované objekty kladeny další nároky.

Tabulka 7. Podmínky pro sdružování objektů [3], [4]
Podmínka Popis
Aktivované sdružování V nástroji Služba Component Services je nutné u daného objektu zatrhnout na záložce "Aktivace" volbu "Povolit sdružování objektů" a nastavit vhodné parametry.
Žádné vazby na vlákno Protože objekt může být volán z jakéhokoli vlákna (klienta) nesmí být vázán na žádné vlákno (například na vlákno klienta, na jehož žádost byl objekt původně vytvořen). To znamená, že vláknový model musí být Both, Free nebo Neutral (vláknové modely se u COM+ liší)
Objekt musí být možné agregovat Tato podmínka zahrnuje několik dalších. Čtenáře odkazuji na [3].
Objekt musí být bezstavový Podobně jako u nezávislosti na vláknu znamená bezstavovost, že objekt nesmí uchovávat žádné informace o stavu, které by byly závislé na klientovi. Tato podmínka není absolutní. Objekt může udržovat stav v rámci transakce, během ní je zaručené, že objekt obsluhuje stejného klienta.
Implementace rozhraní IOBJECTCONTROL Sdružovatelné objekty by měli implementovat rozhraní IOBJECTCONTROL. Je to nutné pouze pro transakční objekty.

Důvody vedoucí k použití sdružování jsou tři: snaha o zvýšení výkonu, škálovatelnost aplikací a řízení zdrojů. Vzhledem k definovaným požadavkům je pro nás nyní důležitý třetí důvod. V aplikaci Klíčky jsou zatím jediným zdrojem databázová spojení, kterých je omezený počet.

K řízení zdrojů lze využít parametry sdružování, které COM+ nabízí. Jsou jimi Minimální a maximální počet sdružených objektů (Minimum and maximum pool size) a Časový limit vytvoření (Creation timeout).

K řízení zdrojů (konkrétně databázových spojení) se používá hlavně parametr maximální počet sdružených objektů. Typicky se nastaví na hodnotu, která odpovídá počtu licencí. Pokud při volání metody objektu klientem již není k dispozici žádný volný objekt a počet sdružených objektů dosáhl horního omezení, nebude vytvořena nová instance objektu, která by požadavek obsloužila. Nejspíš by pro ni ani nebylo volné databázové spojení. Místo toho se volání metody pozastaví na dobu omezenou třetím parametrem (Časový limit vytvoření). Pokud se do té doby uvolní nějaký z objektů, obslouží volání. Pokud se žádný neuvolní, volání metody selže s výsledkem E_TIMEOUT.

Ještě se zmíním o účelu dolní hranice počtu sdružených objektů. Po spuštění COM+ aplikace (vytvořením první instance objektu na základě požadavku klienta, volbou Spustit z nástroje Služba Component Services nebo libovolným jiným způsobem) se automaticky vytvoří tolik instancí objektu, kolik specifikuje tento parametr. Zároveň nikdy (dokud aplikace běží) nedojde ke snížení jejich počtu pod stanovenou hladinu, a to i když žádná instance není využívána. Tuto vlastnost využijeme zejména tehdy pokud je vytvoření instance časově nebo jinak náročné (například z důvodu alokace zdrojů). Předejdeme tím pomalé reakci serveru v normální situaci, kdy se instance vytváří až při požadavku klienta.

Obrázek 14. Nastavení parametrů sdružování objektů
Obrázek 14. Nastavení parametrů sdružování objektů (7 kB)

4.18.2 Implementace rozhraní IOBJECTCONTROL a potvrzování transakcí

Objekt ESA již má aktivovanou podporu sdružování, není vázán na vlákno, je agregovatelný a bezstavový. Poslední podmínkou pro funkční sdružování je implementace rozhraní IOBJECTCONTROL. Delphi definují třídu TMTSAUTOOBJECT, která je odvozená z TAUTOOBJECT a toto rozhraní již má. My ho však budeme implementovat pro názornost sami.

Rozhraní IOBJECTCONTROL obsahuje tři metody: ACTIVATE, DEACTIVATE a CANBEPOOLED. Metoda ACTIVATE resp. DEACTIVATE je volána, když dojde k aktivaci resp. deaktivaci instance objektu. Tedy když je nevyužitá instance povolána k obsluze volání metody rozhraní objektu a naopak když je instance uvolněna. Po té si COM zjistí jestli je možné instanci "odložit" k pozdějšímu použití. To provede voláním metody CANBEPOOLED. Díky ní může objekt zabránit svému odložení. To je nutné zejména v případě, když objekt dočasně porušuje pravidlo bezstavovosti, pokud oprávněně očekává bezprostřední volání dalších metod stejným klientem. Metoda ACTIVATE je volána před jakýmkoli jiným voláním metod ostatních rozhraní objektu a metody DEACTIVATE a CANBEPOOLED naopak po jejich provedení.

Dříve než dojde k odložení objektu musí být dokončena transakce. Pojem transakce se v původní architektuře COM neobjevoval, je to nový prvek COM+. Protože v naší aplikaci transakce nevyužijeme, nebudu se jimi hlouběji zabývat. Podstatné je jen to, že transakce je sekvence volání metod, která musí proběhnout jako celek. U našeho objektu ESA je transakcí každé jednotlivé volání metody. To znamená, že na konec implementace každé z nich musíme přidat potvrzení transakce.

Transakce se obsluhují pomocí rozhraní IOBJECTCONTEXT, které se vytváří samo a je jedinečné pro každého klienta. To znamená, že referenci na něj musí objekt získávat pro každého klienta zvlášť. K tomu se právě hodí metoda ACTIVATE rozhraní IOBJECTCONTROL. K získání reference na rozhraní aktuálního klienta slouží API funkce COGETOBJECTCONTEXT. K potvrzení transakce zpřístupňuje rozhraní metodu SETCOMPLETE.

Abychom získali aktuální definici rozhraní IOBJECTCONTEXT a IOBJECTCONTROL, je vhodné importovat knihovnu typů COM+ Services Type Library (soubor COMSVCS.DLL) volbou Import Type Library z menu Project v IDE Delphi. Tím se vygeneruje soubor COMSVCSLIB_TLB.PAS.

Zajímavé je, že není možné získat referenci na rozhraní IOBJECTCONTROL žádného objektu. Toto rozhraní může používat jen sám systém COM+.

S všemi těmito poznatky můžeme přistoupit k programování.

Ukázka 31. Implementace rozhraní IOBJECTCONTROL a potvrzování transakcí
uses
  ..., COMSVCSLib_TLB;

type
  TEsa = class(TAutoObject, IEsaIdkarta, IEsaKlicek,
    IEsaNastaveni, IEsaDispatch, IObjectControl)
  private
    FObjectContext: IObjectContext;
    { metody rozhrani IObjectControl }
    function Activate: HResult; stdcall;
    procedure Deactivate; stdcall;
    function CanBePooled: Integer; stdcall;
    ...
  protected
    procedure SpravovanaSadaKlicku(out Sada: WideString); safecall;
    ...
  end;

procedure TEsa.SpravovanaSadaKlicku(out Sada: WideString);
begin
  ...
  { potvrzeni transakce (na konci vsech metod vsech rozhrani objektu }
  FObjectContext.SetComplete;
end;

function TEsa.Activate: HResult;
begin
  { ziskani reference na IObjectContext aktualniho klienta }
  OleCheck(CoGetObjectContext(IObjectContext, @FObjectContext));
  Result := S_OK;
end;

function TEsa.CanBePooled: Integer;
begin
  { objekt muze byt vzdy odlozen }
  Result := 1;
end;

procedure TEsa.Deactivate;
begin
  { rozhrani IObjectContrext jiz neni nadale platne }
  FObjectContext := nil;
end;

4.18.3 Klient

V úvodu této kapitoly jsem psal, že při využívání funkcí COM+ se budu držet zásady, že se provedené změny nesmí projevit na straně klienta. Tato zásada byla dodržena. Můžeme tedy stávající klientskou aplikaci stále používat. Podmínkou je, že se nezměnila GUID rozhraní objektu ESA.

V úvodu jsem psal, že pro přístup k objektu ze vzdálené klientské stanice musí být jeho implementace obsažena ve spustitelném EXE souboru. Tato podmínka neplatí pro aplikace COM+, které jsou povinně implementovány v DLL knihovně. Je to proto, že COM+ spouští komponenty v rámci surrogate-procesu DLLHOST, což je EXE aplikace.

4.18.4 Sledování sdružených objektů

Nyní již můžeme aplikaci spustit, abychom si ověřili, že sdružování skutečně funguje. Ke sledování počtu sdružených a aktivovaných objektů lze opět použít nástroj Služba Component Services a jeho položku Komponenty v sekci sledované aplikace (ESA). Je třeba přepnout na zobrazení Stav. V následujícím obrázku jsou z důvodu úspory místa skryté některé části ovládacího panelu.

Obrázek 15. Počty sdružených a aktivovaných objektů
Obrázek 15. Počty sdružených a aktivovaných objektů (13 kB)

4.19 Parametrizování objektu

Pozorný čtenář si jistě již všiml, že nástroj Služba Component Services umožňuje nastavení mnoha dalších parametrů objektu. Hned pod parametry sdružovaní je skupina Vytváření objektů. Ta umožňuje určení volitelného řetězce, který bude předán každé instanci objektu ihned po jejím vytvoření.

Tento parametr lze s výhodou použít k nastavovaní údajů, které se mohou lišit podle umístnění a účelu serveru. Lze tím nahradit konfigurační soubor nebo zápis do registrů Windows. Jako typický příklad se uvádí Connection string pro ADO. My parametru použijeme k určení názvu ODBC zdroje. Ten je zatím napevno nastaven v datovém modulu aplikace.

Obrázek 16. Parametry vytváření objektů
Obrázek 16. Parametry vytváření objektů (2 kB)

Vlastní předání parametru objektu probíhá tak, že po vytvoření instance se zavolá metoda CONSTRUCT rozhraní IOBJECTCONSTRUCT, které je definováno ve standardu COM+. Metoda předá jako jediný parametr referenci na další standardní rozhraní IOBJECTCONSTRUCTSTRING. Pomocí něj již může objekt získat řetězec konstruktoru čtením vlastnosti CONSTRUCTSTRING nebo voláním metody GET_CONSTRUCTSTRING.

Ukázka 32. Získání řetězce konstruktoru
type
  TEsa = class(TAutoObject, IEsaIdkarta, IEsaKlicek,
    IEsaNastaveni, IEsaDispatch, IObjectControl, IObjectConstruct)
  private
    FOdbc: WideString;
    function Construct(const ObjectConstructString: IDispatch): HResult;
      stdcall;
    ...
  end;

function TEsa.Construct(const ObjectConstructString: IDispatch): HResult;
begin
  { ziskani retezce konstruktoru pomoci rozhrani IObjectConstructString }
  FOdbc := (ObjectConstructString as IObjectConstructString).ConstructString;
  Result := S_OK;
end;

Řetězec konstruktoru lze v kombinaci se sdružováním objektů použít k vytvoření několika disjunktních skupin jinak totožných objektů. Tím lze například jemněji rozložit omezený počet zdrojů (databázových spojení) mezi klienty podle jejich priorit. Klienti s vyšší prioritou budou volat objekt, s větší horní hranicí počtu sdružených objektů než objekt určený pro klienty s nižší prioritou. Jinak mohou být implementace všech objektů totožné, lišit se musí jen svým CLSID a případně řetězcem konstruktoru. IID rozhraní by měli zůstat stejné.

4.20 Optimalizace s pomocí sdílených vlastností

4.20.1 Správce skupin sdílených vlastností

Další novou funkcí, kterou COM+ zavádí je Správce skupin sdílených vlastností (Shared Property Group Manager, dále jen SPM). SPM je tzv. Resource Dispenser. S jeho pomocí mohou mezi sebou instance jednoho objektu nebo i více různých objektů sdílet stav (ve smyslu informace, která přetrvává mezi voláním metod objektu). Pro stav je v kontextu SPM používán pojem sdílená vlastnost (shared property). Tu si lze představit jako položku slovníku, má totiž svůj název a hodnotu [12]. Hodnota je VARIANT, může tedy obsahovat informace různých typů.

Aby objekty mohly sdílet vlastnosti, musí být obsažené ve stejné aplikaci COM+. Tím se první rovině řeší problém konfliktu ve jménech. Druhou rovinou jsou skupiny sdílených vlastností, do kterých SPM vlastnosti sdružuje. Skupiny mají opět svůj název, který musí být unikátní v rámci aplikace.

SPM také řeší problém mnohonásobného přístupů pomocí zámků a semaforů. Volitelná je doba udržování hodnot vlastností. Hodnota se může ztratit buď v okamžiku, kdy je zrušená poslední reference na objekt, který vlastnost implementuje (viz dále), nebo až tehdy, když je ukončen proces aplikace. Je tedy zřejmé, že data uložená v SPM nejsou trvalá.

4.20.2 Použití sdílených vlastností

Sdílené vlastnosti lze využít minimálně ke dvěma účelům. První je zřejmý, tedy sdílení určitých dočasných vlastností mezi objekty (typicky instancemi stejné třídy). Druhou možností je využít sdílené vlastnosti k optimalizaci, například jako formu mezipaměti pro přístup často využívaným datům v databázi.

První zmíněný účel se na první pohled velmi podobá funkcím třídy TROZHRANI, kterou jsem zavedl v kapitole 4.7 pro evidenci počtu připojených klientů a počtu volaných metod. Oba údaje jsou sdílené vlastnosti společné pro všechny instance objektu ESA. Pro přístup k nim jsem také musel řešit problém mnohonásobného přístupu pomocí kritické sekce. Jsou to tedy zřejmě stejné problémy, které již SPM řeší.

Protože sdílené vlastnosti jsou již vyřešené třídou TROZHRANI, požijeme SPM k druhému účelu, tedy optimalizaci. Když se vrátíme k implementaci metod objektu ESA, vidíme, že po každé změně v databázi klíčků je vyvolána událost, která na změnu upozorní všechny klienty (viz kapitola 4.11). Ti následně zavolají metodu KLICKYVSADE, aby aktualizovali svoje lokální údaje o stavu sady klíčků. Každé volání metody vyvolá čtení z databáze. Přitom všichni klienti obdrží stejné informace. Při velkém množství obslužných stanic to znamená zbytečnou zátěž pro databázový (a současně i aplikační) server. Po každém zápisu dochází k mnoha čtením, je to tedy typický případ, kdy může vytvoření mezipaměti pro čtení významně urychlit provoz serveru. A právě jako mezipaměť použiji v následující ukázce sdílené vlastnosti.

4.20.3 Práce s SPM

SPM je implementován objektem SHAREDPROPERTYGROUPMANAGER, který má jediné rozhraní ISHAREDPROPERTYGROUPMANAGER. Referenci na tento objekt (resp. jeho rozhraní) získáme jednoduše voláním funkce COCREATEINSTANCE. Pomocí zmíněného rozhraní vytvoříme voláním metody CREATEPROPERTYGROUP skupinu sdílených vlastností. Metoda má jako vstupní parametry jméno skupiny a dva údaje určující režim přístupu k vlastnosti a uvolňování vlastnosti. SPM může pouze zajišťovat, že k vlastnosti přistupuje jen jeden klient (LOCKSETGET) nebo může rezervovat vlastnost klientu po celou dobu vykonávání metody (LOCKMETHOD). O uvolňování vlastností jsem již psal, může k ní dojít po uvolnění všech referencí na skupiny vlastností (STANDARD) nebo s ukončením procesu aplikace (PROCESS). Metoda vrátí dvě hodnoty. Tou první je reference na rozhraní ISHAREDPROPERTYGROUP skupiny vlastností. Druhou je indikátor, který určuje jestli voláním metody došlo k vytvoření skupiny nebo bylo pouze vráceno rozhraní na již existující stejnojmennou skupinu. Je tedy jasné, že metoda CREATEPROPERTYGROUP neslouží jen k vytvoření skupiny, ale i k nalezení existující skupiny.

S referencí na rozhraní skupiny vlastností již můžeme získat přímo referenci na sdílenou vlastnost. Postup získání reference je obdobný práci se skupinou. Voláním metody CREATEPROPERTY (resp. CREATEPROPERTYBYPOSITION) rozhraní ISHAREDPROPERTYGROUP vytvoříme novou nebo získáme referenci na již existující vlastnost určenou jménem (resp. pořadím). Sdílená vlastnost má rozhraní ISHAREDPROPERTY s dvěma metodami: GET_VALUE a PUT_VALUE.

Pro usnadnění práce s SPM definují Delphi dvě funkce: CREATESHAREDPROPERTYMANAGER a CREATESHAREDPROPERTYGROUP. Obě jsou deklarované v knihovně MTX a vrací referenci na rozhraní SPM a skupiny sdílených vlastností. První funkci nemusíme povinně volat, protože je automaticky volaná z funkce druhé. Při vytváření skupiny se nastavují parametry LOCKSETGET a PROCESS (jejich význam byl popsán již dříve). Pro názornost ukázky však tyto funkce nepoužiji.

4.20.4 Implementace mezipaměti s využitím SPM

Pro podporu práce se sdílenými vlastnostmi přidáme do objektu ESA tři privátní metody: CTIMEZIPAMET, NASTAVMEZIPAMET a SMAZMEZIPAMET. Dále do konstruktoru resp. destruktoru objektu doplníme kód, který získá resp. uvolní referenci na skupinu sdílených vlastností.

Pro uvolňování hodnot vlastností použijeme režim PROCESS. To znamená, že se hodnoty uvolní až po ukončení aplikace (procesu) a ne již tehdy, kdy se uvolní poslední reference na skupinu vlastností. Tedy i v situaci, kdy jsou ukončeny všechny klientské aplikace (a tím i všechny instance objektu ESA), data v mezipaměti zůstanou. Budou moci být využita dalším spuštěným klientem.

Ukázka 33. Vytvoření skupiny sdílených vlastností
procedure TEsa.Initialize;
var
  Existuje: WordBool;
  RezimPristupu, RezimUvolnovani: Integer;
begin
  ...
  RezimPristupu := LockSetGet; RezimUvolnovani := Process;
  { vytvoreni SPM, po vytvoreni skupiny ho jiz nebudeme pozdeji potrebovat,
    proto referenci na jeho rozhrani neukladame do zadne promenne }
  with CreateComObject(CLASS_SharedPropertyGroupManager) as
      ISharedPropertyGroupManager do
  begin
    { vytvoreni skupiny sdilenych vlastnosti s danym jmenem a rezimy }
    { FMezipamet je privatni atribut objektu Esa }
    FMezipamet := CreatePropertyGroup('Mezipamet sad klicku',
      RezimPristupu, RezimUvolnovani, Existuje);
  end;
  ...
end;

Ve vytvořené skupině bude uložena jedna vlastnost pro každou ze sad klíčků. Vlastnost se uloží při prvním volání metody KLICKYVSADE. Hodnota se přirozeně získá z databáze. Při každém dalším volání této metody se data přečtou již z mezipaměti. Po každé změně dat, tedy po volání metody KLICEKVYPUJCENI nebo KLICEKVRACENI se mezipaměť vymaže. Naplní se opět při dalším čtení aktualizovaných dat z databáze.

Vlastnosti budou mít stejná jména jako sady klíčků. Hodnota vlastnosti bude typu pole se třemi položkami, které odpovídají parametrům metody KLICKYVSADE: celkový počet klíčků v sadě, počet půjčených a počet volných. Pro označení vymazané mezipaměti se použije hodnota NULL.

V následující ukázce je uvedena implementace metod CTIMEZIMAPET a NASTAVMEZIPAMET. Jsou také ukázány potřebné úpravy metody KLICKYVSADE (volání obou vedených metod). Mimo uvedený kód je třeba provést ještě další změny. Implementace metody SMAZMEZIPAMET je totožná s NASTAVMEZIPAMET s tím rozdílem, že se neukládá pole ale hodnota NULL. Tuto metodu je třeba volat po provedení změn v databázi v metodách KLICEKVYPUJCENI a KLICEKVRACENI.

Ukázka 34. Implementace metod pro práci s mezipamětí (SPM)
function TEsa.CtiMezipamet(Sada: WideString;
  var Celkem, Pujcene, Volne: Integer): Boolean;
var Existuje: WordBool;
begin
  with FMezipamet.CreateProperty(Sada, Existuje) do
  begin
    { zkontrolujeme jestli mezipamet existuje a obsahuje data v potrebnem formatu }
    Result := Existuje and VarIsArray(Get_Value);
    if Result then
    begin
      { nacteni vsech tri hodnot ulozenych v mezipameti }
      Celkem := Get_Value[0]; Pujcene := Get_Value[1]; Volne := Get_Value[2];
    end;
  end;
end;

procedure TEsa.NastavMezipamet(Sada: WideString; Celkem, Pujcene,
  Volne: Integer);
var Exists: WordBool;
begin
  with FMezipamet.CreateProperty(Sada, Exists) do
    { ulozeni vsech tri hodnot do mezipameti }
    Set_Value(VarArrayOf([Celkem, Pujcene, Volne]));
end;

procedure TEsa.KlickyVSade(const Sada: WideString;
  out Celkem, Pujcene, Volne: Integer);
begin
  ...
  { pokud jsou data jiz v mezipameti nepotrebujeme se cist z databaze }
  if not CtiMezipamet(Sada, Celkem, Pujcene, Volne) then
    with FDataModule.KlickyVSadeProc do
    begin
      { ziskani dat z databaze }
      ParamByName('sada').Value := Sada;
      ExecProc();
      Celkem := ParamByName('celkem').Value;
      Pujcene := ParamByName('pujcene').Value;
      Volne := ParamByName('volne').Value;
      { aktualizace dat v mezipameti (ta se vytvori se pri prvnim volani) }
      NastavMezipamet(Sada, Celkem, Pujcene, Volne);
    end;
  ...
end;

Opět jako při implementaci řízení zdrojů v kapitole 4.18 se provedené změny jinak nedotknou klientské aplikace.

4.21 Výpadek spojení

Poslední dosud neprobíraný požadavek, který kladu na architekturu, je řešení problému výpadku spojení. Jak jsem již napsal, spočívá řešení v umožnění práce klientské aplikace i po dobu ztráty kontaktu ze serverem. A to tím, že veškeré (nebo alespoň některé) funkce serveru jsou určitým způsobem suplovány na klientské stanici.

Zřejmým problémem je společná datová základna. Aby klient mohl pracovat i bez spojení se serverem, musí mít k dispozici svoji lokální kopii databáze resp. její určité části. Pro práci s určitými důležitými daty (obzvláště s možností zápisu) nebude možné tento přístup povolit, protože by mohlo dojít ke vzniku nekonzistencí.

U některých funkcí (resp. dat) však není problém vzniku nekonzistencí tak významný nebo dokonce tato situace nemůže ani nastat. Naše jednoduchá aplikace "Klíčky" je typickým příkladem. V případě, že k obsluze každé sady klíčků slouží pouze jedna stanice a ke změnám v platnosti karet nedochází příliš často a ve velkém rozsahu, můžeme problém nekonzistence s klidným svědomým ignorovat.

V okamžiku obnovení spojení se nám objeví další problém. Tím je synchronizace dat mezi klientem a serverem. Tato problematika je velmi komplikovaná a rozsáhlá. Je několik způsobů řešení. Je možné například využít replikačních funkcí samotného databázového serveru. Pro synchronizaci ve směru z klienta na server je možné použít log zaznamenaných operací a předat ho serveru ke zpracování. Tím si server sám aktualizuje svoji centrální databázi. V této kapitole hrubě nastíním jedno z možných řešení založené na podobném principu. Pro tento účel použiji novou funkci COM+ zvanou queued components, která je založena na MSMQ (Microsoft Message Queue Server). Než přejdu k vlastnímu řešení stručně nastíním, co je to MSMQ.

4.21.1 MSMQ

MSMQ je takzvaný Message Oriented Middleware (MOM). Pokud použiji reklamní slogan, je jeho cílem "rychlá a spolehlivá asynchronní elektronická komunikace". Jinak řečeno MOM obecně slouží ke zjednodušení vývoje distribuovaných aplikací, které budou provozovány na různých operačních systémech a síťových protokolech. Odstiňuje vývojáře od rozdílů v systémech a protokolech pomocí jednotného definovaného rozhraní (API). [4] uvádí několik případů a funkcí, kdy je vhodné MSMQ použít.

Tabulka 8. Použití MSMQ [4]
Použití Popis
Komunikace typu "ulož a předej" Technologie umožňuje aplikaci odesílat požadavky na server (ať už na stejné nebo jiné síti), o kterém se nepředpokládá, že bude běžet ve stejnou dobu jako aplikace.
Zabezpečení komunikace MSMQ chrání systém proti výpadkům spojení a umožňuje snazší překonání komunikačních špiček.
Současné spouštění Je možné posílat požadavky mnoha různým příjemcům bez nutnosti čekat na odpověď (dokončení činnosti). Potvrzení může být doručeno volitelně později včetně výsledků požadavku.
Deník komunikace Pokud je aktivní funkce deníku pr ourčitou frontu je automaticky vytvářena kopie všech správ odeslaných do fronty. Tím lze zajistit logování, audit i zálohování dat aplikace.
Komunikace "bez spojení" Pomocí MSMQ může přijímající aplikace předat zprávu jiné aplikaci pouhým její přeposláním do jiné fronty. Zpráva může být poslána i více příjemcům. Nezáleží na tom, jestli adresát je v daném okamžiku spuštěn, není tedy potřeba přímé spojení.

Použití MSMQ pro uvedené účely je zajištěno několika základními funkcemi a vlastnostmi.

Tabulka 9. Funkce a vlastnosti MSMQ
Funkce/vlastnost Popis
Zaručené doručení zprávy MSMQ zaručuje, že zpráva bude doručena. Odesílatel a adresát nemusejí být připojeni k síti ve stejný okamžik. K doručení dojde v okamžiku, kdy je spojení navázáno.
Asynchronní komunikace Jakmile je zpráva odeslána příjemci, může klient provádět další operace bez nutnosti čekat na odpověď od příjemce.
Podpora transakcí MSMQ podporuje transakce. Je úzce provázána s MTS (resp. COM+ ve Windows 2000/XP). Přítomnost MTS není pro vlastní funkce MSMQ nutná.
Doručení ve správném pořadí Je zajištěno, že všechny zprávy budou doručeny pouze jednou a ve stejném pořadí, v jakém byly odeslány.
Služba směrování zpráv Účelem této služby je zajištění předávání zpráv "nejlevnější" cestou. Administrátor systému definuje náklady pro jednotlivé cesty a MSMQ průběžně počítá náklady, aby bylo možné pro každou zprávu zvolit nejekonomičtější dráhu. Dále je možné službu použít k obcházení poruchových míst v nespolehlivých sítích (jako je Internet).
Doručenky Přestože komunikace pomocí MSMQ je asynchronní, je možné aby si odesílatel vyžádal potvrzení o doručení zprávy.
Dynamická konfigurace -
Podpora ActiveX Mimo definovaného API rozhraní je k dispozici i sada ActiveX komponent, které zpřístupňují většinu funkcionality MSMQ.
Integrace s bezpečnostním systémem Windows NT/2000/XP Přístupová práva k jednotlivým frontám MSMQ jsou provázána se systémem přístupových práv systému Windows NT/2000/XP
Integrace s dalšími produkty a systémy MSMQ lze integrovat s dalšími produkty jako je MAPI a MS Exchange. Společnost Level 8 Systems vyvinula produkt FalconMQ, který umožňuje provozovat MSMQ i na systémech jako je AS/400, Unix, MVS, VMS, OS/2 a UNISYS. Dále zajišťuje propojení MSQM s obdobným produktem CICS Transient Data Queue firmy IBM.

MSMQ zná dva typy zpráv: Express a Recoverable. Zprávy typu Express jsou uloženy v paměti, zatímco zprávy typu Recoverable na disku. Úložiště zpráv se nazývají fronty (queues). Každá fronta i zpráva má nastavenou prioritu, zprávy jsou doručovány nejdříve podle priority fronty a pak podle své vlastní priority.

Všechny komponenty MSMQ používají společnou distribuovanou databázi MQSI (MSMQ Information Store). Ta obsahuje všechny údaje o topologii sítě, konfiguraci komponent, informace o frontách a cestách. Aplikace mohou v databázi vyhledávat fronty a jejich vlastnosti.

Fronty jsou dvou typů: veřejné a soukromé. Informace o veřejných frontách jsou obsaženy v MQSI, mohou být tedy nalezeny libovolnou aplikací v síti. K soukromým frontám mohou přistupovat pouze aplikace, které znají její jméno (ve formě úplné cesty).

Komponenty MSMQ jsou servery, závislí klienti a nezávislí klienti. Nezávislí klient může na rozdíl od závislého fungovat i bez synchronní podpory od serveru MSMQ.

MSMQ lze provozovat na těchto operačních systémech: Windows 2000, Windows XP, Windows NT s Option Pack 4 a vyšší, Windows 98 a Windows 95. Windows 2000 a vyšší mají podporu MSMQ přímo integrovanou (jako volitelný prvek při instalaci), do ostatních systémů musí být doinstalována [13].

4.21.2 Queued components

Funkce queued components je novým prvkem technologie COM+. Je to vlastně spojení standardu COM s možnostmi, které přináší MSMQ. Pomocí ní může aplikace posílat požadavky i na server, který je dočasně nedostupný. V takovém případě se požadavek uloží do fronty MSMQ a je doručen až v okamžiku obnovení spojení nebo spuštění serveru [4]. Požadavky se ukládají v podobě zpráv, ve kterých jsou určitým způsobem zakódovány parametry volaných metod.

Z uvedeného popisu vyplývají zřejmá omezení. Abychom mohli queued components využít musíme mít na všech stanicích MSMQ nainstalováno. Je to tedy první funkce COM+, o které se zmiňuji, jejíž použití nevyžaduje podporu a úpravy pouze na straně serveru, ale i na straně klienta. Z toho vyplývají i odpovídající požadavky na operační systém, na kterém bude klientská aplikace provozována.

Druhý limit je dán tím, že klient nečeká na odpověď ze serveru (ten nemusí běžet). Proto volané funkce nemohou vracet žádné hodnoty. Toto omezení blíže specifikuji později. Stojí za zmínku, že při použití queued components probíhá komunikace od klienta k serveru prostřednictvím front (MSMQ) i v případě, kdy by bylo možné komunikovat přímo (spojení je možné okamžitě).

Se znalostí vlastností queued components již mohu nastínit návrh zjednodušeného řešení problému ztráty spojení se serverem. Problém jsem vyřešil vložením další vrstvy mezi klienta a server. Mezivrstva zajišťuje klientovi všechny funkce serveru, má tedy stejné rozhraní jako server. Měla by pokud možno běžet na stejné stanici jako klient, nebo alespoň na stanici, která je v síti umístněna tak, aby bylo riziko výpadku spojení zanedbatelné. Druhou funkcí mezivrstvy je propagovat veškeré požadavky od klienta směrem na centrální server. A právě k propagaci využiji queued components. Centrálnímu serveru přidám rozhraní, prostřednictvím kterého budu předávat změny provedené lokálně. Je snadno představitelné, že mezivrstva může být implementována stejným COM objektem. Odlišnost chování v podobě propagace požadavku do vyšší vrstvy může být aktivována například pomocí parametru objektu (viz kapitola 4.19).

Ještě poznamenám, že návrh neřeší aktualizaci směrem od serveru na klienta. Může k ní docházet opět s použitím queued components. Vzhledem k typu aktualizovaných dat (seznam platných karet...) k ní nemusí docházet průběžně. Efektivnějším způsobem by tedy zřejmě byla například denní replikace centrální databáze v nočních hodinách.

Obrázek 17. Návrh řešení výpadku spojení pomocí queued components (události jsou z důvodu přehlednosti v diagramu vynechané)
Obrázek 17. Návrh řešení výpadku spojení pomocí queued components (události jsou z důvodu přehlednosti v diagramu vynechané) (5 kB)

4.21.3 Implementace rozhraní volaného přes frontu

Vlastní vytvoření rozhraní, které bude voláno pomocí MSMQ (frontou) není obtížné. Podstatné je splnit podmínky, které pro takové rozhraní COM+ definuje. Jak jsem již zmínil, žádná metoda rozhraní nesmí mít výstupní parametr ani návratovou hodnotu. Protože ze tří rozhraní, která implementuje objekt ESA, jen rozhraní IESAKLICEK obsahuje metody, které provádějí nějaké změny v databázi, bude to právě toto rozhraní jehož duplikát pro použití s technologií queued components vytvoříme. Do rozhraní nebudeme přidávat metody, které slouží jen "ke čtení", zůstanou tedy jen metody KLICEKVYPUJCENI a KLICEKVRACENI. Nové rozhraní pojmenujeme IESAKLICEKOFFLINE.

Ještě dodám, že podmínky pro přístup přes MSMQ musí splňovat jen metody určitého rozhraní, ne všechny metody objektu. Některá rozhraní jednoho objektu můžeme tedy volat klasickým způsobem, jiná pomocí queued components.

Ukázka 35. Rozhraní IESAKLICEKOFFLINE volané přes queued components
...
interface IEsaKlicekOffline: IDispatch
{
  [id(0x00000001)]
  HRESULT _stdcall KlicekVypujceni([in] BSTR Sada,
    [in] BSTR Osoba, [in] long PozadovanyKlicek);
  [id(0x00000002)]
  HRESULT _stdcall KlicekVraceni([in] BSTR Sada, [in] BSTR Osoba);
};

Obě metody již splňují podmínku pouze vstupních parametrů. Můžeme tedy pro ně využít již existující implementaci z rozhraní IESAKLICEK, resp. Delphi to za nás udělají sami. Dále musíme nové rozhraní označit pro přístup přes queued components. To lze provést dvěma způsoby: buď přímo v IDL komponenty, přidáním identifikátoru (makra) QUEUABLE do vlastností rozhraní nebo pomocí nástroje Služba Component Services. První volbu nelze v Delphi 5 použít, protože tuto vlastnost neznají a nedovolí nám knihovnu typů uložit. Je však místo něj možné použít atribut CUSTOM s příslušnými parametry (resp. řetězec, na který je makro QUEUABLE expandované). Parametry lze najít v hlavičkových souborech Win API.

Ukázka 36. Atribut QUEUABLE v definici rozhraní
[
  uuid(F6C5896F-6AFB-48FD-9BFF-24F52B7DDEAC),
  helpstring("Interface Klicek pro server Esa pro pristup pres MSMQ"),
  version(1.0), dual, oleautomation, QUEUEABLE
]
interface IEsaKlicekOffline: IDispatch { ... };

Pro nastavení použijeme druhý způsob: nástroj Služba Component Services. V seznamu komponent COM+ nalezneme objekt ESA a jeho rozhraní IESAKLICEKOFFLINE. V jeho okně Vlastnosti je na záložce Fronty volba Zařazeno do fronty. Jejím zatržením aktivujeme volání před MSMQ. Volba není přístupná, pokud rozhraní nesplňuje vyžadované podmínky.

Obrázek 18. Aktivace volání přes MSMQ v nástroji Služba Component Services
Obrázek 18. Aktivace volání přes MSMQ v nástroji Služba Component Services (5 kB)

Posledním krokem je úprava implementace metod KLICEKVYPUJCENI a KLICEKVRACENI tak, aby při jejich provádění v objektu v mezivrstvě byla (mimo úprav na lokální kopii databáze) změna propagována centrálnímu objektu. Volání centrálního serveru se provede stejně jako každé jiné volání COM objektu.

V čem se použití objektu liší je získání první reference na rozhraní. Pokud bychom použili normální postup, tedy volání metod CREATE resp. CREATEREMOTE (tedy API funkce COCREATEINSTANACE resp. COCREATEINSTANACEEX), choval by se objekt obvyklým způsobem. Byl by volán přímo, nikoli prostřednictvím fronty. Pro získání reference musíme použít funkci COGETOBJECT a specifikovat objekt udáním jména fronty, prostřednictvím které má být volán. K identifikaci fronty slouží její přezdívka (moniker) ve formátu "QUEUE:/NEW:OBJEKT", kde OBJEKT je buď CLSID nebo ProgID.

Ukázka 37. Získání reference na objekt identifikovaný přezdívkou (moniker)
var
  Moniker: PWideString;
  EsaKlicekOffline: IEsaKlicekOffline;
begin
  Moniker := 'Queue:/new:EsaServer.Esa';
  OleCheck(CoGetObject(Moniker, nil, IEsaKlicekOffline, EsaKlicekOffline));
  ...
end;

Po zkompilování již můžeme klientskou aplikaci spustit (aniž bychom spouštěli server). Pomocí nástroje Správa počítače, který nalezneme v Ovládacích panelech Windows 2000/XP, lze sledovat frontu požadavků. Ta se vyprázdní až po spuštění COM+ aplikace Esa, tím jak budou zprávy zpět převáděny na požadavky a zpracovávány.

Obrázek 19. Fronta MSMQ, obsahující volání metod objektu ESA
Obrázek 19. Fronta MSMQ, obsahující volání metod objektu ESA (17 kB)

5. Zátěžové testy

Nyní již máme vyjasněné řešení a implementaci všech požadavků na architekturu systému. Dalším logickým krokem je zjištění, jak se bude aplikace (resp. architektura) chovat v běžném provozu a hlavně v nejrůznějších extrémních situacích. Tedy zvláště za velkého zatížení, ať už z hlediska počtu požadavků, množství přenášených dat, počtu připojených klientů a podobně.

V této kapitole se budu věnovat uvedeným charakteristikám. Při testování výkonu architektury COM nepoužiji vytvořenou aplikaci (objekt ESA). Důvodem je fakt, že jeho metody volají databázové procedury. Tím by statistiky byly ovlivněny rychlostí databáze a neodrážely by pouze vliv architektury. Proto jsem si pro účely výkonnostních testů nadefinoval vlastní rozhraní a objekt.

Objektů je ve skutečnosti víc, protože chci také porovnávat výkon objektů při různých konfiguracích a použitých technologiích (dynamické volání, COM+, queued components...). Právě rozhraní objektů je jednotícím prvkem, který zajišťuje objektivitu srovnání. Rozhraní obsahuje dvě metody, z nichž každá je určena pro jiný typ testu. Jejich implementace jsou prázdné, metody tedy nic neprovádí.

Ukázka 38. Rozhraní objektu pro zátěžové testy
interface IZatez: IDispatch
{
  [id(0x00000001)]
  HRESULT _stdcall Jednoducha( void );
  [id(0x00000002)]
  HRESULT _stdcall Pole([in] VARIANT Pole );
};

5.1 Metodika

Všechny hodnoty v následujících přehledech, které ukazují dobu trvání určité operace, jsou průměrem nejméně tří nezávislých testů. Samotná doba trvání nemá žádnou vypovídací hodnotu, protože se bude samozřejmě na různých stanicích lišit. Podstatné je porovnání hodnot pro stejnou operaci pro různé technologie nebo jejich funkční vztah k jiným veličinám. Pokud není uvedeno jinak, jsou všechny časové údaje udány v mikrosekundách (us).

Testy jsem prováděl na třech různých stanicích (konfigurace viz Tabulka 10).

Tabulka 10. Konfigurace stanic, na kterých byly prováděny zátěžové testy
Stanice Operační systém Procesor Paměť Síť
A Windows XP AMD Athlon 1000 Mhz 512 MB, 133 Mhz 100 MB, full duplex. Obě stanice byly připojeny na společný switch CISCO
B Windows 2000 AMD Duron 800 Mhz 240 MB, 100 Mhz
C Windows 2000 AMD Athlon 1400 Mhz 340 MB, 133 Mhz Stanice není připojena do sítě

5.2 Rychlost vyvolání metody při použití různých technologií

V této části uvedu výsledky testu spočívajícího ve sledování doby vyvolání jednoduché metody při použití různých technologií. Těmi jsou standardní COM (resp. DCOM), COM+ a queued components. Test jsem prováděl ve dvou různých konfiguracích. První bylo volání lokálního objektu na stanici číslo C. Druhá konfigurace spočívala ve volání vzdáleného objektu ze stanice A na stanici B.

Abych zpřesnil výsledky měření volal jsem metodu opakovaně mnohokrát za sebou (1000 až 100 000 ) a vypočítal průměr na jedno volání. Porovnával jsem také průměrné hodnoty při různě dlouhých dávkách volání. Podle mého předpokladu by hodnoty měly být hodnoty vždy stejné. Tento předpoklad se nepotvrdil pouze při použití queued components, kde se doba volání významně prodlužovala s délkou dávky.

Tabulka 11. Doba volání metody při použití různých technologií (v mikrosekundách)
Délka dávky Stanice A (lokální objekt) Stanice B a C (vzdálený objekt)
COM COM+ Queued components COM COM+ Queued components
1000 54 72 45 478 592 91
5000 54 76 48 480 590 92
10000 54 76 50 487 590 105
50000 54 76 83 486 601 170
100000 54 76 126 480 601 260

Zároveň je vidět, že volání metody při použití COM+ je vždy asi o 40% (lokální volání) resp. o 25% (vzdálené volání) pomalejší. Nejrychlejší je vždy technologie queued components. Zde je třeba poznamenat, že doba volání zahrnuje pouze zařazení požadavku do fronty, nikoli vlastní vyvolání metody.

5.3 Doba potřebná k získání reference na objekt (rozhraní)

Z hlediska aplikace "Klíčky" méně důležitým, ale přesto zajímavým údajem je rychlost získání reference na objekt (resp. jeho rozhraní), včetně vytvoření (a zrušení) jeho instance. Při tomto testu jsem použil obdobný postup jako v předchozím. Omezil jsem se však pouze na lokální objekt.

Při testování jsem znovu použil různé délky dávky (100 až 100 000), ke kterým jsem spočítal průměr na jedno získání reference. Na rozdíl od předchozího testu se, ale v žádném případě neprojevila statisticky významná závislost potřebné doby na délce dávky. Proto v tabulce uvádím jenom jednu hodnotu.

Tabulka 12. Doba potřebná pro získání reference při použití různých technologií (v mikrosekundách)
COM COM+ Queued components
2 661 6 581 428

Z tabulky je opět vidět, že COM+ je pomalejší než COM. Tentokrát je rozdíl dokonce daleko zřetelnější, více jak dvojnásobný. Queued components je opět nejrychlejší, protože se ve skutečnosti nevytváří instance objektu, ale pouze reference na lokální frontu MSMQ.

5.4 Srovnání s dynamickým voláním

Nyní srovnám dynamické volání metod a získání reference na objekt (bez znalosti rozhraní) s klasickým postup (se znalostí rozhraní). Dá se očekávat že dynamické volání bude pomalejší, zajímá mě tedy hlavně jak moc velký bude rozdíl.

Ke srovnání použiji obyčejný COM objekt, hodnoty ve sloupci "standardní volání" tedy odpovídají hodnotám pro COM objekt z předchozích dvou testů. Stejně jako u nich jsou údaje pro lokální volání měřeny na stanici C. Vzdálené volání bylo měřeno na stanicích A a B.

Tabulka 13. Srovnání s dynamickým voláním (v mikrosekundách)
Operace Standardní volání Dynamické volání Poměr
Získání reference 2 661 1 893 71 %
Lokální volání metody 54 158 292 %
Vzdálené volání metody 478 1 352 282 %

Jak je vidět získání reference je překvapivě rychlejší při použití dynamického volání. Naopak doba potřebná k provedení metody je pro tento přístup podle předpokladu delší (a to podstatně: téměř třikrát).

5.5 Předávání velkého množství dat

V této části popíši výsledky testu práce s metodou POLE, která se podobá metodě KLICEKSEZNAM objektu ESA. Podobnost spočívá v parametru typu VARIANT (OLEVARIANT), pomocí kterého předávám pole hodnot (viz kapitola 4.10). Měření byla provedena na stanici A.

Zkoumal jsem závislost času potřebného k vyvolání metody na velikosti předávaného pole. Test jsem opět prováděl v dávkách o různé délce (1000 až 100 000 volání). Velikosti pole jsem měnil ve stejném rozsahu, elementem pole bylo číslo typu INTEGER (resp. VARIANT s podtypem INTEGER). K testování jsem použil obyčejný COM objekt. Z předchozích testů již vím, že se u něj nemění doba potřebná k zavolání metody podle délky dávky, jako tomu bylo u queued components. To se potvrdilo i tohoto testu. V tabulce je zobrazena doba jednoho volání dělená počtem prvků v poli. Hodnoty jsou v nanosekundách (ns).

Tabulka 14. Průměrná doba potřebná k přenesení jednoho prvku pole (v nanosekundách)
  Počet volání
Velikost pole 1000 5000 10 000 50 000 100 000
1000 302 326 323 313 318
5000 162 146 147 143 143
10 000 124 118 117 117 110
50 000 116 112 112 109 109
100 000 108 106 105 105 106
Obrázek 20. Průměrná doba potřebná k přenesení jednoho prvku pole (v ns)
Obrázek 20. Průměrná doba potřebná k přenesení jednoho prvku pole (v ns) (5 kB)

Je vidět, že hypotetická doba nutná k přenesení jednoho prvku pole klesá s velikostí pole. To je zcela zřejmý vztah, protože samotné vyvolání metody bez ohledu na velikost parametrů trvá nějakou dobu. Při průměrování se tato doba rozpočítává na jednotlivé prvky.

Důvodem proč jsem toto měření prováděl bylo zjistit, zda neexistuje určitá hranice množství přenášených dat, při které se začne volání metody vzhledem k velikost parametru naopak zvětšovat. To by mohlo být způsobeno určitými vnitřními omezeními architektury COM. Jak je vidět ani při dávce 100 000 volání s polem o stejném počtu prvků se žádné takové omezení neprojevilo. Velikost přenesených dat při jednom takovém volání je téměř 400 kB. Doba trvání testu s těmito parametry byla přes 17 minut, tedy asi 10 ms na jedno volání. Přitom uvedené rozsahy dat vysoce převyšují nároky aplikace "Klíčky".

5.6 Současná obsluha více klientů

K podobnému závěru jako u přenášení velkého množství dat jsem došel při testu současné obsluhy více klientů. Jak je vidět z tabulky i grafu, i při velikém množství klientů (až 200), roste doba potřebná k vykonání metody lineárně s počtem souběžných volání.

Hodnoty v tabulce odpovídají testu s obyčejným COM objektem. Měření jsem prováděl pomocí speciálně vytvořeného programu. Ten postupně zvyšoval počet souběžných vláken, z nichž každé volalo v cyklu metodu JEDNODUCHA testovacího objektu. Pro každý počet vláken proběhla v jednom z nich tři měření délky vykonání dávky 300 volání této metody. V tabulce je zobrazena průměrná doba jednoho volání pro vybrané počty klientů (není zahrnuto všech 200 pozorování). Měření byla prováděna na stanici C.

Tabulka 15. Doba potřebná k vyvolání metody při současném běhu více klientů (v mikrosekundách)
Počet klientů Doba volání Počet klientů Doba volání Počet klientů Doba volání
1 54 70 4 465 140 9 349
10 457 80 5 190 150 10 521
20 1 034 90 5 916 160 10 719
30 1 719 100 6 604 170 11 595
40 2 327 110 7 318 180 12 267
50 3 079 120 7 976 190 13 143
60 3 767 130 8 635 200 13 720
Obrázek 21. Doba potřebná k vyvolání metody při současném běhu více klientů (v ms)
Obrázek 21. Doba potřebná k vyvolání metody při současném běhu více klientů (v ms) (5 kB)

Obdobný test jsem provedl i při jiných konfiguracích, například s COM+ objektem. Dále jsem zkoušel při měření použít i jiné metody. Byly to buď metody s určitými parametry nebo takové, které, na rozdíl od metody JEDNODUCHA, vykonávaly nějakou činnost. Ve všech případech jsem pozoroval stejnou lineární závislost, jaká je zobrazena v tabulce a grafu.

6. Závěr

6.1 Zhodnocení aplikace vzhledem k požadavkům

V celém textu této práce jsem postupně vyvíjel aplikaci, na které jsem demonstroval řešení nejrůznějších problémů pomocí architektury COM a jejích rozšíření, včetně dalších technologií (MSMQ). Problémy byly vybírány tak, aby korespondovaly s požadavky, které jsem stanovil v úvodu práce. Tedy ty jejichž vyřešení je potřeba k úspěšnému vývoji informačního systému ESA (a jeho subsystému "Klíčky").

Nyní zhodnotím, jak moc úspěšně se mi podařilo pomocí architektury COM uvedené problémy vyřešit. V případech, kdy jsem řešení nenalezl, se pokusím zhodnotit, jestli existuje jiný způsob, jak tuto potíž obejít bez pomoci architektury COM. Při takovém postupu je zároveň nutné dbát, aby nebyly porušeny principy, které musí aplikace, tuto architekturu využívající, splňovat.

6.1.1 Rychlost odezvy

První požadavek na dostatečnou rychlost odezvy byl shodou okolností sledován až jako poslední v samostatné kapitole 5. Podařilo se mi prokázat, že rychlost odpovědi na požadavek je dostatečná. Ani při zatížení, zřejmě přesahující mez, které bude v běžném provozu dosaženo, se neprojevil pokles výkonu. Protože měření byla provedena na stanicích, které svou konfigurací zdaleka nedosahují současné technologické špičky, lze výsledek považovat za více než dostačující.

Jak jsem již zmínil v úvodu, je nutné mít na paměti, že rychlost serveru závisí ve velké míře hlavně na vlastní implementaci. V práci jsem několikrát upozornil na vhodné postupy, které směřují k lepšímu výkonu.

Třetí faktor, který významně ovlivní provoz systému je databázová vrstva. Optimalizace databázového serveru není tématem této práce. Přesto jsem v kapitole 4.20 ukázal, jak lze využít SPM (Shared Property Manager) jako mezipaměti právě pro přístup do databáze. Velmi doporučuji jeho využití, které je z hlediska implementace velmi jednoduché.

6.1.2 Obsluha více klientů

V předchozí kapitole jsem také testoval výkon serveru při současném dotazování od více klientů. Lze říci, že i tentokrát COM obstál. I při souběžném přístupu k 200 instancí objektu se aplikace chovala dostačujícím způsobem.

6.1.3 Více-vláknové (multithreading) aplikace

Tento problém úzce souvisí s předešlým. Právě povolením volání objektu z více vláken lze razantně zvýšit výkon serveru při obsluze většího počtu klientů. Je třeba poznamenat, že rychlost odezvy serveru za těchto okolností velmi záleží na zvoleném vláknovém modelu.

Při počátečním určování parametrů v kapitole 4.2 jsem zvolil model apartment, který se ukázal jako dostačující. Ještě lepšího výkonu by COM server měl dosahovat při použití modelu free. Jeho implementace je ovšem složitější.

Díky integrované vláknové podpoře v architektuře COM, které je ještě vylepšena v COM+, není implementace více-vláknového serveru obtížná. Je však třeba důsledně dbát na dodržení stanovených pravidel.

V souvislosti s více-vláknovými aplikacemi jsem zmínil problém současného přístupu ke sdíleným datům z více vláken. Přestože tato problematika nesouvisí přímo s architekturou COM, uvedl jsem na příkladu společného uživatelského rozhraní instancí objektu ESA řešení pomocí kritické sekce. Naleznete ho v kapitole 4.7.

6.1.4 Řízení zdrojů

Pro řízení zdrojů nám COM+ dává k dispozici mocný nástroj v podobě sdružování objektů. Jak jsem napsal v kapitole 4.18.1, lze tuto funkci použít i k jiným účelům. Tato další možná využití jsem však nepoužil.

Aby bylo možné sdružování objektů použít, bylo nutné splnit několik podmínek v podobě přizpůsobení implementace objektu ESA. Žádná z těchto úprav objekt v ničem neomezila. Bylo také nutné implementaci objektu přesunout do COM+ aplikace v podobě DLL knihovny. Díky tomu můžeme dále využít několik dalších nových funkcí COM+ (například již zmíněný SPM).

Lze tedy konstatovat, že řízení zdrojů je pomocí COM+ velmi dobře zajistitelné.

6.1.5 Výpadek spojení

Řešení problému nespolehlivého spojení se serverem jsem se pokusil ukázat v kapitole 4.21. Vyšel jsem z nové funkce COM+, kterou jsou queued components. Ukázalo, se že tato funkce může vyřešit jen část problému. To je mimo jiné dané tím, že aplikace "Klíčky" není přesně tím typem úlohy, pro jejíž řešení byly queued components navrženy.

Je tedy nutné ostatní části problému vyřešit jinými prostředky. Mezi zmíněné problémy patří zejména synchronizace dat směrem od serveru na klienta. Nevyřešenou otázkou je také, jak se má systém zachovat, když server při synchronizaci během zpracování fronty odmítne vykonat jeden z příkazů a bude to takový příkaz, na jehož předpokládaném úspěšném dokončení jsou závislé další příkazy ve frontě.

Jinak řečeno problém výpadku spojení je prvním požadavkem, který se mi nepodařilo pomocí architektury COM úspěšně vyřešit. Velmi stručný návrh alternativních řešení je na konci uvedené kapitoly. Myslím že je možné, ačkoli to nebude jednoduché, nalézt vhodný postup, tak aby zároveň nebyl v rozporu s požadavky na COM+ objekt.

6.1.6 Přenos velkého množství dat

Tomuto tématu jsem se věnovat ve dvou částech. Vhodnou formu přenosu většího množství dat jsem navrhl v kapitole 4.10. Upozornil jsem v ní, že z hlediska efektivnosti přenosu je lepší přenášet data v podobě většího datového bloku (v uvedeném příkladu parametrem typu VARIANT podtypu pole).

kapitole 5 věnované zátěžovým testům jsem měřil vliv velikosti parametrů metody na dobu potřebnou k vyvolání metody. Zjistil jsem, že při dané konfiguraci serverové stanice (je uvedena ve zmíněné kapitole) neexistuje rozumně velká hranice, při které by výkon serveru začal výrazně klesat z důvodu určitých skrytých omezení architektury COM.

6.1.7 Bezpečnost

Pro zajištění bezpečnosti nabízí COM velkou škálu možností a nastavení, a to jak z hlediska bezpečnosti přenosu dat, tak za účelem autorizace klienta. Bezpečnostní nastavení dále rozšiřuje COM+ o možnost nastavování práv až na úroveň jednotlivých metod.

Nastavování bezpečnostních parametrů jsem popsal v kapitole 4.15. Lze je nastavovat buď pomocí k tomu určených nástrojů (OleView, DCOMCnfg, Služba Component Services) nebo programově přímo v kódu aplikace.

Bohužel jsem zjistil, že všechny tyto možnosti jsou závislé na integrovaném bezpečnostním systému OS Windows. Při zavádění IS ESA v prostředí VŠE bychom díky tomu narazili na dva problémy. Aby bylo možné se autorizovat ke vzdálenému objektu, musejí obě stanice sdílet uživatelské účty. To lze zajistit jen pomocí sítě Microsoft Network a zahrnutím obou stanic do jedné domény nebo pracovní skupiny. Ani jeden z těchto prvků se v síti VŠE nepoužívá, protože základním prostředím je síť Novel Netware. Druhým problémem je mnoho stanic s operačním systémem Windows 95/98. V těchto systémech jsou bezpečnostní prvky velmi omezené a tomu odpovídají i omezené možnosti konfigurace bezpečnosti COM objektů.

Jako alternativní řešení jsem uvažoval o autorizaci na základě adresy stanice, na které je spuštěna klientská aplikace. Toto řešení se také ukázalo neřešitelným, díky principu transparentnosti umístnění (location transparency), který popisuji v kapitole 4.15.8.

Je tedy zřejmé, že zajištění autorizace bude nutné zajistit prostředky mimo COM. Některé možné postupy jsem již popsal ve zmíněné kapitole. Osobně považuji nemožnost zajištění bezpečnosti bez použití síťových technologií firmy Microsoft za největší nedostatek, na který jsem v této práci narazil.

6.1.8 Volání klienta (události)

Implementace volání událostí v architektuře COM je velmi jednoduché. Existují standardně definovaná rozhraní, která práci dále usnadňují. Práci s nimi jsem věnoval kapitolu 4.11.

Problematické mohou být různé chybové situace. Například "zaseknutí" klienta při vykonávání události, které může způsobit zastavení serveru. Další možnou komplikací je neočekávané ukončení klientské aplikace, bez toho, aby se odhlásila z odběru události.

6.2 Shrnutí

V úvodu práce jsem si určil dva cíle:

Oba uvedené cíle jsem průběžně plnil v celém textu práce. V předešlé části jsem na několika málo stránkách detailněji shrnul řešení požadavků, které jsem v úvodu stanovil. Došel jsem k závěru, že COM umožňuje úspěšné řešení jejich převážné většiny. V některých případech dokonce nabízí mocné prostředky určené přímo pro daný problém. Přesto jsem narazil na několik překážek. Jedna z nich vyplývá ze specifického prostředí školní počítačové sítě. Jedná se o potřebu zajištění bezpečnosti a zvláště autentizace v síti Novell Netware. Problém vyplývá z přílišné vázanosti architektury COM na další produkty společnosti Microsoft.

Dále jsem nenašel dostatečně uspokojivé zajištění provozu a zotavení systému v případě výpadku spojení s aplikační serverem. Bylo by myslím zajímavé použít tuto problematiku jako téma pro samostatnou práci.

Vzhledem k druhému cíli jsem implementaci vybrané ukázkové aplikace "Klíčky" rozdělil na několik kroků. V každém z nich jsem se vždy mimo vyřešení jednoho problému pokusil použít novou funkci architektury COM nebo technologii na ni navazující. Tím jsem, jak doufám, pokryl značnou část možností, které COM vývojáři nabízí. V zahrnutých příkladech kódu jsem uvedl ukázky jejich implementace v prostředí Borland Delphi 5. Snažil jsem se, aby jejich forma umožňovala obecnější využití a nebyl problém je převést do jiného programovacího jazyka.

Upřímně doufám, že práce poslouží dalším programátorům, kteří budou chtít proniknout hlouběji do architektury COM, plně využít jejích možností a tím si ušetřit práci.

7. Literatura

[1]
Ly, Bin: COM Library. 1999 - 2002.
http://www.techvanguards.com/com/
[2]
Socha, Curtis: DCOM Technology. 8.5.2001.
http://www.absoluteresearch.com/dcom/dcom.Htm
[3]
Microsoft Developer's Network.
http://msdn.microsoft.com/
[4]
Raj, Gopalan Suresh: A Component Engineering Cornucopia. 1997 - 2002.
http://my.execpc.com/~gopalan/
[5]
Borland Corporation - Delphi 5, Developers Guide. 1999.
[6]
Borland Corporation - Delphi 5, Object Pascal Recerence. 1999.
[7]
Borland Corporation - Delphi 5, Developing COM-based Applications. Online manual. 1999.
[8]
Zelený, Jindřich: COM/DCOM a CORBA v Delphi 4. Diplomová práce. KIT VŠE. 1999
[9]
Lapierre, Martin: COM+ Basics - Creating your first COM+ Application. 1.5.2000.
http://www.idevresource.com/com/library/articles/com+firstapp.asp
[10]
Talkar, Jeremiah: COM+ Object Pooling. 23.3.2000.
http://www.idevresource.com/com/library/articles/com+objpool.asp
[11]
Verhoeven, Jan: Creating ASP com objects with Delphi 5. 24.4.2000.
http://www.jansfreeware.com/articles/delphiasp.html
[12]
Balaji, Ramesh: Using the COM+ Shared Property Manager.
http://www.4guysfromrolla.com/webtech/091000-1.shtml
[13]
Rao, Santosh: The Microsoft Message Queue. 16.1.2002.
http://www.thecodeproject.com/w2k/msmq.asp
[14]
Borland Developer Network.
http://community.borland.com/
[15]
Sells, Chris: Exposing Multiple Interfaces to Scripting Clients. 2002.
http://www.sellsbrothers.com/tools/multidisp/
[16]
Powers, Shelly: Developing ASP Components. 2. vydání. O'Reilly. 2001. 832 p. ISBN 1 56592 750 8
[17]
Stejskal, Jiří: Standardizace objektově orientovaných komponent. Diplomová práce. KIT VŠE. 2001
[18]
Brablc, Ondřej: Identifikační karty - kontrola oprávněnosti přístupu. Diplomová práce. KIT VŠE. 1998
[19]
Box, Don: Q&A ActiveX/COM. Microsoft Systems Journal. January 1998.
[20]
Diskusní skupina
news://borland.public.delphi.oleautomation
[21]
Diskusní skupina
news://microsoft.public.activex