SlovnikTerminologie: JemnyUvodDoAplikace |
from Wiki KIVu |
Tento dokument má za cíl sloužit jako pomocník pro "rychlé" zorientování se ve aplikaci a její architektuře.
/doc - tento adresář by měl obsahovat dokumentaci... /logs - sem se mohou(podle nastavení v log4j.properties) zapisovat logy aplikace /scripts - obsahuje jednoduché skripty pro windows(bat) i linux(sh), spuštění aplikace, vytvoření war souboru, vytvoření dokumentace /sql - obsahuje sql scripty pro vytvoření potřebných tabulek a sekvencí. /src - adresář obsahuje všechny zdrojové a konfigurační soubory. /main /java/cz/zcu/kiv/spot - adresář, kde se nachází veškeré zdrojové kódy. /resources/static - adresář s javascripty, potřebnými obrázky a kaskádovými styly. /WEB-INF - adresář jako takový obsahuje konfigurační soubory pro spring + web.xml. /classes - obsahuje soubory s lokalizačními řetězci. /jsp - obsahuje veškeré jsp soubory v daných podadresářích. /properties - obsahuje konfigurační properties soubory /tld - potřebné tag libraries.. /test/java/cz/zcu/kiv/spot - adresář obsahující veškeré testy aplikace. /target - defaultní adresář pro vygenerovaný war soubor a vygenerovanou dokumentaci /site - adresář pro vygenerované stránky projektu /apidocs - pro javadoc /temp - obsahuje vzorové soubory pro import slov ze souboru a je pracovním adresářem při hromadném importu slov.
Tato kapitola je v rámci SPOTu? docela problémová. Člověk se v aplikaci může setkat s tím, že jeden termín má i 3 ekvivalentní výrazy. Tomuto bych zde rád věnoval pár řádků. Zde jsou uvedená některá synonyma, na která jsem si vzpomněl:
Snažte se prosím tyto rady dodržovat a nevnášejte do aplikace ještě větší zmatek, než je v ní nyní ;) Dále by bylo více než vhodné třídy, metody a proměnné pojmenovávat anglicky a češtinu používat pouze v komentářích (bez diakritiky).
Je více méně na vás, jaké IDE si zvolíte. Ať už Netbeans nebo Eclipse (případně IntelliJ Idea nebo jiné) nabízí nepřeberné množství různých pluginů, které nám mají usnadnit práci. Dají se nainstalovat pluginy pro práci s Mavenem, Springem aj. Tato volba je opravdu jen na vás. Vhodné je použití Spring Tool Suite, což je vývojové prostředí založené na Eclipse, které usnadňuje vývoj aplikací s využitím Spring frameworku, Mavenu atd.
Pro windows i pro unix: turorial-spot-win32-ide-eclipse.zip [1,4 MB]
Tutoriál pro spustění aplikace naleznete na stránce NasazeniAplikace
Nejčastější klávesové zkratky, které mohou výrazným způsobem urychlit práci eclipse-zkratky.txt, další zajímavé informace na http://tipy.kiv.zcu.cz
Nastavení kódování celého projektu v IDE je velice důležité. Pod operačním systémem linux je automaticky v IDE Netbeans i Eclipse nastaveno kódování projektu (a všech jeho souborů) na UTF8.
V případě použítí MS windows je situace závislá na konkrétním IDE:
-> Resource -> Text file encoding
a vybrat Other UTF-8
. Nebo provést změnu globálně pro všechny projekty: nabídka Window -> Preferences -> General -> Workspace -> Text file encoding
a vybrat Other UTF-8
.
v případě prioblémů zkuste navštívit http://wiki.eclipse.org/SVN_Howto
Pro unix: tutorial-spot-unix-ide-netbeans.zip [0,7 MB] Netbeans IDE maji plugin pro SVN defaultně zabudovaný.
Pro týmovou spolupráci je vhodné zprovoznit SVN pro prostředí Netbeans. Pokud nemáte operační systém Windows Vista 64-bit, u kterého se zprovoznění nepovede, je to velmi jednoduché:
V současné době má datový model okolo 27 tabulek.
mvn javadoc:javadoc
, který se defaultně vygeneruje do ./target/site/apidocs
PropertyPlaceholderConfigurator
.
PropertyPlaceholderConfigurator
, beana pro messages aplikace a beana business logiky DictionaryManager
.
S principiálním popisem začnu od pohledu uživatele.
A teď, po trochu nudném úvodu, se dostáváme ke kořenu věci, tedy k tomu, jakým způsobem nám Spring zajistí propojení celé aplikace.
Servletovému kontejneru přijde požadavek, který obsahuje informace ve stylu: "Pošli mi potřebná data pro toto URL: http://ip.adresa.servletové.kontejneru:port/spot/sprava/uzivatele". Spring ví, že s daným URL je provázán konkrétní controller (mapování URL na controllery je ve spot-servlet.xml
), který se stará o to, aby byla uživateli poslaná potřebná data. Controller je vždy nadefinován ve své beaně.
<bean id="adminUsersController" class="cz.zcu.kiv.spot.controllers.AdminUsersController"> <property name="dictionaryManager" ref="dictionaryManager" /> <property name="pagingHelper" ref="pagingHelper" /> </bean>
Id značí unikátní jméno, pod kterým je daná beana známa ostatním beanám, class je cesta ke controlleru a property říká, že je závislá na těch daných beanách.
Toto provázání je zaznamenáno v beanách v souboru spot-servlet.xml. Konkrétně:
<bean id="urlAbcMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" abstract="false" singleton="true" lazy-init="default" autowire="default" dependency-check="default"> <property name="interceptors"> <list> <ref bean="settingInterceptor" /> <ref bean="authInterceptor" /> </list> </property> <property name="urlMap"> <map> . . <entry key="/sprava/uzivatele" value-ref="adminUsersController" /> . . </map> </property> <property name="alwaysUseFullPath" value="true" /> </bean>
Jenže než přijde ke slovu konkrétní controller, tak v této beaně jsou přiřazeny 2 interceptory v části properties. Takovýto interceptor má na starosti provést implementovaný kód před tím, než předá řízení controlleru AdminUserController
. (Pozn.: tedy v případě,že interceptory jsou implementované ve spotu.
Intercetpory dědí od HandlerInterceptorAdapter
, který má 3 metody:
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
.
Ve Spotu je implementována pouze metoda preHandle()
.) Beana interceptoru vypadá podobně jako beana controlleru. Například AuthInterceptor
se stará o jednoduché řízení přístupu k některým URL na základě práv, která mají jednotlivý uživatelé. Pokud průchod interceptorem skončí návratovou hodnotou true, předá se řízení controlleru AdminUserController
, jinak se (v naší implementaci) provede přesměrování na jiné URL a vyhození vyjímky.
Nyní tedy konečně přichází ke slovu AdminUserController
. Tento implementuje rozhraní Controller, který má k naimplementování pouze 1 metodu, tou je handleRequest(HttpServletRequest request, HttpServletResponse response)
, která navrací ModelAndView(mv)
. ModelAndView
slouží k předávání objektů získaných například z databáze na základě požadavku uživatele. Tyto objekty se pak předají do patřičných jsp stránek a zobrazí uživateli. Patřičné jsp strámky jsou vždy uvedeny uvnitř controlleru. Příklad: mv = new ModelAndView("/WEB-INF/jsp/admin/admin-users.jsp")
;
A nebo v případě nějaké chyby se do mv vloží Object reprezentující chybový stav. Tohoto je pak využito v kódu jsp stránek, kdy v případě, že došlo k nějaké chybě se vypíše chybová hláška například "error.unknow" na místo toho, aby se vypsala nekompletní data. Řetězec error.unknow je k uložen v souborech messages_xx.properties v různých jazykových mutacích.
try { /* Pokud bylo zadane nove heslo */ if (!pass.equals("")) { user.setNpass(user.getPass()); } dictionaryManager.saveUser(user); mv.addObject("completeMsg", "editUser.complete"); } catch (Exception e) { logger.error("V aplikaci doslo k nasledujici chybe: " + e); mv.addObject("errorViewMsg", "error.unknow"); return; }
Tak a nyní se dostáváme k tomu, jak se data z databáze dostanou ke controlleru.
List<User> users = dictionaryManager.getAllUsers();
Tento kousek kódu říká, že do daného Listu, se mají uložit všichni uživatelé. DictionaryManager
je do AdminUserControlleru
injektován Springem, proto je v controlleru tento setter:
public void setDictionaryManager(DictionaryManager dictionaryManager) { this.dictionaryManager = dictionaryManager; }
Zpět k volání dictionaryManager.getAllUsers()
. Podíváme-li se na implementaci rozhraní DictionaryManager
, nalezneme tam tento kód:
public List<User> getAllUsers() { return userDao.getAllUsers(); }
Z něho vidíme, že je třeba jít do datové vrstvy aplikace k DAO rozhraním a jejich implementaci. Implementace UserDaoImpl
je ta třída, která už komunikuje s databází.(Pozn.: S db komunikuje přímo jdbc, ale naše DAO jsou poslední z pohledu aplikace.) Pojďme se podívat jak přesně funguje.
public List<User> getAllUsers () { String sql = "SELECT * FROM users ORDER BY login"; return getJdbcTemplate().query(sql, new UserRowMapper ()); }
Metoda getAllUsers()
je přehledná. Nejdříve je popsán string, který představuje SQL dotaz nad databází. Následuje return, který navrací volání dotazu sql za použití UserRowMapper()
. UserRowMapper
je vnitřní privátní třída třídy UserDaoImpl
a zajišťuje namapování jednotlivých záznamů z tabulky "users" na doménové objekty User.
private class UserRowMapper implements RowMapper { public Object mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User(); user.setId(rs.getInt("users_id")); user.setLogin(rs.getString("login")); user.setPass(rs.getString("pass")); user.setFirstname(rs.getString("firstname")); user.setLastname(rs.getString("lastname")); user.setEmail(rs.getString("email")); user.setEmailpub(rs.getString("email_pub")); user.setHomepage(rs.getString("homepage")); user.setDescription(rs.getString("description")); user.setRights(rs.getInt("rights")); user.setActive(rs.getInt("active")); user.setActiveCode("active_code"); return user; } }
Tak toto byla asi nejtěžší část celého seznámení se SPOTem. Doufám tedy, že jsem vám alespoň objasnil základní princip fungování aplikace. Na principu tohoto příkladu příkladu více méně funguje celá aplikace.
Samostatná stránka věnovaná lokalizaci aplikace SPOT.
Komunikaci mezi klientskou (javascript) a serverovou částí (java) zajišťuje knihovna DWR. Zprovoznění vyžaduje několik kroků, které nejsou úplně intuitivní, proto je zde popíšu.
Zde je nutné určit, který controller bude obsluhovat volání javascriptových funkcí. V tomto případě musí třída WordShowController? obsahovat uvedené metody (ajaxWordEdit, ajaxSeeAlsoAdd,...).
<bean id="wordShowController" class="cz.zcu.kiv.spot.controllers.WordShowController"> <property name="dictionaryManager" ref="dictionaryManager" /> <dwr:remote javascript="WordShowController"> <dwr:include method="ajaxWordEdit" /> <dwr:include method="ajaxSeeAlsoAdd" /> <dwr:include method="ajaxCommentAdd" /> <dwr:include method="ajaxCommentDelete" /> <dwr:include method="ajaxTransDelete" /> <dwr:include method="ajaxTransAdd" /> <dwr:include method="ajaxTransEdit" /> </dwr:remote> </bean>
Poznámka: Pro testování je možné nastavit u tagu dwr:controller
debug na true. V takovém případě je generována stránka pro testování příslušných metod na adrese /dwr/index.html
. Je ale důležité, aby nasazená aplikace měla tento debug mód nastavena na false.
Javascriptové funkce jsou umístěny v adresáři webapp/static/js/ajax/
. Je nutné napsat jednu funkci pro volání metody controlleru na serveru a druhou funkci (callback), která bude přijímat data vrácená controllerem.
Ukázka javascriptové funkce, která volá metodu ajaxWordEdit
controlleru WordShowController? a předává jí hodnoty formulářových polí s ID wordWord
, wordId
, usageId
a stateId
. Poslední parametr je název callback funkce.
function ajaxWordEdit() { WordShowController.ajaxWordEdit(value("wordWord"), value("wordId"), value("usageId"), value("stateId"), ajaxWordEditCallback); }
Callback funkce je zavolána po přijetí dat od metody controlleru a nebo po vytikání timeoutu. Přijatá data jsou uložená v parametru funkce (zde: wordData). Chybové stavy jsou řešeny vrácením null
z controlleru.
function ajaxWordEditCallback(wordData) { if (wordData == null) { alert("Ke změně slova nedošlo. Nastala chyba."); return false; } else { /* zde bude nejaky kod, ktery provede zmenu na strance */ } }
V controlleru musí existovat metoda, kterou v javascriptu voláme. V níže uvedeném kódu je i ukázka, jak načíst kontext.
public String[] ajaxWordEdit(String wordWord, int wordId, int usageId, int stateId) { /* nacteni web kontextu */ WebContext ctx = WebContextFactory.get(); HttpServletRequest request = ctx.getHttpServletRequest(); ... if ( /* nastala chyba */ ) { return null; } ... String[] infoToDisplay = {word.getWord(), word.getUrl(), state.getValue(), usage.getName()}; return infoToDisplay; }
Když se dostanete k práci s neokomentovanou třídou, dookomentujte jí. Času je na to třeba minimum a když se nám pak předhodí log ze serveru s tím, že aplikace padá tehdy a tehdy, tak se alespoň bude dát z logu poznat, co se před spadnutím aplikace dělo a také proč spadla. Z toho plyne, ze nebude problém chybu opravit. Čtení logu má být jako čtení knihy s příběhem, v každé části by mělo být jasné co se děje.
Zkuste psát minimálně dostačující dokumentační komentáře, které budou vašim následovníkům k něčemu a ne k ničemu. Postupem doby se došlo k doporučení používat ke komentářům sice češtinu, ale bez diakritiky. Vývoj SPOTu probíhá na operačním systému windows a linux, každý systém používá defaultně jiné kódování a i přes opratrnost v commitech se vyskytují neustále problémy s diakritikou ve zdrojových kódech. Čeština tedy bez diakritiky!
Bylo by více než vhodné, aby jste při práci s třídami, na kterých budete pracovat, k nim rovnou psali alespoň základní testy. Netvrdím, že můj nástřel testů je dostačující, na druhou stranu ho lze použít jako vodítko.
./src/main/webapp/WEB-INF/jsp/include/header.jsp
:
<link href="http://www.google.com/uds/css/gsearch.css".../>
<script src="http://www.google.com/uds/api?file=uds.js&v=1.0"...</script>
<script src="http://www.google.com/uds/api?file=uds.js&v=1.0&key=AB...</script>
property souboru
je nutný restart servletového kontejneru, protože tyto hodnoty se načítají pouze při jeho startu.
StaticPageControlleru
. Nastaveni kontrolerů pro poskytování statických stránek a namapováni jednotlivých kontrolerů na URL se nachází v souboru spot-servlet.xml