A használata egy jar, hogy egyszerűsítse az alkalmazás raprostraneniya

Valaki azt mondta, hogy a történelem ismétli önmagát, először tragédia, másodszor komédia. Nemrég tapasztaltam ezt első kézből, amikor kellett telepíteni a Java-alapú alkalmazás által végrehajtott ügyfél. Megtettem ezt sokszor, és ez mindig tele van nehézségekkel. A hibák mindenhol létezik: az összeszerelését az alkalmazás JAR-fájl, az írás a dob szkripteket DOS és Unix (és Cygwin) ellenőrzi a vállalkozás a telepítés az összes környezeti változót a felhasználó számítógépén. Ha minden simán megy, az alkalmazás elindul és rendesen. Ha valami elromlik, és ez általában történik - az eredmény igényel sok órás munka közvetlenül a vevőnek.

Miután nemrégiben tárgyalásokat megzavart kliens segítségével a ClassNotFound kivételek, úgy döntöttem, hogy elég volt. Megyek, hogy megtalálja a módját, hogy a csomagolás a kérelmemet egyetlen JAR-fájlt, és így az én ügyfelek egy egyszerű mechanizmus (hasonló java -jar) futtatni.

Az eredmény egy One-JAR - egy nagyon egyszerű szoftver megoldás a csomagolás, amely egy egyéni Java osztály betöltő dinamikusan betölteni minden osztály egyetlen archív, megőrizve a szerkezetét kiegészítő JAR-fájlokat. Ebben a cikkben fogom leírni a folyamat kidolgozása One-JAR, majd megmondja, hogyan kell használni, hogy osztja az alkalmazás egyetlen fájlként.

Felülvizsgálata Egy JAR

Mielőtt részletesen ismertető One-JAR, hadd először megvitatják a célok már tűzte. Úgy döntöttek, hogy a One-JAR archívum Bíróság:

  • Végrehajtható a java -jar mechanizmus.
  • Legyen képes tartalmazza az összes szükséges alkalmazás fájlok, azaz mindkét osztály és a források eredeti formájukban (nem kicsomagolva).
  • Van egy egyszerű belső szerkezete lehet összeszerelni csak egy korsó programot.
  • Láthatatlan az eredeti bejelentés, vagyis az eredeti kérelem kell csomagolni egy One-JAR archívum módosítás nélkül.

Problémák és megoldások

A legnagyobb probléma az, hogy én az eljárás során a fejlődő Egy JAR volt, hogyan kell betölteni a JAR-fájlok belsejében egy JAR-fájlt. Osztálybetöltő Java sun.misc.Launcher $ AppClassLoader. aki dolgozik elején java -jar. tehetünk csak két dolgot:

  • Load osztályok és a források a gyökere a JAR-fájlt.
  • Load osztályok és forrásokat talált a bázisok kód (codebase), amelyre a tulajdonság META-INF / manifest.mf Class-Path.

Sőt, szándékosan figyelmen kívül hagyja a környezeti változó beállítása CLASSPATH vagy -CP parancssori argumentum. meg kell adni. Ő még azt sem tudja, hogyan kell betölteni osztályok és forrásokat a JAR-fájl tartalmazza másik JAR-fájlt.

Egyértelmű, hogy meg kellett biztosítani mindezt elérni a céljaimat a One-JAR.

1. megoldás: Nagyítás támogató JAR-fájlok

Az első kísérlet létrehozása egyetlen futtatható JAR-fájl tenne a nyilvánvaló és kiegészítő JAR-fájlokat a megvalósítandó JAR-fájl, amely azt fogja hívni main.jar. Ha van egy osztály nevezett com.main.Main alkalmazások és az a feltételezés, hogy ez attól függ, hogy a két osztály com.aA (belső a.jar) és com.bB (belső b.jar), Egy-JAR fájl így fog kinézni :

Információk az osztályban A.class kezdetben a.jar, valamint az eredeti helyére B.class elveszett. És bár úgy tűnik, egy kicsit, akkor komoly problémákat okozhat, hogy hamarosan megmondani.

Egy JAR és FJEP

A közelmúltban megjelent a program neve FJEP (FatJar Eclipse Plugin) támogatja az épület lapos JAR-fájlok közvetlenül belül Eclipse. Egy JAR már integrált FatJar végrehajtásának támogatására a JAR-fájlok azok elhelyezését. További információk találhatók a „források” című részben.

Kicsomagolás kiegészítő JAR-fájlt a fájl rendszert hozzon létre egy lapos szerkezet eltarthat egy darabig. Arra is szükség van olyan elrendezést, programok, mint a hangya, hogy csomagolja ki és újra archív kisegítő osztályok.

