A következő címkéjű bejegyzések mutatása: Kód minőség. Összes bejegyzés megjelenítése
A következő címkéjű bejegyzések mutatása: Kód minőség. Összes bejegyzés megjelenítése

2013. július 15., hétfő

PMD - XPath alapú kódellenőrző plugin fejlesztés 2.

Még mindig az XPath alapú plugin fejlesztésnél maradunk, de most egy kicsit összetettebb példát fogok bemutatni.

Explicit (forráskódbeli) GC hívások kijelzése 


A kódból nem javasolt a szemétgyűjtés kikényszerítése, mivel ilyenkor mindig teljes GC-zés történik, azaz a fiatal és öreg heap területeken is megtörténik a takarítás, ami akár hosszabb időre is megállítja az alkalmazás futását. Bár a -DisableExplicitGC JVM argumentummal letiltható az explicit gc használata, a kódbeli hívás tiltása mégis javasolt a "véletlenül kifelejtett" beállítások miatt!

A szabály elkészítéséhez először is kigyűjtöttem a GC forráskódból történő meghívásainak az eseteit:

1. Runtime.getRuntime().gc();
2. Runtime rt=Runtime.getRuntime(); rt.gc();
3. java.lang.Runtime.getRuntime().gc();
4. Java.lang.Runtime rt=java.lang.Runtime.getRuntime(); rt.gc();
5. System.gc();
6. java.lang.System.gc();
7. Runtime és System statikus importja, getRuntime().gc(); ill gc(); meghívása.

Az egyszerűség kedvéért az alábbi szabály most csak az 1. és a 2. esetet fedi le, azonban könnyen kibővíthető a többi eset detektálásához is. Lássuk!

//StatementExpression/PrimaryExpression/PrimaryPrefix
[
./Name[@Image = 'Runtime.getRuntime'] and ../PrimarySuffix[@Image = 'gc']
or
ends-with(Name/@Image, '.gc') and substring-before(Name/@Image, '.') = 
//VariableDeclarator/VariableDeclaratorId/
@Image[../../../Type/ReferenceType[ClassOrInterfaceType/@Image='Runtime']]
]


A szabály a //StatementExpression/PrimaryExpression/PrimaryPrefix kifejezésre illeszkedő csomópontok halmazára fogalmaz meg két feltételt egy logikai vagy kapcsolattal elválasztva. A feltétel első része azokra a csomópontokra illeszkedik, amelyeknek PrimaryPrefix alatti Name csomópontjának a neve Runtime.getRuntime és a PrimaryExpression csomópont alatt tartalmaz egy olyan PrimarySuffix-es node-ot, aminek a neve gc. A feltétel második része pedig olyan Runtime típusú példány deklarációkat keres ahol is a példányon a gc() metódus meghívásra került az adott lokális scope-on belül.

Mivel a feltétel második részének a megvalósítása összetettebb kifejezést eredményezett, ezért a részletes ismertetést az alábbi AST alapján fogom bemutatni.


Az ends-with(Name/@Image, '.gc') kifejezésrész olyan csomópontokra fog rámutatni ami a PrimaryPrefix alatt található és a neve a .gc –re végződik. Az AST példában ez lesz a Name:rt.getRuntime.gc csomópont.

substring-before(Name/@Image, '.')=//VariableDeclarator/VariableDeclaratorId/@Image[../../../Type/ReferenceType[ClassOrInterfaceType/@Image = 'Runtime'

A fenti kifejezés pedig az előzőleg kiválasztott csomópontok névből levágja a legelső pont előtti előtagot, a példa szerint az rt-t, ezután a változó deklarációkra illesztett útvonallal (//VariableDeclarator/VariableDeclaratorId) visszakeresi hogy a lokális scope-n belül ennek a példánynak a típusa Runtime volt-e. Amennyiben talál ezen feltételeknek megfelelő csomópontot a szabály kijelzi a hibát.

Összegzésként elmondhatom hogy az XPath alapú kifejezésekkel általában tömören és jól olvashatóan fogalmazhatjuk meg a szabályainkat. Ha netán mégis nehézségekbe ütköznénk akkor arra is megvan a lehetőség hogy Java nyelven fogalmazzuk meg a szabályainkat.

2013. július 7., vasárnap

PMD - XPath alapú kódellenőrző plugin fejlesztés 1.

A múltkori bejegyzésben átismételtük az XPath alapú szabályok alapjait, most pedig nézzünk meg néhány egyszerű gyakorlati példát a kód ellenőrző plugin-ok fejlesztésre.

Statement használatának a kijelzése


Általános esetben érdemes elkerülni a java.sql.Statement használatát, helyette pedig a java.sql.PreparedStatement paraméter binding-gal való alkalmazása javasolt. XPath alapú szabállyal megfogalmazva ez a következőként nézne ki: 

//ClassOrInterfaceType[@Image='java.sql.Statement' or @Image='Statement']

A fenti kifejezéssel minden olyan osztály és interface típusú deklaráció kijelzésre kerül, aminek a neve Statement vagy java.sql.Statement. Érdemes megemlíteni, hogy a csomagnévvel specifikált nevet és az egyszerű osztálynevet is definiálni kell a szabályban, mivel a fejlesztők mindkét verziót használhatják a forráskódban! A szabállyal azonban probléma jelentkezhet abban az esetben, ha a kódbázisban egy saját Statement osztály is használatban van, mivel erre is illeszkedne a szabály második feltétele. Megoldásként az előbbi szabályt érdemes kibővíteni az import deklarációkra vonatkozó megkötésekkel:

//ClassOrInterfaceType[(@Image='Statement' or @Image='java.sql.Statement')
and (/ImportDeclaration/Name[@Image='java.sql.Statement'] 
or /ImportDeclaration/Name[@Image='java.sql'])]

SELECT * jellegű lekérdezések kijelzése


Az SQL query-k készítésekor érdemes előre megadni az oszlopneveket, így a lekérdezések végrehajtásakor valóban csak azok az adatok kerülnek át a klienshez amire szükség is lesz. A szabály megsértését az alábbi XPath alapú kifejezéssel tudnánk kijelezni:

//Expression[descendant::Literal[contains(upper-case(@Image), 'SELECT')]
and descendant::Literal[contains(@Image, '*')  ]]

