Skip to content

GhoUl

A Cubus Sapiens oldal

Archívum

Címke: eclipse

Kissé eltűntem az utóbbi időben. Szemlátomást továbbra sem tudom tartani azt a magamnak tett ígéretemet, hogy rendszeresen írjak ide valamit. Mivel várhatóan ez a jövőben is hasonlóan bizonytalan lesz (reálisabban gondolkozva: várhatóan ez a jövőben csak romlani fog :) ), most már nem is próbálkozok efféle ígérettel.

Miért nem írok? Szerencsére nem azért, mert nincs miről írni. Például az utóbbi időben szórakoztam olyan, Eclipse-közeli dolgokkal, amelyeket megérné leírni, hogy később ne a saját kódomból kelljen visszafejteni, miért úgy csináltam. :D Arról nem is beszélve, hogy esetleg másnak is hasznos lehet.

A gondolatot folytatva könnyen eljutok odáig, hogy miért (próbálok) írni. Azért, hogy a gondolataim egy részét összeszedjem. Elsősorban magamnak, de ha van még valaki, akinek segít, annál jobb. És igen, egy bejegyzés mindig kicsit rólam is szól. Még akkor is, ha abszolút technológiaismertető vagy hasonló az egész. Más kérdés, hogy ez írás közben gyakran az én fejemben sem tiszta. De ahhoz, hogy bármit leírjak, alaposabban végig kell gondolni, mint a használatához. És ez segít az én fejemben is egy kicsit rendet tenni. Szóval megéri.

Rövidre zárva: sírást befejezem, írás lesz még, vannak konkrét ötletek (például Command framework vagy buildelés kapcsán), csak idő/energia kérdése, hogy ebből valami kisüljön. Addig is jöjjön inkább egy aktuális, Eclipse fejlesztéshez kötődő tapasztalat.

Egy fejlesztői gép: 1000$. Egy Eclipse fejlesztési környezet: ingyen. A JDT hibaüzenet alapján kitalálni, hogy az EMF modell milyen változtatása nem került fel a repository-ba: megfizethetetlen.

Avagy részletesebben: az EMF egy borzasztó jól megtervezett és integrált környezet. Olyan Java kódot generál, hogy gyorsan kitalálható, pontosan mi változott a modellben… Az én mostani esetemben például a multiplicitás változott. A hibaüzenetekből és a generált Javadocból ez gyorsan kiderült. Bár van negatív oldal is, egy konkrét idézet a generált Javadocból, amivel már az EMF környékén mindenki találkozott:

If the meaning of the ‘Replicas’ containment reference list isn’t clear, there really should be more of a description here…

Ez annyira imádnivalóan undorító. :D Tényleg csak egy smiley hiányzik a szövegből, amin keresztül a számítógép kiröhög engem, mint felhasználót, hogy ezt megetted… Bárcsak eljutnánk odáig, hogy ne az ember legyen a számítógépért, hanem a számítógép az emberért…

Az előző kitérő után most térjünk vissza egy kis kocka témához. A minap érdekes felfedezést tettem, miközben véletlenül a GEF belső kódjába tévedtem debug közben. Alapvetően egyébként meg vagyok elégedve a GEF és általában az eclipse platform minőségével, ritka az az eset, hogy a fejemet fogom egy-egy megoldás láttán.

A következő kódrészlet ugyan működik és mivel a publikus api elrejti, az átlag fejlesztő nem találkozik vele, mégis érdemes rávetni egy pillantást. További szócséplés helyett következzék a kód, szerintem magáért beszél:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//AbstractEditPart.class

private Object[] policies;

//...

/**
 * @see EditPart#installEditPolicy(Object, EditPolicy)
 */

public void installEditPolicy(Object key, EditPolicy editPolicy) {
    Assert.isNotNull(key, "Edit Policies must be installed with keys");//$NON-NLS-1$
    if (policies == null) {
        policies = new Object[2];
        policies[0] = key;
        policies[1] = editPolicy;
    } else {
        int index = 0;
        while (index < policies.length && !key.equals(policies[index]))
            index += 2;
        if (index < policies.length) {
            index++;
            EditPolicy old = (EditPolicy)policies[index];
            if (old != null && isActive())
                old.deactivate();
            policies[index] = editPolicy;
        } else {
            Object newPolicies[] = new Object[policies.length + 2];
            System.arraycopy(policies, 0, newPolicies, 0, policies.length);
            policies = newPolicies;
            policies[index] = key;
            policies[index + 1] = editPolicy;
        }
    }
   
    if (editPolicy != null) {
        editPolicy.setHost(this);
        if (isActive())
            editPolicy.activate();
    }
}

Akinek nem világos elsőre, kifejteném a problémát: láthatóan egy tömböt használ a java-ban alapértelmezésként elérhető “Map” funkcionalitásának a kiváltására. A tömb páros (és nulladik) helyén szereplő elem tárolja a kulcsot, az utána lévő páratlan helyen lévő elem az érték.

Minden elem hozzáadásakor dinamikusan növeli a tömb méretét, törléskor meg egyszerűen null-ra állítja a tömb megfelelő elemét. Ehhez még társul egy custom iterátor is, ami a tömb nem null elemeit listázza.

