<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>
<channel>
	<title>AspectWorks</title>
	<atom:link href="http://www.aspectworks.com/feed" rel="self" type="application/rss+xml" />
	<link>http://www.aspectworks.com</link>
	<description>Vyvíjíme chytré webové aplikace pro střední a velké podniky</description>
	<lastBuildDate>Thu, 17 May 2012 10:01:09 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
		<item>
		<title>Odesíláte formulář? Nezapomeňte na redirect!</title>
		<link>http://www.aspectworks.com/2012/05/odesilate-formular-nezapomente-na-redirect</link>
		<comments>http://www.aspectworks.com/2012/05/odesilate-formular-nezapomente-na-redirect#comments</comments>
		<pubDate>Thu, 17 May 2012 08:29:42 +0000</pubDate>
		<dc:creator>Richard Šerý</dc:creator>
				<category><![CDATA[Blog]]></category>
		<guid isPermaLink="false">http://www.aspectworks.com/?p=3324</guid>
		<description><![CDATA[Pokaždé, když odešlete formulář metodou POST, musíte na serveru udělat redirect. Možná je to pro vás samozřejmost, ale stále se setkávám se spoustou lidí, kteří to buď neznají nebo v<a href="http://www.aspectworks.com/2012/05/odesilate-formular-nezapomente-na-redirect" class="moreLink">více&#160;»</a>]]></description>
			<content:encoded><![CDATA[<p>Pokaždé, když odešlete formulář metodou POST, musíte na serveru udělat redirect. Možná je to pro vás samozřejmost, ale stále se setkávám se spoustou lidí, kteří to buď neznají nebo v tom dělají chyby. <span id="more-3324"></span></p>
<h3>Proč?</h3>
<p><a href="http://www.aspectworks.com/2012/05/odesilate-formular-nezapomente-na-redirect/form-resubmit" rel="attachment wp-att-3331"><img src="http://www.aspectworks.com/wp-content/uploads/2012/02/form-resubmit.gif" alt="" title="form-resubmit" width="380" height="163" class="imageRight size-full wp-image-3331" /></a></p>
<p>Poté co prohlížeč přijme data z formuláře, vygeneruje novou stránku. Pokud by na ní uživatel stisknul v prohlížeči tlačítko &#8222;refresh&#8220;, pokusil by se prohlížeč znovu odeslat jednou už odeslaná data. Protože to je nestandardní operace, browser se uživatele zeptá, jestli si to opravdu přeje, ale řekněme si upřímně &#8211; kolik uživatelů si to vůbec přečte? A kolik z nich chápe, o co se jedná?</p>
<p>Jestliže místo vygenerování stránky uděláte redirect na REST URL (tzv. &#8222;POST / Redirect / GET pattern&#8220;), nemusíte se o tlačítka jako je &#8222;back&#8220; nebo &#8222;refresh&#8220; více starat &#8211; alespoň v souvislosti s odesíláním formulářů.</p>
<h3>Jak?</h3>
<p>Udělat serverový redirect je triviální, ale často se chybuje v HTTP kódu. Jediný správný HTTP kód pro tento redirect je 303 &#8222;See Other&#8220;. Bohužel, jak praví <a href="http://http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">dokumentace</a>, <cite>&#8222;Many pre-HTTP/1.1 user agents do not understand the 303 status. When interoperability with such clients is a concern, the 302 status code may be used instead, since most user agents react to a 302 response as described here for 303.&#8220;</cite></p>
<p>Pokud tedy počítáte i se staršími browsery (např. Netscape 4), použijte kód 302 a doufejte, že ho někdo v budoucnu nenaimplementuje správně &#8211; pak by totiž kód 302 nepřešel na GET, ale zopakoval by POST, což by byla docela katastrofa.</p>
<p>Všechny současné browsery by však 303 měly umět, takže pokud zrovna neděláte stránky pro muzeum prohlížečů, můžete ho s klidem a bez rizika používat.</p>
<h3>Platí to vždy?</h3>
<p>Každé pravidlo má svoje výjimky. Určitě se může vyskytnout situace, v jaké by mohlo být opakované odeslání<br />
POSTu výhodné, ale v praxi jsem se s takovou situací zatím nesetkal. Jestli jste se s takovým případem setkali, napište o něm v komentářích.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.aspectworks.com/2012/05/odesilate-formular-nezapomente-na-redirect/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>AspectWorks sponzorem světového poháru MTBO</title>
		<link>http://www.aspectworks.com/2012/05/aspectworks-sponzorem-svetoveho-poharu-mtbo</link>
		<comments>http://www.aspectworks.com/2012/05/aspectworks-sponzorem-svetoveho-poharu-mtbo#comments</comments>
		<pubDate>Sun, 06 May 2012 19:39:06 +0000</pubDate>
		<dc:creator>Pavel Müller</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Jen tak]]></category>
		<guid isPermaLink="false">http://www.aspectworks.com/?p=3647</guid>
		<description><![CDATA[AspectWorks sponzoruje první závod světového poháru MTBO konaného o tomto víkendu v Řitce. MTBO (Mountain Bike Orienteering) jsou orientační závody na kole podobně jako orientační běh. Více o tomto sportu<a href="http://www.aspectworks.com/2012/05/aspectworks-sponzorem-svetoveho-poharu-mtbo" class="moreLink">více&#160;»</a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.aspectworks.com/2012/05/aspectworks-sponzorem-svetoveho-poharu-mtbo/564952_3254666486550_1263709674_32393785_1293878713_n" rel="attachment wp-att-3648" class="imageRight"><img class=" wp-image-3648" title="AspectWorks na stupních" src="http://www.aspectworks.com/wp-content/uploads/2012/05/564952_3254666486550_1263709674_32393785_1293878713_n-300x225.jpg" alt="" width="250" /></a><br />
AspectWorks sponzoruje první závod <a href="http://www.mtboprague.cz/">světového poháru MTBO</a> konaného o tomto víkendu v Řitce. MTBO (Mountain Bike Orienteering) jsou orientační závody na kole podobně jako orientační běh. Více o tomto sportu a dalších obdobných závodech najdete na stránkách <a href="http://mtbo.cz/">mtbo.cz</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.aspectworks.com/2012/05/aspectworks-sponzorem-svetoveho-poharu-mtbo/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rok 2011: Rekordní obchodní výsledky</title>
		<link>http://www.aspectworks.com/2012/04/rok-2011-rekordni-obchodni-vysledky</link>
		<comments>http://www.aspectworks.com/2012/04/rok-2011-rekordni-obchodni-vysledky#comments</comments>
		<pubDate>Fri, 27 Apr 2012 14:08:04 +0000</pubDate>
		<dc:creator>Richard Šerý</dc:creator>
				<category><![CDATA[Homepage]]></category>
		<category><![CDATA[Novinky]]></category>
		<guid isPermaLink="false">http://www.aspectworks.com/?p=3630</guid>
		<description><![CDATA[AspectWorks pokračuje ve svém úspěšném tažení na IT trhu v ČR a opět dosáhl rekordního obchodního výsledku. Obrat společnosti za rok 2011 přesáhl 60 milionů korun. To představuje meziroční nárůst<a href="http://www.aspectworks.com/2012/04/rok-2011-rekordni-obchodni-vysledky" class="moreLink">více&#160;»</a>]]></description>
			<content:encoded><![CDATA[<p>AspectWorks pokračuje ve svém úspěšném tažení na IT trhu v ČR a opět dosáhl rekordního obchodního výsledku.<br />
<span id="more-3630"></span><br />
Obrat společnosti za rok 2011 přesáhl 60 milionů korun. To představuje meziroční nárůst o 53%.</p>
<p>Již od svého založení roste naše společnost každoročně o více než polovinu &#8211; jedinou výjimkou byl rok 2009, postižený světovou ekonomickou krizí. Za mimořádnými výsledky stojí především snaha o dlouhodobé, oboustranně přínosné vztahy se zákazníky a týmy kvalitních, velmi oceňovaných specialistů společnosti.</p>
<p><img src="http://www.aspectworks.com/wp-content/uploads/2012/04/chart2.png" alt="Obrat AspectWorks v letech 2005-2011 v mil. Kč bez DPH" title="Obrat AspectWorks v letech 2005-2011 v mil. Kč bez DPH" /></p>
]]></content:encoded>
			<wfw:commentRss>http://www.aspectworks.com/2012/04/rok-2011-rekordni-obchodni-vysledky/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Unit testy a jazyk PL/SQL</title>
		<link>http://www.aspectworks.com/2012/04/unit-testy-a-jazyk-plsql</link>
		<comments>http://www.aspectworks.com/2012/04/unit-testy-a-jazyk-plsql#comments</comments>
		<pubDate>Tue, 24 Apr 2012 09:02:05 +0000</pubDate>
		<dc:creator>Tomáš Holý</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Testování]]></category>
		<category><![CDATA[junit]]></category>
		<guid isPermaLink="false">http://www.aspectworks.com/?p=3600</guid>
		<description><![CDATA[Možná jste se již také na vašem projektu setkali s nutností vytvořit unit testy pro části kódu, které při načítání dat z databáze využívají uložené procedury nebo funkce. Toto na<a href="http://www.aspectworks.com/2012/04/unit-testy-a-jazyk-plsql" class="moreLink">více&#160;»</a>]]></description>
			<content:encoded><![CDATA[<p>Možná jste se již také na vašem projektu setkali s nutností vytvořit unit testy pro části kódu, které při načítání dat z databáze využívají uložené procedury nebo funkce. Toto na první pohled jednoduché zadání s sebou může ovšem nést jeden problém, a tím je použití java embedded databáze pro testování. Obecně mají embedded databáze slabou nebo dokonce žádnou podporu pro psaní uložených procedur a funkcí v jazyce PL/SQL, a proto je testování částí kódů, které je využívají, problematické.<br />
<span id="more-3600"></span></p>
<p>Na našem projektu <a title="Starthead" href="http://www.starthead.com">Starthead</a> používáme pro unit testy embedded databázi HSQLDB (Spring nám dává jednoduchou podporu pro využívání embedded databází HSQLDB, H2 nebo DERBY). Ovšem podpora jazyka PL/SQL je v této i dalších embedded databázi téměř nulová. Přesto jsme ale potřebovali nějak otestovat jednu část kódu používající uloženou funkci.<br />
Pokud se podíváme do dokumentace <a href="http://www.hsqldb.org/doc/2.0/guide">HSQLDB</a> najdeme sice podporu procedur a funkcí v rámci SQL/PSM (Persistent Stored Modules), což nám v některých případech může stačit. Ve spoustě případů ovšem ne. A právě zde přichází na řadu podpora SQL/JRT, tedy SQL Routines and Types for the Java Programming Language. JRT nám dovoluje místo vytváření procedur a funkcí registrovat statické metody psané v jazyce Java, které poté budou volány na pozadí místo daných procedur, případně funkcí.</p>
<p>Dost bylo nudné teorie, pojďme se podívat na jednoduchý příklad, který celý princip osvětlí.</p>
<p>V aplikaci máme jednoduchou uloženou funkci, která například počítá průměr ze dvou čísel a vrací výsledek (berme to opravdu jen jako triviální případ pro vysvětlení principu). Naše funkce je psaná pro databázi MySQL.</p>
<pre class="brush: java; title: ; notranslate">
CREATE FUNCTION average (a INT, b INT)
  RETURNS  DOUBLE
   DETERMINISTIC
    BEGIN
     DECLARE sum DOUBLE;
     SET sum  = (a + b) / 2;
     RETURN sum;
    END
</pre>
<p>Nyní musíme tuto metodu převést do jazyka Java, aby poté mohla nahradit stejnou funkcionalitu v embedded databázi.</p>
<pre class="brush: java; title: ; notranslate">
package com.aspectworks;
public class EmbeddedFunction{
  public static double average(int a, int b){
    return (a + b) / 2;
  }
}
</pre>
<p>Posledním krokem je registrace metody napsané v Javě pro volání funkce average v embbeded databázi. (Toto řešení je možné použít v HSQLDB 2.0 a vyžší. Nižší verze měli podporu aliasů, které fungovali na podobném principu.)</p>
<pre class="brush: java; title: ; notranslate">
CREATE FUNCTION average (a INT, b INT)
  RETURNS CHAR  DOUBLE
  LANGUAGE JAVA DETERMINISTIC NO SQL
  EXTERNAL NAME 'CLASSPATH:com.aspectworks.EmbeddedFunction.average'
</pre>
<p>Pokud nyní v unit testech zavoláme sql dotaz, který používá tuto average funkci, na pozadí se provede vykonání metody average ve třídě EmbeddedFunction s danými parametry a jako výsledek se použije návratová hodnota metody. Jako návratová hodnota může být použita i tabulka, Java metoda poté musí mít návratový typ java.sql.ResultSet. Vnitřní chování převolávání takovéto metody se poté liší. Podrobně a srozumitelně je to popsáno v dokumentaci <a href="http://www.hsqldb.org/doc/2.0/guide/sqlroutines-chapt.html#src_jrt_routines">Java Language Routines (SQL/JRT)</a>.</p>
<p>Nevýhodou tohoto řešení je nutnost udržovat společně s uloženou funkcí i metodu v dané třídě. Nechám už na posouzení každého, jestli je jednodušší použít toto řešení nebo pro testování použít některou z databází podporující jazyk PL/SQL.<br />
Uvítám vaše názory na tento způsob řešení, případně napište, jakým způsobem testujete uložené procedury a funkce vy.</p>
<p>Pár odkazů:<br />
<a href="http://hsqldb.org">http://hsqldb.org</a> &#8211; HSQLDB databáze<br />
<a href="http://en.wikipedia.org/wiki/SQL/JRT">http://en.wikipedia.org/wiki/SQL/JRT</a> &#8211; popis JRT</p>
]]></content:encoded>
			<wfw:commentRss>http://www.aspectworks.com/2012/04/unit-testy-a-jazyk-plsql/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>DB žurnál pomocí Hibernate interceptoru</title>
		<link>http://www.aspectworks.com/2012/04/db-zurnal-pomoci-hibernate-interceptoru</link>
		<comments>http://www.aspectworks.com/2012/04/db-zurnal-pomoci-hibernate-interceptoru#comments</comments>
		<pubDate>Tue, 10 Apr 2012 07:33:34 +0000</pubDate>
		<dc:creator>Jan Pacek</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Java]]></category>
		<guid isPermaLink="false">http://www.aspectworks.com/?p=3543</guid>
		<description><![CDATA[Na mnoha projektech je třeba řešit databázový žurnál aplikace, který zaznamenává události v systému včetně dat, která při těchto událostech byla změněna či jen čtena. Zákazníci často požadují u událostí,<a href="http://www.aspectworks.com/2012/04/db-zurnal-pomoci-hibernate-interceptoru" class="moreLink">více&#160;»</a>]]></description>
			<content:encoded><![CDATA[<p>Na mnoha projektech je třeba řešit databázový žurnál aplikace, který zaznamenává události v systému včetně dat, která při těchto událostech byla změněna či jen čtena. Zákazníci často požadují u událostí, které mění data, žurnálovat jak staré tak i nové hodnoty záznamu.</p>
<p>Implementace takového mechanismu přímo na DAO vrstvě by byla pracná a hlavně složitě konfigurovatelná. Řešení žurnálování na úrovni databázových procedur a triggerů by zase zrušilo nezávislost aplikace na konkrétním typu databáze. Hibernate pro žurnálování nabízí zajímavou funkčnost <strong>interceptor</strong> pro zachycení a zpracování různých událostí, jako například databázové CRUD operace.<br />
<span id="more-3543"></span></p>
<p>Vycházím z <a title="http://www.mkyong.com/hibernate/hibernate-interceptor-example-audit-log/" href="http://www.mkyong.com/hibernate/hibernate-interceptor-example-audit-log/">příkladu použití interceptoru na audit log (žurnál)</a> a samozřejmě ze samotné dokumentace <a title="http://docs.jboss.org/hibernate/core/3.3/reference/en/html/events.html" href="http://docs.jboss.org/hibernate/core/3.3/reference/en/html/events.html">http://docs.jboss.org/hibernate/core/3.3/reference/en/html/events.html</a></p>
<p>Po menší modifikaci lze uvedený příklad použít i na žurnálování starých a nových hodnot. Vytvořímě si entitu <strong>AuditLogData</strong> pro ukládání změněných hodnot a přidáme ji do entity <strong>AuditLog</strong>.</p>
<pre class="brush: java; title: ; notranslate">
@Entity @Table(name = &quot;auditlog&quot;, catalog = &quot;mkyong&quot;)
public class AuditLog implements java.io.Serializable {
private Long auditLogId;
private String action;
private String detail;
private Date createdDate;
private Long entityId;
private String entityName;
@OneToMany() @JoinColumn (name = &quot;auditLogId&quot;) @OrderBy(&quot;propertyName&quot;)
privateList auditLogData;
...
}
</pre>
<p>&nbsp;</p>
<pre class="brush: java; title: ; notranslate">
@Entity @Table(name = &quot;auditlogdata&quot;, catalog = &quot;mkyong&quot;)
public class AuditLogData implements java.io.Serializable {
private Long auditLogDataId;
private String propertyName;
private String oldValue;
private String newValue;
...
}
</pre>
<p>Při ukládání záznamu budeme pracovat přímo se záznamem AuditLog třídy a ne pouze s entity.</p>
<pre class="brush: java; title: ; notranslate">
public boolean onFlushDirty(Object entity,Serializable id,
                Object[] currentState,Object[] previousState,
                String[] propertyNames,Type[] types)
                throws CallbackException {
                ...
   if (entity instanceof IAuditLog){
       AuditLog auditRecord = new AuditLog(&quot;Updated“,entity.getLogDeatil(), new Date(),entity.getId(), entity.getClass().toString());
       fillChangedData(auditRecord, previousState,  currentState, propertyNames, entity);
	 //Add the auditLog to be saved as update after commit of transaction
       updates.add(auditRecord);
   }
   return false;
}
</pre>
<p>Kde plnění změněných dat by mohlo vypadat přibližně následovně:</p>
<pre class="brush: java; title: ; notranslate">
private void fillChangedData(AuditLog auditLog, Object[] startState, Object[] endState, String[] propertyNames, Object newObject, Long persistedObjectId)
           throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        //check input parameters
        …
        for (int i = 0; i &lt; propertyNames.length; i++) {
            //check if the property has been changed
            if (propertyValueChanged(startState[i], endState[i])) {
                // property changed, add it to auditLog.auditLogData list
                final String pn = propertyNames[i];
                String valueOld = propertyValueToString(startState[i]);
                String valueNew = propertyValueToString(endState[i]);
                addDataToAuditLog(auditLog, pn, valueOld, valueNew);
            }
        }
    }
</pre>
<p>Jak je na příkladu vidět, Hibernate interceptor je mohutný nástroj, který umožňuje na aplikační úrovni nejen odchytávat operace nad databází, ale i dále zpracovávat jejich data.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.aspectworks.com/2012/04/db-zurnal-pomoci-hibernate-interceptoru/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Quartz Scheduler a dynamické plánování úloh</title>
		<link>http://www.aspectworks.com/2012/03/quartz-scheduler-a-dynamicke-planovani-uloh</link>
		<comments>http://www.aspectworks.com/2012/03/quartz-scheduler-a-dynamicke-planovani-uloh#comments</comments>
		<pubDate>Tue, 27 Mar 2012 09:24:13 +0000</pubDate>
		<dc:creator>Jan Bumbala</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[quartz]]></category>
		<category><![CDATA[scheduler]]></category>
		<category><![CDATA[spring]]></category>
		<guid isPermaLink="false">http://www.aspectworks.com/?p=3416</guid>
		<description><![CDATA[Na zákaznickém projektu bylo potřeba umožnit asynchronní spouštění různých handlerů, registrovaných jako Spring bean. Typicky jde o případ, kdy uživatel iniciuje vykonání nějaké činnosti, která může trvat desítky sekund nebo<a href="http://www.aspectworks.com/2012/03/quartz-scheduler-a-dynamicke-planovani-uloh" class="moreLink">více&#160;»</a>]]></description>
			<content:encoded><![CDATA[<p>Na zákaznickém projektu bylo potřeba umožnit asynchronní spouštění různých handlerů, registrovaných jako Spring bean. Typicky jde o případ, kdy uživatel iniciuje vykonání nějaké činnosti, která může trvat desítky sekund nebo dokonce minut. Zároveň jsme potřebovali, aby řešení bylo persistentní, a tedy aby se naplánované úlohy neztrácely při pádu serveru.<br />
Protože jsme již na projektu využívali Quartz Scheduler pro spouštění pravidelných úloh, použili jsme ho i pro účel tohoto asynchronního zpracování.<br />
<span id="more-3416"></span><br />
Scénář použití Quartz Scheduleru, se kterým jsem se nejčastěji setkal, je takový, že v systému jsou předem nakonfigurované joby, které chceme spouštět, a více či méně složité triggery. Každý job je jednoúčelový a spouští předem definovanou business logiku. Pokud chceme umožnit vyvolávat nějaký nový handler, je potřeba přidat a nakonfigurovat nový Quartz job.</p>
<p>Zde popisované řešení je trochu jiné v tom, že je definované obecné API pro naplánování asynchronního zpracování. Motivací bylo umožnit asynchronní procesing handleru bez nutnosti statické konfigurace Quartz jobu pro každý takový handler. Pro každou naplánovanou úlohu je použit společný Quartz job, který v okamžiku vyvolání vyhledá požadovaný handler ve Spring contextu a spustí jej. Přidání nového handleru, nového typu úlohy kterou chceme asynchronně spouštet, pak nevyžaduje žádné další konfigurační změny Quartz scheduleru, pouze konfiguraci nového beanu ve Spring kontextu.</p>
<p>Pro vyvolání metody na Spring beanu již existuje <code><a title="MethodInvokingJobDetailFactoryBean" href="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/scheduling/quartz/MethodInvokingJobDetailFactoryBean.html">MethodInvokingJobDetailFactoryBean</a></code>, jenže tento způsob neumožňuje předat metodě parametry. Navíc i zde je potřeba mít pro každý bean nakonfigurovaný vlastní trigger. V našem scénáři nechceme pro každý springový bean definovat nový trigger, pouze definujeme a nakonfigurujeme univerzální scheduler vrstvu. Popsané řešení je sice závislé na Springu, nicméně klientský kód je oddělený interfacem, takže není až takový problém vytvořit implementaci, která Spring používat nebude.</p>
<h3>Implementace</h3>
<p><a href="http://www.aspectworks.com/wp-content/uploads/2012/03/Scheduler-schema.gif" rel="lightbox[3416]"><img src="http://www.aspectworks.com/wp-content/uploads/2012/03/Scheduler-schema.gif" alt="" title="Scheduler-schema" width="696" height="244" class="size-full wp-image-3549" /></a></p>
<p>Celý mechanismus je skrytý za API, které tvoří 2 třídy:<br />
-<em><code>SchedulerDAO</code></em> &#8211; určeno pro přístup z business logiky, pomocí tohoto interfacu se naplánuje asynchronní zpracování<em></em><br />
-<em><code>Task</code></em> &#8211; rozhraní, které musí naimplementovat handler, který chceme asynchronně spouštět</p>
<pre class="brush: java; title: ; notranslate">
public interface SchedulerDAO {
    /**
     * Schedule task for asynchronous processing.
     * @param taskClass task that is about to be executed asynchronously
     * @param properties data to be passed to the task
     */
    &lt;T extends Task&gt; void schedule(Class&lt;T&gt; taskClass, Properties properties);
}
</pre>
<pre class="brush: java; title: ; notranslate">
public interface Task {
    /**
	 * run scheduled action
	 * @param properties data the task was scheduled with
	 */
	void run(Properties properties);
}
</pre>
<p>Jednoduchá implementace SchedulerDAO může vypadat takto:</p>
<pre class="brush: java; title: ; notranslate">
public class SimpleSchedulerDAO implements SchedulerDAO {
	//...instance variables omitted..
    public SimpleSchedulerDAO(Scheduler scheduler, String jobName, String jobGroup, String classNameProperty) {
        this.scheduler = scheduler;
        this.jobName = jobName;
        this.jobGroup = jobGroup;
        this.classNameProperty = classNameProperty;
    }
    public &lt;T extends Task&gt; void schedule(Class&lt;T&gt; taskClass, Properties properties) {
        JobDataMap jobDataMap = new JobDataMap(properties);
        //todo validation that classNameProperty not present in passed properties
        jobDataMap.put(classNameProperty, taskClass.getName());
        try {
            scheduler.triggerJob(jobName, jobGroup, jobDataMap);
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        }
    }
}
</pre>
<p>Navržená implementace naplánuje jeden univerzální job, společný pro všechny <code>Task</code> implementace. Pokaždé je vytvořen nový trigger, který je okamžitě naplánován ke spuštění. Složitější implementace může např. získávat instanci triggeru pomocí trigger factory, kde bude vyřešena různá konfigurace triggeru pro různé typy handlerů.<br />
Důležité je, že do persistentních dat daného triggeru (jobDataMap) se uloží název třídy/interface, který chceme asynchronně spustit. Tj. název třídy, která byla jako parametr předána metodě <code>schedule(..)</code>. V okamžiku, kdy Quartz vyvolá takto vytvořený trigger, se podle uloženého názvu třídy vyhledá Springový bean, který chceme zavolat (viz dále). Třída jobu je konfigurovatelná, stejně jako název skupiny a klíč, pod kterým je uložený persistovaný název třídy.</p>
<p>Ukázková konfigurace Spring contextu:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;bean id=&quot;simpleSchedulerDAO&quot; class=&quot;com.aspectworks.example.scheduler.impl.SimpleSchedulerDAO&quot;&gt;
   &lt;constructor-arg index=&quot;0&quot; ref=&quot;scheduler&quot;/&gt;
   &lt;constructor-arg index=&quot;1&quot; value=&quot;taskExecutorJob&quot;/&gt;
   &lt;constructor-arg index=&quot;2&quot; value=&quot;taskGroup&quot;/&gt;
   &lt;constructor-arg index=&quot;3&quot; value=&quot;@className&quot;/&gt;
&lt;/bean&gt;
&lt;bean name=&quot;taskExecutorJob&quot; class=&quot;org.springframework.scheduling.quartz.JobDetailBean&quot;&gt;
   &lt;property name=&quot;jobClass&quot; value=&quot;com.aspectworks.example.scheduler.impl.TaskExecutorJob&quot;/&gt;
   &lt;property name=&quot;group&quot; value=&quot;taskGroup&quot;/&gt;
&lt;/bean&gt;
&lt;bean id=&quot;scheduler&quot; class=&quot;org.springframework.scheduling.quartz.SchedulerFactoryBean&quot; destroy-method=&quot;destroy&quot;&gt;
   &lt;property name=&quot;jobFactory&quot;&gt;
      &lt;bean class=&quot;org.springframework.scheduling.quartz.SpringBeanJobFactory&quot;/&gt;
   &lt;/property&gt;
   &lt;property name=&quot;applicationContextSchedulerContextKey&quot; value=&quot;applicationContext&quot;/&gt;
   &lt;property name=&quot;schedulerContextAsMap&quot;&gt;
      &lt;map&gt;
	     &lt;entry key=&quot;classNameProperty&quot; value=&quot;@className&quot;/&gt;
	  &lt;/map&gt;
   &lt;/property&gt;
   &lt;property name=&quot;jobDetails&quot;&gt;
      &lt;list&gt;
	     &lt;ref bean=&quot;taskExecutorJob&quot;/&gt;
      &lt;/list&gt;
   &lt;/property&gt;
&lt;/bean&gt;
</pre>
<p>V konfiguraci je vidět název třídy společného Quartz jobu &#8211; <code>TaskExecutorJob</code>, který je spouštěn všemi triggery. Job se chová tak, že podle názvu interface uloženého v persistentních datech triggeru nalezne Springový bean, a na něm spustí metodu run(). Za zmínku stojí ještě čtvrtý parametr konstruktoru <code>SimpleSchedulerDAO</code>, jedná se o název klíče, pod kterým bude v persistentních datech triggeru uložen název třídy Spring beanu, zde tedy hodnota <code>"@className"</code>. V implementaci <code>SimpleSchedulerDAO</code> je vhodné dodělat kontrolu, že předané properties neobsahují klíč, který by kolidoval s hodnotou <code>classNameProperty</code>, a ještě lépe tuto hodnotu pevně stanovit a zdokumentovat v API.<br />
Na závěr tedy ještě implementace samotného jobu, zde se vyhledá a spustí naplánovaný handler.</p>
<pre class="brush: java; title: ; notranslate">
public class TaskExecutorJob implements Job{
    private String classNameProperty;
    private ApplicationContext applicationContext;
    public void execute(JobExecutionContext context) throws JobExecutionException {
	    //read class from persisted data
        Class taskClass = readClass(context, classNameProperty);
		//lookup bean instance
        Task task = lookupJobBean(taskClass);
        if (task != null) {
            Properties properties = new Properties();
            properties.putAll(context.getTrigger().getJobDataMap());
            properties.remove(classNameProperty);
			//finally execute the target task
            task.run(properties);
        }
    }
    /**
     * read persisted Task class name from trigger's jobDataMap
     */
    protected  Class readClass(JobExecutionContext context, String beanClassKey) {
        String beanClassName = context.getTrigger().getJobDataMap().getString(beanClassKey);
        try {
            if (beanClassName != null){
                return (Class) Class.forName(beanClassName);
            }
        } catch (ClassNotFoundException e) {
            //log error
        }
        return null;
    }
    /**
     * lookup Task implementation from Spring context
     */
    protected  T lookupJobBean(Class clazz){
        if (clazz != null){
            try {
                return applicationContext.getBean(clazz);
            } catch (BeansException e) {
                //log error
            }
        }
        return null;
    }
}
</pre>
<h3>Možné nevýhody</h3>
<p>Existuje zde ovšem také jeden nedostatek. Klientský kód je sice oddělený dvěma rozhraními (<code>Task</code> a <code>SchedulerDAO</code>), nicméně popsané řešení je orientované na Spring framework a toto má jeden dopad: je potřeba, aby handler, který chceme asynchronně spouštět, definoval <em>nové rozhraní</em> rozšiřující interface <code>Task</code>. Například takto:</p>
<pre class="brush: java; title: ; notranslate">
public interface TestingTask extends Task{
}
public class TestingTaskImpl implements TestingTask {
    public void run(Properties properties) {
      //do something..
    }
}
</pre>
<p>Nelze definovat přímo implementaci interface <code>Task</code> a tuto se snažit naplánovat přes <code>SchedulerDAO</code>. Jde o to, jak funguje Springový lookup <code>BeanFactory.getBean(Class requiredType)</code>: pokud Spring pro AOP používá jdk proxy, nebude možné provést lookup Spring beanu podle jeho implementace, pouze podle interface. Dále je nutné, aby v kontextu byla registrovaná od každého tohoto rozhraní pouze jedna implementace. Popsané řešení by bylo možné změnit např. tak, že scheduling nebude probíhat na základě rozhraní handleru, ale pomocí jména beanu. Toto by ovšem do kódu zaneslo mnohem větší závislosti na Spring frameworku.</p>
<p>Budu rád, pokud se podělíte se svými názory, či jste třeba řesili něco podobného, nebo zda v tom vidíte nějaké problémy.</p>
<p>Ukázkový funkční příklad si můžete stáhnout zde: <a href="http://www.aspectworks.com/wp-content/uploads/2012/03/quartz-example.zip">quartz-example.zip</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.aspectworks.com/2012/03/quartz-scheduler-a-dynamicke-planovani-uloh/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Doporučená četba</title>
		<link>http://www.aspectworks.com/2012/03/doporucena-cetba-2</link>
		<comments>http://www.aspectworks.com/2012/03/doporucena-cetba-2#comments</comments>
		<pubDate>Wed, 14 Mar 2012 11:10:02 +0000</pubDate>
		<dc:creator>Luboš Račanský</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Jen tak]]></category>
		<guid isPermaLink="false">http://www.aspectworks.com/?p=3512</guid>
		<description><![CDATA[Na twitteru vám pravidelně přinášíme doporučení nejen na technické články. Pro ty, kteří nás nesledují, ale i pro ty, kterým například nějaký odkaz utekl, přinášíme seznam toho nejzajímavějšího. Doporučená četba,<a href="http://www.aspectworks.com/2012/03/doporucena-cetba-2" class="moreLink">více&#160;»</a>]]></description>
			<content:encoded><![CDATA[<p>Na <a href="https://twitter.com/#!/AspectWorks">twitteru</a> vám pravidelně přinášíme doporučení nejen na technické články. Pro ty, kteří nás nesledují, ale i pro ty, kterým například nějaký odkaz utekl, přinášíme seznam toho nejzajímavějšího. Doporučená četba, která by neměla zapadnout ve štěbetání twitteru. Nakonec pro odlehčení jeden Dilbert strip.<br />
<span id="more-3512"></span></p>
<p><a href="http://agilemanifesto.org/iso/cs/">Agilní manifest</a> je dostupný v češtině.</p>
<p>Slyšeli jste už termín death-by-Powerpoint? <a href="https://twitter.com/#!/presentationzen">Garr Reynolds</a> píše o <a href="http://www.presentationzen.com/presentationzen/2006/04/slideuments_and.html">Slideuments</a>, anti-pattern míchání slajdů a dokumentů do jednoho.</p>
<p><a href="https://twitter.com/#!/martinfowler">Martin Fowler</a> sepsal článek <a href="http://martinfowler.com/articles/newMethodology.html">New Methodology</a> , ve kterém se věnuje agilnímu vývoji. Mimo jiné se zamýšlí nad tím, že zatímco ve stavením inženýrství zabere návrh asi 10% rozpočtu, tak v softwarovém inženýrství je to naopak téměř většina. Softwarový vývoj je kreativní činnost, a proto se těžko předvídá a plánuje.</p>
<p>Lidé nejsou zdroj, lineární proměnná, ačkoliv se tak s nimi v softwarovém inženýrství často počítá &#8211; <a href="http://alistair.cockburn.us/Characterizing+people+as+non-linear%2c+first-order+components+in+software+development">Characterizing people as non-linear, first-order components in software development</a></p>
<p>Stanford University pořádá on-line <a href="http://www.crypto-class.org/">kurz kryptografie</a>, zdarma. Právě začal, tak si pospěšte, ať ještě stihnete domácí úkoly. Seznam všech kurzů naleznete na <a href="https://www.coursera.org">https://www.coursera.org</a></p>
<p>Jediným správným <a href="http://www.osnews.com/story/19266/WTFs_m">měřítkem kvality kódu je WTF za minutu</a>.</p>
<p><a href="http://dilbert.com/strips/comic/2012-01-09">Open space kanceláře mají svá úskalí</a>, Dilbert o tom ví své.</p>
<p>Předchozí <a href="http://www.aspectworks.com/2011/11/doporucena-cetba">doporučená četba</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.aspectworks.com/2012/03/doporucena-cetba-2/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Pořádáme školení Spring Framework</title>
		<link>http://www.aspectworks.com/2012/03/skoleni-spring-framework</link>
		<comments>http://www.aspectworks.com/2012/03/skoleni-spring-framework#comments</comments>
		<pubDate>Tue, 06 Mar 2012 16:17:29 +0000</pubDate>
		<dc:creator>Marta Křížová</dc:creator>
				<category><![CDATA[ @cs]]></category>
		<category><![CDATA[Blog]]></category>
		<guid isPermaLink="false">http://www.aspectworks.com/?p=3389</guid>
		<description><![CDATA[V termínu 11. &#8211; 13. dubna 2012 pořádáme školení Spring Framework v Praze a máme ještě několik volných míst. Jde o nejúspěšnější kurz z dílny AspectWorks, který je tentokrát otevřený<a href="http://www.aspectworks.com/2012/03/skoleni-spring-framework" class="moreLink">více&#160;»</a>]]></description>
			<content:encoded><![CDATA[<p>V termínu 11. &#8211; 13. dubna 2012 pořádáme školení Spring Framework v Praze a máme ještě několik volných míst. Jde o nejúspěšnější kurz z dílny AspectWorks, který je tentokrát otevřený všem zájemcům, kteří chtějí získat přehled o vývoji nad Spring Frameworkem. Cílem školení je představit Spring Framework pro všechny oblasti vývoje webových aplikací.</p>
<p><span id="more-3389"></span></p>
<p>Tento kurz se zaměřuje na praktický vývoj a není (jako většina odborných školení) koncipován jako teoretická přednáška. V průběhu tří dnů poznáte a hlavně se naučíte vývoj kompletní aplikace na Springu a Hibernatu. Ze školení si odnesete funkční aplikaci, která se může směle stát základem pro velký projekt.</p>
<p><strong>Agenda školení je následující:</strong></p>
<p>• Spring Core<br />
• Spring AOP<br />
• Spring JDBC<br />
• ORM<br />
• Transactions<br />
• Spring Test<br />
• Spring MVC<br />
• Spring Remoting<br />
• Spring Security</p>
<p>Školit bude Pavel Müller, technický ředitel a hlavní softwarový architekt společnosti AspectWorks, se zkušenostmi z mnoha rozsáhlých projektů za použití Springu. Kromě běžných věcí se tak dozvíte zajímavosti z oblasti architektury systémů, best practices, novinky ve Springu 3.1 a další tipy a triky. Další informace můžete nalézt <a href="http://www.aspectworks.com/sluzby/skoleni-it/skoleni-spring">zde</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.aspectworks.com/2012/03/skoleni-spring-framework/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Pořádáme školení Spring Framework</title>
		<link>http://www.aspectworks.com/2012/03/poradame-skoleni-spring-framework</link>
		<comments>http://www.aspectworks.com/2012/03/poradame-skoleni-spring-framework#comments</comments>
		<pubDate>Tue, 06 Mar 2012 15:42:52 +0000</pubDate>
		<dc:creator>Martin Korbel</dc:creator>
				<category><![CDATA[Homepage]]></category>
		<category><![CDATA[Novinky]]></category>
		<guid isPermaLink="false">http://www.aspectworks.com/?p=3379</guid>
		<description><![CDATA[V termínu 11. &#8211; 13. dubna 2012 pořádáme školení Spring Framework. Jde o nejúspěšnější kurz z dílny AspectWorks a tentokrát je otevřený všem zájemcům, kteří chtějí získat přehled o vývoji nad<a href="http://www.aspectworks.com/2012/03/poradame-skoleni-spring-framework" class="moreLink">více&#160;»</a>]]></description>
			<content:encoded><![CDATA[<p>V termínu 11. &#8211; 13. dubna 2012 pořádáme školení Spring Framework. <span id="more-3379"></span>Jde o nejúspěšnější kurz z dílny AspectWorks a tentokrát je otevřený všem zájemcům, kteří chtějí získat přehled o vývoji nad Spring Frameworkem. Školení bude probíhat v plně zařízené učebně na Praze 5. V ceně jsou školící materiály, pomůcky, občerstvení a certifikát. Podmínky a způsoby přihlášení a další podrobnosti o kurzu najdete na <a title="Školení Spring" href="http://www.aspectworks.com/sluzby/skoleni-it/skoleni-spring">zde</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.aspectworks.com/2012/03/poradame-skoleni-spring-framework/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Implementační fiasko ve čtyřech dějstvích</title>
		<link>http://www.aspectworks.com/2012/03/implementacni-fiasko-ve-ctyrech-dejstvich</link>
		<comments>http://www.aspectworks.com/2012/03/implementacni-fiasko-ve-ctyrech-dejstvich#comments</comments>
		<pubDate>Mon, 05 Mar 2012 07:03:17 +0000</pubDate>
		<dc:creator>Richard Šerý</dc:creator>
				<category><![CDATA[Blog]]></category>
		<guid isPermaLink="false">http://www.aspectworks.com/?p=3203</guid>
		<description><![CDATA[Zažili jste při vývoji softwaru nějaký ten &#8222;epic fail&#8220;? To se jistě rádi pobavíte na cizí účet. Následující řádky budou o tom, jak se posloupností logických kroků a racionálních rozhodnutí<a href="http://www.aspectworks.com/2012/03/implementacni-fiasko-ve-ctyrech-dejstvich" class="moreLink">více&#160;»</a>]]></description>
			<content:encoded><![CDATA[<p>Zažili jste při vývoji softwaru nějaký ten &#8222;epic fail&#8220;? To se jistě rádi pobavíte na cizí účet. Následující řádky budou o tom, jak se posloupností logických kroků a racionálních rozhodnutí dopracovat k neúspěchu. Podotýkám že osoby vystupující v tomto příspěvku jsou čistě smyšlené&nbsp;:-)</p>
<p><span id="more-3203"></span></p>
<h3>Dějství první: Plánujeme</h3>
<p><em>User story byla prostá: Umělec nabízí svým fanouškům odměny za to, že přispějí na realizaci jeho projektu. Čím vyšší příspěvek, tím lepší odměny. Měli jsme vyvinout uživatelské rozhraní, které umožní umělci zadat jednotlivé odměny do systému, který je pak zobrazí fanouškům.</em></p>
<p>Dle hesla “dohnat a předehnat” jsme se rozhodli udělat více než konkurence: tam kde si jiní vystačili s prostým seznamem odměn, máme dva objekty: samotnou odměnu (název a popis), která ovšem nemá cenovku, a balíček, který má cenovku (ale chybí mu název) a obsahuje jednu či více odměn.</p>
<p><img src="http://www.aspectworks.com/wp-content/uploads/2012/02/rewards-concept-model.png" alt="" title="rewards - concept model" /></p>
<p>Co nás vedlo k takovému zjevnému nesmyslu? Alibismus, v podobě klasické věty &#8222;byznys to tak chce&#8220;. Věta, která vede ke zkáze, zní &#8222;Je to asi nesmysl, ale technicky to není nic složitého, tak to uděláme&#8220;. Tento způsob uvažování vede vždy ke katastrofě. Jestliže byznys nedokáže o nezbytnosti něčeho přesvědčit vývojáře, jak o tom chce přesvědčit zákazníky?</p>
<p>Krutou zbraní na straně byznysu je psychologický tlak na efektivitu v podobě osvědčené věty &#8222;už o tom kecáme tak dlouho, že za tu dobu byste to měli naprogramované&#8220;. To je podpásovka a třeba v tomto konkrétním případě by pár hodin analýzy a diskusí navíc možná ušetřilo stovky hodin vývoje. </p>
<p>Rozhodnutí nakonec opravdu vedlo k obrovskému množství problémů, neboť složitější struktura je obtížnější na pochopení a tím i na ovládání. Na strukturu typu “n x n” nelze použít žádný z osvědčených vzorů UI a ani architektura serverové části nám implementaci neusnadnila. Navíc jsou odměny lokalizované a odměny i balíčky mají stav (schvaluje je moderátor serveru, již schválené odměny a balíčky nelze nadále editovat).</p>
<p><em>Poučení: Jestliže nevidíte jasnou konkurenční výhodu, udělejte méně než konkurence. Pokud nějaká vlastnost vypadá jako nezbytná, najděte způsob, jak se bez ní obejít, a teprve až ho budete znát, rozhodněte se pro nebo proti.</em></p>
<h3>Dějství druhé: Navrhujeme architekturu</h3>
<p>Rozhodovali jsme se mezi dvěma variantami řešení. První varianta předpokládala, že odměny a balíčky odměn se budou spravovat samostatně. Tvůrce si nejdříve nadefinuje, jaké nabízí odměny, a pak je v další obrazovce seskupí do balíčků a dá jim cenovky. Druhá varianta typu “všechno v jednom” předpokládala, že tvůrce vytvoří balíček, a ten bude rovnou plnit odměnami, které si hned na místě vytvoří.</p>
<p>Nejdříve jsme se pustili do první varianty, která vypadala jednodušší. Brzy jsme se však dostali do potíží a navíc se ukázalo, že řešení má vážné vady v oblasti použitelnosti. Uživatel musel stále přecházet z jedné obrazovky do druhé, nechápal co je balíček odměn ani proč odměna nemá cenovku atd.</p>
<div class="clearfix">
<img src="http://www.aspectworks.com/wp-content/uploads/2012/02/rewards-wireframe-A-1.png" alt="" title="rewards - wireframe A-1" style="width: 48%; float: left;" /></p>
<p><img src="http://www.aspectworks.com/wp-content/uploads/2012/02/rewards-wireframe-A-2.png" alt="" title="rewards - wireframe A-2"  style="width: 48%; float: right;" />
</div>
<p>Prohlásili jsme tedy koncept za neúspěšný a pustili se do druhé varianty. Ta kupodivu docela fungovala, ale občas se tam děly prapodivné věci. Považovali jsme je za drobné potíže, které bude snadné odladit, řešení však už obsahovalo skrytý technologický dluh a navíc nebylo tak docela kompatibilní s architekturou nižších vrstev aplikace. Oprava jednoho problému obvykle odhalila další potíže, práce se táhly zdánlivě do nekonečna.</p>
<p><img src="http://www.aspectworks.com/wp-content/uploads/2012/02/rewards-wireframe-B-1.png" alt="" title="rewards - wireframe B-1" /></p>
<p><em>Poučení: Jestliže všechna řešení, která se nabízí, jsou špatná, nezapomeňte hledat chybu v zadání. Zpochybněte cíle. Snažte se najít to podstatné a ostatního se zbavit. Hledejte taková řešení, která jsou v souladu s dostupnou technologií, neútočte hlavou proti zdi. Proces plánování je bezpochyby nutný a užitečný, hotový plán je však přítěž, která svazuje vaše myšlení. Zbavte se sentimentality a zahoďte plán, když jeho realizace začne váznout. Důležité je dosáhnout cíle, nikoli držet se plánu.</em></p>
<h3>Dějství třetí: Programujeme</h3>
<p>Znáte ty části kódu, které sice fungují, ale nikdo neví jak? Pravděpodobně je napsal programátor s pragmatickým přístupem “zbouchám to aby to bylo” a tím vám zadělal na problémy v budoucnu. Jistá dávka pragmatismu je samozřejmě nezbytná, v podstatě ale jde o škodlivý přístup, který do projektu vnáší zbytečná rizika a zvětšuje technologický dluh. Čím větší projekt, tím důležitější je psát kód přehledný a transparentní a vyhýbat se zkratkovitým řešením.</p>
<p>Náš kód od začátku trpěl neduhem pragmatického přístupu. Programátor sestavil controller tak, že sice fungoval, ale dalo opravdu hodně přemýšlení pochopit, jak je to možné. Kodér pak nad tím sestavil javascriptovou aplikaci, jejíž kód byl v podstatě v pořádku, ale měla naprosto tragickou strukturu. Názvy proměnných nedávaly smysl, parametry funkcí a metod se jmenovaly a fungovaly pokaždé jinak. Například pod názvem “id” se někdy skrývalo skutečné databázové id, jindy ale pořadové číslo položky v seznamu a občas i něco úplně jiného. Na nečitenosti kódu se také podepsalo časté používání různých zájmen, hlavně klíčového slova “this”, v kódu.</p>
<p><img src="http://www.aspectworks.com/wp-content/uploads/2012/02/rewards-code1.png" alt="" title="rewards-code1" /></p>
<p>Neštěstí by asi nebylo tak velké, kdyby si někdo včas všimnul, co se děje. Ale celý tým byl tou dobou pod enormním tlakem, protože projekt se blížil dokončení a kvůli špatně nastavenému rozsahu “hořel” na mnoha místech. Proto tuto funkcionalitu programovali junioři bez jakéhokoli dohledu (“není čas dělat code review”).</p>
<p><em>Poučení: Kód, který je pochopitelný a čitelný, ale nefunguje, můžete snadno spravit. Kód, který funguje, ale je nepochopitelný, se může kdykoli rozbít a neuděláte s ním nic.<br />
Nejlepší forma boje proti “pragmatickému přístupu” jsou pravidelné code reviews, které by měl vždy provádět vývojář-senior. Zdánlivě jde o ztrátu času, ale pečlivou kontrolou před každým commitem zabráníte mnohem větším nákladům a zdržením v budoucnu.</em></p>
<h3>Dějství čtvrté: Refactoring</h3>
<p>Když se po několika sprintech ukázalo, že vývoj definitivně uváznul, sáhli jsme k nepopulárnímu opatření: refactoringu. Refactoring byl velmi bolestný a nakonec trval přibližně dvakrát déle, než jsme původně odhadovali.</p>
<p>Ukázalo se, že hlavní zdroj problémů byl špatně čitelný kód. Většina práce tedy spočívala v přejmenování všech proměnných, funkcí, objektů a metod tak, aby názvy odpovídaly projektovému slovníku. Druhý důležitý krok spočíval v novém rozdělení existujícího kódu do logicky pojmenovaných funkcí, s jasně definovaným rozhraním. Přibylo tak velké množství funkcí, které však byly jednoduché a jednoúčelové, což se později ukázalo jako výhoda.</p>
<p><img src="http://www.aspectworks.com/wp-content/uploads/2012/02/rewards-code2.png" alt="" title="rewards-code2" /></p>
<p>Jakmile začalo být fungování aplikace zřejmé i z kódu, ukázaly se skryté problémy a zjistili jsme, že některé funkce zcela chybí. Co bylo horší, zjistili jsme také, že stávající řešení nelze správně propojit s naším validačním frameworkem a že je nutné celý front end přepracovat. Naštěstí, vyčištěná struktura umožnila kompletní přepracování celého řešení během jediného sprintu.</p>
<p><em>Poučení: Je opravdu důležité, jak pojmenujete svoje proměnné. Vytvořte si projektový slovník a přísně ho dodržujte, nejen v běžné řeči a v dokumentaci, ale i v názvech objektů, vlastností a metod, v pojmenování tříd a id v HTML kódu, v pojmenování funkcí v JavaScriptu, v názvech souborů a pojmenování sloupců v databázi&#8230; Dbejte na čitelnost názvů, nevytvářejte zbytečně různé zkratky. Pište kód tak, aby ho přečetlo i malé děcko (má-li samozřejmě příslušné vývojářské certifikace). Máte-li se rozhodnout mezi elegantním, výkonným kódem a kódem, který je čitelný, zvolte čitelný kód.</em></p>
<h3>Dějství páté: Bilancujeme</h3>
<p>Po bitvě je každý generál. I my, když se podíváme zpět, nechápeme jak jsme mohli být tak tupí, akceptovat podivné zadání a snažit se ho implementovat i přes jeho zjevné nedostatky. U některých věcí je jasné, že jseme udělali chybu, ale už vůbec není jasné, jak se jí příště vyhnout:</p>
<p>Porušili jsme pravidlo Ockhamovy břitvy. Pravidlo zní “Pluralitas non est ponenda sine necessitate.” což většina vývojářů zná spíše pod zkratkou KISS (keep it simple stupid). Máme sice dobrou výmluvu &#8211; zadavatel na tom trval &#8211; nicméně i tak je to jenom výmluva. Náš postoj v této otázce nebyl tak pevný, abychom trvali na zjednodušení zadání i za cenu vážného konfliktu se zadavatelem, a to se obrátilo proti nám. Tohle je problém, na který zatím neznám jasné řešení &#8211; má vývojář vystupovat jako despotický egomaniak, který šikanuje zadavatele “pro jeho vlastní dobro”, nebo raději bez odporu realizovat nesmyslné vize zadavatele a po nevyhnutelném neúspěchu být označován za neschopného packala? Existuje nějaká třetí cesta?</p>
<p>Uvažovali jsme od začátku příliš pragmaticky. Pragmatický vývoj s sebou přináší technologický dluh. Jestliže technologický dluh vznikne na konci projektu, je to v pořádku &#8211; vývoj pravděpodobně nebude pokračovat, takže ho nebude třeba splatit. Pokud ale vznikne hned na začátku, zaplatí vývojový tým všechno i s úroky. Proto je na začátku projektu nutné vyvíjet toho co nejméně, co nejčistším způsobem a nebát se velkých refactoringů. Později tyhle věci už budou mnohem dražší. Problém je, že v každém týmu se najde nejméně jeden fanoušek “pragmatického přístupu” a že v krátkém časovém horizontu tento přístup skutečně přináší oslňující  výsledky. Bohužel v dlouhodobém horizontu zruinuje jak projekt tak i vztah se zákazníkem. Ani tady nemám jasný recept, jak tento problém řešit. Vize rychlých, snadných řešení je prostě příliš lákavá a kdo je zpochybní, je nevyhnutelně v roli neschopného škarohlída.</p>
<p>Nenašli jsme včas odvahu zahodit rozdělanou práci. Když zazní věta “teď už jsme do toho investovali příliš, než abychom to zahodili a celé předělali”, je třeba začít hodně přemýšlet. Pokud tato věta zazní o čtrnáct dnů později a týká se stejného problému, je to jasné &#8211; přišel čas na bolestivý řez. Opět je výhoda, pokud vývojář je ochoten udělat ze sebe exemplární příklad arogantního tvrdohlavého idiota a říci kategorické “ne” dalšímu vývoji. Pokračování může být v podstatě dvojí, buď vyletí z projektu nebo prosadí refactoring. Možná tím nezachrání projekt, ale určitě tím ušetří svoje nervy.</p>
<p><em>Jak skončit v pozitivním duchu? Poučili jsme se z neúspěchu? Silně o tom pochybuji, tahle rozhodnutí jsou vždy těžká a nepopulární. Ale projekt jsme dokončili a spustili, takže vlastně nakonec všechno dobře dopadlo.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.aspectworks.com/2012/03/implementacni-fiasko-ve-ctyrech-dejstvich/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
	</channel>
</rss>