Ez a kifejezés minden olyan string-re illeszkedik akár összefűzésen keresztül is, ahol a select és a * literálok is szerepelnek. Mivel nem lehetünk biztosak abban, hogy a kódban a select betűit kis vagy nagy betűsen, netán vegyesen adták meg, ezért az upper-case() XPath függvénnyel nagybetűsre alakítjuk a vizsgálat előtt.

A folytatásban pedig egy összetettebb példát fogunk megnézni.

2013. július 1., hétfő

PMD - Az XPath kifejezések használata

XPath alapú szabályokat legegyszerűbben a PMD Rule Designer segítségével készíthetünk, amivel a célunk az AST megfelelő csomópontjaira illeszkedő kifejezés létrehozása az adott szabályra vonatkozólag. Mielőtt elkezdenénk a PMD-s kódellenőrző kiegészítőket fejleszteni, érdemes egy kicsit felfrissíteni az XPath tudásunkat.


Az XPath kifejezések lehetőséget adnak arra, hogy bizonyos feltételek mentén kiválasszunk egy csomópontot vagy csomópont halmazt az AST-n. A fenti ábrán látható mintakódot ill. AST-t felhasználva nézzük meg a leggyakrabban használt XPath kifejezéseket.

Hivatkozás a gyerek csomópontokra

 A "/" segítségével az aktuális csomópont gyerekét tudjuk elérni, ami alapesetben a gyökér csomópont. Az alábbi kifejezéssel a gyökér node-tól kezdődően eljuthatunk a ResultType node-ig: 
/TypeDeclaration/ClassOrInterfaceDeclaration/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration/ResultType

Hivatkozás a leszármazott csomópontokra

A "//" operátorral az aktuális node leszármazottaira vonatkozó illeszkedést fogalmazhatunk meg. Példaként az előző kifejezés helyett használhattuk volna a //ResultType kifejezést is, mivel a mintakódot tekintve ugyanarra a node-ra illeszkednek.

Attribútumok és feltételes szerkezetek

A "@" segítségével egy adott csomópont valamelyik attribútumára hivatkozhatunk. Érdemes megjegyezni, hogy a PMD által felépített AST fában az elem nevét mindig a @Image property-vel érhetjük el. A //Literal[@Image='"hello world"'] kifejezés a mintakód AST-jának a legutolsó csomópontjára fog illeszkedni ahol is "[]" jelek között a csomópont nevére vonatkozó feltételt adtunk meg.

Az XPath beépített függvényeinek használata

Az XPath kifejezések használata során igénybe vehetjük a beépített függvényeket is. Példaként a contains() ill. a starts-with() függvényeket használhatjuk ahhoz, hogy kiválasszuk a minta AST fa legutolsó csomópontját: //Literal[contains(@Image,'hello')] ill. //Literal[starts-with(@Image,'"hel')] .

"Wild card"-ot használó kifejezések

Egy adott helyen a "*" karakter bármilyen node-ot reprezentálhat. A //ClassOrInterfaceBodyDeclaration/*/ResultType kifejezés minden ResultType típusú node-ra illeszkedni fog, aminek a nagyszülője ClassOrInterfaceBodyDeclaration típusú.

Az "XPath Axis"-t használó kifejezések

Az Xpath Axis lehetőséget ad arra, hogy az aktuális csomópont-hoz viszonyítva a rokonsági fok alapján lekérjünk a kívánt node-ok halmazát. A //Arguments/descendant::PrimaryPrefix kifejezéssel minden olyan PrimaryPrefix csomópontot visszakapunk, amelyek egy Arguments típusú csomópont leszármazottai. Összehasonlításként ugyanez a kifejezés a "*" karakterrel megfogalmazva a //Arguments/*/*/*/PrimaryPrefix kifejezés lett volna.

Az érdeklődők számára a további XPath lehetőségekről a w3schools.com tutorial-t ajánlanám. A folytatásban pedig néhány gyakorlati példát fogok mutatni az XPath alapú plugin fejlesztéshez.

2013. június 9., vasárnap

PMD - Bevezetés a kódellenőrző plugin fejlesztéshez

Hibák tömkelegét találhatjuk meg a dynaTrace segítségével, de ha valamilyen minőségbiztosítási folyamat révén nem ellenőrizzük le ezeket, az egyszer már kigyomlált problémák idővel megint vissza fognak kerülni a rendszerbe. Emiatt szoktam javasolni a korábban már megtalált hibák és az ügyfelek környezetében néha több 100 oldalon lefektetett fejlesztési és keretrendszer használati szabálysértések automatizált kijelzését. Erre egy remek eszköz lesz a Sonar, kiegészítve a saját fejlesztésű PMD ill. FindBugs alapú kódellenőrző szabályokkal.

A PMD egy olyan statikus forráskód ellenőrző eszköz, amivel megtalálhatjuk a Java fejlesztők által leggyakrabban "elkövetett" kódolási hibákat. Tipikusan ilyen hibák a kivételkezelésnél üresen hagyott catch ágak, objektumok összehasonlítása az == operátorral vagy a for ciklusban történő String-ek összefűzése a StringBuilder.append() használata helyett. A PMD alapértelmezésként közel 250 szabályt tartalmaz, de támogatja a saját szabályokkal való bővítést is XPath és Java alapú kiterjesztésekkel.

A PMD-vel történő analizálás során, először is a JavaCC beolvassa az Extended Backus-Naur Form által definiált nyelvtan és legenerálja azt a parszert, ami alkalmas lesz a java nyelvű források felolvasásához, majd a JJTree add-on segítségével felépíti az adott forráskódhoz tartozó absztrakt szintaxis fát (AST). A forráskód struktúrájának olyan faszerű reprezentációja az AST, ahol az egyes csomópontok az adott nyelv egy-egy konstrukciójának felelnek meg (pl.: WhileStatement, Block, ClassOrInterfaceDefinition, stb…).

A felépített fát pedig azért nevezzük absztraktnak, mivel a forráskódnak csak a releváns részeit tartalmazza,  a java szintakszisának megfelelő magas szintű leírással. Miután a PMD felépítette a forráskód alapján az AST-t, megkezdi a csomópontok bejárását és az aktivált szabályokban definiált node-okra a Visitor Design Pattern szerint meghívódnak a callback-ek. Ezek a callback-ek tipikusan a visit(ASTWhileStatement node, Object data) {…} jellegű metódusok, amelyekben a saját szabályainkat implementálhatjuk.

