Archive for December, 2009

YAML

Tuesday, December 15th, 2009 | Uncategorized | 3 Comments

Ahogy azt egy kedves szomszéd szokta monda, a törpök élete se mindig csak játék és mese. Ez különösen így van, ha a törpök db4o-t használnak. Ráadásul BigDecimal-t akarnak perzisztálni, amihez tartozó kiterjesztés csak a legújabb SNAPSHOT artifactokban van benne.

És valószínű ennek a folyománya az, ami rendszeresen szembe jön, egy exception (hová is lenne az életünk exceptionök nélkül), ami valahogy így néz ki:

Caused by: com.db4o.ext.InvalidSlotException: id: 5182
        at com.db4o.internal.marshall.UnmarshallingContext.invalidSlot(UnmarshallingContext.java:81)
        at com.db4o.internal.marshall.UnmarshallingContext.read(UnmarshallingContext.java:49)
        at com.db4o.internal.ObjectReference.read(ObjectReference.java:281)
        at com.db4o.internal.ObjectReference.read(ObjectReference.java:267)
        at com.db4o.internal.ObjectContainerBase.getHardObjectReferenceById(ObjectContainerBase.java:956)
        at com.db4o.internal.marshall.AbstractReadContext.classMetadataForObjectId(AbstractReadContext.java:85)
        at com.db4o.internal.marshall.AbstractReadContext.readObject(AbstractReadContext.java:57)
        at com.db4o.internal.marshall.AbstractReadContext.readAtCurrentSeekPosition(AbstractReadContext.java:46)
        at com.db4o.internal.OpenTypeHandler.read(OpenTypeHandler.java:172)
        at com.db4o.internal.Handlers4.readValueType(Handlers4.java:313)
        at com.db4o.internal.marshall.AbstractReadContext.readAtCurrentSeekPosition(AbstractReadContext.java:48)

És így tovább.

A hiba teljesen kiszámíthatatlanul, ugyanakkor jól reprodukálhatóan előjön. Pl. előveszek egy backup db4o file-t, megnyitom, teszek-veszek benne, lezárom az alkalmazást. A következő megnyitáskor már nem nyílik meg az adatbázis. Ha viszont ugyanazt a cselekmény sort újra végigviszem az elmentett db4o file-on ugyanúgy elszáll.

Tehát előszőr is ki kéne dobni a db4o-ból a BigDecimal-okat, amihez át kéne konvertálni a sémát. (Tegyük fel, hogy egy számlázó programban sosem fogok beleütközni a float korlátaiba). Ha nincs több BigDecimal mezőm, akkor használhatom a stabil db4o változatot. A másik meg, hogy szükségem lesz egy dump file formátumra. Egyrészt mivel féltem a már bent lévő adatokat, másrészt mert a mező típus konvertálás is nagy szívás egy meglévő db4o adatbázison, az egyik legegyszerűbb megoldás, hogy kidumpolom az adatbázist, majd típus refaktor után vissza.

