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.