A működéséből eredően, a PMD-vel csak olyan kódellenőrzési szabályokat definiálhatunk, amelyek megfogalmazhatók a forráskód szintjén. Amennyiben olyan jellegű szabályokat szeretnénk készíteni, amihez a bytecode analizálása szükséges (pl.: Hibás cast-olás kijelzése) a FindBugs kódelemzőt kell felhasználni. Mivel a Sonar platform a PMD 4.3-as verziójával kompatibilis, ezért azokat a kódellenőrző plugin-okat amelyeket a Sonar-ba is beakarunk kötni, a 4.3-as PMD verzióval kell elkészíteni. A plugin fejlesztés során a leghasznosabb eszközünk a PMD Rule Designer lesz, ami legenerálja a minta forráskód AST-jét valamint az XPath alapú PMD szabályok elkészítéséhez is segítséget nyújt.

Nézzük meg ezt a gyakorlatban is!

Indítsuk el a PMD Rule Designer-t a pmd-bin-4.3.zip bin/designer.bat paranccsal és a Source code ablakba illesszünk be egy minta kódot, majd a go gombra kattintva megjelenik az Abstract Syntax Tree ablakban a felépített AST. Ha az XPath Query ablakban megadunk egy XPath kifejezést is, akkor a jobb alsó ablakban megjelennek az XPath kifejezésre illeszkedő csomópontok és azok forráskódbeli helye is.


A fenti ábrán jól látható, hogy a java forráskódbeli kifejezések hogyan felelnek meg az AST egy-egy csomópontjának. Az AST csomópontok és a forráskód részek megfeleltetéséhez hasznos feature, hogyha az AST-n kiválasztunk egy node-ot, akkor az ehhez tartozó forráskód blokk is kijelölésre kerül. Sőt, ha az XPath találati lista elemeire kattintunk, a megfelelő forráskód blokkok is kijelölődnek!

A folytatás hamarosan következik...

2013. május 25., szombat

SZSZK 2013 - Szoftverminőség-biztosítás open source eszközökkel

Frissítve: 2013.06.05. - Lesz workshop is!

Az FSF.hu Alapítvány 2013. június 8.-án ismételten megrendezi a Szabad Szoftver Konferencia és Kiállítást Budapesten, az Óbudai Egyetem Bécsi úti épületében. A konferencia reggel fél tíztől délután ötig fog tartani. Három szekcióban párhuzamosan zajlanak majd a szakmai előadások, ezekkel párhuzamosan az aulában szabad szoftverekkel és szabad kultúrával kapcsolatos kiállítás lesz.

A konferenciára, a Szoftverminőség-biztosítás open source eszközökkel - Hol a határ? címmel egy olyan gyakorlatias előadással készülök, ami a reményeim szerint jópár hasznos eszközt és technikát fog tartogatni a kezdő és haladó Java fejlesztőknek egyaránt. Idézem az előadásom beharangozóját:

"Az előadás során áttekintjük azokat az open source eszközöket és legjobb gyakorlatokat, amelyek a Java platformon hozzájárulhatnak a szoftver folyamataink biztosításához és a minőségi követelmények betartásához. Megnézzük az open source eszközök legfontosabb előnyeit néhány saját példával illusztrálva, majd megmutatom hogy hol van az a határ, ahol már kereskedelmi termékeket is érdemes bevetni.Címszavakban: Jenkins, Ant, Maven, Nexus, SVN, GIT, JUnit, TestNG, JMeter, Selenium, Sonar, PMD, CheckStyle, FindBugs."

Az előadást követő workshop-on gyakorlatban is kipróbálhatod mindazt amit elméletben már elsajátítottál. Egészen a telepítéstől kezdve a konfiguráláson át megismerkedhetsz a Jenkins és a Sonar használatával, felveszünk egy Java alapú projektet majd megvizsgáljuk a kódminőségi jellemzőit. A workshop-on való aktív részvételhez kérlek kövesd a konferencia honlapján közzétett útmutatásokat!

Érdemes eljönni, mert a program alapján nemcsak hasznos tudáshoz juthatnak a résztvevők, de a szervezők jóvoltából a reggel érkező és a rendezvény végéig maradó hallgatók között két 120GB-os és egy 250GB-os SSD is kisorsolásra kerül.


Az előadás prezentációját ide, a workshop útmutatóját pedig ide töltöttem fel.

2013. április 28., vasárnap

Sonar - A metrikák értelmezése 3.

Ahogy a múltkori bejegyzésemben is említettem, nemcsak a csomagok kialakításánál, hanem az osztályok és metódusok szintjén is érdemes odafigyelni a tervezésre!

A célunk, hogy a projekt osztályai minél függetlenebbek legyenek egymástól (laza csatolás), az osztályok metódusai pedig minél jobban kötődjenek az adott osztályhoz (erős kohézió)! Ha az osztályok szorosan kapcsolódnak egymáshoz, akkor az osztályokon végzett módosítások továbbgyűrűzhetnek a többi osztály felé és más osztályokra is hatással lehetnek. Ha pedig valamely osztály két metódusa nem használ közös attribútumokat ill. metódusokat, akkor valószínűleg nincs is közös tulajdonságuk, így ezeket két különböző osztályba kellene kiszervezni.

Afferent Coupling, Efferent Coupling

Az Afferent Coupling értéke azoknak az osztályoknak a számát adja, amelyek a kiválasztott osztályt használják, így ha a kiválasztott osztályban módosítások történnek, ezekre az osztályokra lehet majd hatással. Az Efferent Coupling értéke pedig azt mutatja, hogy a kiválasztott osztály hány másik osztálytól függ, azaz hogy hány másik osztályt importált be (vagy hivatkozott be).

Az alábbi példát tekintve, a ReaderInitializationException osztály Efferent Coupling értéke 1, mivel a funkcionalitásához csak egy másik osztályt, mégpedig az InitializationException osztályt használja fel és importálja be, amit a source fül alatt le is ellenőrizhetünk. Az Afferent Coupling értéke pedig 6, mivel ennyi osztály használja a ReaderInitializationException osztályt.



RFC (Response For Class)

