Amikor elkezdett foglalkoztatni a gondolat, hogy PHP-ban fejlesszek Google App Engine-re, az egyik legnagyobb problémának az adatbázis elérése tűnt, hiszen PHP-ból általában SQL használunk erre, ami ugye App Engine alatt nem megoldható, hiszen a datastore egy nem relációs adatbázis. Utánanéztem kicsit, hogy mások hogy oldják meg ezt, és igazából két megoldást találtam. Az egyik az App Engine által szolgáltatott JPA vagy JDO réteg használata. Itt ugye az a gond, hogy ezzel elveszítjük a rendszer hordozhatóságát, hiszen egy standard LAMP környezetben már nem állnak rendelkezésre ezek a programkönyvtárak. A másik lehetséges megoldás, hogy valamilyen SQL wrappert használunk, amin keresztül a datastore szabvány SQL adatbázisnak tűnik. Ezzel az volt a gondom, hogy feleslegesnek tűnt, hiszen az SQL réteg felett úgyis valamilyen ORM réteget használnék, és ha már így van, miért ne építsük az ORM réteget közvetlenül a datastore fölé. Így született tehát az elhatározás, hogy saját ORM réteget fejlesszek PHP-hoz, és ha már belefogtam, megpróbáltam belegyúrni az eddigi tapasztalataimat.
Az első szempont az egyszerűség volt. Minimalista megoldást akartam, ami egyszerű, de mégis hatékony. E mellett legyen transzparens, tehát az elkészült PHP alkalmazás egyaránt képes legyen futni egy általános LAMP környezetben, és az AppEngine datastore-ja felett is. Mivel annak idején a Hibernate-el volt némi nézeteltérésem, mindenképp olyan megoldást akartam, ahol az entitások memória objektumok, tehát nincsenek az adatbázishoz kötve (csúnyán mondva nem attach-oltak). Itt most nem térnék ki a részletekre, de a lényeg, hogy ennek köszönhetően az adatbázis objektumokat büntetlenül tárolhatjuk a session-ben, vagy mondjuk JSON formában mozgathatjuk őket két rendszer között. Kb. ezek voltak az előfeltételek, lássuk hát a megvalósítást.
A rendszer használatához elsőként egy YAML állományban le kell írnunk az adatbázis sémát és az entitások kapcsolatrendszerét. Ez a YAML formátum felépítésének köszönhetően nem túl bonyolult, mégis szép és átlátható. A YAML alapján a ModelGenerator osztály használatával legenerálhatjuk az entitás osztályokat. Az entitás objektumok csak egyszerű adattároló szerepet töltenek be, így felépítésük az adatbázis megvalósítástól független. Ha ezzel megvagyunk, a megfelelő Datastore factory hívásával kérhetünk a rendszertől egy datastore példányt, aminek segítségével az adatokkal kitöltött entitás az adatbázisra menthető, illetve onnan felolvasható. Ugyancsak a datastore segítségével generálhatunk lekérdezéseket, aminek segítségével egyenlőre csak szűrni és rendezni lehet az eredményeket. Ennek az az oka, hogy az alacsony szintű datastore API csak ezeket teszi lehetővé. Bár lehet, hogy ez az eszközkészlet kicsit szegényesnek tűnik, de az esetek nagy részében általában elég, vagy némi kódolással kiegészíthető. Az entitás példányok kapcsolatainak kezelésére azt találtam ki, hogy az entitás objektum minden kapcsolatához egy metódust rendeltem, ami paraméterként kapja meg az aktuális datastore-t. A metódus egy kezelő objektumot ad vissza, ami a kapcsolatnak megfelelően karbantartja a kapcsolódó objektumokat. Tehát vegyük például a User (felhasználó) és Role (szerepkör) entitásokat, melyek közt many-to-many (több-több) kapcsolat van. Ebben az esetben a User osztálynak lesz egy getRolesRelation metódusa, ami egy kezelő objektumot ad vissza. Ezen az objektumon keresztül adhatunk hozzá Role-okat az adott felhasználóhoz, ezen keresztül listázhatjuk ki a hozzárendelt szerepköröket, vagy törölhetjük azokat. Mivel ezek a Relation objektumok mindig létrehozáskor kapják meg a szükséges adatbázis kapcsolatot, az entitásnak magának nem kell az adatbázishoz kötődnie, amiről az előzőekben már írtam. Röviden tehát körülbelül ennyi.
Az eredmény tehát egy nagyon kompakt kis ORM rendszer. Jelenleg a MySQL-t és a Google App Engine Datastore-ját támogatja, de nagyon egyszerűen kiterjeszthető. Maga a kód nagyon egyszerű, áttekinthető és könnyen használható. Bár ennek a lehetőségét nem vizsgáltam meg mélyebben, de a csökkentett eszközkészletnek köszönhetően valószínűleg a datastore-on kívül más nem relációs adatbázisokra is alkalmazható lenne a rendszer, pl. MongoDB-re. A későbbiekben talán érdemes ezt az irányt is megvizsgálni. A programkönyvtár másik nagy előnye, hogy nagyon pici, így akár kódméret kritikus rendszerekben is használható lenne. A későbbiekben lehet, hogy készül Java, Python, esetleg C++ változatot, amit például mobiltelefonok programozásához lehetne használni.
A kód és néhány példa elérhető a Google Code-on a http://code.google.com/p/lworm/ címen.
Ez a blog az eredeti http://lf.estontorise.hu címen található blogom másolata.
2010. november 19., péntek
2010. november 16., kedd
gae-filestore (Írható/olvasható virtuális fájlrendszer Google AppEngine-re)
A Google AppEngine egyik elég erős megszorítása, hogy a telepített alkalmazás csak olvasni tudja a fájlrendszert. Új fájlokat nem hozhatunk létre, illetve nem módosíthatjuk a fájlokat. Bármilyen adattárolásra az AppEngine által biztosított adatbázist, a Datastore-t kell használnunk. Igazából ha jobban meggondoljuk, egy webalkalmazás többnyire valóban csak az adatbázist használja, így sokszor tényleg nincs szükség fájlrendszerre, de azért néha jól jön. Egy projektem kapcsán épp ilyesmire volt szükségem, ezért készítettem egy igazán egyszerű virtuális fájlrendszer implementációt Google AppEngine-hez. A rendszer működésének megértéséhez először írnék kicsit a Datastore-ról, annak is az alacsony szintű megvalósításáról.
Az AppEngine Datastore-ja egy BigTable alapú nem relációs adatbázis rendszer. Bár pontosan nem tudom, hogy a Google alkalmazások (GMail, Google Maps, a kereső, stb.) is ezt a megvalósítást használják-e, erről nem találtam konkrét leírást, de mindenesetre ez valami nagyon hasonló dolog kell hogy legyen. A rendszer használatára két lehetőségünk van. Egyfelől kapunk egy szabványos JPA/JDO réteget, így J2EE alkalmazásainkat változtatás nélkül, vagy kisebb változtatásokat követően futtathatjuk AppEngine-en. A másik lehetőség a low-level API használata, ami közvetlen hozzáférést biztosít a DataStore-hoz, így sokkal hatékonyabb, ugyanakkor kicsivel kényelmetlenebb is, mint a szabványos réteg. Én ez utóbbiról írnék kicsit bővebben. A Datastore adattárolásának alapja az Entity. A relációs adatbázis kezelőkhöz hasonlítva az Entity az adatbázis rekordnak felel meg, azzal a különbséggel, hogy az Entity-nek nincs fix szerkezete. Ebből a szempontból az Entity inkább olyan mint egy asszociatív tömb, vagy Java terminológiával élve Map. Egy ilyen Entity-be szabadon helyezhetünk el név-érték párokat. A másik nagyon fontos komponens a Key. A Key az entitás elsődleges azonosítója. A Key 3 féle módon épülhet fel. Generálhatjuk egy string-ből vagy long-ból, ezen felül definiálhatunk egy kulcs típust (kind), ami kb. a tábla megfelelője, és végül definiálhatjuk úgy a kulcsot, hogy ezeken felül egy szülő kulcsot is megadunk. Ez utóbbi megoldással a kulcsok és ezzel az Entity-k hierarchiába szervezhetőek. Ha összeállítottunk egy Entity-t, azt egyetlen metódussal lerakhatjuk a datastore-ra, és onnan a Key segítségével vissza is olvashatjuk. Amennyiben nem a Key alapján szeretnénk visszanyerni az entitást, úgy létre kell hoznunk egy Query-t. A Query segítségével az Entitás bármely értékére szűrhetünk az alapműveletek (kisebb, nagyobb, egyenlő, stb.) használatával. Az ilyen keresések segítésére definiálhatunk indexeket, de erre sincs feltétlenül szükség, hiszen a rendszer a lekérdezések alapján automatikusan legenerálja a szükséges indexeket. Az eredményként kapott entitás listát bármely eleme alapján rendezhetjük is, és nagyjából ennyi. Ha több entitást szeretnénk összekapcsolni (JOIN), VAGY kapcsolatot kialakítani a szűrési feltételekben, vagy bármi hasonlót tenni, amire egy relációs adatbázis amúgy önmagában képes, azt kódból kell megvalósítanunk. Cserébe viszont az egész rendszer masszívan elosztott, sémamentes, flexibilis és gyors. Most hogy megismertük a Datastore-t, lássuk a virtuális fájlrendszer működését.
A virtuális fájlrendszer legfelső szintű eleme a DataStoreFile entitás. Ezek az entitások testesítik meg az állományokat és a könyvtárakat egyaránt. Egy ilyen entitásnak 4 attribútuma van. A fájl típusa (könyvtár v. fájl), az utolsó módosítás ideje, a fájl mérete, és a fájlt tartalmazó útvonal. A fájl entitás egyedi azonosítója annak elérési útja, így ez alapján nagyon könnyen elérhetőek a fájl metaadatai. Mivel a metaadatokra sokszor szükség lehet, ezért a fájl entitás mentésekor és betöltésekor a memcache-ben is tároljuk, így annak további elérése már nagyon gyorsan megy a cache-ből. A fájl tényleges tartalmának tárolása Blob-okban történik. A Blob tulajdonképpen egy byte tömb, aminek mérete maximálisan 1 Mb lehet AppEngine esetén. Én 512 Kb méretű blokkokat használtam, amiket sorszámmal azonosít a rendszer. Az egyes blokkok a fájl entitás alatt helyezkednek el (a blokk kulcsa a fájl entitás kulcsának leszármazottja). Amikor adatokat írunk vagy olvasunk a fájlból, a rendszer a fájlmutató alapján meghatározza a blokk sorszámát ahol az adat van, majd ebben módosítja a megfelelő adatot. Egyszerű kialakítás, mégis kényelmes és hatékony.
A kód szokásos módon a Google Code-on megtalálható a http://code.google.com/p/gae-filestore/ címen.
Az AppEngine Datastore-ja egy BigTable alapú nem relációs adatbázis rendszer. Bár pontosan nem tudom, hogy a Google alkalmazások (GMail, Google Maps, a kereső, stb.) is ezt a megvalósítást használják-e, erről nem találtam konkrét leírást, de mindenesetre ez valami nagyon hasonló dolog kell hogy legyen. A rendszer használatára két lehetőségünk van. Egyfelől kapunk egy szabványos JPA/JDO réteget, így J2EE alkalmazásainkat változtatás nélkül, vagy kisebb változtatásokat követően futtathatjuk AppEngine-en. A másik lehetőség a low-level API használata, ami közvetlen hozzáférést biztosít a DataStore-hoz, így sokkal hatékonyabb, ugyanakkor kicsivel kényelmetlenebb is, mint a szabványos réteg. Én ez utóbbiról írnék kicsit bővebben. A Datastore adattárolásának alapja az Entity. A relációs adatbázis kezelőkhöz hasonlítva az Entity az adatbázis rekordnak felel meg, azzal a különbséggel, hogy az Entity-nek nincs fix szerkezete. Ebből a szempontból az Entity inkább olyan mint egy asszociatív tömb, vagy Java terminológiával élve Map. Egy ilyen Entity-be szabadon helyezhetünk el név-érték párokat. A másik nagyon fontos komponens a Key. A Key az entitás elsődleges azonosítója. A Key 3 féle módon épülhet fel. Generálhatjuk egy string-ből vagy long-ból, ezen felül definiálhatunk egy kulcs típust (kind), ami kb. a tábla megfelelője, és végül definiálhatjuk úgy a kulcsot, hogy ezeken felül egy szülő kulcsot is megadunk. Ez utóbbi megoldással a kulcsok és ezzel az Entity-k hierarchiába szervezhetőek. Ha összeállítottunk egy Entity-t, azt egyetlen metódussal lerakhatjuk a datastore-ra, és onnan a Key segítségével vissza is olvashatjuk. Amennyiben nem a Key alapján szeretnénk visszanyerni az entitást, úgy létre kell hoznunk egy Query-t. A Query segítségével az Entitás bármely értékére szűrhetünk az alapműveletek (kisebb, nagyobb, egyenlő, stb.) használatával. Az ilyen keresések segítésére definiálhatunk indexeket, de erre sincs feltétlenül szükség, hiszen a rendszer a lekérdezések alapján automatikusan legenerálja a szükséges indexeket. Az eredményként kapott entitás listát bármely eleme alapján rendezhetjük is, és nagyjából ennyi. Ha több entitást szeretnénk összekapcsolni (JOIN), VAGY kapcsolatot kialakítani a szűrési feltételekben, vagy bármi hasonlót tenni, amire egy relációs adatbázis amúgy önmagában képes, azt kódból kell megvalósítanunk. Cserébe viszont az egész rendszer masszívan elosztott, sémamentes, flexibilis és gyors. Most hogy megismertük a Datastore-t, lássuk a virtuális fájlrendszer működését.
A virtuális fájlrendszer legfelső szintű eleme a DataStoreFile entitás. Ezek az entitások testesítik meg az állományokat és a könyvtárakat egyaránt. Egy ilyen entitásnak 4 attribútuma van. A fájl típusa (könyvtár v. fájl), az utolsó módosítás ideje, a fájl mérete, és a fájlt tartalmazó útvonal. A fájl entitás egyedi azonosítója annak elérési útja, így ez alapján nagyon könnyen elérhetőek a fájl metaadatai. Mivel a metaadatokra sokszor szükség lehet, ezért a fájl entitás mentésekor és betöltésekor a memcache-ben is tároljuk, így annak további elérése már nagyon gyorsan megy a cache-ből. A fájl tényleges tartalmának tárolása Blob-okban történik. A Blob tulajdonképpen egy byte tömb, aminek mérete maximálisan 1 Mb lehet AppEngine esetén. Én 512 Kb méretű blokkokat használtam, amiket sorszámmal azonosít a rendszer. Az egyes blokkok a fájl entitás alatt helyezkednek el (a blokk kulcsa a fájl entitás kulcsának leszármazottja). Amikor adatokat írunk vagy olvasunk a fájlból, a rendszer a fájlmutató alapján meghatározza a blokk sorszámát ahol az adat van, majd ebben módosítja a megfelelő adatot. Egyszerű kialakítás, mégis kényelmes és hatékony.
A kód szokásos módon a Google Code-on megtalálható a http://code.google.com/p/gae-filestore/ címen.
2010. november 8., hétfő
PHP futtatása Googel App Engine-en
A GAE vagy Google App Engine a Google alkalmazás hoszting szolgáltatása. Nagy előnye, hogy segítségével alkalmazásunka a Google infrastruktúráján futtathatjuk. Nem kell olyan apróságokkal foglalkoznunk, mint a load balanceing, erőforrás allokáció, elosztott session kezelés, stb. hiszen a Google a terheléstől és a kérések helyétől függően optimálisan fogja "szétkenni" az alkalmazásunkat. Adatbázisként egy Google BigTable alapú nem relációs adatbázist kapunk, aminek klaszterezését ugyancsak a rendszer végzi automatikusan. E mellett kapunk még néhány "nyalánkságot", mint amilyen az elosztott memcache, e-mail, vagy xmpp lehetőség. Amiért azonban igazán vonzó lehet induló startup-ok számára, az az, hogy a rendszer körülbelül havi 1 000 000 lapletöltésig és 500 Gb tárhelyigényig teljesen ingyenes. Ha pedig az embernek sikerült ekkora forgalmat generálnia, akkor már jó eséllyel ki tudja termelni a plusz sávszélesség és tárhely árát is, ami néhány cent gigabájtonként, tehát igen baráti. Persze ezért a nagy kényelemért némi kompromisszumot is kénytelenek vagyunk meghozni. Az egyik ilyen korlát, ami véleményem szerint nagyban gátolja a technológia elterjedését, hogy egyenlőre csak Java és Python nyelven programozható. Az egyik talán leggyakoribb kérés szokott lenni a felhasználók felől, hogy más hoszting szolgáltatókhoz hasonlóan támogassák a PHP nyelvet is. Így megnyílna az út sok népszerű web framework, és PHP fejlesztő előtt. Ezt a dolgot viszont valamiért a Google nem annyira erőlteti. Bár a PHP hivatalosan nem támogatott, mégis viszonylag egyszerű módon futtathatunk PHP scripteket AppEngine felett.
A megoldás kulcsa a Caucho Quercus nevű Java Servlet alapú PHP implementáció, amely az App Engine-en való futtatást is támogatja. A gyakorlati megoldás sem olyan bonyolult. Töltsük le a legújabb resin verziót, és a resin.jar-t másoljuk a WEB-INF/lib könyvtárba. A web.xml-t bővítsük ki a következő bejegyzéssel:
Így a .php állományokat a Quercus automatikusan a QuercusServlet-nek továbbítja. Magukat a PHP állományokat a root könyvtárba helyezzük. Ezt követően az appengine-web.xml-hez adjuk hozzá a következő sorokat:
E nélkül a rendszer a php állományokat statikus fájloknak tekintené, és nem adná át a szervlet motornak. Körülbelül ennyi. Innentől kezdve már PHP-t is futtathatunk a Google App Engine-en. Ezzel a technikával elvileg sikeresen futtattak már Drupal-t, Wordpress-t, és néhány hasonló PHP keretrendszert. Persze azért a PHP alkalmazások szállítása nem olyan egyszerű, hiszen ezek többnyire MySQL-t használnak, AppEngine-en viszont ugye nincs relációs adatbázis. Tehát valamilyen szintű módosításra mindenképp szükség van, de ez talán megéri mindazokért az előnyökért cserébe, amit az AppEngine, és eleve maga a Quercus adhat (pl. egyszerű PHP-Java integráció, Java libek használata, stb.).
A megoldás kulcsa a Caucho Quercus nevű Java Servlet alapú PHP implementáció, amely az App Engine-en való futtatást is támogatja. A gyakorlati megoldás sem olyan bonyolult. Töltsük le a legújabb resin verziót, és a resin.jar-t másoljuk a WEB-INF/lib könyvtárba. A web.xml-t bővítsük ki a következő bejegyzéssel:
<servlet>
<servlet-name>Quercus Servlet</servlet-name>
<servlet-class>com.caucho.quercus.servlet.GoogleQuercusServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Quercus Servlet</servlet-name>
<url-pattern>*.php</url-pattern>
</servlet-mapping>
Így a .php állományokat a Quercus automatikusan a QuercusServlet-nek továbbítja. Magukat a PHP állományokat a root könyvtárba helyezzük. Ezt követően az appengine-web.xml-hez adjuk hozzá a következő sorokat:
<static-files>
<exclude path="/**.php"/>
</static-files>
<resource-files>
<include path="/**.php"/>
</resource-files>
E nélkül a rendszer a php állományokat statikus fájloknak tekintené, és nem adná át a szervlet motornak. Körülbelül ennyi. Innentől kezdve már PHP-t is futtathatunk a Google App Engine-en. Ezzel a technikával elvileg sikeresen futtattak már Drupal-t, Wordpress-t, és néhány hasonló PHP keretrendszert. Persze azért a PHP alkalmazások szállítása nem olyan egyszerű, hiszen ezek többnyire MySQL-t használnak, AppEngine-en viszont ugye nincs relációs adatbázis. Tehát valamilyen szintű módosításra mindenképp szükség van, de ez talán megéri mindazokért az előnyökért cserébe, amit az AppEngine, és eleve maga a Quercus adhat (pl. egyszerű PHP-Java integráció, Java libek használata, stb.).
2010. október 28., csütörtök
SimpleRSA
Nem tudom hányan próbáltak már Java-ban RSA-val titkosított dokumentumokat PHP-ból olvasni, vagy fordítva, esetleg Java-ban aláírt dokumentumok hitelességét PHP-ból ellenőrizni, de mindenesetre nekem most ilyenre volt szükségem. Naív módon azt hittem, hogy a nyílt kulcsú titkosítás valami egyszerű dolog (mármint programozás szintjén a megfelelő library-k használatával), fogok valami RSA library-t, egyik oldalon elkódolom valami függvénnyel, a másik oldalon kikódolom, és ennyi. Néhány napi Google használat után, elveszve a különböző kulcsformátumok, és certificate-ek között, rá kellett döbbennem, hogy ez az egész mégsem annyira triviális. A gond forrása, hogy a PHP (és amúgy más szkript nyelvek is) az OpenSSL-t használja a titkosításhoz, míg Java esetén ott a Crypto API, aminek viszont nem sok köze van az OpenSSL-hez. Gondoltam keresek valami Java OpenSSL wrappert, de ilyet sem találtam. A gond tulajdonképpeni forrása az volt, hogy a PHP-s titkosításhoz és aláíráshoz PEM fájlból kellett volna olvasni a kulcsokat, ilyen formában viszont sehogyan sem tudtam menteni a Java-ban generált kulcsokat. Az alkalmazás jellege miatt mindenképp Java-ból akartam generálni a kulcspárt, tehát az a megoldás nem jöhetett szóba, hogy openssl-el készítem el a kulcspárt, és majd azt használom PHP és Java esetén is. Végül a megoldást a BouncyCastle Java library hozta el számomra, hiszen végül itt sikerült PEM fájl kezelő objektumokat találnom. Össze is raktam egy teszt kódot, és csodák csodájára a PHP kód képes volt olvasni a Java-ban generált kulcsokat, visszafejteni a Java-ban titkosított doksikat, és ellenőrizni az aláírások hitelességét.
Mivel nekem is sok segítséget nyújtottak mások leírásai, szinte kötelességemnek éreztem, hogy valamiképp megosszam frissen szerzett tudásomat. Ennek az egyik formája, hogy kódrészleteket közöl az ember (én is sok ilyenekből szedtem össze az RSA-val kapcsolatos tudásomat), én azonban azt tettem, amit eddig is, összeraktam egy kis library-t, amivel nagyon egyszerűen tudunk be/ki kódolni, illetve digitálisan aláírni adatokat. Egyre inkább az a meggyőződésem, hogy az ilyen kis minimál library-k sokkal hasznosabbak, mint a kóddarabok, vagy akárhány részletes leírás, hogy ezt meg azt hogy kell csinálni. Az elkészült library Java és PHP nyelven elérhető, és metódusok szintjén azonos, így elég egyetlen programkönyvtár használatát elsajátítani.
A kódot, ahogyan eddig is mindig, feltöltöttem a Google Code-ra, itt elérhető:
http://code.google.com/p/simplersalibrary/
Mivel nekem is sok segítséget nyújtottak mások leírásai, szinte kötelességemnek éreztem, hogy valamiképp megosszam frissen szerzett tudásomat. Ennek az egyik formája, hogy kódrészleteket közöl az ember (én is sok ilyenekből szedtem össze az RSA-val kapcsolatos tudásomat), én azonban azt tettem, amit eddig is, összeraktam egy kis library-t, amivel nagyon egyszerűen tudunk be/ki kódolni, illetve digitálisan aláírni adatokat. Egyre inkább az a meggyőződésem, hogy az ilyen kis minimál library-k sokkal hasznosabbak, mint a kóddarabok, vagy akárhány részletes leírás, hogy ezt meg azt hogy kell csinálni. Az elkészült library Java és PHP nyelven elérhető, és metódusok szintjén azonos, így elég egyetlen programkönyvtár használatát elsajátítani.
A kódot, ahogyan eddig is mindig, feltöltöttem a Google Code-ra, itt elérhető:
http://code.google.com/p/simplersalibrary/
2010. október 19., kedd
jin-plugin
Egy projektem kapcsán próbáltam valamilyen általános megoldást keresni beépülő modulok (plugin-ek) kezelésére. Ugye a lényeg röviden annyi, hogy a kész alkalmazáshoz bárki könnyen írhasson kiegészíthetőket, amiket egyszerű módon lehet telepíteni. Rövid keresgélés után rá is találtam az OSGi-re, ami tulajdonképpen minderre a szabványos megoldás. Ezt használja például az Eclipse is. Át is néztem a doksit, de úgy találtam, hogy az én igényeimhez képest ez amolyan ágyúval verébre megoldás. Valami olyan egyszerű megoldást kerestem, amit a leendő plugin fejlesztők néhány perc alatt el tudnak sajátítani. Olyat, amilyet például a PHP CMS-ek is használnak (pl. Wordpress, Joomla), ahol a plugin csak néhány függvényből áll. Találtam is egy annotation alapú jspf nevű keretrendszert, ami már nagyon megközelítette azt amire én gondoltam, de ekkor már úgy voltam vele, hogy csak és kizárólag a minimalista megoldás érdekel. Ahogyan a jin-template esetén, itt is arra gondoltam, hogy ha már készítek egy egyszerű megoldást, rögtön elkészítem a PHP implementációt is, amit később más programnyelvekre történő adaptáció követhet, így kvázi megteremtve a "minimális plugin architektúra szabványát". Ezt valószínűleg már sokan sokféle módon megtették előttem is, de sehol nem láttam publikálva ilyen jellegű különálló programkönyvtárat. Ennek valószínűleg az az oka, hogy a fejlesztők értelmetlennek találták leválasztani a plugin rendszert a kész alkalmazásról (éppen annak egyszerűsége miatt). Erre tipikus példa a PHP CMS-ekben használt plugin rendszer, ahol bár a megoldások nagyon hasonlóak, mégis minden rendszernek saját megoldása van. Így fogant meg hát a jin-plugin rendszer gondolata.
A rendszer felépítése a célnak megfelelően nagyon egyszerű. A plugin manager végignézi a paraméterként megadott könyvtárat, ahol minden pluginhez külön könyvtár tartozik. Minden könyvtárban egy yaml fájl található, ami definiálja a plugin osztályt, valamint az esetleges függőségeket. A plugin manager a függőségeket betartva tölti be a plugineket, és hívja meg a plugin osztály init metódusát. Maga a plugin kód a plugin könyvtárában található. PHP esetén a kódok helye közvetlenül a könyvtár, Java esetén pedig a classes könyvtár. Ez utóbbi esetben lehetőségünk van a program számára szükséges programkönyvtárak csomagolására is, amik a lib könyvtárban kapnak helyet. A pluginnek lehetősége van névvel ellátott szolgáltatások installálására, amiket más pluginek felhasználhatnak. Tulajdonképpen ez minden. Ennyi az a feltétlenül szükséges minimális működés, amire szükségem volt. A plugin installálása egyszerűen a plugin könyvtárba történő könyvtármásolással történik, a pluginek készítésének elsajátítása pedig a rendszer egyszerűségének köszönhetően néhány perc. Ez utóbbi szerintem bíztatólag hat a fejlesztőkre, ami igen fontos, ha azt szeretnénk, hogy minél több kiterjesztés szülessen az alkalmazásunkhoz. Már csak abban kell reménykednem, hogy a megoldásom elterjed, és így talán másokat is sikerül egy kicsit hozzásegítenem sikeres kiterjeszthető alkalmazások fejlesztéséhez.
A rendszer rövid dokumentációja, és a teljes forráskód LGPL licenc alatt megtalálható a Google Code-on:
http://code.google.com/p/jin-plugin/
A rendszer felépítése a célnak megfelelően nagyon egyszerű. A plugin manager végignézi a paraméterként megadott könyvtárat, ahol minden pluginhez külön könyvtár tartozik. Minden könyvtárban egy yaml fájl található, ami definiálja a plugin osztályt, valamint az esetleges függőségeket. A plugin manager a függőségeket betartva tölti be a plugineket, és hívja meg a plugin osztály init metódusát. Maga a plugin kód a plugin könyvtárában található. PHP esetén a kódok helye közvetlenül a könyvtár, Java esetén pedig a classes könyvtár. Ez utóbbi esetben lehetőségünk van a program számára szükséges programkönyvtárak csomagolására is, amik a lib könyvtárban kapnak helyet. A pluginnek lehetősége van névvel ellátott szolgáltatások installálására, amiket más pluginek felhasználhatnak. Tulajdonképpen ez minden. Ennyi az a feltétlenül szükséges minimális működés, amire szükségem volt. A plugin installálása egyszerűen a plugin könyvtárba történő könyvtármásolással történik, a pluginek készítésének elsajátítása pedig a rendszer egyszerűségének köszönhetően néhány perc. Ez utóbbi szerintem bíztatólag hat a fejlesztőkre, ami igen fontos, ha azt szeretnénk, hogy minél több kiterjesztés szülessen az alkalmazásunkhoz. Már csak abban kell reménykednem, hogy a megoldásom elterjed, és így talán másokat is sikerül egy kicsit hozzásegítenem sikeres kiterjeszthető alkalmazások fejlesztéséhez.
A rendszer rövid dokumentációja, és a teljes forráskód LGPL licenc alatt megtalálható a Google Code-on:
http://code.google.com/p/jin-plugin/
2010. október 12., kedd
jin-template
Már több webes projekt van a hátam mögött, de igazából még nem sikerült olyan template rendszert találnom, ami maradéktalanul kielégítené az igényeimet. Az egyik legnagyobb problémát az szokta jelenteni, hogy a HTML designt öcsém készíti el rendszerint Dreamweaver-el, és a későbbi karbantartás miatt meg is kell őriznünk a HTML tisztaságát. A legtöbb template rendszer speciális tag-eket, vagy más extra blokkokat használ, ami rendszerint teljesen tönkreteszi a HTML forrást. Az ilyen tag-ekkel teletűzdelt HTML már nem úgy jelenik meg a böngészőben vagy a szerkesztőben, ahogyan az eredmény, így a vizuális szerkesztés a standard HTML-es eszközökkel (pl. az általunk is használta Dreamwaver) nagyon nehézkes. A problémát általában úgy hidaltam át, hogy a template motornak szóló parancsokat HTML kommentekben helyeztem el, és egy előfeldolgozó után eresztettem rá a template motort. Ez a megoldás egész hatékonynak bizonyult, de még így sem voltam maradéktalanul elégedett. Az a gond ezekkel a template motorokkal, hogy szűkös a tudásuk. Ez a szűkös tudás persze az esetek nagy részében elegendő is, de ha esetleg olyan igényünk van, amit a template rendszer alapból nem támogat, sokszor igen nehézkes kiterjeszteni azt. Smarty plugineket kell gyártanunk, saját taglib-eket, stb. De miért kell ennek ilyen macerásnak lennie? Valójában arról van szó, hogy a template rendszer egy primitív programozási nyelvet definiál, és ezt kell kiterjesztenünk. Innen jött az ötlet, hogy a template rendszer speciális nyelve helyett használjuk a jól megszokott programozási nyelvet, és ezzel tulajdonképpen bontsuk ketté a template-et egy tiszta HTML forrásra, és egy felület generáló kódra (view building logic). Ez volt a jin-template alapötlete.
A rendszer maga nagyon egyszerű, néhány osztályból áll az egész. A forrás HTML-ben kommentekkel jelölhetünk ki blokkokat, majd a kódból ezen blokkok tartalmát használhatjuk fel, és cserélhetjük ki. Ez alapján tehát egy fórum motor bejegyzéseinek listája úgy áll elő, hogy definiálunk egy bejegyzés konténert, és egy bejegyzés elemet. Ezt követően kódból előszedjük a bejegyzés elemet, feltöltjük a megfelelő adatokkal, majd a bejegyzéseket behelyezzük a bejegyzés konténerbe. Ezzel a módszerrel a HTML forrásokat megtarthatjuk eredeti formájukban (csak néhány komment hozzáadása szükséges), a tartalom generálásához pedig felhasználhatjuk az implemetáló programozási nyelv teljes eszközkészletét.
Az elkészült rendszert feltöltöttem a Google Code-ra, ahol a működést egy egyszerű példán szemléltettem is:
http://code.google.com/p/jin-template/
A rendszer maga nagyon egyszerű, néhány osztályból áll az egész. A forrás HTML-ben kommentekkel jelölhetünk ki blokkokat, majd a kódból ezen blokkok tartalmát használhatjuk fel, és cserélhetjük ki. Ez alapján tehát egy fórum motor bejegyzéseinek listája úgy áll elő, hogy definiálunk egy bejegyzés konténert, és egy bejegyzés elemet. Ezt követően kódból előszedjük a bejegyzés elemet, feltöltjük a megfelelő adatokkal, majd a bejegyzéseket behelyezzük a bejegyzés konténerbe. Ezzel a módszerrel a HTML forrásokat megtarthatjuk eredeti formájukban (csak néhány komment hozzáadása szükséges), a tartalom generálásához pedig felhasználhatjuk az implemetáló programozási nyelv teljes eszközkészletét.
Az elkészült rendszert feltöltöttem a Google Code-ra, ahol a működést egy egyszerű példán szemléltettem is:
http://code.google.com/p/jin-template/
2010. szeptember 21., kedd
Programfejlesztés Android-ra
Néhány hónapja beszereztem egy Samsung Galaxy Spica-t, azzal a határozott szándékkal, hogy végre megtanulok Android-ot programozni. A jelenlegi tendenciákat figyelve az Android valószínűleg hamarosan a legelterjedtebb mobil platformmá válhat, és az sem elhanyagolandó tény, hogy Java nyelven fejleszthetünk rá, ami (legalábbis szerintem) sokkal barátságosabb, mint a C++ (Symbian), vagy az Objective-C (iPhone). Úgy tervezem, hogy több bejegyzést is írok majd a témában. Ebben a mostaniban azt írom le, hogyan állíthatjuk be a fejlesztőkörnyezetet, és kezdhetjük meg a fejlesztést.
Ha Android fejlesztésre adjuk a fejünket, legjobb választás az Eclipse fejlesztőrendszer használata. Elvileg létezik NetBeans plugin is, de mivel én eddig is Eclipse-et használtam fejlesztésre (még a PHP projektekhez is), ezért soha nem próbáltam. Szóval maradjunk az Eclipse-nél. Én a 3.5-ös Galileo-t telepítettem (a cikk írásakor ez volt a legfrissebb Android plugin által támogatott változat). Az Eclipse telepítése után telepítsük az ADT plugint. Ha ez megvan, még szükségünk van az Android SDK-ra, amit innen tölthetünk le. Ha fent van az SDK, futtassuk az SDK Manager.exe-t (Windows esetén), amivel letölthetjük és telepíthetjük a legfrissebb fejlesztői csomagokat. Ha a fejlesztéshez emulátort használunk, akkor a Virtual Devices fül alatt adjunk hozzá egy virtuális eszközt. Ha ezzel megvagyunk, lépjünk vissza eclipse-be, és Window/Preferences fül alatt keressük ki az Android részt, és állítsuk be az SDK location-t. Ha mindez megvan, rendszerünk készen áll a fejlesztésre.
A Package Explorer-ben adjunk hozzá gy Android projektet, állítsuk be az alkalmazás nevét, a package-t és a célrendszert, és kész is az első Android projektünk. A default Android projekt egy önmagában is futtatható Hello World alkalmazás, így nincs más dolgunk, mint elindítani azt a Debug As Android Application opcióval. Ha nincs csatlakoztatva telefon, a rendszer automatikusan az emulátort indítja el. Ha rögtön telefonon szeretnénk tesztelni (én így szoktam), akkor kapcsoljuk be a telefonon a fejlesztői módot. Nálam ez a Beállítások/Alkalmazások/Fejlesztés alatt kapcsolható be az USB-hibakeresés opcióval. Ha bekapcsoltuk, az adatkábellel kössük össze a telefont a PC-vel. Ilyenkor a PC vagy felismeri a telefont (nálam már valahonnan fent volt egy Samsung ADT driver, ami valószínűleg a gyári szoftverrel települt). Ha nincs fent driver, próbálkozhatunk az SDK-ban lévő usb driverrel, ami az usb_driver könyvtárban található. Ha sikerült csatlakoztatni a telefont, újra indítsuk el az alkalmazást a Debug opcióval, és ha minden jól megy, most már az a telefonon fog elindulni. Ez a módszer szerintem sokkal jobb, mint az emulátor használata, hiszen élő környezetben tesztelhetjük a programot, mégis ugyanúgy tudunk debugolni, mint emulátor esetén. Ráadásul a natív környezetnek köszönhetően sokkal gyorsabban is fut az alkalmazás. Egy dolog van még, amit érdemes megtenni. Az Eclipse Window / Show view menüpontjában található a LogCat view, ahová a programból logolhatunk, és ahová a rendszer a hibaüzeneteket küldi. Ha valami nem megy, érdemes egy pillantást vetni ide.
Ha további infóra lenne szükség a környezet telepítését illetően, az SDK honlapja-án mindent meg lehet találni.
Sok szerencsét minden leendő Android fejlesztőnek!
Ha Android fejlesztésre adjuk a fejünket, legjobb választás az Eclipse fejlesztőrendszer használata. Elvileg létezik NetBeans plugin is, de mivel én eddig is Eclipse-et használtam fejlesztésre (még a PHP projektekhez is), ezért soha nem próbáltam. Szóval maradjunk az Eclipse-nél. Én a 3.5-ös Galileo-t telepítettem (a cikk írásakor ez volt a legfrissebb Android plugin által támogatott változat). Az Eclipse telepítése után telepítsük az ADT plugint. Ha ez megvan, még szükségünk van az Android SDK-ra, amit innen tölthetünk le. Ha fent van az SDK, futtassuk az SDK Manager.exe-t (Windows esetén), amivel letölthetjük és telepíthetjük a legfrissebb fejlesztői csomagokat. Ha a fejlesztéshez emulátort használunk, akkor a Virtual Devices fül alatt adjunk hozzá egy virtuális eszközt. Ha ezzel megvagyunk, lépjünk vissza eclipse-be, és Window/Preferences fül alatt keressük ki az Android részt, és állítsuk be az SDK location-t. Ha mindez megvan, rendszerünk készen áll a fejlesztésre.
A Package Explorer-ben adjunk hozzá gy Android projektet, állítsuk be az alkalmazás nevét, a package-t és a célrendszert, és kész is az első Android projektünk. A default Android projekt egy önmagában is futtatható Hello World alkalmazás, így nincs más dolgunk, mint elindítani azt a Debug As Android Application opcióval. Ha nincs csatlakoztatva telefon, a rendszer automatikusan az emulátort indítja el. Ha rögtön telefonon szeretnénk tesztelni (én így szoktam), akkor kapcsoljuk be a telefonon a fejlesztői módot. Nálam ez a Beállítások/Alkalmazások/Fejlesztés alatt kapcsolható be az USB-hibakeresés opcióval. Ha bekapcsoltuk, az adatkábellel kössük össze a telefont a PC-vel. Ilyenkor a PC vagy felismeri a telefont (nálam már valahonnan fent volt egy Samsung ADT driver, ami valószínűleg a gyári szoftverrel települt). Ha nincs fent driver, próbálkozhatunk az SDK-ban lévő usb driverrel, ami az usb_driver könyvtárban található. Ha sikerült csatlakoztatni a telefont, újra indítsuk el az alkalmazást a Debug opcióval, és ha minden jól megy, most már az a telefonon fog elindulni. Ez a módszer szerintem sokkal jobb, mint az emulátor használata, hiszen élő környezetben tesztelhetjük a programot, mégis ugyanúgy tudunk debugolni, mint emulátor esetén. Ráadásul a natív környezetnek köszönhetően sokkal gyorsabban is fut az alkalmazás. Egy dolog van még, amit érdemes megtenni. Az Eclipse Window / Show view menüpontjában található a LogCat view, ahová a programból logolhatunk, és ahová a rendszer a hibaüzeneteket küldi. Ha valami nem megy, érdemes egy pillantást vetni ide.
Ha további infóra lenne szükség a környezet telepítését illetően, az SDK honlapja-án mindent meg lehet találni.
Sok szerencsét minden leendő Android fejlesztőnek!
Feliratkozás:
Bejegyzések (Atom)