Ettől eltekintve kis kellemetlenség, de gyorsan ütközött két komoly probléma, ha belül elhelyezett archív kiegészítő JAR-fájlok:

  • Ha a.jar és b.jar tartalmaznak erőforrás azonos elérési utat (mondjuk log4j.properties), melyiket fogod választani?
  • Mit tennél, ha egy engedély b.jar kifejezetten előírja, hogy terjeszthető módosított formában? Nem lehet szétosztani oly módon, amely nem sérti az engedély feltételeit.

Úgy éreztem, hogy ezek a korlátozások eltérő megközelítést igényel.

2. megoldás: jegyzékfájlok Class-Path

Úgy döntöttem, hogy vizsgálja meg a mechanizmus a java -jar. betölti a felsorolt ​​osztályok egy külön fájlban az archívumban nevű META-INF / manifest.mf. Class-Path iránynak tulajdon. Reméltem, hogy képes felvenni más fájlokat az eredeti osztály betöltő. Itt van, hogy ez hogyan néz ki egy fájlt One-JAR:

URLClassloader az alap osztály sun.misc.Launcher $ AppClassLoader. támogatja egy meglehetősen rejtélyes URL szintaxist, amely lehetővé teszi az erőforrások, belsejében egy JAR-fájlt. A szintaxis a következőképpen néz ki: jar: file: /fullpath/main.jar /a.resource !.

Elméletileg a belépés a JAR fájlt, akkor érdemes használni valami hasonló jar: file: !! /fullpath/main.jar /lib/a.jar /a.resource. de sajnos ez nem működik. A JAR-fájl kezelő protokollt úgy véli, csak az utolsó elválasztó karaktert! „/” Mivel a JAR-fájl mutatót.

De ez a szintaxis tartalmazza a kulcsot az végső megoldás „One-JAR”.

Működik? Különben is, úgy tűnt addig, amíg nem költözött main.jar fájlt egy másik helyre, és megpróbálta futtatni. Összeszerelhető main.jar Csináltam egy elnevezett könyvtárban lib és tedd a.jar és b.jar fájlokat. Sajnos, a rakodó alkalmazás osztályok egyszerűen válassza kiegészítő JAR-fájlok a fájlrendszer. Nem osztályokat betölteni JAR-beágyazott fájlokat.

A megjelenése JarClassLoader

Ebben a szakaszban, csalódott voltam. Hogyan tudnám, hogy egy alkalmazást, hogy töltsék fel osztályok egy lib könyvtárban belül saját JAR-fájlt? Úgy döntöttem, hogy létre kell hozni egy egyedi osztálybetöltője felemelni ilyen terhelés. Írásban osztálybetöltője - ez nem egy elvégzendő feladat könnyedén. Annak ellenére, hogy nem is olyan bonyolult valójában osztály rakodógépek ilyen mély hatással alkalmazás ellenőrzi, hogy ez lesz nehéz diagnosztizálni és értelmezni hibák, amikor előfordulnak. Bár részletes vizsgálatát a folyamat osztály betöltése túlmutat a jelen cikk (lásd. „Resources”), azt meg fogja vitatni az alapfogalmak, hogy biztosan kap a maximális mennyiségű információt a következő tárgyalás.

terhelési osztály

A JVM átjön egy tárgy, amelynek osztály ismeretlen, ez okozza a osztálybetöltése. Work osztálybetöltője, hogy megtaláljuk a bájtkódot az osztály (név alapján), majd bájtok a JVM, ami összeköti őket, hogy az a rendszer többi részéhez, és elérhetővé teszi új osztályát futó kódot. A legfontosabb osztály a JDK java.lang.Classloader és az ő módszere loadClass. amelynek a szerkezetét az alábbiakban mutatjuk be:

A fő belépési pont az osztály ClassLoader loadClass () metódust. Vegye figyelembe, hogy ClassLoader egy absztrakt osztály, de nem nyilatkoznak az elméleti módszerekkel. Ez arra utal, hogy a loadClass () metódus a fő szempont. Tény, hogy ő nem: menjen vissza a régi szép napok a JDK 1.1 classloaders, loadClass () volt az egyetlen hely, ahol lehet hatékonyan kiterjeszti a classloader, de mivel JDK 1.2 érdemesebb egyedül maradt, és nem zavarja csinál valamit, ami úgy tervezték, nevezetesen:

  • Annak ellenőrzése, hogy az osztály már be van töltve.
  • Ellenőrizze, hogy a szülő osztály betöltőt egy osztály.
  • Hívjon findClass (String name), hogy a származtatott classloader az osztály betöltésére.