Az RFC értéke az osztályok felelősségét mutatja, aminek az értéke az osztály minden metódusával ill. külső metódushívással (kivéve getter/setter) eggyel növekszik. Az alacsony RFC értékre és az oszlop diagram balra tolódására kell törekedni, általánosságban pedig elmondható, hogy csak a több száz RFC értékkel bíró osztályokra érdemes ránézni és a javításukat megfontolni.

 
Példaként nézzük meg az alábbi forráskód részletet, ahol is a TestRFC osztály RFC értéke 6 lesz a kommenteknél megjelölt hívások miatt.
public class TestRFC {
 //Empty TestRFC constructor   -> +1
 private Person p=new Person(); //new Person()  -> +1
 public void testMethod1(){ //testMethod1() -> +1
  System.out.println("test1"); //System.out.println(…) -> +1
 }
 public void testMethod2(){ //testMethod2() -> +1
  System.out.println(p.toString()); //p.toString()  -> +1
 }
}

LCOM4 (Lack Of Cohesion Of Methods)

Az LCOM4, a Single Responsibility Principle tervezési minta betartásához nyújt segítséget, miszerint minden osztálynak csak 1 felelőssége legyen ami hozzásegít ahhoz, hogy megszűnjenek a hatalmas osztályok és helyettük sok kicsi, könnyen tesztelhető, gyorsan átlátható és módosítható osztály jöjjön létre! Megjegyezném, hogy a munkám során már nem egyszer találkoztam 10.000 kódsort meghaladó osztályokkal, amik ugye nem javadoc-al voltak tele... :)

Az LCOM4 metrika arra a felépítésre épít, hogy az egymással kapcsolatos metódusok, ugyanazokkal az osztály szintű (nem metódus hatókörű) változókkal dolgoznak vagy egymást hívják. Egy kohézív osztályban tehát csak összetartozó metódusok találhatók, amelyek kizárólag az osztály funkciójának a biztosításáért dolgoznak. A nem kohézív osztályok egynél több felelősséggel is rendelkeznek, így ezeket az egyes felelősségek szerint több kisebb osztályba kell kiszervezni.

Mivel az LCOM4 az osztályonkénti felelősségek számát jelöli, ezért csak akkor tekinthető elfogadhatónak, ha az értéke 1. A 0 értékkel rendelkező osztálynak nincsen konkrét felelőssége, az 1-nél több értékkel rendelkező osztályoknak pedig több felelősségük van.

Az alábbi osztálynál - a standard szabály szerint számítva - az LCOM4 értékének 3-nak kellene lennie, így a megadott osztályt 3 másik osztályra kellene szétbontani, melyek a következő metódusokat ill. attribútumokat tartalmaznák:
  1. video, playVideo(), stopVideo(), restartVideo()
  2. audio, playAudio()
  3. autoSleep()
public class AudioVideoManager   {

 private Video video;
 private Audio audio;

 public void playVideo() {
  video.play(); 
 }  

 public void stopVideo() {
  video.stop();
 }

 public void restartVideo() {
  stopVideo();
  playVideo();
 }

 public void playAudio() {       
  audio.play(); 
 }
 
 public void autoSleep() {  
  System.out.println("auto sleep");   
 } 
}
A valós életbeli példákon nézve a tapasztalat azt mutatja, hogy standard szabály túl sok ’false positive’ értéket jelezne, ezért a Sonar egy feltunningolt LCOM4 algoritmussal dolgozik, miszerint ha az LCOM4==1, lehetséges hogy további szétbontás is szükséges azonban az LCOM4>1 esetén biztosan kijelenthető, hogy szét kell bontani az osztályt. A fenti kódrészletnél az LCOM4 értéke a módosított szabály miatt így nem 3, hanem 1 lesz.


Ha az LCOM4 widget-en az LCOM4>1 linkre kattintunk, akkor az osztályok kiválasztása után az LCOM4 tab aktivizálódik, ahol megtekinthetjük, hogy ezt az osztályt milyen metódusok mentén kellene kisebb osztályokra szétbontani.


A Sonar metrikák elemzése ezzel a cikkel véget ért, legközelebb egy kicsit más témával jelentkezem.

2013. március 3., vasárnap

Sonar - A metrikák értelmezése 2.

A múltheti bejegyzésem folytatásaként most a komplexitás és a csomagok közötti függőségek jellemzőiről fogok írni!

Complexity

A komplexitást a metódusokban található feltételes és vezérlési szerkezetek száma határozza meg, azaz az értéke a for, do, while, break, continue, if, else, case, default, return, catch, throw, throws, finally, &&, ||, ?, :, ^, &, | szerkezetekkel eggyel fog nőni. A widget jobb oldalán egy oszlop diagramon látható a projekt összes metódusának, ill. fájljának a komplexitás szerinti eloszlása. A komplexitásnál az alacsonyabb értékek tekinthetők jónak, mivel ezek kevesebb metódusonkénti feltételes és vezérlési szerkezetet, így karbantarthatóbb kódot jelentenek. A widget jobb oldali oszlop diagramja akkor tekinthető ideálisnak amikor baloldalra súlyozódik, mivel ilyenkor az egyszerűbb metódusok vannak többségbe. Általánosan elmondható, hogy a metódusonkénti 2.5 érték jónak tekinthető, 7 felett pedig már érdemes refaktorálni, azaz átszervezni a metódus kódját!


Package Tangle Index

Habár a Java projekteket jar-okba, csomagokba, fájlokba, osztályokba és metódusokba szervezzük ettől még nem feltétlenül lesz az alkalmazásunk moduláris! A tervezésnél figyelnünk kell arra, hogy az osztályok lazán csatoltak és erősen összetartóak legyenek (LCOM4, RFC) valamint arra is, hogy a csomagok között ne alakuljanak ki függőségi körök (Package Tangle Index). A package tangle index tehát a projekt csomagjai között kialakult függőségeket mutatja, aminek a feloldásához a Dependency Structure Matrix (DSM) használatát ajánlja a Sonar. Két java package között akkor alakul ki körbefüggés, amikor az egyik csomag valamelyik java osztályának egy másik csomag valamely java osztályára van referenciája és ez fordítva is teljesül! A csomagok közötti függőségeket azért érdemes elkerülni, mert ezek a csomagok egy szoros egységet fognak alkotni - ezzel csökkentve a modularitást - így azok majd nem választhatók el a későbbiekben egymástól! 

