tifyty

pure Java, what else ?

Havi archívumok: november 2013

javax0 a top 100-ban a programcreek szerint

Ez itt a reklám helye.

A javax0 blogomat (ami kb. a tifity-nek felel meg, csak angolul) a Programcreek az első 100 Java blog közé sorolta, nem tudom milyen sorrend szerint, de olyan impozáns társaságba kerültem, mint Martin Fowler. A blog cikk itt érhető el:

100 High-Quality Java Developers’ Blogs

semver

Szoftvereket készítünk, és minden szoftverből készítünk különböző verziókat. A legegyszerűbb verziókezelést egy kis magyar KFT-nél láttam, diszkrécióm és szenilitásom megakadályozza, hogy megmondjam a nevét. Itt a verziózás abból állt, hogy volt egy aktuális forráskód állapot, az ami a fejlesztő notebook-ján volt, és abból build-eltek, és az a verzió volt. Ez a megoldás sok sebből vérzik, talán magyaráznom sem kell.

Aztán vannak más verzió kezelési módszerek és sémák is, amiket legjobban talán a Wikipédia szoftver verziókról szóló oldala foglal össze. Nekem a TeX és a METAFONT programok verzió számozása tetszik a legjobban. A TeX harmadik verziója után a 3.1-est adták ki, aztán a 3.14-at, majd 3.141-et és így tovább. Most valahol a 3.1415626 környékén járnak, és Donald Knuth szerint halála után az utolsó update a verzió beállítása pi-re lesz az utolsó verzió. A METAFONT struktúrája ugyanez, de ott a határérték e.

Ez a verzió számozás azonban a legtöbb esetben nem praktikus. A TeX és a METAFONT programok életciklusa nem nevezhető igazán szokásosnak. Sokkal célra vezetőbb a valamilyen a.b.c.d.e.f… alakú verziózás. Persze ebben sem szabad túl messzire menni. A tapasztalat azt mutatja, hogy két verzió szám gyakran kevés, négy meg általában már felesleges, így megragadunk a a.b.c alaknál. Az egyes verziószámok jelentése pedig major verzió, minor verzió valamint patch, vagy bug-fix verzió szám. Ezek persze csak elnevezések, a pontos jelentés az, amit a fejlesztő akar, de javasolt, hogy ebben is legyen valami egységesség, hiszen nem attól lesz valaki jobb, vagy rosszabb fejlesztő, hogy valami szuper új verziózási sémát talál ki. Nem lehetetlen, de elég kicsi a valószínűsége. A mostanában elfogadott szokás, vagy ha úgy tetszik szabvány a szemantikus verziózás, semantic versioning.

Formailag a leírás olyan, mint egy szabvány, és részletezettségében, precizitásában is. Ezt a verziózást és a verziószámok ilyetén használatát nem a felhasználói programok számára javasolja. Ott sokkal fontosabb a marketing, és olyan verzió “számok” működnek, mint Vista, Maverick vagy Blue Death. A szemantikus verziózás a library-k esetében fényleg magasan, és ott látszik az igazi nagy haszna. A library-k esetében a felhasználó programok szemantikus verziózás esetén támaszkodhatnak a szabványban leírtak. Mindjáűrt meglátjuk, hogy hogyan is.

A szabvány azt mondja, hogy ha egy library verziója M.m.p ami a Major, minor és patch (a Major azért van nagybetűvel írva, mert a jelentése nagy, és mivel pont mint a minor ‘m’-mel kezdődik, nehéz lenne tudni, hogy m, mint major vagy m, mint minor), akkor minden olyan változás, ami a kompatibilitást visszafelé sérti, az a Major verzió növekedését kell, hogy maga után vonja. Ha tehát van egy alkalmazásod, a melyik a log4j 1.1.3 verzióját használja, immár 12 éve, akkor ne várd, hogy csak a könyvtárat kicserélve a loj4j 2.0-beta9-re minden működni fog. (Amúgy ez a verziózás nem is felel meg a semver-nek, mert 2.0.0-beta9 lenne a helyes forma.)