ClassLoader.findClass () végrehajtás létrehoz egy új kivétel ClassNotFoundException. Mivel az első módszer, amelyek célja a végrehajtása során egy speciális osztály betöltő.

Amikor a JAR-fájl nem JAR-fájlt?

A következő lépés az volt, hogy halad végig az összes rekordot JAR-fájlt a kérelmemet, és betölti memóriába, amint az 1. lista:

1. listán iterálás megtalálni beágyazott JAR-fájlok

Megjegyezzük, hogy LIB_PREFIX telepített lib / bar. és MAIN_PREFIX - a fő / vonal. Szeretném ciklus betölteni a memóriába történő felhasználásra bytecode osztályok, kezdve lib / vagy fő /. és figyelmen kívül hagyja a többi elem a JAR-fájlt.

főkönyvtártól

Beszéltem a szerepe az alkönyvtár lib /, de mi ez a katalógus fő /? Röviden: a küldöttség módot classloaders igényel helyezi a fő osztály com.main.Main saját JAR-fájlt, hogy tudott találni a könyvtár osztályok (amelyektől függ). Az új JAR-fájl így néz ki:

Az 1. listában fenti loadByteCode () metódus kap az adatfolyam a JAR-fájl elem neve, az elem tölti be a byte memóriát és rendeli őket két név, attól függően, hogy az adott elem egy osztály vagy erőforrás. Ezt leginkább egy példát. Tegyük fel, hogy a.jar tartalmaz A.class osztály és az erőforrás A.resource. A osztálybetöltője One-JAR létrehozza az alábbi térkép szerkezetet elemzi JarClassLoader.byteCode. amelynek egy kulcs-érték pár osztályok és két kulcsot a forrás:

A belső szerkezet 1. ábrán látható egy-JAR

Ha jobban megnézed az 1. ábrán látjuk, hogy az osztály bejegyzés kulcsa alapján az osztály nevét, és az erőforrás elemek azonosítása függően pár nevek: a globális nevek és helységneveket. Ez a mechanizmus arra használjuk, hogy a konfliktus megoldására az erőforrás nevét: ha két könyvtár JAR-fájl határozza meg a forrás az azonos globális nevet fogja használni a helyi nevek alapján a verem a hívó fél. lásd a „források” című részben talál.

Keresés osztályok

Emlékezzünk vissza, hogy abbahagytam az én osztály betöltése a felülvizsgálati eljárás findClass (). findClass () módszer kapja a nevét az osztály, mint a String és meg kell felderíteni és azonosítani a bájtkódot ez a neve. Mivel loadByteCode kedves épít térkép között az osztály neve és bytecode, hogy észre ezt most nagyon egyszerű: meg kell találni a bájtkódot alapján osztály nevét, és a hívás defineClass (). amint azt a 2. lista:

2. lista fragmens findClass ()

Letöltés erőforrások

A fejlesztés során egy-JAR findClass volt az első dolog, amit kapott dolgozik, mint egy prototípus. De amikor elkezdtem terjeszteni bonyolultabb alkalmazások, rájöttem, hogy kell foglalkozni erőforrás terhelés, valamint osztályok. Ez az, ahol a munka kezdett sorra kudarcot vallanak. Meghatározott ClassLoader, hogy felülbírálja a megfelelő módszert kell keresni forrásokat, én választottam ki, amellyel én leginkább ismerős. Lásd a 3. listát:

3. lista getResourceAsStream () metódus

Bell ezen a helyen kellett volna hangzó: nem tudtam megérteni, hogy miért a keresést felhasznált források URL. Szóval, azt figyelmen kívül hagyta ezt végrehajtását és beilleszteni a saját, látható a 4. listában:

4. lista Az eljárás megvalósítása getResourceAsStream () egy-egy JAR

Az utolsó akadály

Az új végrehajtása getResourceAsStream (). Úgy tűnt, hogy ezt a trükköt, amíg megpróbáltam nem One-JAR olyan alkalmazás, amely betöltött erőforrás az URL url = object.getClass () getClassLoader () getResource () ..; Itt a program nem. Miért? Mivel a végrehajtás egy visszatérítendő alapértelmezett ClassLoader URL. Ez volt egyenlő null, ami megtörte a kódot a hívó fél.

Attól a pillanattól kezdve, minden összezavarodott. Meg kellett kitalálni, hogy melyik URL kell használni, hogy olvassa el az erőforrás a JAR-fájlt a / lib. Kellene lennie, például jar: file: main.jar lib / a.jar com.a.A.resource !!?

