futas kozbeni osztalycsere?

Fórumok

Hello,

Olyan megoldast keresek, hogy egy osztalyt ki tudjak cserelni menet kozben, tehat pl megvaltoztatni
a konstruktorat, metodusparameterezest, visszateresi erteket, etc.

jRebel nem jatszik, fizetos.

a beepitett JVM API (amit a debuggerek hasznalnak) pedig korlatozott ilyen szempontbol.

Milyen megoldasokat ismertek?

UPDATE: ja, azt nem irtam: fontos lenne, hogy az uj osztalyleiro betoltesekor a mar meglevo, hasznalatban levo peldanyok is transzformalodjanak, azaz az allapotteruk megmaradjon.

Hozzászólások

Konkret megoldast nem ismerek, de megnezheted a Play! keretrendszert, abban is valami hasonlot csinalnak es openszosz!

----------------------
"ONE OF THESE DAYS I'M GOING TO CUT YOU INTO LITTLE PIECES!!!$E$%#$#%^*^"

Ha nincs direkt hivatkozas az adott osztalyra, akkor egy uj classloaderrel ujra be tudod tolteni az osztalyt. A regit meg majd a GC kisopri, es akkor az osztaly is megy a lecsoba. De ha van hivatkozas az osztalyra, akkog a CL bent marad, es akkor lesz egy classloader leaked. Lasd meg: osgi.

A debugger API-k nem véletlenül korlátozottak, hanem azért, mert metódus szignatúra, vagy új mező esetén az összes példány, vagy a class teljes memóriaképét át kellene faragni, illetve az összes hivatkozásnál refaktorolni kellene.
(gondolom egy idegen osztály mezőjére való hivatkozást, de lehet hogy a metódus hivatkozást is, a JIT sima bázis-relatív címzésre fordítja, így többé nem követhető, hogy kik érik el a mezőt)

Ezt nem implementálták még. Csodálkoznék, ha lenne erre működő megoldás, úgyhogy subscribe.

Mondjuk új adattagot definiálsz egy osztályban és azt ráadásul egy új konstruktor is inicializálná (ez lenne mondjuk egy kontruktor csere, ha jól gondolom). Na most a már létező példányaidban annak nemigen lesz értéke, hacsak az összes példányt újra létre nem hozod vagy valami mást ki nem találsz rá, hogy értéket kapjon a régebbiekben is. Ha jól olvastam, akkor a JRebel-nél is van ilyen "hátulütő", ugyanis nem hozza létre a példányokat újra. Így az erre az új adattagra épített esetleges műveleteknél a régebbi példányok mondjuk könnyedén dobhatnak nullpointert, ha jól gondolom.
Annak meg sok értelmét önmagában nem látom, hogy definiál valaki egy új adattagot, de semmit nem kezd vele. Valószínűleg a JRebel már foglalkozik egy ideje ezzel, nem hiszem, hogy triviális lenne a megoldás a problémára.
Vagy mondjuk valami külsős kód épül a saját belsős kódjaidra, te meg szignatúrát cserélsz valamelyik saját metódusodnál.

De az is lehet, hogy csak nem értem az egészet, ebben az esetben viszont örülök mindenféle felvilágosításnak. :)

Biztos jobban tudod az adott feladat kontextjet, nekem nem szimpatikus az, hogy felig JVM felig Java kodban turhazik az ember. Ha mar JVM-et kell ezert patchelni, az erdekesen hangzik.

En inkabb valasztanek olyan megoldast (mondjuk interfeszekkel) ami normal Java szinten kepes egyik osztalyt lecserelni futas kozben egy masikra, azaz mendzselhetove kell oket varazsolni. Ez a kovetkezo hasonlo cserenel pont jol fog jonni.

Nyilvan ez mar mukodo es futo kodon keson jovo otlet, ebben az esetben ket lepesben is megoldhato a feladat, elso lepeskent lecsereled a nem verzio-aware osztalyokat verzio-aware-re (ehhez nem szukseges allapotter transzformacio, azaz nincs szukseg osztaly specifikus tamogatasra), majd azokat utana mar a normalis Javas interfeszen at tudod menedzselni.

Te jó ég, ezt hogy? Érdekel a működési elv, mert azt gondoltam, hogy a JVM ezt nem engedi.

Ha az osztály tagjai ugyanazok maradnak (signature szerint legalábbis), akkor az osztály elég jól cserélhető egy saját class loaderrel, ami kicsit megpeccseli a betöltött osztályokat. De paraméterlista változását vagy más hasonló tagváltozást nem tudom hogyan lehetne megcsinálni.

tobb otlet is van, valszin a JDK -t patchelni kell majd... :)

amit patcheles nelkul meg lehet csinalni, hogy minden betoltott osztalyt bytecodetranszformalsz, a metodushivasokat lecsereled egy olyan statikus metodushivasra a sajat osztalyodba, ami igazabol reflectionnel keresi ki a megfelelo metodust, amit hivni kell => tetszolegesen tudsz routeolni metodushivasokat.
a perf overheadet ki kell merni, 6os JVM mellett elvileg 1-2%.

a masik otlet, amihez a patch kell, hogy a dynamicinvoke -ot kicsit felokositani.

mindket otlettel leginkabb a globalis Class csere a gond, h a kanonikus neveden is az uj legyen, ezen dolgozni kell meg.