Őszintén nem értem a tervezési döntést, ami a tömb-alapú Map-hez vezethetett. A memóriaigénye a HashMap-nek nem sokkal több, és mivel jellemzően kis elemszámú esetek fordulnak elő, ez nem számottevő. A sebessége a HashMap-nek jobb, a “get” és “put” metódusok általános esetben konstans, de mindenképpen kevesebb a tömb végigjárásánál. Mindennek a tetejébe a fenti kód Map alkalmazásával kb. 3 sorra cserélhető, nem beszélve az osztály egyéb kódjáról, ami a tömböt piszkálja.

Egyetlen érthető mentségként csak arra tudok gondolni, hogy esetleg a kód korábban íródott, minthogy a java collections API-ba belekerült volna a Map. Ez viszont az 1.2-es verzióban történt meg, tehát elég régen. Nem tudom mennyi idős a GEF, így ezt nem tudom eldönetni.. Mindenesetre ez a kód nálam megütötte a WTF szintet.

Elég sok időt töltöttem már azzal, hogy Java 6 alapú fejlesztést lehessen végezni Eclipse-szel és OSX-szel.

Az alapprobléma az volt, hogy egyszerre kellett 32 (az SWT Carbon API-ja kötelezően 32 bites) és 64 biten dolgozni (mert a Java 6 kötelezően 64 bites). Szerencsére ezt a fejlesztők is belátták, és elkészítették az SWT Cocoa portját (ami mellesleg nem lett rossz, de ez nem ennek az írásnak a témája).

Eredmény: némi varázslás után 3.5-ös Eclipse-szel lehetett Java6-ra fejleszteni (a varázslás nem ártott, de erről szintén nem most írok). De ez bizonyos esetekben nem elég. Például, ha az ember kénytelen 3.4-es Eclipse-szel is kompatibilis maradni, és ezt még ellenőrizni is szeretné.

Ez, és az apróbb problémák a 64 bites Java 6-tal győztek meg végül arról, hogy kb. egy hónappal a megjelenés után frissítsek Snow Leopardra. Elvégre abban van 32 bites Java 6 is, ezért elvileg mennie kellene a dolognak. Sőt, csak Java 6 van a rendszerben, szóval ez még jobb.

Na, Ganymede indul, szépen megy is. Összegyűjtöm a tesztelendő projekteket, és indítanám a runtime workbenchet, mire közli velem, hogy a Carbon SWT nem működik a 64 bites JVM-en. No comment.

A szokásos trükkjeimet ilyen esetekre végigpróbáltam, eredmény teljes kudarc, mígnem  az ESE konferenciáról beszámoló blogbejegyzés kommentjében Kevin Barnes leírja a megoldást: a VM-nek a -d32 paramétert átadva 32 bites JVM-et indít.

És ez tökéletesen működött. Szuper.

PS.: jellemző Eclipse probléma, hogy a megoldás triviális, csak megtalálni nehézkes. De legalább most már tudom, hogyan lehet 32 bitre force-olni a JVM-et.

Némi bugfixing kapcsán eljutottam egy Eclipse plug-in belsejében a JFace ListDialog osztályhoz.

A dolog lényege, hogy egy definiált listát megjelenít, és lehetővé teszi a felhasználó számára az elemek kiválasztását egy dialógusban. Az ötlet jó, hiszen viszonylag gyakran szükséges feladat.

Ugyanakkor egy érdekes adalék a működéséhez: működik az a hasznos (és elvárt) funkció, hogy a lista elemei dupla kattintással történő kiválasztása működik. Feltéve, hogy a Mégsem gomb is engedélyezve van… :D No comment. (Ld. még Bug 292576).

Volt ma egy szép debug köröm. Nagyon nem értettem, miért nem működik egy kód – ami ráaásul régebben (július végén) szépen ment, és azóta nem nyúltam hozzá, és elvileg a kapcsolódó libekben sem volt lényegi változás azóta.

Úgy gondolom, bemutatom a kódot, és felteszem a kérdést, látja-e más is a hibát benne.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private ICoreNotificationObject notificationObject;

public void actionPerformed(ICoreNotificationObject notification) {
  this.notificationObject = notification;
  Display.getDefault().aSyncExec(new Runnable() {

  public void run() {
    String action = notificationObject.getActionType();
    if (isOneOf(action, new String[] {
          ICoreNotificationObject.TA_TRANSACTION_END,
          ICoreNotificationObject.TA_UNDO_END,
          ICoreNotificationObject.TA_SUBTRANSACTION_END})) {
       updateGraph();
       transactions.pop();
    } else if (isOneOf(action, new String[] {...}){
       //...
    }
  });

Még némi információ a kód működéséről: a kód egy eseményfigyelő osztály belsejében van, és a Runnable adatváltozásokat próbál követni, amely tranzakciókba van szervezve, ill. a tranzakciók során visszavonás események is érkezhetnek.

Na, kinek van tippje, mi lehet a hiba? Ha nincs tipp véges időn belül (előre nem specifikálnám), akkor majd megosztom a helyes megfejtést. Annyit mondok előre, hogy fejet falbaverős hiba :D .

Update: először is helyesbítettem a kódot, mert sikeresen a javított változatot töltöttem fel.

A problémát az okozta, hogy az asyncExec() hívás indított egy új jobot, amit valamikor majd végrehajt. Csak közben visszaadja a vezérlést, és ezzel lehetővé teszi a rendszer számára, hogy felülírja a run() metóduson belül is használt notificationObject változót.

Az asyncExec() hívás syncExec()-re cserélése megoldotta a problémát, ugyanis az megvárja, hogy visszatérjen a meghívott thread.

Ez a hiba kifejezetten mocskos dolog, mert eredetileg működött, míg a környezet refactoringja előhozta a bugot…