A következőkben a TinyJEE project, Doxia :: Include - Macro-ján keresztül fogom ismertetni a csomagok közötti függőségek felkutatását és azok feloldását. Az alábbi ábrán látható Sonar widget szerint 2-nél több körbefüggés van a projektben és ezek megszüntetéséhez 1 helyen kell a csomagok közötti függőségeket szétvágni, ami úgy tűnik hogy nem lesz nehéz feladat mivel csak 1 fájlt fog érinteni a módosítás!


Ha rákattintunk a widget-en található valamelyik linkre, akkor az alábbi ábrán látható függőségi struktúra mátrixhoz (DSM) jutunk, ami segítségünkre lesz a függőségek feloldásához. A táblázat sorainak első celláiban az egyes java csomagok nevei találhatóak, az oszlop nevek pedig ehhez hasonlóan alakulnak csak helyhiány miatt nincsenek feltüntetve. A gyanúsított függőségeket a piros négyzetek jelölik, így a munkát is itt kell kezdeni.

 

Ha kétszer rákattintunk az  org.tinyjee.maven.dim sorban található piros négyzet 1-es számára, akkor a táblázat alatt megjelenik, hogy a org.tinyjee.maven.dim.utils csomagból egy osztályának (AbstractAliasHandler) referenciája van a org.tinyjee.maven.dim csomag RequestParameterTransformer osztályára.


Látható, hogy a kattintás során a piros négyzet háttere lilára vált és a táblázatban kijelölődik egy másik cella is lila színűre. Ha ebben a cellában található 3-as számra kétszer rákattintunk, akkor az alábbi ábra szerint a táblázat alatt megjelennek a konkrét függőségek listája. Az org.tinyjee.maven.dim csomagban vannak olyan osztályok, amelyek a org.tinyjee.maven.dim.utils csomag osztályaira tartanak referenciát, ami az előző ábrát tekintve pont fordított, azaz megállapítható, hogy az említett 2 csomag között körbefüggés áll fent amit érdemes megszüntetni a modularitás fenntartása érdekében!


Látható, hogy a DSM segítségével gyorsan felderíhetők a csomagok közötti függőségek. A Sonar további eszközöket is biztosít a forráskód minőségének feljavításához amelyekről hamarosan olvashatsz a blogon.

2013. február 21., csütörtök

Sonar - A metrikák értelmezése 1.

A múltkori Sonar bevezető után a következő néhány bejegyzésemben a Sonar által mért jellemzőkről és azok értelmezéséről fogok írni. Az analizálások során begyűjtött információk alapján nemcsak az aktuális állapotot, hanem a korábban mért adatokat is megtekinthetjük, így lehetőség adódik a jellemzők alakulásának, a trendeknek a követésére!

Ha a Projects Dashboard-on ráklikkelünk valamelyik projektre, akkor eljuthatunk a kiválasztott projekt alapértelmezett dashboard-jához, aminél a megjelenő widget-eket a Configure widgets menüponton keresztül állíthatjuk be. Érdemes megemlíteni, hogy a widget-ek módosításával nemcsak az aktuális projekt dashboard-ját, hanem az összes ugyanilyen típusú projekt dashboard-ját is módosítjuk! A felmerült igényeink szerint akár újabb dashboard-okat is létrehozhatunk és megoszthatunk másokkal is.


Lines Of Code, Classes

A Lines Of Code/Classes widget segítségével megtudhatjuk, hogy a kiválasztott projekt hány forrás fájlt, kódsort, osztályt, csomagot, vezérlési szerkezetet és metódust tartalmaz. Ezek a metrikák jól mutatják a projekt méretét, ami legtöbbször arányos a projektben előforduló hibák számával is. A méret metrika magasabb értékei tehát összetettebb projektet jelölnek! A tapasztalat azt mutatja, hogy a giga projektekkel szinte mindig komoly problémák vannak, így ezek külön odafigyelést igényelnek!

Violations, Violation Density

Talán ez az egyik leggyakrabban használt widget, amellyel 5 prioritási szintbe sorolva megtekinthetjük a projekthez rendelt Quality Profile-ban rögzített kódolási szabályok sértéseit. A prioritási szintekre kattintva esetleg a Violations Drilldown menüpontot kiválasztva az egyes szabálysértések szerint lefúrhatunk a forráskód szintjéig is, sőt ajánlásokat is kapunk a hibák kijavításához. A kódolási szabályoknál nem feltétlenül a kijelzett hibák számát kell mérvadónak tekinteni, hanem a blokkoló és kritikus szintű hibák típusait és mennyiségét, mivel ezek bírnak nagyobb jelentőséggel. Ebből következik, hogy a szabályok priorizálására mindig szánjunk időt!

Comments and Duplications

A Comments and Duplication widget komment oszlopa megmutatja a kommentezett sorok számát és a java dokumentációval ellátott API-k arányát, a duplikátum oszlop pedig nemcsak egy adott forráskódon belüli duplikált kódrészeket jelzi ki, hanem az adott projekt fájljai és más projektek fájljai közötti duplikációkat is!

A Sonar korábbi verziói a PMD-CPD (Copy Past Detector) –t használták a duplikációk kijelzésére, azonban a magas memória használat és a projektek közötti duplikátum keresés hiánya miatt egy Sonar-CPD modul kifejlesztése mellett döntöttek a Sonar fejlesztői, így a legfrissebb Sonar verzióban már ez az engine dolgozik.

A kommentek magas száma általában negatívumnak tekinthető (lásd Clean Coding Technikák írásomat) mivel a sok magyarázat arra utal, hogy a forráskód nem önleíró, annak megértéséhez további információk szükségesek. Mivel a kódok módosításával a kommenteket is mindig aktualizálni kellene (ami rendszerint elszokott maradni) ez a fejlesztőktől plusz munkát is igényel. A java dokumentációk megléte viszont pozitívumnak tekinthetők, mivel más fejlesztők gyorsabban megérthetik az API felhasználását. A kódbeli duplikációkat pedig mindenféleképpen érdemes megszüntetni, mivel ez a rossz kódolási szokás nagyon megnehezítheti a forráskód későbbi aktualizálását, ugyanis azt több helyen is végre kellene hajtani. A Sonar képes rámutatni a projekten belüli és a projektek közötti duplikációkra is, ami egy kifejezetten hasznos feature.

