Implementacja systemu wtyczek w Javie

0

Core aplikacji wczytuje ze wskazanego miejsca wtyczki, ktore skompilowane sa do postaci biblioteki .jar. Do obslugi tych wtyczek, posiada interfejs IPlugin i kazda wtyczka implementuje ten interfejs.
Wtyczka A implementuje interfejsy: IPlugin, IA
Wtyczka B implementuje interfejsy: IPlugin, IB, posiada interfejs IA
Core wszystkim wtyczkom poprzez funkcje init() przekazuje mape wtyczek (LinkedHashMap<String, IPlugin>). Core nie zna interfejsow IA, iB.
Wtyczka B chce skorzystac z funkcji wtyczki A. Majac mape wtyczek, pobieramy sobie odpowiednia wtyczke poprzez: mapa.get("A"). Otrzymujemy wiec obiekt typu IPlugin. z niego wydobywamy instancje obiektu-wtyczki: mapa.get("A").getInstance(). Interfejs IPlugin posiada funkcje getInstance. Nastepnie to, co otrzymalem, mam zapisane w obiekcie o nazwie a. Nastepnie, jako ze wtyczka B ma interfejs IA, rzutuje otrzymana instancje obiektu: IA.class.cast(a). Chcialbym nastepnie wywolac jakas funkcje z klasy a, powiedzmy test(), wiec moge napisac IA.class.cast(a).test();

Problem: ClassCastException podczas wywolania IA.class.cast(a). Problem znika, jesli Core posiada interfejs IA. Niestety w Core nie moge umieszczac interfejsow wtyczek, jedynie IPlugin i jedynie z nim Core moze byc skompilowane do pliku .jar. Wtyczka A w swoim pliku .jar posiada interfejs IA, wtyczka B posiada w swoim pliku .jar interfejs IB i IA, aby mogla korzystac z funkcji wtyczki A.
Ogolniej: calosc polega na tym, by zaimplementowac taki system obslugi wtyczek, by wtyczki mogly komunikowac sie miedzy sobie przy pomocy odpowiednich interfejsow, jednak Core nie moze posiadac zadnego z tych interfejsow, poniewaz nie wie, jakie wtyczki powstana.

Nie wiem, czy jest to dobry sposob implementacji systemu wtyczek, ktore maja sie miedzy soba komunikowac, czy tak to sie fachowo rozwiazuje. W Sieci znalazlem jakis przyklad, w ktorym wczytywane sa wszystkie klasy znajdujace sie w pliku .jar wtyczki, jednak to nie rozwiazuje problemu. Core wydaje sie potrzebowac interfejsu IA w tej sytuacji, jednak nie jest on dostarczany wraz z Core, natomiast jest dostarczany wraz z wtyczka A.jar oraz B.jar. Mimo wszystko jednak Core tego interfejsu nie widzi. Byc moze cos pominalem. A byc moze tego typu obsluge wtyczek nalezy zaimplementowac w zupelnie inny sposob.

Mam nadzieje, ze wszystko opisalem dostatecznie jasno i, ze znajdzie sie ktos, kto bedzie mogl udzielic mi odpowiedzi.

0

Hm... sprawdź jeszcze jedną rzecz. Zasięgi interfejsów.

Co do samego rozwiązania to warto IPlugin wyposażyć w standardową metodę do komunikacji i w ten sposób załatwić ten problem. Cała metoda fajna.

0

Hej. Nie wiem czy dobrze zrozumialem, nie zastanawialem sie tez nad tym czy twoj sposob ma sens czy nie, zakladam ze ma. Jednakze, piszesz IA.class.cast(a) i ze to zwraca ClassCastException bo Core nie zna interfejsu IA - moim skromnym zdaniem to sie nawet nie skompiluje, poniewaz IA nie jest znane podczas kompilacji, wiec nie mozesz uzyc literalu klasy IA.class. Zatem, cos smierdzi z tym wyjatkiem ktory opisujesz. Mozesz to zrobic tak (zakladam ze jary sa w classpath, a wiec Class.forName() znajdzie pliki .class):
IPlugin a = ....; // myki zeby dostac plugin A
Class<?> c = Class.forName("twoj.pakiet.IA");
c.cast(a); <= przyznam sie ze nie mam pojecia co mozna z takim czyms zrobic - w Core nie przypiszesz tego do IA bo sam poweidziales ze Core IA nie zna, a jesli ten kod wywolujesz w kodzie jara z pluginem B, to zadziala zwykle rzutowanie na IA poniewaz ten interfejs jest znany.
W takim wypadku wydaje mi sie ze mozna tylko refleksja wywolywac metody rozne, no ale Core i tak nie wie jakie te metody moga byc.

