M A S A R Y K O V A U N I V E R Z I T A F A K U L T A I N F O R M A T I K Y Autobusový rezervační systém obsluhovaný prostřednictvím SMS B A K A L Á R S K A PRÁCE Tomáš Bezdek Brno, jaro 2010 Prohlášení Prohlašuji, že tato bakalářská práce je mým původním autorským dílem, které jsem vypracoval samostatní?. Všechny zdroje, prameny a literaturu, které jsem pri vypracování používal nebo z nich cerpal, v práci rádne cituji s uvedením úplného odkazu na příslušný zdroj. Vedoucí práce: doc. PhDr. Josef Prokeš, Ph.D ii Shrnutí Cílem BP je naprogramování systému pro správu rezervací na autobusových linkách Student Agency, obsluhovaného prostrednictvím SMS zpráv. Systém bude uživatelům umožnovat vytvárení, rušení a získání prehledu o aktuálních rezervacích na kreditových, otevřených a predplacených jízdenkách. Dále umožní vyhledávání a z'ištení obsazenosti spojil obsluhovaných dopravcem. Soucástí systému bude modul, který klientum umožní hodnocení autobusu, ridicu a stevardek ve spoji, jímž byli prepravováni. Pro vnitrní potreby firmy bude systém umeřt rozpoznávat a zpracovat SMS o závadách na autobusech. Stevardkám systém umožní získání prřehledu o aktuální obsazenosti autobusu. iii Klíčová slova parser, SMS, Java, Guice, SA iv Obsah 1 Úvod 1 2 Analýza původního systému 2 2.1 Databáze rezervačních systémů 2 2.1.1 Hlavní databázové funkce 2 2.1.2 Tabulky 4 2.2 Databáze SMS 5 2.3 Technologie 6 2.3.1 sms_gate 2.3.2 parser 7 2.4 Shrnutí 9 3 Návrh nového systému 10 3.1 Databázové schéma 10 3.2 Schéma aplikace 11 3.2.1 IMessage 12 3.2.2 Dispatcher 13 3.2.3 Moduly 13 3.2.3.1 Params 14 4 Implementace 15 4.1 Použité technologie a frameworky 15 4.1.1 Java 15 4.1.2 Inversion of Control 15 4.1.2.1 Dependency Injection 17 4.1.2.2 Google Guice 18 4.1.3 JDBC Template 19 4.1.3.1 Template method 20 4.1.4 NamedParameterStatement 21 4.2 Beh aplikace na serverů 22 4.2.1 Java Daemon 23 5 Záver 25 Literatura 26 Rejstrík 27 A CD 28 B Hlavní databázové funkce a datové typy rozdelené podle klonů 29 B.1 Brno 29 B.2 Liberec 32 B.3 Ostrava 36 B.4 Plzen 39 B.5 Slovensko 43 v Kapitola 1 Úvod Společnost Student Agency působí na ceském trhu od roku 1996. Zpočátku provozovala mezinárodní autobusovou dopravu do nekolika evropských zemí a v roce 2004 zahájila provoz na vnitrostátních linkách. První provozovaná linka byla na trase Praha-Brno, ke které se postupne pridávaly další, v soucasnosi je obsluhováno 8 linek. Svým klientum nabízí Student Agency nadstandardní služby nejen behem cesty ve svých autobusech, ale již pri nákupu jízdenky. Klienti si krome standardní jízdenky na pevné datum mohou zakoupit i takzvanou Otevrenou (Elektronickou), nebo Kreditovou jízdenku, které jsou vhodné pro klienty, kterí si nejsou jistí termínem odjezdu, nebo jezdí casteji a nechtejí si pokaždé kupovat jízdenku. Oba tyto typy jízdenek je možné rezervovat nejen pomocí Internetu, ale navíc také prostrednictvím SMS zasláním zprávy na císlo 736 333 999. SMS rezervacní systém byl uveden do provozu v roce 2006 a umožííoval pouze základní operace jako vytvárení a rušení rezervací (BUS, BUS ZRUS). Postupem casu se systém rozrostl o další klientské služby jako vyhledávání volných míst (BUS MISTA), nebo výpis rezervací a zustatku kreditu (BUS VYPIS). Systém se také zacal využívat pro vnitrní potreby autobusového oddelení, kdy si stewardky mohou nechat zasílat aktuální obsazení autobusu (BUS HELP), informovat centrálu o zpoždění, nebo poruchách v autobuse (BUS LATE, BUS ZAVADA). Přidáváním funkcionality a díky nekolika ruzným systémum spravujícím jednotlivé linky se z puvodne jednoduchého systému stal témer neudržovatelný moloch, který pro programátora majícího za úkol provést i jednoduchou úpravu nebo opravit drobnou chybu znamenal nekolik hodin práce a spoustu nervu. Trešnickou na pomyslém dortu bylo i to, že systém nebylo možné žádným rozumným zpusobem ladit, a potrebné informace se získᬠvaly výpisem do systémového logu. S ohledem na predcházející odstavec, a z duvodu brzkého nasazení zcela nového rezervacřního systému, který není v žádném ohledu kompatibilní se soucřasnými, bylo roz¬ hodnuto, že je trřeba celý systém zanalyzovat a prřepsat tak, aby byl snáze udržovatelný, prřehledný a do budoucna rozširřitelný. 1 Kapitola 2 Analýza původního systému Rezervační systém Student Agency se skládá z 5 podobných, avšak nezávisých systému - tzv. klonu. Tyto klony vznikaly z původního rezervačního systému pro linku Praha-Brno (nadále Brnenský systém) a postupem času mezi nimi začaly vznikat drobné odlišnosti v závislosti na rozdílných požadavcích na jednotlivé linky. Po vytvorení 5. klonu začaly být systémy neudržovatelné, protože každá zmena, kterou bylo potreba provést na všech linkách, se musela petkrát naprogramovat a otestovat. Proto byl při vzniku 6. linky upraven puvodní Brnenský systém tak, aby v nem bylo možné vytváret další linky. 4 již existující klony (Liberecký, Plzenský, Ostravský a Slovensko) ale zustaly, a odlišnosti mezi nimi a Brnenským systémem se tak neřkolikanásobneř zveřtšily. 2.1 Databáze rezervačních systému Všechny rezervační systémy (klony) jsou postaveny na relační databázi PostgreSQL, na kterou jsou napojené další klientské systémy jako je web, internetová kavárna nebo SMS brána, a systémy používané zamestnanci Student Agency, jako je webová aplikace BusAdmin, nebo desktopový rezervační systém používaný prodejci na stáncích - Yellow Project (YP). Každá ze zmínených aplikací je naprogramována v jiném jazyce (web, BusAdmin a SMS brána jsou v PHP, kavárna v Javí? a Yellow Project je v Delphi) Kvuli používání tolika rozmanitých jazyku je nutné, aby aplikační logika byla obsažena na místeř spolecřném pro všechny subsystémy, kterým je v tomto prřípadeř databáze. Apli¬ kační logika je postavená na jazyce PL/PgSQL a je realizována prostrednictvím triggeru a uložených procedur. Toto rřešení má své nesporné výhody zejména v rychlosti provádeřných operací, vzájemné nezávislosti subsystému a možnosti používat ruzné programovací jazyky, avšak co se týcře udržovatelnosti a programátorské prříveřtivosti, tak znacřneř pokul¬ hává. Problémem je, krome chybející dokumentace, kterou však nepovažuji za životne duležitou, hlavne nemožnost použít jakékoliv pomucky, které v dnešní dobe obsahuje snad každé použitelné vývojové prostředí, jako je napríklad refaktorování, nebo dohledání, odkud se která procedura či funkce volá, a bez kterých se programátor jen težko obejde. 2.1.1 Hlavní databázové funkce Vetšina aplikační logiky je obsažena v nekolika funkcích. Mezi nejduležitejší patrí ty, které se starají o vytvárení a rušení rezervací, vyhledávání spojiä a platby za lístky. Krome techto 2 2.1. DATABÁZE R E Z E R V A (ŽNÍCH SYSTÉMŮ hlavních je v systému spousta podpůrných funkcí používaných pro generování čárových kódu, vyhledávání informací o spojích, recyklaci lístku a podobne. Tyto funkce mají v každém klonu stejný název, avšak pocet parametru nebo jejich výstupy se mnohdy liší. V dalších odstavcích, popisujících jednotlivé funkce, zmíním pouze nejzásadnejší rozdíly ve funkcích mezi klony, podrobnejší popis se pak nachází v príloze B. V SMS rezervacním systému se používá 5 hlavních funkcí: create_rezervace funkce pro vytvárení rezervací. Jejím úkolem je provést sadu kontrol - napríklad zda klient muže na daném míste sedet nebo zda je lístek ješte platný, poté zarezervovat požadované místo a strhnout z úctu cástku za rezervaci. Vstupní pa¬ rametry se již v této základní funkci mezi jednotlivýmy systémy liší, rozdíly však naštestí nejsou nijak zásadní. Návratovou hodnotou funkce je prázdný retezec, pokud byla rezervace úspešná, nebo chybová hláška. erase_rezervace funkce pro rušení rezervací. Kontroluje, zda již rezervace nebyla využita v kavárne, a zda je možné ji ješte zrušit (napr. jestli již spojneodjel). Vstupní parametry jsou podobní? jako u c r e a t e _ r e z e r v a c e analogické, avšak mají jednu nepeknou zvláštnost, a tou je parametr, který funkci ríká, zda byla volána klientem z online rezervacního systému (web nebo SMS). Ve funkci c r e a t e _ r e z e r v a c e se tento parametr jmenuje i n e t a nabývá hodnoty t r u e , pokud je funkce volána z on¬ line systému. Ve funkci e r a s e _ r e z e r v a c e se parametr jmenuje nenet a nabývá hodnot opacných, což muže být casto matoucí. Bohužel se mi nepodarilo zjistit, proc tato zvláštnost vznikla. Stejne jako funkce c r e a t e _ r e z e r v a c e vrací i tato prázný řetezec, pokud bylo zrušení úspešné, nebo chybovou hlášku v opacném prípade. get_rezervace funkce pro vyhledání rezervací na lístku. Funkce vrací seznam rezervací na lístku (pro Pevnou a Elektronickou jízdenku vždy jeden rádek, pro Kreditovou jíz¬ denku historii všech provedených rezervací). Toto je první funkce, která má ve všech klonech stejné vstupní parametry - id lístku, avšak o to veřtší chaos je v návratových hodnotách. Funkce vrací datový typ t _ r e z e r v a c e . Jeho základní atributy (id re¬ zervace, odkud a kam cestující jede a datum odjezdu) jsou stejné, avšak další jako cena, p o s i l a , happy nebo byznys mezi klony chybí, nebo jsou prřeházené. get_first_spoj funkce pro vyhledání prvního spoje jedoucího od zadaného casu požadova¬ ným smeřrem. Tato funkce má jako jediná z vyjmenovaných stejné vstupní parametry (odkud, kam, datum, cas) i návratovou hodnotu (id spoje). w_spoj_vyhledat funkce pro vyhledání spojil jedoucích v zadaný den požadovaným smerem. Co se týce vstupních parametru a návratových hodnot, je funkce velice po- 3 2.1. DATABÁZE R E Z E R V A (ŽNÍCH SYSTÉMŮ dobná funkci g e t _ r e z e r v a c e . Ve všech klonech má stejné tři vstupní parametry, avšak návratový typ t _ w _ s p o j _ v y h l e d a t je znaCne odlišný. 2.1.2 Tabulky Přestože je snaha umisťovat hlavní aplikaCní logiku do databázových funkcí, není to vždy možné nebo optimální. Příkladem jsou dotazy specifické pro danou aplikaci, které se v ostatních nevyužijí a pouze by zneprehlednovaly seznam funkcí. Proto je pro nekteré úkony potrebné pouštet standardní SQL dotazy prímo nad tabulkami. V každém klonu se nachází okolo 70-100 tabulek, v závislosti na rozvinutosti klonu a funkcionalite, kterou obsahuje. Zjednodušené schéma podobné pro všechny klony popisuje obrázek 2.1, podrobná databázová schémata jsou dostupná v příloze A ve forme? SQL dotazu, pomocí kterých lze databáze vytvorit. Podobne jako funkce jsou i tabulky casto pomerne rozdílné, nejvíce jsou rozdíly poznat na Brneřnském klonu, který je upraven tak, aby mohl sdružovat neřkolik linek dohro¬ mady a jsou v neřm implementovány mnohá další vylepšení, která jsou v ostatních klonech rešena raznými hacky1 (napríklad pridávání posilových autobusu do spoju, popsané dále). Základní tabulky používané v SMS rezervacním systému jsou: listek zde se uchovávají informace o lístku (Elektronickém, na pevné datum i Kreditové jízdence). Hlavními údaji v tabulce jsou: kredit, cárový kód, typ lístku, stav (otevrřený, rezervovaný, stornovaný, zrušený, karta), tarif a v prřípadeř Elektronického lístku nebo Kreditové jízdenky také osobní údaje klienta. Tato tabulka je ve všech systémech stejná, výjimku tvorří Slovenský klon, který obsahuje ješteř kredit v eurech. Lístky na slovenském systému totiž mohou mít kredit v cřeských korunách a zárovenř v eurech, prři strhávání kreditu za rezervaci se chybeřjící kredit v ( Z K prřepocřítává na EŮR a zbytek cřástky se strhává v EŮR. rezervace tabulka obsahuje údaje o rezervaci - id lístku, id rezervace, cenu a zastávky od¬ kud kam klient jede. Tabulka je ve všech systémech stejná, výjimku opeřt tvorří Slo¬ venský systém a kredit v eurech. rad tato tabulka sama o sobeř obsahuje pouze nic nerříkající cřas odjezdu spoje z výchozí zastávky. N a tabulku jsou však navázané další, které se využívají zejména prři ge¬ nerování spoju ( r a d _ p l a t n o s t , rad_vyjimka), a hlavne tabulka r a d _ z a s t a v k a obsahující informace o zastávkách, kterými spoj projíždí a cřasech, kdy se v dané za¬ stávce nachází. (ř as je uložen jako rozdíl mezi cřasem odjezdu z výchozí zastávky 1. Slovíčko „hack" je zde myšleno jako narychlo vytvořená a do detailu nepromyšlená úprava existujícího systému. Viz 4 2.2. DATABÁZE SMS časem odjezdu ze zastávky na trase - tzv. delta. Výchozí zastávka má deltu 0. Výjimkou je Liberecký systém, který v tomto ohledu dodnes kopíruje prapůvodní Brnenský systém, kde byly v dobe, kdy se jezdilo pouze mezi dvema zastávkami, informace o odjezdech a prijezdech uloženy pomocí atributu cas1, cas2 a smer. Tyto atributy ríkaly, kdy spojodjíždí z první a prijíždí do druhé zastávky, informace která zastávka je první byla vyádrena pomocí smeru. spoj obsahuje informace o spojích, obsahuje datum odjezdu, id rádu, podle kterého byl nagenerován, a informace o hlavním, prípadne? posilovém autobuse, který spojobsluhuje. Pokud je posil více, jsou v systému speciální typy autobusu, kterým se říká násobné. Jejich princip spocívá v tom, že násobný posilový autobus obsahuje pro první posilu standardní místa 1-n, místa v dalších posilách jsou pak oddeleny stovkami (201-2xx, 301-3xx). Specialitou je Brnenský systém, kde je krome tabulky s p o j , ve které jsou v ostatních systémech uloženy informace o autobuse a posilovém au¬ tobuse, který daný spojobsluhuje, tabulka sezbus, která je vazební tabulkou mezi spojem a autobusy, které jej obsluhují. To, zda se jedná o hlavní nebo posilový auto¬ bus, urcuje atribut p o s i l a , který má pro hlavní bus hodnotu 0, pro posily potom 1,2,3,.... misto rádky v této tabulce vznikají pri generování spojil na základe nadefinovaného typu autobusu a slouží hlavneř jako vazební tabulka mezi spojem, rezervací a sedadlem ve autobuse. Obsahuje také informaci o tom, zda se jedná o sedadlo v hlavním, nebo posilovém autobuse. V témeřrř všech klonech jsou místa v posile znacřena pouze jako t r u e / f a l s e , výjimkou je Brnenský systém, kde jsou místa navázána na tabulku sezbus. Pro z'ištení, do které posily sedadlo patrí, je potom nutné navázat si tuto tabulku. 2.2 Databáze SMS Celá databáze se skládá pouze ze dvou jednoduchých tabulek - sms_in a sms_out a slouží jako komunikacřní bod mezi dveřmi nezávislými cřástmi celého systému. Zárovenř se také po¬ užívá pro vyhledávání v prijatých a odeslaných zprávách v sekci SMS Brnenského BusAd- minu. sms_in slouží pro uchovávání přijatých SMS zpráv. Jejími atributy jsou i d - automaticky generovaný identifikátor sloužící mimo jiné pro svázání s odchozími sms, sender telefonní císlo odesilatele v mezinárodním formátu, t x t - text zprávy, s t a t u s - její stav (0 - nezpracováno, 1 - zpracováno, 9 - rozpracováno , -1 - chyba pri zpracování), t i m e _ i n s e r t -casprijetí zprávy, time_update -cas dokoncení zpracování zprávy. 5 2.3. T E C H N O L O G I E Obrázek 2.1: Zjednodušené schéma databáze sms_out slouží pro uchovávání odeslaných SMS zpráv. Jejímy atributy jsou i d - automaticky generovaný identifikátor, r e c e i v e r - telefonní císlo príjemce v mezinárodním formátu, t x t - text zprávy, s t a t u s - její stav (0 - neodesláno, 1 - odesláno), i d _ i n - vazební atribut do tabulky sms_in, t i m e _ i n s e r t - cas odeslání zprávy, time_update - cas dokoncení odesílání zprávy. 2.3 Technologie Puvodní SMS rezervacní systém se skládá ze dvou cástí. Tou první je malý program sms_gate napsaný v jazyce C, který sloužíjako prostredník. Na jedné strane komunikuje s databází, do které ukládá prříchozí zprávy a nacřítá odpoveřdi, na druhé straneř komunikuje prřes V P N se 6 2.3. T E C H N O L O G I E servery spolecnosti T-Mobile, ze kterých príjmá a odesílá odpovedi. Nezávisle na sms_gate beží na serveru aplikace - parser - napsaný v jazyce PHP, který se stará o nacítání dosud nezpracovaných zpráv z databáze, jejich zparsování, provedení požadovaných úkonu a vytvorření odpoveřdi, kterou opeřt uloží do databáze, odkud ji sms_gate odesílá. 2.3.1 sms_gate Tento program zastává v celém SMS rezervacním systému pomerne malou, o to však duležitejší roli. Pro jeho beh je potreba mít na serveru spusteného V P N daemona2 , který zajišuje spojení do privátní síteř spolecřnosti T-Mobile a umožnřuje komunikaci s jejich servery. Se vzdáleným serverem majícím na starost zpracování a prřeposílání SMS komunikuje program pomocí knihovny LibEMI[10], která umožnuje komunikaci prostrednictvím protokolu EMI (External Machine Interface) 3 . EMI je modifikací protokolu ERMES UCP obsahující specifická rozšírení pro komunikaci s SMS centry GSM operátorů. Telem sms_gate je nekonecná smycka, která se pri každé iteraci pokusí nejprve príjmout a poté odeslat nahromadeřné SMS. Příjem SMS je realizován funkcí e m i _ s e l e c t ( ) z knihovny LibEMI. Pokud je SMS správne prijata, následuje její zápis do databáze sms, konkrétní? do tabulky sms_in. Krome údaju jako císlo odesilatele, textu zprávy a casu prijetí je zapsán i parametr s t a t u s , nastavený na hodnotu 0, který indikuje prijaté a parserem dosud nezpracované SMS. Odeslání SMS je opacný proces, kdy se sms_gate kouká do databáze do tabulky sms_out na rádky, které mají atribut s t a t u s nastaven na 0, pokusí se je odeslat a v databázi je oznací jako odeslané zmenou atributu s t a t u s na 1. 2.3.2 parser Parser je sada 5 skriptu napsaných v jazyce PHP 4 (všechny jsou dostupné na CD v príloze A). Hlavní z nich (sms.php) je periodicky každou minutu spoušten Cronem 5 a zajišťuje postupné vykonávání dalších skriptu. sms.php První ze skriptu, obsahuje 139 rádek kódu a slouží pouze pro nactení prijatých SMS z databáze a jejich iniciální kontrolu. Teřlo skriptu tvorří smycřka, která beřží v cyklu 1 minutu a pri každé iteraci nacte jednu nezpracovanou zprávu. Zkontro¬ luje, zda zpráva zacříná na jedno z klícřových slov, a rozhodne, zda ji poslat k dalšímu zpracování, nebo uživateli vrátit chybovou SMS zprávu. sms_lib.php Velikostí 1311 rádku kódu se jedná o druhý nejvetší soubor. Samotný skript nic 2. < h t t p : / / c s . w i k i p e d i a . o r g / w i k i / V i r t u a l n i _ p r i v a t n i _ s i t ' > 3. 4. . 5. . 7 2.3. T E C H N O L O G I E nedělá, obsahuje pouze funkce, které se často používají na různých místech v dalších skriptech. sms_typ.php Pokud function.php rozhodne, že skript predá k dalšímu zpracování, dostává se ke slovu tento skript. Jeho velikost je pouhých 78 rádku kódu a jeho jedinou funkcí je zpracovat další cást prijatého textu. Rozhoduje, zda následující klícové slovo odpovídá jednomu z definovaných, a poté predává rízení funkci tomuto slovu odpovídající ze skriptu sms_overeni.php, pnpadne stejne jako sms.php vrací chybovou zprávu. sms_overeni.php 920 rádku kódu v 10 funkcích se stará o finální zpracování zbytku textu zprávy. Pokud se mu podarí precíst a zvalidovat všechny parametry vyžadované daným typem zprávy, volá funkci ze skriptu sms_vykon.php s již konkrétními parametry, jako jsou napríklad datum, cas, kód rezervace, identifikátor odjezdová/príjezdové zastávky a podobne. sms_vykon.php Nejvetší a také nejneprehlednejší ze všech skriptu se sestává z témer 1750 řádek kódu a zajišťuje finální zpracování SMS zprávy. Rezervuje, ruší rezervace, vyhledává v jízdních rádech nebo zjišťuje volná místa ve spojích. Vzhledem k použitým technologiím (PHP a uložené funkce v 5 PostgreSQL databázích) a faktu, že kód parseru nebyl témer vubec okomentován, byla jakákoliv úprava nebo hledání chyby v parseru pomeřrneř nárocřnou operací. Ladeřní a krokování (debugging) PHP není jed¬ noduchou záležitostí ani v moderních vývojových prostredích, jako jsou Eclipse nebo NetBeans, a ješte pred nekolika lety, kdy jsem na této práci zacínal pracovat, byla podpora P H P v techto prostredích na velmi omezené úrovni. Debugování uložených procedur v PostgreSQL v té dobe dokonce nebylo ani možné6 . Jedinou možností, jak se tedy mohl programátor dostat k potrřebným hodnotám promeřnných nebo informaci do které veřtve se skript dostal, bylo jejich vypisování na konzoli nebo do souboru. Jednotlivé skripty mezi sebou žádným zpusobem (nepocítáme-li spolecnou sms_lib.php a predávání nekolika vstupních parametria) nespolupracovaly a data, která jeden skript zpracoval prřípadneř získal z databáze, byla v dalším skriptu nedostupná, a ten je musel získávat znovu. Z duvodu naprostého oddeleného zpracování a také proto, že jednotlivé funkce mnohdy nevedely, co mužou od klonu, se kterým zrovna pracují, ocekávat, byly skripty zaplneny mnoha if-then-else kontrukcemi a volaly spoustu zbytecných dotazu na databázi. 6. S 2.4. SHRNUTÍ 2.4 Shrnutí Po dukladném zvážení všech možností (zejména ve svetle skutecnosti, že v Jave neexistuje žádná rozumná a udržovaná implementace protokolu EMI) jsem se rozhodl, že zᬠkladní koncept dvou oddeřlených komponent komunikujících pomocí spolecřné databáze ponechám a budu se veřnovat pouze návrhu a implementaci vlastního parseru. 9 Kapitola 3 Návrh nového systému 3.1 Databázové schéma Databáze SMS, se dvema velmi podobnými tabulkami obsahujícími pouze nekolik sloupcu, plne postačuje svému účelu, a proto nebylo potreba v ní cokoliv menit. Naproti tomu struktura databáze rezervačního systému jednotlivých klonu ve vetšine prípadu již dávno neodpovídá požadavkum, které jsou na ni kladeny, avšak jakékoliv úpravy struktury techto databází ani koncepcřní úpravy uložených procedur nejsou možné. závisí na nich totiž velké množství aplikací a objem práce spotrřebovaný na jejich úpravu by byl mnohonásobneř veřtší, než návrh a implementace nového rezervacřního systému, který je již v soucřasnosti vyvíjen. Brnenský systém je, jak bylo rečeno v kapitole 2.1, nejpropracovanejší a snadno se z jeho struktury a uložených procedur zístávají dobrře zpracovatelná data 1 . Proto jsem tento sys¬ tém vybral jako referencřní, nad jeho strukturou jsem vystaveřl diagram základních P o J o 2 trříd parseru. POJO tríd je v parseru celkem 9, vazby mezi nimi jsou znázorneny na obrázku 3.1. Spoj trída reprezentující jeden spoj. Uchovává své datum a čas odjezdu a príjezdu, výchozí a cílovou zastávku, ID spoje, seznam míst a nekolik dalších údaju. Dále obsahuje mapu Sezbusů, neboli autobusu které spojobsluhují. Každý spojmusí mít alespon jeden Sezbus, v mape je identifikován číslem 0, které ríká, že jde o hlavní bus. Posilové busy jsou v mape identifikovány čísly od 1 výš. Sezbus reprezentuje jeden bus obsluhující daný spoj, nese si v sobeř seznam míst, cříslo busu, informaci o tom, zda je posilový a údaj o místenkovosti (zda je možné do neřj koupit místenku). Misto reprezentuje sedadlo ve spoji, jsou na neřm údaje o cřísle sedadla, i D místa v databázi a také je na neřj navázaná prřípadná rezervace. 1. Pri dotazu na číslo sedadla v posilovém autobuse ze spoje obsahujícího posilu není nutné počítat z obdrženého čísla zbytek po delení 100 pro získání čísla sedadla a hodnotu po celočíselném delení 100 pro získání čísla posily. 2. 10 3.2. SCHÉMA A P L I K A C E Rezervace vazební trřída mezi lístkem, spojem a místem. Listek trřída reprezentující lístek, otevrřenou i kreditovou jízdenku, nese v sobeř seznam re¬ zervací, údaje o majiteli lístku, cárový kód pro identifikaci lístku, kredit, stav lístku otevrený, rezervovaný - a pocet kilometru, v případe, že je klient zapojen do kilome¬ trového programu.3 N a lístku nemusí být uloženy vždy všechny údaje. Naprříklad v tom prřípadeř, když se nejedná o kreditovou jízdenku, nejsou vyplneřny údaje o ma¬ jiteli lístku. Zastavka je trřída reprezentující zastávku, prřes kterou se jezdí. Prřestože v databázi existuje ekvivalent této trídy, není z ní nacítána! Mezi klony mají totiž stejné zastávky razná ID a je proto nutné uchovávat údaje o nich stranou. Zastávka si v sobe nese identifi¬ kátor systému do kterého patrří, prřípadneř linky v tomto systému (v systémech Brno a Slovensko), seznam zkratek pro svou identifikaci, a seznam kodu (zkratek), se kterými se nesmí párovat - napríklad v Ostravském klonu jsou zastávky Brno a Praha, pres které spoje jezdí, avšak parser nesmí kombinaci techto zastávek najít v Ostrav¬ ském systému, protože tyto zastávky jsou obsluhovány systémem Brno. Zpozdeni slouží jen jako trřída pro ukládání informací o zpoždeřní, má vazbu pouze na spoj, kterého se týká. SmsIn, SmsOut jsou pouze ekvivalenty tabulek sms_in a sms_out z databáze SMS. Aby bylo možné ostatní klony napojit na POJO trídy parseru, bylo potreba nejakým z p ů sobem docílit toho, že uložené procedury a prřípadné SQL dotazy budou vracet vhodná data, aby nedocházelo ke zbytecnému volání dotazu na databázi. Protože úprava struktury ani procedur nebyla možná, došlo ve všech nebrneřnských klonech ke zduplikování velké cřásti základních procedur a datových struktur tak, aby jejich výsledky byly ekvivalentem brneřnských. Mnohdy meřly tyto nové procedury stejné vstupní parametrya a proto musely být pojmenovány s apendixem _new. 3.2 Schéma aplikace Základem aplikace je trída Application a její metoda run, která je spouštena v nekonecné smycce. Při každém prächodu si metoda z databáze vytáhne všechny nove příchozí zprávy (stav 0), které postupne zpracovává. Zpracování probíhá tak, že se zavolá metoda getResponses, 3. 11 3.2. SCHÉMA A P L I K A C E Obrázek 3.1: POJO schéma která vrátí seznam zpráv, instancí tríd implementujících rozhraní IMessage 3.2.2, které vznik¬ nou jako odpoveřd na SMS. N a tyto zprávy poté volá metodu send, kterou má každý poto¬ mek IMessage implementouvanou podle sebe. Aby parser vedel, jak má zprávu zpracovat, musí z ní metoda getResponses nejprve získat její typ - modul, implementující rozhraní IModul 3.2.3, který se stará o zpracování daného typu zprávy. K tomu slouží trída Dispatcher 3.2.2 a její jediná verejná metoda getKey. 3.2.1 IMessage Rozhraní IMessage definuje metody potrebné pro jednotlivé zprávy, které jsou z parseru odesílány. Parser totiž nepracuje pouze s SMS, ale musí být schopen zasílat také e-maily, prípadne vytváret úkoly v systému, který slouží pro zadávání úkolu ve Student Agency. Rozhraní definuje 4 typy operací ( s e t S u b j e c t , setMessage, addReciever, send). První trři jsou ve všech typech zpráv víceméneř stejné, pouze v neřkterých modulech nejsou nekteré nepotrebné implementovány - nar. v modulu Sms nenéí implementována metoda s e t S u b j e c t . Metoda send je specifická pro každý typ zprávy a je popsána níže. Sms trída zajišťuje odesílání SMS zpráv, má ponekud komplikovanejší medotu setMessage. Sms_gate totiž nedokáže odesílat dlouhé zprávy ani diakritiku. Proto musí být ode- 12 3.2. SCHÉMA A P L I K A C E sílaná zpráva nejprve zbavena diakritiky a poté rozsekána na úseky délky 150 znaku. Za každý rozseknutý úsek je poté prřipojen údaj o porřadovém cřísle zprávy z celko¬ vého pocřtu, pro lepší orientaci. Vlastní odeslání poté probíhá prostým ukládáním seznamu dílcích zpráv do databáze SMS (tabulka sms_out), pro každého nastave¬ ného prříjemce. Email odesílá ze systému emaily, nemá žádnou zvláštní konfiguraci. Todo vytvárří úkoly v úkolovníku. NoReply nemá implementovanou žádnou metodu, slouží pouze pro indikaci toho, že prříchozí zpráva byla úspeřšneř zpracována. 3.2.2 Dispatcher Trřída v sobeř obsahuje stromovou strukturu, kde v každém uzlu je uložen klícř a regulární výraz. Jediná verřejná metoda getKey dostane na vstup SMS, ze které postupneř získává jednotlivé tokeny zprávy, které v uzlech potovnává s regulárním výrazem. Pokud token odpovídá regulárnímu výrazu, volá rekurzivneř sama sebe, dokud nenarazí na odpovídající list. Nalezne-li list, vrací hodnotu jeho klíce, jinak vrací null. V každé vetvi muže být jeden uzel, který má místo regulárního výrazu hotnotu fallback a který je vracen namísto null v prřípadeř, že regulární výraz žádného uzlu neodpovídá tokenu. Na základe klíce uzlu je poté vyhledán modul, který zajišťuje další zpracování SMS. 3.2.3 Moduly Moduly jsou hlavní komponenty parseru starající se o vlastní zpracování zprávy a vytvo¬ rení odpovedi. Jsou to trídy implementující rozhraní IModule, které definuje dve metody: setKey, sloužící pro nastavení klíce, na jehož základe je modul použit, a getResponse, která se stará o vlastní zpracování zprávy. Jakmile je na základe nekolika prvních tokenu zprávy nalezen modul, predává se mu rízení a ze zbývajících tokenu se stávají parametry modulu. Parametry jsou v surové textové podobe předány potomkovi abstraktní trídy Params 3.2.3.1, kterého má každý modul vytvorřen a nastaven podle svých potrřeb. Po zpracování parametru si modul na základe nekterého z parametru (v závislosti na typu modulu) získá systém, se kterým bude pracovat, a zpracuje odpoved' pro odesilatele zprávy, prřípadneř více odpoveřdí, pokud jde o moduly zasílající naprříklad e-maily. 13 3.2. SCHÉMA A P L I K A C E 3.2.3.1 Params Pro každý modul existuje potomek této abstraktní trřídy, který zpracovává parametry prředané z modulu. Má dveř verřejné metody, setTokens a e v a l u a t e . První slouží k iniciali¬ zaci, jejími parametry jsou tokeny zbylé po vyhledání modulu a cříslo urcřující minimální pocet tokemí (parametrů), které má dostat. Po inicializaci se na trídu spouští metoda e v a l u a t e , kterou má každý potomek naimplementovanou zvlášt. Metoda je ve vetšine případu sesta¬ vena z prostého volání metod z abstraktní trřídy Params, které se využívají ve více modu¬ lech (napr. checkSeat), nebo vlastních naimplementovaných check-metod. Check-metody vezmou první nezpracovaný token, ten se pokusí zpracovat, výsledek uloží do promeřnné podle svého typu a výsledek zpracování vracejí jako boolean. N a základe návratové hodnoty z check-metod je možné cástecne rídit tok zpracování jednotlivých parametru. Pokud dojde ke kritické chybeř, naprříklad chybí další token ke zpracování, nemusí check¬ metoda vrátit boolean, ale vyhazuje ParameterException, kterou zachytává metoda getResponse a posílá odesilateli zprávy sms s informací, k jaké došlo chybeř. 14 Kapitola 4 Implementace 4.1 Použité technologie a frameworky 4.1.1 Java Java je objektove orientovaný programovací jazyk, který vyvinula firma Sun Microsystems a predstavila 23. kvetna 1995. [9] Je jedním z nejpoužívanejších programovacích jazyku na svete. Díky své prenositelnosti je používán pro programy, které mají pracovat na razných systémech pocínaje cipovými kartami (platforma JavaCard), pres mobilní telefony a razná zabudovaná zarízení (platforma Java ME), aplikace pro desktopové pocřítacře (platforma Java SE) až po rozsáhlé distribuo¬ vané systémy pracující na rade spolupracujících pocítacu rozprostrené po celém svete (platforma Java EE). Tyto technologie se jako celek nazývají platforma Java. Dne 8. kvetna 2007 Sun uvolnil zdrojové kódy Javy (cca 2,5 miliónu rádku kódu) a Java je dále vyvíjena jako open source. Java je vyspeřlý objektoveř orientovaný programovací jazyk, obsahující všechny vlast¬ nosti, které jsou vyžadovány v moderním programování, od modularity programu, rřídících konstrukcí, prřes silnou typovou kontrolu, vícevláknovost, ošetrření vyjímek, správu pameřti, i silnou podporu pro databáze, X M L a sít'ové operace. K jejím výhodám patrří kromeř již zmíneřné multiplatformity robustnost, škálovatelnost a vysoká bezpecnost, která jí profituje pro používání na kritické aplikace na mainfraimových pocřítacřích. Proti programovacím jazykum, které provádejí tzv. statickou kompilaci (napr. C++), je start programu psaných v Jave pomalejší, protože prostredí musí program nejprve preložit a potom teprve spustit. Je však možnost využít mechanismu JIT a HotSpot, kdy se casto provádeřné nebo neefektivní cřásti kódu prřeloží do strojového kódu a program se zrychlí. Na zrychlení se také podílí nové prístupy ke správe pameti (generacní správa pameti). [8] 4.1.2 Inversion of Control Inversion of Control (IoC), nebo také obrácené rřízení, je návrhový vzor, který umožnřuje uvolnit vztahy mezi jinak teřsneř svázanými komponentami. V klasickém modelu progra¬ mování vytvárříme neřjakou trřídu, která pak využívá další trřídy a tak dále. Tím jsou potom trřídy velmi pevneř svázány a zmeřna používané trřídy za jinou vyžaduje zásah do kódu. IoC umožnuje uvolnit tuto vazbu, což nejjednodušeji vystihuje Hollywoodský princip „Nevo- 15 4.1. POUŽITÉ T E C H N O L O G I E A F R A M E W O R K Y lejte nám, my se ozveme."1 . To je klasická odpoveď, kterou dostávají amatérští herci, kterí se snaží získat v Hollywoodu nejakou roli ve filmu, seriálu apod. Proto tedy Hollywoodský princip. V podstate to znamená, že trída nevytvárí sama instance dalších tríd, které potrebuje, ale jsou jí dodány nejakým zpuusobem z vnejšku. Techto zpiůsobiů existuje nekolik, nejznámejší jsou tri: Constructor Injection třídy, do nichž je vkládána instance další třídy, kterou potřebují, musejí mít vytvořen konstruktor, který umí přijímat potřebné typy objektu. Setter Injection třídy, do nichž jsou vkládány instance, musí mít definovány potřebné settery. Interface Injection nejprve je definováno rozhraní, které urcuje metody, pomocí kterých budou nastavovány instance objektu, které príslušná třída potrebuje. Každá trída, která chce tyto instance využívat, musí implementovat príslušné rozhraní. Meřjme trřídu SeznamAut, která umí pomocí neřjaké své metody získat seznam všech aut od neřkterého výrobce. V metodeř, která se bude jmenovat ziskejSeznamPodleVyrobce vy¬ užívá trřídu VyhledavacAut, pomocí které získá auta práveř od vybraného výrobce. To je klasický prístup bez využití IoC kontejneru, názorne ho mužeme prohlédnout na obrázku 4.1. Obrázek 4.1: Príklad programování bez IoC 1. < h t t p : / / e n . w i k i p e d i a . o r g / w i k i / H o l l y w o o d _ P r i n c i p l e > 16 4.1. POUŽITÉ T E C H N O L O G I E A F R A M E W O R K Y Z obrázku je názorne videt, že SeznamAut potrebuje znát implementace rozhraní VyhledavacAut a je s ním tak velmi tesne svázán. Pro uvolnení této vazby milžeme využít práve IoC. Místo toho, aby SeznamAut inicializoval trídu VyhledavacAutImplementace, využije nekterou z technik IoC, napríklad bude mít setter metodu, pomocí které mu bude nastavena nejaká implementace rozhraní VyhledavacAut. Nemusí už tedy znát implementaci rozhraní, kterou bude využívat. K tomu je ale potřeba, aby zde existoval práve nejaký IoC kontejner, který se o toto postará. inicializuje <> VyhledavacAut • ŕ A VyhledavacAutImplementace Obrázek 4.2: Príklad programování s IoC Z obrázku 4.2 je patrné, že SeznamAut je nyní závislý pouze na rozhraní VyhledavacAut. Tím jsme uvolnili závislost mezi trřídou SeznamAut a konkrétní implementací rozhraní VyhledavacAut. O sestavení jednotlivých komponent dohromady se nyní stará IoC kontejner. 4.1.2.1 Dependency Injection Dependency injection (DI), nebo také vkládání závislostí, je další návrhový vzor. V podstate je to novejší název pro IoC, zužuje ale okruh, na který se vztahuje. Jedná se o konkrétní techniku využití IoC. DI má neřkolik zásadních výhod pro programování: • jednodušší psaní a údržba jednotlivých komponent, díky vzájemné menší závislosti • výrazne jednodušší testování kódu. • není trřeba starat se o prřetypování trříd, o neřj se stará IoC kontejner • závislosti komponent jsou definovány explicitne, je tedy jednodušší se v nich vyznat 17 4.1. POUŽITÉ T E C H N O L O G I E A F R A M E W O R K Y • IoC kontejner ve vetšine prípadu nevyžaduje speciální zásah do kódu aplikace, jed¬ notlivé komponenty je pak možné snadno využít i jinde bez nutnosti prřepisování kódu DI má samozrejme i nekteré nevýhody. Mezi nejvýznacnejší patrí: • pro programátora, který nezná návrhový vzor IoC, je teřžké vyznat se v takto napsa¬ ném kódu • pro jednodušší aplikace je zbytecřné zabývat se složitým kódem, ale je snazší napsat aplikaci v klasické Jave • pokud používané vývojové prostrředí nezná IoC, je složiteřjší vývoj aplikací 4.1.2.2 Google Guice Google Guice je DI framework vyvinutý firmou Google pro potreby jejich aplikace AdWords a uvolnený jako open-source. Je postaven nad vlastnostmi Javy 5.0 - anotacemi a generi¬ kami, díky cemuž, narozdíl od jiných DI frameworku, nevyžaduje konfiguraci v X M L a je proto snadno refaktorovatelný a typoveř bezpecřný. Dveřma základními stavebními kameny Google Guice jsou takzvané moduly a anotace @Inject. Moduly jsou implementací rozhraní com.google.inject.Module, vetšinou pretežují abstraktní implementaci tohoto rozhraní - AbstractModule. Anotace @Inject frameworku rříká, kam má vkládat závislosti. Příkladem konfigurace je trída BindingConfiguration p u b l i c class BindingConfiguration extends AbstractModule { @Override protected v o i d configure() { bind(Dispatcher.class); bind(BranchTree.class) .toInstance(new BranchTreeBuilder("ROOT").getTree()); bind(IModul.class) .annotatedWith(Names.named("SA.NEMOC")).to(Nemoc.class); bind(IBusSystem.class).annotatedWith(BrnoAnnotation.class) .to(Brno.class).asEagerSingleton(); } } První rádek konfigurace jednoduše na oanotované místo vloží instanci třídy Dispatcher, další rřádek deřlá v podstateř to samé, pouze instanci nevytvárří sám, ale použije instanci trřídy, kterou jsme mu explicitneř prředali. Trřetí a cřtvrtý rřádek injektují instance IModul.class, re¬ spektive IBusSystem.class, na místa oznacená prímo anotací frameworku - @Named("SA.NEMOC"), nebo vlastní vytvorenou anotací B r n o A n n o t a t i o n . c l a s s . Poslední rádek navíc ríká, že prři vkládání trřídy nemá pokaždé vytvárřet novou instanci, ale má vytvorřit singleton, který použije pokaždé, je-li tato trřída vyžádána. 18 4.1. POUŽITÉ T E C H N O L O G I E A F R A M E W O R K Y Celý framework se inicializuje třídou Guice. Ta vytváří I n j e c t o r , který se pak stará o vytvárení a správu požadovaných objektu. Injector i n j e c t o r = Guice.createInjector( new BindingConfiguration(), new BindingOthers() ); Dispatcher dispatcher = injector.getInstance(Dispatcher.class); Použití frameworku ve trídách, které spravuje, je velmi jednoduché. StaCí pomocí anotace @Inject, prípadne dalších doplnujících anotací, oznaät kontruktor třídy a promenné, které chceme vkládat. p u b l i c f i n a l class Brno implements IBusSystem { @Inject public Brno() {} @Inject @BrnoAnnotation public IBusDB db; } 4.1.3 JDBC Template Ve Student Agency se mezi programátory rešícími v Jave prístup k databázi stalo pomerní? oblíbené rešení Romana Pichlíka nazvané JDBC Template [11], které jsem se ve své aplikaci rozhodl použít i já. Přistupovat k databázi v Jave není vubec složité a vše potrebné je definováno rozhraním JDBC (Java Database Connectivity). Práce s tímto rozhraním se skládá z nekolika kroku • získání databázového pripojení • vytvorení statementu • nastavení parametru a exekuce SQL • procházení resultsetu a zpracování dat • uzavrření resultsetu, statementu a datab. prřipojení Typický kód pak vypadá takto Connection con = n u l l ; PreparedStatement ps = n u l l ; ResultSet r s = n u l l ; try{ con = getConnection(); ps = con.prepareStatement("select * from foo"); rs = ps.executeQuery(); while(rs.next()){ 19 4.1. POUŽITÉ T E C H N O L O G I E A F R A M E W O R K Y //zpracuj hodnoty } } f i n a l l y { r s . c l o s e ( ) ; ps.close(); con.close(); } Všechny tyto kroky, krome zpracování vrácených dat, se neustále v kódu opakují pro každý SQL dotaz. Tím pádem nám vzniká zbytecne velké množství redundantního kódu. Navíc musíme pracovat s všudyprítomnou SQLException, která je nejen príliš obecná, ale navíc i kontrolovaná (checked). Velké množství opakovaného kódu cřasto svádí k bezmyš¬ lenkovitému kopírování kódu. Tím pádem se muže lehce prihodit, že do kódu bude zavlecena chyba, která se bude pozdeřji velice teřžko dohledávat. Klasickým prřípadem je neuzavrření databázového prřipojení, resp. jeho vrácení do poolu. Rešení techto problému (redundance, exception handling, odpoveřdnost) spocřívá v separování spolecřného kódu ala návrhový vzor Template Method. 4.1.3.1 Template method Template method je návrhový vzor spadající do rodiny vzoru chování (bahavioral). Smysl tohoto vzoru je založen na tom, že na abstraktneřjší úrovni prředka je vytvorřen scénárř (me¬ toda), který je složen z volání neřkolika polymorfních metod. Potomkové používají scénárř tak, že polymorfní operace prřepíší a scénárř zavolají. Viz diagram 4.3. Kostra celého scénárře je daná metodou templateMethod, ze které se provolávají kromeř jiného metody o p e r a t i o n 1 a o p e r a t i o n 2 . Ty jsou v predkovi nadefinovány jako abs¬ traktní, takže je možná jejich prázdná implementace. Vlastní spušteřní se provede voláním metody new C o n c r e t e C l a s s ( ) . t e m p l a t e M e t h o d ( ) ; . Aplikaci tohoto vzoru v JDBC Template je spolecný kód, v podobe získání databázového pripojení, vytvorení statementu, nastavení parametru, spuštení SQL, procházení výsledku (resultsetu) a uzavrení všeho potrebného, izolovaný v predkovi. Ten definuje dve abstraktní metody. Metodu pro získání databázového prřipojení a metodu pro zpracování rřádky result- setu. Objekt JDBCTemplate definuje dví? šablonové metody urcené k volání a to executeQuery a executeUpdate. Dále definuje dve polymorfní metody a to g e t C o n n e c t i o n a handleRow. Potomkové mohou prepsat ješte metody setParams a metodu handleSQLException. V aplikaci jsou potomkové nazvaní JDBCTemplate (kde je jeden z Brno, Ostrava, Liberec, Plzen, Slovensko), kterří realizuje získání databázového prřipojení a implementují handleRow prázdným teřlem. O d nich se ve trřídách implementujících roz¬ hraní IBusSystem deřdeřním vytvárřejí další anonymní trřídy, které již konkrétneř rřeší jednotlivé dotazy na databázi. 20 4.1. POUŽITÉ T E C H N O L O G I E A F R A M E W O R K Y Obrázek 4.3: Diagram tríd (UML) 4.1.4 NamedParameterStatement Každý SQL dotaz ve třídách implementujících rozhraní IBusSystem je nejakým způsobem parametrizován, at' jde o pouhé zadání cárového kódu lístku, nebo o komplexní zadání parametria například pro rezervaci lístku. V Javí? je možné parametrizovat pomocí spojování textu pri vytváření dotazu, takže typický kód vypadá takto S t r i n g s q l = "SELECT * FROM table WHERE columni = " + p a r a m L t o S t r i n g + " LIMIT 5"; Connection con = DriverManager.getConnection(server, username, password); Statement stmt = con.createStatement(); ResultSet r s = stmt.executeQuery(sql); nebo pomocí trídy nazvané PreparedStatement2 , která má oproti predchozímu príkladu nekolik výkonnostních i bezpecřnostních výhod. Typický kód prři použití PreparedStatementu vypadá takto S t r i n g s q l = "SELECT * FROM table WHERE columni = ? LIMIT 5"; PreparedStatement pstmt = con.prepareStatement(sql); pstmt.setString(1, parami); ResultSet r s = pstmt.executeQuery(); 2. 21 4.2. B E H A P L I K A C E N A SERVERŮ Velkou nevýhodou PreparesStatementu je, že parametry jsou znaceny pomocí otazníku. To znacřneř zteřžuje orientaci v kódu, zejména má-li programátor upravit neřjaký delší dotaz se spoustou parametru. Existují urcité zpusobyjak si usnadnit práci tak, aby pokaždé nemusel pocítat otazníky od zacátku retezce dotazu (napríklad pomocí komentáru v nem), ale nenexistuje zpusob, jak by mohl jeden parametr použít na více místech dotazu. Jeden otazník = jeden parametr. Abych si ulehcil orientaci v kódu a nemusel psát zbytecné komentáre, hledal jsem zpusob, jak si zjednodušit práci, až jsem narazil na rřešení Adama Crumeho, trřídu nazvanou NamedParameterStatement [6]. Tu jsem použil pro úpravu trídy JDBCTemplate. Její syntaxe je stejná jako v prípade trídy ParameterStatement, ale namísto otazníku jsou parametry zastupovány jako dvojtecka následovaná identifikátorem. S t r i n g s q l = "SELECT * FROM table WHERE columni = :param1 LIMIT 5"; NamedParameterStatement npstmt = new NamedParameterStatement(con, s q l ) ; npstmt.setString("param1", parami); ResultSet r s = pstmt.executeQuery(); Trída funguje tak, že identifikátory z dotazu nahradí otazníky a vytvorí PreparedStatement. Nahrazené identifikátory si ukládá do mapy spolu se seznamem jejich výskytu. Pri volání metody setXXX() se na tuto mapu odkazuje a na indexy v ní uložené volá metodu setXXX na PreparedStatementu. 4.2 Beh aplikace na serveru Posledním implementacřním detailem, který bylo potrřeba vyrřešit, byl beřh aplikace na ser¬ veru. Java je interpretovaný jazyk a pro svuj beh vyžaduje virtuální stroj(Java Virtual Ma¬ chine - JVM) který interpretuje mezikód (bytecode) generovaný kompilátorem. Virtuální strojje „obal" ve kterém aplikace beží, a není proto možné s ní komunikovat pomocí bežných prostredku poskytovaných operacním systémem. Jediný zpťisob, jak spustit Java aplikaci, je metoda p u b l i c s t a t i c v o i d m a i n ( S t r i n g [ ] ) , což je vhodné rešení pro klientské aplikace, které muže uživatel interaktivne ukoncit zavoláním metody S y s t e m . e x i t ( i n t ) , ale pro neiteraktivní serverové aplikace neexistuje žádný jednoduchý zpusob, jak interpret Javy informovat o blížícím se ukoncení behu aplikace. V závislosti na typu aplikace to muže znamenat urcřité problémy. Veřtšina serverových aplikací totiž prřed svým ukoncřením potrřebuje vykonat sérii úkonu, napríklad korektne ukoncit spojení s klienty a ukoncit podpurné aplikace. Teprve potom je možné, aby se JVM ukoncřil. Existuje neřkolik možných rřešení tohoto probému. Jedním z nich je naprříklad vytvorření komunikacřního bodu aplikace s jejím okolím - tzv. socketu. Aplikace potom cřeká na prřijetí zprávy prřes socket, a když se tak stane, provede operace nutné ke korektnímu ukoncření a nakonec sama zavolá metodu System.exit pro ukoncření beřhu JVM. Tato metoda má však nekolik nevýhod, napríklad pri ukoncování celého systému se muže stát, že operacní systém ukoncří JVM prřímo bez upozornéní beřžící aplikace. Casteji využívanou metodou je spuštení aplikace v takzvaném Service Wrapperu - pro¬ gramu obalujícím aplikaci a umožnujícím jí beřžet na pozadí. Neřkteré wrappery mají prři- 22 4.2. B E H A P L I K A C E N A S E R V E R U danou funkcionalitu naprříklad pro hlídání stavu aplikace, nebo s ní umožnují komuniko¬ vat. Mezi nejpoužívanejší patrí Java Service Wrapper3 a Apache Commons Daemon4 , avšak ani jeden z nich se mi pro beřh SMS parseru nezdál vhodný. Obeř rřešení totiž spoléhají na knihovny napsané v jazyce C, které jim umožnují využívat služeb operacního systému způ¬ sobem beřžným pro nativní programy, a protože jsou Java Service Wrapper i Apache Com¬ mons Daemon multiplatformní, musejí se snažit prizpťisobit se rozdílům mezi jednotlivými operacřními systémy. Vzhledem k tomu, že parser pobeřží výhradneř pod operacřním systé¬ mem s jádrem Linux, jsou obeř rřešení prříliš komplikovaná a rozhodl jsem se proto zkusit najít jiné rřešení, které by bylo jednoduché, a prřesto splnřovalo všechny požadavky pro beřh jako korektní serverová aplikace. 4.2.1 Java Daemon Nejvhodnejším „rešením" se ukázal být Java Daemon Petera Williamse [13], což je, spíše než ucelené rešení ve forme nejaké knihovny ci trídy, jen série pokynu, jak vytvorit trídu, která se stará o beřh aplikace na pozadí, které jsem si upravil dle vlastních potrřeb. První probém, který je potrřeba vyrřešit, je spušteřní aplikace tak, aby nebyla zabita prři ukoncření konzole ze kterého byla spušteřna. Je možné použít standardní unixový prříkaz nohup, ale ten přesmerovává standardní vstup, výstup a chybový výstup do souboru, což je zbytecřné, protože celý parser využívá logování postavené na knihovneř Log4J. Namísto toho je možné spustit aplikaci prikazem java -jar application.jar <&- 1>/dev/null 2>&1 &, což ji spustí na pozadí, zavrře standardní vstup a standardní výstupy prřesmeřruje do bitového koše (/dev/null). Zavrením standardního vstupu a presmerováním výstupu zajistíme, že aplikace nebude ukoncřena a bude beřžet na pozadí, nezávisle na konzoli, která ji spustila. Bylo by však vhodné mít prřístup k chybovým hlášením, která se mohou objevit beřhem spouštení aplikace - napríklad když chybí konfiguracní soubor. Nebudeme proto přesmerovávat standardní výstup, a aplikaci spustíme pomocí příkazu java -jar application.jar <&&. To ale opet způsobí závislost aplikace na konzoli. Rešením je odpojit aplikaci od standardních výstupťi po dokoncení spouštení aplikace. Vytvoríme proto metodu daemonize, kterou zavoláme prřed spušteřním hlavní aplikacřní logiky. Tak dosáhneme možnosti sledovat výstup aplikace do konzole beřhem startu, a umožníme konzoli, ze kterého byla spušteřna, ukoncřit se, jakmile je start dokoncřen. Nyní máme program kompletne odpojen od konzole, a jediný způsob ukoncení je zabít ho pomocí signálu. K tomu potrebujeme znát identifikátor procesu (process ID - pid). V Jave neexistuje zpuísob, jak jednoduše pid z'istit, protože je to príliš závislé na operacním systému. Proto vytvorříme skript, který aplikaci spustí a uloží pid pro budoucí použití. Skript zapisuje pid do souboru p a r s e r . p i d , takže bežící aplikaci můžeme snadno ukoncit príkazem kill 'cat parser.pid'. Pokud se aplikace nespustí korektne, nebo pri behu spadne, soubor obsahující pid stále existuje, což může vést k domence, že vše funguje v porádku. Problém s pid souborem zůstávajícím po pádu aplikace vyrešíme predáním parame- 3. 4. 23 4.2. BĚH A P L I K A C E N A S E R V E R U tru -Ddaemon.pidfile=parser.pid do JVM, úpravou metody daemonize() přidáním řádku g e t P i d F i l e ( ) . d e l e t e O n E x i t ( ) , a vytvořením metody g e t P i d F i l e ( ) která vrací objekt, ve kterém je soubor, specifikovaný systémovou hodnotou „daemon.pidfile". Tím zaruCíme, že soubor s pid bude smazán, když se JVM ukonCí. Problém s vytvorením pid souboru pred spustením aplikace vyrešíme úpravou spouštecího skriptu tak, aby si po spuštení JVM zkontroloval, jestli JVM skuteCne beží. Toho docílíme vytvorením funkce launch_daemon(), ve které se vytvorí potomek konzole, a z nej se aplikace spustí. Funkce se ukoncí pouze v prípade, že dojde k ukoncení potomka konzole, což nastane v prípade, že se JVM ukoncí, nebo odpojí od konzole. Návratovou hodnotou funkce je hodnota pid spusteného JVM, takže po návratu z funkce mtižeme snadno overit, zda proces s tímto pid stále existuje. Nyní máme vyrešeny problémy se spouštením a ukoncováním aplikace, ale stále nemáme rozumný zpťisob, jak aplikaci ciste ukoncit. To vyrešíme pridáním promenné s t a t i c p r o t e c t e d b o o l e a n shutdownRequested = f a l s e , metod s t a t i c p u b l i c v o i d shutdown() , s t a t i c p u b l i c isShutdownRequested() a úpravou aplikace tak, aby si kontrolovala návratovou hodnotu funkce isShutdownRequested() . Pokud tato vrátí t r u e , ukoncíme aplikaci. Mohlo by se zdát, že nyní už je vše v porádku, ale při bližším pohledu zjistíme, že metoda shutdown() je volatelná pouze zevnitr aplikace a jsme tedy tam, kde jsme byli na zacátku. Nyní ale nvůžeme využít tzv. shutdown hook, který JVM nabízí, což je funkce, kterou JVM spouští pred svým ukoncením. V metode daemonize() ukožíme do promenné a p p l i c a t i o n T h r e a d ( ) aktuální vlákno, a metodu shutdown() upravíme tak, aby se pomocí funkce j o i n ( ) na toto vlákno pripojila. Poté staä JVM ríct, aby jako shutdown hook volala v novém vlákneř metodu shutdown() . Tím zajistíme, že JVM bude cřekat na dokoncení vlákna, které ceká na dokoncení hlavní logiky programu. Nyní už mťižeme jednoduše zabít aplikaci pomocí príkazu kill 'cat parser.pid', přicemž máme zajišteřno, že aplikace bude mít dostatek cřasu na to, aby korektneř provedla všechny ukoncřovací operace. 24 Kapitola 5 Záver Parser byl nasazen v srpnu 2008 a od té doby zpracovává prumerne 0.18 príchozích a 0.24 odchozích SMS za minutu, ve špickách zhruba 65 príchozích a 135 odchozích zpráv za mi¬ nutu. Od nasazení v neřm byly provedeny desítky úprav a vylepšení a také opraveno neřkolik chyb. Nejveřtší úpravou, kterou parser prošel, bylo propojení s novým rezervacřním systémem, se kterým parser komunikuje pomocí webových služeb1 . Je také plánován další rozvoj parseru, naprříklad vylepšení trřídy Params tak, aby byla schopná vyporřádat se naprříklad s prohozenými parametry, prřípadneř aby byla schopna si od odesilatele zprávy neřjaký chybeřjící parametr vyžádat. 1. 25 Literatura [1] Metsker, S.: Building parsers with Java, Boston : Addison-Wesley, 2001, ISBN 0-201¬ 71962-2, <> . [2] Spell, B.: Java programujeme profesionálne: tvorba tríd, vlákna, zpracování událostí, správci rozvržení, komponenty Swing, JDBC, XML, zabezpečení, JavaHelp, JNI, výkon, distribuované objekty, lokalizace., Praha : Computer Press, 2002, ISBN 80-7226¬ 667-5, <> . [3] Herout, P.: Učebnice jazyka Java, Ceské Budějovice : Kopp nakladatelství, 2007, ISBN 978-80-7232-323, <> . [4] Student Agency, . [5] Chlouba, T.: Inversion of Control, Dependency Injection, 2008, . [6] Crume, A.: Named Parameters for PreparedStatement, 2007, . 4.1.4 [7] Fowler, M.: Inversion of Control Containers and the Dependency Injection pattern, 2004, < h t t p : / / w w w . m a r t i n f o w l e r . c o m / a r t i c l e s / i n j e c t i o n . h t m l > . [8] Programování v jazyku Java, 2004, < h t t p : / / w w w . l i n u x s o f t . c z / a r t i c l e . p h p ? i d _ a r t i c l e = 2 4 4 > . 4.1.1 [9] Java (programovací jazyk), 2010, < h t t p : / / c s . w i k i p e d i a . o r g / w i k i / J a v a _ (programovací_jazyk) > . 4.1.1 [10] Knihovna LibEMI, . 2.3.1 [11] Pichlík, R.: JDBC Template, 2006, . 4.1.3 [12] , R.: Introduction to Google Guice, 2008, . [13] Williams, P.: Java Daemon, 2005, . 4.2.1 26 Rejstrík aplikacřní logika, 2, 4 27 Príloha A CD C D obsahuje zdrojové kódy bakalárské práce, zdrojové kódy puvodního parseru a databázovou strukturu ve forme SQL dotazu. 28 Příloha B Hlavní databázové funkce a datové typy rozdelené podle klonu B.1 Brno • C R E A T E _ R E Z E R V A C E Funkce pro vytvárření rezervace Návratová hodnota c h a r a c t e r v a r y i n g - prázdný retezec, pokud byla rezer¬ vace v porřádku vytvorřena, nebo chybové hlášení popisující chybu. id_rezervace i n t e g e r - id rezervace login c h a r a c t e r v a r y i n g - login operátora odkud i n t e g e r - id místa odjezdu kam i n t e g e r - id místa prijezdu id_spoj i n t e g e r - id spoje, do kterého rezervujeme id_mista i n t e g e r - id místa které má být rezervováno, pokud je n u l l najde a vybere se volné místo automaticky inet b o o l e a n - zda je rezervace vytvárena online nebo na prodejním míste posila b o o l e a n - zda se rezervuje posilový nebo hlavní spoj nacten b o o l e a n - ríká, jestli se se má lístek vytvorit jako Otevrený hlaska_id b o o l e a n - zda má funkce vracet chybové hlášení ve forme vety (použito v YP), nebo pomocí identifikátoru vhodného pro lokalizaci (použito v online rezervacním systému) 29 B . l . B R N O • E R A S E _ R E Z E R V A C E Funkce pro rušení rezervace Návratová hodnota c h a r a c t e r v a r y i n g - prázdný retezec, pokud byla rezervace v pořádku zrušena, nebo chybové hlášení popisující chybu. id_rezervace id rezervace nenet b o o l e a n - zda rezervace není rušena online login c h a r a c t e r v a r y i n g - login operátora time_insert timestamp - čas rušení rezervace. Pokud je n u l l , použije se aktuální cřas. Slouží hlavneř pro rušení rezervací pomocí sms, jako time_insert se bere cřas prřijetí sms, ne cřas jejího zpracování parserem hlaska_id boolean - zda má funkce vracet chybové hlášení ve forme vety (použito v YP), nebo pomocí identifikátoru vhodného pro lokalizaci (použito v online rezervačním systému) • G E T _ R E Z E R V A C E nalezení rezervace dle jejího id Návratová hodnota t _ r e z e r v a c e - struktura t_rezervace, popsána níže id_rezervace i n t e g e r - id rezervace • GET_FIRST_SPOJ Funkce pro vyhledání nejbližšího spoje mezi dveřma zastávkami v daný den od za¬ daného cřasu Návratová hodnota i n t e g e r - id spoje nebo n u l l pokud není nalezen odkud i n t e g e r - id místa odjezdu kam i n t e g e r - id místa pnjezdu 30 B . l . B R N O datum date - den, ve kterém se má spojvyhledat cas t i m e - cas, od kterého se má hledat (vcetne) W _ S P O J _ V Y H L E D A T Funkce pro vyhledání spoju odjíždejících v zadaný den Návratová hodnota t _ w _ s p o j _ v y h l e d a t - struktura t _ w _ s p o j _ v y h l e d a t po¬ psána níže datum timestamp - den, ve kterém se má spojvyhledat zastavka_od i n t e g e r - id místa odjezdu zastavka_do i n t e g e r - id místa príjezdu T _ R E Z E R V A C E Struktura obsahující informace o rezervaci id_rezervace i n t e g e r - id rezervace odkud i n t e g e r - id místa odjezdu kam i n t e g e r - id místa príjezdu datum date - datum odjezdu cas t i m e - cas odjezdu sedadlo i n t e g e r - císlo sedadla cena numeric(10,2) - cena rezervace cena_sk numeric(10,2) - stejné jako cena (nepoužívá se) 31 B.2. LIBEREC posila b o o l e a n - zda jde o rezervaci do posilového spoje happy i n t e g e r - nepoužívá se • T _ W _ S P O J _ V Y H L E D A T Struktura obsahující informace o vyhledaných spojích id_spoj i n t e g e r - id spoje cas_od timestamp - cas odjezdu cas_kam timestamp - cas prijezdu volných i n t e g e r - pocet volných míst volnych_ruzyne i n t e g e r - pocet volných míst, pokud spojjede pres zastávku Praha - Ruzyní? filmy c h a r a c t e r v a r y i n g [ ] - pole filmů pouštených ve spoji audio c h a r a c t e r v a r y i n g [ ] - pole hudby pouštené ve spoji (nepoužívá se) happy i n t e g e r - nepoužívá se economy b o o l e a n - zda se jedná o economy spoj(bez služeb) B.2 Liberec • C R E A T E _ R E Z E R V A C E Funkce pro vytvárení rezervace Návratová hodnota c h a r a c t e r v a r y i n g - prázdný retezec, pokud byla rezervace v pořádku vytvorena, nebo chybové hlášení popisující chybu. id_rezervace i n t e g e r - id rezervace 32 B.2. LIBEREC login c h a r a c t e r v a r y i n g - login operátora odkud i n t e g e r - id místa odjezdu kam i n t e g e r - id místa prříjezdu id_spoj i n t e g e r - id spoje, do kterého rezervujeme id_mista i n t e g e r - id místa, které má být rezervováno. pokud je n u l l najde a vybere se volné místo automaticky inet boolean - zda je rezervace vytvárena online nebo na prodejním míste posila b o o l e a n - zda se rezervuje posilový nebo hlavní spoj hlaska_id boolean - zda má funkce vracet chybové hlášení ve forme vety (použito v YP), nebo pomocí identifikátoru vhodného pro lokalizaci (použito v online rezervacřním systému) • E R A S E _ R E Z E R V A C E Funkce pro rušení rezervace Návratová hodnota c h a r a c t e r v a r y i n g - prázdný řetězec, pokud byla rezervace v pořádku zrušena, nebo chybové hlášení popisující chybu. id_rezervace id rezervace nenet b o o l e a n - zda rezervace není rušena online login c h a r a c t e r v a r y i n g - login operátora time_insert timestamp - cas rušení rezervace. Pokud je n u l l , použije se aktuální cas. Slouží hlavne pro rušení rezervací pomocí sms, jako time_insert se bere cas prijetí sms, ne cas jejího zpracování parserem hlaska_id boolean - zda má funkce vracet chybové hlášení ve forme vety (použito v YP), nebo pomocí identifikátoru vhodného pro lokalizaci (použito v online rezervacním systému) 33 B.2. LIBEREC • G E T _ R E Z E R V A C E nalezení rezervace dle jejího id Návratová hodnota t _ r e z e r v a c e - struktura t_rezervace, popsána níže id_rezervace i n t e g e r - id rezervace • GET_FIRST_SPOJ Funkce pro vyhledání nejbližšího spoje mezi dveřma zastávkami v daný den od za¬ daného cřasu Návratová hodnota i n t e g e r - id spoje nebo n u l l pokud není nalezen odkud i n t e g e r - id místa odjezdu kam i n t e g e r - id místa prijezdu datum date - den, ve kterém se má spoj vyhledat cas time - čas, od kterého se má hledat (včetne) • W _ S P O J _ V Y H L E D A T Funkce pro vyhledání spoju odjíždejících v zadaný den Návratová hodnota t _ w _ s p o j _ v y h l e d a t - struktura t _ w _ s p o j _ v y h l e d a t po¬ psána níže datum timestamp - den, ve kterém se má spojvyhledat zastavka_od i n t e g e r - id místa odjezdu zastavka_do i n t e g e r - id místa prijezdu • T _ R E Z E R V A C E Struktura obsahující informace o rezervaci 34 B.2. LIBEREC id_rezervace i n t e g e r - id rezervace odkud i n t e g e r - id místa odjezdu kam i n t e g e r - id místa prříjezdu datum d a t e - datum odjezdu cas t i m e - cas odjezdu osob i n t e g e r - pocet osob na rezervaci (nepoužívá se) sedadla t e x t - cřárkami oddeřlený seznam sedadel na rezervaci cena numeric(10,2) - cena rezervace byznys i n t e g e r - nepoužívá se posila b o o l e a n - zda jde o rezervaci do posilového spoje • T _ W _ S P O J _ V Y H L E D A T Struktura obsahující informace o vyhledaných spojích id_spoj i n t e g e r - id spoje cas_od timestamp - cas odjezdu cas_kam timestamp - cas prijezdu volnych i n t e g e r - pocet volných míst filmy c h a r a c t e r v a r y i n g [ ] - pole filmu pouštených ve spoji audio c h a r a c t e r v a r y i n g [ ] - pole hudby pouštené ve spoji (nepoužívá se) happy i n t e g e r - nepoužívá se 35 B.3. OSTRAVA economy b o o l e a n - zda se jedná o economy spoj(bez služeb) B.3 Ostrava • C R E A T E _ R E Z E R V A C E Funkce pro vytvárření rezervace Návratová hodnota c h a r a c t e r v a r y i n g - prázdný rřeteřzec, pokud byla rezer¬ vace v porřádku vytvorřena, nebo chybové hlášení popisující chybu. id_rezervace i n t e g e r - id rezervace login c h a r a c t e r v a r y i n g - login operátora odkud i n t e g e r - id místa odjezdu kam i n t e g e r - id místa prijezdu id_spoj i n t e g e r - id spoje, do kterého rezervujeme id_mista i n t e g e r - id místa, které má být rezervováno. Pokud je n u l l najde a vybere se volné místo automaticky inet b o o l e a n - zda je rezervace vytvárena online nebo na prodejním míste posila b o o l e a n - zda se rezervuje posilový nebo hlavní spoj nacten b o o l e a n - ríká, jestli se se má lístek vytvorit jako Otevrený hlaska_id b o o l e a n - zda má funkce vracet chybové hlášení ve forme vety (použito v YP), nebo pomocí identifikátoru vhodného pro lokalizaci (použito v online rezervacřním systému) • E R A S E _ R E Z E R V A C E Funkce pro rušení rezervace 36 B.3. OSTRAVA Návratová hodnota c h a r a c t e r v a r y i n g - prázdný rřeteřzec, pokud byla rezer¬ vace v porřádku zrušena, nebo chybové hlášení popisující chybu. id_rezervace id rezervace nenet b o o l e a n - zda rezervace není rušena online login c h a r a c t e r v a r y i n g - login operátora time_insert timestamp - cas rušení rezervace. Pokud je n u l l , použije se aktuální cřas. Slouží hlavneř pro rušení rezervací pomocí sms, jako time_insert se bere cřas prřijetí sms, ne cřas jejího zpracování parserem hlaska_id boolean - zda má funkce vracet chybové hlášení ve forme vety (použito v YP), nebo pomocí identifikátoru vhodného pro lokalizaci (použito v online rezervacřním systému) • G E T _ R E Z E R V A C E nalezení rezervace dle jejího id Návratová hodnota t _ r e z e r v a c e - struktura t_rezervace, popsána níže id_rezervace i n t e g e r - id rezervace • GET_FIRST_SPOJ Funkce pro vyhledání nejbližšího spoje mezi dveřma zastávkami v daný den od za¬ daného cřasu Návratová hodnota i n t e g e r - id spoje nebo n u l l pokud není nalezen odkud i n t e g e r - id místa odjezdu kam i n t e g e r - id místa prříjezdu datum date - den, ve kterém se má spoj vyhledat 37 B.3. O S T R A V A cas time - čas, od kterého se má hledat (včetne) W _ S P O J _ V Y H L E D A T Funkce pro vyhledání spoju odjíždejících v zadaný den Návratová hodnota t _ w _ s p o j _ v y h l e d a t - struktura t _ w _ s p o j _ v y h l e d a t po¬ psána níže datum timestamp - den, ve kterém se má spojvyhledat zastavka_od i n t e g e r - id místa odjezdu zastavka_do i n t e g e r - id místa prijezdu T _ R E Z E R V A C E Struktura obsahující informace o rezervaci id_rezervace i n t e g e r - id rezervace odkud i n t e g e r - id místa odjezdu kam i n t e g e r - id místa prijezdu datum date - datum odjezdu cas time - čas odjezdu sedadlo i n t e g e r - číslo sedadla cena numeric(10,2) - cena rezervace cena_sk numeric(10,2) - jako cena (nepoužívá se) posila b o o l e a n - zda jde o rezervaci do posilového spoje 38 B.4. P L Z E N happy b o o l e a n - zda jde o rezervaci do tzv. HAPPY spoje T _ W _ S P O J _ V Y H L E D A T Struktura obsahující informace o vyhledaných spojích id_spoj i n t e g e r - id spoje cas_od timestamp - cas odjezdu cas_kam timestamp - cas príjezdu volných i n t e g e r - pocet volných míst filmy c h a r a c t e r v a r y i n g [ ] - pole filmu pouštených ve spoji audio c h a r a c t e r v a r y i n g [ ] - pole hudby pouštené ve spoji (nepoužívá se) B.4 Plzen C R E A T E _ R E Z E R V A C E Funkce pro vytvárení rezervace Návratová hodnota c h a r a c t e r v a r y i n g - prázdný retezec pokud byla rezervace v porádku vytvořena, nebo chybové hlášení popisující chybu. id_rezervace i n t e g e r - id rezervace login c h a r a c t e r v a r y i n g - login operátora odkud i n t e g e r - id místa odjezdu kam i n t e g e r - id místa príjezdu id_spoj i n t e g e r - id spoje, do kterého rezervujeme 39 B.4. PLZENř id_mista i n t e g e r - id místa, které má být rezervováno. Pokud je n u l l najde a vybere se volné místo automaticky osob i n t e g e r - vždy n u l l (nepoužívá se) inet boolean - zda je rezervace vytvárena online nebo na prodejním míste posila b o o l e a n - zda se rezervuje posilový nebo hlavní spoj hlaska_id boolean - zda má funkce vracet chybové hlášení ve forme vety (použito v YP), nebo pomocí identifikátoru vhodného pro lokalizaci (použito v online rezervacřním systému) • E R A S E _ R E Z E R V A C E Funkce pro rušení rezervace Návratová hodnota c h a r a c t e r v a r y i n g - prázdný retezec, pokud byla rezer¬ vace v porřádku zrušena, nebo chybové hlášení popisující chybu. id_rezervace id rezervace nenet b o o l e a n - zda rezervace není rušena online login c h a r a c t e r v a r y i n g - login operátora time_insert timestamp - cas rušení rezervace. Pokud je n u l l , použije se aktuální cřas. Slouží hlavneř pro rušení rezervací pomocí sms, jako time_insert se bere cřas prřijetí sms, ne cřas jejího zpracování parserem hlaska_id boolean - zda má funkce vracet chybové hlášení ve forme vety (použito v YP), nebo pomocí identifikátoru vhodného pro lokalizaci (použito v online rezervacřním systému) • G E T _ R E Z E R V A C E nalezení rezervace dle jejího id Návratová hodnota t _ r e z e r v a c e - struktura t_rezervace, popsána níže 40 B.4. P L Z E N id_rezervace i n t e g e r - id rezervace • GET_FIRST_SPOJ Funkce pro vyhledání nejbližšího spoje mezi dvema zastávkami v daný den od zadaného času Návratová hodnota i n t e g e r - id spoje nebo n u l l pokud není nalezen odkud i n t e g e r - id místa odjezdu kam i n t e g e r - id místa prijezdu datum date - den, ve kterém se má spoj vyhledat cas time - čas, od kterého se má hledat (včetne) • W _ S P O J _ V Y H L E D A T _ N E W Funkce pro vyhledání spoju odjíždejících v zadaný den Návratová hodnota t _ w _ s p o j _ v y h l e d a t - struktura t _ w _ s p o j _ v y h l e d a t po¬ psána níže datum timestamp - den ve kterém se má spojvyhledat zastavka_od i n t e g e r - id místa odjezdu zastavka_do i n t e g e r - id místa prijezdu • T _ R E Z E R V A C E Struktura obsahující informace o rezervaci id_rezervace i n t e g e r - id rezervace odkud i n t e g e r - id místa odjezdu 41 B.4. P L Z E N kam i n t e g e r - id místa prijezdu datum date - datum odjezdu cas time - cas odjezdu osob i n t e g e r - vždy n u l l (nepoužívá se) sedadla i n t e g e r - cárkami oddelený seznam sedadel na rezervaci cena numeric(10,2) - cena rezervace posila b o o l e a n - zda jde o rezervaci do posilového spoje byznys b o o l e a n - nepoužívá se • T _ W _ S P O J _ V Y H L E D A T Struktura obsahující informace o vyhledaných spojích id_spoj i n t e g e r - id spoje cas_od timestamp - cas odjezdu cas_kam timestamp - cas prijezdu volnych i n t e g e r - pocet volných míst filmy c h a r a c t e r v a r y i n g [ ] - pole filmu pouštených ve spoji audio c h a r a c t e r v a r y i n g [ ] - pole hudby pouštené ve spoji (nepoužívá se) happy i n t e g e r - nepoužívá se economy economy - zda se jedná o economy spoj(bez služeb) 42 B.5. S L O V E N S K O B.5 Slovensko • C R E A T E _ R E Z E R V A C E Funkce pro vytvárení rezervace Návratová hodnota c h a r a c t e r v a r y i n g - prázdný retezec, pokud byla rezervace v pořádku vytvorena, nebo chybové hlášení popisující chybu id_rezervace i n t e g e r - id rezervace login c h a r a c t e r v a r y i n g - login operátora odkud i n t e g e r - id místa odjezdu kam i n t e g e r - id místa prříjezdu id_spoj i n t e g e r - id spoje, do kterého rezervujeme id_mista i n t e g e r - id místa, které má být rezervováno. Pokud je n u l l najde a vybere se volné místo automaticky osob i n t e g e r - vždy n u l l (nepoužívá se) inet b o o l e a n - zda je rezervace vytvárena online nebo na prodejním míste posila b o o l e a n - zda se rezervuje posilový nebo hlavní spoj mena c h a r a c t e r v a r y i n g - třípísmenná zkratka meny, ve které se má rezeravce vytvorřit (CZK, SKK, EUR) nacten b o o l e a n - ríká, jestli se se má lístek vytvorit jako Otevrený • E R A S E _ R E Z E R V A C E Funkce pro rušení rezervace Návratová hodnota c h a r a c t e r v a r y i n g - prázdný rřeteřzec, pokud byla rezer¬ vace v porřádku zrušena, nebo chybové hlášení popisující chybu 43 B.5. S L O V E N S K O id_rezervace id rezervace nenet b o o l e a n - zda rezervace není rušena online login c h a r a c t e r v a r y i n g - login operátora time_insert timestamp - cas rušení rezervace. Pokud je n u l l , použije se aktuální cas. Slouží hlavne pro rušení rezervací pomocí sms, jako time_insert se bere cas prřijetí sms, ne cřas jejího zpracování parserem • G E T _ R E Z E R V A C E nalezení rezervace dle jejího id Návratová hodnota t _ r e z e r v a c e - struktura t_rezervace, popsána níže id_rezervace i n t e g e r - id rezervace • GET_FIRST_SPOJ Funkce pro vyhledání nejbližšího spoje mezi dvema zastávkami v daný den od za¬ daného cřasu Návratová hodnota i n t e g e r - id spoje nebo n u l l pokud není nalezen odkud i n t e g e r - id místa odjezdu kam i n t e g e r - id místa prříjezdu datum date - den, ve kterém se má spoj vyhledat cas time - cas, od kterého se má hledat (vcetne) W _ S P O J _ V Y H L E D A T Funkce pro vyhledání spoju odjíždejících v zadaný den Návratová hodnota t _ w _ s p o j _ v y h l e d a t - struktura t _ w _ s p o j _ v y h l e d a t po¬ psána níže 44 B.5. S L O V E N S K O datum timestamp - den ve kterém se má spojvyhledat zastavka_od i n t e g e r - id místa odjezdu zastavka_do i n t e g e r - id místa prijezdu T _ R E Z E R V A C E Struktura obsahující informace o rezervaci id_rezervace i n t e g e r - id rezervace odkud i n t e g e r - id místa odjezdu kam i n t e g e r - id místa prříjezdu datum date - datum odjezdu cas t i m e - cas odjezdu sedadlo i n t e g e r - cříslo sedadla cena numeric(10,2) - cena rezervace v C Z K cena_eu numeric(10,2) - cena rezervace v EUR posila b o o l e a n - zda jde o rezervaci do posilového spoje cena_sk numeric(10,2) - cena rezervace v SKK T _ W _ S P O J _ V Y H L E D A T Struktura obsahující informace o vyhledaných spojích id_spoj i n t e g e r - id spoje cas_od timestamp - cas odjezdu 45 B.5. S L O V E N S K O cas_kam timestamp - cas príjezdu volných i n t e g e r - pocet volných míst filmy c h a r a c t e r v a r y i n g [ ] - pole filmu pouštených ve spoji audio c h a r a c t e r v a r y i n g [ ] - pole hudby pouštené ve spoji (nepoužívá se) 46