2013. január 5., szombat

Sonar - Figyelj a forráskód minőségére!

A stabilizációs projekt során feltárt hibákból jól látszott, hogy a problémák nagy része olyan programozói hibákra vezethetők vissza, amelyek manuális kód review-k nélkül is, statikus kód ellenőrzők használatával kimutathatóak lettek volna. Csak néhányat említve: System.gc() és Runtime.exec() hívások, túl szinkronizált metódusok, elfelejtett timeout-ok, tipikus naplózási problémák. 

 
Az ehhez hasonló problémák elkerülésére az automatizált kód ellenőrzés bevezetését és néhány környezetspecifikus kódellenőrző plugin elkészítését javasoltam. Mivel hatalmas a kódbázis és sok projekt kezelésére kellet megoldást találnom egy olyan eszközt kerestem, ami biztosítja az egységes konfigurációt és nézetet, lehetőséget ad a hibákhoz való lefúráshoz a kódban, biztosítja a hibák időbeli követését és persze nyílt forráskódú. A választásom egyértelműen a Sonar platformra esett, a statikus kód ellenőrzések automatizált meghajtásához pedig a Jenkins-t javasoltam.

A Jenkins-ről már korábban blogoltam, most pedig a Sonar következik! A Sonar egy nyílt platform a forráskód minőségének vizsgálatához, ami összefogja a legfontosabb kódanalizáló eszközök használatát (FindBugs, PMD, CheckStyle) és egy egységes felületet biztosít a használatukhoz. A külső kódanalizálók mellett a Sonar egy saját fejlesztésű Squid analizátort is használ, aminek a feladata az standard metrikák pl.: RFC, LCOM4, NOC meghatározása. 


Az analizálások eredményét és azok alakulását a webes felületen időben is nyomon követhetjük, ami kifejezetten hasznos amikor bizonyos metrikákra vonatkozó trend-eket akarunk vizsgálni. A rengeteg hasznos feature-ből érdemes kiemelni a projektekhez való szerep alapú hozzáférést, a projekten belüli és a projektek közötti kód duplikáció vizsgálatot, az eltérő kódanalizáló eszközök standardizált prioritási szint kezelését és a saját fejlesztésű komponensekkel történő kiegészíthetőséget is. 

