SlovnikTerminologie: JemnyUvodDoAplikace

from Wiki KIVu

Jemný úvod od aplikace

Tento dokument má za cíl sloužit jako pomocník pro "rychlé" zorientování se ve aplikaci a její architektuře.

Adresářová struktura

 /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.

Podrobnější popis

Jmenné konvence

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).

Význam některých výrazů:

Nastavení IDE

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.

Eclipse

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

IDE, nastavení kódování projektu

Linux

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.

Windows

V případě použítí MS windows je situace závislá na konkrétním IDE:

SVN

SVN pluginy pro eclipse

v případě prioblémů zkuste navštívit http://wiki.eclipse.org/SVN_Howto

Nastavení SVN pro Netbeans

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é:

Odkazy

Datový model

V současné době má datový model okolo 27 tabulek.

Implementace

  1. Dodržujte prosím ImplementacniKonvence.
  2. Pro snažší zorientování ve třídách a metodách je doporučeno vygenerování javadocu příkazem mvn javadoc:javadoc, který se defaultně vygeneruje do ./target/site/apidocs

Konfigurační soubory

MVC - Model-view-controller

S principiálním popisem začnu od pohledu uživatele.

  1. Tomu přijde do browseru html kód, které umí browser zobrazit.
  2. HTML kód se odešle ze servletového kontejneru po příchozím requestu.
  3. Servletový kontejner vytváří tento zmiňovaný kód ze servletů.
  4. Ty získává dynamicky překladem jsp stránek na servlety.

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>

interceptory

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:

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.

Controllery

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();
}

DAO

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.

Zpracování vyjímek

Lokalizace v JSP

Samostatná stránka věnovaná lokalizaci aplikace SPOT.

Ajax a knihovna DWR (Direct Web Remoting)

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.

Nastavení spot-servlet.xml

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.

Javascript

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 */
	}
}

Serverová část

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;
}

Logování a komentáře

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!

Testování

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.

Tipy a triky

Všeobecně

Retrieved from http://wiki.kiv.zcu.cz/SlovnikTerminologie/JemnyUvodDoAplikace
Content last modified on 20 February 2014, 00:16