Európa-fogyatkozás

Elég hamar kicseréltem az Eclipse 3.3 Europa integrált fejlesztői környezetemet az új verzióra. Hamarabb, mint eredetileg terveztem. Ennek annyi volt az oka, hogy letöltöttem az M6-os release-üket kipróbálásra, és amikor meghalt a rendes fejlesztői környezetem (volt 900 MB telepítve, rendes kis pluginkönyvtár :p ), úgy döntöttem, megspórolom az egész újratöltését, és inkább berakom a Ganymede-be a cuccokat.

Nem mondom, akkor még korai volt egy kicsit (de csak egy kicsit), nem volt (sokkal) nagyobb szívás ott újra összerakni a munkakörnyezetet, mint az Europa-ban lett volna. Mostanra meg már, hogy van végleges 3.4-es változat, nem mennék vissza.

Elég hamar kicseréltem az Eclipse 3.3 Europa integrált fejlesztői környezetemet az új verzióra. Hamarabb, mint eredetileg terveztem. Ennek annyi volt az oka, hogy letöltöttem az M6-os release-üket kipróbálásra, és amikor meghalt a rendes fejlesztői környezetem (volt 900 MB telepítve, rendes kis pluginkönyvtár :p ), úgy döntöttem, megspórolom az egész újratöltését, és inkább berakom a Ganymede-be a cuccokat.

Nem mondom, akkor még korai volt egy kicsit (de csak egy kicsit), nem volt (sokkal) nagyobb szívás ott újra összerakni a munkakörnyezetet, mint az Europa-ban lett volna. Mostanra meg már, hogy van végleges 3.4-es változat, nem mennék vissza.

Ami a leghasznosabb újdonság számomra, az a megújult csomagkezelő. Egyrészt az az előnye, hogy nem kell előre eldönteni, hogy én most új csomagot akarok telepíteni, vagy a meglevő csomagokon akarok valamit módosítani (korábban idegesítő volt, amikor a rossz menüpontra kattintottam, és egy percig várhattam, amíg megnézte, hogy mit lehet csinálni), ugyanis két almenüpont helyett egy közös dialógusablakból lehet változtatni két fül között – ezek a fülek tartalmazzák a korábbi funkcionalitást. Legalábbis nagyrészt.

Ami úgy tűnik számomra, hogy hiányzik, az a korábbi változatban Select required nevű gomb a telepítendő csomagok választásánál. Lehet, hogy már nincs rá szükség, mert automatikusan bejelöli (nem vagyok benne egészen biztos, ezért ezt nem merem elítélni). Amiben biztosabb vagyok, az a csomagok eltávolítása. Ezt még egyáltalán nem sikerült az új verzióban véghezvinni.

Ami viszont roppant hasznos új funkcionalitás, az a dropin mappa koncepciója. Ez egy kijelölt mappa, amibe ha bedobunk letöltött csomagokat, akkor azokat a csomagkezelő látja, és a függőségeivel együtt telepíthetőek. Ez nagyon hasznos lehet akkor, ha valami olyan projektet akarunk telepíteni, ami valami miatt nem szerepel az Eclipse csomagkezelőben.

Egy másik apró változás, amivel találkoztam, az az Eclipse pluginek (illetve RAP programok) fejlesztésekor jött elő: bizonyos fájlokat megnyitva az Eclipse nem jön rá, hogy én egy megadott futtatási konfigurációval akarom futtatni egy automatikus futtatás parancs kiadásakor, hanem kézzel kell megjelölni. Az Europaban erre nem találtam alternatív megoldást, de a Ganymede sokkal kevésbé idegesítő. A problémát ugyen nem oldották meg, de legalább megkerülték: első futtatáskor lehet, hogy ki kell választanom a futtatási konfigurációt, de utána megjegyzi, és nyugodtan futtatja úgy is.

Az Eclipse 3.4 lesz Ubuntuhoz is – az Europa nem volt, mert valami ütközött az SWT-ben és az Ubuntuban, és ezt csak az új verzióra javították ki; ezért is van az, hogy még az LTS kiadásban is csak a két éves 3.2 (Callisto) került be. Nem tudom, hogy végül is kijavították-e, mert per pillanat nincs lehetőségem ezt ellenőrizni, de valamikor majd ezt is meg lehet tenni. Ez a probléma egyébként akkor merült fel, amikor Balage a leírásom alapján telepíteni próbálta az [[PHP debug Eclipse PDT-ben|Eclipse PDT-t debuggerrel]].