Beépített megoldásként az URLClassLoader-rel próbálkozhatsz.

Amúgy meg osgi-nek érdemes utánanézni. Én ugyan nem használtam még, de tudtommal az egyik feature-je a hot redeployment.

Az, hogy a meglévő objektum példányok átkonvertálódjanak ezekkel a megoldásokkal automatikusan nem fog működni. Esetleg kézzel átmásolgathatod a tulajdonságokat. Ha van egy közös tárolód amiben a példányok vannak, akkor új class-ból instance aztán a régi instance-ból copy. Ha bean szerű, akkor BeanUtils.copyProperties() megcsinálja az összes propertyre.

Ezen kívül maradnak a bytekód variáló megoldások. javassist, cglib.

A címből azt hittem, valami tornatanárnak támadt érdekes problémája :)

--
"If God exists...why is there no porn of Him?"

Tehát van egy rakás x,y,z,... = new LóFütty(blabla, rand()), s te az x,y,z,... LóFütty példányokat akarod egyszerre lecserélni, úgy, hogy az adattagok értékei megmaradnak, de bizonyos metódusok kódja megváltozik (+egyéb igények), anélkül, hogy a változóknak újra értéket adnál?

--
"SzAM-7 -es, tudjátok amivel a Mirage-okat szokták lelőni" - Robi.

mondjuk van egy osztalyod (Cica extends Thread), benne egy List<Integer>, a run() mondjuk meghivja minden masodpercben az x() metodust, ami a listahoz hozzaad egy szamot, majd kiirja a listat.

aztan rajossz, hogy mast szeretnel. x() -et le akarod cserelni masra, de ugy, hogy az allapotter maradjon (azaz a lista aktualis elemei, a countered allasa), etc.

ez most mukodik :-)

A byte code buherálás, meg a dinamikus proxy nem jó ötlet szerintem, mert a végén a jó édesanyja sem fogja tudni mi fut, és az hogy jött össze.
Mondjuk én úgy csinálnám, hogy a debugger apival le lehet kérdezni egy osztály összes példányát, meg azt is, ki hivatkozza. Ezután újra kell példányosítani az összes példányt az új osztállyal (új classloaderrel), közben meg kell csinálni a szükséges transzformációkat. Erre egy helper osztály megadható pl. De ha trivi, akkor property másolással is mehet, ahogy előttem már mondták. Aztán ki kell javítani (szintén reflectionnel) az összes hivatkozást. (Meg persze a régi objektumok összes referenciáját ki kell nullozni)
Persze így is van pár dolog:
Az új osztálynak legalább interfész szinten kompatibilisnek kell lennie.
Csere közben ne fusson semelyik példányon szál, mert akkor megjósolhatatlan lesz az eredmény. Persze ha a cserélendő osztály támogatja valahogy (pl. interrupt) a cserét, akkor spec. esetben talán megoldható.
Szerintem azért ez egy kicsit erőltetett, mert java-ból nem lesz php, és járó motort sem szokás szerelni. De ahogy érzed :-)

Én megnézném a helyedben a Terracottát. Elég sokat már most is tud, és amit jelenleg nem, arra azt írják, hogy tervezik.

Ha TDK-ban gondolkodsz, akkor ez egy olyan téma lehetne, ahol az implementációnak van esélye production kóddá válni.

Tény, hogy nem 1 db VM-en belül oldja meg, és vannak korlátok, de ez már egy működő, bejáratott megoldás.

Üdv,
Gergely

Pénzbe kerül igen a jRebel.. én most fizettem érte <~100$.
Ez nem hiszem, hogy olyan komoly összeg.. főleg a fejlesztéseknél.

Ugyan te kérdeztél először (milyen megoldás...), de had kíváncsiskodjak egy kicsit: mire lenne jó ez?

Ja. Kicsit megakadtam ott, h egy osztállyal minek ilyet csinálni, ha a többi csak a régi interfészről (API) tud, de nyilván ilyenkor a többit is cseréled vele...

Nyilván megvan a megfelelően jól körülhatárolható terület, ahol ez alkalmazható is. Azért néhány életszagú use case-re kíváncsi lennék.

ez nekem kicsit ugy hangzik, mint kernel panic utan ksplice-cal defibrillalni a linuxot :)
ha mar felborult az a rendszer, amit javitani kell, akkor jobb egy tisztabb restart, mint az elozo hiba miatt kitudja milyen allapotteret probalni az uj kodba migralni.

szoval szemely szerint azt mondanam, hogy ha kellhet is ilyen, akkor valami olyasmit csinalnek, hogy a teljes allapotteret hibernalni, leloni az alkalmazast, elinditani az ujat, allapotteren ha kell migralni, akkor megtenni, majd kiolvasztani.

persze en nem ertek a javahoz annyira mint te. :)

Tyrael

1. azért ez egy elég durva overkill egy olyan problémára, amit máshol már megcsináltak nagyon egyszerűen: új szerverre deploy, load-balancer vagy DNS-routing átkapcsol, és done. Visszakapcsolás ugyanolyan egyszerű.

2. osgi, ahogy már sokan írták. Bár nem tudom hogy mennyire eszi meg, ha a public service osztályok metódus-szignatúrája változik, de valami ilyesmi az ígérete amúgy.