Ha csak olyan módosítás történt a programban, amelyik visszafelé kompatibilis, azaz az újabb verzió valószínűleg használható a programodban a régi helyett, de fordítva nem biztos. Vagyis valószínűleg áttérhetsz a log4j 1.2.11-es verziójára, de az 1.0.4-re visszafelé nem biztos, mert lehet, hogy használsz olyan funkciót, api-t, ami abban még nem volt meg. Viszont az 1.2.11-esnek minden olyan funkciót támogatnia kell ami az 1.1.3-ban benne volt, és pontosan ugyan úgy, ahogy az 1.1.3-ban volt, kivéve, ha az adott működés, amit kihasználsz egy bug eredménye. (Na ezért kell a dokumentáció, mert különben mi különbözteti meg a bug-ot a feature-től? Az, hogy a feature le van dokumentálva, hogy így lett kitalálva, a bug meg csak van.)

A patch verziót akkor növeljük ha semmi új nincs a funkcionalitásban, csupán hibát javítottunk.

A verziószámokat mindig növeljük, és ha M-et növeljük, akkor m.p 0.0 lesz, ha m-et növeltük akkor p lesz 0, M marad ami volt. A verziók sorrendje értelemszerűen balról jobbra történik, tehát 1.2.3 korábbi verzió, mint 2.1.1, ami korábbi, mint 2.2.0 ami viszont későbbi, mint 2.1.56413

A növelés mértéke általában 1, de ha egy release készítés elromlik valamiért, de valami azzal a, mondjuk 1.5.3 verziószámmal mégis kikerül, akkor semmi baj: kiadunk egy új verziót, ami az 1.5.4 lesz és dokumentáljuk, hogy az 1.5.2 után az 1.5.4 jön, az 1.5.3 pedig mintha nem is lenne, még akkor is, ha ezzel a verzióval látszik is valami a nexus centrálban, vagy bárhol is. Ez egyébként a gyakorlatban éppen tegnap fordult elő velem, 22 perces build során kedves kolléga belekommittált a release branch-ba, így a release nem tudta visszaírni konfliktus okán az új development verziót és törött, mint a háromezer éves kínai váza a rossz filmekben.

Ezen kívül a szemantikus verziózás megengedi, hogy - után olyan pre-release verzió jelöléseket adjunk meg, mint alpha1, alpha2, beta9 és itt a - utáni részben is lehetnek pontok. A sorrendiség szempontjából ilyenkor is balról jobbra történik az összehasonlítás, ASCII sorrendben. Azaz 1.1.0-1 korábbi, mint 1.1.0-alpha. A verzió szám végére + után oda lehet tenni build információkat is, de két verzió nem szabad, hogy csak ebben különbözzön, és két verzió időbeli sorrendjében sem vesz részt.

Milyen verziót használjunk fejlesztés közben?

A maven hagyományok szerint a fejlesztés során a.b.c-SNAPSHOT alakú verziót használunk, és release készítés során lesz ebből a.b.c. Vagy a.(b+1).0. Vagy (a+1).0.0. Mert lehet, hogy kiadtuk a 1.0.0 verziót, és amit szerkesztünk az épen a fejlesztő környezetben az 1.0.1-SNAPSHOT néven fut, de lehet, hogy rögtön a 2.0.0-t fogjuk kiadni. De az is lehet, hogy az 1.1.0-t. Vagy az 1.0.1-et. Más nem nagyon lehet a szemantikus verziózás szabályai szerint. Vagy felkészítjük a release folyamatot arra, hogy ezt kezelje, vagy két lépésre vágjuk a dolgot, és az a.b.c készítése előtt a forráscsomagban átírjuk a verziót a.b.c-SNAPSHOT-ra, ha nem az lett volna. Az első megoldás összetett release folyamat, például három nagyon hasonló TeamCity projekt, amelyek csak abban térnek el, hogy milyen verzió számot generálnak a fejlesztői verzióból. A második verzióban viszont manuális verzió szerkesztgetés történik, ami hibaforrás, legalábbis peer review-t kíván (jobb helyeken, bocs).

Persze el tudom fogadni azt az érvelést is, hogy amikor fejlesztesz, akkor a release előtt valamivel már tudni fogod, hogy milyen verziót fogsz kiadni, és akkor illik átírni, hogy a release processznek csak a -SNAPSHOT részt kelljen levágnia. Valóban nem a release folyamán derül ki, hogy ami a kódban van az csak hibajavítás, visszafelé kompatibilis új lehetősége, vagy teljesen új szoftver verzió.

És van, amikor a szemantikus verziókezelés sem segít.