Mi legyen a dump formátum. Kapásból a JAXB és Json jutnak eszembe, de ezek ahogy én tudom nem támogatják a referenciák kezelését. (Ha az objektum fában többszőr is előfordul ugyanaz az objektum, akkor visszatöltés után ne két, hanem csak egy objektum legyen, amire két helyről mutat referencia.

És itt jön kébe a YAML, amit pont tudja mindezt.

Előszőr a jYaml-t próbáltam, ami alapból egyszerűen működik, de régóta nem fejlesztik, és túlságosan is elnézően bánt a Yaml fájl hibáival. Aztán jött a Snakeyaml. Ez már tette a dolgát mint a kisangyal.

Objektum mentése:

Yaml yaml = new Yaml();
yaml.dump(eztmentemel, new FileWriter(file));

Objektum betöltése:

Yaml yaml = new Yaml();
DatabaseBackup backup = (DatabaseBackup) yaml.load(new FileInputStream(file));

Maga a Yaml file meg valahogy így néz ki.

!!net.anzix.nona.entity.DatabaseBackup
felhasznalok:
- email: geza@hutira.net
  partner: &id001
    partnerek:
    - &id005
      partnerek: []
      szamlak: []
      torzs: {adoszam: null, bankszamlaSzam: null, nev: Asztal kft., szamlaCim: Sötét út 20.,
        szamlaIrsz: '1111', szamlaVaros: Budapest}
      tulajdonos: *id001
....

Itt már rögtön látszik is a referencia kezelése. A partner objektum instance azonosítója id001, és a partnerek listában található egyik partner tulajdonos mezője pont erre mutat vissza referenciával.

Mivel a db4o-val egy paranccsal elmenti az objektumfát, ezért 3 sorban tudok backupolni és visszatölteni a backupot, és a yaml file-t minimálisan módosítgatva (segít a nagyerejű grep parancs) még a típus konverziókat is sikerült viszonylag kis fájdalom árán meglépni.

Yaml a mi barátunk.

Tags: ,

printStackTrace()

Monday, December 7th, 2009 | exception | 4 Comments

Régóta tervezem, hogy indítok egy blogot, amiben csak exceptionok vannak. Egy munkahelyi csatorna már van, ahol ilyenekkel bombázom a kolegákat, nem mintha megoldást várnék tőlük, sőt. De úgy gondolom, hogy az exceptionok olyan méltatlanul mellőzőtt ipari képződmények, amik maguk egyszerűségében és monotonitásában valódi művészi értéket hordoznak.

Persze igazából egy stack trace megosztó social site lenne jó. Napi exception, hasonló exceptionnel barátkozó fejlesztők, ismerős exceptionok bejelölése, stb.

Amíg a nagyra törő álmok megvalósulnak, addig is itt egy kategória, ahová mintegy ilusztárcióként feltöltök egy-egy szép darabot.

javax.ejb.EJBException: The bean encountered a non-application exception; nested exception is:
java.lang.reflect.UndeclaredThrowableException
at org.apache.openejb.core.ivm.BaseEjbProxyHandler.convertException(BaseEjbProxyHandler.java:358)
at org.apache.openejb.core.ivm.BaseEjbProxyHandler.invoke(BaseEjbProxyHandler.java:286)
at $Proxy54.existsValami(Unknown Source)
at hu.xxx.xxx.xxx.ValamiServiceV1X0Test.testExistsValami(ValamiServiceV1X0Test.java:59)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:73)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:46)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)
at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)
at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:165)
at org.apache.maven.surefire.Surefire.run(Surefire.java:107)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:289)
at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:993)
Caused by: java.lang.reflect.UndeclaredThrowableException
at $Proxy55.getEM(Unknown Source)
at hu.xxx.xxx.xxx.ValamiServiceV1X0.queryValamiFind(ValamiServiceV1X0.java:52)
at hu.xxx.xxx.xxx.ValamiServiceV1X0.existsValami(ValamiServiceV1X0.java:64)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.openejb.core.interceptor.ReflectionInvocationContext$Invocation.invoke(ReflectionInvocationContext.java:158)
at org.apache.openejb.core.interceptor.ReflectionInvocationContext.proceed(ReflectionInvocationContext.java:141)
at org.apache.openejb.core.interceptor.InterceptorStack.invoke(InterceptorStack.java:122)
at org.apache.openejb.core.stateless.StatelessContainer._invoke(StatelessContainer.java:221)
at org.apache.openejb.core.stateless.StatelessContainer.invoke(StatelessContainer.java:174)
at org.apache.openejb.core.ivm.EjbObjectProxyHandler.businessMethod(EjbObjectProxyHandler.java:217)
at org.apache.openejb.core.ivm.EjbObjectProxyHandler._invoke(EjbObjectProxyHandler.java:77)
at org.apache.openejb.core.ivm.BaseEjbProxyHandler.invoke(BaseEjbProxyHandler.java:281)
... 30 more
Caused by: java.io.NotSerializableException: org.apache.openejb.persistence.JtaEntityManager
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1156)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
at org.apache.openejb.core.ivm.BaseEjbProxyHandler.copyObj(BaseEjbProxyHandler.java:501)
at org.apache.openejb.core.ivm.BaseEjbProxyHandler.copy(BaseEjbProxyHandler.java:301)
at org.apache.openejb.core.ivm.BaseEjbProxyHandler.invoke(BaseEjbProxyHandler.java:283)
... 45 more