Apropó PHP debugger: a PDT projekt nincs szinkronizálva az Eclipse kiadásokkal, ebből még nincs hivatalosan kiadott verzió (sem pedig update site). De a dropin megoldás segítségével könnyen telepíthető a rendszerbe, és utána minden gond nélkül megy. A php debugger is szépen megy.

Viszont még egy negatívumról is írnék: az SWT widget-készlet még mindig Carbon-alapú OSX alatt, ami nem jó hír. Java 6-tal nem megy, mert az OSX-en kötelezően 64 bites, míg a Carbon 32. De szerencsére már elkezdődött a Cocoa-alapú változat fejlesztése, ha minden jól megy, a következő kiadásba már be is kerülhet. Ha ez tényleg így lesz, akkor lehet, hogy megint kiadás előtt fogok váltani. De ez majd kiderül. 🙂

Szóval egy hasznos új változatról van szó, nagyon forradalmi változás nincs benne, de megfelelő továbbfejlesztése a népszerű IDE-nek. Úgy gondolom, tele lehet még az előzőekhez hasonló apró változtatásokkal, de ezek felismeréséhez nem használtam eleget a korábbi változatokat, ezért nem kívánok most róla írni. Majd esetleg máskor. Mindenesetre bárki számára javaslom a verziófrissítést, ha nincs túlságosan előrehaladott állapotban egy projektjében, mert akkor kellemetlen lehet a váltás. De érdemesnek érdemes szerintem, nem sok helyen van inkompatibilitás a következő verziókkal. Az egyedüli gond az lehet, ha valami szükséges plugin nem érhető el az új változatban.

De ha valakinek nincs valami nagyon különleges igénye, akkor nyugodtan lehet frissíteni, szépen megy az új Ganymede is.

Java osztályok tárolása MySQL adatbázisban