Megpróbáltam minden kombinációt, hogy tudtam képzelni - melyek közül egyik sem működött. A jar: szintaxis egyszerűen nem támogatja a beágyazott JAR-fájlok, amelyek engem előtt nyilvánvalóan reménytelen helyzet az én megközelítésem a One-JAR. Bár a legtöbb alkalmazás nyilvánvalóan nem használ ClassLoader.getResource. Néhány biztosan nem, és nem voltam boldog kivétel, mondván: „Ha az alkalmazás ClassLoader.getResource () nem használható One-JAR”.

És végül, a döntést.

Megpróbálom kitalálni a jar szintaxis. Elestem mechanizmust, amely a Java Runtime Environment megjelenítéséhez URL-előtag rakodók. Ez volt a kulcs kellett, hogy megoldja a problémát findResource. Én csak kitalálni a saját protokoll előtag nevű onejar. Én aztán Térkép az új előtagot protokollkezelőt, ami visszaadja a byte stream az erőforrás, ahogy az 5. lista jébe 5. lista képviseli kódot két fájl, JarClassLoader és egy új fájl nevét com / simontuffs / onejar /Handler.java.

5. lista findResource és onejar protokoll:

bootstrapping JarClassLoader

Ezen a ponton, akkor valószínűleg csak egy az a kérdés: hogyan lehet beszúrni JarClassLoader a boot sorrendet úgy, hogy indulhasson loading osztályok a One-JAR fájlt először? A részletes magyarázatot túlmutat a jelen cikk; de, a következők. Ahelyett, hogy a fő osztály com.main.Main a META-INF / manifest.mf / Main-Class hoztam létre egy új osztályt a csomagtartó com.simontuffs.onejar.Boot. amely jelzi a Main-Class attribútumot. Az új osztály a következőket teszi:

  • Létrehoz egy új JarClassLoader.
  • Ez használ egy új boot betöltő com.main.Main fő / main.jar (az elem META-INF / manifest.mf Main-Class main.jar).
  • Okok com.main.Main.main (String []) (vagy más néven Main-Class. Megjelölt main.jar / manifest.mf file) töltik osztály és a leképezés main () hívást. Megadva a parancssorban One-JAR érvek kerülnek az alkalmazás fő módszer változtatás nélkül.

Összefoglalva

Ha távol minden szédül, ne aggódj: A One-JAR sokkal könnyebb, mint hogy kitaláljam, hogyan működik. Az Advent a plug-FatJar Eclipse Plugin (lásd. FJEP lásd "Resources"), Eclipse felhasználók hozhatnak létre egy-JAR-alkalmazás ellenőrzésével varázsló. Függő könyvtárak kerülnek a könyvtárban / lib, a fő program, és az osztályok kerülnek a fő / main.jar, a META-INF / manifest.mf fájl van írva automatikusan. Ha ön használ JarPlug (ismét lásd a „források” című részben), akkor belenézel a JAR-fájlt, és futtassa az IDE.

Általában egy-JAR egy egyszerű, de hatékony megoldást arra a problémára, csomagolási célokra forgalmazás. Azonban ez nem alkalmas az összes lehetséges forgatókönyvet. Például, ha az alkalmazás a régi stílusú osztálybetöltő JDK 1.1, ami nem ruházhatja át a szülő, ez az osztály betöltő sikertelen keresésekor osztályok egy beágyazott JAR-fájlt. Akkor leküzdeni ezt az akadályt létrehozásának és telepítésének „wrapper” (csomagolás) a osztálybetöltője módosítani az ellenszegülő rakodó, bár ez maga után vonná a bytecode manipulációs technikák segítségével olyan eszközök, mint a javassist vagy Byte Code Engineering Library (BCEL).

Ön is problémákba ütközik amikor valamilyen speciális típusú classloader által használt beágyazott alkalmazások és web-alapú szerverek. Különösen akkor van egy probléma osztály rakodók azonnal nem kereskednek a szülő rakodó, vagy azok, akik keresik a codebases a fájlrendszerben. Ebben az esetben segíthet szerepel a One-JAR mechanizmus, amely lehet bővíteni JAR-fájl a tagok a fájlrendszerben. Ez a mechanizmus vezérli attribútuma One-JAR-kibontása a fájl META-INF / manifest.mf. Esetleg próbálja meg bytecode manipuláció, hogy módosítsa a osztálybetöltője on the fly, működés közben, anélkül, hogy veszélyeztetné a integritását a kiegészítő JAR-fájlokat. Ha ezt az utat, hogy minden egyes esetben előfordulhat, hogy szükség van egy speciális „wrapper” osztály betöltő.

A „források” című részben a linkeket letölteni a plug-inek FatJar Eclipse Plugin és JarPlug, és további információt a One-JAR.

Letölthető Resources

Kapcsolódó témák