A Sonar az alapértelmezett Java mellett többféle nyelv analizálását is támogatja (XML, JavaScript, C, C#, PHP, Python, PL/SQL, COBOL, VB6), valamint lehetőséget ad saját nyelvekkel való bővítésre is. A forráskódok elemzése kiterjed az architektúra és tervezés, kommentek, kódolási szabályok, potenciális bug-ok, kód duplikációk (akár projektek között is), unit tesztek, teszt lefedettség és a kód komplexitás követelményeire is. 

Működését tekintve a Sonar egy konténerben futó java web-alkalmazás, amit a Jenkins-hez hasonlóan standalone alkalmazásként is elindíthatunk. A webes felületen keresztül nincs lehetőség új projektek felvételére, azok a legelső analizálás lefuttatásakor kerülnek rögzítésre. A webes felület mellett a Sonar Eclipse plugin segítségével a fejlesztői gépeken is végrehajthatjuk a kódellenőrzést (távoli vagy lokális módban) így akár az IDE-ben is ráugorhatunk a hibás kódrészletre.
 

A kódanalizálást a Sonar Runner modullal (maven plugin, ant vagy java runner) indíthatjuk el felparaméterezve az adott típusú analizáláshoz szükséges információkkal. (1. lépés) A webes felületen definiált Quality Profile-ban előírt szabályokat felhasználva a 2. lépésben végrehajtódik a forráskódok analizálása, majd a 3. lépésben az analizálás eredménye beíródik a Sonar saját adatbázisába. Miután lefutott az analizálás, a Sonar webes felületén keresztül elérhetjük az analizálás eredményét, amit a Sonar a 4. lépésnél is jelzett adatbázisából fog kinyerni. Az aktuális analizálás eredményeit az Eclipse felületéről is elérhetjük a Sonar Eclipse plugin segítségével. (5.)

A Sonar részletesebb megismeréséhez érdemes átolvasni a dokumentációt, körbenézni a demo oldalon és az elérhető Sonar kiegészítők között!

2012. január 18., szerda

Jenkins - Folyamatos integráció kialakítása

A folyamatos integráció egy olyan fejlesztési folyamat, ami hatékonyabbá teszi a csapatmunkában történő fejlesztést azáltal, hogy a fejlesztők gyakran (legalább naponta) és már a kezdetektől összeillesztik (integrálják) a kódjukat. A Continuous Integration arra a felismerésre épít, hogy a fejlesztés során a kódok integrációja a legproblematikusabb fázis és hogyha ezt korán lekezeljük akkor az esetleges problémák is gyorsabban javíthatóak lesznek!

A gyakorlatban ez úgy néz ki, hogy a fejlesztők bekommittolják a forráskód módosításaikat a verziókezelőbe, amiről a CI szerver értesül így frissíti a helyi kódbázisát majd lebuildeli a kódot és lefuttatja a teszteket, végül a build terméket kiteszi egy teszt szerverre ahol is a tesztelők egyből elkezdhetik a legfrissebb verzió tesztelését! Hibás build esetén a fejlesztők automatikusan visszacsatolást kapnak (email, sms, rss), így lehetővé válik a gyors beavatkozás a probléma elhárítása érdekében. Az értesítések mellett a CI szerverek lehetőséget adnak a buildek és a tesztek sikerességének időbeli követésére is egy dashboard-on keresztül.


Az előbb ismertetett folyamat akár manuálisan is elvégezhető lenne, azonban a CI szerverek segítségével ez kiválóan automatizálható és valóban egy gombnyomással minden elintézhető!

A piacon többféle kereskedelmi és open source CI szerver is megtalálható, a legelterjedtebb azonban mégis a Hudson ill. Jenkins a rengeteg rendelkezésre álló plugin-nak köszönhetően! Amikor az Oracle felvásárolta a SUN-t, a Hudson core fejlesztői létrehozták a Jenkins projectet, így a hasonlóság miatt a két CI szerver üzemeltetése és a konfigurálása szinte teljesen megegyezik.

Nemrég az én feladatom volt a céges folyamatos integráció bevezetése, így a következőkben megosztok egy-két hasznos tanácsot ezzel kapcsolatosan:
  • Hudson helyett használjunk inkább Jenkins-t, mivel az eredeti Hudson csapat fejleszti, dinamikusabban fejlődik és néhány plugin csak a Jenkins alatt érhető el!
  • Definiáljuk a JENKINS_HOME ill. JENKINS_BASE környezeti változókat!
  • A Jenkins jobok futtatását igény szerint állítsuk be kommitonként vagy napi egyszeri futtatáshoz (éjszakai build).
  • Archiváljuk a sikeres build termékeket (EAR, WAR) majd tegyük letölthetővé egy-egy link formájában.
  • Automatizáljuk a tesztek futtatását. (unit teszt, felületi teszt, integrációs teszt, performancia teszt)
  • Automatizáljuk a kód lefedettségi riportok futtatását.
  • Használjunk statikus kód ellenőrzőket a kódolási hibák kijelzésére és a cégen belüli kódolási konvenciók betartásához. (findbugs, pmd, checkstyle)
  • Sikertelen build esetére állítsunk be automatikus e-mail értesítőt, így a fejlesztők azonnal értesülnek a hibákról.
  • A webalkalmazás teszt szerverre történő telepítése után, hajtsunk végre egy egyszerű Smoke tesztet a telepítés sikerességének ellenőrzéséhez.
  • Aktiváljuk és használjuk a Jenkins security lehetőségeit.

Amennyiben a napi egyszeri build mellett döntünk, a web-alkalmazás telepítését követően érdemes automatizáltan lefuttatni egy 2-3 órás JMeter tesztet hétköznapi terhelést szimulálva, majd a teszt végén kigyűjteni a heap dump és verbose gc napló állományokat, melyeket a build termékek mellé is kirakhatunk, így szükség esetén gyorsan elővehetjük és analizálhatjuk. Sőt, a verbose gc információkból akár egyből egy diagramot is legenerálhatunk amit szintén betehetünk a build termékek közé!

Végül következzen egy-két hasznos Jenkins és Hudson plugin, amelyeket érdemes feltelepíteni:

A Jenkins további megismeréséhez és részletesebb konfigurációjához a Jenkins: The Definitive Guide ingyenesen letölthető e-könyvet ajánlom!

2012. január 4., szerda

Hasznos Eclipse pluginok Java alapú fejlesztéshez

Már elég régóta használok Eclipse-t a fejlesztéshez és mostanra összegyűlt pár hasznos plugin amiket mindig telepíteni szoktam, így most összeírtam ezeket. Az Eclipse Marketplace (Help/Eclipse Marketplace...) bevezetése óta, szerencsére a szükséges pluginok összevadászása is gyorsan megvan, nem kell sokat keresgélni.
JBoss, Hibernate és JSF alapú projectek fejlesztéséhez elengedhetetlen. Segítséget nyújt a projektek generálásához, kódok kiegészítéséhez, EJB-QL lekérdezések real-time teszteléséhez, JBoss ESB és JBPM-es fejlesztésekhez is.

JRebel plugin
A blogom olvasóinak gondolom nem kell bemutatnom a JRebel-t, nélkülözhetetlen eszköz Enterprise Java fejlesztéshez! A hozzákapcsolódó plugin megkönnyíti a telepítést és a konfigurálást is.

Eclipse Memory Analyzer plugin
Az EMA egy kiváló Java Heap analizáló kiterjesztés, ami segít a memory leak-ek megtalálásában, továbbá hasznos információkat szolgáltat arról, hogy milyen objektumok foglalják a memóriát! A legújabb fejlesztéseknek köszönhetően nemcsak SUN-os, hanem IBM Java-s heap dump-ot is analizálhatunk vele.
Log fájlok nézegetésére szolgáló plugin, a console view-hez hasonló működéssel. Erre a pluginra akkor volt szükségem amikor egy Websphere 6.1-es fejlesztésénél áttértünk a RAD folytonos belassulása miatt Eclipse-re és ott is fontos volt a napló állományok kényelmes követése. A plugin bevált, azóta is ezt használjuk!

Java Decompiler plugin (JAD)
Ez a tool lehetőséget ad a Java class fájlok visszafejtésére és analizálására. Hasznos lehet ha elveszett Java forrásainkat akarnánk a class fájlokból visszaállítani vagy ha egy nem open sorce Java library osztályainak tartalmába szeretnék betekintést nyerni! Van egy standalone verziója is JD-GUI néven.

Subclipse plugin
A népszerű Subversion-hoz (SVN) készült Eclipse plugin. Már régóta használom, gond nélkül működik.

FindBugs plugin 
A FindBugs felfedi a kódban a gyakori programozói hibákat, bad practice-eket és javaslatokat ad a kijavításukra. Pl.: objektumok összehasonlítása == operátorral; hashCode() nélküli equals() metódus; stb... Létezik még egy findbugs-contrib kiegészítő is, ami további hiba detektorokkal egészíti ki a keresést. Érdemes mind kettőt telepíteni!
A findbugs-hoz hasonló, potenciális Java hibák keresésére szolgáló plugin, ami segít megtalálni a nem optimális kódokat, lehetséges bugokat és a túlkomplikált kifejezéseket is. Jól paraméterezhető és egyszerűen szűkíthető a detektálandó hiba típusok köre, továbbá lehetőségünk van saját hiba detektáló kiterjesztéseket is beilleszteni! A PMD pluginhoz tartozik még egy paraméterezhető Copy-Paste-Detector (CPD), amivel segíthetünk kiiktatni ezt a rossz programozói szokást! :)

Checkstyle plugin
Szintén egy statikus kód ellenőrző plugin, azonban ezzel a kódolási standardokra fogalmazhatunk meg szabályokat. pl.: maximum 500 soros lehet egy Java osztály, legfeljebb 3 paramétere lehet egy metódusnak; a paraméternevek nem tartalmazhatnak ékezetes betűket; stb... A PMD-hez hasonlóan ez is jól paraméterezhető és egyedi szabályokkal is bővíthető.
Különböző metrikákat tudhatunk meg a kódunkról és az osztályok közötti csatolásról, melyeket grafikusan is megtekinthetünk! pl.: Ciklomatikus komplexitás; kohézió, stabilitás, stb...
A CodePro Analytix szintén egy hasznos tool amit a Google fejleszt. Az előbb említett statikus kód analizálók tulajdonságait ötvözi és kiegészíti kód lefedettség ellenőrzéssel valamint automatikus unit teszt generálással is, bár ez utóbbit azért kezeljük óvatosan! A statikus kód analizálók közül ez tekinthető a legteljesebbnek.


