Mendelova univerzita v Brně Provozně ekonomická fakulta
Připojení externího modemu k J2EE aplikaci Diplomová práce
Vedoucí práce: Ing. Jan Kryštof, Ph.D. Bc. Pavel Palát
Brno 2012
Prohlášení Prohlašuji, že jsem tuto diplomovou práci vypracoval samostatně pod vedením Ing. Jana Kryštofa, Ph.D. Uvedl jsem všechny literární prameny a publikace, ze kterých jsem čerpal. V Brně dne 20. dubna 2012
___________________
Abstract Integrating Java EE systems with hardware modem (Motorola G24) – analysis of modem control requirements, different methods for connecting external systems to Java EE platform with focus on Java Connector Architecture (JCA). Keywords Java Connector Architecture, Motorola G24, Java EE, Glassfish, Java EE integration
Abstrakt Integrace Java EE systému s hardwarovým modeme Motorola G24 – analýza požadavků na řízení, různých metod integrace v Java EE platformě s důrazem na Java Connector Architecture. Klíčová slova Java Connector Architecture, Motorola G24, Java EE, Glassfish, Java EE integrace
Obsah 1 Úvod................................................................................................................................................11 2 Analýza současného stavu...............................................................................................................12 2.1 Prostředí Java EE ....................................................................................................................12 2.1.1 Aplikační server...............................................................................................................12 2.1.2 Architektura aplikace v J2EE...........................................................................................14 2.2 Modem Motorola G24.............................................................................................................14 2.2.1 Propojení modulu Motorola G24 s PC.............................................................................16 2.2.2 Komunikace s modulem G24...........................................................................................17 2.2.3 Požadavky na řízení ........................................................................................................19 2.2.4 Způsob řízení....................................................................................................................21 2.2.5 Protokol pro komunikaci s G24.......................................................................................22 2.2.6 Rozbor AT příkazů pro řízení...........................................................................................23 2.2.6.1 Práce se SMS............................................................................................................23 2.2.6.2 Práce s odchozími hlasovými hovory.......................................................................25 2.2.7 Řízení G24 z prostředí Java.............................................................................................27 2.3 Platforma Java EE a integrace cizích komponent....................................................................29 2.3.1 Bussiness vrstva a její charakteristika..............................................................................30 3 Návrh řešení.....................................................................................................................................33 3.1 Propojení bussiness vrstvy s cizími prostředky.......................................................................33 3.1.1 Síťový server s pomocí vlastního protokolu ...................................................................34 3.1.2 RMI server – řídící modul................................................................................................36 3.1.3 Oddělené servery se společným datovým úložištěm.......................................................37 3.1.4 Aplikační klient jako externí server.................................................................................37 3.1.5 Java Connector Architecture (JCA).................................................................................39 3.1.6 Zhodnocení a výběr optimálního propojení.....................................................................39 3.2 Java Connector Architecture....................................................................................................41 4 Implementace..................................................................................................................................42 4.1 Postup vytvoření základního JCA connectoru.........................................................................42 4.1.1 Rozhraní pro JCA connector............................................................................................42 4.1.2 Outbound spojení ............................................................................................................43 4.1.3 Inbound spojení ...............................................................................................................49 4.1.4 Použití konektoru z klientské strany................................................................................52 4.1.5 Propojení outbound a inbound spojení.............................................................................53 4.2 Řídící modul GSM modemu....................................................................................................53
4.2.1 Abstrakce AT příkazů.......................................................................................................55 4.2.2 Odesílání SMS.................................................................................................................58 4.2.3 Práce s odchozím hovorem..............................................................................................59 4.2.4 Stavový automat...............................................................................................................60 4.2.5 Fronta požadavků.............................................................................................................62 4.2.6 Správa kontextu modemu a řešení chyb..........................................................................64 4.3 Propojení GSM ovladače a integrace do JCA konektoru........................................................66 4.3.1 Outbound spojení.............................................................................................................67 4.3.2 Inbound spojení................................................................................................................70 4.4 Nasazení a konfigurace konektoru...........................................................................................72 4.5 Implementace ukázkového klienta...........................................................................................72 5 Závěr................................................................................................................................................75 Citace..................................................................................................................................................76 Seznam obrázků..................................................................................................................................78 Seznam zkratek...................................................................................................................................79
1 Úvod Java EE je moderním prostředím pro vývoj enterprise aplikací – tzn. aplikací používaných většinou ve středních a větších firmách pro jejich informační systémy a řešení. Jedná se o sofistikovanou platformu, která nám umožňuje ušetřit spoustu času při vývoji, platforma je schopna spoustu věcí řešit za nás a dává nám efektivní nástroje pro tvorbu aplikací. Problematické je ovšem do prostředí Java EE, které spolu s výhodami přináší i určitá omezení (zejména jsou tato omezení nutná v distribuovaném prostředí), přidávat další komponenty a systémy stojící mimo uzavřené kontejnery J2EE. Samostatnou kapitolou je poté samozřejmě spolupráce s hardwarovými zařízeními, což je obecně něco, na co Java (natož J2EE) nebyla nikdy příliš stavěna. Ale i to je bohužel (či bohudík) občas potřeba, řešení takového problému ovšem není v J2EE triviální díky jejím omezením (viz Platforma Java EE a integrace cizích komponent). Moje práce se zabývá právě touto tématikou propojení dvou zcela odlišných světů – spravovaného prostředí J2EE kontejnerů a prostředí řízení hardwarového zařízení Motorola G24. Důvod je jednoduchý – Motorola G24 je hardwarový modem, který mimo jiné umožňuje posílat a přijímat SMS zprávy, což je v dnešní době velmi oblíbená forma notifikace, řízení či interakce s koncovým uživatelem. Cíl této práce bude dosažen v následujících krocích: •
Definovat nutný způsob řízení a omezení kladená prostředím J2EE
•
Prozkoumat možnosti, jak oba systémy propojit
•
Zvolit nejvhodnější variantu
•
Implementovat ukázkové řešení
•
Zhodnotit dosažené výsledky a vhodnost řešení po implementaci
11
2 Analýza současného stavu Nejprve musíme definovat běhové prostředí a identifikovat příslušné vlastnosti modemu pro stanovení optimálního způsobu integrace.
2.1 Prostředí Java EE Java EE [J2EETUT] je postavena na technologii Java, jedná se de facto o její nástavbu ve formě dalších knihoven a aplikačního serveru. Používat v ní můžeme samozřejmě všechny knihovny/funkcionality, které nabízí Java SE. Prostředí klade ovšem spoustu omezení na určité typy komponent a jejich operací – tato omezení jsou nutná z důvodu zajištění některé funkcionality (např. migrovatelnost komponent v clusterovém prostředí).
2.1.1
Aplikační server
Java EE aplikace jsou spouštěny v prostředí tzv. aplikačního serveru neboli kontejneru, který obsahuje několik důležitých součástí: •
•
•
Webový kontejner ◦
Slouží k běhu webových aplikací na daném aplikačním serveru
◦
Jedná se o webový server implementující protokol HTTP/HTTPS
EJB kontejner ◦
Obsahuje implementaci byznys vrstvy (tzn. vnitřní logiky aplikace)
◦
Stará se o přístup k databázi a interakci s ostatními systémy
Kontejner aplikačního klienta ◦
Není
přímo
součástí
aplikačního
serveru,
ale
jeho
infrastruktury
a doprovodných nástrojů a knihoven ◦
Zajišťuje možnost volání metod byznys vrstvy z prostředí aplikačního klienta
Aplikačních serverů existuje celá řada, komerčně i nekomerčně dostupných, např.:
12
•
Glassfish – produkt Sun Microsystems (v současné době divize společnosti Oracle). Glassfish je nabízený jako open source aplikační server bez podpory. Komerční zákazníci mohou využít i placené verze, která má svůj vlastní vydávací cyklus, který je podrobován dodatečnému testování, navíc je v této verzi k dispozici podpora – tento produkt Oracle nabízí jako Oracle Application Server [GLASSFISH]
•
RedHat Jboss [JB]
•
IBM Websphere[IWS]
•
BEA WebLogic[BEAWL]
•
Oracle Containers for Java EE[OC]
•
SAP NetWeaver[SNW] Java EE jako taková je pouze soubor funkčních specifikací pro aplikační server,
které musí každý server splnit, aby mohl být certifikován jako Java EE kompatibilní (v příslušné verzi specifikace). Nicméně ale ani fakt, že se jedná o certifikovaný aplikační server, nezajišťuje bohužel 100% přenositelnost aplikací mezi různými aplikačními servery. Pro moji práci byl vybrán aplikační server Glassfish zejména z důvodů: •
Open source produkt Glassfish je open source verzí komerčního Oracle Application Serveru, máme k dispozici kompletní zdrojový kód.
•
Je zdarma pro komerční i osobní použití
•
Většinou nejrychleji drží krok s vývojem technologie Java EE ◦
Často slouží jako referenční implementace pro technologie v Java EE (např. u Java Server Faces či u celé specifikace Java EE 6, která byla jako první implementována v aplikačním serveru Glassfish v3 právě pro potřeby ukázání referenční implementace)
Použita byla verze serveru Glassfish 3.1, jelikož aplikace bude postavena na posledním standardu Java EE 6. Aplikace jako taková a přístupy v ní obsažené jsou ovšem aplikovatelné na jakýkoliv jiný aplikační server – důležitá je podstata problému a jeho řešení – tudíž by řešení mělo být aplikovatelné i na jiné aplikační servery, které splňují požadavky dané J2EE 13
specifikací.
2.1.2
Architektura aplikace v J2EE
Klasická architektura Java EE aplikace obsahuje následující vrstvy [J2EETUT]: •
Prezentační vrstva Zodpovědná za komunikaci a interakci s uživatelem. Typickou prezentační vrstvou je např. webové rozhraní či aplikační klient. Podstatným rysem vrstvy je, že neprovádí komunikaci s databází ani jiným perzistentním úložištěm či externím systémem – tyto operace přenechává byznys vrstvě. Vrstva je provozována v příslušném kontejneru (např. webový kontejner aplikačního serveru v případě, že se jedná o webovou prezentační vrstvu)
•
Byznys vrstva Byznys vrstva neobsahuje žádné elementy uživatelského rozhraní, jedná se o vrstvu služeb, nejčastěji bezstavových. Tyto služby slouží k provádění všech elementárních operací aplikace, včetně práce s databází či jiným backendem. Byznys vrstva je typicky odpovědna za všechny logické operace a jejich ověření – např. ověření práv uživatele, ukládání a vyčítání dat jsou jedny z typických odpovědností této vrstvy. Vrstva běží v EJB kontejneru v aplikačním serveru.
•
Vrstva EIS (Enterprise Information System) Vrstva EIS je provozována mimo prostředí aplikačního serveru a jedná se o obecný termín popisující jakékoliv systémy mimo něj. Takovým systémem je typicky databáze se kterou pracuje byznys vrstva. Může se jednat ovšem o jakékoliv jiné systémy, např. zprávové sběrnice atd.
2.2 Modem Motorola G24 Modem Motorola G24 je průmyslovým zařízením určeným k integraci GSM funkcionality do produktu. Modul jako takový není určený na přímé připojení k PC (ani k žádnému jinému zařízení) – vyžaduje speciální patici, kterou musí být osazena deska plošného spoje – to potom tvoří samotné rozhraní k dalšímu hardware, který se o řízení modulu stará přes sériovou linku (je to dáno primárně tím, že modul Motorola G24 není 14
určený pro přímé použití s žádným hardwarem, ale pro integraci). Samotný modul existuje v několika provedeních [HW_G24], [MOT_G24_1], [MOT_G24_2],[MOT_G24_3]: •
Motorola G24 – EDGE
•
Motorola G24 – Java
•
Motorola G24 – Lite
Obrázek 1: Modul G24-Edge Samotné moduly jsou si velmi podobné co se týká dostupných funkcionalit. Modul EDGE je základním modulem, který se dříve prodával pouze pod označením Motorola G24, jedná se o původní plnohodnotný modul. Modul LITE je ochuzen o některé funkcionality původního modulu EDGE – jako je např. podpora datových přenosů EDGE (LITE nabízí pouze GPRS), chybějící je také např. podpora protokolu MUX (používaného pro simultánní připojení přes GPRS a současné řízení pomocí AT příkazů) nebo druhý UART (LITE poskytuje pouze jeden). Modul JAVA vychází z plnohodnotného modulu EDGE, navíc k němu přidává možnost spouštět Java midlety,
15
které mohou usnadnit tvorbu výsledné aplikace (ač tato možnost může vzhledem k tématu působit poměrně lákavě, jedná se o kompletně jinou funkcionalitu – význam má v případě implementace logiky v modulu a zjednodušení operací z řídícího modulu – což má význam skutečně jen ve velmi omezeném embedded systému). Pro účely práce jsem si vybral modul G24-EDGE, ale její závěry jsou aplikovatelné na všechny ostatní moduly G24 a jiná kompatibilní zařízení.
2.2.1
Propojení modulu Motorola G24 s PC
Modul G24 poskytuje následující rozhraní pro připojení: •
Dvě UART rozhraní (RS232)
•
USB rozhraní
Podle [MOT_AT] může být použito v jeden okamžik pouze jedno rozhraní, nikoliv oboje. U modulů, které nabízí dvě UART rozhraní (G24-EDGE a G24-JAVA), mohou být použity obě rozhraní současně, ale specifikace definuje určitá omezení a doporučené způsoby řízení pro režim s dvěma UART rozhraními. Nutno říci, že tato podpora je do značné míry redundantní – podobné funkcionality je možno dosáhnout použitím softwarového protokolu MUX, který definuje virtuální sériové kanály nad jedním fyzickým portem, což je obecnější (a lepší) řešení. Při použití USB rozhraní se G24 hlásí jako běžný modem, do většiny operačních systémů se nainstaluje ovladač, který ji zpřístupní jako za virtuální sériový port. Způsob připojení samotného rozhraní je poměrně jednoduchý:
16
Obrázek 2: Připojení modulu přes UART [MOT_G24_1]
Obrázek 3: USB připojení [MOT_G24_1]
I přes avizovanou jednoduchost tohoto připojení jsem nakonec zvolil ovšem variantu existujícího zařízení pro připojení modemu – primárním důvodem byla nedostupnost vhodné pájecí techniky a složité vytváření desky tištěného spoje. Jako jediný dostupný na našem trhu se ukázal kit od polské firmy Elproma, obsahující modul G24 zapouzdřený do krabičky s možností externího napájení, připojení k PC je realizováno přes USB a sériový port. Kit interně obsahuje právě modul G24-EDGE, cena sady byla při objednání cca 5500 Kč.
2.2.2
Komunikace s modulem G24
Komunikace není v základním režimu s modulem G24 příliš složitá – používá standardní formát komunikace pomocí „AT“ příkazů. AT příkazy tvoří standard pro podobná zařízení, jejich historický vznik se datuje do začátku osmdesátých let minulého století, kde se poprvé objevily jako „Hayes command set“ pro tehdejší moderní modem
17
Hayes Smartmodem, který umožňoval přenosy přes telefonní linku realizované až rychlostí 300 baud. Později se z těchto příkazů stal standard (ITU-T 250, ETSI GSM 07.07 – 3GPP TS 27.007) Modemy implementující AT příkazy pracují obecně v dvou režimech: •
Režim AT příkazů
•
Datový režim
Režim AT příkazů je definován jako stav (dle [AT]), ve kterém modem čeká na AT příkazy a zpracovává je. Naopak do datového režimu se dostaneme vytočením čísla protistrany s navázáním datového spojení. Na modernějších GSM modemech se do něj dostaneme též aktivací GPRS rozhraní (či jiného datového paketového módu). Přepnutí zpět do režimu AT příkazů z datového režimu je komplikovanější. Originální moduly Hayes zavedly přepínání mezi těmito režimy pomocí příkazu +++. Snadno se ale mohlo stát, že uživatelská data obsahovala takovouto sekvenci znaků (stačilo pouze přenést textový soubor, který je obsahoval – nebo přenést TCP paket, jež je obsahuje). Nežádoucí přepnutí samozřejmě způsobilo přerušení datových přenosů. Řešení bylo zvoleno poměrně jednoduché – pro úspěšné přepnutí bylo nutné, aby po sekvenci +++ následovala nějaká doba nečinnosti komunikace (u starých modemů Hayes to byla jedna vteřina, na Motorolách je to konfigurovatelné pomocí registru S12). Řešení je velice kostrbaté a problematické, pokud potřebujeme zároveň využívat GPRS či jiné datové přenosy a zároveň modem řídit pomocí AT příkazů. Typickým příkladem jsou např. GSM aplikace, které provádějí datové komunikace přes GPRS, ale zároveň využívají SMS/VOICE funkcionalitu, kterou modem nabízí. Motorola G24 nabízí dvě řešení tohoto problému řízení: •
Použití dvou UART vstupů – jeden se použije pro řízení, druhý se použije pro VOICE/SMS
•
Režim MUX První řešení je nevhodné z důvodu zvyšování počtu HW vodičů (a nedá se použít
u USB připojení). MUX režim je naopak řešením poměrně vhodným – definuje SW protokol, kde se otevře několik virtuálních kanálů (klasické uspořádání je např. řídící kanál, VOICE/SMS kanál, GPRS kanál), kde probíhá nezávislá komunikace. G24 poté automaticky vede v patrnost kontext každého kanálu, odesílá odpovědi a data na 18
správné kanály. Tím je možné velmi efektivně dosáhnout současného použití GPRS/dat i hlasové/SMS funkcionality. Další výhodou, kterou MUX nabízí, je podpora kontrolních součtů. Klasické AT příkazy nejsou vybaveny žádnou kontrolou dat ani CRC, na konzistenci spoléhají výlučně na linkovou vrstvu (což v případě RS232 a UART není též žádnou kontrolou vybaveno) a tudíž může dojít k jejich zkomolení. Zkomolení může v ideálním případě vyústit v ne-akceptaci příkazu (což je nejlepší varianta – příkaz poté pošleme znovu), v horším případě to může vést k provedení neúplného/špatného příkazu (třeba takové zkomolení v případě vytáčeného čísla, těla SMS atd. je poměrně dost nepříjemné). Rámce v protokolu MUX jsou vybaveny kontrolními součty a tudíž tímto problémem netrpí – v případě, že se přijme rámec s neplatným CRC kódem, tak se nepotvrdí a neakceptuje – tudíž je zaručeno, že data na virtuálních kanálech budou již neporušená. Na modulech Motorola G24 je MUX režim k dispozici pouze na edicích EDGE a JAVA, zjednodušená verze LITE jej nepodporuje. Popis protokolu MUX shrnuje [AT], část Appendix B: MUX. Pro naše potřeby jsou primárně důležité operace pro práci se SMS a demonstrace principu připojení, proto budeme využívat primárně základního režimu AT příkazů.
2.2.3
Požadavky na řízení
Cílem aplikace je demonstrovat základní funkcionalitu integrace modemu G24. Jako požadované operace byly stanoveny následující: •
Odeslání SMS
•
Příjem SMS
•
Vytočení telefonního čísla (prozvonění)
•
Notifikace o příchozím hovoru Jak lze tedy vidět, jedná se o operace vstupní (notifikované od G24 směrem
k nám) i výstupní (odesílané na G24 od nás). V reálném provozu by mezi další požadavky řízení přibyla automatická korekce chyb – G24 může často reportovat dočasné chyby (vlivem GSM sítě či modemu), či mohou vznikat další chyby při přenosu. Je otázkou diskuse, jak moc má být integrace vysokoúrovňová. Vysokoúrovňová implementace se bude snažit maximálně odstínit aplikaci od těchto chyb. Nízkoúrovňová implementace naopak přenechá ošetření těchto 19
chyb na aplikaci, která je za ně zodpovědná (komunikuje s potenciálně nespolehlivým zařízením využívající nespolehlivý komunikační kanál). Vznik chyb není možné nikdy vyloučit, proto by s nimi aplikace měla počítat v každém případě. Proto jsem se rozhodl pro nízkoúrovňovou integraci poskytující pouze základní operace nad modulem. Při reálném propojení s PC by ovšem musela být vyřešena ještě jedna záležitost řízení – možnost restartování G24. Bohužel se při ladění ukázalo, že je možné dostat G24 modul do nekonzistentního stavu, ze kterého se není modul schopen sám probrat. Reálná aplikace by tedy musela obsahovat: 1. Detekci správné činnosti G24 (což je netriviální a vyžadovalo by jistou formu funkčního testu v konektoru) 2. HW rozhraní pro resetování modulu G24 Použitý modul ELRPOMA bohužel neumožňuje bod 2 – tzn. provést hardwarový reset modulu. Softwarový reset se v takovýchto případech ukázal jako neúčinný. Tato možnost je tedy ponechána na budoucí rozšiřování, jelikož by se musela přesně na míru konkrétnímu hardware (který by se pro tyto účely musel navrhnout). Původně, vzhledem k možnosti napájení z USB, se mi jevilo jako řešení použít vypnutí tohoto portu, podle [LINSUSP] je toto možné provádět suspendování portu na nových verzích Linuxového jádra. Modul se ovšem nikdy nepodařilo z suspendovaného stavu probudit jinak než vytažením z USB portu a tudíž jsem tuto možnost zavrhl jako nespolehlivou. Dalším řešením bylo přímo vypínat napájení portu – moderní EHCI kontroléry podle [EHCI] mají možnost řídit napájení jednotlivých portů. Na Linuxu je možné tuto vlastnost zjistit příkazem lsusb -v, pokud jeho výstup u hubu bude obsahovat: •
Per-port power switching
•
Per-port overcurrent protection
•
Port indicators V takovém případě je možné řídit individuální napájení jednotlivých portů, což by
při napájení G24 z USB mělo způsobit její reset. Bohužel se mi ale na žádném z pro mne dostupných chipsetů (AMD 760G, AMD 960G, Intel C200) nepodařilo tuto podporu rozchodit – nejblíže tomu měl chipset od Intelu, který korektně reportuje „Per-port power switching“, ale po pokusu o změnu power stavu portu se port skutečně 20
na oko vypne (operační systém na něm nevidí zařízení), ale nedojde k odpojení napájení od proudu. Toto bylo otestováno i na zařízení s vysokým odběrem (mobilní telefon, který se nabíjí z USB), jelikož mou původní domněnkou bylo to, že G24 zůstane běžet ze zbytkového proudu (G24 modul má velmi malý odběr v klidu – kvůli použití v embedded zařízeních, tudíž může delší dobu být napájen pouze případnými kondenzátory na ELPROMa desce). Nicméně i mobilní telefon se bez problémů nabíjel mnoho minut, tudíž nezbývá než konstatovat, že podpora na desce není (což potvrdili i informace na fórech či u aplikace USB power control1 – kde je Intel chipset uveden mezi nepodporovanými). V reálném provozu by jako asi nejspolehlivější řešení bylo možné použít externě ovládaný spínač napájení pro G24 modem. Naštěstí takový hardware je běžně dostupný – u nás ho vyrábí např. firma Papouch2. Jejich modul nabízí připojení přes USB (funguje jako virtuální sériový port) či RS232 linku, pomocí jednoduchého protokolu je poté možné ovládat IO výstupy modulu, což by se dalo jednoduše použít ke spínání napájení modulu. Pozor je ovšem nutné věnovat zbytkovému proudu v modulu, experimentálně bylo zjištěno, že např. odpojení napájení na 30 vteřin nemusí provést reset modulu G24, spolehlivějším řešením by bylo tedy nejenom odpojit modul od napájení, ale i zároveň zkratovat jeho napájecí svorky.
2.2.4
Způsob řízení
Modem G24 může být řízen různým způsobem, každý má určité výhody i nevýhody. Základní přístupy jsou následující: •
Asynchronní notifikace
•
Synchronní pollování (Synchronous polling) Klasickým příkladem je např. příjem SMS – řešení první spoléhá na to, že G24
pošle nevyžádanou zprávu informující o nové SMS (a pokud ji zmeškáme, tak se o ní 1USB
Power
Control
Tool:
http://www.gniibe.org/development/ac-power-control-by-USB-hub/index 2Modul
Quido
(firma
Papouch):
http://www.papouch.com/cz/shop/products/io-moduly/quido/ 21
nedozvíme), naopak ponechává linku v klidu pokud po modemu nic nepožadujeme. Druhý přístup je méně efektivní, ale je spolehlivý – pokud se nějaká zpráva ztratí či poškodí tak se nic neděje – o zprávě se dozvíme při příštím pollování. Je poměrně běžné používat oba způsoby řízení dohromady. Jednak si můžeme většinou vybrat u konkrétních typů událostí, za druhé existují situace, kdy nemáme jinou možnost (např. notifikace RING/CLIP – příchozí hovor). Dalším důležitým atributem řízení je použití vláken. Máme dvě základní možnosti: Využít vlákna a blokující IO operace
•
Vytvoříme nové vlákno (vlákna) obsluhující jenom linku s G24. Jakákoliv operace čeká na dokončení. Pro komunikaci s ostatními částmi aplikace musíme použít synchronizačních mechanismů a front. Neblokující IO a stavový automat
•
Zpracovávání provádíme pomocí událostí na komunikačním kanálu. Z kanálu nám mohou principiálně přijít dva typy událostí – možnost čtení a možnost zápisu. Žádná operace neblokuje, každá operace okamžitě skončí s informací, kolik byte se podařilo přečíst / zapsat. Aplikace si musí ošetřit bufferování. Implementace řízení poté využívá stavový automat jehož vstupy jsou příchozí znaky. Obecně se dá říci, že oba přístupy mají své výhody i nevýhody. Výhodou vláknového přístupu je jednodušší implementace, jasné oddělení kódu zpracovávajícího komunikaci s modulem G24. Nevýhodou je naopak nutnost synchronizace dat, která může být velmi netriviální. Naopak přístup s využitím neblokujícího IO je efektivnější (nevyžaduje vlákna ani jejich synchronizaci), naopak je složitější na návrh a výrazně hůře se v něm provádějí modifikace či složitější posloupnosti příkazů (je nutné často zásadním způsobem předělat stavový automat).
2.2.5
Protokol pro komunikaci s G24
Základním řídícím prvkem je pro modul Motorola G24 AT příkaz zaslaný v příkazovém režimu. 22
Podle [AT] má komunikace s G24 modulem následující protokol:
Obrázek 4: Struktura AT příkazů – viz [AT]
Příkaz (Command) má následující formát [AT]: <prefix>
<delimiter>...<suffix> Kde prefix je definován jako řetězec „AT“, suffix je znak , delimiter je definován buď jako znak „;“ nebo jako mezera. Odpověď (Response) má poté strukturu: <Suffix> kde: Prefix je definován jako sekvence znaků . Suffix je definován jako .
2.2.6
Rozbor AT příkazů pro řízení
Pro požadavky řízení budeme potřebovat následující příkazy: 2.2.6.1 Práce se SMS
Pro práci se SMS potřebujeme zvolit vhodný formát reprezentace SMS. G24 nám nabízí následující:
23
•
PDU režim
•
Textový režim PDU režim je standardním a je podporován každým GSM modemem –
koresponduje s interním formátem, který pro SMS používá GSM síť. Jedná se o poměrně složitý formát – používá 7bitovou abecedu a tudíž vyžaduje kodér a dekodér z ASCII. Textový režim je naproti tomu naprosto jednoduchý na použití – do modemu pošleme pouze ASCII text jako takový a modul se poté automaticky postará o vytvoření PDU rámce. Textový režim je sice součástí standardu ETSI 07.07, ale jeho podpora je volitelná a mnoho GSM modulů ji neimplementuje (např. starší mobilní telefony, starší průmyslové modemy Siemens). Modul Motorola G24 ji nicméně podporuje. Pro potřeby naší aplikace využije právě textový režim z důvodu jednodušší implementace. Jedinou nevýhodou textového režimu je horší podpora UNICODE zpráv a textu obsahující řídící znaky (např. znak nového řádku), ale řešení těchto speciálních typů zpráv je mimo rozsah této práce (a může být do konektoru kdykoliv doplněno). Práci v textovém režimu je nutné zapnout – standardním formátem je PDU. Volba formátu se děje pomocí příkazu: AT+CMGF=1 OK
Pro práci se SMS máme k dispozici následující základní příkazy: Odeslání SMS – CMGS, příklad příkazu: AT+CMGS="+420111111111" >Telo zpravy +CMGS: 238 OK
Seznam nepřečtených SMS zpráv v paměti získáme následující sekvencí příkazů:
24
AT+CMGL +CMGL: 1,"REC UNREAD","+972544565034",,"05/01/01,09: 21: 22+08" OK AT+CMGL
Smazání přečtených zpráv: AT+CMGD=1 OK
Algoritmus pro práci se SMS je tedy následující: •
Získáme seznam SMS – pomocí AT+CMGL či AT+MMGL (Motorola rozšíření)
•
Nepřečtené zprávy nám přijdou jako +CMGL odpovědi
•
Zpracujeme je a smažeme všechny přečtené zprávy pomoci AT+CMGD=1 Způsob řízení je mírně náchylný na chyby na přenosové lince – spolehlivějším
řešením by bylo použití MMGL (které umožňuje získat ID SMS) a CMGD s mazáním podle ID – ale toto řešení je možné pouze na modelu G24, na jiných modemech není k dispozici. Proto jsem se rozhodl použít standardnější (i když horší) řešení – z důvodu zachování zpětně kompatibility s jinými GSM moduly. Algoritmus odesílání SMS je jednoduchý, prostě použijeme příkaz AT+CMGS, který SMS odešle v textovém režimu. Odeslání SMS ovšem může selhat, třeba i z důvodu momentální nedostupnosti či přetížení sítě. Stejně tak odesílání selhává při příliš rychlém odesílání SMS. Empiricky bylo zjištěno, že se nejedná o realtimový proces – odeslání trvá několik vteřin (až 10) – než přijde finální potvrzení příkazu. 2.2.6.2 Práce s odchozími hlasovými hovory
Podle [AT] je práce s hovorem definována následujícím stavovým diagramem:
25
Obrázek 5: Stavový diagram hovoru – dle [AT] G24 tedy provádí notifikaci v průběhu vytáčení o jeho aktuálním stavu. Teprve tehdy, až dojde k finálnímu spojení G24 pošle notifikaci OK (či jinou chybovou hláškou) Začátek vytáčení se děje pomocí příkazu ATD (historicky jsme se mohli setkat s různými variantami příkazu pro tónovou a pulsní volbu – to mělo smysl pouze u pevných telefonních linek – u mobilních přístrojů nikoliv). Základní syntaxe je: ATD+42011111222;
Pomocí středníku na konci příkazu se rozlišuje typ volání – standardně se provádí datové volání, G24 přejde po navázání spojení do datového režimu, domluví si s protistranou přenosové parametry a přenáší data mezi dvěma modemy. Naproti tomu znak „;“ určuje, že se bude jednat o hlasové volání. My budeme používat hlasové vytáčení – sice nemáme připojený mikrofon, ale stačí nám pouze principiální navázání spojení či prozvonění. Příkaz posílá několik odpovědí (na rozdíl od základních příkazů). První odpověď OK signalizuje, že G24 začala vytáčet (tzn. přešlo se do stavu DIAL). Druhá odpověď OK signalizuje, že spojení bylo navázáno (resp. že protistrana zvedla telefon u hlasového módu). Případně můžeme dostat některou z následujících chyb:
26
•
NO CARRIER, BUSY, NO ANSWER – odmítnutí protistranou či mobilní sítí
•
ERROR – obecná chyba (popř. CMEE ERROR, pokud je zapnutý extended error
reporting) UNKNOWN CALLING ERROR – neznámá chyba spojení
•
Pokud se spojení úspěšně naváže, tak přejdeme do stavu CONNECTED. V tomto stavu máme aktivní hovor. Hovor můžeme poté ukončit příkazem ATH: ATH NO CARRIER OK
Asynchronní zpráva „NO CARRIER“ obecně informuje o tom, že spojení se rozpadlo (např. v případě zavěšení druhé strany).
2.2.7
Řízení G24 z prostředí Java
Vzhledem k charakteristice modulu G24 je tedy naprosto jasné, že potřebujeme přístup k sériovému portu – buď k portu virtuálnímu (USB připojení), nebo portu fyzickému. V běžných operačních systémech se rozhraní pro přístup k USB portu a fyzickému portu nikterak neliší, každý COM port má své jednoznačné identifikační jméno zařízení (např. COM1, COM10 na Windows, /dev/ttyS0 či /dev/ttyUSB0 na Linuxu). Pokud využijeme možnosti sériového převodníku, tak máme k dispozici celou řadu různých výrobců a produktů. Většinou ovšem obsahují jenom několik málo běžných chipsetů (např. FTDI, PL atd.). Na Windows je většinou nutné nainstalovat ovladač převodníku, případně alespoň INF soubor s popisovačem zařízení, aby Windows správně přiřadily ovladač zařízení. Na aktuální verzi Linuxu proběhne instalace ovladače většinou bez zásahu uživatele, alespoň pokud použijeme podporovaný sériový převodník (běžné chipsety jako FTDI jsou samozřejmě plně podporovány). Pokud bychom použili USB port, který má k dispozici modem G24, tak se nám zařízení bude jevit jako tzv. USB CDC zařízení – v běžných operačních systémech se nám nainstaluje jako virtuální sériový port (přesněji jako modem, ke kterému je dán tento virtuální port). Instalace nevyžaduje žádné speciální ovladače, pouze na Windows je potřeba mít k dispozici INF soubor. Zbývá tedy vyřešit jaké jsou možnosti přístupu k sériovému portu z Javy. Java nenabízí standardně žádné API pro práci se sériovým portem, můžeme použít standardní
27
IO streamy a otevřít port jako soubor (což je podporováno na všech platformách), tím ovšem dojde k otevření portu bez možnosti nastavení přenosových parametrů (které většinou nebudou správně). Pro komunikaci s G24 jsou potřeba následující parametry linky: 9600bps, 8bit data, 1 bit stop, žádná parita. G24 v sobě obsahuje dle [AT] modul detekce parametrů přenosu, ale otázkou je samozřejmě jeho spolehlivost, zvlášť v případě různých USB převodníků. Rychlost 9600bps byla zvolena proto, že pro potřeby aplikace je zcela dostačující a zároveň se jedná o velmi bezpečnou rychlost pro přenos. Musíme tedy použít knihovnu pro přístup k sériovému portu. Bohužel nemůžeme vystačit pouze s Java API, ta nám to technicky nedovoluje. Je nutné použít nativní knihovnu a platformní API pro práci s COM portem (API je samozřejmě na každé platformě jiné). Nativní knihovny můžeme v Javě připojovat pomocí technologie JNI (Java Native Interface), v praxi to znamená, že vytvoříme třídy v Javě bez implementace těla metod a specifikujeme, které metody jsou implementovány v nativním kódu pomocí modifikátoru native. Takové metody se chovají stejně, jako ty normální napsané v jazyku Java. Uživatel tedy nepozná, že ve skutečnosti volá metodu implementovanou v jazyku C v externí knihovně. Je naší odpovědností načíst před voláním těchto funkcí příslušnou dynamickou knihovnu pomocí volání loadLibrary. Jelikož se vytvoření vlastní knihovny ukázalo jako poměrně komplikované v případě potřeby dodržet kompatibilitu s co nejvíce HW/SW platformami, tak jsem se pokusil nalézt knihovnu pro práci se sériovým portem. Požadavky byly následující: •
Přístup k COM portům, možnost konfigurace sériového portu pomocí standardních parametrů (rychlost, parita, počet datových/stop bitů atd.) bez nutnosti zásahu do systémové konfigurace
•
Funkční řešení alespoň pro platformy Windows a Linux (preferovaně pro 32 i 64 bit platformy)
28
•
Open source licence
•
Aktivně vyvíjená knihovna
Jako jediná vyhovující se ukázala knihovna RXTX3, která splnila všechny požadavky na řízení. Jediné omezení u knihovny se ukázalo být v použití neblokujícího režimu deskriptoru (neblokující režim funguje tak, že se provádí vstupně výstupní operace a v případě nemožnosti je provést okamžitě se vrací chyba), který knihovna podporuje pouze částečně. Problém je ovšem snadno řešitelný použitím blokujících operací spolu s krátkou maximální dobou pro provedení operace.
2.3 Platforma Java EE a integrace cizích komponent Pro připojení externích zdrojů se jako jednoznačná jeví bussiness vrstva – prezentační vrstva je z pohledu třívrstvé architektury zcela nevhodné místo pro připojování externích prostředků. [UCON] definuje obecnou problematiku propojení software systému v enterprise sféře, bez přímé vazby na konkrétní technologie. Softwarový konektor je definován jako propojující entita mezi různými softwarovými komponentami. Práce se zabývá propojením pomocí dvou běžných middleware řešení transportní technologie – RMI a CORBA. RMI je doporučováno jako jednodušší řešení pro Java systémy, jelikož je to technologie primárně určená pro tento jazyk a nejlépe schovává detaily distribuční technologie. CORBA je prezentována jako univerzálnější řešení, snadno poskytující prostředky pro interoperabilitu mezi různými jazyky. V obou případech ovšem je vyžadována implementace komponent na obou stranách a jejich propojení pomocí transportního kanálu. V tomto prostředí se jedná o zobecnění situace – jelikož zde je primárním problémem integrace konektoru do prostředí J2EE.
3Knihovna RXTX – http://rxtx.qbang.org/wiki/index.php 29
2.3.1
Bussiness vrstva a její charakteristika
Byznys vrstva je standardně v prostředí Java EE vytvořena pomocí komponent EJB (Enterprise Java Bean). EJB komponenty v Javě EE 5 a vyšší jsou velmi podobné standardním třídám (v anglické literatuře se můžeme setkat s pojmem POJO – plain old java objects), implementují se stejně, jen jsou doplněny anotacemi a vyžadují určité chování a zakazují použití určitých mechanismů. Java EE zná následující typy EJB komponent: •
Session beany ◦
Stateless Stateless beany jsou, jak už název napovídá, bezstavové – tzn. mezi jednotlivými voláními si neuchovávají žádný vnitřní stav .
◦
Statefull Statefull beany si naproti tomu uchovávají vnitřní stav mezi jednotlivými voláními. Jsou mnohem náročnější na prostředky, protože aplikační server musí vytvářet mnohem více instancí než u stateless session bean.
◦
Singleton Singleton je bean, který má pouze jednu instanci v rámci aplikačního serveru. Jedná se o novinku v Java EE 6 [J2EETUT], která částečně řeší omezení plynoucí z nemožnosti používat statické proměnné v komponentě. Singleton je též nepoužívá, ale aplikační server zajišťuje, že tento bean existuje pouze jednou a že všichni uživatelé pracují pouze s jednou instancí.
•
Entity beany ◦
Nahrazeny
JPA (slouží
jako
ORM
mapovač
mezi
Java
třídami
a databázovými tabulkami – viz [JPA]) a entitními třídami (viz [J2EE6]) ◦
Z důvodu zpětné kompatibility jsou pořád podporovány, novější verze Java EE ale podporu bude odstraňovat.
•
Message driven beany (beany řízené zprávami) ◦
Beany jsou integrované s technologií JMS (Java Messaging Service), která umožňuje posílání zpráv mezi jednotlivými komponentami a kanály
30
◦
Komunikační kanály pro mesage driven beany musí být vytvořeny v konfiguraci aplikačního serveru
◦
Zprávy mohou být zasílány jak v rámci lokálního serveru, tak i přes síť.
Díky charakteristice, vlastnostem EJB a implementaci některé funkcionality, je na EJB komponenty kladeno mnoho omezení (dle [EJB]): •
Restrikce na používání statických dat třídy – to je u EJB komponent striktně zakázané (s výjimkou finálních proměnných objektu – kombinace finálního a statického modifikátoru vytvoří v Javě konstantu, což samozřejmě je povolené), protože podle architektury Java EE mohou EJB komponenty běžet na různých aplikačních serverech (v režimu clusteru či pouze jako nahradní cluster pro případ selhání– tzv. failover) a může se provádět migrace klientů mezi nimi. V aplikačních serverech neexistuje žádný způsob synchronizace těchto statických dat, tudíž je použití této techniky zakázáno. Toto omezení je částečně řešeno v Java EE 6 pomocí singleton beanů, v dřívější verzi specifikace problém nemá portabilní řešení.
•
Většina používání reflection API
•
Vracení ukazatele this či jeho jiné způsoby předávání (EJB jsou standardně spouštěny v poolu – tímto ho obcházíme a děláme méně efektivní)
•
Vytváření threadů a práce s nimi (opět problematická synchronizace mezi aplikačními servery), změna priority threadů
•
Využití AWT
•
Změna class loaderu, security konfigurace atd.
•
Práce se sokety v režimu server
•
Práce se soubory a obecně většina IO operací
•
Práce se nativními knihovnami Omezení je tedy celá řada, nicméně platí, že nejsou občas úplně striktně
dodržována. Můžeme si dovolit za určitých podmínek omezení ignorovat, ale musíme si být vědomi, co nám to přinese – např. to, co si můžeme dovolit v konfiguraci s jedním serverem, si nemůžeme dovolit v serverovém clusteru, kde nám klienti mohou migrovat
31
mezi jednotlivými aplikačními servery a jejich instancemi EJB komponent. Cílem těchto omezení je udělat práci s EJB (a jejich implementaci) co nejvíce transparentní, aby se programátor nemusel zabývat detaily, jakým způsobem jsou EJB komponenty spouštěny a provozovány. Ačkoliv si teoreticky můžeme za cenu ztráty kompatibility tyto omezení dovolit ignorovat (bohužel je to velmi častým jevem, s kterým jsem se setkal na mnoha projektech – včetně občasných fatálních důsledků těchto rozhodnutí), lepším (i když pracnějším) řešení je samozřejmě dodržet specifikaci, což nás ochrání před závislostí nad implementačními detaily aplikačního serveru. Pokud chceme navíc naši aplikaci provozovat v clusterovém prostředí (což např. Glassfish/Oracle Application Server umožňuje – pro bussiness critical backendy je to nutnost), tak nám ani nic jiného nezbývá – zde již není možné tyto restrikce ignorovat ani neformálně.
32
3 Návrh řešení Nyní máme jasno, kde se integrace musí provést – v bussiness vrstvě. Vyjdeme z omezení pro EJB komponenty, na první pohled je jasné, že triviální řešení pro řízení modemu není možné použít, jelikož máme zablokovaný přístup k IO operacím – navíc jsme ukázali, že řízení modemu G24 je stavovou záležitostí – což je další důvod, proč není možné provést tuto integraci přímo v EJB. Posledním problémem je nemožnost práce s nativními knihovnami z EJB – což budeme opět potřebovat pro přístup k sériovému portu. Jaké jsou tedy možnosti pro připojení, které nebudou odporovat specifikaci EJB?
3.1 Propojení bussiness vrstvy s cizími prostředky Pokud se podíváme na existující používané řešení v Java EE architektuře, můžeme se setkat s následujícími způsoby řešení problému: Dle [JEE6] jsou databázové systémy v platformě J2EE typicky implementovány pomocí databázových ovladačů popsaných standardem JDBC (v současné verzi 4) – viz [JDBC]. Tento typ propojení je zcela orientován na databázové systémy a pro připojení externího nedatabázového systému je nevhodný. [SAPJ] ukazuje integraci jiného SW systému v prostředí Java EE – informačního systému SAP. SapJCO (SAP Java Connector) má architekturu vzdáleně podobnou naší situaci – jednoduché připojení komplikuje nutnost linkování pomocí JNI k C knihovně s poměrně specifickým rozhraním. SapJCO řešení spočívá ve vlastní architektuře pro připojování a správu connection poolu, což do značné míry supluje funkcionalitu implementovanou v aplikačním serveru. Další analýza ukazuje, že knihovna SapJCO je použitelná obecně mimo prostředí J2EE, naopak v prostředí J2EE je aplikovatelná pouze v nedistribuovaném prostředí. SAP pro potřeby plné integrace poskytuje další produkt SAP JRA, který implementuje standard Java Connector Architecture a slouží
33
jako softwarový most mezi rozhraním JCA (J2EE standard – viz [JCA]) a SAP JCO. JCA je tedy od začátku potenciálním řešením pro další zhodnocení, nicméně předtím je třeba zvážit i další možnosti, zejména i kvůli složitosti implementaci JCA konektoru.
3.1.1
Síťový server s pomocí vlastního protokolu
Vyjdeme z omezení EJB a využijeme možnosti vytváření socketových klientů v vrstvě EJB. Architektura bude obsahovat následující komponenty: •
Síťový server poslouchající na TCP či UDP portu jako server Server čeká na příchozí požadavky od EJB na síťovém portu. Server v paralelním vlákně provádí řízení spojení G24 a udržování jejího stavu. Síťový server je zodpovědný za synchronizace vláken a řízení. Volání do serveru by měla být co nejrychlejší, neměla by čekat na skutečné odeslání (které může trvat několik minut), protože jinak dojde k zablokování dané části EJB kontejneru.
•
EJB kontejner, v kterém je implementována EJB komponenta (komponenty) implementující rozhraní pro řízení modemu. Rozhraní vzhledem k celkové architektuře bude spíše vysokoúrovňové – tzn. obsahovat příkazy jako „pošli SMS“, „vytoč číslo XYZ“ atd.
34
G24 Sériová linka Síť. server TCP/IP spojení
EJB kontener
EJB kontejner
Obrázek 6: Návrh architektury: Síťový server Složitější je zde řešení zpětných volání (callbacks) – tzn. jak notifikovat bussiness vrstvu o tom, že vznikla nějaká událost. Tento problém nemá uspokojivého řešení – jelikož není možné nechat socket otevřený mimo EJB, ani není možné použít serverové sockety z bussiness vrstvy, tzn. jediné řešení je použít EJB časovač a průběžný polling. Takovéto řešení ovšem není velmi elegantní a je zatíženo velkou režií. Zhodnocení řešení: Výhody: •
Jednoduché na implementaci, přenositelné řešení
Nevýhody: •
Nutnost návrhu vlastního protokolu
•
Nutnost zajistit běh serveru separátně
•
Nemožnost zajistit zpětné volání
Verdikt: Řešení je použitelné pouze v případě, že nepožadujeme zpětný kanál – např. pro odesílání SMS, které se budou frontovat v paměti serveru.
35
3.1.2
RMI server – řídící modul
G24 Sériová linka Síť. server RMI
EJB kontener
EJB kontener
Obrázek 7: Návrh architektury: RMI server Řešení s RMI (RMI – viz [J2EETUT] ) serverem je podobné tomu s vlastním síťovým protokolem – navíc ale odstraníme jednu z jeho nevýhod – nutnost tvorby vlastního protokolu a síťového serveru. RMI (Remote Method Invocation) je protokolem umožňujícím vzdálené volání metod nad objekty v prostředí Java (jeho ekvivalentem v prostředí jiných jazyků je např. RPC v C či Windows Communication Foundation v světe.NET). RMI může buď pracovat nad protokolem JRMP (poté komunikuje pouze s Javovskými protistranami, jedná se o původní variantu se kterou RMI pracovalo), nebo může využívat standardního protokolu IIOP (pak může komunikovat s jakoukoliv protistranou – protokol IIOP je standardizován v rámci specifikace – viz [CORBA]). RMI server nám tedy vyřeší nutnost vlastního protokolu, jediné, co musíme udělat je definovat rozhraní pro server, server ho poté naimplementuje a my ho budeme volat. Problémem opět zůstávají zpětná volání, ta sice RMI umožňuje, ale my bychom tím porušili zásady vytváření EJB komponent, zejména zákaz předávat instanci this EJB komponenty dále – což RMI zpětné volání jednoznačně poruší. Doposud jsme zmiňovali pouze výhody RMI, má ovšem i jednu nevýhodu a to je práce s Naming context – pro komunikace musíme definovat naming context (sloužící 36
jako služba pro převod logického jména komponenty na informaci o hostiteli, portu a dalších atributech nutných pro připojení k IIOP službě – viz [CORBA]), alespoň pokud chceme dodržet možnost komunikace pomocí protokolu IIOP. Pokud ne, můžeme použít přímého propojení. Zhodnocení řešení: RMI představuje zlepšení řešení, ale nejpalčivější problém nám neřeší. Jinak se jedná o poměrně jednoduše realizované řešení volání externího modulu z prostředí aplikačního serveru. Při své jiné práci jsem takto úspěšně vyřešil integraci nestabilní knihovny SAPJCO (konektor do systému SAP), která vyžívá nativní knihovnu přes JNI, což znemožňovalo přímé použití v prostředí aplikačního serveru Glassfish. Řešením bylo vytvořit podobný RMI server, který fungoval jako mezivrstva do systému SAP. Řešení se ukázalo jako stabilní i v clusterovém prostředí Glassfish.
3.1.3
Oddělené servery se společným datovým úložištěm
Dalším řešením může být eliminace RMI či síťového protokolu a použít sdílené paměťové úložiště pro synchronizaci informací. Bohužel nám to opět neřeší zpětné volání – tzn. pro jednostranné použití je to použitelné, ale řešení vyžaduje polling (pravděpodobně i na straně serveru – pokud neumí úložiště notifikaci o změně). Zhodnocení řešení: Nevhodné z důvodu příliš vysoké režie, pomalosti a nutnosti využití externího úložiště.
3.1.4
Aplikační klient jako externí server
Java EE definuje tzv. kontejner aplikačního klienta, který se může použít pro připojení externí aplikace do aplikačního serveru. Aplikační klient je nejčastěji využíván pro desktopové aplikace poskytující standardní uživatelské rozhraní. Zároveň ale aplikace může využít vzdálených rozhraní EJB komponent. Aplikace má tak přístup k bussiness vrstvě bez nutnosti mezivrstvy (jako např. webové služby). Na tvorbu aplikačního klienta nejsou kladeny žádné specifické nároky, tzn. může vytvářet vlákna a spravovat je a přistupovat k nativním prostředkům. Jasně nám to tedy řeší problém zpětných volání – prostě zavoláme příslušnou metodu EJB v momentě, kdy dojde k notifikaci a problém se zdá vyřešený – ale jen na
37
oko, protože zde naopak máme problém jak provádět volání z EJB. Museli bychom: •
Využít zpětného pollování (aplikační klient polluje EJB pro požadavky)
•
Zkombinovat řešení s jinou dříve definovanou metodikou (a používat tedy 2 technologie pro komunikaci – to je ovšem zásadní nevýhoda)
G24 Sériová linka Aplikační klient IIOP EJB kontener Obrázek 8: Návrh architektury: Aplikační klient jako řídící server Resumé: Metodika řeší problém zpětných volání, ale zavádí nový zásadní problém. Kombinace dvou technologií pro integraci se jeví jako nestabilní a komplikovaná. Experimenty s tímto řešením se navíc v praxi příliš neosvědčily – problém nastane v momentě, kdy dojde k novému nasazení aplikace či restartu aplikačního serveru – aplikace spadne s neznámou chybou po uplynutí timeoutu. Problém se projevoval na verzi Glassfish v2 i současné verzi v3 – řešením by mohlo být použití webových služeb, které se mnohem lépe vypořádaly se ztrátou původního kontextu (jsou bezstavové). Dalším problémem je složitější konfigurace a spouštění aplikačního klienta – je nutné provést bootstrap kontejneru – postup se liší podle aplikačního serveru.
38
3.1.5
Java Connector Architecture (JCA)
JCA představuje nejkomplexnější způsob pro práci s externími prostředky. JCA je rozhraní vytvořené přímo k tomuto účelu – připojování externích prostředků. Bohužel se jedná o rozhraní, které je velmi komplexní a JCA konektor je složitý pro vytvoření. Proto se příliš často nesetkáme s implementacemi konektorů, ani k jejich tvorbě není k dispozici příliš mnoho materiálů či literatury. Oracle poskytuje oficiální zdroj informací v [JCA] – bohužel se jedná o informace 7 let staré a jediné, co zde najdeme, je finální specifikace JCA 1.5 (což je aktuální verze), která se více zaměřuje na formální popis architektury a problematiku implementace JCA v aplikačním serveru než na popis tvorby JCA konektoru samotného. Ačkoliv platforma J2EE prošla dramatickým vývojem a zjednodušováním, tento proces se evidentně JCA obloukem vyhnul – programování JCA konektorů ze všeho nejvíc připomíná staré doby J2EE 1.4 se spoustou implementovaných rozhraní prostředí (pouze pro splnění architektonických požadavků) a XML deskriptorů (novější verze platformy tento model nahradily anotacemi). Základním typem interakce JCA konektoru a aplikace je kontrakt. JCA obsahuje dva typy kontraktů: •
Outbound – základní typ kontraktu pro odchozí komunikaci s konektorem – pomocí něj navážeme spojení a tím se umožní interakce s konektorem.
•
Inbound – kanál slouží k zpětnému hlášení směrem do EJB. JCA zde primárně využívá Message driven beany – které jsou aktivovány z konektoru.
Lze tedy konstatovat, že letmou analýzou JCA splňuje všechny požadavky kladené na propojovací mechanismus s modemem G24, navíc se jedná o standardní způsob řešení, který je kompatibilní se specifikací EJB. Jedinou nevýhodou JCA řešení je komplexnost vytvoření JCA konektoru.
3.1.6
Zhodnocení a výběr optimálního propojení
Vzhledem k definovaným požadavkům a nalezeným možnostem se jako nejlepší volba 39
jeví implementace vlastního konektoru v JCA. Zvolená architektura je tedy následující:
G24 Sériová linka JCA connector Zpětná notifikace EJB MDB
Resource EJB Stateless
Obrázek 9: Návrh architektury: Propojení pomocí JCA connectoru Řízení směrem od EJB je tedy řešeno pomocí JCA outbound contractu. Tento contract získáme jako prostředek pomocí aplikačního serveru. JCA konektor je poté zodpovědný za řízení modulu. V případě vzniku asynchronní události (např. událost RING z G24) je možné použít inbound contract, který provede notifikaci pomocí message driven beanu (MDB). Tím jsou splněny všechny požadavky na integraci. Řešení má jedinou nevýhodu a tou je použití v clusterovém prostředí. Zde by musel být JCA konektor nasazen jako prostředek na všech nódech – ovšem v takovém případě by musel být připojen k tolika modemům, kolik je clusterových nódů – což je ovšem pravděpodobně nežádoucí (jednak kvůli ceně, jednak kvůli tomu, že by se používalo několik telefonních čísel). I tento problém má však potenciálně řešení – stačilo by udělat mezivrstvu mezi JCA konektorem a serverem obsluhujícím modem (např. pomocí RMI) – pak by se všichni připojili k tomuto stejnému síťovému prostředku. Toto řešení je už analogické k navrženému řízení – je možné vymyslet spousty různých kombinací a případů nasazení v momentě, kdy je vyřešen fundamentální způsob propojení.
40
3.2 Java Connector Architecture Struktura JCA je následující:
Obrázek 10: Struktura JCA connecotoru – dle [JCA] Contract zde reprezentují interakci mezi konektorem a mezi aplikací, případně ještě aplikačním serverem. JCA konektor je samostatně nasazován na aplikační server, kde musí být následně nakonfigurován a vytvořeny jeho prostředky. Poté, když je k dispozici JNDI prostředek, může aplikace začít používat metody konektoru. Zpětná komunikace bude dít pomocí message driven beanů.
41
4 Implementace Pro implementaci jsem zvolil aplikační server Glassfish 3.1.1 a vývojové prostředí NetBeans 7.1. Cílovou Java platformou se stala Java 7, nicméně krom kosmetických vlastností (např. diamantový operátor) nebylo použito z platformy 7 nic zásadního a tudíž by se práce dala velmi jednoduše přizpůsobit i pro starší verze J2EE/Java platformy. Jediným zásadním omezením se stala cílová verze JCA specifikace (verze 1.5).
4.1 Postup vytvoření základního JCA connectoru Pro vytvoření JCA konektoru není k dispozici žádný průvodce ani předpřipravená kostra, jak jsme zvyklí u standardních J2EE projektů. Vytvoření alespoň základního JCA konektoru, který jde nasadit na aplikační server Glassfish, se ukázalo jako výrazně složitější než jak jsem původně očekával. Základem se stal běžný EJB projekt v NetBeans z jediného důvodu – je možné ho automaticky nasadit, plus vytváří základní strukturu modulu. Postup pro přetvoření EJB projektu na modul kompatibilní s JCA byl následující: •
Přejmenování výstupního souboru z jar na rar (poněkud nešikovná standardní koncovka pro JCA moduly) – v NetBeans projektu je jméno výsledného souboru uloženo v konfiguraci projektu „project.properties“, není tedy nutné výstup přejmenovávat po každém sestavení.
•
Přidání XML deskriptoru „ra.xml“ – to je základním kamenem JCA deskriptoru. Deskriptor popisuje:
4.1.1
◦
Třídu adaptéru
◦
Třídy pro inbound a outbound spojení
Rozhraní pro JCA connector
Rozhraní definuje aplikační API, které bude využívat klientská část aplikace a implementovat samotný konektor. Rozhraní musí být speciálním projektem – není
42
možné linkovat klientskou část přímo na konektor (to je problematické vzhledem k fungování class loaderu). Proto bylo API vytvořeno jako speciální projekt typu „Java class library“, ke kterému se linkuje jak konektor, tak klientská aplikace. Je nutné definovat minimálně následující části rozhraní: •
Rozhraní pro získání spojení (datasource)
•
Rozhraní pro samotné spojení – toto rozhraní poté definuje všechny užitečné metody pro outbound spojení Rozhraní příjemce událostí MDB beanu pro inbound spojení
•
4.1.2
Outbound spojení
Outbound spojení vyžaduje následující třídy: •
Managed connection factory
•
Connection factory – rozhraní
•
Connection factory – implementace
•
Connection – rozhraní
•
Connection – implementace
•
Konfigurační properties Konfigurační vlastnosti (properties) definujeme následovně:
•
COM port pro modem Vzhlede k tomu, že se jedná z principu o prostředek, který může v jeden okamžik využívat pouze jeden proces a který určuje, se kterým modemem pracujeme, je COM port jako jednoznačný identifikátor plně dostatečný. Connection factory a Connection rozhraní – rozhraní definujeme v balíčku
rozhraní, jelikož s nimi musíme pracovat přímo z klienta. Connection factory slouží pro klienta k vytvoření spojení typu Connection, které poté poskytuje pro všechny funkce, jež outbound contract nabízí. Managed connection reprezentuje fyzické spojení do EIS, vytvářené pomocí Managed connection factory. Managed connection jsou spravované pomocí Connection pool v aplikačním serveru a jsou automaticky s Connection svázány. Connection v klasickém pojetí JCA konektoru reprezentuje logické spojení z klienta, které je přes 43
Managed connection poté propojeno s EIS. Smyslem této architektury je umožnit oddělenou správu spojení (managed connection) od správy spojení klientského. V naší aplikaci nicméně toto striktní oddělení nemá příliš smysl, jelikož spojení do GSM modemu
může
být
principiálně
pouze
jedno,
tudíž
můžeme
funkcionalitu
implementovat přímo v Connection a nekomplikovat si práci předáváním událostí z Connection do Managed Connection a poté do EIS (modemu), jak by tomu bylo u klasického konektoru. Architekturu JCA konektoru shrnuje následující diagram:
Obrázek 11: Interakce spojení v aplikačním serveru (převzato z [JCA])
44
Požadované rozhraní je definované specifikací SPI (Service Provider Interface – viz [SPI]), které se používá pro JDBC i pro JCA. Popíšeme si základní třídy SPI, které musíme implementovat a některá úskalí tohoto procesu: ManagedConnectionFactory Základním
kamenem
je
třída
implementující
rozhraní
ManagedConnectionFactory, předepisující následující důležité metody: •
createConnectionFactory – vytvoří connection factory pro klientské spojení
•
createManagedConnection – vytvoří novou managed connection (fyzické spojení do EIS). Metoda dostává dva parametry – Subject (identifikuje uživatele/entity pokoušející se o operaci podle J2EE security definicí) a ConnectionRequestInfo (může být použito pro nastavení parametrů per spojení). Použití ConnectionRequestInfo je volitelné, můžeme si ho vytvořit a nastavit sami (což je logické i v našem případě – connection samotná nemá žádnou konfiguraci, tu má celý konektor).
•
matchManagedConnections – metoda slouží k opačnému párování – dostane seznam connection v instanci třídy Set (bohužel negenerické), subject a connection request info. Poté má metoda vrátit spojení, které odpovídá těmto parametrům (pokud takové existuje). Běžná implementace provede iteraci přes Set, dále získání ConnectionRequestInfo (či spíše jejich podtříd) objektů a jejich porovnání. Pozor, v případě že metodu implementujeme špatně (či vůbec), bude aplikační server vytvářet nová a nová spojení. matchManagedConnections se používá v případě, že vznikne požadavek na získání spojení a existuje už nějaké jiné otevřené. Pokud matchManagedConnection spojení nevrátí, aplikační server bude vytvářet nové spojení – což v důsledku způsobí jenom růst počtu spojení (až se vyčerpá connection pool)
•
equals – MCF musí implementovat tuto metodu (včetně hashCode) a provést porovnání na základě parametrů (konfigurace) konektoru ConnectionRequestInfo ConnectionRequestInfo je popisovačem žádosti o spojení – mělo by obsahovat
všechny parametry, které jednoznačně identifikují spojení. ConnectionRequestInfo by 45
mělo
implementovat
metody
equals/hashCode
pro
jejich
porovnávání.
ConnectionRequestInfo je ve skutečnosti rozhraní, my musíme vytvořit třídu, která toto rozhraní implementuje pro náš konektor. ManagedConnection ManagedConnection, jak již bylo řečeno výše, reprezentuje fyzické spojení do EIS. Stejně tak je MC rozhraním, které implementuje nějaká naše třída v konektoru. O vytváření
instancí
ManagedConnection
objektů
se
stará
metoda
createManagedConnection v ManagedConnectionFactory. Rozhraní předepisuje velké množství metod, valná většina z nich však má smysl pouze u plnohodnotných konektorů podporující transakce a XA prostředky (XA reprezentuje jednotlivé prvky globální transakce mezi několika systémy). Důležitou odpovědností ManagedConnection objektů je ovšem práce s klientským spojením. Práci s metadaty a informacemi zastupují následující metody: •
ManagedConnectionMetaData getMetaData () throws ResourceException Metoda vrací metadata spojení (nemají žádný speciální význam, klient si je může vyžádat, případně s nimi může pracovat aplikační server)
•
setLogWriter / getLogWriter – slouží pro nastavení PrintWriter instance pro logování (umožňuje aplikačnímu serveru přesměrovat logování konektoru do speciálního místa – zde je vidět, že JCA je poněkud archaická záležitost – tento způsob logování je zcela nevhodný a místo něj je dnes běžné používat java.logging API, která má mnohem větší možnosti pro konfiguraci)
Důležité metody pro práci s klientským spojením jsou tyto: •
getConnection (Subject subject, ConnectionRequestInfo cxRequestInfo) Metoda vrací novou instanci klientského spojení na základě specifikovaného request info objektu. Spojení jsou většinou automaticky spárována. Toto je jediným místem, kde se typicky vytváří instance klientské spojení – logické místo pro tuto operaci – tedy třída ConnectionFactory – pouze volá ConnectionManager s požadavkem na vytvoření spojení.
•
46
associateConnection – provede asociaci fyzického managed spojení s jiným
klientským spojením. Co se týká podpory transakcí, JCA definuje dvě úrovně podpory: •
Jednofázová transakce (lokální)
•
Dvoufázová transakce (distribuovaná, XA transakce), skládající se typicky z: ◦
Operace Prepare (příprava na commit) a Commit (skutečné zapsání/commit dat)
◦
XA prostředku jednoznačně identifikující transakci mezi distribuovanými systémy
Metody pro práci s transakcí: •
getLocalTransaction – vrací objekt lokální (jednofázové) transakce
•
getXAResource – vrací objekt identifikující globální transakci. Obě metody mohou vrátit NULL v případě, že nechceme podporu transakcí implementovat
ManagedConnection objekt generuje události, na které může reagovat aplikační server. Mechanismus je klasický – aplikační server si zaregistruje příjemce událostí, které jsou poté zavolány, když dojde k určité události. I když nebudeme žádné typy chyb generovat, tak musíme poskytnout metody pro zaregistrování listenerů kvůli implementaci tohoto rozhraní. Metody pro správu seznamu událostí: •
addEventListner
•
removeEventListener
Metody listeneru (notifikované události) jsou poté následující: •
connectionErrorOccurred – obecná chyba spojení
•
connectionClosed – spojení do EIS bylo uzavřeno
•
localTransactionCommitted – lokální transakce byla commitnuta
•
localTransactionRolledback – lokální transakce byla rollbackována
•
localTransactionStarted – začátek lokální transakce
47
Správa prostředků spojení: •
destroy – metoda je volána aplikačním serverem v momentě, kdy chce uvolnit všechny prostředky spojení (typicky při zmenšování velikosti connection pool). destroy je zodpovědné za uvolnění všech prostředků (zejména včetně nativních)
•
cleanup – metoda uklízí stav klientského spojení (např. po neúspěšně ukončené transakci). ConnectionRequestInfo Rozhraní předepisuje pouze přepsání metod hashCode a equals. Každý konektor si
vytvoří třídu implementující toto rozhraní. Instance této třídy poté popisuje parametry spojení, se kterým je spojení získáváno a navázáno. V prostředí databázových ovladačů by se jednalo např. o uživatelské jméno/heslo, server, jméno databáze atd. V prostředí GSM konektoru to bude port, na kterém je GSM konektor připojen. ConnectionFactory ConnectionFactory zde uvádím pouze abstraktně, taková třída v SPI neexistuje (pouze v CLI). Návrh této třídy je tedy na nás. Typicky ovšem bude ConnectionFactory (jak již její název napovídá) vytvářet spojení (klientské). To není samozřejmě možné dělat přímým instancováním oné třídy – tím by nevzniklo managed spojení a celá správa spojení v aplikačním serveru by vyšla kompletně vniveč. Pro vytvoření connection použijeme objekt typu ConnectionManager (opět ze SPI) poskytující následující metodu: Object allocateConnection (ManagedConnectionFactory mcf, ConnectionRequestInfo cxRequestInfo)
Tuto metodu zavoláme v případě, že chceme vytvořit nové spojení. Metoda se postará o automatické přiřazení managed connection (případně její vytvoření). Samotná connection
factory
nevzniká
z ManagedConnectionFactory.
48
též
přímo,
ale
instancováním
4.1.3
Inbound spojení
Inbound spojení je definováno pomocí listener rozhraní (specifikuje jednotlivé metody, pomocí kterých je prováděna notifikace do klienta) a activation specifcation. Activation specification definuje parametry spojení (či spíše celého prostředku), pro které chceme přijímat notifikace. Opět zde použijeme COM port, jelikož je unikátním identifikátorem, tudíž je možné jej použit pro jednoznačnou identifikaci objektu událostí. Samotný listener je poté MDB (Message Driven Bean) komponentou implementující toto rozhraní se specifikovanou aktivační konfigurací. Způsob vzniku aktivací je složitější – jedná se o asynchronní události vznikající v EIS systému. Typický postup implementace inbound je následující: •
Implementujeme třídu pro Resource Adapter (čímž se konektor stává závislý na specifikaci JCA 1.5 – dřívější verze specifikace to neumožňuje)
•
V resource
adaptéru
poté
v metodě
start
pomocí
předaného
BootstrapContext získáme instanci na Workmanager. Workmanager je zapouzdřením thread poolu definovaného v aplikačním serveru. Tento workmanager je volitelný pro použití, i když doporučovaný. Výhodu má zejména v prostředí, kde dynamicky vytváříme vlákna, jelikož thread pool zaručuje omezení počtu vytvářených vláken. Vlákno je poté třída implementující
rozhraní
Work.
V tomto případě vytvoříme jeden objekt typu Work a spustíme ve Workmanageru. •
Z vlákna
implementující
Work
poté
můžeme
provést
získání
MessageEndpoint z MessageEndpointFactory (předané od aplikačního serveru z Resource Adapter při volání endpointActivation – volá se při nasazení MDB beanu s activation
specification
a listener třídou
odpovídající tomuto RA) •
Na MessageEndpoint poté můžeme provést přetypování na GsmListener a zavolat příslušné metody. MDB beanu se poté doručí tyto události.
Rozebereme si nyní některé důležité třídy inbound contractu v JCA podle [JCA]. 49
ActivationSpec ActivationSpec popisuje prostředek, u kterého chceme dostávat notifikace. Třída implementující ActivationSpec definuje get/set metody pro jednotlivé vlastnosti popisující aktivaci. Rozhraní předepisuje metodu validate (), kterou může aplikační server využít k validaci nasazení (zejména nastavených parametrů aktivace). Příliš se nepoužívá, nemá vztah ke klientovi jako takovému. ResourceAdapter Jedná se o rozhraní reprezentující celý konektor. Objekt typu ResourceAdapter slouží primárně k lifecycle managementu v daném konektoru. K dispozici je až ve verzi 1.5 JCA specifikace, dřívější verze JCA jsou nevhodné pro tento typ konektorů. RA definuje následující důležité metody: •
start – volána automaticky při startu (nasazení) ResourceAdapteru. Metoda dostává jako první parametr BootstrapContext, který může použít pro získání dalších prostředků.
•
stop – požadavek na ukončení resource adaptéru a jeho aktivit probíhajících na pozadí
•
endpointActivation – aktivace endpointu. Slouží jako styčný pro spuštění aktivit na pozadí.
•
endpointDectivation – deaktivace endpointu. Typickou reakcí RA je ukončení aktivit na pozadí.
•
getXAResources – vrací seznam relevantních XA prostředků pro distribuované transakce.
WorkManager Třída nám umožňuje pracovat s vlákny vysokoúrovňovým způsobem. Používá rozhraní Work, ne nepodobné rozhraní Runnable, ovšem s tím rozdílem, že definuje dvě metody – run (odpovídá Runnable, jedná se o kód který se spustí při zpracování úlohy) a release (odpovědný za uvolnění prostředků). Třída WorkManager je interně implementována 50
nad konceptem thread pools v aplikačním serveru. Tento přístup má oproti „naivnímu“ vytváření threadů několik výhod – zapouzdřuje a omezuje nám vytváření vláken, parametry thread pool je navíc možno parametrizovat z prostředí aplikačního serveru. WorkManager poskytuje následující služby: •
doWork – spustí novou Work a čeká na její dokončení. Návrat se děje až po dokončení specifikovaného Work objektu
•
startWork – zařadí Work objekt do fronty a počká na jeho spuštění. Na dokončení ovšem nečeká.
•
scheduleWork – zařadí Work objekt do fronty a okamžitě skončí Typickým případem použití pro inbound spojení je vytvoření objektu Work, který
necháme běžet na pozadí dokud je endpoint aktivovaný. Takový Work objekt můžeme vytvořit
v metodě
endpointActivation
u ResourceAdapter
třídy
a při
volání
endpointDeactivation jej poté ukončíme (nepřímo, většinou nastavením nějaké stavové proměnné, případně notifikace nad objektem). MessageEndpointFactory MessageEndpointFactory zapouzdřuje funkcionalitu pro notifikace směrem k message driven beanům. Metoda vytváří objekt typu MessageEndpoint, nad kterým můžeme provádět samotné notifikace. MEF poskytuje tyto metody: •
createEndpoint – vytvoří nový endpoint pro odesílání zpráv
•
isDeliveryTransacted – vrací true, pokud je dané volání metody příjemce události transakční či nikoliv.
MessageEndpoint MessageEndpoint zapouzdřuje cíl notifikace. Můžeme ho bezpečně přetypovat na typ rozhraní MDB příjemce událostí. ME podporuje transakční doručení – v případě podpory transakce musíme používat metodu beforeDelivery před voláním notifikační metody a afterDelivery po jejím dokončení. Aplikační server se poté může rozhodnout, zda bude vytvářet novou transakci či použije existující (pokud existuje). Důležité je při použití MessageEndpoint objektu nezapomenout zavolat jeho metodu release, pokud ho 51
již nebudeme potřebovat. Garbage collector by samozřejmě objekt dříve či později uklidil, ale je to velmi neefektivní, jelikož MDB pooly mají omezenou velikost na počet ME.
4.1.4
Použití konektoru z klientské strany
Použití outbound spojení konektoru je z klientské strany jednoduché a podobá se práci s databází. V první řadě musíme provést nasazení konektoru v aplikačním serveru a konektor nakonfigurovat. Konfigurace konektoru spočívá ve vytvoření connection pool, v aplikačním serveru Glassfish najdeme v „Resources“/„Connector connection pool“ – zde vybereme typ RA adaptéru a jméno poolu, poté můžeme nastavit parametry dané connection factory. U poolu můžeme nastavit např. minimální a maximální počet spojení do EIS, aplikační server poté automaticky vytvoří příslušný počet managed connection. Správnost vytváření spojení můžeme vyzkoušet v Glassfish pomocí tlačítka „Ping“, které se pokusí vytvořit managed connection a zinicializovat ji s parametry – což je poměrně dost průkazný způsob ověření funkčnosti celého konektoru. Při vývoji jsem zjistil nedostatky automatického nasazovacího procesu v NetBeans – pokud projekt nasazujeme z vývojového prostředí, tak se v uživatelském rozhraní Glassfish tváří, že je správně nasazen jako typ Resource adapter/Connector, ovšem v momentě, kdy se pokusíme vyzkoušet funkci „Ping“ (tzn. vytvořit managed spojení), tak celý proces selže na chybě neinicializovaného / špatně nasazeného konektoru. Pokud ovšem nasazení provedeme ručně (toho stejného konektoru), tak vše funguje správně. Jedná se tedy zřejmě o chybu v nasazovacím procesu, ať již na straně vývojového prostředí či Glassfish. NetBeans pro nasazení využívá trochu jiný model než administrační rozhraní serveru Glassfish, NetBeans využívá tzv. „directory deployment“ (při kterém se aplikace sestaví do adresáře a vynechává se archivace do výsledného rar/war/ear/jar souboru), kdežto přes webové rozhraní Glassfishe se provádí nasazení pomocí výsledného archivu (jeden soubor). Pokud máme vytvořený a správně nakonfigurovaný connection pool, tak můžeme přistoupit ke konfiguraci samotného prostředku. Podobně jako u databázových prostředků je konfigurace velmi jednoduchá
52
4.1.5
Propojení outbound a inbound spojení
Standardní JCA konektory (např. databázové) používají oddělené fyzické spojení pro práci s inbound a outgoing spojeními. Je to logické, protože outgoing spojení nemusí vůbec existovat, pokud je aplikační server nakonfigurován tak, že nebude vytvářet žádný počáteční počet spojení. Navíc je nepřípustné spojení blokovat pro klienta, který jej získal. Naproti tomu situace s GSM modemem je zcela odlišná – není efektivní spojení s GSM modemem vázat na fyzické otevření portu z následujících důvodů: •
Inicializace GSM modemu je netriviální a časově náročná
•
Bylo by jen obtížně možné provádět detekci příchozích událostí (je zde reálná šance, že by tyto asynchronní události přišly některému z klientů, kteří získali outgoing spojení) Naopak existuje mnohem efektivnější způsob řízení v takovémto případě:
•
Vytvoříme jeden obslužný thread pro modem, který provede jeho inicializaci a bude periodicky provádět polling modemu (SMS) či přijímat asynchronní události o změně stavu (hovor)
•
Ze spojení poté provedeme sběr požadavků, které doručíme do obslužného vlákna.
•
Z obslužného vlákna doručíme asynchronní události do inbound work objektu, který následně provede volání nad příslušným MessageEndpoint, který se postará o volání všech potřebných MDB beanů
4.2 Řídící modul GSM modemu Z důvodů komplikovanější implementace řízení GSM modemu byla implementace nejprve provedena mimo prostředí J2EE – zde je mnohem jednodušší provádět ladění a zkoušení jednotlivých vlastností. Základem je třída GsmModem, která se stará o řízení modemu nad specifikovaným portem. Třída sama není závislá na J2EE prostředí (i když je použita v tomto systému). GsmModem definuje následující základní inicializační metody: •
Konstruktor – specifikuje port pro práci s modemem
53
•
openPort – provede otevření portu
•
initPort – nastaví přenosové parametry portu
•
closePort – uzavře port
•
shutdown – metoda nastaví příznak shouldRunning na false (čímž se provede ukončení řídícího vlákna)
Třída vytváří nové vlákno v metodě openPort (pokud toto vlákno ještě neexistuje). Toto vlákno řídí celý GSM modem. Samostatné vlákno se zde vytváří z důvodu, že asynchronní notifikace pomocí knihovny RXTX se ukázala jako nespolehlivá při dlouhodobých experimentech. Je tedy použito blokující řízení s omezením maximální délky provádění.
54
Obrázek 12: Class diagram ovladače GSM modemu
4.2.1
Abstrakce AT příkazů
AT příkazy jsou základním prostředkem pro řízení GSM modemu. Jelikož mají AT příkazy podle [AT] stejnou, definovanou strukturu, je žádoucí provést abstrakci jejich
55
zpracování (které není úplně jednoduché a jednoznačné, jak by člověk očekával od průmyslového zařízení – díky historické kompatibilitě s AT příkazy starých modemů). Definujeme proto třídu AtCommand, která očekává jako svůj parametr konstruktoru AT příkaz (jako řetězec), který chceme odeslat do modemu. Definujeme následující vlastnosti pro všechny AT příkazy: •
Stav zpracování příkazu
•
Timeout (maximální doba provádění AT příkazu)
•
Počet provedených opakování
•
Počet maximálních opakování příkazu
Základní postup při odeslání AT příkazu je tedy následující: •
Odešleme příkaz na sériovou linku a zároveň si poznamenáme aktuálně zpracovávaný AT příkaz. Zpracovávaný může být v jeden moment pouze jeden AT příkaz, tzn. v tomto momentě se čeká na výsledek zpracování AT příkazu modemem. Toto omezení je dáno primárně architekturou modemu.
•
Snažíme se z hlavní smyčky vyčíst odpověď modemu (s kontrolou maximální doby
pro
provedení),
pokud
modem
odpoví,
uložíme
data
do
ByteArrayOutputStream. Při každém čtení se pokusíme zjistit, zda nebyl načtený úplný řádek. ◦
Pokud ano, voláme metodu handleLine na AT příkazu (třída AtCommand). Tato metoda v základní implementaci provádí následující klíčové věci: ▪
Kontroluje, zda se jedná o ukončovací odpověď a pokud ano, změní stav AT příkazu
▪
V opačném případě uloží řádek do pole replies (obsahující všechny odpovědi na AT příkaz pro pozdější zpracování)
◦
Pokud ne, zapamatujeme si poslední kontrolovaný byte v bufferu a při dalším čtení kontrolu opakujeme. Zpracování vychází ze sémantiky AT příkazů a jejich odpovědí, které se ukončují novým řádkem.
•
Timeout AT příkazu je definován tak, že v požadované době nezmění AT příkaz stav na dokončený (úspěšně či neúspěšně). V takovém případě se provádí tyto kroky: ◦
56
Zvedneme počítadlo pokusů o zpracování AT příkazu o 1
◦
Zkontrolujeme, zda počítadlo pokusů je menší než maximální počet opakování. ▪
Pokud ano, opakujeme odeslání příkazu a resetujeme jeho timeout. Celé kolečko zpracování se opakuje
▪
Pokud ne, prohlásíme příkaz za neúspěšný
Timeout AT příkazů zde slouží primárně k detekci správné komunikace s modemem, tzn. situace, kdy se zpracování příkazu (ať již úspěšné či neúspěšné) nepodaří dokončit v nějakém stanoveném termínu. Tento timeout musí být variabilní pro různé příkazy – typický AT příkaz nastavující parametry modemu může mít timeout v jednotkách vteřin (čímž snadno poznáme, zda modem funguje správně), naopak příkazy pro práci se sítí (např. odeslání SMS, vytočení hovoru) musí mít podle [AT] timeout mnohem větší, minimálně 30 vteřin (modem před jejich potvrzením/zamítnutím čeká na reakci sítě – která samozřejmě není okamžitá). Situace, kdy se příkaz provede včas, ale G24 zahlásí neúspěšné provedení příkazu, samotná třída AtCommand neřeší, tyto situace jsou ponechány na interpretaci vyšší vrstvě, jelikož zde není možné nalézt obecné řešení. Obecně detekce dokončení příkazu je následující: •
Pokud přijmeme řetězec „OK“, jedná se o úspěšně provedený příkaz.
•
Pokud přijmeme řetězec „ERROR“ nebo řetězec začínající „+CMS ERROR“, jedná se o chybu provádění příkazu. Tento způsob reportování chyb se nazývá základní. Řádek ERROR se používá pro obecné chyby a odpovědi na běžné AT příkazy. CMS ERROR je podle standardu použit pro rozšířené SMS příkazy pro reportování chyb s chybovým kódem. Modem G24 je možné přepnout do režimu „extended error reporting“ pomocí příkazu AT+CMEE. V extended error reporting režimu G24 posílá místo ERROR příkazy „+CMS ERROR: popis chyby“ a umožňuje nám i u základních AT příkazů zjistit přesný důvod selhání (což původní režim AT příkazů neumožňuje). Můžeme si vybrat, zda chceme popis chyby dostávat textově (poté se automaticky vkládá řetězec z tabulky chyb), či zda chceme dostávat pouze kódy chyb (což je pro strojové zpracování většinou výhodnější).
57
Stavy příkazu jsou následující: •
INIT (počáteční stav, ve kterém je příkaz vytvořen)
•
SENT (příkaz byl odeslán do modemu)
•
DONE (příkaz byl úspěšně dokončen)
•
DONE_FAILED (příkaz byl dokončen s chybou) Stav je automaticky spravován třídou, primárně metodou handleLine, která
zajišťuje přechod do stavu DONE (pokud je přijata odpověď „OK“) či DONE_FAILED (v případě, že dostaneme odpověď „ERROR“ či „+CMS ERROR“). Extended error reporting knihovna nevyužívá (je zde zbytečný), ale jeho doplnění by bylo triviální záležitostí. Ošetření speciálních AT příkazů Způsob, který implementuje základní třída AtCommand, je vhodný nejlépe pro práci s jednoduchými příkazy, které mění stav modemu a modem okamžitě odpoví, zda byla operace úspěšná či nikoliv. Pro speciální případy, kde není tato podmínka splněna, je nutné vytvořit podtřídy této třídy a implementovat změněnou funkcionalitu. Základními prostředky, které máme k dispozici pro změnu chování, jsou následující metody: •
handleLine – reakce na odpovědi modemu, je možné změnit i sémantiku práce se stavy
•
eventReceivedByte – metoda, kterou původní AtCommand nepoužívá a která je primárně určena pro reakci na události mimo definovaný rámec řádků a odpovědí. Metoda je automaticky volána při každém přijetí byte z sériové linky a umožňuje provádět vlastní interpretaci. Zároveň se předává instance na GsmModem třídu, která obsahuje přístupný output stream – což dává velkou výhodu v rozšiřitelnosti tohoto řešení pro jakékoliv situace.
4.2.2
Odesílání SMS
Odesílání SMS je implementováno v třídě AtSmsCommand. Základním příkazem pro 58
odeslání SMS je AT+CMGS=“ telefonní číslo“. Podle [AT] vypadá sekvence odesílání následovně: •
Odešleme AT+CMGS=“ telefonní číslo“ a čekáme na odpověď
•
Modem odpoví sekvencí „\n>“ – tzn. na novém řádku nám pošle znak „>“
•
Odešleme textovou zprávu ukončenou znakem CTRL+Z (0x1A)
•
Modem příkaz standardně potvrdí a zpracuje Díky tomu je jasné, že se odeslání SMS nedá realizovat pomocí standardní třídy
AtCommand. Vytvoříme tedy podtřídu AtSmsCommand, která přepisuje metody eventReceivedByte tak, že v případě, že se příkaz nachází ve stavu „SENT“ a zároveň je přijatým znakem „>“, tak si získá přes předanou instanci GsmModem výstupní stream do portu a odešle zbytek dat (=tělo zprávy ukončené CTRL+Z). Poté po příjmu odpovědi „OK“ přejde do stavu dokončeno.
4.2.3
Práce s odchozím hovorem
Odchozí hovor je podobně nerealizovatelný pomocí AtCommand jako SMS – důvod je složitější řídící tok příkazu ATD, které typicky vypadá následovně: •
Odešleme ATD<číslo>; (středník zde určuje typ hovoru – hlasový, nikoliv datový)
•
Modem odpoví „OK“ v případě, že se mu podařilo přejít do stavu „Dialing“ – tedy začít vytáčet
•
Nyní se čeká na odpověď sítě a reakci protistrany
•
Pokud modem odpoví „OK“, znamená to, že přešel do stavu „CONNECTED“ – tzn. hlasové spojení navázáno (v případě, že by se jednalo o datové spojení, odpověď by byla „CONNECT“ místo „OK“ – sémantika se zde liší) ◦
Spojení je navázáno a můžeme ho přerušit buď my (pomocí příkazu ATH) a nebo protistrana zavěšení/selháním sítě
◦ •
Přerušení spojení je indikováno asynchronní událostí „NO CARRIER“
V opačném spojení modem odpoví: ◦
„NO CARRIER“ – spojení nebylo možné navázat (generická chyba)
◦
„BUSY“ – obsazeno a aktivní odmítnutí uživatelem
59
◦
„NO ANSWER“ – timeout vyzvánění – uživatel nepřijal/odmítnul hovor
Praxe ovšem ukázala jakousi nespolehlivost těchto návratových chyb – při aktivním odmítnutí modem někdy odeslal odpověď „NO CARRIER“ místo očekávané „BUSY“. Přesný důvod se mi bohužel nepodařilo zjistit. Kromě stavu příkazu (enum State v AtCommand) obsahuje AtVoiceCommand i svůj vlastní výčet stavů vycházející z [AT] – viz Fronta požadavků. Sémantika vykonání příkazu AtCommand je následující: Třída přetěžuje metodu handleLine a upravuje podle odpovědí modemu stav volání a posléze i finální stav příkazu. Příkaz zůstává nedokončený, dokud nedojde k ukončení hovoru – proto příkaz provádí i vlastní nastavení timeoutu (které se liší podle stavu, v němž se nachází). Tím je zajištěno, že nedojde k žádným interferencím ze strany jiných příkazů. Nevýhodou tohoto přístupu je, že v době hlasového hovoru a jeho zpracování nejsou obsluhovány SMS příkazy (ani příchozí, ani odchozí). Tento problém by byl řešitelný pollováním fronty z AtVoiceCommand, případně by byl řešitelný pomocí režimu MUX (v jednom virtuálním kanálu zpracovávat řízení hlasu a v druhém SMS), ale pro demonstrační konektor mi tato funkcionalita již přišla zbytečná – zvláště vzhledem k tomu, že se nedá v tomto nastavení čekat využití audio linek a tudíž se vytáčení bude provádět primárně pro prozvonění (což je mimochodem oblíbená notifikační metoda i různých zabezpečovacích a hlídacích systémů).
4.2.4
Stavový automat
Jedná o stavový automat s následujícími stavy: •
INIT1 (počáteční stav, inicializace modemu pomocí ATZ – reset do standardního nastavení)
•
INIT2 (nastavení parametrů SMS – AT+CMGF)
•
SMS_SENT (po odeslání první části SMS pomocí AT+CMGS]
•
SMS_QUERY (stav po odeslání dotazu na nepřečtené zprávy pomocí AT+CMGL)
•
60
OUTGOING_CALL (vzniká po požadavku na vytočení hovoru)
•
READY (modem je připraven, není nic lepšího na práci)
•
REINIT (modem selhal a je nutné jeho opakovaná inicializace)
OUTGOING_CALL
SMS_SENT
SMS_QUERY
READY
SMS_DELETE INIT2
INIT1
REINIT
Obrázek 13: Stavový diagram řízení modemu G24
Přechody stavů jsou poté znázorněny v následujícím stavovém diagramu: Přechod do stavu REINIT je automatický a děje se ze všech kontextů – nejedná se ovšem o klasický stavový přechod, ale pouze signalizaci fatální chyby, kdy je nutné 61
modem reinicializovat. Pokud dojde k selhání v stavech INIT1 a INIT2, provádí se zároveň uzavření a opětovné otevření portu. Tudíž k opakované inicializaci portu bude docházet při opakovaných selhání – takovým typickým problémem může být přerušení komunikace (ztráta USB portu atd.), kdy jediným řešením je zavřít port (čímž se port uvolní) a jeho opětovné otevření (může vyžadovat opakované vložení USB zařízení do portu). Pokud bychom měli implementovanou funkcionalitu i pro řízení napájení G24 (jak bylo definováno v Požadavky na řízení ), tak při opakovaných přechodech do stavu INIT1 bez dosažení stavu READY by bylo žádoucí provádět i reset napájení G24 (při experimentech se mi několikrát podařilo modul dostat do stavu, kdy přestal kompletně odpovídat na AT příkazy a nebylo jiného řešení než odpojit ho od napájení, chvíli počkat a připojit ho znovu – kde začal fungovat bezchybně).
4.2.5
Fronta požadavků
GSM řízení funguje asynchronně – tzn. klienti paralelně vkládají požadavky do fronty a ovladač modemu je zpracovává v momentě, kdy je to možné. Pro řízení definujeme následující pomocné struktury: •
Sms Sms reprezentuje jednu SMS zprávu – základními informacemi je odesílat (příjemce) a text zprávy. Struktura je použita pro příjem i odeslání.
•
VoiceTransaction Struktura obsahuje dvě logická data – telefonní číslo a stav telefonního hovoru. Stav telefonního spojení je následující (vychází z [AT]):
•
INIT – prvotní stav spojení
•
DIALING – modem začal vytáčet
•
CONNECTED – spojení bylo navázáno (tzn. audio je spojeno)
•
TERMINATED – spojení se ukončilo (přechod do tohoto stavu může přijít kdykoliv z kteréhokoliv stavu – ze stavu DIALING reprezentuje aktivní odmítnutí či timeout navázání spojení, ze stavu CONNECTED přerušení spojení)
62
Díky asynchronní povaze celého driveru je zvoleno následující schéma řízení: •
Klient vloží požadavek do příslušné fronty
•
Ovladač GsmModem získá první požadavek z fronty a začne jeho zpracovávání
•
Ovladač poté provede notifikaci o stavu požadavku do definovaných příjemců událostí. Instance příjemců událostí jsou definované nad instancí třídy GsmModem –
přidávají se pomocí volání metody addListener a odebírají pomocí removeListener. Třída GsmModem definuje samostatné fronty pro požadavky: •
Fronta SMS zpráv (jako List<SMS>)
•
Fronta volání (jako List – transakce jsou vkládány v stavu INIT) Zbývá vyřešit problém notifikací – do GsmModem může být vloženo mnoho
požadavků od různých klientů, které budou řešeny postupně. Proto je nutné tyto požadavky identifikovat jednoznačným způsobem. Třída GsmModem tedy při každé operaci s frontou (addSms a addVoiceTransaction) vrátí unikátní ID požadavku, které je poté použito pro zpětné volání. ID je přímo součástí třídy SMS a VoiceTransaction, generováno je ovšem automaticky při vložení požadavku do fronty a uživatel API nemá možnost jej změnit. Fronta požadavků je zpracovávána ve stavu „READY“, tento stav znamená, že modem je plně inicializován a může zpracovávat požadavky. Ve stavu READY modem tedy provádí: •
Zpracování požadavků fronty SMS – v případě že je ve frontě alespoň jeden požadavek, tak dochází k nastavení aktuálního AT příkazu na novou instanci AtSmsCommand (které předáme instanci třídy Sms z fronty), odeslání příkazu a přechodu do stavu SMS_SENT
•
Požadavky fronty VoiceTransaction jsou analogicky řešeny, vytvoří se instance AtVoiceCommand jako aktuální příkaz, předá se jí VoiceTransaction instance. Stav hlasového spojení je poté udržován v instanci VoiceTransaction – samotný AtVoiceCommand vlastní stav nemá, pracuje pouze se stavem VoiceTransaction a se stavem celého AtCommand (který ovšem nastaví až po dokončení 63
transakce) Periodicky (pokud není žádný požadavek fronty) se provádí pollování
•
doručených SMS pomocí příkazu AT+CMGL. Příkaz se odesílá standardně jednou za pět vteřin. Po odeslání příkazu se přechází do stavu SMS_QUERY. Příkaz se standardně zpracuje pomocí AT_COMMAND, ale po jeho dokončení se v GsmModem zpracuje pole replies z AtCommand (obsahuje všechny odpovědi, které neukončily zpracování AT příkazu). V případě, že existuje alespoň jedna nepřečtená SMS zpráva, tak přijde mezi AT+CMGL a finálním „OK“ řádek (či řádky) začínající „+CMGL:“ – tento řádek značí informace o zprávě, kterou takto listujeme. První řádek zde obsahuje meta informace – tzn. odesílatele, datum odeslání SMS, stav atd. Druhý řádek poté obsahuje tělo samotné zprávy. Poněkud problematické jsou zprávy, které obsahují znaky nového řádku (které SMS podporuje a novější telefony je mohou odeslat) – G24 zde neprovádí žádné escapování. Implementace je tedy taková, že všechny řádky, které následují a nezačínají +CMGL, jsou interpretovány jako řádky zprávy. Lepší práci by se SMS zřejmě umožnil PDU režim.
4.2.6
Správa kontextu modemu a řešení chyb
GSM modem je principiálně nespolehlivé zařízení z mnoha důvodů – pomineme-li jeho hardwarové a softwarové nedokonalosti (které se prostě něčemu tak složitému nevyhnou), je tu navíc ještě element GSM sítě, který představuje principiálně nespolehlivé médium (ať už z jakéhokoliv důvodu – přetížení, výpadek, špatný signál atd.). Při obsluze modemu tedy musíme dbát na zotavení z chyb. Řešení chyb na úrovní příkazu je definováno jednoduše: •
V případě, že k selhání došlo ve stavu INIT1 či INIT2, uzavři port a otevři ho znovu
•
Přejdi do stavu REINIT Pokud dojde k takovému selhání při zpracovávání požadavku z fronty (tzn. jedná
se o SMS či hlasovou transakci), nedojde k žádné ztrátě dat – z fronty jsou požadavky
64
odstraňovány teprve po jejich úspěšném dokončení a tudíž pokud dojde k reinicializaci, GsmModem po úspěšné inicializaci a přechodu do stavu READY začne znovu zpracovávat požadavky všech front.
65
4.3 Propojení GSM ovladače a integrace do JCA konektoru
Obrázek 14: Class diagram JCA konektoru Nyní nezbývá než pomocné třídy a GsmModem ovladač zaintegrovat do formy JCA konektoru. Do API projektu tedy byly dány následující třídy:
66
Sms (z GsmModem) – reprezentuje SMS zprávu VoiceTransaction (z GsmModem) – reprezentuje hlasovou transakci GsmListener – notifikační rozhraní pro události z GSM. Definované jsou následující události: •
eventSmsDelivered (Sms sms) – voláno při úspěšném doručení dané SMS (která byla předtím zařazena do fronty)
•
eventReceivedSms (Sms sms) – přijata příchozí SMS. SMS neobsahuje v takovém případě ID (nemá smysl)
•
eventOutgoingCallStateChange (VoiceTransaction va) – změna stavu hovoru
GsmConnection – rozhraní pro spojení v JCA, poskytující následující služby: •
addSms (String phone, String body) – přidá SMS do fronty pro odesílání. Fronta je neperzistentní. Vrací ID požadavku (int)
•
call (String phone) – přidá požadavek na vytočení daného čísla. Vrací ID požadavku (int).
GsmConnectionFactory -rozhraní pro vytváření spojení typu GsmConnection •
Tento objekt je automatický získáván z aplikace pomocí resource injection (jedná se o mechanismus pro inverzi řízení IoC v Java Enterprise – viz [J2EETUT] a [JEE6])
•
Pomocí metody getConnection získáme poté fyzické spojení do konektoru
•
Teoreticky bychom mohli provést „odklon“ od specifikace a metody pro řízení GSM modemu implementovat rovnou v tomto rozhraní
Implementace samotného JCA konektoru bude obsahovat následující části:
4.3.1
Outbound spojení
GsmManagedConnectionFactory
–
factory
třída
pro
vytváření
instancí
GsmManagedConnection (implementující spravované spojení pro aplikační server) 67
GsmCciConnectionFactory – Connection factory třída pro normální (klientská) spojení GsmConnectionImpl – implementace spojení a rozhraní GsmConnection Pro správnou funkci musíme v ra.xml deskriptoru tyto prostředky nastavit (jinak je nebude umět aplikační server používat):
68
<managedconnectionfactory-class> cz.palat.gsm.connector.adapter.spi.GsmManagedConnectionFactory comPort java.lang.String /dev/ttyUSB0 javax.resource.cci.ConnectionFactory cz.palat.gsm.connector.adapter.cci.GsmCciConnectionFactory cz.palat.gsm.connector.GsmConnection cz.palat.gsm.connector.adapter.cci.GsmConnectionImpl NoTransaction BasicPassword javax.resource.spi.security.PasswordCredential false
69
V connection definition se nám objevuje tag config-property – pomocí tohoto tagu specifikujeme, že aplikační server má nabídnout konfiguraci tohoto parametru nad managed connection factory. Aplikační server zde automaticky zavolá set metodu (v našem případě setComPort (String v)) a předá nastavené parametry při nasazení / startu. Tím je zajištěna možnost konfigurace konektoru v konkrétním aplikačním serveru. Můžeme samozřejmě definovat libovolný počet bloků config-property (kolik jich naše aplikace potřebuje). transaction-support je též povinným atributem každého konektoru a specifikuje úroveň izolace transakce – zde specifikujeme, že náš konektor nepodporuje transakce (ale stejně musíme implementovat spoustu metod pro práci s transakcí – jedná se pouze o informaci o sémantice chování pro aplikační server). Podobně authentication-mechanism specifikuje formu ověření identity uživatele – v našem konektoru nemá žádné použití, proto specifikujeme BasicPassword jako univerzální základní mechanismus autentizace uživatele (BasicPassword je definován v [JCA] jako standardní hodnota v XML deskriptoru, pokud konektor nechce mít k dispozici vlastní sofistikovaný mechanismus přihlášení, případně nechce podporovat přihlášení pomocí certifikátů). GsmManagedConnectionFactory Implementuje
standardní
rozhraní
ManagedConnectionFactory
ze
SPI.
ManagedConnectionFactory je asi nejdůležitější třídou celého konektoru. Zde
vytváříme
ConnectionFactory
pro
klientská
spojení
–
metoda
createConnectionFactory
4.3.2
Inbound spojení
GsmResourceAdapter
–
implementuje
rozhraní
ResourceAdapter
a v metodě
endpointActivation spouští objekt typu GsmWork GsmWork – provádí práci na pozadí. Work ve své hlavní smyčce pouze spí, neprovádí
70
žádnou další užitečnou činnost. GsmWork se ovšem při svém vytvoření zaregistruje jako listener nad modemem (typ rozhraní GsmEventListener) a tudíž mu jsou doručovány
asynchronní
události.
Pokud
je
událost
doručena,
tak
pomocí
MessageEndpointFactory vytvoří objekt typu MessageEndpoint, přetypuje ho na GsmListener a provede příslušnou notifikaci. Bylo by možné teoreticky sjednotit rozhraní GsmEventListener (používané přímo pro modem) a GsmListener (používané pouze pro JCA), ale z hlediska oddělení API od implementace se to nejeví jako přínosné. Inbound spojení je poté definováno v ra.xml deskriptoru následovně: <messageadapter> <messagelistener> <messagelistener-type>
cz.palat.gsm.connector.mdb.GsmListener cz.palat.gsm.connector.adapter.spi.GsmActivationSpec <required-config-property>
comPort
Za zmínku zde stojí nutnost specifikovat rozhraní typu příjemce událostí pro MDB bean a popis všech properties. Tím se vytvoří mapování mezi MDB beany odpovídající této signatuře a jednotlivými instancemi resource adaptéru. Důležité je ovšem specifikovat i instanci ResourceAdapter (resp. třídu), která bude mít na starosti vytvoření Work objektu (a obecně lifecycle management):
71
cz.palat.gsm.connector.adapter.spi.GsmResourceAdapter
4.4 Nasazení a konfigurace konektoru Konektor
se nasadí
standardním postupem – pomocí
okna „Applications“
v administračním rozhraní Glassfish provedeme nasazení konektoru, při němž nastavíme parametr konektoru comPort na požadovaný port (kde je umístěn modem G24). Poté provedeme následnou konfiguraci: •
Vytvoříme pro konektor connection pool GsmPool s parametry: ◦
•
Počáteční počet spojení = 1
Vytvoříme JNDI prostředek Gsm vázaný na GsmPool. Tento prostředek budeme používat z klientské části.
4.5 Implementace ukázkového klienta Ukázkový klient je implementován pomocí webových služeb – mohl by sloužit jako integrační článek pro externí subsystémy či příklad řešení integrace v platformě J2EE. Webové služby byly zvoleny schválně, protože ukazují postup integrace nejjednodušším možným způsobem – bez zbytečné omáčky jakou by měla např. webová prezentační vrstva. Postup integrace webového rozhraní by byl ovšem velmi podobný – stejně tak by se dalo GSM integrovat např. s JMS sběrnicí atd. Klient by měl demonstrovat následující:
72
•
Zadání odchozího požadavku konektoru (např. odchozí SMS)
•
Příjem asynchronní notifikace od JCA konektoru (např. dokončení požadavku)
Obrázek 15: Class diagram ukázkového klienta Základem klienta jsou následující komponenty: •
GsmWebService – implementuje webovou službu. Webová služba implementuje požadavky na odeslání SMS či volání
•
GsmMdbBean – implementuje message driven bean – k němu přicházejí notifikace z konektoru.
•
GsmClientSingleton – třída ukládá informace o aktuálním stavu. Tyto informace zde
automaticky
aktualizuje
GsmMdbBean
(který
získá
instanci
GsmClientSingleton pomocí resource injection). Použití tohoto singletonu je nutné pouze z důvodu experimentálního web service klienta, který je z podstaty WS jednosměrný. Nicméně notifikace „plnohodnotně“ přijde asynchronně a v singleton beanu si ji pouze uložíme, až ji bude webová služba pollovat. Vazba mezi GsmWeb Service a JCA konektoru je vytvořena pomocí JNDI prostředku, který je nakonfigurován v aplikačním serveru v době nasazení konektoru. Pomocí resource injection získáme factory třídu pro spojení (samozřejmě pouze klientské, managed spojení jsou spravována aplikačním serverem):
73
@Resource (name = "Gsm", mappedName = "Gsm") private GsmConnectionFactory gsmConnection;
V metodách webové služby poté vždy získáme spojení pomocí metody getConnection (), což nám vrátí klientský objekt spojení. Poté můžeme provést volání jednotlivých metod spojení. U GsbMdbBean je vazba k správnému konektoru dána touto anotací: @MessageDriven (messageListenerInterface = GsmListener.class, activationConfig = { @ActivationConfigProperty (propertyName = "comPort", propertyValue = "/dev/ttyUSB0"),}) public class GsmMdbBean implements GsmListener {
Tím říkáme, že se jedná o MDB bean (anotace @MessageDriven) pro notifikační rozhraní GsmListener. Po nasazení klienta již začne automaticky práce s GSM modemem, kde uvidíme v logu, jak modem prochází svojí inicializací. Pokud je konektor špatně nasazen, tak se neprovede ani nasazení klienta a proces selže kvůli nemožnosti získat prostředky přes resource injection.
74
5 Závěr V práci se mi podařilo prozkoumat několik principiálních možností pro připojení externích zařízení a subsystémů v rámci prostředí Java EE. Po zvážení různých možností se zvolila cesta implementace vlastního JCA konektoru, který poskytuje nejlepší možnost integrace cizího systému a zařízení do prostředí J2EE, jeho zásadní výhodou je možnost obousměrné komunikace a notifikace. Směrem k zařízení (outbound
směr)
používá
implementace
standardní
spojení
získané
z DataSource/ConnectionFactory (ne nepodobně jako je tomu u databázových spojení), kde poté nad spojením může aplikace předávat požadavky pro zařízení. I přesto, že zařízení z principu nepodporuje více než jedno spojení a že není účelné ho pro každé spojení zvlášť inicializovat a blokovat pro ostatní, se abstrakce s použitím spojení ukázala jako neomezující a poměrně pohodlná pro klientskou aplikaci pro odeslání požadavku do zařízení. Zpětný směr je poté v JCA realizován pomocí tzv. inbound contractu, který v důsledku zajistí volání message driven beanu pro příslušné zařízení, kde daný bean implementuje specifikované rozhraní a konfiguraci. Synchronizace mezi regulérním session beanem (outgoing) a message driven beany (inbound) je možné realizovat poměrně triviálně standardními prostředky J2EE – jako úplně nejjednodušší se jeví použití Singleton beanu z Java EE 6 (standardní session EJB a MDB poté využívají stejnou, sdílenou a synchronizovanou instanci). Funkčnost celého řešení a koncepce byla demonstrována na jednoduchém JCA konektoru nasazeném v aplikačním serveru Glassfish, k PC byl připojen standardní modem Motorola G24. Lze konstatovat, že navržené řešení může být snadno aplikované i pro jiné modemy či podobná zařízení. Cíl práce se tedy podařilo naplnit.
75
Citace [UCON] Bureš, Tomáš and Hala, Josef and Petr Hnětynka: Using Connectors to Address Transparent Distribution in Enterprise Systems – Pitfalls and Options. 2009. ISBN: 978-3-642-01202-0 [SAPJ] Yang, Jitao and Su, Hongqi and Wu, Yuanfeng and Liu, Junwei: Enterprise Java Applications and SAP R/3 System Integration Using JCO. 2008. ISBN: 978-0-38775901-2. [JCA] J2EE Connector Architecture: [online]. 2012, [cit 2012-01-20]. http://java.sun.com/j2ee/connector/ [EHCI] EHCI Specification [online], 2012. [cit 2012-01-25]. URL: http://www.intel.com/technology/usb/ehcispec.htm [HW_G24] GSM moduly Motorola G24 – Maximální výbava, maximální spolehlivost | HW.cz: [online]. 2012, [cit 2012-01-20]. URL: http://www.hw.cz/produkty/gsm-moduly-motorola-g24-maximalni-vybava-maximalni-s polehlivost.html [MOT_G24_1] G24-JAVA – Motorola Solutions USA [online]. 2012, [cit 2012-01-20]. URL: http://www.motorola.com/Business/US-EN/Business+Product+and+Services/M2M+Wi reless+Modules/G24+JAVA_US-EN [MOT_G24_2] G24-LITE – Motorola Solutions USA [online]. 2012, [cit 2012-01-20]. URL: http://www.motorola.com/Business/US-EN/Business+Product+and+Services/M2M+Wi reless+Modules/G24+Lite_US-EN [MOT_G24_3] G24-EDGE – Motorola Solutions USA [online]. 2012, [cit 2012-01-20]. URL: http://www.motorola.com/Business/US-EN/Business+Product+and+Services/M2M+Wi reless+Modules/G24+Edge_US-EN#specs_tab
76
[MOT_AT] G24-EDGE – Reference AT commands [online]. 2012, [cit 2012-01-20]. URL: http://www.motorola.com/web/Business/Products/M2M%20Wireless %20Modules/G24%20Lite/_Documents/static %20files/AT_Commands_Reference_Manual2.pdf [EJB] Enterprise JavaBeans API 3.0 Final Release [online]. 2012, [cit 2012-01-30]. URL: http://download.oracle.com/otndocs/jcp/ejb-3_0-fr-api-oth-JSpec/ [LINSUSP] Device and Bus Power Management [online, 2012. [cit 2012-01-30]. URL: http://www.lesswatts.org/projects/devices-power-management/usb.php [JEE6] Antonio Goncalves: Beginning Java™ EE 6 Platform with GlassFish™ 3. 2010. ISBN: 978-1-4302-2890-5_1 [JDBC] JSR-000221 JDBC 4.0 [online]. 2012, [cit 2012-02-20]. URL: http://jcp.org/aboutJava/communityprocess/final/jsr221/index.html [GLASSFISH]
Glassfish [online]. 2009, [cit 2009-05-20]. URL:
https://glassfish.dev.java.net/ [JB]
JBoss [online]. 2012, [cit 2012-01-20]. URL: http://www.jboss.org
[IWS] IBM WebSphere [online]. 2012, [cit 2012-01-20]. URL: http://www.ibm.com [OWL]Oracle WebLogic [online]. 2012, [cit 2012-01-20]. URL: http://www.oracle.com/technology/products/weblogic/index.html [OC] Oracle Containers for Java EE [online]. 2012, [cit 2012-01-20]. URL: http://www.oracle.com/technology/tech/java/oc4j/index.html [SNW]SAP NetWeaver [online]. 2012, [cit 2012-01-20]. URL: http://www.sap.com/cz/platform/netweaver/index.epx [JSE] Java SE technologies – Database [online]. 2012, [cit 2012-01-20]. URL: http://java.sun.com/javase/technologies/database/ [J2EETUT] Jendrock, E.; Ball, J.; Carson, D.; aj.: The Java EE 5 Tutorial. Prentice Hall, 2006. ISBN 0321490290 [CORBA] Common Object Request Broker Architecture (CORBA/IIOP) [online]. 2012, [cit 2012-04-1]. URL: http://www.omg.org/spec/CORBA
77
Seznam obrázků Obrázek 1: Modul G24-Edge..........................................................................................15 Obrázek 2: Připojení modulu přes UART [MOT_G24_1]..............................................17 Obrázek 3: USB připojení [MOT_G24_1]......................................................................17 Obrázek 4: Struktura AT příkazů – viz [AT]....................................................................23 Obrázek 5: Stavový diagram hovoru – dle [AT]..............................................................26 Obrázek 6: Návrh architektury: Síťový server................................................................35 Obrázek 7: Návrh architektury: RMI server....................................................................36 Obrázek 8: Návrh architektury: Aplikační klient jako řídící server................................38 Obrázek 9: Návrh architektury: Propojení pomocí JCA connectoru...............................40 Obrázek 10: Struktura JCA connecotoru – dle [JCA].....................................................41 Obrázek 11: Interakce spojení v aplikačním serveru (převzato z [JCA])........................44 Obrázek 12: Class diagram ovladače GSM modemu......................................................55 Obrázek 13: Stavový diagram řízení modemu G24........................................................61 Obrázek 14: Class diagram JCA konektoru.....................................................................66 Obrázek 15: Class diagram ukázkového klienta..............................................................73
78
Seznam zkratek
J2EE JMS G24 JCA UART AT RMI GSM USB CORBA IIOP EJB MDB
Java Enterprise Edition Java Messaging Service Modem Motorola G24 Java Connector Architecture Universal Asynchronous Receiver/Transmitter Druh příkazu pro řízení modemu Remote Method Invocation Global System for Mobile Communications Universal Serial Bus Common Object Request Broker Architecture Internet Inter-ORB Protocol Enterprise Java Beans Message Driven Bean
79