(Élesebb észrevehetik, hogy egy maven által futtato junit tesztből próbálok standalone összerántani egy EJB konténert OpenEJB-vel, hogy konténeren kívül futtathassam az integrációs teszteket. Egyelőre kevés sikerrel)

Db4o tapasztalatok

Sunday, December 6th, 2009 | Uncategorized | 2 Comments

Aktuálisan kedvenc állatorvosi lovam, egy wicket + guice + warp-persistes alkalmazás. Illetve a technológiák gyakran változnak benne, például alig egy órája dobtam ki az utolsó JPA függőséget is belőle, és teljesen átáltam db4o-ra. (A JasperReport helyét is könnyen átveheti a BIRT, illetve a Salve is sorban áll tesztelésre)

Az első tapasztalatok:

  1. Nem árt tudni, hogy GPL, tehát az über titkos üzleti alkalmazásban nem használhatom.
  2. Érezhetően gyors. Mondjuk iszonyú kis adatbázisom van, gondolom jól betölti az egészet a memóriába.
  3. Az API-ja csábítóan egyszerű. Queryket írni annonymous-inner-class-okkal igazi fényűzés.
  4. Cascade-ra ugyanúgy gondolni kell. Objektum lekérésnél alapból 5 mélységű a rekurzió, de update/store-nál default nincs rekurzió. Eltartott egy ideig míg rájöttem, miért nem megy rendesen. A rekurzió szintje állítható teljes adatbázis, entitás és mező szinten is. Csak kérni kell.
  5. A séma változásra ez is érzékeny (különösen, ha az objektum hierarhia változik)
  6. A managed-detached objektumokra ugyanúgy figyelni kell. Amíg nem sikerült összeraknom számos esetben elszabadultak az objektumok, és megsokszorozódtak. (Csak egy oc.store() metódus van, és ha detached-et akarok updatelni, akkor lelkiismeret furdalás nélkül létrehoz új objektumot.)
  7. Alapból tud olyat, hogy minden objektumnak van ID-ja (van UUID-ja is, de az nem kellett). Ebben az a furcsa, hogy az ID egy belső dolog, és nem az entitásban van tárolva, hanem API-ból lekérdezhető: kedves konténer, ennek a managed objektumnak mennyi az ID-ja? Az viszont nem meglepő, hogy egy detached objektumnak már nem lehet elkérni. Talán van valami merge itt is, de még nem találtam meg. Igaz nem is nagyon kellett, mert a Wicket detachable modeljei pont tudják azt, hogy az ID-t eltárolják, amíg még managed az entitás, és az alapján visszatöltik, amikor kell.
  8. Nagyon barátságosan tesztelhető, mert egy statikus függvény meghívásával át lehet az egészet in-memory db-re állítani. Onnantól kezdve csak iniciaálizálni kell benle pár adatot, és mehetnek az integráiós(abb) tesztek.
  9. Constraintek Java API-ból adhatóak meg. A Bean Validation Frameworkot valószínű 5 sorból be fogom tudni kötni.
  10. Nincs jó felület, ahol meg tudom nézni a tartalmát, és alap szerkesztéseket tudok végrehajtani. Egy BeanShell-t próbáltam már rá állítani, de még nem sikerült összelőni.
  11. Elég jó doksija van, pedig még csak főleg a tutorialt olvastam, a reference manualnak csak a részeit.
  12. Érdekes következménye van annak, hogy minden objektumot el tud menteni. Pl. a JPA-ból kiszelektált csoda objektumokat nem érdemes rögtön elmenteni, mert előhúzás után a lazy mezők rosszul fogják érezni magukat. Ugyanígy a BigDecimal serializálása alapból nem triviális, ennek a kezelésére külön kiterjesztést kell bekapcsolni (bekapcsolt kiterjesztéssel szerkesztett db fájlt olvasni legközelebb szintén csak bekapcsolt kiterjesztéssel rendelkező konténerrel lehet).

Szóval egyelőre barátok maradunk és bent marad. Még meg kell nézni a tranzakciókat, a Bean Validation Frameworkot bele reszelni, és majd byte[]-el kell még tesztelni.

Tags:

Meta

Search