ii
České vysoké učení technické v Praze Fakulta elektrotechnická Katedra počítačů
Bakalářská práce
Ladění restauračního systému Jan Krutina
Vedoucí práce: Ing. Martin Komárek
Studijní program: Otevřená informatika, Bakalářský Obor: Softwarové systémy 22. května 2014
iv
v
Poděkování Chtěl bych poděkovat Ing. Martinu Komárkovi za odbornou pomoc a vedení mé bakalářské práce. Dále bych chtěl poděkovat rodině a přátelům za jejich podporu.
vi
vii
Prohlášení Prohlašuji, že jsem práci vypracoval samostatně a použil jsem pouze podklady uvedené v přiloženém seznamu. Nemám závažný důvod proti užití tohoto školního díla ve smyslu §60 Zákona č. 121/2000 Sb., o právu autorském, o právech souvisejících s právem autorským a o změně některých zákonů (autorský zákon).
V Praze dne 22. 5. 2014
viii
Abstract The goal of my bachelor thesis was to resolve issues in the CashBob project to make it ready for deployment to production environment. The thesis is focused on analysis, proposal of solutions and implementation of fixes of individual issues within the whole CashBob project. This thesis also deals with common software debugging methods. Keywords: CashBob, Java, Spring, debugging, REST, Conditional GET
Abstrakt Cílem mé bakalářské práce bylo odstranění problémů v projektu CashBob tak, aby byl projekt připraven pro nasazení do produkčního prostředí. Práce je zaměřena na analýzu, návrh řešení a implementaci jednotlivých řešení v rámci celého projektu CashBob. Práce rovněž pojednává o běžných metodách ladění softwaru. Klíčová slova: CashBob, Java, Spring, debugging, REST, Conditional GET
ix
x
Obsah 1 Úvod 1.1 O projektu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Cíl bakalářské práce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1 1 1
2 Ladění 2.1 Motivace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Obecný proces ladění . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3 Techniky a nástroje usnadňující ladění . . . . . . . . . . . . . . . . . . . . . .
3 3 3 4
3 Řešení nejprioritnějších problémů 3.1 Chyba č. 118: Automatické pojmenování účtu podle čísla stolu 3.2 Chyba č. 104: Zobrazit nápovědu . . . . . . . . . . . . . . . . . 3.3 Chyba č. 92: Přidat nového zaměstnance . . . . . . . . . . . . . 3.4 Chyba č. 93: Zobrazit recepturu . . . . . . . . . . . . . . . . . . 3.5 UC-10: Vytisknout sklad . . . . . . . . . . . . . . . . . . . . . . 3.6 UC-80: Nastavení adresy . . . . . . . . . . . . . . . . . . . . . . 3.7 Chyba č. 121: Automatické označení nově vytvořené buňky . . 3.8 UC-85: Smazat menu . . . . . . . . . . . . . . . . . . . . . . . . 3.9 Chyba č. 122: Updatovat buňku na pokladně . . . . . . . . . . 3.10 UC-48: Vytisknout menu . . . . . . . . . . . . . . . . . . . . . . 3.11 UC-58: Smazat kategorie . . . . . . . . . . . . . . . . . . . . . . 3.12 UC-25: Upravit důvod odpisu . . . . . . . . . . . . . . . . . . . 3.13 UC-13: Načíst čárový kód . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
7 7 8 9 11 14 16 18 19 23 24 25 26 26
4 Závěr
29
A Výsledný tisk menu
33
B Obsah přiloženého CD
35
xi
xii
OBSAH
Seznam obrázků 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13
Formulář pro vytvoření nového účtu . . . . . . . . . Dialog nápovědy . . . . . . . . . . . . . . . . . . . . Dialog vytvoření nového zaměstnance . . . . . . . . . Dialog vytvoření nové receptury . . . . . . . . . . . . Tisk materiálu do PDF s chybným kódováním . . . . Tisk materiálu do PDF finální verze . . . . . . . . . Nastavení adresy v dialogu nastavení . . . . . . . . . Nové nastavení adresy na serveru . . . . . . . . . . . Pozastavený běh programu na podezřelém místě . . . Nelze smazat menu . . . . . . . . . . . . . . . . . . . Chybová zpráva po smazání menu v modulu manažer Zpráva po aktualizaci druhu odpisu . . . . . . . . . . HTTP stavový kód 404 odchycený v debuggeru . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
8 9 10 11 16 17 18 19 20 20 22 25 26
A.1 Tisk menu do PDF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
xiii
xiv
SEZNAM OBRÁZKŮ
Seznam tabulek 3.1
Naměřené hodnoty časové odezvy metodou Conditional GET . . . . . . . . . 22
xv
xvi
SEZNAM TABULEK
Kapitola 1
Úvod 1.1
O projektu
Projekt CashBob je informační systém zaměřující se na správu a řízení restauračního podniku. Projekt vzniká již dlouhou řadu let, přispívají do něj z největší části studenti zejména v rámci svých akademických prací. Systém umožňuje jeho uživatelům provádět běžné funkce, jako je objednávání, vyřízení platby zákazníka, správa zaměstnanců a správa položek na skladě. Systém je postaven na architektuře klient-server. Klientská část systému používá k dosažení výše zmíněných funkcí čtyř základních modulů. Pokladna, Správa zaměstnanců, Správa skladu a Správa práv. Projekt poskytuje i možnost připojení přes smartphone, pomocí klientské aplikace CashWalk. Z pohledu zákazníka aplikace poskytuje možnost procházení menu a objednání jídel nebo nápojů přímo od svého stolu bez přítomnosti číšníka. Zákazník tak má možnost se připojit přes svůj smartphone k serverové části systému a objednat si přímo od stolu. Z pohledu číšníka by mělo jít k především k ulehčení při placení útraty zákazníky. Systém spočítá veškerou útratu v rámci jednoho stolu a číšník může jednoduchou operací rozdělit útratu mezi zákazníky. Provozní restaurace má přehled o zboží na skladě.
1.2
Cíl bakalářské práce
K projektu jsem se připojil ve fázi, kdy implementace je více méně hotová, nebo se implementací posledních funkčních celků zabývají jiní členové týmu. Nyní je nutné v systému odladit a otestovat nejprioritnější problémy a tím jej připravit pro reálné nasazení do produkčního prostředí. V rámci řešení nejprioritnějších problémů systému provedu jejich analýzu, návrh řešení, implementaci a testování. K projektu existuje Issue tracker1 , který popisuje současné problémy systému. Vlastní obsah bakalářské práce je rozdělen do dvou hlavních částí. V první části nastíním obecně problematiku ladění a běžné metody, které jej usnadňují. V druhé části se zaměřím na vlastní řešení problémů. 1
Issue tracker - nástroj pro správu chyb, https://redxml.felk.cvut.cz/cashbob/cashbob/issues
pro
1
systém
CashBob
je
dostupný
na
adrese
2
KAPITOLA 1. ÚVOD
Kapitola 2
Ladění 2.1
Motivace
Produkt softwarového vývoje je náchylný na chyby, které jsou během ladění odstraňovány. Testování a ladění je základním kamenem každého úspěšného vývoje, schopnost ladění softwarových systémů je potřebnou dovedností programátora. Softwarový projekt se stává v průběhu vývoje stále více komplexnějším a proces ladění se tak stává složitějším. Tato kapitola se snaží poskytnout základní porozumění problematice ladění a techniky ladění softwaru. Informace o problematice ladění byly čerpány ze zdrojů [5], [7], [15].
2.2
Obecný proces ladění
Obecný proces ladění je posloupnost kroků, které musí vykonat programátor při výskytu chyby v testovaném softwaru. Ještě před laděním chyby je nutné porozumět požadavkům na chování systému. Je potřeba rozpoznat, zdali chyba není pouze špatnou interpretací běžného chování. Proces ladění může být rozčleněn do následujících kroků: • reprodukce chyby, • lokalizace chyby, • porozumění chybě, • opravení chyby. Programátor potřebuje testovací scénář pro dosažení chybového stavu. Testovací scénář slouží k ověření opravy. Programátor musí porozumět všem vnějším faktorům, které mohou způsobovat chybový stav systému. Důležitou roli hraje prostředí, ve kterém je problém nalezen. Běh programu mohou ovlivňovat například operační systém nebo externí knihovny používané programem. Testovací scénář musí být deterministický, což nemusí být lehkým úkolem například v systémech, ve kterých dochází k paralelnímu zpracování výpočtů. 3
4
KAPITOLA 2. LADĚNÍ
Po sestavení deterministického testovacího scénáře pokračuje ladění k lokalizaci chyby. Lokalizace chyby je obecně netriviálním úkolem. Během vykonávání testovacího scénáře systému programátor analyzuje běh programu, k tomu používá ladící nástroje a techniky, které jsou předmětem sekce 2.3. Na základě získaných faktů programátor sestaví hypotézy o chybném chování systému a scénáře, jak tyto hypotézy otestovat. Častým problémem začínajících programátorů zde bývá vyvozování závěrů z nepodložených faktů. Chybě musí programátor plně porozumět, dříve než se ji pokusí opravit, v opačném případě může dojít k zanesení nových chyb do systému, navzdory tomu, že testovací scénář funguje. Programátor musí analyzovat, zda hypotéza nezachycuje pouze důsledek chybového chování systému ale jeho skutečnou příčinu. Po nalezení skutečné příčiny navrhuje možnosti řešení s analýzou, jaký dopad navrhované řešení může mít na chování systému. Ve finální fázi ladění programátor provede modifikace zdrojového kódu. Oprava chyby musí být řádně otestována. Pokud analýza možných dopadů nebyla správná, zvyšuje se pravděpodobnost výskytu nových chyb. Mezi metody jak se vyhýbat těmto situacím patří například statická a dynamická analýza kódu, které jsou popsány v sekci 2.3.
2.3
Techniky a nástroje usnadňující ladění
Výpis proměnných programu Technika výpisu stavu systému je velice rozšířená mezi začínajícími programátory pro svoji jednoduchost. Programátor přidává do zdrojového kódu instrukce pro výpis informací na standardní výstup systému, aby mohl sledovat tok programu větvemi a aktuální hodnoty proměnných. Tento způsob je časově náročný. Programátor musí přidat tyto instrukce na všech potřebných místech a po odstranění problému tyto instrukce odebrat.
Logování Logování je vylepšený systém pro výpis událostí, které nastávají při běhu programu. Jedná se o běžně používanou techniku, při které probíhá automatický sběr informací o běhu programu za účelem pozdějšího zpracování a možné diagnostiky problémů. Množství vypisovaných dat je možné kontrolovat pomocí priorit.
Práce s informačními zdroji Programátor při ladění používá informační zdroje. Velice důležitou technikou k osvojení je posouzení relevantnosti nalezených informačních zdrojů. Jako příklad špatného vyhodnocení informačních zdrojů uvádím nalezení dokumentace k řešenému problému, kde verze dokumentace neodpovídá verzi použité knihovny.
Statická analýza kódu Statická analýza kódu je soubor technik, které slouží k nalezení potenciálních chyb nad zdrojovým kódem bez nutnosti jeho spuštění. Existují softwarové nástroje provádějící statickou analýzu kódu. Opravou chyb a varování, které byly nalezeny je možné předcházet potenciálním chybám.
2.3. TECHNIKY A NÁSTROJE USNADŇUJÍCÍ LADĚNÍ
5
Dynamická analýza kódu Dynamická analýza kódu již vykonává instrukce zdrojového kódu. Během dynamické analýzy se testuje korektnost získaných výstupů programu oproti očekávaným výstupům pro konkrétní vstupy. Do dynamického testování patří zejména jednotkové a integrační testy. Nejdůležitější funkcí dynamické analýzy je předcházet regresím zdrojového kódu. Programátor po modifikaci zdrojového kódu spustí dynamickou analýzu, která ověří dopad jeho změn na systém.
Debugger Debugger je softwarový nástroj pro ladění jiných programů, napomáhá programátorovi zjistit, co zkoumaný program dělá na úrovni zdrojového kódu. Debugger umožňuje procházení zdrojového kódu za běhu zkoumaného systému s možností zastavit vykonávaný kód na určitém místě. Pomocí debuggeru lze ovládat běh programu, na zastaveném místě sledovat a měnit hodnoty jeho proměnných. Ukazuje také, které funkce programu jsou volány a v jaké funkci se právě nacházím na funkčním zásobníku. Debugger se ovládá pomocí breakpointů. Program vykonává zdrojový kód dokud nenarazí na breakpoint, kde se vykonávání programu zastaví.
6
KAPITOLA 2. LADĚNÍ
Kapitola 3
Řešení nejprioritnějších problémů Při získávání reprodukčních scénářů jsem využíval Issue tracker. Jelikož problémy jsou rozděleny v Issue trackeru do dvou kategorií, případ užití1 a chyba, zavedl jsem jednoduchou jmennou konvenci pro pojmenovávání problémů. Chybu označuji v následujícím textu vzorem Chyba č. 101: jméno chyby a případ užití označením UC-150: jméno případu užití. Zadání problémů obsahuje vždy heslovitý popis, převzatý z Issue trackeru a údaj Cesta, který reprezentuje reprodukční scénář. Testování oprav v produkčním prostředí probíhalo průběžně na školním terminále.
3.1
Chyba č. 118: Automatické pojmenování účtu podle čísla stolu
Zadání Pojmenovávání účtu - název ”osoba”, nebo ”číslostolu”, nebo složením ”číslostolu_osoba” Cesta: Pokladna, Vytvořit nový účet
Analýza Pojmenování účtu bude probíhat následujícím způsobem. Pokud je vyplněna Osoba, automaticky se vyplní Název jménem osoby, pokud bude vyplněn Stůl, vyplní se Název číslem stolu. Pokud budou vyplněny Stůl i Osoba zároveň, Název se pojmenuje sloučením jména osoby číslem stolu oddělených podtržítkem. Formulář pro vytváření účtu je zachycen na obrázku 3.1. Validaci a řízení formuláře Vytvoření nového účtu zprostředkovává ve zdrojovém kódu třída AccountSettingsForm, stav polí dialogu reprezentují její globální proměnné. Důležité jsou pro mě metody, které nastavují jméno osoby a číslo stolu. Protože jsou tyto proměnné globální, uvnitř metod znám stav všech polí formuláře a jsem schopen snadno nastavit text do pole Název. 1
český výraz pro use case
7
8
KAPITOLA 3. ŘEŠENÍ NEJPRIORITNĚJŠÍCH PROBLÉMŮ
Obrázek 3.1: Formulář pro vytvoření nového účtu
Návrh řešení Jako řešení navrhuji implementaci metody, která podle aktuálního stavu polí Stůl a Osoba nastaví hodnotu poli Název, podle popsaných pravidel v Analýze. Tato nová metoda pak bude zavolána z metod pro nastavení proměnných osoby a stolu.
Závěr Implementace proběhla úspěšně, pro účely testování jsem zvolil uživatelské rozhraní systému, vyzkoušel jsem možnosti zmíněné v Analýze.
3.2
Chyba č. 104: Zobrazit nápovědu
Zadání Nabídka nápověda zobrazuje pouze informaci o aplikaci Cashbob - Manažerský modul. Cesta: Manažerský modul, skladový modul
Analýza Nápověda by měla poskytovat komplexnější informaci než pouze zprávu, ve kterém modulu se nacházím. Po spuštění nápovědy tlačítkem O aplikaci se zobrazí dialog 3.2.
3.3. CHYBA Č. 92: PŘIDAT NOVÉHO ZAMĚSTNANCE
9
Obrázek 3.2: Dialog nápovědy
Vedoucí projektu rozhodl odstranit nápovědu z hlavní nabídky, dle jeho slov není v tuto chvíli co do nápovědy dát. Vytváření hlavních oken v modulech zajišťují třídy MainFrame, obě třídy obsahují metodu createMenu, ve které vytvářejí hlavní nabídku menu. Zakomentováním zdrojového kódu pro přidání nápovědy do hlavního menu jsem dosáhl opravy chyby.
Závěr Pomocí uživatelského rozhraní systému jsem ověřil viditelnost nápovědy, nápověda již není dostupná.
3.3
Chyba č. 92: Přidat nového zaměstnance
Zadání Při vytváření nového zaměstnance nelze vyplnit uživatelské jméno (formulář uživ.jméno není aktivní). Nelze dokončit vytvoření nového zaměstnance. Cesta: správa podniku, zaměstnanci, správa uživatelů
Analýza a návrh řešení Ve formuláři vytváření nového zaměstnance jsou všechna pole kromě pole Uživatelské role povinná. Po vyplnění všech editovatelných polí se nový uživatel nevytvoří, dojde k zobrazení chybové hlášky s textem Musí být uvedeno uživatelské jméno nového uživatele. Formulář vytváření nového uživatele reprezentuje Java třída UserForm. Při inicializaci komponent tohoto formuláře se ve zdrojovém kódu explicitně nastavuje poli Uživatelské jméno vlastnost needitovatelný. Řešením tohoto problému je odstranění explicitního zakázání editace. Zakázaná editace uživatelského jména je vidět na obrázku 3.3.
Testování Testovací prostředí systému CashBob obsahuje předdefinovaného uživatele Admin, pod tímto účtem jsem vytvořil nového uživatele. Test skončil úspěchem.
10
KAPITOLA 3. ŘEŠENÍ NEJPRIORITNĚJŠÍCH PROBLÉMŮ
Obrázek 3.3: Dialog vytvoření nového zaměstnance
Při testování opravy chyby jsem narazil na následující problém. Po úspěšném vytvoření nového uživatele jsem se odhlásil a zkusil přihlásit pod novým účtem. Přihlášení skončilo neúspěchem s chybovou hláškou Chybné přihlašovací údaje. Později jsem zjistil, že tento problém je způsoben nedostatečnými oprávněními nového uživatele. Pokud novému uživateli po vytvoření nespecifikuji žádné Uživatelské role, nově vytvořený uživatel se nebude moci přihlásit. Pokud má uživatel přiřazenou roli s právy USER_GET a ROLE_GET, může se přihlásit do systému. Systém by měl poskytovat zpětnou vazbu, že zaměstnanec vytvořený bez patřičných práv se nebude moci přihlásit.
Závěr Implementace proběhla úspěšně, po povolení editace pole Uživatelského jména systém začal korektně vytvářet nové uživatele. Pro zpětnou vazbu systému jsem vytvořil dialog, který o situaci informuje uživatele. Autor už není součástí vývojového týmu, nepodařilo se mi zjistit důvod explicitního zakázání editace Uživatelského jména.
3.4. CHYBA Č. 93: ZOBRAZIT RECEPTURU
3.4
11
Chyba č. 93: Zobrazit recepturu
Zadání Při zvolení nějaké existující položky menu se zaktivní možnost RECEPTURA, ale ta po kliknutí nereaguje (nic se nezobrazí). Cesta: Správa podniku, Menu, Vytvořit položku menu.
Popis problému Receptura slouží v systému CashBob k propojení položek menu se skladovými záznamy. Funkcionalita receptury je důležitá pro inventuru. K vybrané položce menu vytvořím recepturu, pokud jsem poté prodal položku menu, ze skladu bude odečteno odpovídající množství surovin specifikovaných na receptuře. Po opravení triviálních výjimek se zobrazí dialog pro vytvoření receptury, ten ale nezobrazuje žádná data. Pomocí reverzního inženýrství jsem zjistil, jak by uživatel měl dialog používat. Hodnoty comboboxů2 se stáhnou přes rozhraní REST[11] ze serveru bez uživatelské akce. Uživatel zadefinuje množství dané suroviny a stiskne tlačítko vytvořit záznam, tento záznam se pomocí REST rozhraní uloží do databáze. Po této akci se v tabulce viditelné v dolní polovině dialogu zobrazí nově vytvořený záznam, který uživatel může editovat nebo smazat. Problémy dialogu pro recepturu jsou dobře vidět z obrázku 3.4.
Obrázek 3.4: Dialog vytvoření nové receptury
2
Combobox - grafický prvek, který po kliknutí zobrazí seznam hodnot, z nich si uživatel jednu vybere.
12
KAPITOLA 3. ŘEŠENÍ NEJPRIORITNĚJŠÍCH PROBLÉMŮ
Analýza Dialog pro správu receptury reprezentuje třída ReceiptDialog. Hlubší analýzou třídy ReceiptDialog jsem zjistil, že tato třída pro veškerou svoji funkcionalitu používá ke komunikaci se serverem již nepodporované rozhraní RMI3 . Tato třída používá metody třídy ManagerViewController, jejichž implementace je zakomentována, situaci vystihuje ukázka zdrojového kódu 3.1. ReceiptDialog má tedy následující problémy. • Popisek ’Položka menu’ je prázdný • combobox ’Druh suroviny’ nezobrazuje žádné hodnoty • combobox ’Surovina’ nezobrazuje žádné hodnoty • combobox jednotek nezobrazuje žádné hodnoty • Tlačítko ’Vytvořit záznam’ není obsluhováno • Tlačítko ’Smazat záznam’ není obsluhováno • Tabulka v dolní polovině dialogu není plněna daty ze serveru
Požadavky na implementaci Je potřeba obnovit komunikaci klient-server pomocí podporovaného rozhraní REST. Na straně klienta je komunikace se serverem realizována pomocí tříd REST komunikátorů. Nyní je implementace ve stavu, kdy každý modul vlastní svojí sadu komunikátorů. Dialog receptury je umístěn v modulu manažer, ovšem pro správné dotahování hodnot pro comboboxy vyžaduje komunikátory dostupné v modulu sklad. Jelikož modul manažer neobsahuje Maven závislost[10] na modul sklad, tyto komunikátory jsou nedostupné. Další analýzou zdrojového kódu jsem zjistil, jak je problematika sdílených komunikátorů řešena v ostatních modulech. Tyto problémy se řešili kopírováním potřebných komunikátorů mezi moduly. Klientská část systému navíc neposkytuje komunikátor pro REST zdroj receptury, bude potřeba vytvořit nový. Na serverové straně receptuře odpovídá třída Usedmaterial, ta je pouze definovanou databázovou entitou, ale neexistuje pro ni rozhraní REST pro základní CRUD4 operace, potřebné pro fungování receptury. Pro detailnější pochopení CRUD operací nad REST zdroji doporučuji [11].
Návrh řešení Pro klientskou stranu navrhuji dva možné způsoby řešení, kopírování komunikátorů mezi moduly vylučuji jako řešení dle principu ”Don’t repeate yourself” [9]. Prvním řešením je přidání Maven závislosti modulu manažer na modul sklad. Takto řešit problémy sdílených komunikátorů lze pouze do chvíle, dokud nebudou vznikat cyklické 3 4
RMI - Remote Method Invocation CRUD - základní operace pro vytvoření, čtení, aktualizaci a smazání dat
3.4. CHYBA Č. 93: ZOBRAZIT RECEPTURU
13
závislosti mezi moduly[10]. Nicméně pro aktuální rychlé opravení problému by bylo řešení postačující. Jako druhé řešení navrhuji přesunout všechny komunikátory na jedno místo. Za nejvhodnějšího existujícího kandidáta považuji modul CashBobRestLib, protože moduly pokladna, manažer i sklad obsahují závislost na tento modul a zároveň tento modul již obsahuje základní podporu REST komunikace. Po sjednocení komunikátorů bude přidán nový pro správu receptury. Pro serverovou část vytvořím rozhraní REST obsluhující zdroj receptury.
Implementace Po návrhu řešení vedoucímu projektu jsem dostal za úkol provést sjednocení komunikátorů do CashBobRestLib. Každý modul poskytující komunikátory obsahuje třídu RemoteFactory, pomocí které jsou tyto komunikátory získávány. Třídy RemoteFactory jsem sjednotil do jedné třídy RemoteFactory a umístil ji do modulu CashBobRestLib. Při vytváření komunikátorů je potřeba těmto komunikátorům předat objekt třídy RestClient. V současné implementaci je tento objekt vytvořen při startu klientské části systému, a předáván mezi moduly. Tento objekt je možné vytvářet pouze v modulech, které mají Maven závislost na modulu CashBobStorage a umí tak přečíst konfigurační soubor s nezbytnými informacemi pro jeho konstrukci. Modul CashBobRestLib nemůže mít Maven závislost na modul CashBobLibrary, protože by došlo k cyklické závislosti. Rozhodl jsem se tedy objekt RestClient vytvořit při startu aplikace a předat jej třídě RemoteFactory. Při vytváření komunikátorů se vždy znovu použije již existující RestClient ve třídě RemoteFactory a tak nebude vznikat v systému více objektů třídy RestClient. Cyklické závislosti by bylo možné se vyhnout vyčleněním implementace logiky třídy ConfigFileParser z modulu CashBobLibrary do samostatného modulu, který by pak byl přidán jako Maven závislost do CashBobRestLib modulu i CashBobLibrary modulu. Vedoucí práce mi oznámil, že nyní tento postup není nutný, pro současné požadavky na implementaci postačí předcházející řešení. Dalším problémem bylo, že předchozí implementace rozlišovala položky comboboxů pouze podle jejich jména a k dotahování údajů ze serveru používala jako unikátní identifikátor právě toto jméno. Surovina ale jde vytvořit se jménem, které nemusí být unikátní. Pro plnění comboboxy daty jsem využil mechanismus, který se používá v modulu sklad, kde jednotlivé položky comboboxu nejsou pouze jména DTO5 objektů ale celé DTO objekty. V uživatelském rozhraní se pak zobrazí v comboboxu pouze jméno ale pro stahování dat ze serveru mohu vytvořit HTTP6 požadavek pomocí ID, které je již unikátní. V uživatelském rozhraní jsem také zamezil zobrazování sloupce ID, podle mého názoru by neměl být uživateli zobrazován. Na straně serveru jsem vytvořil rozhraní REST pro entitu UsedMaterial s CRUD operacemi a navíc s operací getByMenuItem, která vrací pro položku menu všechny použité receptury a tím poskytuje data pro tabulku v dolní polovině dialogu. 5 6
DTO - data transfer object HTTP - Hypertext Transfer Protocol
14
KAPITOLA 3. ŘEŠENÍ NEJPRIORITNĚJŠÍCH PROBLÉMŮ
Testování Pro testování serverového rozhraní REST jsem vytvořil jednotkový test nazvaný UsedMaterialApiTest, ve kterém testuji všechny vytvořené metody tohoto rozhraní. Testovací metody jsem označil anotací @Transactional, framework Spring zajistí vyčištění testovací databáze po ukončení testu [12]. Pokud v databázi zůstávají nesmazané objekty, může dojít k vytváření závislostí mezi jednotkovými testy, což často vede k špatně odhalitelným chybám. Důvody nevytváření závislostí mezi jednotkovými testy jsou také popsány v knize [9].
Závěr Podařilo se mi obnovit dialog pro manipulování s recepturou. Při analýze jsem ve zdrojovém kódu objevil mechanismus pro převádění jednotek, který jsem zanechal v původní implementaci. Mechanismus převádí zvolené jednotky v comboboxu oproti základním jednotkám zvolené suroviny. Například pokud je materiál v základní jednotce litr, při zadání množství 500 ml, se vytvoří receptura s množstvím 0,5 litru. Mechanismus ale nekontroluje korektnost převáděných jednotek, například lze převádět gram na litr, výsledek pak končí vždy hodnotou 0,0 u vytvořeného materiálu. Pro CRUD operace s jednotkami slouží komunikátor IUnitTypeCommunicator. Metody pro vytvoření, mazání a aktualizace jednotky se ale v systému CashBob nikde nepoužívají. Z toho důvodu usuzuji, že dané jednotky se dostávají do systému pouze přes databázový skript. Nutno tedy podotknout, že databázový skript musí být správně namapován na ’hardcoded’ převodník jednotek, to znamená že ID jednotek v databázi musí odpovídat ID Java výčtového typu UnitTypeIdEnum. Vzhledem k tomu, že systém CashBob má být dostupný nejen pro český trh, dané měrné jednotky a koeficienty jejich převodu se mohou lišit v jednotlivých státech a v současné době neexistuje možnost, jak je pomocí uživatelského rozhraní nadefinovat. Vedoucí projektu rozhodl, že tento problém není v tuto chvíli prioritou. Listing 3.1: Ukázka nepodporovaného rozhraní RMI ve zdrojovém kódu public String getMenuItemNameById ( int menuItemId ) { // try { // return ServiceFacade . getInstance () . // getMenuItemById ( menuItemId ) . getName () ; // } catch ( Exception ex ) { // return null ; // } return null ; }
3.5
UC-10: Vytisknout sklad
Zadání Tisk do souboru .csv funguje. Tisk do .pdf a na tiskárnu hlásí chybovou hlášku. Cesta: Klient, Správa skladu, Sklad, Skladové záznamy, Vytisknout
3.5. UC-10: VYTISKNOUT SKLAD
15
Popis problému Po dohodě s vedoucím práce se problém tisku rozpadl na následující podproblémy. Tisk na tiskárnu nebude pro tisk materiálu podporovaný, tato možnost tedy bude skryta. Zůstává tisk do formátů PDF a tisk do CSV. Tisk do PDF nefunguje.
Analýza K tisku do PDF se v systému CashBob používá knihovna Jasper Reports [1]. Ke konfiguraci tisku slouží vždy po dvojici souborová rozšíření jrxml a jasper. Jrxml je xml definice, ve které se nadefinuje jak má výsledný PDF soubor vypadat. Pomocí jasper template kompilátoru se z definice jrxml vytvoří jasper template, kterou pak lze při běhu programu naplnit parametry a použít pro tisk do PDF i do CSV. V systému Cashbob se templaty používají pouze k tisku do PDF. K tisku v systému Cashbob je určena Java třída Printer. Po opravení triviálních problémů se objevili nové problémy. Jasperová templata očekává parametry, které nejsou dodávány datovým modelem pro její naplnění. Datový model poskytuje ke každé jednotce materiálu jméno materiálu, aktuální počet materiálu, typ materiálu, jednotky materiálu, minimální množství a hustotu. Jasperová templata ovšem vyžaduje navíc pole čárový kód, které není v datovém modelu pro tisk přístupno, proto nelze vytisknout materiál do PDF.
Návrh řešení Pro opravu chyby navrhuji parametry vhodně spárovat v datovém modelu a v templatě tak, aby bylo možné podívat se, jak vypadá vytisknutý materiál v PDF pro další možné ladění. To znamená nahradit parametr templaty čárový kód parametrem hustota, který je dostupný v datovém modelu.
Implementace Obrázek 3.5 ukazuje výsledný PDF soubor materiálu po takto popsané úpravě, jsou z něj vidět problémy s textem. Příčina těchto problémů byla v defaultním kódování PDF, to bylo nastavené na hodnotu CP1252, toto kódování ale neobsahuje veškeré potřebné znaky českého jazyka. Změna kódování z CP1252 na CP1250 problém s chybějícími písmenky abecedy vyřešila. Skrytí možnosti tisknout na tiskárnu v comboboxu bylo triviální. Chybějící části textu v hlavičce tabulky vyřešilo zmenšení písma z velikosti 14 na velikost 10. Výsledný PDF soubor tisknutého materiálu je na obrázku 3.6.
Závěr Pro kompilaci definice jrxml do jasper templaty jsem použil nástroj JasperStarter [8]. Technologie Jasper Reports byla pro mě naprosto neznámá, autor navíc již není součástí vývojového týmu, analýza problému spolu s jeho řešením mi tedy zabrala více času, než jsem původně očekával. Správnost řešení jsem otestoval v uživatelském rozhraní systému.
16
KAPITOLA 3. ŘEŠENÍ NEJPRIORITNĚJŠÍCH PROBLÉMŮ
Obrázek 3.5: Tisk materiálu do PDF s chybným kódováním
3.6
UC-80: Nastavení adresy
Zadání Přesunout nastavení adresy (fyzická adresa provozovny, město, tel, DIČ) na server. Každý připojený klient pak získá nastavení adresy přímo ze serveru. cesta: Server
Popis problému Jedná se o údaje telefon, číslo DIČ, adresa a jméno společnosti. Nyní se tyto údaje nastavují na klientské části systému. Systém by mohl na serverové části poskytovat informace tak, aby si všichni klienti mohli stáhnout informace přes rozhraní REST. Tímto se ulehčí práce zaměstnancům se zbytečnou konfigurací. Adresu lze změnit v dialogu nastavení, které je vidět na obrázku 3.7.
Analýza Na klientské straně se informace o prodejně ukládají do konfiguračního souboru mainConfig.cfg. Pomocí Java třídy ConfigFileParser se načtou údaje z tohoto souboru, které jsou poté dostupné přes ’gettery’ této třídy. Adresa z parseru se získává na třech místech a to při načítání dat do dialogu nastavení, při vykonávání metod tisku a tisku účtenky. Na těchto místech tedy bude potřeba změnit způsob získávání dat na rozhraní REST ze serverové části.
3.6. UC-80: NASTAVENÍ ADRESY
17
Obrázek 3.6: Tisk materiálu do PDF finální verze
Návrh řešení Na klientské části systému bude potřeba vytvořit nový komunikátor pro zdroj adresa. Na serverové straně bude potřeba udělat nové rozhraní REST, které bude sloužit k manipulaci s adresou, bude také potřeba vytvořit nový DTO objekt pro výměnu informací o adrese mezi klientem a serverem. Pro současné účely postačí pouze implementace metody pro získání adresy ze serveru. Klient tedy bude mít možnost číst adresu nakonfigurovanou na serveru, nikoliv ji aktualizovat.
Implementace Na straně serveru jsem analogicky podle modulu CashBob vytvořil konfigurační soubor mainConfig.cfg, který obsahuje čtyři výše zmíněné informace o adrese. Nepovažuji za nutné pro adresu vytvářet databázovou entitu, protože by pak měla pouze jeden záznam. Java třída ConfigFileParser obsahuje metody pro získání veškerých dat. Serverová část potřebuje ale pouze metody pro získávání adresy, ostatní jsou zbytečné. Proto jsem se rozhodl rozdělit tuto třídu na třídy ConfigParserClient a ConfigParserServer, které rozšiřují třídu ConfigParser. Nově vytvořená třída ConfigParser obsahuje protected7 konstruktor, aby se zamezilo možnosti jejímu explicitnímu vytváření a sdílenou funkcionalitu pro obě rozšiřující třídy. Při konzultaci s vedoucím práce vyšlo najevo, že zákazník by neměl nastavovat adresu pomocí konfiguračního souboru mainConfig.cfg, ale prostřednictvím uživatelského rozhraní systému. Dostal jsem tedy za úkol pro nastavení adresy vytvořit dialog. Serverový dialog obsahoval pouze lokalizační dialog, který jsem zakomponoval do nově vytvořeného dialogu pro nastavení adresy. Ukázka serverového dialogu nastavení je vidět na obrázku 3.8. Na klientské části systému jsem údaje o adrese v dialogu nastavení označil jako needitovatelné. 7
protected - klíčové slovo jazyka Java
18
KAPITOLA 3. ŘEŠENÍ NEJPRIORITNĚJŠÍCH PROBLÉMŮ
Obrázek 3.7: Nastavení adresy v dialogu nastavení
Závěr Přesunul jsem mechanismus získávání adresy z klienta na server. Pro testování rozhraní REST jsem vytvořil testovací Java třídu AddressApiTest, která testuje získání adresy ze serveru. Pro ověření korektnosti řešení pro případy zmíněné v analýze a pro otestování lokalizace nově vytvořeného dialogu jsem použil uživatelské rozhraní systému CashBob.
3.7
Chyba č. 121: Automatické označení nově vytvořené buňky
V dialogu ’Zvolte menu’ po vytvoření nového menu není vybráno žádné menu. Mělo by být automaticky vybráno nově vytvořené menu. Cesta: Pokladna, Výběr libovolného účtu, Editovat položky menu, Kliknutí na prázdnou buňku, Přidat menu, nový.
Popis problému Na tento problém narazil vedoucí práce při testování uživatelského rozhraní pokladny, výběr menu je odznačen a stisknutím tlačítka OK se nic neděje. Pokud se automaticky vybere nově vytvořené menu po jeho vytvoření, nebude již docházet k těmto matoucím situacím a uživatel bude ušetřen vyhledávání nově přidaného menu v seznamu.
3.8. UC-85: SMAZAT MENU
19
Obrázek 3.8: Nové nastavení adresy na serveru
Analýza a návrh řešení Za správu menu zodpovídá Java třída EditMenu. Současná implementace pomocí komunikátoru IMenuCommunicatorPokladna vytvoří nové menu. Pokud bylo vytvoření menu úspěšné, systém provede refresh dialogu pomocí dalšího RESTového volání a získá kompletní seznam menu, včetně nově vytvořeného. K vytvoření menu se jako parametr používá pouze jméno nového menu, pokud jméno nového menu není v rámci databáze unikátní, vytvoření nového menu nebude úspěšné. Jméno si uložím do proměnné a využiji ho při vyhledávání v získaném kompletním seznamu.
Závěr Implementace byla triviální, problémy nenastaly. Správnost opravy jsem otestoval pomocí uživatelského rozhraní systému.
3.8
UC-85: Smazat menu
Zadání Nelze smazat menu, funguje pouze vkládání nového. Cesta: Klient, Správa podniku, Menu, Spravovat položky menu.
Popis problému a prvotní analýza Při stisknutí tlačítka smazat nebo aktualizovat menu se na standardním výstupu objevuje výjimka java.lang.String cannot be cast to java.net.URI. Na obrázku 3.9 je vidět, že výjimka je způsobena přístupem ke špatnému indexu při získávání URI ve zdrojovém kódu (URI) table.getModel().getValueAt(row, 3), po změně indexu sloupce z 3 na 1 podle obrázku jsem se dostal o krok dále k novým problémům. Po stisknutí tlačítka smazat menu se objevuje
20
KAPITOLA 3. ŘEŠENÍ NEJPRIORITNĚJŠÍCH PROBLÉMŮ
Obrázek 3.9: Pozastavený běh programu na podezřelém místě
chybová zpráva znázorněna na obrázku 3.10
Obrázek 3.10: Nelze smazat menu Na standardním výstupu klientské části systému se objevila výjimka SEVERE: errorCode Http stavový kód s číslem 500 znamená problémy na serverové části systému. Z informací získaných analýzou logu serveru lze vyvodit následující problémy. Smazáním menu by byla porušena integrita databáze, protože menu je odkazováno z jiných entit. Pro práci s databází jsem využíval databázového klienta SquirrelSQL, pro jeho konfiguraci jsem postupoval dle [13]. Pomocí programu SquirrelSQL a analýzou databázových entit jsem zjistil, že entita Menu je odkazována z CustMenuNode a Cell. Menu v systému CashBob má celkem dvě reprezentace. První pomocí entity Cell, tato entita je rodičovskou třídou pro entity CellMenuItem a CellMenu. Dědičnost je v systému realizována pomocí Hibernate dědičnosti [6], potomci entity Cell pak slouží jako reprezentace menu a položky menu na pokladně. Entitu Cell dále označuji názvem buňka. V modulu manažer je menu reprezentováno entitou CustMenuNode, analogicky pro ní existují entity CustMenuNodeMenu a CustMenuNodeMenuItem, dále je nazýváno zákaznickým menu. : null httpCode:500.
3.8. UC-85: SMAZAT MENU
21
Zákaznické menu reprezentuje menu pomocí stromové struktury, menu a položek menu. Při analyzování zdrojových kódů systému jsem odhalil další problém spojený s mazáním menu. Buňky viditelné na pokladně se neaktualizují podle změn zadaných v modulu manažer. Změna názvu menu, ceny položky menu se neprojeví na pokladně. Pro co největší rychlost práce s pokladnou jsou buňky na pokladně stahovány ze serveru, až když jsou potřeba8 , a cachovány9 . Takto uložené v paměti se již dále žádným způsobem neaktualizují, změny se projeví až po restartu klientské části systému. Na základě objevení těchto skutečností jsem byl vedoucím projektu požádán o následující úpravy. Při smazání menu v modulu manažer se na pokladně smažou rovněž všechny buňky spojené s tímto menu. V zákaznickém menu se menu nebude přímo mazat, ale v databázi se mu nastaví příznak, zdali je dostupné. Zákaznické menu s takto nastaveným příznakem již nebude nikde zobrazováno. Změny v manažerském modulu se budou projevovat na pokladně.
Analýza problému s mazáním menu a návrh řešení Ze zavedení nesmazatelnosti entity CustMenuNode plyne, že ani entitu Menu nebude možno smazat kvůli referenční integritě, bude pro ní rovněž nastavován příznak. Pro entity budou vytvořeny SQL dotazy pro nastavení příznaku při mazání, DAO10 vrstvy budou upraveny tak, aby používaly nové dotazy. Posledním krokem bude zpřístupnit DAO vrstvy přímo v Java třídě MenuServiceImpl, aby je bylo možné použít v metodě remove při mazání. V této metodě se poté provede smazání zákaznického menu, smazání buněk a smazání menu.
Analýza problému s aktualizací buňky na pokladně Jak již bylo zmíněno, buňky na pokladně mohou být dvojího typu, položka menu nebo menu. Pokud uživatel poprvé po vstupu do pokladny vybere menu, klientská část systému stáhne data přes REST rozhraní, a uloží je do paměti. Při každém následujícím zobrazení se menu uložené v paměti již nestahuje ale rovnou se vezme z paměti. Pro synchronizaci dat navrhuji použít metodu Conditional GET [3], ta zajistí synchronizaci dat s modulem manažer a oproti obyčejné HTTP metodě GET nabízí optimalizaci rychlosti. Komunikace pomocí conditinal get probíhá následovně. Klient pošle HTTP požadavek na server při prvním HTTP požadavku, server načte data z databáze, vypočítá pro ně hash a ten pošle odpovědí spolu s požadovanými daty. Klient si uloží data a hash. S každým dalším HTTP požadavkem klient posílá svůj hash. Server při zpracovávání požadavku vypočítá nový hash pro požadované data, pokud se hash pro požadované data shoduje s hashem v požadavku klienta, server odešle odpověď se stavovým kódem 304 - nezměněno. To znamená, že klient má u sebe k dispozici data v aktuální verzi a komunikace bude zjednodušena o přenos požadovaných dat. Bude potřeba upravit Java třídu RestClient aby dokázala používat mechanismus Conditional GET. Dále bude potřeba upravit v REST rozhraní CellApi metodu getCellsByParentMenu pro získávání buněk podle rodičovského menu, tato metoda bude počítat hash pro požadované objekty z databáze a provádět validaci klientského hashe. 8
Lazy loading cachování - počeštělý výraz pro anglické slovo cache 10 DAO - Data access object - rozhraní pro práci s databází 9
22
KAPITOLA 3. ŘEŠENÍ NEJPRIORITNĚJŠÍCH PROBLÉMŮ Nástroj ApacheBenchmark IntellijIdea Maven
MinRT12 83ms 104ms 89ms
MaxRT13 209ms 194ms 171ms
AvgRT14 109ms 125ms 95ms
Tabulka 3.1: Naměřené hodnoty časové odezvy metodou Conditional GET
Implementace Conditional GET Vytvořil jsem generickou třídu CashedObject, která uchovává data ukládaná do cache s hodnotou jejich hashe. Pro detekci změny dat v databázi jsem použil Hibernate verzování [6] a dále pro výpočet hashe jsem vytvořil třídu EntityTagHelper, která podle jednotlivých verzí požadovaných objektů vytvoří jednoznačný textový řetězec. Pro konverzi řetězce do MD5 hashe jsem použil zdrojový kód převzatý a upravený z [14].
Obrázek 3.11: Chybová zpráva po smazání menu v modulu manažer
Implementace odstranění menu Pro mazání entit CustMenuNode jsem vytvořil rekurzivní algoritmus. Rekurzivní algoritmus zde slouží k načtení celé stromové struktury do Java kolekce. Pro každou položku této kolekce poté nastavím příznak na není dostupný a změny uložím do databáze.
Testování Pro testování metody byParent v Java třídě CellApi jsem vytvořil jednotkový test pro měření výkonu systému. Jednotkový test testuje rychlost odezvy jednoho klienta při počtu deseti požadavků za sebou. Test jsem spouštěl příkazem mvn test -Dtest=CellCommunicatorPerformance a v programu IntellijIdea11 . Druhým nástrojem je testování pomocí nástroje ApacheBenchmark [4]. Pro účely testu jsem použil příkaz ab -A admin:1234 -n 10 -c 1 https://127.0.0.1:5551/rest/cell/byParent/-1. Tento příkaz zaručí stejné testovací podmínky jako výkonnostní jednotkový test. 11
IntellijIdea - Java IDE
3.9. CHYBA Č. 122: UPDATOVAT BUŇKU NA POKLADNĚ
23
Závěr Data mezi pokladnou a manažerem jsou nyní synchronizována. Naměřené hodnoty v tabulce 3.1 ukazují naměřené hodnoty časových odezev při použití metody Conditional GET. Čas potřebný k načtení požadované úrovně buněk na pokladně se sníží v průměru o 95 - 109 ms o proti klasické metodě GET, při počtu 40 stahovaných buněk. Po otestování na školním terminálu byla odezva systému přijatelná, test probíhal při nasazení klient-server na jednom zařízení. Pokud na pokladně otevřu buňku menu, které bylo mezitím smazáno v modulu manažer, objeví se chybová zpráva znázorněna na obrázku 3.11. Jako další možnou optimalizaci navrhuji metodu Push Notification [2].
3.9
Chyba č. 122: Updatovat buňku na pokladně
Zadání Při updatování buňky menu na pokladně kliknutím na tlačítko OK nereaguje. Cesta: Pokladna, libovolný existující účet, editovat položky menu, nad existující buňkou udělat operaci přidat
Analýza Při provedení reprodukčního scénáře se v posledním kroku po stisknutí tlačítka OK objeví na standardním výstupu klientské části systému výjimka NullPointerException. Současná implementace se snaží při označení neprázdné buňky pomocí metody deleteCell Java třídy CellCommunicator nejprve tuto buňku smazat. K úspěšnému smazání buňky je potřeba znát jeho rodičovské menu, pro jeho odstranění z cache. V tomto případě, kdy má uživatel otevřeno menu nejvyšší úrovně, rodičovské menu přepisované buňky je nastaveno na hodnotu null, program skončí výjimkou. První možností opravy chyby je ošetření zdrojového kódu tak, aby v případě když se uživatel nachází v nejvyšší úrovni menu, rodičovskému menu nastavil identifikátor s hodnotou -1, pod tímto identifikátorem se nyní nejvyšší menu ukládá do cache. Druhá možnost je zablokování tlačítka přidat menu a přidat položku menu ve chvíli, kdy vyberu neprázdnou buňku. Vedoucí práce se rozhodl pro druhé řešení.
Závěr Implementace byla triviální. Správnost řešení jsem ověřil pomocí uživatelského rozhraní systému. 12
MinRT – Minimální čas odezvy MaxRT – Maximální čas odezvy 14 AvgRT – Průměrný čas odezvy 13
24
3.10
KAPITOLA 3. ŘEŠENÍ NEJPRIORITNĚJŠÍCH PROBLÉMŮ
UC-48: Vytisknout menu
Zadání UC48 Vytisknout menu Netiskne, nezahlásí žádnou chybu. Cesta: Klient, Správa podniku, Menu, Spravovat položky menu.
Analýza problému Po stisknutí tlačítka Vytisknout menu se nic neděje. Na standardním výstupu se objevuje výjimka java.lang.ClassCastException: java.net.URI cannot be cast to java.lang.String. Tato výjimka vzniká ze stejné příčiny, jako v podkapitole 3.8, její řešení proběhlo obdobně. Analýzou zdrojového kódu jsem zjistil, že tisk menu používá pro získávání dat ze serveru nepodporované RMI rozhraní. Pro operace s REST zdrojem CustMenuNode již v systému existuje komunikátor IMenuCommunicator, ten ale neposkytuje metodu pro získání menu a všech jeho podsložek. Serverová část systému k manipulaci se zákaznickým menu využívá REST rozhraní CustMenuNodeApi. Tato Java třída obsahuje obslužnou metodu RESTových volání getNodesByParentMenu, která k danému menu vrátí všechny podsložky první úrovně.
Návrh řešení Po konzultaci s vedoucím práce jsem dostal za úkol vytisknout menu kompletně, se všemi úrovněmi podpoložek. Jako řešení navrhuji na serverové straně znovu použít rekurzivní algoritmus, kterým se získávají všechny podsložky menu. Kolekci obdrženou rekurzivním algoritmem bude potřeba na serverové straně vhodně seřadit pro tisk a odeslat na klientskou část systému, která Java kolekci pouze vytiskne.
Implementace Pro získání celého podstromu jsem v komunikátoru vytvořil metodu List
getCustMenuWithSubmenus(Long menuId). Na serverové straně jsem metodu getNodesByParentMenu upravil tak, aby používala rekurzivní algoritmus a poskytovala tak všechny podsložky menu. Při implementování jsem narazil na další problémy. Java třída Printer obsluhující tisk do formátu CSV dokázala využít jako zdroj dat pouze Java třídu JModel. Vytvořil jsem generickou Java třídu PrintableCollection. Tato třída obsahuje generickou Java kolekci dat a generické rozhraní pro převod mezi Java třídou JModel a kolekcí. Po dohodě s vedoucím projektu jsem zamezil tisknutí menu na tiskárnu, tato funkce nebude v prvním nasazení systému podporována. Listing 3.2: Rozhraní převádějící mezi datovými zdroji public interface CollectionToTableAdapter { public TableModel adapt ( Collection collection ) ; }
3.11. UC-58: SMAZAT KATEGORIE
25
Pro použití tisku kolekce do CSV stačí implementovat rozhraní uvedené v ukázce zdrojového kódu 3.2. Poté pomocí kolekce dat spolu s implementovaným rozhraním vytvořit objekt PrintableCollection, který již lze předat jako datový model dialogu tisku.
Závěr Tisk menu vyžadoval zprovoznění funkcionality tisku do PDF i do CSV. Při problému tisku do PDF se objevily podobné problémy jako v podsekci 3.5. Problémy jsou vyřešeny, tisk menu do PDF i do formátu CSV jsem otestoval pomocí uživatelského rozhraní systému a jednotkového testu. Výsledné PDF s vytisknutým menu je přiloženo v příloze A.1.
3.11
UC-58: Smazat kategorie
Zadání Nelze smazat vybranou kategorii. Cesta: Klient, Správa podniku, Menu, Vytvořit položku menu, Kategorie.
Analýza Tlačítko smazat kategorii nereaguje. Na standardním výstupu klientské části systému se objevuje výjimka ClassCastException: java.lang.Long cannot be cast to java.lang.Integer. Tuto výjimku jsem vyřešil obdobným způsobem podle podsekce 3.8. V současné implementaci mazání kategorie zajištuje třída ManagerController v metodě deleteMenuItemType. Tato metoda obsahuje zakomentovaný zdrojový kód s informací TODO předělat na REST. Na klientské části systému pro operace se zdrojem kategorie je již hotový komunikátor MenuItemTypeCommunicator. Na serverové části systému rovněž pro kategorii existuje REST rozhraní MenuItemTypeApi, které poskytuje metodu delete. Tyto existující implementace použiji pro předělání funkcionality z RMI na REST.
Závěr Přepojením funkcionality se ukázalo, že kategorie nemůže být smazána kvůli integritnímu omezení. Kategorie je odkazována z položky menu. Vedoucí projektu rozhodl, že kategorie nebude možné mazat. Pro zamezení smazání kategorie jsem skryl zobrazování tlačítek pro mazání.
Obrázek 3.12: Zpráva po aktualizaci druhu odpisu
26
KAPITOLA 3. ŘEŠENÍ NEJPRIORITNĚJŠÍCH PROBLÉMŮ
3.12
UC-25: Upravit důvod odpisu
Zadání Nelze upravit důvod odpisu. Cesta: Klient, Správa skladu, Sklad, Důvody odpisu
Analýza Po provedení kroků reprodukčního scénáře systém zobrazí informační dialog, který je vidět na obrázku 3.12. Na standardním výstupu klientské části systému se objevuje výjimka RestClientException, která v sobě nese HTTP stavový kód, ten má hodnotu 405. Tato hodnota znamená, že požadovaná metoda serveru není povolena. Po další analýze se ukázalo, že klient při aktualizaci zdroje ReasonType posílá PUT požadavek na URL15 /reasontype. Na serverové části toto URL obsluhuje metoda getAllReasonTypes, která je označená Java anotací @GET a slouží k získání všech důvodů odpisů. Pro aktualizaci zdroje by se měl posílat PUT požadavek na URL /reasontype/id, kde id je identifikátor odpisu.
Závěr Opravil jsem špatné URL pro aktualizaci důvodu odpisu v komunikátoru ReasonTypeRestCommunicator. Implementace byla triviální, nesetkal jsem se s problémy. Při testování opravy jsem narazil na další chybu. Pokud existuje odpis, který odkazuje důvod odpisu, nebude možné tento důvod odpisu smazat. Tato chyba je uživateli oznámena chybovým dialogem Záznam nebyl smazán. Chybovou hlášku jsem nahradil hláškou Záznam nemůže být smazán, protože se na něj odkazují existující záznamy, která lépe popisuje stav situace. Problém jsem otestoval pomocí uživatelského rozhraní systému.
Obrázek 3.13: HTTP stavový kód 404 odchycený v debuggeru
3.13
UC-13: Načíst čárový kód
Funkce načíst čárový kód vypisuje chybu při komunikaci se serverem. Cesta: Klient, Správa skladu, Sklad, Výdej surovin ze skladu 15
URL - Uniform resource locator
3.13. UC-13: NAČÍST ČÁROVÝ KÓD
27
Analýza Po analýze stacktrace a odchycení běhu programu na podezřelém místě jsem zjistil, že komunikace klient-server je v pořádku. Pokud serverová část systému nenalezne podle daného čárového kódu entitu Packagetype v databázi, vrátí HTTP odpověd s HTTP stavovým kódem 404 - nenalezeno. Odchycení tohoto problému v debuggeru je vidět na obrázku 3.13. Klient očekává návratovou hodnotu 200 - OK a tedy běh programu skončí výjimkou. Výjimka je zde špatně interpretována matoucí chybovou hláškou.
Návrh řešení a implementace Pro řešení problému navrhuji na klientské části systému implementovat jemnější rozlišení příchozích výjimek ze serverové části. Výjimka obsahující návratový kód s hodnotou 404 - nenalezena zobrazí chybovou zprávu Položka nenalezena. Zkuste ruční vyhledávání. Pro ostatní případy bude zobrazena hláška Chyba při komunikaci se serverem.
Závěr Požadované změny jsem implementoval, chybové stavy jsem otestoval pomocí uživatelského rozhraní systému.
28
KAPITOLA 3. ŘEŠENÍ NEJPRIORITNĚJŠÍCH PROBLÉMŮ
Kapitola 4
Závěr V rámci mé bakalářské práce se mi podařilo splnit veškeré body zadání, odstranil jsem všechny chyby, které bránily úspěšnému nasazení systému do produkčního prostředí. Funkcionalita, kterou se mi nepodařilo zprovoznit je funkcionalita zrcadla a zálohování. Podle názoru vedoucího práce tyto funkce nebyly prioritou zadavatelů pro první verzi a nebudou v první verzi systému poskytovány. Opravení těchto chyb by bylo již nad rámec rozsahu bakalářské práce. Osobně pro mě největším problémem v počátcích práce bylo nastartování do projektu. Projekt obsahuje mnoho technologií, kterým bylo potřeba porozumět. Komunikace s vedoucím projektu probíhala bez problémů, nicméně často jsem po nově objevených problémech musel tyto problémy řešit s vedoucím práce a při jedné schůzce za týden se oprava problémů protahovala. Zajímavou zkušeností byla pro mě práce s čtečkou čárových kódů, se kterou jsem dříve nikdy nepracoval. Jako další možné pokračování práce bych navrhl odstranění chyb v zrcadle a zálohování. Issue tracker projektu dále obsahuje spoustu navrhovaných věcí, mezi nimi například implementace slev. Ve zdrojovém kódu existuje stále spousta nedořešených problémů označených komentářem TODO. Za vznikem těchto nedořešených problémů dle mého názoru stojí skutečnost, že v projektu neexistuje nikdo stálý, kdo by udržoval zdrojový kód. Vývojový tým je tvořen z řad studentů v rámci jejich akademických prací. Student během své práce nastartuje do projektu, provede požadované změny a po splnění práce projekt opouští.
29
30
KAPITOLA 4. ZÁVĚR
Literatura [1] Jasperreports library, Březen 2014. . [2] Chapter 14. server-sent events (sse) support, Duben 2014. . [3] Hypertext transfer protocol – http/1.1, Duben 2014. . [4] Apache http server benchmarking tool, Duben 2014. . [5] P. Adragna. Software debugging techniques. Inverted CERN School of Computing, page 71, 2008. . [6] J. Elliott, T. M. O’Brien, and R. Fowler. Harnessing Hibernate. O’Reilly Media, Inc., 2008. [7] T. Grtker, U. Holtmann, H. Keding, and M. Wloka. The Developer’s Guide to Debugging. Springer Publishing Company, Incorporated, 2008. . [8] Jasperstarter - running jasperreports from command line, Květen 2014. //jasperstarter.sourceforge.net/>.
[9] R. C. Martin. Clean Code: A Handbook of Agile Software Craftmanship. Pearson Education, Inc, 1st edition, 2009. [10] T. M. O’Brien, J. Casey, B. Fox, B. Snyder, J. Van Zyl, and E. Redmond. Maven: The Definitive Guide. O’Reilly Media, 2008. [11] J. Sandoval. RESTful Java Web Services : Master Core REST Concepts and Create RESTful Web Services in Java. Packt Publishing Ltd, 2nd edition, 2009. [12] Introduction to spring framework transaction management, Duben 2014. . 31
32
LITERATURA
[13] Squirelsql - introduction, features and screen shots, Duben 2014. squirrelsql.org/index.php?page=screenshots>.
[14] Generate md5 hash in java, Duben 2014. . [15] P. Westman. Debugging methods-applied on networking operating systems. Duben 2014. .
Příloha A
Výsledný tisk menu Na obrázku A.1 je vytisknuté menu Kávy s podmenu Ledové kávy do PDF.
33
34
PŘÍLOHA A. VÝSLEDNÝ TISK MENU
Kávy Množství
Název
Cena/Ks
Ledové kávy 120ml
Ledove cappuccino
54.0 Kč
120ml
Ledova cokolada
54.0 Kč
120ml
Ledová káva
130ml
Ledová káva se zmrzlinou
70.0 Kč 100.0 Kč
Kávy malé
Espresso
34.0 Kč
velké
Espresso
34.0 Kč
120ml
Espresso bez kofeinu
39.0 Kč
120ml
Macchiato
44.0 Kč
120ml
Cappuccino
44.0 Kč
120ml
Café al lait
44.0 Kč
120ml
Café latté
54.0 Kč
120ml
Café Amaretto
54.0 Kč
120ml
Alžírská káva
54.0 Kč
120ml
Irská káva
64.0 Kč
120ml
Překapávaná
34.0 Kč
120ml
Turecká káva
34.0 Kč
30g
Sušenka ke kávě
3.0 Kč
20ml
Extra porce šlehačky
7.0 Kč
120ml
Café al lait bez kofeinu
59.0 Kč
120ml
Cappuccino
74.0 Kč
300ml
Frappé
54.0 Kč
120ml
Cappuccino bez kofeinu
49.0 Kč
Přejeme dobrou chuť Obrázek A.1: Tisk menu do PDF
Příloha B
Obsah přiloženého CD . |-- BP ............................................ text práce ‘-- bachelor_thesis.pdf
35