Egy projektben egy régebbi verzióban hiba van. Mondjuk a verzió 7.2.0. Nosza gyorsan, ügyfél AAA Bt. kér egy hibajavítást, és lesz 7.2.1. De közben kiderül még egy hiba is, egy másik, BBB Kft. ügyélnél és így azt is ki kell javítani a 7.2.0-ban. No akkor most mi lesz? Lesz egy 7.2.1a meg egy 7.2.1b ? Mert az ‘a’ hiba a BBB Bt-nél nem okoz gondot. Nem csak nem jött elő, de ők olyan módon használják a szoftvert, hogy az ‘a’ hiba abszolúte semmi gondot nem okozhat. A javítása, viszont potenciálisan hibaforrás, ezért ők nem akarnak egy 7.2.1-re épülő 7.2.2 verziót használni. Ők csak arra a hibára akarnak egy javítást, amelyik a ‘b’ hibát javítja a 7.2.0 verzióban. Hasonlóan van ezzel az AAA Bt. is.

Itt véget ér a szemantikus verzió határa, mert ha kiadjuk a 7.2.1-et és kiadunk egy 7.2.2-t, akkor implicite azt kellene jelentse, hogy ami hiba kijavult a 7.2.1-ben az a 7.2.2-ben sincs benne. Ilyenkor jöhetnek a szemantikus verzió által megengedett mínusz és valami, mint a 7.2.1-AAA.bt meg a 7.2.1-BBB.kft verziók, amik… hát nem szépek, de semmi sem tökéletes, nincs bölcse köve, ami mindent megoldana.

Ha az informatikában mindenre lenne egyszerű megoldás nem lenne szükség informatikusokra.

Ismered enumot?

Egyszerű kérdés: mit ír ki a program:

package enumTest;
public class EnumElementsClass {
    enum A {
        A01{},
        A02,
        A03
    }
    public static void main(String[] args) {
        System.out.println(A.A01.getClass() == A.A02.getClass());
        System.out.println(A.A02.getClass() == A.A03.getClass());
    }
}

Ha rájöttél, hogy mi van ott az A01 után, akkor már meg is tudod válaszolni.

Találós kérdés

A következő kérdésre szerintem senki nem tudja kapásból a választ, és … talán nem is érdemes tudni. Végül is a Java nyelvet használni akarjuk, és nem minden apró marhaságát részletesen ismerni. De azért gondolkodósnak itt a találós kérdés:

Van valahol egy metódus, amelyik public, és minden olyan struktúra, amelyik körülötte van szintén public. A fordító mégsem engedi, hogy meghívjuk, csak reflection-ön keresztül lehet meghívni, és nem is kell beállítani a setAccessible(true)-val az elérhetőséget, mert hát valóban public.

package enumTest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class NonFinalEnumTest {
    public enum A {
        A01 {
            public void println() {
                System.out.println("This is println in " + this);
            }
        },
        A02;
    }
    public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        A a = A.A01;
        Method m = A.A01.getClass().getDeclaredMethod("println", (Class[]) null);
        //m.setAccessible(true);
        m.invoke(A.A01, null);
    }
}

Ezek a kérdések úgy keletkeznek, hogy fejlesztés közben belefutok valamibe, amit először nem értek, aztán utánaolvasok, és akkor rájövök, hogy milyen ocsmány csűrcsavarokat lehet még csinálni, és utána kitalálom, hogy ebből hogyan lehet jó fejtörőt készíteni. Mindenki azt csinál hobbiból amit szeret, meg tud. Van aki keresztrejtvényt fejt, van aki meg tehenet.

Amikor kitalálok egy egy ilyen kérdést, akkor persze többnyire nem egy jön össze, és akkor ezeket elrakom, és amikor nincs időm hétvégén sem, akkor szerdán csak rákattintok, hogy “publish”. 2013. októberben így jött ki a négy Java Horror cikk is, egyéb idő hiányában (amikor ezt írom, épp a harmadikat publikáltam, és lesz/volt még egy). És ez a cikk is megvan már vagy hat hete a forráskódig bezárólag. És most kinyitottam, mert fogalmam nem volt a válaszról, és vagy két percig néztem, mire rájöttem, hogy mit hogy és miért. Pedig én írtam a cikket másfél hónapja. (És még annyi lesz kb. mire kikerül.)