Érdemes még megnézni az Eclipse Marketplace toplistát, mert rátalálhatunk egy-két hasznos kiegészítőre! A jövőben még bővíteni fogom ezt a bejegyzést, így a későbbiekben is látogass majd vissza! .)

2011. április 26., kedd

Clean Coding Technikák : A funkciók megvalósítása

A fejlesztés során az egyik legfontosabb szempont, hogy olyan kódot adjunk ki a kezünkből, amit bármely más programozó azonnal átlát és képes módosítani vagy továbbfejleszteni. A Clean Coding technikák használata pontosan ebben lesz majd a segítségünkre, azáltal hogy a kódunk öndokumentálóvá válik!

Jómagam elkötelezett híve vagyok ezen elveknek, így ebben a cikkben a funkciók megvalósításához ajánlott Clean Coding technikákat fogom pár példával ismertetni.

  1. Fejlesszünk úgy minden egyes metódust mintha egy történetet akarnánk elmesélni az adott domain kifejezéseit használva.
  2. Az alábbi metódusok működése egyszerűen kiolvasható a kódból, így bármely fejlesztő számára könnyen módosíthatóvá válik.
    private void loginUser(){
        checkUserCredentials();
        setupUserWebSessionInformations();
        modifyUserLastLoggedInTime();
        redirectUserToLoginPage();
      }
    
    private void makeTea(){
        if(kettle.isEmpty()){
         fillKettle(Liquid.Water);
        }
        plugTheKettleIntoThePowerPoint();
        switchTheKettle(Switch.On);
        placeTeaLeavesInTheTeapot();
        waitUntilTheWaterIsNotBoiling();
        switchTheKettle(Switch.Off);
      }
    
    
  3. Készítsünk rövid metódusokat. (max. 20-25 sor hosszú metódusok)
  4. Az if, else, for, while, try blokkok legyenek rövidek, kb. 1-3 sor hosszúak. Az ennél hosszabb kódrészeket szervezzük ki külön private metódusokba.
  5. A metódus mindig csak 1 dolgot csináljon, de azt jól!
  6. A funkción belüli műveletek mindig ugyanahhoz az absztrakciós szinthez tartozzanak. Az eltérő absztrakció szinthez tartozó kódrészt szervezzük ki külön osztályba ill. metódusba.
  7. Az alábbi példánál az incrementVideoViews() metódusnak nem kellene a HttpServletResponse-al és Cookie kezeléssel foglalkoznia, mivel ez már egy alacsonyabb szintű dolog. Az incrementVideoViews() szintjén csupán annyit kellene tudni, hogy a cookieManager szolgáltatását igénybevéve az addNewCookie() ezt majd elvégzi valahogyan, a konkrét megvalósítás azonban már a CookieManager felelőssége.
    public void incrementVideoViews(Video video){    
      ...
      Cookie cookie = new Cookie(
        video.getName(), video.getId() .toString());
      cookie.setMaxAge(cookieAge);
      cookie.setPath("/xyz");
      ((HttpServletResponse) facesContext
        .getExternalContext().getResponse())
        .addCookie(cookie);
    }
    
    A fenti kód helyett, inkább használjuk ezt:
    public void incrementVideoViews(Video video){
      ...
      cookieManager.addNewCookie(
        video.getName(),video.getId().toString(),cookieAge);
    }
    
  8. A metódus neve pontosan mutasson rá hogy mit is csinál.  Ha nem sikerül kifejező nevet adni, akkor rossz úton járunk. Ha csak hosszú nevet tudunk adni, akkor valószínűleg kisebb részekre kell bontatnunk a metódust.
  9. A metódusok névadásánál legyünk konzisztensek a domain fogalmaival és az eddig használt elnevezéseinkkel.
  10. Metódus argumentumok számának minimalizálása. Törekedjünk minél kevesebb argumentum használatára mivel rontják a kód olvashatóságát.
  11. public void createUser(String firstName, String lastName, int age, String address);
    
    Helyett:
    public void createUser(User newUser);
    
  12. Side Effectek elkerülése: Olyan műveletet ne végezzen a metódus, ami a metódus nevéből nem következik!
  13. A checkPassword() metódus nevéből senki sem gondolná, hogy akár egy session inicializálás is lefuthat.
    public boolean checkPassword(String userName, String password) {
       User user = findUserByName(userName);
       if (user != null) {
        if(PasswordHandler.checkPasswordMatch(
          password,user.getPassword()))
         Session.initialize();
         return true;
        }
       }
       return false;
      }
    
  14. Command And Query Separation: A metódus vagy változtassa meg az objektum állapotát vagy kérdezzen le valamit az objektumtól, de a kettőt ne tegye egyszerre.
  15. Az alábbi metódusnál vajon egyértelmű a visszatérési érték jelentése?
    public boolean reportError(Error error,List<User> admns);
    
    Sikerült a hibát bejelenteni? Legalább 1 adminisztrátort értesítettek? Minden adminisztrátort értesítettek? Javításként válasszuk szét a Command és a Query funkciókat:
    public void reportError(Error error,List<User> admns);
    public boolean isSuccessfulErrorReport();
    
  16. Kód duplikációk szigorú elkerülése.
  17. A kommentek számának minimalizálása! A sok komment használata rossz kódot jelent, mivel a kód ilyenkor nem önleíró. Törekedjünk arra, hogy maga a kód legyen a dokumentáció!

Ha tetszett a bejegyzés, akkor ne hagyd ki Robert C. Martin előadását sem a Clean Coding technikákról!



Amennyiben szeretnél többet megtudni a tiszta kódolásról, ajánlom az erről szóló könyvet is!  A véleményem szerint olyan időtálló gondolatokat tartalmaz, hogy egy fejlesztőnek sem hiányozhat a virtuális könyvespolcáról!