Plugin deploy
A 2. fejezet lefedi a Maven projekt beállítást, a build konfigurációt és a
ServiceLoader regisztrációt. Ez a fejezet onnan indul, ahol az elkészült
target/plugins/{artifactId}/ könyvtár már ott van – és megmutatja,
hogyan kerül a szerverre, hogyan frissíthető újraindítás nélkül, és mik
a korlátok.
11.1 A plugin a szerveren
A platform egy dedikált plugins könyvtárat figyel, amelyet a
PLUGINS_DIR környezeti változó jelöl ki. A könyvtáron belül minden
plugin a saját alkönyvtárában él – ennek neve lesz a plugin azonosítója.
plugins/ ← PLUGINS_DIR
plugin-example/ ← plugin neve (alkönyvtár neve)
plugin.jar
plugin.properties ← opcionális konfiguráció (lásd 11.2)
lib/
custom-driver.jar ← saját függőségek (ha vannak)
plugin-other/
plugin.jar
A platform induláskor beolvassa a plugins könyvtárat, és futás közben is folyamatosan
figyeli (→ 11.3). Minden alkönyvtárból betölti
a plugin.jar-t és a lib/ tartalmát, majd a ServiceLoader
fájl alapján regisztrálja a tool-okat. Ha egy alkönyvtárban nincs plugin.jar,
a platform kihagyja.
artifactId-dal – ez nem
véletlen, a parent POM a build kimenetet erre a névre állítja be. Így a
target/plugins/ tartalma közvetlenül átmásolható a szerver
plugins/ mappájába.
11.2 Éles telepítés
A szerver Docker Compose-ban fut, a plugins könyvtár volume-on keresztül van becsatolva. Új plugin telepítése két lépés, újraindítás nélkül:
- Másold a build kimenetét a szerver plugins könyvtárába – a platform kb. 2 másodperc múlva automatikusan betölti.
- Ellenőrizd a logokban, hogy a tool-ok regisztrálódtak.
# Példa – plugin telepítése
cp -r target/plugins/plugin-example /srv/plugins/plugin-example
# Betöltési logok ellenőrzése
docker compose logs -f backend | grep -i plugin
Plugin frissítése éles környezetben is újraindítás nélkül történik: a régi alkönyvtár törlése, majd az új bemásolása (hot-replace, 11.3 fejezet). A frissítés pillanatában futó kérések a régi verzióval befejeződnek, az új kérések már az új verziót kapják.
plugin.jar-t, mert a lib/ is változhatott.
11.3 Hot reload – újraindítás nélkül
A platform WatchService-szel figyeli a plugins könyvtárat. Alkönyvtár megjelenésekor automatikusan betölti az új plugin-t, eltűnésekor eltávolítja. Ez lehetővé teszi a fejlesztési körök gyorsítását anélkül, hogy a teljes platformot újra kellene indítani.
Hot-add – új plugin betöltése
Másold be a plugin alkönyvtárát a PLUGINS_DIR-be.
A platform ~2 másodperc várakozás után (JAR másolás befejezéséhez) betölti.
# Új plugin bedobása futó platformba
cp -r target/plugins/plugin-example /srv/plugins/plugin-example
# Logban megjelenik:
# [PluginLoader] Plugin betöltése: plugin-example
# [PluginLoader] plugin-example – tool regisztrálva: example_tool (domain=null)
# [PluginLoader] plugin-example – betöltve: 1 tool, 0 configurer.
Hot-remove – plugin eltávolítása
Töröld a plugin alkönyvtárát. A platform azonnal eltávolítja a tool-okat a regisztrációból – új kérések már nem kapják meg a tool-t.
# Plugin eltávolítása futó platformból
rm -rf /srv/plugins/plugin-example
# Logban megjelenik:
# [PluginLoader] Plugin könyvtár eltűnt, unload: plugin-example
# [PluginLoader] Plugin eltávolítva: plugin-example
Hot-replace – frissítés újraindítás nélkül
Hot-replace a törlés + bemásolás kombinációja. A platform az eltűnéskor unloadolja a régi verziót, a megjelenéskor betölti az újat.
# 1. Régi verzió eltávolítása
rm -rf /srv/plugins/plugin-example
# 2. Új verzió bemásolása (a platform ENTRY_DELETE után ENTRY_CREATE-t vár)
cp -r target/plugins/plugin-example /srv/plugins/plugin-example
# Logban:
# [PluginLoader] Plugin könyvtár eltűnt, unload: plugin-example
# [PluginLoader] Plugin eltávolítva: plugin-example
# [PluginLoader] Plugin betöltése: plugin-example
# [PluginLoader] plugin-example – betöltve: 1 tool, 0 configurer.
plugin.jar-t írja felül valaki a könyvtár törlése nélkül,
nem keletkezik ENTRY_DELETE esemény a szülő könyvtáron –
a platform nem észleli a változást. Mindig a teljes alkönyvtárat kell
törölni és visszamásolni.
Korlátok és viselkedés
| Szituáció | Viselkedés |
|---|---|
| In-flight kérés unload közben | A futó kérés befejezi magát – a tool Java példánya él, amíg van rá aktív referencia. Új kérések már nem kapják meg az eltávolított tool-t. |
@Cacheable cache unload után |
Spring cache bejegyzések megmaradnak unload után. Ha a plugin új verziója más eredményt ad ugyanarra a cache kulcsra, rövid ideig stale adatot szolgálhat ki. TTL vagy manuális cache evict oldja meg. |
| Orchestrátor in-flight kérés | Az unload pillanatában már példányosított orchestrátor példányok befejezik a kérést. A supplier eltávolítása csak az új kéréseket érinti. |
| IPluginConfigurer bean-ek | A configurer által regisztrált extra Spring bean-ek (pl.
EntityManagerFactory) az unload során automatikusan
eltávolítódnak – hot-replace után az új konfigurátor frissen regisztrálja őket. |
| System prompt supplement | ArangoDB-ben marad (szándékos) – újrabetöltéskor az
OverwriteMode.ignore megakadályozza a duplikációt,
a konfiguráció megőrződik. |
Hot reload fejlesztési munkafolyamat
A platform lassan indul (Spark, modellek betöltése). Hot reload-dal a fejlesztési ciklus lerövidül: csak a plugint kell újraépíteni, a platformot nem kell újraindítani.
# Maven build (csak a plugin modul)
mvn package -pl my-plugin -am
# Hot-replace egy lépésben (bash alias-ként érdemes felvenni)
rm -rf /srv/plugins/my-plugin && \
sleep 1 && \
cp -r target/plugins/my-plugin /srv/plugins/my-plugin
sleep 1 biztosítja, hogy az unload log megjelenjen mielőtt
a betöltés elindul – diagnosztikailag jobb olvashatóság, nem funkcionális
követelmény.
11.4 Tool-ok közötti függőségek – services fájl sorrend
Ha egy plugin tool egy másik, ugyanabban a pluginban lévő tool-t injektál
@Autowired-del, a függőséget a META-INF/services fájlban
előbb kell felsorolni, mint az injektáló tool-t.
A PluginLoader a services fájl sorrendjében dolgozza fel a tool-okat:
minden tool autowireBean() + registerSingleton() hívást
kap – ebben a sorrendben, egymás után. Amikor az injektáló tool autowireBean()-je
fut, a függőségének már Spring beanként regisztrálva kell lennie.
# META-INF/services/hu.medical.rag.common.tool.intf.IChatbotTool
# ✅ Helyes: MyWebNERIngestionTool (függőség) előbb van mint MyContextSearchTool
hu.medical.rag.plugin.example.tool.MyWebNERIngestionTool
hu.medical.rag.plugin.example.tool.MyContextSearchTool # @Autowired MyWebNERIngestionTool
# ❌ Hibás: fordított sorrend → UnsatisfiedDependencyException betöltéskor
hu.medical.rag.plugin.example.tool.MyContextSearchTool # @Autowired MyWebNERIngestionTool
hu.medical.rag.plugin.example.tool.MyWebNERIngestionTool
@Lazy annotáció itt nem használható.
A Spring CGLIB proxy generátor a platform classloaderéből próbál subclasst generálni
a plugin osztályából – ez cross-classloader esetén
ClassNotFoundException: ...$$SpringCGLIB$$0 hibával meghiúsul.
A megoldás mindig a helyes services fájl sorrend.
Az IPluginConfigurer.configure() által regisztrált Spring bean-ek
(pl. saját asszisztens osztályok) a tool-ok betöltése előtt kerülnek
regisztrálásra – ezekre az ordering szabály nem vonatkozik, bármely tool
injektálhatja őket.
11.5 Fejlesztői tesztelés lokálisan
A Maven build kimenetét közvetlenül lehet lokális tesztelésre használni –
nincs szükség kézzel másolni. A platform PLUGINS_DIR konfigurációját
a build output-ra kell irányítani:
# alkalmazás konfigurációban vagy környezeti változóként
PLUGINS_DIR=target/plugins
Mivel a target/plugins/{artifactId}/ struktúra azonos az éles deploy
struktúrával, a fejlesztői szerver pontosan ugyanúgy tölti be a plugin-t.
Fejlesztés közben elegendő mvn package után hot-replace-szel
frissíteni – a platformot nem kell újraindítani.
11.6 Deploy ellenőrzőlista
| Lépés | Ellenőrzés |
|---|---|
| IChatbotTool ServiceLoader fájl megvan | META-INF/services/hu.medical.rag.common.tool.intf.IChatbotTool
létezik a JAR-ban és helyes osztályneveket tartalmaz (lásd 2. fejezet) |
| IPluginConfigurer fájl megvan (ha szükséges) | Ha a plugin JPA-t vagy extra Spring bean-t regisztrál:
META-INF/services/hu.medical.rag.common.plugin.IPluginConfigurer
létezik és tartalmazza a konfigurátor osztály nevét.
Lásd Adatbázis-hozzáférés fejezet. |
Saját függőségek a lib/-ben |
Ha volt compile scope-os függőség, megjelent a
target/plugins/{artifactId}/lib/-ben |
Platform JAR-ok nincsenek a lib/-ben |
A platform-bom által kezelt függőségek nem kerültek be – ha mégis benne vannak, scope-problémára utal |
| Orchestrátor plugin: no-arg konstruktor megvan | Plugin JAR-ból betöltött orchestrátor tool-hoz kötelező |
Tool-ok közötti @Autowired függőség: sorrend helyes |
Ha Tool A injektál Tool B-t, Tool B a services fájlban előbb szerepel.
@Lazy nem használható plugin tool-ok között – cross-classloader
esetén CGLIB proxy generálás meghiúsul (lásd 11.4 fejezet) |
| Teljes alkönyvtár lett másolva | Frissítésnél ne csak a plugin.jar-t cseréld –
a lib/ is változhatott |
| Hot-replace: alkönyvtár törölve, majd visszamásolva | In-place JAR csere nem triggereli a hot reloadot – a teljes alkönyvtárat kell törölni és visszamásolni |
| Log ellenőrzés | A platform naplózza a betöltött tool-ok számát és nevét. Hiba esetén a cause-lánc is megjelenik a logban. |