Albo, mozesz w Core:
IPlugin a = ....; // myki zeby dostac plugin A
po czym przekazac a do kodu w pluginie B, i tam rzutowac na IA, przypisac gdzies i wywolywac jego metody.

Nie wiem czy pomoglem, pare piwek strzelilem juz. Pozdro.

0
Koziołek napisał(a)

Hm... sprawdź jeszcze jedną rzecz. Zasięgi interfejsów.

Co do samego rozwiązania to warto IPlugin wyposażyć w standardową metodę do komunikacji i w ten sposób załatwić ten problem. Cała metoda fajna.

O to właśnie chodzi, że nie wiem na tym etapie, jakie metody będą we wtyczkach, więc metoda do komunikacji w IPlugin raczej odpada.
A co masz na myśli mówiąc "zasięgi interfejsów"? Możesz rozwinąć myśl?

Pikseloza napisał(a)

Hej. Nie wiem czy dobrze zrozumialem, nie zastanawialem sie tez nad tym czy twoj sposob ma sens czy nie, zakladam ze ma. Jednakze, piszesz IA.class.cast(a) i ze to zwraca ClassCastException bo Core nie zna interfejsu IA - moim skromnym zdaniem to sie nawet nie skompiluje, poniewaz IA nie jest znane podczas kompilacji, wiec nie mozesz uzyc literalu klasy IA.class. Zatem, cos smierdzi z tym wyjatkiem ktory opisujesz.

Core kompiluję osobno do pliku Core.jar i w Core nie ma tego wywołania.
Wywołanie to jest we wtyczce B, którą kompiluję do osobnego pliku B.jar. Ona posiada interfejs IA i znajduję się on (po skompilowaniu) w pliku B.jar. Wtyczka A (skompilowana do pliku A.jar) implementuje natomiast ten sam interfejs i także w pliku A.jar można znaleźć ten interfejs.
Core wczytuje dynamicznie pliki .jar ze wtyczkami. Być może sam proces dynamicznego wczytywania zakodowałem w zły sposób. Ktoś może przedstawić, jak to należy poprawnie rozwiązać?
Z Twojej odpowiedzi (za którą oczywiście dziękuję) wnioskuję, że zrozumiałeś, iż wywołanie (i rzutowanie do IA) odbywa się w Core. Tak nie jest - wszystko to odbywa się we wtyczce B, która ładowana jest dynamicznie. Core nie wywołuje żadnych funkcji wtyczek, z wyjątkiem metody init(), która jest dostarczana poprzez interfejs IPlugin.

Pikseloza napisał(a)

Albo, mozesz w Core:
IPlugin a = ....; // myki zeby dostac plugin A
po czym przekazac a do kodu w pluginie B, i tam rzutowac na IA, przypisac gdzies i wywolywac jego metody.

Tak właśnie robię. Core posiada instancję obiektu klasy A (ściślej - wszystkich wtyczek, które udało się wczytać dynamicznie z plików .jar), która znajduje się dokładniej rzecz ujmując w hashmapie, hashmapa jest przekazywana do pozostałych wtyczek (w tym także do wtyczki B) i wtyczki te rzutują sobie tę instancję do odpowiedniego interfejsu. Chcę właśnie we wtyczce B zrzutować instancję klasy A do IA, ale właśnie w tym momencie JVM rzuca mi w twarz wyjątkiem.

0

Zasięg interfejsów czyli czy są publiczne, defaultowe itp. Jak wygląda struktura pakietów. Może się okazać, że IA nie jest widoczny bo ma zasięg pakietu, a nie publiczny.

Co do nie wiedzy jakie metody będą potrzebne to właśnie na tym etapie wychodzą wszelkiej maści kwiatki, które powodują, że dostajemy dodatkowe wspólne metody dla większości wtyczek. Można oczywiście przenieść tą metodę do oddzielnego interfejsu ICommunicable, po którym będzie dziedziczył IPlugin. Następnie tworzysz abstrakcyjną klasę, w której implementujesz metody send() i recive() w jakiś standardowy sposób. Potem pluginy rozszerzają klasę AbstractPlugin i implementują tylko metody init(), getName() itp.

0

Kody źródłowe znajdują się pod adresem: http://rogersoft.webpark.pl/wtyczki.zip
Po wejściu do katalogu Core/dist, z konsoli uruchamiamy: java -jar Core.jar
Oczywiście pokazuje się błąd (wyjątek). Proszę o przeanalizowanie kodu i ewentualne poprawki.

1 użytkowników online, w tym zalogowanych: 0, gości: 1