PŘÍRODOVĚDECKÁ FAKULTA UNIVERZITY PALACKÉHO KATEDRA INFORMATIKY
BAKALÁŘSKÁ PRÁCE
Systém pro odevzdávání a hodnocení úloh v jazyce Java
2014
Martin Jašek
Anotace Cílem práce je vytvořit webový informační systém pro kontrolu a hodnocení úkolů do Javy. Hlavní částí aplikace je pak mechanizmus pro automatickou kontrolu odeslaných řešení. Z bezpečnostních důvodů je kontrola řešení prováděna v tzv. sandboxu. Z tohoto důvodu se značná část práce věnuje popisu bezpečnostního modelu v Javě.
Chtěl bych poděkovat vedoucímu práce, Mgr. Petru Krajčovi, PhD., za cenné rady při psaní této práce.
Obsah 1. Úvod 1.1. Požadavky na aplikaci . . . . . . . . . . . . . . . . . . . . . . . . 1.2. Návrh a analýza . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8 8 8
2. Sandbox 2.1. Potřeba sandboxu . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2. Požadavky na sandbox . . . . . . . . . . . . . . . . . . . . . . . . 2.3. Sandbox v aplikaci JAssignment . . . . . . . . . . . . . . . . . . .
9 9 9 10
3. Bezpečnostní model v Javě 3.1. Java Security API . . . . . . . . . . . . . . . . . 3.2. Poskytovatelé zabezpečení . . . . . . . . . . . . 3.3. Kryptografie a zabezpečená komunikace . . . . 3.4. Autorizace a autentizace . . . . . . . . . . . . . 3.5. Řízení přístupu . . . . . . . . . . . . . . . . . . 3.6. Povolení . . . . . . . . . . . . . . . . . . . . . . 3.7. Důvěryhodnost zdroje kódu . . . . . . . . . . . 3.8. Security manager . . . . . . . . . . . . . . . . . 3.9. Konfigurace oprávnění . . . . . . . . . . . . . . 3.10. Práce s bezpečnostní politikou za běhu aplikace 3.11. Nástroje pro usnadnění správy bezpečnosti . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
10 10 11 11 11 12 12 14 15 16 19 22
4. Programové rozhraní překladače Javy 4.1. Překladač javac . . . . . . . . . . . . . . . . . . . 4.2. Java Compiler API . . . . . . . . . . . . . . . . . 4.3. Komponenty překladače . . . . . . . . . . . . . . 4.4. Komponenty file manageru . . . . . . . . . . . . . 4.5. Existující implementace překladače . . . . . . . . 4.6. Existující implementace file manageru . . . . . . . 4.7. Další nástroje implementující Java Compiler API 4.8. Vlastní implementace překladače a file manageru
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
23 23 24 25 26 27 28 28 28
. . . . . . .
29 29 30 31 31 32 33 33
5. Knihovna jasglib pro překlad a testování kódu 5.1. „In-memory“ přístup . . . . . . . . . . . . . . . . 5.2. Implementace překladače . . . . . . . . . . . . . . 5.3. Překlad zdrojových kódů a testů . . . . . . . . . . 5.4. Spouštění testů . . . . . . . . . . . . . . . . . . . 5.5. Implementace sandboxu . . . . . . . . . . . . . . 5.6. Konfigurace sandboxu . . . . . . . . . . . . . . . 5.7. jasglib konzolová aplikace . . . . . . . . . . . .
4
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
6. Aplikace JAssignment 6.1. Struktura aplikace . . . . . . . . 6.2. Uživatelské role . . . . . . . . . . 6.3. Implementace modelu databáze . 6.4. Realizace webové aplikace . . . . 6.5. Systém zpráv a odesílání e-mailů 6.6. Konfigurace aplikace . . . . . . . 6.7. Testování aplikace . . . . . . . . . 6.8. Možná rozšíření aplikace . . . . . 6.9. Uživatelská příručka . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
34 34 34 35 36 38 39 40 40 41
Závěr
43
Conclusions
44
Reference
45
A. Instalační příručka
46
B. Obsah přiloženého CD
51
5
Seznam obrázků 1. 2. 3. 4. 5. 6. 7. 8. 9.
Aktivace security manageru programově . . . . Znázornění kontroly oprávnění . . . . . . . . . . Syntaxe klauze keystore . . . . . . . . . . . . . Syntaxe klauze grant entry . . . . . . . . . . . . Původní zápis privilegovaného režimu . . . . . . Náhled aplikace policytool . . . . . . . . . . . . Základní použití Java Compiler API . . . . . . Aplikace JAssignment po přihlášení studenta . . Aplikace JAssignment po přihlášení vyučujíciho
6
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
15 17 18 18 21 22 24 42 43
Seznam tabulek 1.
Zástupné symboly v názvu FilePermission . . . . . . . . . . . .
7
13
1.
Úvod
Tato práce se zabývá implementací informačního systému pro automatizovanou kontrolu úkolů napsaných v programovacím jazyce Java. Účelem aplikace je zjednodušení práce vyučujícího, resp. opravování úkolů. Odeslaná řešení úkolů nejdříve projdou strojovými testy, čímž jsou odfiltrována chybná řešení, a vyučující tak opravuje jen řešení, která splňují zvolené požadavky. Vzhledem k tomu, že při testování řešení bude nutné jejich kód spouštět, hrozí zde potencionální bezpečnostní riziko a je potřeba s ním počítat. Z tohoto důvodu je značná část práce věnována bezpečnostnímu modelu v Javě a implementaci tzv. sandboxu.
1.1.
Požadavky na aplikaci
Aplikace by měla umožnit vyučujícímu zadávat studentům úkoly, které budou vypracovávat. Vypracovaná řešení budou studenti prostřednictvím aplikace odesílat. Ihned po odeslání je řešení přeloženo, otestováno a v případě, že testy prošlo, je odesláno vyučujícímu na dodatečnou kontrolu. Testování správnosti řešení by mělo být realizováno pomocí testovacího frameworku JUnit, standardního nástroje pro jednotkové testování v jazyce Java. Mezi další požadované funkce patří možnost konfigurace sandboxu a možnost spuštění dodatečných testů přímo vyučujcím. Samozřejmostí je pak agenda pro správu studentů a úkolů. To znamená možnost prohlížet a případně upravovat data a provádět jejich import a export. Navíc by aplikace měla umožňovat komunikaci mezi studenty a vyučujícím prostřednictvím e-mailu. Požadavkem také je, aby byla aplikace spustitelná na webovém serveru Apache Tomcat.
1.2.
Návrh a analýza
Vzhledem k programovým nástrojům, které poskytuje jazyk Java a jeho knihovny bylo možné celou aplikaci implementovat v Javě bez použití externích nástrojů (jako je např. příkazový intepret cílové platformy). Prvním krokem při vývovoji aplikace byla příprava knihovny pro provádění testů. Vzhledem k tomu, že tato část je pro aplikaci stěžejní, byla implementována jako první. Po jejím důkladném otestování (formou konzolové aplikace) byl proveden návrh a implementace databáze. Bylo určeno, aby byl i model databáze implementován samostatně. Původně se totiž počítalo i s případnou implementací jednoduchého (konzolového) databázového klienta. Další výhodou tohoto řešení bylo kromě lepší testovatelnosti i striktní oddělení modelu od aplikační logiky.
8
Poté byly oba moduly nasazeny do webové aplikace. Bylo rozhodnuto, že webová aplikace bude vytvořena pomocí webového frameworku JSF1 . Vzhledem ke svému účelu a použitém jazyku Java byl aplikaci přiřazen název JAssignment. Jednotlivé moduly aplikace jsou pak označeny jasglib, jasgdb a jasgweb.
2.
Sandbox
Sandbox (česky pískoviště) je programový nástroj, který slouží ke spouštění kódu v uzavřené oblasti [4]. Tato oblast je izolována od svého okolí (zbytku hostitelského počítače) a eleminuje tak bezpečnostní rizika.
2.1.
Potřeba sandboxu
Spouští-li aplikace cizí kód, přináší tím automaticky bezpečnostní rizika. V momentě, kdy je spuštěn kód, automaticky získává všechny možné nástroje, které mu jazyk, ve kterém je napsán, umožňuje. Pokud takovýto kód obsahuje záškodné funkce, můžou být vykonány. Důležité je, že kód může být záškodný, aniž by za tímto účelem vznikl. Mezi takový kód může patřit například kód, který není dostatečně odladěn nebo nemá ošetřeny mezní případy. Nežádoucí také může být například kód, který vypisuje nadměrné množství informací do konzole nebo logovacího souboru.
2.2.
Požadavky na sandbox
První funkcí sandboxu je ochrana hositelského počítače před jeho poškozením. Ta spočívá především v zakázání (nebo omezení jen na určitý vymezený adresář) zápisu na disk a spouštění dalších procesů a aplikací. Další funkcí sandboxu je zabezpečení dat – sandbox by neměl umožit číst data z disku (případně jen ve zvoleném pracovním adresáři) či jinak získávat data. Sandbox by rozhodně měl dostatečně regulovat i přístup k vnějšku hostitelského počítače. Tím je myšleno, že spouštěný kód by neměl mít možnost provádět spojení po síti či přistupovat k periferiím. Pokud spouštěný kód potřebuje síť používat, je nutné přesně určit, na kterých portech a se kterými hosty může komunikovat. V neposlední řadě by také sandbox mohl řídit přidělování množství paměti (jak velikost zásobníku, tak haldy), stanovit maximální dobu běhu kódu, určit maximální počet vláken a podob. 1
Java Server Faces, framework pro vývoj webových aplikací v Javě
9
2.3.
Sandbox v aplikaci JAssignment
Realizace sandboxu pro účely aplikace JAssignment – a obecně pro libovolnou webovou aplikaci – přináší další komplikace. Aplikace je totiž spuštěna na serveru, což znamená, že je součástí již běžícího procesu. Je tak třeba přesně vymezit, jak veliká část aplikace bude spuštěna v sandboxu, aby nebyla ohrožena jak funkčnost ostatních běžících aplikací, tak serveru samotného. V následující kapitole bude důkladně popsán bezpečnostní model Javy a v kapitole 5.5. bude popsána implementace sandboxu pro účely aplikace JAssignment.
3.
Bezpečnostní model v Javě
Bezpečnost platformy Java byla vždy jednou z hlavních priorit [6]. Za základní prvky bezpečnosti se považuje například silná typovost jazyka, povinná inicializace proměnných, důsledná kontrola mezí polí a nebo také verifikace bytekódu před spuštěním. Díky tomu v Javě odpadají některé situace známé z jazyka C jako například přetypovávání int na ukazatel, čtení/zápis mimo pole a s tím související (bezpečnostní) chyby. Kromě těchto základních bezpečnostních prvků, které jsou zakomponovány přímo ve standardech jazyka, Java obsahuje spoustu dalších bezpečnostních prvků realizovaných pomocí tříd a rozhraní z tzv. Java Security API. Je třeba poznamenat, že velmi důležitou roli v bezpečnostním modelu Javy hraje operační systém. Je zřejmé, že i když aplikaci přidělíme sebevyšší oprávnění, stále je omezena možnostmi, které ji povoluje zabezpeční operačního systému. A platí to i naopak - některé funkce z bezpečnostního modelu Java nepodporuje, protože je neposkytuje cílová platforma nebo je např. v zemi či regionu zakázána [10].
3.1.
Java Security API
Java Security API je sada nástrojů pro zajištění bezpečnosti aplikací v Javě. Kromě běžných nástrojů pro kontrolu řízení přístupu obsahuje i nástroje pro šifrování a nebo autentizaci a autorizaci. Nyní budou popsány základní komponenty Java Security API. Zvláštní důraz bude věnován problematice řízení kontroly přístupu. Tato část bezpečnostního modelu je totiž běžnému programátorovi nejbližší a měl by o ní mít alespoň základní povědomí. Znalost této problematiky je také potřebná pro implementaci sandboxu.
10
3.2.
Poskytovatelé zabezpečení
Jednotlivé položky bezpečnostního modelu bývají implementovány (poskytovány) objekty, kterým se říká poskytovatelé zabezpečení. Každý předepisuje služby, které implementuje. Vzhledem k tomu, že různí poskytovatelé mohou poskytovat stejnou službu, jsou při požadavku o poskytovatele této služby seřazeni podle preference a je tak vybrán nejpreferovanější poskytovatel [12]. Poskytovatel zabezpečení je instance třídy java.security.Provider. O správu poskytovatelů bezpečnosti se stará třída java.security.Security.
3.3.
Kryptografie a zabezpečená komunikace
Java poskytuje širokou škálu kryptografických nástrojů. Obsahuje třídy implementující různé šifrovací algoritmy (například RSA, DSA, MD5, SHA-1, SHA256 a nebo algoritmus pro Diffie-Hellmanovu výměnu klíčů) [12]. Tyto třídy se nachází v balíčcích java.security a javax.crypto. Java také disponuje nástroji pro šifrovanou komunikaci pomocí pomocí výměny klíčů. V balíčcích java.security a java.security.cert se nachází implementace digitálních certifikátů (např. standardu X.509) a dalších služeb (např. CRL). Pro ukládání klíčů (bezpečnostních certifikátů) slouží třída KeyStore (CertStore). Samotný bezpečnostní klíč (certifikát) je reprezentován třídou Key (Certificate). Všechny tyto třídy se nachází v balíčku java.security. Java navíc implementuje vlastní uložiště klíčů nazvané Java KeyStore (JKS). Pro práci s JKS slouží nástroj keytool, který je popsán v kapitole 3.11. Pro komunikaci pomocí protokolů SSL a TLS slouží třída javax.net.ssl.SSLSocket. Standard SASL je v Javě reprezentován tzv. Java SASL API. To obsahuje třídu javax.security.sasl.Sasl pro správu klientů (javax.security.sasl.Client) a serverů (javax.security.sasl.Server). Kromě těch základních kryptografických technik obsahuje Java i další, méně známé, nebo specifické pro určitou platformu. Díky systému poskytovatelů zabezpečení je možné doimplementovat libovolné vlastní kryptografické techniky.
3.4.
Autorizace a autentizace
Java předepisuje API pro autorizaci a autentizaci [12]. Aplikace může pomocí třídy javax.security.auth.login.LoginContext autorizovat své uživatele. O přesný průběh autorizace se stará konkrétní implementace přihlašovacího modulu (třídy javax.security.auth.spi.LoginModule). S autorizací a autentizací souvisí ještě třída javax.security.auth.Subject a rozhraní java.security.Principal. Třída Subject reprezentuje obecný subjekt, který může provádět určité akce. Každý subjekt obsahuje množinu svých principálů, které subjektu dodávají identity. Je-li subjektem například osoba, 11
pak jeho principály můžou být jméno, rodné číslo a uživatelská jména k různým elektronickým službám (e-mail, sociální sítě, diskuzní fóra).
3.5.
Řízení přístupu
Základní komponentou bezpečnostního modelu Javy je systém pro kontrolu řízení přístupu. Obsahuje komponenty sloužící k zabezpečení jednak různých částí aplikace samotné (např. izolace vláken), ale především jejího vnějšího prostředí (počítače a jeho okolí). Implementuje přidělování a kontrolu povolení, která může aplikace (nebo některé její části) získat, aby se tak mohla minimalizovat bezpečnostní rizika. Základním prvkem kontroly řízení přístupu je tzv. povolení. Tato komponenta je používána napříč celým systémem pro řízení přístupu, takže proto bude zmíněna jako první.
3.6.
Povolení
Abstraktní třída java.security.Permission reprezentuje povolení k určité operaci. Každá instance obsahuje svůj název, textový řetězec popisující konkrétněji o jakou operaci se jedná, a také seznam „akcí“ (textový řetězec obsahující jeden nebo více symbolů oddělených čárkami), které umožňuje provádět [11]. Každá z tříd, která je potomkem této třídy pak oběma parametrům přiřazuje specifičtější (nebo také žádný) význam. Implikace povolení Třída Permission obsahuje významnou metodu implies, která reprezentuje logickou implikaci povolení. Tato metoda bere jako parametr jiné povolení (označme „perm“) a porovává jej s povolením, které tuto metodu volá (označme „this“). Pokud platí, že povolení „this“ povoluje (implikuje) i povolení „perm“, vrací metoda true. Jinými slovy, pokud je povoleno povolení „this“, pak musí být povoleno i povolení „perm“. Například máme-li povolení k přístupu ke všem souborům v adresáři /foo/bar/, pak máme určitě i povolení k přístupu ke všem souborům a složkám v tomto adresáři a všech jeho podadresářích. Naopak, přístup k nadřazenému adresáři, /foo/ nám toto povolení neumožňuje. Je zřejmé, že tato metoda má pro bezpečnost velmi veliký význam. Umožňuje totiž porovávat jednotlivá oprávnění a vytvářet mezi nimi vztahy. Jak bude ukázáno dále, podobnou metodou disponují i další třídy a rozhraní z bezpečnostního systému. Nyní následuje výpis nejvýznamějších typů oprávnění (potomků třídy Permission).
12
Třída FilePermission Třída java.io.FilePermission reprezentuje oprávnění k práci se soubory v systému souborů. Název povolení má zde význam cesty k souboru. Může však být použit některý zástupný symbol, jak ukazuje tabulka 1. název povolení /foo/bar /foo/* /foo/<
>
povolení se vztahuje na soubor /foo/bar všechny soubory a složky ve složce /foo všechny soubory a složky ve složce /foo a všech jejich podsložkách všechny soubory
Tabulka 1. Zástupné symboly v názvu FilePermission Seznam akcí pak odpovídá některým z těchto symbolů: • read (čtení souboru) • write (zápis do souboru) • execute (spouštění souboru voláním Runtime.exec()) • delete (smazání souboru) • readlink (získání cíle odkazu) Třída BasicPermission Abstraktní třída java.security.BasicPermission shromažďuje společné rysy několika dalších povolení. U každého potomka musí platit, že jeho název je hierarchický, jako oddělovač používá tečku a jeden nebo více symbolů zleva může být nahrazen zástupným symbolem (hvězdičkou) [11]. Jedná se stejný princip, jaký se využívá např. u příkazu import ve zdrojových kódech Javy. Tato třída je předkem většiny povolení, se kterými se lze běžně setkat. Třída PropertyPermission Patří sem napříkad třída java.util.PropertyPermission. Ta stanovuje povolené operace (read a write) se zadanou systémovou proměnnou. Třída RuntimePermission Významným potomkem třídy BasicPermission je třída java.lang.RuntimePermission. Ta reprezentuje povolování rutinních operací pro běh aplikace. Může nabývat například těchto názvů: 13
• loadLibrary. (načtení knihovny) • accessClassInPackage. (používání tříd ze zadaného balíčku) • defineClassInPackage. (deklarace třídy v zadaném balíčku) • getenv. (přístup k systémové proměnné) • createClassLoader (vytvoření classloaderu) • setSecurityManager (změna security manageru) • exitVM (ukončení virtuálního stroje) • setIO (změna System.in, System.out a nebo System.err) • modifyThread (modifikace jiného vlákna) • accessDeclaredMembers (získání seznamu konstruktorů, slotů, metod a tříd pomocí reflexe) Třída AllPermission Specifickým povolením je třída AllPermission. Tato třída je potomkem třídy Permission, ale nemá specifikován žádný název ani seznam akcí. Reprezentuje povolení všech operací. Kolekce povolení Často však potřebujeme pracovat s více než jedním povolením. K tomuto nám slouží kolekce povolení, abstratkní třída java.security.PermissionCollection a její potomek java.security.Permissions. Důležitým rysem třídy Permissions je, že optimalizuje vkládání povolení. Pokud přidáváme povolení, jehož typ se již v této kolekci nachází, je nejdříve zkontrolováno, zda-li již není implikováno některým z povolení, které se již v kolekci nachází a teprve poté je přidáno. Z toho vyplývá, že tato třída implementuje metodu implies.
3.7.
Důvěryhodnost zdroje kódu
Většina dnešních aplikací využívá spousty různých knihoven. Vzhledem k tomu, že knihovny pochází z různých zdrojů, je třeba uchovávat si informace, jak moc těmto zdrojům důvěřujeme z pohledu bezpečnosti. Podle toho pak patřičným knihovnám můžeme přidělovat nebo zamítat požadovaná oprávnění. Kupříkladu 14
základní knihovny Javy pochází určitě od věrohodného zdroje a proto si můžeme dovolit jim přidělit všechna oprávnění. Naopak například Java applety, které bývají stahovány z internetu, mívají přiřazena jen minimální povolení. Kromě zdroje kódu se však ještě rozlišuje, zda-li byl požadovaný kód elektronicky podepsán (a kým) a jaký uživatel se dožaduje jeho používání. Java má toto zabezpeční implementováno pomocí tzv. bezpečnostních domén. Bezpečnostní doména je instance třídy java.lang.ProtectionDomain. Každá třída v Javě má přidělenu bezpečnostní doménu. Každá bezpečnostní doména je tvořena popisovačem zdroje kódu, kolekcí povolení, která garantuje, a seznamem přiřazených principálů. Popisovač zdroje kódu je v Javě reprezentován třídou CodeSource z balíčku java.security. Každá instance této třídy je určena URL adresou, ze které kód pochází a volitelně také seznamem bezpečnostních certifikátů nebo podpisovačů.
3.8.
Security manager
V začátcích Javy byl základním kamenem bezpečnostního systému tzv. security manager. Dnes se již od používání security manageru upouští (především kvůli nedostatučující konfigurovatelnosti a škálovatelnosti), nicméně v současné implementaci je stále ještě jeho přítomnost nezbytná (mimo jiné kvůli zpětné kompatibilitě). V Javě 7 (a novějších) je veškerá funkcionalita security manageru delegována na třídu AccessController [12]. Důležité však je, že aby byl aktivován bezpečnostní systém, je třeba security manager explicitně aktivovat. Prvním způsobem, jak toho docílit je spouštět aplikaci s parametrem virtuálního stroje -Djava.security.manager. Security manager lze aktivovat i programově, a to vytvořením instance třídy java.lang.SecurityManager a nastavením třídě System, jak ukazuje kód na obrázku 1. SecurityManager security = System . g et Se cu ri ty Ma na ger () ; if ( security == null ) { security = new SecurityManager () ; System . set Se cu ri ty Ma na ge r ( security ) ; }
Obrázek 1. Aktivace security manageru programově Třída SecurityManager obsahuje (mimo jiné) přibližně 30 veřejných metod, sloužících ke kontrole oprávnění ke specifickým operacím. Až na pár výjimek se jedná o metody, jejiž název začíná předponou „check“, které mají návratovou hodnotu typu void a deklarují klauzuli throws s výjimkou java.lang.SecurityException. Zavoláním této metody security manager ověří, zda je požadovaná operace povolena a v případě, že není, vyvolá právě SecurityException. 15
Tyto metody lze symblicky rozdělit do čtyř skupin. Jako první lze označit metody pracující s operačním systémem. Mezi tyto metody by se řadily například metody checkExit, checkPropertyAccess, checkExec a dále pak ještě napříkald metody pro práci s vlákny. Metoda checkExit testuje, zda-li může aplikace ukončit proces virtuálního stroje voláním System.exit(int code). Metoda checkPropertyAccess se stará o kontrolu přístupu k systémovým proměnným. Metoda checkExec pak kontroluje možnost volání systémových příkazů. Další skupinou jsou metody pro kontrolu práce s balíčky a třídami. Umožňují omezovat používání classloaderů, přístup k třídám v určitých balíčcích a načítání knihoven. Sem patří například metody checkCreateClassLoader a nebo checkPackageAccess a checkPackageDefinition. Třetí skupinou jsou metody pro kontrolu vstupů a výstupů. Patří sem metody pro kontrolu přístupu k disku (čtení, zápis a mazání souborů) a k síti (vytváření socketů nebo jejich naslouchání). Jedná se například o metody checkDelete, checkRead, checkListen a nebo checkConnect. Poslední skupinou metod jsou metody pro okenní (AWT/Swing) aplikace. Security manager umožňuje řídit přístup k systémové schránce (metoda checkSystemClipboardAccess), vytváření tiskových úloh (metoda checkPrintJobAcces), AWT frontě událostí (metoda checkAwtEventQueueAccess) a může blokovat vytváření top-level oken (metoda checkTopLevelWindow). Nejdůležitější metodou security manageru je metoda pro kontrolu obecného povolení, metoda checkPermission. Jejím parametrem je instance třídy Permission. Potřebuje-li aplikace provést některou potencionálně nebezpečnou operaci (např. otevřít soubor pro čtení), je nejdříve dotázána security managera (pokud je aktivován) voláním právě některé z těchto metod. Pokud je operace security managerem povolena, je provedena. V opačném případě je volána výjimka. Názorně je to ukázáno na obrázku 2. (třída Policy bude popsána později).
3.9.
Konfigurace oprávnění
Vzhledem k tomu, že o zabezpeční běhu aplikací se zatím stále stará security manager, je jeho konfigurace stále základním nástrojem pro správu zabezpečení. Veškerá další dodatečná nastavení se odvíjí od nastavení security manageru. V základní konfiguraci je security manager vcelku přísný [6]. Umožňuje aplikaci deklarovat třídy v libovolném balíčku a stejně tak i všechny třídy ze všech balíčků používat (až na pár výjimek). Pak ještě umožnuje pracovat i s cizími vlákny a například ukončit virtuální stroj. Všechny ostatní operace by totiž mohly nést potencionální bezpečností rizika a proto bývají zakázány nebo velmi silně omezeny. Aplikacím tak je velmi omezen přístup k disku (povoleno je jen čtení aktuálního adresáře), síti (možnost 16
Obrázek 2. Znázornění kontroly oprávnění, zdroj: [12] pouze naslouchat několika základních portech) a většině systémových proměnných. Velmi omezené je také používání class loaderů (např. vytváření vlastních). Zakazuje se také většina objektů ze samotného bezpečnostního modelu (není tak umožněna další dodatečná konfigurace) a aplikace například nemá ani povoleno získat obsah zásobníku (stack trace) výjimky. Policy file Původní implementace security manageru je konfigovatelná pomocí tzv. policy (configuration) file (česky „soubor s bezpečnostní politikou“) [8]. Jedná se o soubor se syntaxí vzdáleně podobnou syntaxi jazyka Java. Úložiště klíčů V policy file je možné specifikovat uložiště veřejných klíčů, které slouží k ově17
ření zdrojů kódů. Zapisuje se pomocí klauzule keystore. Syntaxi můžete vidět na obrázku 3. keystore " URL ␣ uloziste ␣ klicu " , " typ ␣ uloziste " , " poskytovatel ␣ uloziste " ; k eys t o re P a ss w o rd U R L " heslo ␣ k ~ ulozisti " ;
Obrázek 3. Syntaxe klauze keystore Na adrese s uložištěm klíčů se musí nacházet soubor s klíči, jejiž typ je specifikován jako druhý parametr. Zadaný typ musí být poskytovaný poskytovatelem uložiště. Pokud je přístup k uložišti klíčů chráněn heslem, je možné heslo zadat pomocí klauzule keystorePasswordURL. Pro generování klíčů slouží nástroj keytool, který je podrobněji popsán v kaptiole 3.11. Není to nutné, ale uložiště klíčů by se mělo uvádět na začátku souboru policy file. Přidělování oprávnění Hlavní částí policy file je seznam jednotlivých oprávnění. Zapisuje se pomocí tzv. grant entry záznamů. Jedná se o klauzule uvedené klíčovým slovem grant a následované seznamem podpisovačů, URL zdroje kódu a seznamem principálů. Ve složených závorkách pak obsahuje samotný seznam oprávnění. Přesná syntaxe záznamu grant entry se nachází na obrázku 4. grant [ signedBy " seznam ␣ podpisovacu " ,] [ codeBase " URL ␣ zdroje " ,] [ principal trida principala 1 " trida ␣ principala ␣ 1 " ,] ... [ principal trida principala n " trida ␣ principala ␣ n " ] { [ permission trida prava 1 " nazev ␣ 1 " , " akce ␣ 1 " , signedBy " seznam ␣ podpisovacu " ;] ... [ permission trida prava m " nazev ␣ 1 " , " akce ␣ m " , signedBy " seznam ␣ podpisovacu " ;] };
Obrázek 4. Syntaxe klauze grant entry (položky v hranatých závorkách jsou nepovinné) Každý záznam grant entry koresponduje s bezpečnostní doménou (tedy třídou ProtectionDomain) popsanou v kapitole 3.7. Díky tomu je i jasný význam jednotlivých parametrů a není třeba se jimi dále zabývat. 18
Policy file a security manager Máme-li vytvořený policy file, je nutné říci virtuálnímu stroji, aby jej použil. To lze učinit čtyrmi způsoby: 1. Přepsat naše oprávnění do globálního policy file souboru. Globální policy file soubor obsahuje výchozí nastavení pro všechny aplikace. Standardně se jedná o soubor ${java.home}/lib/security/java.policy. Tato varianta pochopitelně není doporučena pro konfiguraci bezpečnosti běžných aplikací. 2. V konfiguračním sobouboru bezpečnosti vlastní globální policy file. To se provede policy.url.n=URL policy file souboru, kde n další číselnou hodnotou v řadě.
Javy přidat vložením řádku bude nahrazeno
3. Vložit naše oprávnění do policy file souboru uživatele, ${user.home}/.java.policy. Vložená oprávnění budou pochopitelně aplikována jen na aplikace spouštěné tímto uživatelem. 4. Předat URL policy file souboru přímo konkrétní aplikaci přepínačem virtuálního stroje -Djava.security.policy=URL policy file souboru. Je doporučeno spolu s tímto přepínačem uvádět také přepínač -Djava.security.manager, aby bylo zajištěno, že bude security manager aktivován. Je třeba poznamenat, že výše uvedené cesty mohou být změněny v konfigurčním souboru bezpečnosti Javy, ${java.home}/lib/security/java.security. Stejně tak může být zakázáno používání přepínače -Djava.security.policy. Pokud je pro konfiguraci bezpečnosti použito více policy souborů (což je vlastně vždy, protože globální i uživatelský jsou načítány pokaždé) jejich konfigurace se slučuje (ve smyslu množinového sjednocení). Takže například umožňuje-li globální policy file čtení a zápis v adresáři /tmp, uživatelský policy file k němu může přidat čtení souborů domovském adresáři uživatele a přepínač virtuálního stroje pak může dodat i právo pro zápis v domovském adresáři.
3.10.
Práce s bezpečnostní politikou za běhu aplikace
Jak bylo popsáno v předchozích odstavcích, security manager umožňuje konfigurace pouze pomocí policy file souborů. Z tohoto důvodu začaly vznikat nové nástroje pro flexibilnější správu bezpečnosti. Třídy Policy a PolicyFile Abstraktní třída java.security.Policy je nositelem bezpečnostní politiky aplikace [7]. Aplikace obsahuje právě jednu instanci bezpečnostní politiky. V případě, že je požádáno o instanci bezpečnostní politiky (pomocí statické metody 19
getPolicy), je tato instance vrácena. V případě, že zatím žádná nebyla třídě Policy nastavena, je vytvořena, nastavena a vrácena výchozí implementace, třída sun.security.provider.PolicyFile. Tato třída, jak již její název napovídá, provádí přidělování povolení na základě konfigurace získané z policy file souborů, jak bylo popsáno v předchozích kapitolách. Třídu PolicyFile však lze vytvořit i ručně a v jejím konstruktoru specifikovat URL souboru, který se má použít jako policy file. Třída Policy předepisuje metodu implies pro kontrolu, zda-li aktuální bezpečnostní politika povoluje pro zadanou bezpečnostní doménu zadané povolení. Má také metodu getPermissions, která vrací kolekci povolení zadané bezpečnostní domény. Třída Policy však sama o sobě nenese žádné nástroje pro provádění kontroly oprávnění. Třída AccessController Základní třídou pro provádění kontroly oprávnění je třída AccessController z balíčku java.security. Tato třída má na starost kontrolovat zadaná oprávnění. K tomu slouží metoda checkPermission. Nestačí však jen zkontrolovat povolení třídy, která požavek na kontrolu oprávnění vznesla, protože by například třída, která ji volala, toto povolení nemusela mít. Access controller proto musí projít všechny třídy na programovém zásobníku jak aktuálního vlákna, tak i postupně všech jeho rodičovských vláken [3]. Pro každou třídu se dotazuje jejího bezpečnostního kontextu, zdali požadované povolení povoluje, a v opačném případě volá výjimku java.security.AccessControllException. Pokud aplikaci povolují všechny třídy na zásobníku, je povolení povoleno a operace může být provedena. Tento fakt se tak u aplikací s aktivovaným security managerem může projevovat mírným snížením výkonu. Privilegovaný režim Access controller však navíc ještě umožňuje tzv. privilegovaný režim. Privilegovaný režim je kód, který je prováděn s jiným oprávněním, jaké je stanoveno aktuálním kontextem. Je tak možné krátkodobě zvýšit (nebo snížit) oprávnění aplikace. To je umožněno tak, že access controller kontrolu oprávnění provádí pouze pro třídy, které se na programovém zásobníku nachází v privilegovaném režimu a třídy, které se nachází mimo něj ignoruje. Původní zápis privilegovaného režimu tak, jak se používal dříve je na obrázku 5. Vzhledem k tomu, že bylo nutné vždy privilegovaný režim ručně ukončit, znamenalo toto řešení potenciální bezpečnostní chyby [4]. Proto byl tento zápis zakázán a nahrazen metodou doPriviledged, která je aktuálně implementována
20
// obycejny zabezpeceny kod try { AccessController . beginPrivileged () ; // privilegovany kod } finally { AccessController . endPrivileged () ; } // opet obycejny zabezpeceny kod
Obrázek 5. Původní zápis privilegovaného režimu nativně2 . Tato metoda bere instanci třídy implementující rozhraní PrivilegedAction (nebo PrivilegedExceptionAction), která ve své metodě run provádí privilegovaný kód. Rozdíl mezi PrivilegedAction a PrivilegedExceptionAction je v tom, že metoda run u PrivilegedAction nesmí vyvolat výjimku. Volitelně je možné specifikovat bezpečnostní kontext, který má být použit pro privilegovaný režim. Ten se stará o kontrolu oprávnění pro bezpečnostní domény, které má k sobě přiřazené. Bezpečnostní kontext je reprezentován třídou java.security.AccessControlContext. Třída SecureClassLoader Třída SecureClassLoader je potomek třídy ClassLoader a dodává mu možnost explicitně specifikovat zdroj kódu vytvářené třídy. Chráněné objekty Regulovat přístup lze i přímo k objektům. Slouží k tomu tzv. chráněné objekty. Objekt, jehož přístup má být omezen je obalen chráněným objektem (třída GuardedObject) spolu s nějakým „ochráncem“, třídou implementující rozhraní Guard. Je-li chráněný objekt požádán o instanci objektu, kterou drží, je nejdříve zkontrolováno, zda-li to ochránce umožňuje. Rozhraní Guard je implementováno většinou tříd z bepečnostního modelu.
2
JNI, Java Native Interface, rozhraní pro implementaci javovských tříd v jazyce C
21
3.11.
Nástroje pro usnadnění správy bezpečnosti
Aby byla vývojářům usnadněna správa bezpečnosti, byly vytvořeny nástroje, které k tomu slouží. Aplikace policytool Policytool je jednoduchá AWT aplikace sloužící pro editaci policy file souborů. Při používání policytool se vývojář vůbec nemusí zabývat syntaxí a strukturou policy file souborů. Pomocí formulářů vytvoří veškerou požadovanou konfiguraci, která je poté vyexportována do policy file souboru. Náhled aplikace policytool je na obrázku 6.
Obrázek 6. Náhled aplikace policytool, zdroj: vlastní
Program keytool Nástroj keytool je konzolová aplikace, která slouží ke správě uložišť klíčů JKS. Umožňuje vytvářet uložiště klíčů, generovat certifkáty a provádět jejich kontroly. Podrobný popis nástroje keytool lze nalézt v dokumentaci [9].
22
Režim pro ladění bezpečnosti Jedním z nástrojů, který napomáhá při správě bezpečnostního systému je i tzv. režim pro ladění bezpečnosti. Je-li aktivován, na standardní chybový výstup bývají vypisovány informace o průběhu provádění bezpečnostních operací (a přidělování oprávnění). Režim ladění bezpečnosti se aktivuje nastavením systémové proměnné java.security.debug na patřičnou hodnotu (např. policy pro výpis přidělování povolení dle konfigurace policy file). Výpis všech povolených hodnot se nachází v dokumentaci [13].
4.
Programové rozhraní překladače Javy
Překladač Javy je obecně nástroj, který slouží k překladu zdrojových kódů tříd na jejich bytekód. Co přesně je myšleno pojmem zdrojový kód a bytekód je popsáno standardy Javy. Syntaxe (a její sémantika) zdrojového kódu je popsána standardem The JavaTM Language Specification [5]. Tento standard popisuje syntaktickou strukturu (gramatiku) zdrojového kódu a rozlišuje základní tokeny – komentáře, bílé znaky, klíčová slova, identifikátory, anotace, operátory a další symboly (např. závorky). Také zavádí typový systém, primitivní datové typy, základní třídy Object a String, ostatní třídy, pole, vzájemné převody mezi datovými typy a jmenné konvence. K syntaktické stránce dodává i sémantiku, zmiňuje se o vyhodnocování jednotlivých výrazů a konstrukcí, významu balíčků, tříd, rozhraní, slotů a metod, o výjimkách a vláknech. Předepisuje také, že soubory se zdrojovými kódy mají příponu java. Co je to bytekód předepisuje dokument The JavaTM Virtual Machine Specification [5]. Zabývá se strukturou virtálního stroje (Java Virtual Machine), strukturou souboru třídy, instrukční sadou bytekódu a jeho verifikací, překladem a načítáním tříd. Soubory tříd mají příponu class.
4.1.
Překladač javac
Základním překladačem jazyka Java je program javac. Ten je distribuován jako součást JDK3 . Nachází se spolu s dalšími nástroji (jako např. jar, javadoc nebo debugger jdb) v podadresáři bin domovského adresáře Javy. Program na svém vstupu bere seznam souborů se zdrojovými kódy, které má přeložit, spolu s nepovinnými přepínači. Pokud jsou zdrojové soubory syntakticky správné (splňují standard The JavaTM Language Specification), jsou přeloženy a jejich bytekódy uloženy do odpovídajících souborů tříd. 3
JDK, Java development kit, sada nástrojů pro vývoj aplikací v Javě
23
4.2.
Java Compiler API
Samotný překladač javac je však jen „jednoduchá“ aplikace implementující Java Compiler API. Java Compiler API je sada nástrojů (tříd a rozhraní) z balíčku javax.tools, poskytující funkcionalitu pro překlad zdrojových kódů. Zdrojový kód na obrázku 7. ukazuje základní použití Java Compiler API. Popisuje kostru programu pro přeložení několika souborů. Jednotlivé komponenty Java Compiler API (a současně i jednotlivé části ukázkového kódu) budou nyní rozebrány podrobně. Rozděleny budou do dvou skupin – pvní se zabývá komponentami překladače samotného, druhá pak třídami a rozhraními jeho tzv. file manageru. JavaCompiler compiler = ... if ( compiler == null ) { System . err . println ( " Prekladac ␣ nenalezen " ) ; return ; } DiagnosticListener super JavaFileObject > d iag no st ic Li st en er = ... Locale locale = ... Charset charset = ... Iterable < String > compilerOptions = ... Iterable < String > classesNames = ... Iterable extends JavaFileObject > compilationUnits = ... JavaFileManager fileManager = compiler . g e t S t a n d a r d F i l e M a n a g e r ( diagnosticListener , locale , charset ) ; CompilationTask task = compiler . getTask ( compilerOutput , fileManager , diagnosticListener , compilerOptions , classesNames , compilationUnits ) ; boolean success = task . call () ; if ( success ) { System . out . println ( " Preklad ␣ uspesne ␣ dokoncen " ) ; } else { System . out . println ( " Preklad ␣ se ␣ nezdaril " ) ; } try { fileManager . close () ; } catch ( IOException e ) { System . err . println ( " Chyba ␣ pri ␣ uzavirani ␣ File ␣ Managera " ) ; }
Obrázek 7. Základní použití Java Compiler API
24
4.3.
Komponenty překladače
Rozhraní JavaCompiler Na začátku práce je třeba získat instanci překladače [2]. Překladačem se zde rozumí třída implementující rozhraní JavaCompiler. Pro získání systémového překladače můžeme použít příkaz ToolProvider.getSystemJavaCompiler(). Rozhraní DiagnosticListener a Diagnostic Překladač při svém chodu může generovat výstupy (chybová a varovná hlášení) [11]. Každé toho hlášení je reprezentováno rozhraním Diagnostic<S>. Parametr S udává typ objektu, který toto hlášení vyvolal (v našem případě se jedná o třídu nebo potomka třídy JavaFileObject, který bude zmíněn později). Toto hlášení, kromě samotné chybové nebo varovné hlášky, obsahuje také například číslo řádku, číslo znaku, nebo typ hlášení. Třída implementující rozhraní DiagnosticListener<S> tyto výstupy zachytává a umožňuje je zpracovávat. V balíčku javax.tools se nachází implemetace tohoto rozhraní, třída DiagnosticCollector<S>. Ta hlášení shromažďuje a vrací je jako seznam. Rozhraní CompilationTask Samotná instance překladače se pouze stará o provádění překladu. Překlad zdrojových kódů se proto provádí tak, že je od překladače vyžádána tzv. instance překladu, která je poté překladačem provedena [11]. Instance překladu je reprezentována rozhraním CompilationTask. Instanci překladu lze získat metodou getTask překladače. Metoda má následující vstupní parametry: • compilerOutput (java.io.Writer) Pokud není uveden diagnosticListener specifikuje, kam se mají vypisovat dodatečná hlášení (jinak nemá význam). Je-li null, píší se na standardní chybový výstup (System.err). • fileManager (JavaFileManager) Specifikuje file manager použitý pro tento překlad. • diagnosticListener (DiagnosticListener super JavaFileObject>) Specifikuje diagnostic listener. Pokud je null, je výstup předáván na compilerOutput. • compilerOptions (Iterable<String>) Specifikuje posloupnost parametrů překladače (jako u programu javac). Může být null. 25
• classesNames (Iterable<String>) Specifikuje seznam tříd určený ke zpracování anotací. Může být null. • compilationUnits (Iterable extends JavaFileObject>) Specifikuje seznam jednotek (souborů zdrojovýc kódů) k překladu. Může být null.
4.4.
Komponenty file manageru
File manager si zaslouží nemalou pozornost. Má za úkol starat se překladači o přístup k souborům, a to jak se zdrojovými kódy, tak s bytekódy tříd. Nyní budou popsány rozhraní a třídy, které file manager používá. Rozhraní FileObject Rozhraní FileObject reprezentuje objekt souboru, se kterým pracuje file manager a překladač. File manager poskytuje překladači soubory zdrojových kódů a bytekódů tříd právě pomocí tohoto rozhraní. Každý soubor je jednoznačně určen svým URI. Pro čtení (zápis) dat ze (do) souboru má definovány metody openInputStream, openReader a getCharContent (openOutputStream a openWriter). Rozhraní JavaFileObject Rozhraní JavaFileObject rozšiřuje FileObject o specifikaci typu souboru (specializované na použití v Javě), výčet JavaFileObject.Kind. Ten reprezentuje následující typy souborů: • Kind.SOURCE (zdrojový kód) • Kind.CLASS (soubor třídy) • Kind.HTML (HTML soubor) • Kind.OTHER (ostatní soubory) Rozhraní JavaFileManager.Location Rozhraní JavaFileManager.Location reprezentuje cestu k dané skupině souborů. Konkrétní implementace, výčet StandardLocation, specifikuje následující skupiny souborů: • StandardLocation.PLATFORM_CLASS_PATH (systémové třídy) • StandardLocation.CLASS_PATH (uživatelské třídy)
26
• StandardLocation.SOURCE_PATH (zdrojové kódy) • StandardLocation.CLASS_OUTPUT (pomocné třídy) • StandardLocation.SOURCE_OUTPUT (pomocné zdrojové soubory) • StandardLocation.ANNOTATION_PROCESSOR_PATH (procesoři anotací) Rozhraní JavaFileManager File manager, jako třída implementující JavaFileManager, má za úkol řídit překladači přístup k souborům [11]. Není-li určen, je použit výchozí (nativní), tzv. standardní file manager. Ten je reprezentován třídou StandardJavaFileManager z balíčku javax.tools. Překladač si od file manageru vyžaduje soubory, jejichž obsah aktuálně potřebuje. File manager mu je poskytuje pomocí metod getFileForInput a getFileForOutput a to na základě jména třídy, umístění (JavaFileManager.Location) a případně typu (JavaFileObject.Kind). Překladači jsou vraceny objekty typu JavaFileObject. File manager poskytuje další funcionalitu, jako vylistování všech souborů ve zvolném balíčku (a dle typu a lokace). Dále umožňuje testovat dva soubory na shodu a nebo se dotazovat, jestli file manager dokáže pracovat se zadaným umístěním. Důležitou součástí file manageru je asociace class loaderu4 . Soubory s přeloženými bytekódy totiž nemusí být ve formátu akceptovatelném standardními class loadery, a proto je nutné implementovat class loader vlastní.
4.5.
Existující implementace překladače
Jak bylo zmíněno výše, překladač (a obecně celé Java Compiler API) je realizován rozhraním (souborem rozhraní). To umožňuje značnou škálovatelnost a podporuje to vznik různých implementací jak samotného překladače, tak i jeho nástrojů (především file manageru). Apache Commons JCI (Java Compiler Interface) je sada nástrojů umožňující (mimo jiné) překlad zdrojových souborů Javy [2]. Obsahuje například minimalistický a rychlý překladač Janino nebo Eclipse JDT compiler (jako jeden z nástrojů vývojového prostředí pro platformu Eclipse). Vlastní překladač implementuje také OpenJDK. 4
Class loader (instance třídy java.lang.ClassLoader) se stará o vyhledávání a načítání tříd (např. z disku, po síti a podob.)
27
4.6.
Existující implementace file manageru
Mnohem rozsáhlejší možnosti však nabízí implementace file managerů. Oproti samotným překladačům, což jsou velmi složité aplikace, které se navíc musí držet standardů Javy [11], file manager je mnohem jednodušší a často je možné jej osadit určitou specifickou funcionalitou. Existuje tak opravdu spousta implementací file manageru, například: • com.intellij.psi.impl.file.impl.JavaFileManagerImpl • org.jclarion.clarion.compile.javac.ClarionFileManager • com.github.marschall.pathjavafilemanager.PathJavaFileManager • org.vertx.java.platform.impl.java.MemoryFileManager
4.7.
Další nástroje implementující Java Compiler API
Kromě samotných překladačů existují i nástroje, které překladač využívají, ale poskytují služby na tak vysoké úrovni, že je uživatel od návaznosti na něj naprosto odstíněn. Jedná se o nástroje, které mají umožňovat dynamické načítání tříd za chodu aplikace (přímo ze zdrojových kódů), nebo modifikaci existujících tříd. Mezi tyto nástroje patří například Javaassist, ASM, SERP nebo BCEL.
4.8.
Vlastní implementace překladače a file manageru
Vlastní překladač lze naimplementovat pouhým vytvořením třídy implementující rozhraní JavaCompiler (a případně CompilationTask). Stejný postup by šel použít i u implementace tříd file manageru. Vzhledem k tomu, že file managery bývají implementovýny častěji, byly vytvořeny třídy, které slouží jako delegáti na existující instance [11]. Tyto třídy umožňují provést požadovanou funkcionalitu, nebo ji předat existujícímu objektu implementující dané rozhraní. Patří mezi ně třídy: • ForwardingFileObject • ForwardingJavaFileObject • ForwardingJavaFileManager<M extends JavaFileManager> Při implementaci vlastního file manageru je doporučeno vytvářet potomky těchto tříd namísto přímé implementace rozhraní. Navíc existuje třída SimpleJavaFileObject, která implementuje rozhraní JavaFileObject. U této třídy můžeme přepsat pouze metody, které potřebujeme (a např. metody pro zápis dat ponechat nenaimplementované).
28
5.
Knihovna jasglib pro překlad a testování kódu
Pro účely aplikace JAssignment vznikla samostatná knihovna pro testování zdrojových kódů řešení označená jasglib. Vstupem této knihovny je sada souborů zdrojových kódů, sada testů a objekt konfigurace (viz kapitola 5.6.), výstupem pak třída reprezentující výsledek. Rozhraním knihovny je třída JAsgTestsRunner z balíčku cz.upol.jasglib, která se o popsanou funkcionalitu stará. Její činnost je rozdělena do tří kroků. Nejdříve provede překlad zdrojových kódů řešení, poté testovacích tříd a nakonec (v sandboxu) testovací třídy spustí.
5.1.
„In-memory“ přístup
Vzhledem k tomu, že provádění testů probíhá v sandboxu a testované třídy tak (v základní konfiguraci) nemají přístup k disku, byly vedeny snahy, aby celá knihovna mohla fungovat bez přístupu na pevný disk. Z tohoto důvodu byl překladač implementován jako tzv. in-memory překladač. To znamená, že překladač si veškeré soubory, které pro překlad potřebuje, uchovává v objektech v paměti a z disku tak čte jen statická data (knihovny). Z tohoto důvodu je třeba udržovat si instanci překladače napříč celým procesem testování. Z toho také vyplývá, že jakmile tato instance zanikne, zaniknou s ním i všechny přeložené soubory tříd a není tak třeba po překladu a otestování provádět „úklid“. Nemůže také dojít (resp. není třeba nijak ošetřovat) ke konfliktům názvů souborů a adresářů, ke kterému by při souběžném běhu více instancí mohlo docházet. Dalším důsledkem in-memory přístupu je, že soubory zdojových kódů a testů zadávané do knihovny se nemusejí nacházet na pevném disku a jsou zadávány formou proudů. Konkrétněji, soubory zdrojových kódů jsou zadávány jako množina dvojic (mapa) název třídy – input stream5 jejího zdrojového kódu. Vstupem knihovny tak můžou být soubory na disku, data z databáze nebo z formuláře na webové stránce. V případě aplikace JAssignment se bude jednat o input streamy vzniklé rozbalením ZIP souboru. Rozbalení souboru provádí pomocná třída Unzipper z balíčku cz.jasglib.tools, která má taktéž na svém vstupu input stream.
5
Input stream, instance třídy java.io.InputStream, reprezentuje v javě proud sloužící ke čtení dat
29
5.2.
Implementace překladače
Překladač, jakožto hlavní entita knihovny jasglib je reprezentován třídou Compilator z balíčku cz.upol.jasglib.compiling. Tato třída zaobaluje implementaci Java Compiler API, obsahuje tedy instanci překladače Javy a dvojici file managerů. Překladač Javy je použit systémový. Zásadní funkci překladače tvoří implementace file manageru. Pro účely in-memory překladu byl implementován in-memory file manager, třída InMemFileManager. Třídy, které tento file manager nedokáže překladači doložit (nemá k dispozici jejich zdrojový kód ani soubor bytekódu třídy), což jsou např. všechny knihovní třídy, deleguje na standardní file manager. In-memory file manager si uchovává seznam zdrojových kódů k překladu a také seznam přeložených tříd. Zdrojové kódy má z důvodu líné inicializace uloženy jak ve formě vstupní (tedy input streamy), tak ve formě pro překladač Javy (jako instance souborů zdrojového kódu Javy, SourceFileInMem). V momentě, kdy si překladač vyžádá otevření zvoleného souboru se zdrojovým kódem, file manager nejdříve ověří, že pro tuto třídu má k dispozici zdrojový kód. Pokud zjistí, že nemá, deleguje požadavek na standardní file manager. Pokud ví, že zdrojový kód má, vyhledá (nebo případně vytvoří a uloží) instanci souboru Javy. Vzhledem k tomu, že se jedná konkrétně o soubor zdrojového kódu, vrací instanci SourceFileInMem. Obdobně funguje i odpovídání na žádosti o otevření souboru s bytekódem třídy. V tomto případě však není vracena instance třídy SourceFileInMem ale ClassFileInMem. Ta totiž na rozdíl od souboru zdrojového kódu nenese input stream pro čtení dat, ale naopak output stream6 . Jak bude ukázáno později, bylo výhodné, aby byl použit konkrétně ByteArrayOutputStream (tedy proud zapisující do pole bytů). Vzhledem k tomu, že standardní file manager byl navržen pro účely překladače javac, soubory tříd vyhledává pouze na místech specifikovaných proměnnou CLASSPATH7 . Pokud však potřebujeme při překladu využívat třídy, které se nenachází na CLASSPATH aktuálně běžícího procesu, ale máme k nim přístup pomocí nějakého class loaderu, je toto omezení pro nás problémem. Tento problém se projevuje i u samotné aplikace JAssignment. Aplikace totiž bude spuštěna na webovém serveru Apache Tomcat, který každé aplikaci přiřazuje vlastní class loader (WebappClassLoader). Tento class loader se stará aplikaci o přístup ke knihovnám (jak globálním pro celý server, tak lokálním pro jednotlivé aplikace) aniž by byly umístěné na CLASSPATH Tomcatu samotného. Z tohoto důvodu je třeba překladači Javy říci, že knihovní třídy existují a kde je má najít. Řešením je při požadavku na vylistování všech podporovaných tříd ve zvolném balíčku (metoda list file manageru) provést vyhledání 6
Output stream, instance třídy java.io.OutputStream, reprezentuje v javě proud sloužící pro zápis dat 7 Systémová proměnná CLASSPATH určuje umístění, kde se mají hledat soubory tříd
30
jejich souborů tříd. Tuto problematiku řeší třídy PackageInternalsFinder a CustomJavaFileObject z balíčku ru.atamur.compilation, které byly převzaty z [1]. Jakmile je dokončen překlad a chceme-li přeložené třídy používat, je třeba k tomu využít class loader poskytovaný file managerem. In-memory file manager poskytuje instanci třídy InMemClassLoader. Tento class loader si uchovává seznam souborů Javy, který přebírá od file manageru. Je-li tento class loader požádán o třídu, ověří, zda-li již tuto třídu načetl, to znamená, že ze souboru třídy (ClassFileInMem) vytvořil třídu (Class). Pokud je již třída načtena, rovnou ji vrací. V opačném případě provede její načtení – ze souboru třídy si získá pole bytů s jejím bytekódem (proto je vhodné použít ByteArrayOutputStream) a provede vytvoření třídy. Vzledem k tomu, že bude potřeba pracovat se security managerem, musíme takto vytvářené třídě explicitně specifikovat bezpečností doménu. Protože se ale soubory tříd nacházejí „někde v paměti“ a nemají žádné fyzické umístění a nelze je tak nijak adresovat (pomocí URL), je možné si cesty k přeloženým souborům zvolit libovolné. Java jejich platnost totiž nijak neověřuje, slouží pouze k porovnávání. Vzhledem k pozdějšímu použití jsou tyto soubory proto „umístěny“ do adresáře, který je specifikován konfigurací sandboxu.
5.3.
Překlad zdrojových kódů a testů
Podobně jako u překladače Javy ani překladač Compiler sám o sobě překlad neprovádí. Slouží k tomu třída Compilation. Tato třída v sobě nese seznam souborů k překladu (input streamy) a volitelně také soubory, které mají být přiloženy na CLASSPATH či seznam dalších parametrů pro překladač. Pokud při překladu dojde k chybě, je odchytnuta listenerem CompilationResultDiagList. Tato třída každé chybové hlášení zpracuje, vytvoří z něj instanci FileError a uloží do výsledku překladu, CompilationResult. Po dokončení překladu je výsledek překladu vrácen. Tímto způsobem jsou přeloženy zdrojové kódy a testy. Pokud při překladu zdrojových kódů (testů) dojde k chybě, proces testování je ukončen a označen jako neúspěšný. Pokud se podaří přeložit jak zdrojové kódy, tak testy, jsou předány dále ke spuštění testů.
5.4.
Spouštění testů
Samotné spouštění testů je realizováno pomocí frameworku JUnit. Jednotlivé testovací třídy jsou osazeny anotacemi (v případě JUnit verze 3 použitím konvencí názvů tříd a metod), které předepisují parametry testování. V testovacích třídách se používáním metod třídy org.junit.Assert (jedná se např. o metody assertEquals, assertTrue, assertFalse nebo fail) specifikují správné či nesprávné výstupy testovaného kódu. 31
Dojde-li při testování k chybě (porušením podmínky u assert, vyhodnocením příkazu fail nebo neodchycenou výjimkou) je testovací metoda okamžitě ukončena (v prvních dvou případech tak, že je vyvolána výjimka AssertionError). Vzhledem k tomu, že samotné psaní testů je záležitostí uživatele aplikace (vyučujícího), nebude zde framework JUnit více popisován. Knihovna jasglib je implementována tak, aby jako testovací třídy považovala pouze ty třídy, které jsou potomky třídy JAsgTestCase z balíčku cz.upol.jasglib.export. Prvním důvodem, proč byla zvolena tato cesta je, že třída JAsgTestCase tak může nést konfiguraci nebo funkce společné pro všechny testy. V aktuální implementaci pouze informaci o časovém limitu pro provedení testů, ale je možné třídu upravit a doprogramovat libovolnou funkcionalitu. Druhý aspekt, který vedl k tomuto řešení je implementační. Jak bylo posáno v sekci 5.2., překladač přeložené třídy ukládá do jedné tabulky a nerozlišuje mezi třídami k testování a testovacími třídami. Tímto způsobem je možné vyhledat mezi přeloženými třídami právě ty, které mají být spuštěny jako testy. Třída TestsRunner z balíčku cz.upol.jasglib.tests se postará o vyhledání těchto tříd, jejich inicializaci, vytvoření a předání sandboxu a spuštění. Samotné spuštění tříd JUnit testů provádí třída frameworku JUnit JUnitCore z balíčku org.junit.runner. Po dokončení testů je zpracován jejich výsledek. Pokud došlo k chybám, je každá chyba (reprezentovaná třídou Failure z balíčku org.junit.runner.notification) uložena do TestFailureError z balíčku cz.upol.jasglib.results a ze všech těchto chybových hlášení je poté sestaven výsledek běhu testů, třída TestsResult z téhož balíčku. Tato instance je poté vrácena jako výsledek testů.
5.5.
Implementace sandboxu
Sandbox je v knihovně jasglib implementován třídami z balíčku cz.upol.jasglib.tests.sandbox, zvláště pak třídou Sandbox. Třída Sandbox se stará o inicializaci sandboxového prostředí a spuštění jeho „těla“, tedy instance třídy JUnitInSandbox. Nastavení bezpečnostních oprávnění spouštěného kódu je řešeno pomocí AccessController.doPrivileged, jak je popsáno v kapitole 3.10. Privilegovaná sekce má přidělen nový bezpečnostní kontext, který obsahuje jedinnou bezpečnostní doménu. Zdroj kódu a seznam povolení této domény jsou určeny konfigurací sandboxu. Vzhledem k tomu, že bezpečnostní doména, která je testovaným třídám přiřazována, má zdroj kódu shodný se zdrojem kódu bezpečnostní domény, kterou tyto třídy získaly při vytváření class loaderem (kapitola 5.2.), je zaručeno, že tato oprávnění budou na testované třídy aplikována. Aby však testování fungovalo, je nutné k povolení sandboxu přidat povolení k operacím, které vyžaduje samotný framework JUnit. Sandbox je tedy nutné spouštět s povolením k přístupu k zásobníku výjimky a přístupem ke 32
slotům a metodám. To znamená, že doména, se kterou je sandbox spouštěn musí umožňovat povolení RuntimePermission a to o názvech getStackTrace a accessDeclaredMembers. O tuto vlastnost však není třeba se starat, je automaticky předchystána v každé instanci konfigurace sandboxu.
5.6.
Konfigurace sandboxu
V požadavích na aplikaci bylo, aby bylo možné sandbox konfigurovat, tedy umožnit modifikovat oprávnění přidělovaná testovanému kódu. Z tohoto důvodu vznikla třída SandboxConfig nacházející se v balíčku cz.upol.jasglib.tests.sandbox. Třída nese následující údaje: • directory, jméno fiktivního adresáře, ve kterém se budou nacházet (jak soubory zdrojových kódů, tak) soubory tříd. Na soubory v tomto adresáři budou aplikována bezpečnostní povolení, mělo by se proto jednat o unikátní adresář, který se nenachází v soborovém systému hostitelského počítače. Výchozí hodnotou tohoto atributu je "/java". • timeout, časový limit pro provední jednotlivých testů v milisekundách. Výchozí hodnota je 5000. • permissions, seznam dodatečných oprávnění pro sandbox. Ve výchozím provední je prázdný. Kromě seznamu („uživatelských“) povolení obsahuje nezávisle na něm seznam výchozích povolení potřebných pro běh testů. Konfiguraci sandboxu lze načítat (ukládat) z (do) XML souboru. Struktura souboru je demonstrována v adresáři s konfigurací aplikace JAssignment.
5.7.
jasglib konzolová aplikace
Vzhledem k tomu, že knihovna jasglib je vyvíjena pro univerzální použití, disponuje také konzolovou aplikací, která umožňuje její použití. Jedná se o třídu JAssignmentConsoleApp z balíčku cz.upol.jasglib. Aplikace kopíruje funkcionalitu knihovny jasglib, na vstupu je tak potřeba ji specifikovat seznam souborů zdrojových kódů k otestování, soubory testů a (nepovinně) cestu ke konfiguračnímu souboru sandboxu. Zdrojové kódy a testy je možné zadat výčtem souborů nebo jako cestu k ZIP archivu. Není-li konfigurační soubor sandboxu specifikován, je použita výchozí konfigurace. Přesná syntaxe parametrů je popsána v nápovědě k programu (spuštění s přepínačem -help).
33
6.
Aplikace JAssignment
Aplikace JAssignment, která vznikla jako výsledný produkt této práce, je plnohodnotný informační systém pro správu úkolů do školních předmětů či jiných kurzů, kde se vyučuje programovací jazyk Java. Vzhledem k tomu, že byla realizována pro účely Katedry informatiky Univerzity Palackého, obsahuje několik funkcí specifických právě pro toto pracoviště.
6.1.
Struktura aplikace
Aplikace JAssignment byla vyvíjena jako Maven8 projekt. To znamená, že aplikace musí dodržovat Mavenem předepsanou strukturu. Aplikace JAssignment je proto reprezentována modulem jasg-main, jehož jedniným úkolem je spojovat drohromady funkční moduly aplikace. Aplikace je tvořena třemi funkčními moduly. Prvním je knihovna jasglib popsaná v kapitole 5. Druhým je modul databáze, označený jasgdb, který je popsán v kapitole 6.3. Třetím modulem je modul samotné webové aplikace. Ten má označení jasgweb.
6.2.
Uživatelské role
Aplikace byla navržena, aby s ní pracovali dva druhy uživatelů. Do skupiny s nižšími oprávněními patří studenti. Studenti mají možnost se do aplikace přihlásit, odesílat řešení úkolů, a svá nahraná řešení si prohlížet. V nastavení si mohou změnit jen heslo kontaktní e-mail. Druhým typem uživatele je vyučující. Aplikace byla navržena, aby roli vyučjího plnil jen jeden člověk. Vyučující má prakticky neomezená oprávnění – může si prohlížet všechna řešení všech studentů, zadávat úkoly a upravovat nastavení studentů. Vyučující se stará o přidělování a odebírání zápočtů a nastavování studentů jako aktivní/neaktivní. Má také možnost posílat ostatním studentům zprávy. Mezi uživatele aplikace se přímo neřadí, ale je třeba zmínit i administrátora aplikace. Administrátor se stará o správný běh aplikace a její konfiguraci. Administrátor je také ten, kdo řeší problémy s aplikací – chyby a pády. Administrátor nemá přidělen přímý přístup do aplikace (je-li to nutné, přihlašuje se jako vyučující).
8
Maven je nástroj pro kompletní správu projektů v Javě
34
6.3.
Implementace modelu databáze
Aplikace JAssignment využívá pro ukládání dat o studentech a úkolech relační databázi. Vzhledem k implementaci v Javě bylo zvolenou použití databázového systému Apache Derby. Komunikace probíhá pomocí standardního jdbc rozhraní, poskytovatelem připojení je klient databázového serveru Derby, třída org.apache.derby.jdbc.ClientDriver. V základním nastavení se aplikace připojuje na databázový server umístěný na lokálním stroji na portu 1527. Aplikace obsahuje jednoduchý framework objektově-relačního mapování, samostatný modul celé aplikace označený jasgdb. Vzhledem k tomu, že se od počátku počítalo se spuštěním aplikace na webovém serveru Apache Tomcat, který poskytje službu JNDI9 , je aplikace určena především pro připojení k databázovému serveru touto cestou. Použití JNDI přináší kromě centralizace konfigurace zdrojů celé aplikace také například možnost tzv. „poolování“. To znamená, že databázová připojení, která jsou označena jako uzavřená jsou ve skutečnosti recyklována a jsou znovu použita, což vede k urychlení navazování spojení. V případě, že není dostupné připojení pomocí JNDI se modul jasgweb připojuje standardním způsobem. V tomto případě je nutné specifikovat parametry připojení pomocí třídy DatabaseParams z balíčku cz.upol.jasgdb.connection. Jak již bylo zmíněno, modul implementuje objektově-relační mapování. Abstraktní třída PersistentObject z balíčku cz.upol.jasgdb.modification je předkem všech objektů, které mohou být uloženy v databázové tabulce. V konstruktoru je třeba jí specifikovat třídy (nebo jednu třídu, která implementuje obě) implementující rozhraní DataToQuery (vkládání hodnot do SQL dotazů a získávání z jejich výsledků) a PersistenceProperties (informace ohledně perzistence). Rozhraní PersistenceProperties dále vyžaduje implementovat potomka třídy TableProperties, který pak předepisuje samotnou strutkuru databázové tabulky a rozdělení jednotlivých atributů do skupin (AttributesGroup). Takto vytvořený „persistentní objekt“ disponuje metodami load, save a delete pro načtení, uložení (vložení nebo aktualizaci) a odstraněnění záznamu z tabulky. Vzhledem k tomu, že aplikace JAssignment vyžaduje ukládat a načítat rozsáhlá binární a textová data (CLOB a BLOB), je nežádoucí, aby byly objekty načítané a ukládané celé. Z tohoto důvodu si nese každý objekt seznam atributů, se kterými aktuálně pracuje, a které má ukládat, resp. načítat. Přístup k databázi je ošetřen vlastními výjimkami. Dojde-li při práci s databází k chybě, je volána výjimka DatabaseException, která bývá přebalována na obecnější DataAccessException. Obě třídy výjimek se nacházejí v balíčku cz.upol.jasgdb.exceptions. Aplikace JAssignment používá tři třídy perzistentních objektů, nacházejí se v balíčku cz.upol.jasgdb.jasgData. Jedná o třídy Student, Assignment a Solution. 9
Java Naming and Directory Interface, rozhraní pro přístup ke zdrojům (např. soubory, databáze nebo obecné servery)
35
Třída Student implementuje rozhraní LoginablePerson a tudíž disponuje atributy: uživatelské jméno, křestní jméno a přijmení, e-mailová adresa a heslo. Navíc si eviduje informace o tom, zda-li je student stále aktivně studující a zda-li již získal zápočet (a případně kdy). Heslo je reprezentováno třídou Password. Heslo je šifrováno pomocí hashovacího algoritmu SHA-256 a to ve 4 iteracích. Před šifrováním je proloženo náhodně vygenerovanou solí délky 20 bytů. Zadání úkolu je reprezentováno třídou Assignment. Tato třída nese název tématu úkolu, text zadaní, datum zadání a termín pro vypracování (pokud úkol zatím nebyl zadán jsou údaje nastaveny na konec roku 9999). Obsahuje také ZIP archiv testů (reprezentovaný polem bytů). Jednotlivá řešení úkolů jsou reprezentována třídou Solution. Každý úkol se odkazuje na studenta, který jej nahrál a na zadání úkolu, kterého je řešením. Dále obsahuje datum nahrání, příznaky (zda-li prošlo testy, zda-li bylo zkontrolováno vyučujícím a zda-li bylo vyučujícím schváleno) a samotná data ZIP souboru se zdrojovými kódy řešení. Pro snadnější orientaci mezi nahranými řešeními je také ukládán název souboru. Modul jasgdb je pro testovací a ladící účely vybaven jednoduchým konzolovým (neinteraktivním) klientem. Pro jeho spuštění slouží třída JAssgDbSimpleConsoleClient z balíčku cz.upol.jasgdb. Tato třída provede připojení k databázi a volitelně výpis seznamu všech studentů, úkolů nebo nahraných řešení. Použití pro účely ověření připojení je nastíněno v instalační příručce (Příloha A.).
6.4.
Realizace webové aplikace
Jak je popsáno v kapitole 1.2., webová aplikace JAssignment (značená jasgweb) byla vytvořena jako JSF projekt. Přesněji řečeno, jedná se o PrimeFaces projekt. PrimeFaces je JSF framework, který rozšiřuje standardní JSF nástroje o vlastní sadu komponent. Původní implementace webové aplikace počítala s využitím frameworku RichFaces. Vzhledem k tomu, že při vývoji se však začalo projevovat veliké množství omezení a docházelo k velikým komplikacím, byla aplikace přepsána pro framework PrimeFaces. Tuto změnu jistě uvítají i uživatelé, neboť framework PrimeFaces využívá javascriptové knihovny JQuery UI, takže aplikace vypadá velmi pěkně a moderně s již předpřipravenými grafickými šablonami. Komunikace se serverem je také díky tomu realizována výhradně ajaxovými voláními, tedy bez nutnosti obnování celé stránky. V logu a ikoně aplikace se vyskytuje symbol „Duke“, maskot Javy. Pochází z [14] a jeho licence umožňuje jak redistribuci, tak úpravy. Nyní budou ve zkratce popsány některé významné komponenty webové aplikace. Zvláštní část je pak věnována systému pro odesílání zpráv prostřednictvím 36
e-mailů. Přihlašování O přihlašování (a odhlašování) do aplikace se stará třída Login z balíčku cz.upol.jasgweb.login. Je-li proveden pokus o přihlášení, je vytvořena a uložena instance této třídy do sezení (třída je session-scoped bean10 ), je do ní uložena instance přihlášeného uživatele a je překreslena stránka. V případě odhlášení je přihlášený uživatel z objektu Login odebrán a sezení je zrušeno. Vzhledem k tomu, že sezení nedokáže být zrušeno prostřednictvím ajaxového volání, je po odhlášení stránka obnovena. Uživatel je také automaticky odhlášen v případě nečinnosti po vypršení časového intervalu 40 minut. Notifikace a hlášení chyb Framework PrimeFaces obsahuje komponentu p:growl, která provádí notifikaci (zobrazení „bublin“ v pravém horním rohu stránky), zobrazování hlášení vygenerovaných na serveru. O vytváření hlášení v aplikaci na serveru se stará třída ResultReporter z balíčku cz.upol.jasgweb. Prostřednictvím této třídy je uživatel informován jak o úspěšném dokončení prováděných operací, tak o chybách. Dojde-li k chybě v aplikaci, je o ní uživatel informován, je zalogován zásobník její výjimky a je odeslána zpráva administrátorovi aplikace. V případě, že selže i odeslání zprávy, je o nahlášení chyby požádán uživatel. Validace vstupů Webový framework JSF má vestavěnou podporu pro validaci uživatelských vstupů. V aplikaci JAssignment je toho využito použitím knihovny Hibernate Validator. Validace vstupů je pak pouze záležitostí napsání odpovídajících anotací (např. @Length, @Date nebo @Pattern) k příslušným slotům beanu (objektu), který formulář obsluhuje. Ostatní validace (např. kontrola přihlašovacích údajů) je implementována samostatně a neúspěšná validace je hlášena uživateli pomocí třídy ResultReporter. Import a export dat Aplikace je koncipována tak, že někdy před začátkem nového semestru vyučující provede tzv. zahájení nového semestru, což je operace, která smaže veškerá data a vyžádá si zadání nových. Součástí je také import seznamu studentů. První možností, jak vložit seznam studentů, je vložit do aplikace obsah CSV souboru s jejich údaji. Druhá možnost je využitím Webových služeb nad IS/Stag. 10
Session-scoped bean je objekt, jehož instance je udržována na serveru napříč celým přihlašovacím sezením
37
Po vyplnění údajů o předmětu (název, zkratka, rok a semestr) aplikace umožňuje zaslat požadavek na získání seznamu studentů zapsaných na předmět v informačním systému IS/Stag. Komunikace s IS/Stag je volitelná funkce, takže je realizována velmi jednoduše. Z tohoto důvodu je seznam studentů získáván anonymně, uživatel tak nemusí zadávat své přístupové údaje do IS/Stag. Nevýhodou je, že IS/Stag nepřihlášeným uživatelům neposkytuje e-mailové adresy nalezených studentů. Pokud má student uživatelské jméno ve vhodném formátu (čtyři znaky z příjmení, dva znaky z křestního jména a dva znaky pořadového čísla), je mu automaticky přiděleno e-mailová adresa ve tvaru: .<pořadové číslo>@upol.cz. Vzhledem k tomu, že import seznamu studentů lze provádět pouze jednorázově, je vhodné provádět tuto akci až v době, kdy je seznam zapsaných studentů ustálen. V momentě, kdy je zahájen nový semestr, lze studenty pouze po jednom přidávat. Odebírat studenty nejde, je třeba je označit jako neaktivní. Import seznamu úkolů a řešení implementován nebyl. Pokud by bylo tuto operaci potřeba provést, je třeba ji provést přímo na databázovém serveru. Exportování dat je řešeno dvěma způsoby. Znění zadání úkolu nebo seznam studentů je možné odeslat přímo na tiskárnu (pomocí komonenty p:printer). Druhý způsob exportování dat je formou stažení ZIP archivu. Stáhnout lze jednak celou zálohu aplikace (obsah databázových tabulek v CSV, všechnny soubory řešení a testů plus všechy konfigurační soubory), všechna řešení zvolného úkolu a nebo jen řešení úkolu, která byla schválena.
6.5.
Systém zpráv a odesílání e-mailů
Součástí aplikace JAssignment je nástroj pro vzájemnou komunikaci mezi studenty, vyučujícím a případně administrátorem aplikace. Komunikace je realizována pomocí odesílání e-mailů, nicméně uživatelé jsou od tohoto faktu odstíněni a napříč aplikací se používá pojem „zpráva“. K odesílání e-mailů je používáno standardní rozhraní Java Mail API. Připojování se k mailovému (SMTP) serveru obstarává služba JDNI. Zprávy, které aplikace rozesílá se dělí na dva druhy. Prvním jsou zprávy odesílané uživatelem, tedy zprávy, které uživatel odesílá vyplněním formuláře na stránce. Druhým jsou zprávy automaticky generované při určených událostech. Každá zpráva je označena svým jedinečným ID (např. "solutionSubmitted" odpovídající zprávě o tom, že student do aplikace nahrál řešení úkolu). V momentě, kdy je od aplikace vyžádáno odeslání zprávy (nebo od uživatele otevření patřičného formuláře zprávy), je vyhledána šablona požadované zprávy. Šablona zprávy je reprezentována třídou MessageTemplate, nositelem všech šablon je třída MessagesTemplates (obě z balíčku cz.upol.jasgweb.mails.templating). U automaticky generovaných zpráv je nejdříve ověřeno, zda-li je odesílání této zprávy povoleno (tímto lze deaktivovat např. zasílání zpráv vyučujícímu, kdykoliv student nahraje řešení). Poté 38
jsou zkontrolována oprávnění odesílatele (např. student nemůže odesílat zprávu ostatním studentům) a jsou zpracovány parametry zprávy. Pomocí parametrů je šablona osazena konkrétními hodnotami a je z ní vytvořena samotná zpráva (Message z balíčku cz.upol.jasgweb.mails). O osazování šablon hodnotami se stará třída VariablesExpander z balíčku cz.upol.jasgweb.mails.templating. Šablona zprávy totiž může obsahovat speciální sekvence, např. ${lecturer.surname}, ${assignment.title} nebo ${exception.message}. Tyto symboly jsou v předmětu i textu zprávy nahrazeny konkrétními hodnotami v aktuálním kontextu. Sekvence, které v aktuálním kontextu nemají definovanou hodnotu jsou vypuštěny. Takto vytvořená zpráva je poté přímo odeslána nebo přednastavena do formuláře pro odeslání zprávy (v závislosti na jejím typu) a odeslána až po jeho potvrzení. V případě, že zprávu odesílá nepřihlášený uživatel (když kontaktuje administrátora s problémem s přihlášením) je nutné, aby uživatel vyplnil svůj kontaktní e-mail (u přihlášeného uživatele je znám). Také bylo potřeba zajistit ochranu proti odesílání formuláře roboty. Framework PrimeFaces pro tyto účely obsahuje komponentu p:captcha pro validaci CAPTCHou. V případě aplikace JAssignment však bylo zvoleno řešení jednodušší, které méně narušuju konzistenci uživatelského rozhraní. Po uživateli je proto vyžádáno, aby zadal předposlední slovo z textu zprávy.
6.6.
Konfigurace aplikace
V aplikaci JAssignment je možné provádět dodatečné změny konfugurace a to bez potřeby opětovného sestavování a instalace aplikace (stačí poté jen restartovat server). Zásadní konfigurační soubor celé aplikace je soubor context.xml, který se nachází v adresáři META-INF kořenového adresáře aplikace na serveru. Je to konfigurační soubor služby JNDI. To znamená, že v tomto souboru se specifikují parametry pro připojení k databázovému i SMTP serveru. Také je zde specifikována cesta k aresáři s dalšími konfiguračními soubory. Ve výchozím nastavení se odkazuje na adresář resources/config/ v kořenovém adresáři aplikace. Tento adresář obsahuje veškerou další konfiguraci aplikace. Obsahuje následující soubory: • jasg-configuration.properties, hlavní konfigurační soubor (obsahuje jen e-mailovou adresu administrátora) • messages.xml, seznam šablon zpráv • sandbox-configuration.xml konfigurace sandboxu pro spouštění testů • lecturer.properties vlastnosti vyučujícho • subject.properties vlastnosti předmětu 39
První tři soubory jsou určené ke konfiguraci administrátorem přímo na serveru, obsah zbylých dvou pak umožňuje editovat přímo aplikace (vyučujícímu) pomocí formulářů. Spousta další konfigurace (například požadavky na měněné heslo) je možná přepsáním konstant přímo ve zdrojových kódech a následným opětovným sestavením a instalací aplikace.
6.7.
Testování aplikace
Vzhledem k tomu, že aplikace bude spuštěna na aplikačním serveru, je třeba aby byla aplikace důkladně otestována před nasazením do ostrého provozu. Z tohoto důvodu byla postupně vytvářena testovací data, na kterých byla aplikace testována. Jedna sada testovacích dat je distribuována přímo spolu s aplikací. U těchto dat je zaručená konzistence a současně i jistá logická soudružnost. Data jsou tvořena čtveřicí studentů, každý reprezentující rozdílnou kategorii (se zápočtem, neaktivní, se splněnými všemi úkoly a s úkolem k vypracování). Data také obsahují čtyři ukázková zadání úkolů. Data lze do aplikace naimportovat spuštěním třídy JAsgTestDataCreator z balíčku cz.upol.jasgdb.testData. Aplikace také byla otestována na zvýšenou zátěž, ke které může docházet těsně před termínem odevzdávání úkolů. Na serveru byly spuštěny procesy, které jej zatěžovaly (reprezentace provádění testů) a byly vysílány požadavky na připojení. V takovéto situaci server začal mít s vyřizováním požadavků nebo je začal odmítat až v případě, kdy jich bylo několik tisíc, což by mělo být pro aplikaci JAssignment dostačující. Aplikace byla pro účely testování a objahoby nainstalována na studentský server tux.inf.upol.cz studentů Katery informatiky Univerzity Palackého. Pro testování odesílání zpráv byl využit SMTP server smtp.gmail.com.
6.8.
Možná rozšíření aplikace
Vytvořená aplikace splňuje všechny požadavky zadavatele a je tak připravena k použití. Existují však další volitelné funkce, kterými by aplikace ještě mohla být vybavena. Asi nejzajímavější by mohlo být kompletní provázání aplikace s informačním systémem IS/Stag. Kromě importu seznamu studentů by mohla aplikace do tohoto informačního systému také vkládat záznamy o přidělených zápočtech a udržovat si aktuální seznam studentů i po dobu semestru. V aplikaci také nebyla přímo implementována podpora pro konfiguraci sandboxu. To je z důvodu, že konfigurace bezpečnostních oprávnění se přímo váže na rozhraní Java Security API a u uživatele se nepředpokládá jeho znalost. Mezi další funkce by mohlo patřit automatické dodatečné testování více úkolů současně. Uživatelé by jistě uvítali možnost prohlížení souborů řešení a testů 40
přímo v prohlížeči, např. podobně, jak to funguje u služby grepcode.com.
6.9.
Uživatelská příručka
Prvním krokem při práci s aplikací JAssignment je přihášení. Každý student i vyučující mají přiděleno uživatelské jméno a heslo. Pokud zapomenete své heslo, můžete si zažádat o nové. Při jiných komplikacích máte možnost kontaktovat administrátora.
Příručka pro studenta Na obrázku 8. je vidět aplikace po přihlášení studenta – seznam úkolů, které má vypracovat (karta „K odevzdání“). Student má možnost otevřít dialog pro nahrání řešení úkolu, zobrazit již nahraná řešení a nebo poslat vyučujícímu dotaz. Po otevření dialogu pro nahrání řešení je student po přečtení instrukcí vyzván, aby náhrál ZIP soubor s řešením. Po nahrání je soubor zpracován (rozbalen, zdrojové kódy přeloženy a otestovány) a je mu zobrazen výsledek. V případě, že řešení testy prošlo, má možnost k řešení přidat komentář a poslat jej vyučujícímu. Pokud je řešení nesprávné, je uloženo a studentovi umožní jen přidat komentář. Nesprávná řešení jsou ukládána automaticky, aby byli studenti odrazeni od odesílání řešení „metodou pokus-omyl“. Na kartě „Všechny úkoly“ je k dispozici seznam všech zadaných úkolů, karta „Informace“ obsahuje základní informace o předmětu. V pravém horním rohu se nachází uživatelský panel. Ten zobrazuje informace o právě přihlášeném uživateli (studentovi). Umožňuje poslat dotaz vyučujícímu, zobrazid dialog nastavení a nebo se odhlásit. Dialog nastavení umožňuje studentovi pouze upravit svou e-mailovou adresu, změnit heslo a oznámit ukončení studia předmětu. Student, který oznámí ukončení studia předmětu již nadále nebude evidován jako aktivní student, nemůže dále nahrávat řešení úkolů a nemá tak nárok na získání zápočtu. Příručka pro vyučujícího Po přihlášení je vyučujícímu zobrazena karta „Řešení ke kontrole“ (obrázek 9.). Zde vidí seznam nahraných řešení, která prošla testy a čekají na kontrolu. U každého řešení má možnost pomocí dialogu pro schválení/zamítnutí řešení stáhnout jeho zdrojové kódy, provést dodatečné testy, přidat komentář a schválit jej nebo vrátit k přepracování. Kliknutím na tlačítko „Všechna řešení“ má možnost zobrazit všechna nahraná řešení (všech aktivních studentů a úkolů). Na kartě „Úkoly“ se nachází seznam (zadaných i nezadaných) úkolů. U každého úkolu lze stáhnout soubor nahraných řešení, otevřít dialog pro úpravy zadání úkolu a nebo zadání vytisknout. Na kartě „Úkoly“ se také nachází tlačítko pro 41
Obrázek 8. Aplikace JAssignment po přihlášení studenta zadání nového úkolu. Při vytváření nového (úpravách stávajícího) úkolu je třeba (je možné) nahrát třídy testů. To lze učinit buďto přímým nahráním ZIP souboru se zdrojovými kódy nebo vložením zdrojového kódu testovací třídy do vstupního pole formuláře. Karta „Studenti“ obsahuje seznam studentů. Každému studentovi umožňuje zaslat zprávu, změnit nastavení, přidělit nebo odebrat zápočet a označit nebo odoznačit studenta jako aktivního. Neaktivní studenti jsou vypsáni šedě. Poslední karta, karta „Předmět“, obsahuje informace o předmětu a aplikaci. Umožňuje upravit parametry předmětu, stáhnout soubor zálohy aplikace a otevřít dialog zahájení nového semestru. Dialog zahájení nového semestru umožní změnu nastavení předmětu, smazání všech úkolů a vložení nového seznamu studentů. V pravém horním rohu se nachází uživatelský panel. Umožňuje vyučujímu se odhlásit, poslat zprávu všem studentům, všem aktivním studentům a otevřít dialog nastavení. V dialogu nastavení si lze změnit (všechny) uživatelské údaje a nebo heslo.
42
Obrázek 9. Aplikace JAssignment po přihlášení vyučujíciho
Závěr V rámci této práce vznikla aplikace JAssignment splňující všechny požadavky zadavatele. Vyučujícímu umožňuje zadávat úkoly, kteří studenti vypracovávají. Po nahrání do aplikace jsou zkontrolovány pomocí JUnit testů a v případě úspěchu jsou odeslány vyučujícímu. Aplikace také umožňuje import a export dat a vzájemnou komunikaci mezi studenty a vyučujícím. Kromě toho byla aplikace vybavena některými dalšími funkcemi, například import dat z IS/Stag nebo evidence přidělených zápočtů. Nejdůležitější částí aplikace se stala knihovna jasglib, která má za úkol testovat správnost řešení úkolů. Knihovna provádí jednak překlad zdojových kódů a poté jejich spouštění v prostředí sandboxu. Zdrojové kódy jsou překládány tzv. inmemory překladačem, který byl implementován pomocí Java Compiler API. Pro implementaci sandboxu bylo použito nástrojů pro řízení kontroly přístupu z Java Security API. Rozhraní Java Security API bylo důkladně nastudováno a popsáno s důrazem právě na nástroje spjaté s kontrolou řízení přístupu. Aplikace JAssignment bude (po domluvě se zadavatelem) před začátkem zimního semestru akademického roku 2014 přesunuta z provizorního úložiště a nasazena do ostrého provozu na vlastním serveru.
43
Conclusions The JAssignment application have been created within this thesis and it satisfies all requirements of the contracting authority. It allows lecturer to enter assignments which students solve. Submitted solutions are checked by JUnit tests and when succeed they will be sent to lecturer. Application allows to import and export application data and communicate between students and lecturer as well. Moreover, application have been fitted with some other functions, for instance importing data from IS/Stag or recording assigned credits. The most important part of application became library jasglib which is tasked to test correctness of assignments’ solutions. The library performs compilation of source codes and then their running in the sandbox environment. Source codes are compiled using in-memory compiler which have been implemented using The Java Compiler API. The sandbox have been implemented using access control tools of The Java Security API. The Java Security API interface have been studied thoroughly and described with an emphasis on tools of access control architecture. The JAssignment application will be moved (after agreement with the contracting authority) before winter semmester of the academical year 2014 starts from temporary location and deployed into live operation on its own server.
44
Reference [1] Atamur. Using built-in JavaCompiler with a custom classloader. Elektronická publikace, 2009. [2] Biesack. Create dynamic applications with javax.tools. Elektronická publikace, 2007. [3] Gong, Elliston, Dageforde. Inside JavaTM 2 Platform Security: Architecture, API Design, and Implementation, 2nd edition. Pearson Education, Upper Saddle River (New Jersey), 2003. [4] Gong, et. al. Going Beyond the Sandbox: An Overview of the New Security Architecture in the JavaTM Development Kit 1.2. Proceedings of the USENIX Symposium on Internet Technologies and Systems, Monterey (California), 1997. [5] Gosling, Joy, Steele, et. al. Java Language and Virtual Machine Specifications. Elektronická publikace, 2014. [6] Kalinovsky. Covert Java Techniques for Decompiling, Patching, and Reverse Engineering. Sams Publishing, Indianapolis, 2004. [7] Neward. java.security.Policy: When “java.policy” Just Isn’t Good Enough. JavaGeeks.com, 2002. [8] Oracle. Default Policy Implementation and Policy File Syntax. Elektronická publikace, 2014. [9] Oracle. keytool - Key and Certificate Management Tool. Elektronická publikace, 2014. [10] Oracle. Java Cryptography Architecture Oracle Providers Documentation. Elektronická publikace, 2014. [11] Oracle. JavaTM Platform, Standard Edition 7 API Specification. Elektronická publikace, 2014. [12] Oracle. JavaTM Security Overview. Elektronická publikace, 2014. [13] Oracle. Troubleshooting Security. Elektronická publikace, 2014. [14] Palrang, et. al. Duke Images. Elektronická publikace, 2010.
45
A.
Instalační příručka
Příprava Sestavení a spuštění aplikace JAssignment je vcelku rutinní záležitostí a lze ji provést několika příkazy v příkazové řádce, bez použí libovolného vývojového prostředí. Vzhledem k tomu, že aplikace poběží na serveru, popíši instalaci pouze touto cestou. Celý postup také bude popisován pro spuštění na platformě GNU/Linux. Všechny použité nástroje jsou však přenositelné i na ostatní platformy, takže postup při instalaci tam bude obdobný. Většinu práce při sestavování a spouštění aplikace za nás obstará program Maven, je proto třeba jej mít nainstalovaný. Cílem naší práce bude následující adresářová struktura: jasg-main dbserver webserver database
zdrojové a binární soubory aplikace databázový server webový server databáze
Na začátku si v kořenovém adresáři celé aplikace rozbalíme zdrojové kódy aplikace JAssignment. To provedeme následujícím příkazem: $ cd JAssignment $ tar - zxvf jassignment -1.0 - SNAPSHOT . source . tar . gz
Sestavení aplikace O sestavení aplikace (buť celé, nebo jen některého modulu) se postará Maven. Pro sestavení kteréhokoliv z modulů je třeba se přesunout do jeho kořenového adresáře. Například pro modul jasgdb je třeba přejít do adresáře jasg-main/jasgdb. Poté už jen stačí zavolat maven a říct mu, že chceme aplikaci sestavit. To se porovádí nejčastěji příkazem: $ mvn clean install
Tento příkaz provede stažení všech knihoven závislých na daném modulu, sestavení modulu, spuštění testů a nainstalování do tzv. lokálního repozitáře. Sestavujeme-li moduly jasgweb nebo jasg-main, Maven automaticky sestaví i zbývající moduly, neboť je mají uvedeny v závislostech. Chceme-li získat přímo *.JAR (*.WAR) archiv, použijeme příkaz $ mvn clean package
46
a výsledný binární archiv se nachází v podadresáři target. Dokumentaci javadoc vytvoříme příkazem $ mvn javadoc : javadoc
a vytvořena bude v podaresáři target/site/apidocs modulu.
Spuštění databázového serveru Pro správnou funkci aplikace je nutné mít nainstalovaný a spuštěný databázový server Apache Derby. Stáhneme archiv databázového serveru db-derby-verze-bin.zip a rozbalíme jej do adresáře dbserver. Vzhledem k tomu, že server vytváří databázi v adresáři, ze kterého je spuštěn, vytvoříme si adresář database a server spustíme z něj. Instalaci a spuštění databázového serveru tedy lze provést následujícími příkazy: $ $ $ $ $
unzip db - derby - verze - bin . zip mv db - derby - verze - bin dbserver mkdir database cd database ../ dbserver / bin / s ta rt Ne two rk Se rv er
Pokud server vypíše hlášku Síťový server Apache derby (...) spuštěn a připraven přijímat připojení na portu 1527, server běží a můžeme s k němu připojit. O kontrolu připojení (a součastně inicializaci databáze) se postará samotný modul jasgdb. Stačí v jeho kořenovém adresáři spustit předpřipravený skript run.sh. Přesuneme databázový server na pozadí (Ctrl+Z a příkaz bg) a zadáme: $ cd ../ jasg - main / jasgdb $ ./ run . sh
Pokud je vše v pořádku, měl by vypsat varování, že se nepodařilo připojit k databází pomocí JNDI a informace o tom, že byly vytvořeny databázové tabulky. Pokud je vypsána nějaká chyba, je třeba postupovat podle chyby, ke které došlo.
Spuštění webového serveru Modul webové aplikace jasgweb byl od počátku vyvíjen pro chod na webovém serveru Apache Tomcat 7.0. Stáhneme si tedy archiv webového serveru Apache Tomcat (apache-tomcat-7.0-verze.zip nebo tar.gz) a rozbalíme. $ unzip apache - tomcat -7.0 - verze . zip $ mv apache - tomcat -7.0 - verze webserver $ cd webserver
V mém případě bylo nutné nastavit skriptům serveru práva pro spouštění: $ chmod u + x bin / catalina . sh bin / startup . sh bin / shutdown . sh
Poté je umožněno samotné spuštění serveru příkazem: 47
$ bin / startup . sh
Server vypíše některé základní informace a je spuštěn. Ověřit jeho funkčnost lze zadáním adresy http : // localhost :8080/
do adresního řádku webového prohlížeče.
Umístění aplikace na server O umístění aplikace na webový server se nám taktéž postará Maven. Musíme je oba k tomu ale nejdříve nakonfigurovat. Nejdříve vytvoříme uživatele serveru, pod kterým se bude Maven přihlašovat. To učiníme tak, že v souboru conf/tomcat-users.xml vytvoříme uživatelskou roli a následně uživatele, kterému ji přiřadíme. Mezi značky a vložíme tyto řádky: < role rolename = " manager - script " / > < user username = " tomcatMavenAdmin " password = " admin1234 " roles = " manager - script " / >
Uživatelské jméno a heslo si pochopitelně můžeme zvolit vlastní. Poté server restartujeme: $ bin / shutdown . sh $ bin / startup . sh
Nyní musíme nakonfigurovat Maven. Otevřeme si jeho konfigurační soubor, zpravidla se jedná o soubor ~/.m2/settings.xml a přidáme náš server. To znamená vložit záznam <server> mezi servery - do značek <servers> a . Pokud tyto značky (nebo celý soubor) neexistují, vytvoříme je. Po úpravě by měl soubor vypadat takto: < settings xmlns = " http :// maven . apache . org / SETTINGS /1.0.0 " xmlns : xsi = " http :// www . w3 . org /2001/ XMLSchema - instance " xsi : schemaLocation = " http :// maven . apache . org / SETTINGS /1.0.0 ␣ http :// maven . apache . org / xsd / settings -1.0.0. xsd " > ... < servers > ... < server > JAsgServer id > < username > tomcatMavenAdmin username > < password > admin1234 password > server > ... servers > ... settings >
48
Doplníme pochopitelně naše uživatelské jméno a heslo, které jsme nastavili uživateli serveru. ID serveru ponecháme JAsgServer, neboť je určeno konfigurací modulu jasgweb. Nyní můžeme aplikaci JAssignment nainstalovat na server. To provedeme následujícími příkazy: $ cd ../ jasg - main / jasgweb $ mvn tomcat7 : deploy
Pokud nebyla vypsána žádná chyba, můžeme zkusit, zda-li se aplikace nainstalovala, a to zadáním adresy aplikace do webového prohlížeče: http : // localhost :8080/ jasgweb
Pokud se nám objeví přihlašovací obrazovka (v tuto chvíli to bude pravděpodobně to jedinné, co bude fungovat), pak se nám aplikaci podařilo nainstalovat a můžeme se pustit do konfigurace pro její správný běh. Pokud se nám nainstalova aplikaci na server nepodařilo, tak máme pravděpodobně chybně vyplněné některé údaje. Je třeba opět důkladně zkontrolovat popsanou konfiguraci. Pokud se v adresáři webserver/webapps nachází soubor jasgweb.war nebo adresář jasgweb, je třeba je smazat.
Konfigurace První věc, kterou je potřeba udělat aby aplikace mohla fungovat, je nastavené bezpečnostní politiky. Aplikace vyžaduje pro svůj chod aktivovaný security manager a tak je třeba server nastavit, aby jej aktivoval. Ještě předtím mu však musíme přidělit bezpečnostní povolení. Nejjednoduší způsob, jak tak učinit (za předpokladu, že na serveru nebudeme provozovat žádné další aplikace vyžadující běh security manageru) se provede tak, že serveru povolíme vše. Otevřeme tedy soubor conf/catalina.policy a vložíme do něj (někde na začátek, za úvodní komentáře) následující řádky: // Nastavime serveru vsechna opravneni grant { permission java . security . AllPermission ; };
Vzhledem k tomu, že spuštění security manageru nese i další omezení, je třeba ještě v konfiguraci serveru explicitně povolit, aby načítal konfigurační soubory modulů. To se provede přidáním atributu deployXML="true" elementu Host v souboru conf/server.xml. Druhou částí konfigurace aplikace je zprovoznění odesílání e-mailů. K tomu je třeba explicitně překopírovat knihovny pro jejich odesílání do adrešáře knihoven celého serveru. To se provede následujcí dvojící příkazů spuštěných v kořenovém adresáři webového serveru: $ cp ~/. m2 / repository / javax / mail / mail /1.4/ mail -1.4. jar lib / $ cp ~/. m2 / repository / javax / activation / activation /1.1/ activation -1.1. jar lib /
49
Poté už stačí server opět restartovat, tentokrát se zapnutým security managerem: $ bin / shutdown . sh $ bin / startup . sh - security
a aplikace JAssignment by měla být plně funkční.
Zastavení aplikace Potřebujeme-li aplikaci ukončit, je třeba zastavit oba servery. Databázový sever se zastaví snadno - stačí přerušit (nebo ukončit, pokud běží na pozadí) jeho proces. Webový server se pak ukončí příkazem $ bin / shutdown . sh
Otevření aplikace v Eclipse IDE Maven umožňuje z každého projektu vytvořit Eclipse projekt. Provede se to následujícími příkazy v kořenovém adresáři modulu: $ mvn eclipse : clean $ mvn eclipse : eclipse
Pokud chceme otevírat v Eclipse modul jasgweb, je vhodné Mavenu říci, aby nevytvářel běžný Java projekt, ale aby vytvořil Web Tools Platform projekt. Předchozí příkazy se tak nahradí: $ mvn eclipse : clean $ mvn eclipse : eclipse - Dwtpversion =2.0
Poté stačí projekt naimportovat do Eclipse jako existující projekt.
Vygenerování souboru s testy pro aplikaci Pro vytváření testů aplikace JAssignment je dědit z třídy JAsgTestCase (z balíčku cz.upol.jasglib.export). Ta je součástí celé knihovny jasglib. Aby však nebylo nutné přikládat k testům celou knihovnu, lze si nechat vytvořit JAR archiv pouze s třídou JAsgTestCase. Udělá se to pomocí Mavenu příkazem: $ mvn - Pexport - jasg - test - case
Výsledný soubor se nachází v adresáři target.
50
B.
Obsah přiloženého CD
bin/ Soubor jasgweb.war webové aplikace JAssignment určtený k instalaci na server. Knihovny potřebné pro běh aplikace se nacházejí v adresáři lib. Adresář jasgweb obsahuje rozbalený soubor webové aplikace se všemi knihovnami. doc/ Dokumentace práce ve formátu PDF včetně všech příloh (soubor doc.pdf). Všechny soubory nutné pro bezproblémové vygenerování PDF souboru dokumentace (soubor doc.zip). src/ Zdrojové kódy aplikace JAssignment (modulu jasg-main), knihovny jasglib, modulu databáze jasgdb a samotné webové aplikace jasgweb. readme.txt Stručný popis spuštění databázového a webového serveru. Informace pro instalaci aplikace JAssignment na webový server. Popis aplikace spuštěné pro účely obhajoby.
51