A cím talán megtévesztő lehet, most nem arról van szó, hogy hogyan lehet szérializált osztálypéldányokat tárolni adatbázisban. Ehelyett magukat a Java osztályokat (típusokat) tárolom ilyen módon. Ez megtehető, hiszen a Java futásidőben tölti be az osztályokat, egészen pontosan az osztály első aktív használata elött. A továbbiakban a cikk feltételezi a Java és a MySQL alapszintű ismeretét, és a [[http://java.sun.com/javase/technologies/database/|JDBC]] használatát se fogom részletezni. Kezdetnek essen pár szó a Java osztálybetöltő mechanizmusáról.

A cím talán megtévesztő lehet, most nem arról van szó, hogy hogyan lehet szérializált osztálypéldányokat tárolni adatbázisban. Ehelyett magukat a Java osztályokat (típusokat) tárolom ilyen módon. Ez megtehető, hiszen a Java futásidőben tölti be az osztályokat, egészen pontosan az osztály első aktív használata elött. A továbbiakban a cikk feltételezi a Java és a MySQL alapszintű ismeretét, és a [[http://java.sun.com/javase/technologies/database/|JDBC]] használatát se fogom részletezni. Kezdetnek essen pár szó a Java osztálybetöltő mechanizmusáról.

A Java ClassLoader osztály használható arra, hogy egy keresett osztályt betöltsön a memóriába. Minden JVM indulásakor létrejön egy alapértelmezett rendszer ClassLoader, és ha nem teszünk semmit ez marad végig az egyetlen ilyen jellegű objektum a JVM-ben. A ClassLoader-ek működésének megértéséhez meg kell ismernünk néhány metódusát:

  • A konstruktor: ClassLoader(ClassLoader parent) : Minden ClassLoader-nek létezik pontosan egy szűlője (kivéve az alapértelmezett ClassLoader-t), ugyanis mielött az adott ClassLoader megpróbálná betölteni a keresett osztályt, megkérdezi a szülőt, hogy ő be tudja-e tölteni, és csak akkor próbálkozik meg, ha a még nincs betöltve, és a szülő sem tudja betölteni.
  • Class loadClass(String name) :ez hívódik meg, amikor felmerül az igény egy osztály betöltésére. Ez a metódus elöször meghívja a szülő ClassLoader hasonló metódusát, és ha az ClassNotFoundException-t dob, akkor meghívja ezen osztály findClass metódusát.
  • Class findClass(String name) :a fentiekből látszik, hogy ez a metódus hívódik meg akkor, ha már ránk hárult az osztály betöltésének a feladata. Ha új ClassLoader-t írunk, rendszerint ezt a függvényt akarjuk felülírni.
  • Class defineClass(String name, byte[] b, int off, int len) :ez a lényeg. Ez a függvény hozza létre az osztályt a memóriában a beolvasott bytecode-ból, aminek meg kell egyeznie egy “.class” fájl tartalmával.

Már látszik a turpisság: a fentiek alapján lehet írni egy ClassLoader-t, ami nem fájlból, hanem adatbázisból olvassa be a megfelelő bytesorozatot. Első lépésként írjuk meg az adatbázis réteget, ami a kérdéses bytecode-ot tudja menteni/kiolvasni az adatbázisból (az adatbázisba a bytecode-ot Base64 kódolásban mentem, amit egy segédosztály végez. Lásd: lent. Ugyanúgy nem térek ki a File beolvasására, annak a megoldásában is a Google segített):


package mysqlclasses;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

public class ClassSaver {

public static byte[] getBytesFromFile(File file) throws IOException {
.....
}

public static void saveToDB(Connection con, String name, byte[] data){
try{
Statement s = con.createStatement();
s.execute("CREATE TABLE IF NOT EXISTS classes (name VARCHAR(120) NOT NULL,bytecode TEXT)");

String bytecode = new String(Base64Coder.encode(data));

ResultSet rs = s.executeQuery("SELECT * FROM classes WHERE name = '"+name+"'");
if (rs.next()){
//update
s.execute("UPDATE classes SET bytecode = '"+bytecode+"' WHERE name = '"+name+"'");
}else{
//insert
s.execute("INSERT INTO classes SET name = '"+name+"', bytecode = '"+bytecode+"'");
}
}catch(Exception e){
System.err.println(e.getMessage());
}
}

public static byte[] loadFromDB(Connection con, String name){
try{
Statement s = con.createStatement();
ResultSet rs = s.executeQuery("SELECT * FROM classes WHERE name = '"+name+"'");
if (rs.next()){
return Base64Coder.decode(rs.getString("bytecode"));
}else{
return null;
}
}catch(Exception e){
System.err.println(e.getMessage());
return null;
}

}
}

Ezek után következzék a MySQLClassLoader:


package mysqlclasses;

import java.sql.Connection;

/**
* @author balage
*
*/
public class MySQLClassLoader extends ClassLoader {

Connection con;

public MySQLClassLoader(Connection sqlcon) {
con = sqlcon;
}

public MySQLClassLoader(Connection sqlcon,ClassLoader arg0) {
super(arg0);
con = sqlcon;
}

@Override
protected Class< ?> findClass(String name) throws ClassNotFoundException{
System.out.println("Searching for class: "+name);
byte[] data = ClassSaver.loadFromDB(con, name);
if (data != null){
return defineClass(name, data, 0, data.length);
}else{
throw new ClassNotFoundException();
}
}

}

Meglepően egyszerű, nemde? A kipróbálásához létrhozunk pár egyszerű osztályt:

package test;

public interface Test {
public void some();
}


package test;

public class TestA implements Test {

@Override
public void some() {
System.out.println("I'm an A instance.");
}

}


package test;

public class TestB implements Test {

@Override
public void some() {
System.out.println("I'm a B instance.");
}

}

Az egyszerűbb tesztelés érdekében hoztam létre egy Interface-t is, ami fordítás időben ismert, így egyszerűen meghívható a some() metódus. A létrehozott osztályokkat most le kell fordítani, és be lehet írni az adatbázisba:


Class.forName("com.mysql.jdbc.Driver");

Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "balage", "");

Test a = new TestA();
a.some();
Test b = new TestB();
b.some();

ClassSaver.saveToDB(con, a.getClass().getName(), ClassSaver.getBytesFromFile(new File("bin/test/TestA.class")));
ClassSaver.saveToDB(con, b.getClass().getName(), ClassSaver.getBytesFromFile(new File("bin/test/TestB.class")));

Ezen a ponton érdemes ellenőrizni az adatbázis tartalmát, ahol jól láthatóan szerepel egy “classes” tábla, benne a két osztály bytecode-jával. Ezek után lehet kiovasni onnan őket a következő kóddal (Arra vigyázzunk, hogy a kiolvasandó osztályok NE legyenek elérhetőek a classpath-ban, különben az adatbázis helyett az alapértelmezett ClassLoader fogja őket betölteni!):


Class.forName("com.mysql.jdbc.Driver");

Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "balage", "");
MySQLClassLoader loader = new MySQLClassLoader(con);

Class< ?> clsa = loader.loadClass("test.TestA");
Test a = (Test)clsa.newInstance();

a.some();

Ha mindent jól csinálunk, a kimeneten a TestA osztály üdvözlőüzenete elött látni fogjuk a MySQLClassLoader-ben elrejtett kiírást is, ami egyértelműen jelzi, hogy valóban ő olvasta be az osztályt. Siker. Mielött még kikiáltanám a cikk végét, had tegyek pár megjegyzést, ami szükséges lehet a módszer komolyabb használatához:

Jól láthatóan szövegként hivatkozunk a betölteni kívánt test.TestA osztályra. Ekkor felmerül a kérdés, mi van azokkal az osztályokkal, amikre a betöltött test.TestA osztály hivatkozik? Például létrehoz egy test.TestB osztályt, ami ugyancsak az adatbázisból érhető el, és nem szerepel a megadott classpath-ban. A válasz egyszerű. Azokat akkor próbálja betölteni, amikor aktívan használni próbáljuk. Akkor az osztály nevét átadja az alapértelmezett ClassLoader-nek, ami megkeresi a classpath-ban és.. ClassNotFoundException. A probléma ott van, hogy arra már nem a mi MySQLClassLoader-ünket használja. A teendő tehát: alapértelmezetté kell tenni a létrehozott MySQLClassLoader-t, amit megtehetünk az aktuális Thread ismeretében:


MySQLClassLoader loader = new MySQLClassLoader(con);
Thread.currentThread().setContextClassLoader(loader);

Ezen sor után a futásidőben felmerült osztályhivatkozásokat már az általunk megadott ClassLoader-rel fogja betölteni. Kiemelném, hogy ekkor is létezik a MySQLClassLoader szűlője, ami prioritást élvez, azaz mindig az próbálja meg betölteni a keresett osztályt elsőként (persze csak akkor, ha nincs még betöltve).

Még egy probléma: az általunk létrehozot ClassLoader-t csak futásidőben tudjuk használni, a javac fordításkor csak a beépített ClassLoader-t tudja használni, azaz fordításkor minden hivatkozott osztálynak kéznél kell lennie, csak fordítás után lehet az egyes osztályokat bepakolni az adatbázisba. Ami után viszont az adatbázisból hivatkozott .class fájlok törölhetőek a classpath-ból. A programunkat indító osztálynak, és a MySQLClassLoader-nek viszont minden esetben elérhetőnek kell lennie a classpath-ban, ugyanis a java indulásakor csak az alapértelmezett ClassLoader áll rendelkezésünkre.

Végezetül pár hasznos link:

  • [[http://java.sun.com/docs/books/tutorial/ext/basics/load.html]] Egy részletesebb leírás a Java ClassLoader-ek működéséről
  • [[http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Class.html]] A Class metaosztály javadoc-ja
  • [[http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ClassLoader.html]] A ClassLoader osztály javadoc-ja
  • [[http://www.source-code.biz/snippets/java/2.htm]] A Base64 kódolást végző osztály
  • [[http://www.java-tips.org/java-se-tips/java.io/reading-a-file-into-a-byte-array.html]] Egy függvény, ami egy fájlból beolvassa a byte-okat tömbbe

Felhasználói felületek és a programozás

Ennek az írásnak az ötlete már jó ideje motoszkál bennem. Mindig is érdekelt a programozás – magas szinten. Azaz érdekelt, hogyan lehet nagy rendszereket tervezni, érdekelt, hogy ezek a rendszerek hogyan használhatóak az egyszerű felhasználó szemében. Érdekelt, hogyan lehet megcsinálni, de valahogy sosem csináltam meg. Egyszer ki kéne próbálni, de erre a közeljövőben ismételten nem lesz időm.

Ez persze nem akadályozott meg abban, hogy utánaolvasgassak a témának. Illetve azon oldalak ajánlásait kövessem, akikre már amúgy is feliratkoztam. 🙂 Így találtam rá az Ars Technica cikksorozatára, ami a fejlesztési eszközöket, nyelveket, APIkat veszi górcső alá. Korábban már volt szerencsém az Ars Technicahoz, ők képesek voltak korábban például a Leopardot tizenegynéhány oldalban elemezni, elvárásokkal, hiányokkal és hasonló dolgokkal. Szerintem tanulságos volt, ezért érdemesnek láttam a cikksorozatba is belekóstolni.

Ennek az írásnak az ötlete már jó ideje motoszkál bennem. Mindig is érdekelt a programozás – magas szinten. Azaz érdekelt, hogyan lehet nagy rendszereket tervezni, érdekelt, hogy ezek a rendszerek hogyan használhatóak az egyszerű felhasználó szemében. Érdekelt, hogyan lehet megcsinálni, de valahogy sosem csináltam meg. Egyszer ki kéne próbálni, de erre a közeljövőben ismételten nem lesz időm.

Ez persze nem akadályozott meg abban, hogy utánaolvasgassak a témának. Illetve azon oldalak ajánlásait kövessem, akikre már amúgy is feliratkoztam. 🙂 Így találtam rá az Ars Technica cikksorozatára, ami a fejlesztési eszközöket, nyelveket, APIkat veszi górcső alá. Korábban már volt szerencsém az Ars Technicahoz, ők képesek voltak korábban például a Leopardot tizenegynéhány oldalban elemezni, elvárásokkal, hiányokkal és hasonló dolgokkal. Szerintem tanulságos volt, ezért érdemesnek láttam a cikksorozatba is belekóstolni.

Az írás alapvető példája, mint mondtam, az volt, hogy összevesse a lehetőségeket. Előrebocsátom, hogy az ő írásuk is (az enyém meg pláne) az Apple megoldását preferálja a Microsoftéval szemben, ezért aki ebből flamet csinálna, az inkább most hagyja abba. Annak semmi értelme sincs (mondom ezt én 🙂 ).

Na, ideje belevágni a témába. A vonatkozott cikksorozat engem leginkább egy vonatkozásban fogott meg: a második részben feszegette azt, hogy a Microsoft APIk mennyire nem kifinomultak – valószínűleg ez lehet az oka, hogy szerte a cégen belül is a “kerék újra feltalálása” a divat. Ezt talán a mellékelt kép is szemlélteti (a kép eredeti változata a http://arstechnica.com/articles/culture/microsoft-learn-from-apple-II.media/vista.png címen érhető el).

A Microsoft konzisztensnek nem nevezhető kínálata felhasználói felület témában

A helyzet rossz. Nagyon. A képen szereplő ablakok a Visual Studio 2005 kivételével az aktuálisan elérhető legfrissebb változatok. Érdemes megfigyelni, hogy vannak alkalmazások menüvel és menü nélkül (utóbbi esetben szalaggal vagy rejtett menüvel). További érdekesség, hogy az Office család szereplő tagjainál alapvető eltérések vannak szerkezetben (szalag nézet a Wordben, hagyományos nézet a Visioban, és az Outlook mindig is egyedi volt). Másik érdekesség az előre-hátra nyilak az Intézőben, az Internet Exporerben és a Windows Media Playerben. Pontosabban a mellette levő lefele nyíl, ami a listát jelenítené meg: minő véletlen, ebből is van háromféle változat.

Biztos, hogy van még különbség, de ezek felderítését az unatkozó olvasókra bízom. 🙂

Azzal semmi bajom sincs, hogy a Vistában a felület máshogy néz ki, mint korábban. Lehet vitatkozni rajta, hogy jobb lett vagy rosszabb, mint a korábbi, én nem teszem meg, mert csak képernyőképeken láttam a rendszert. De ez így katasztrófa. Az Ars Technica sorozatában még mondott egy érdekes példát: a Vista kezdeti változatában azért voltak gondok a hangkártyadriverekkel, mert a Microsoft a fejlesztés végső szakaszában módosította a hangrendszert. Az ok: leültek multimédiában érintett emberekkel, és azok jelezték, hogy a lejátszóprogramok feleslegesen terhelik a gépet, ha pollozzák a hangbuffer állapotát, jobb lenne, ha az értesítené őket, hogy fogyóban az adat. Felmerül a kérdés: ez miért nem került a cég multimédiás programjának, a Windows Media Player fejlesztésekor? Például mert nem használja a saját APIt?

Ezzel szemben OSX-en az Apple szinte minden programja (nem állítom, hogy minden, mert azért az sem lenne igaz) egy közös megjelenítési APIt használ, és ez az API elérhető a fejlesztők számára is. Ezen felül elérhető még [[http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/|Apple Human Interface Guidelines]] című dokumentum is, ami egyfajta szakácskönyvet ad a fejlesztők kezébe, hogy az Apple milyen elvek szerint építi fel a programjait (többé-kevésbé). Eredmény: ha jön egy programozó, aki OSXre szeretne szoftvert írni, megnézi ezt a dokumentációt, és ez alapján tervezi meg a felhasználói felületet, és a beépített APIt felhasználva valósítja meg. Következmény: a 3rd party programok is hasonlóak lesznek a beépítettekhez. Ez a Microsoftos káosznál elérhetetlen állapot…

Persze a dolog messze nem ilyen egyértelmű: a http://www.neopoleon.com/home/blogs/neo/archive/2008/03/17/29941.aspx oldalon például a szerző azt részletezi, hogy miért nem szereti az OSX APIjait. Ez érthető is. Ahogy az Ars Technicas cikk harmadik részében szerepel, az Apple által erőltetett Objective-C nyelv alapvetően különbözik a megszokott objektum-orientált nyelvektől, csak OSX-en lehet használni, az XCode nevű program semmi különöset nem nyújt egy Visual Studiohoz képest, sőt, az extrák tekintetében le is van maradva. Szintén ebben a cikkben szerepel, hogy OSX-en meg hiányolja a .NET bizonyos részeit vagy éppen az Office 2007-et. Más szóval a helyzet messze nem egyértelmű, érdemes a továbbiakban is figyelni a témát, milyen változások lesznek.

Frissítés: Az Ars Technica cikksorozatára való hivatkozásokat pótolom.

  • [[http://arstechnica.com/articles/culture/what-microsoft-could-learn-from-apple.ars|Áttekintés, történelem]]
  • [[http://arstechnica.com/articles/culture/microsoft-learn-from-apple-II.ars|A Microsoft megoldásainak összegzése]]
  • [[http://arstechnica.com/articles/culture/microsoft-learn-from-apple-III.ars|Az Apple APIk ismertetése]]

Lonely HDR, azaz HDR kép készítése egyetlen képből

Sok mindenre képes az ember, ha van egy kis ideje. Például elgondolkodhat, hogyan tudná a rengeteg közepesen jól sikerült fényképét egyszerűen feltúrbózni, amit már szívesebben mutogat bárkinek. Kis gyakorlással csodát lehet művelni Photoshop (vagy a nyílt forráskód híveinek – küztük nekem – Gimp) segítségével, azonban ez néhány ezer kép nagyságrendnél kicsit idő- és munkaigényes. Fontos tehát, hogy mindezt automatikusan tudjuk csinálni. Már csak egy jó ötlet hiányzik. Ezen segít ha kicsit ránézünk a HDR technológiára.

High Dynamic Range imaging, avagy mi is ez? – dióhéjban

Sok mindenre képes az ember, ha van egy kis ideje. Például elgondolkodhat, hogyan tudná a rengeteg közepesen jól sikerült fényképét egyszerűen feltúrbózni, amit már szívesebben mutogat bárkinek. Kis gyakorlással csodát lehet művelni Photoshop (vagy a nyílt forráskód híveinek – küztük nekem – Gimp) segítségével, azonban ez néhány ezer kép nagyságrendnél kicsit idő- és munkaigényes. Fontos tehát, hogy mindezt automatikusan tudjuk csinálni. Már csak egy jó ötlet hiányzik. Ezen segít ha kicsit ránézünk a HDR technológiára.

High Dynamic Range imaging, avagy mi is ez? – dióhéjban

Kicsit bővebb leíráshoz: http://en.wikipedia.org/wiki/High_dynamic_range_imaging

A HDR célja általában olyan képek létrehozása, amelynek minden része teljes részletességgel látszik, attól függetlenül, hogy az sötét-e vagy világos. Ami azért nehéz, mert ha a világos részt kiemelve rövid ideig exponál a gép, a sötét részek nem fognak látszódni, míg túlexponált képpel előhozhatóak a sötét részletek, de a világos rész lesz kivehetetlen. A HDR alapötlete az, hogy egyetlen kép helyett csináljunk többet, különböző exponálással. Ezen képek alapján aztán összerakható olyan kép, amelynek minden részlete a megfelelő exponálással készült.

HDR-hez több kép kell, de nekünk csak egy van?

Nyílván egyetlen képből nem nyerhető annyi információ, mint akár három különbözőből, azonban nem is ezt akaruk elérni. A cél csupán az, hogy a rendelkezésre álló képből hozzuk ki a lehető legtöbbet, és ehhez használjuk fel a HDR-nél is használt technikát. A módszer nem más, mint a képet visonylag apró szegmensekre bontjuk, és minden szegmensre kiválasztjuk a rendelkezésre álló variánsok közül a legjobbat. A HDR esetén fizikailag van több képünk, ebben az esetben ugyanazt a képet világosítjuk/sötétítjük néhányszor. A világosság változtatásával ugyan nem nyerünk információt, a kép hatását a nézőre azonban jelentősen befolyásolhatjuk. Így tehát minden szegmensre kiválasztjuk az optimálisat, elsimítjuk az illesztéseket, végül kicsit megnöveljük a színtelítettséget, hogy tényleg szép legyen.

A fenti ötlet alapján készült egy kis java program, ami mély elmélkedés árán egész szép eredményt ér el a bemeneti képpel. Letöltés után valami ilyesmit kell beírni egy parancssorba:


java -jar lonelyhdr.jar input.jpg output.jpg

Rövid várakozás után egy kicsinosítgatott kép kacsint vissza ránk:

input.jpg output

Letöltés: lonelyhdr.jar

Fontos megjegyzés: a program értelmes eredményt csak megfelelő méretű (értsd: mai átlagos fényképezőgép által készített) képpel tud elérni. A fenti kép például egy 5 megapixeles szappantartóal készült, és a képet eredeti méretében (2592 x 1944 pixel) eresztettem át a programon.

J2EE és JUnit – avagy a platformfüggő Java

Végre, beadtuk. Az UML tárgyból esedékes házi feladatot (lásd még [[J2EE – a nagy aknamező|a múltkori írást]]). Ez J2EE egyszerűen valami félelmetes. Ez a technológia szép. De tényleg…

Az implementáció után mindössze teszteseteket kellett írni az utolsó beadásra. Mindössze. Persze ez akkor sem olyan egyszerű, hiszen azt JUnitban kellett megírni, ami alapvetően maga szeretné futtatni a teszteket. Semmi gond, akkor átverjük, feltöltjük a JUnitot is a konténerbe, és ott majd látni fogja a keresett dolgokat.

Végre, beadtuk. Az UML tárgyból esedékes házi feladatot (lásd még [[J2EE – a nagy aknamező|a múltkori írást]]). Ez J2EE egyszerűen valami félelmetes. Ez a technológia szép. De tényleg…

Az implementáció után mindössze teszteseteket kellett írni az utolsó beadásra. Mindössze. Persze ez akkor sem olyan egyszerű, hiszen azt JUnitban kellett megírni, ami alapvetően maga szeretné futtatni a teszteket. Semmi gond, akkor átverjük, feltöltjük a JUnitot is a konténerbe, és ott majd látni fogja a keresett dolgokat.

Hehe, már megint optimista vagyok ezekkel a cuccokkal kapcsolatban. Ez, hogy feltöltsük a JUnitot, ez körülbelül két óra szívást jelentett a classpath, build path, referenced projects, requirements és hasonló témában projektek és pluginek viszonylatában. Ezek tulajdonképpen mind-mind egyfajta függőséget jelentenek a projektek és más projektek, esetleg libek vagy hasonlók között, de az a poén az egészben, hogy a szerepük között jelentős átfedések vannak. És ami igazán kellemetlen az az, hogy ha a helyes megoldás mellett egy nem szükséges helyen megadod a függőséget, akkor már nem jó. Lehet vele szépen játszadozni.

Jó, túl vagyunk az alapozáson, megvan a teszt keretrendszer, kezdünk teszteseteket írni. Egyszer csak egyik kolléga megkeres, hogy nagyon eszement hibaüzenetet ad egy importra: nem azt, hogy nem találja, hanem azt, hogy nem engedélyezett a használata. Remek, újabb szép körnek néz ki. Ezúttal sajnos nem tévedtem. Némi játszadozás után értettem meg, hogy tényleg látja, csak valami miatt nem akarja használni. Közben szétvertem a tesztkörnyezetet (utána újból órákba kerül, amíg mindenkinél összeáll, de erről később). Na, ekkor némi Google, meg váltogatás a különböző Eclipse környezeteim között (bizony, nekem több is van, be-be-be 🙂 ), és kiderül, hogy a probléma egészen csúnya: az Eclipse támogat egy package-korlátozást (access rule-nak hívja) a különböző pluginekre. Ok, ez önmagában nem csúnya, hisz így az Eclipse-plugin írója rákényszerítheti a felhasználó plugint, hogy az api-n keresztül érje el. De ami gáz, hogy ezt alkalmazta a beépített Java típuskönyvtárra.

Ok, módosítsuk a dolgot. Némi segítség után rájövök, hogy melyik jarban van a keresett osztály. Adjunk hozzá egy engedélyező bejegyzést a jarhoz tartozó access rule-ok közé. Újabb pofára esés, és kiderül, hogy nem írható. Remek. További leírásböngészés, és ekkor kiderül, hogy az ősét, ha tudom szerkeszteni, az is jó. Na, azt tudom, mert a globálisat engedi szerkeszteni, csak a system jarokét nem, meg a workspace-ben definiált pluginekét sem (azokat a plugin.xml szerkesztőjén lehet hakkolni). Remek, beírom az osztálynevet com.sun…. formában, örülnék, hogy megy, de nem megy. Ok, nézzünk mintát, igaza van, /-rel kell írni, és ha *-gal zárom, akkor egy package-re lebontva tudom szerepeltetni. Ok, egy részét átírom /-esre (nem kéne azért a teljes com. tartományt engedélyezni), majd csillaggal lezárom. Még mindig nem jó. Aztán kiderült. Ahhoz, hogy rekurzívan elinduljon lefelé a csomaghierarchiában, ahhoz nem árt **-gal lezárni… Na, ezután már megy.

Illetve csak az a hivatkozás. Csak a libekkel szórakozás miatt elment a futtatókörnyezet. Sebaj, rutinos róka vagyok már, nekilátok és összerakom a megfelelő fájlt (jó, most csaltam, összeklikkeltem a run dialogban). Végre megy nálam. Ekkor töltsük fel svn-re, hogy a többieknek is menjen.

Eltelik pár perc, és kiderült, hogy nem ment át. Mint utóbb kiderült, verzió- és oprendszerkülönbségek miatt. Az OSX->Windows irányban nem működik. És fordítva sem. Az lett a vége, hogy a Windows-osok megcsinálták a környezetet, majd feltöltötték svn-re, ezután én is megcsináltam az enyémet, de én nem töltöttem fel. Erre ezután minden commitnál vigyázni kellett remek. Még szerencse, hogy a Java platformfüggetlen.

De ez semmi ahhoz képest, ami ma hajnali 5 magasságában derült ki, 6 órával a beadás előtt (ameddig persze be is kellett érni az egyetemre). Megírtam már 40 tesztesetet – ez majdnem ezer sor kód, ezek segítségével több hibát is kijavítottam a tesztelt kódrészben. De tényleg. Futott minden. Amikor a kolléga közli velem, hogy a teszteseteim egy része meghal, és ez hazavágja az egész adatbázist. Nálam persze semmi ilyesmi. Némi kísérletezés után kiderül, a gond az, hogy a Windows nem eszi meg a megoldást. Pont. Akkor mutassuk be Macen. De Macen meg egyes Windowson megírt tesztesetek nem futnak. 😕

A hibát nem sikerült megoldani… Mindenesetre kezdem azt hinni, hogy olyan, hogy platformfüggetlen nem létezik. A Java legalábbis biztosan nem az. Például az AbevJava máshogy megy Windows-on, mint Linuxon vagy OSX-en. És ez a mostani hiba is megerősíti ezt az elképzelést. Azt, hogy mi lehet a hibás, csak tippelni tudom. Van egy elszúrt (értsd: feleslegesen túlbonyolított) entitáshalmazunk, van egy toplinkünk mysql adatbázishoz csatlakoztatva, van alatta oprendszer, OSGi konténer, stb. stb. stb. Erre már csak Murphyt idézném zárógondolatként:

  1. A bonyolult rendszerek hajlamosak a bonyolult meghibásodásokra.
  2. Ezzel szemben az egyszerű rendszerek is hajlamosak a bonyolult meghibásodásokra.