10. diel - Java server - Systém pluginov
V minulej lekcii, Java server - Event bus , sme sa venovali propagáciu udalostí naprieč serverom pomocou event bus. Dnes navrhneme a implementujeme systém, pomocou ktorého budeme môcť ľahko rozširovať funkcionalitu servera.
Plugin
Na úvod by bolo dobré zadefinovať, čo to plugin vlastne je. Plugin bude predstavovať jednotlivé funkcionality servera. Pod termínom funkcionalita servera si môžete predstaviť:
- správa užívateľov
- prístup k databáze
- chat
- komunikácia s externou službou
- a ďalšie ...
Návrh systému pluginov
Náš pluginový systém bude relatívne jednoduchý. Bude mať za úlohu pri štarte servera načítať všetky dostupné pluginy a inicializovať je. V budúcnosti doplníme tento systém o načítanie pluginov z externých jar súborov, aby sme nemuseli server překompilovat zakaždým, keď bude niekto chcieť pridať novú funkciu.
Implementácia
Implementáciu začneme návrhom rozhranie, ktoré bude reprezentovať
plugin. Rozhranie nazveme IPlugin
a vložíme ho
do samostatného balíčka plugins
. Rozhranie bude
pre začiatok obsahovať metódu getName()
, ktorá ako už názov
napovedá vráti názov pluginu. Ďalej metódu init()
, v ktorej sa
bude plugin inicializovať. Pomocou metódy
registerMessageHandlers()
bude môcť plugin zaregistrovať všetky
udalosti, na ktoré bude v budúcnosti reagovať. V metóde
setupDependencies()
bude možné prepojiť jednotlivé doplnky
dohromady. Celé rozhranie vyzerá nasledovne:
package cz.stechy.chat.plugins; public interface IPlugin { String getName(); void init(); void registerMessageHandlers(IEventBus eventBus); void setupDependencies(Map<String, IPlugin> otherPlugins); }
V rovnakom balíčku vytvoríme zoznam, ktorý bude obsahovať všetky
pluginy, ktoré budú zakomponované priamo v servera. Výpočet nazvime
jednoducho Plugin
. Výpočet bude obsahovať
konštantu typu Class<? extends IPlugin>
. Nezabudnite na
konštruktor:
public enum Plugin { ; public final Class<? extends IPlugin> clazz; Plugin(Class<? extends IPlugin> clazz) { this.clazz = clazz; } }
Táto konštanta bude odkazovať na triedu, ktorá implementuje plugin. Pre zatiaľ je výpočet prázdny, preto musí byť na začiatku prítomný bodkočiarku.
Teraz zaregistrujeme všetky moduly do Google Guice, aby nám ich automaticky
instancioval. Vytvoríme nový modul PluginModule
,
ktorý bude dediť z triedy AbstractModule
, ktorú
nám poskytuje knižnica Guice a implementujeme metódu configure:
public class PluginModule extends AbstractModule { @Override protected void configure() { MapBinder < String, IPlugin > pluginBinder = MapBinder.newMapBinder(binder(), String.class, IPlugin.class); for (Plugin plugin: Plugin.values()) { pluginBinder.addBinding(plugin.name()).to(plugin.clazz).asEagerSingleton(); } // TODO načíst externí pluginy } }
V tejto metóde si pripravíme premennú pluginBinder
, pomocou
ktorej škárujeme implementácie všetkých pluginov s naším rozhraním
IPlugin
. Viac o implementácii
MapBinderu
nájdete na wiki Guice.
Spárovanie vykonáme v slučke, v ktorej budeme iterovat cez zoznam pluginov,
ktoré sú priamo na serveri. Za zmienku už stojí len volanie metódy
asEagerSingleton()
, ktorá hovorí, že kedykoľvek zažiadame o
konkrétnu inštanciu pluginu, zakaždým dostaneme tú istú. Načítanie
externých pluginov si necháme na neskôr.
Ďalej zaregistrujeme novovytvorený modul do Guice. V triede
Server
, v metóde main()
, kde sa
vytvára inštancie triedy Injector
, pridáme
nový modul PluginModul
:
final Injector injector = Guice.createInjector(new ServerModule(), new PluginModule());
Nakoniec upravíme triedu Server
. Pridáme konštantu typu
Map<String, IPlugin>
a konštantu typu
IEventBus
. Tieto konštanty bude trieda prijímať v
konstruktoru:
@Inject public Server(IParameterFactory parameterFactory, IServerThreadFactory serverThreadFactory, IEventBus eventBus, Map<String, IPlugin> plugins) { this.parameterFactory = parameterFactory; this.serverThreadFactory = serverThreadFactory; this.eventBus = eventBus; this.plugins = plugins; }
Teraz už zostáva postarať sa o inicializáciu pluginov. Na tento účel si
vytvoríme privátne metódu initPlugins()
, v ktorej sa postaráme
o inicializácii:
private void initPlugins() { for (IPlugin plugin: plugins.values()) { plugin.init(); } for (IPlugin plugin: plugins.values()) { plugin.registerMessageHandlers(messageRegistrator); } for (IPlugin plugin: plugins.values()) { plugin.setupDependencies(plugins); } }
Najskôr zavoláme nad všetkými pluginy metódu init()
a dáme
im možnosť sa inicializovať. Ďalej prejdeme do fázy registrácie
poslucháčov na príslušné udalosti a nakoniec necháme pluginy, aby si
nastavili svojej závislosti medzi sebou. Metódu initPlugins()
budeme volať pred štartom vlákna servera.
Použitie
Pre demonštračné účely vytvoríme jednoduchý plugin, ktorý nebude
robiť nič, iba vo fáze inicializácia vypíše svoj názov do konzoly. V
balíčku plugins vytvoríme ďalší balíček s názvom hello
. V
tomto balíčku vytvoríme triedu HelloPlugin
, ktorá bude
implementovať naše rozhranie IPlugin
. Požadované metódy
implementujeme nasledovne:
package cz.stechy.chat.plugins.hello; public class HelloPlugin implements IPlugin { @Override public String getName() { return "HelloPlugin"; } @Override public void init() { System.out.println("Inicializace pluginu: " + getName()); } }
Záznam o pluginu pridáme do zoznamu pluginov. Nezabudnite, že nestačí pridať iba názov, ale i triedu, ktorá implementuje daný plugin:
public enum Plugin { HELLO(HelloPlugin.class); ... }
To je všetko. Keď spustíte server, dostanete hlášku, že sa plugin Inicializoval.
To by bolo z dnešnej lekcie všetko. V budúcej lekcii, Java server - Propagácia lokálnou sieťou (1. časť) , pripravíme základ pre zviditeľnenie servera v lokálnej sieti.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 22x (288.88 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Java