Tworzenie aplikacji w oparciu o pluginy JUST JAVA 28.03.2007 Michał Bereta Intytu Modelowania Komputerowego Wydział Fizyki, Matematyki i Informatyki Stosowanej beretam@torus.uck.pk.edu.pl http://torus.uck.pk.edu.pl/~beretam
Cel Warsztatu Próba stworzenia aplikacji w języku Java z możliwością jej rozbudowy: bez dostępu do źródeł i ponownej kompilacji poprzez dodawanie pluginów (wtyczek, dodatków) Aplikacja składa się zatem z programu głównego oraz dowolnej liczby pluginów.
Co trzeba zaplanować? Sposób komunikacji programu głównego z pluginami. Wymagania odnośnie prawidłowo stworzonego pluginu.
Jak to zrobić korzystając z możliwości Javy? Z czego nie będziemy korzystać: Java Plug-in Technology Java Plug-in technology, included as part of the Java Runtime Environment, Standard Edition (Java SE), establishes a connection between popular browsers and the Java platform. This connection enables applets on Web sites to be run within a browser on the desktop. Java Plug-in technology is part of the current version of the Java Runtime Environment, Standard Edition (Java SE). Download the JRE. Deploying JavaBeans Components The JavaBeans Bridge for ActiveX technology is part of Java Plug-in technology and bundled with the JRE. It provides developers of legacy OLE/COM/ActiveX containers such as Word or Visual Basic with the ability to embed and use portable JavaBeans components in the same way they would previously embed and use platform-specific OLE/COM/ActiveX components.
Jak to zrobić korzystając z możliwości Javy? Z czego nie będziemy korzystać: The Extension Mechanism The extension mechanism was introduced as a new feature in the JavaTM 1.2 platform. The extension mechanism provides a standard, scalable way to make custom APIs available to all applications running on the Java platform. As of the Java 1.3 platform release, Java extensions are also referred to as optional packages. This trail may use both terms interchangeably Extensions are groups of packages and classes that augment the Java platform through the extension mechanism. The extension mechanism enables the runtime environment to find and load extension classes without the extension classes having to be named on the class path. In that respect, extension classes are similar to the Java platform's core classes. That's also where extensions get their name -- they, in effect, extend the platform's core API.
Jak to zrobić korzystając z możliwości Javy? The Extension Mechanism
Jak to zrobić korzystając z możliwości Javy? Z czego będziemy korzystać: Java Archive (JAR) Files The Reflection API
Aplikacja Główna
Aplikacja Główna
Aplikacja Główna
Aplikacja Główna
Aplikacja Główna http://torus.uck.pk.edu.pl/~beretam/just_java/plugins/mojaaplikacja_1.zip W tej wersji program robi niewiele. Brak jakiejkolwiek obsługi wtyczek.
Tworzenie pluginów Założenia: Każdy plugin jest dostarczany jako plik JAR i umieszczany w katalogu plugins. Aplikacja główna skanuje katalog plugins i wyszukuje te pliki JAR, które są rzeczywistymi pluginami. Nie wszystkie pliki JAR w katalogu plugins muszą być pluginami niektóre mogą być bibliotekami pomocniczymi naszych pluginów.
Tworzenie pluginów Problem: Skąd aplikacja główna ma wiedzieć, które pliki JAR są pluginami i jakie obiekty jakich klas stworzyć dla danego pluginu i jak je przechowywać, skoro nazwa klasy nie jest wcześniej znana. Rozwiązanie: Dodać do pliku JAR dodatkowe informacje. Jeśli plik JAR je zawiera, jest potencjalnym pluginem (potencjalnym, gdyż musi być poprawnie zaimplementowany).
Tworzenie pluginów Opis zawierający nazwę klasy startowej (głównej) danego pluginu Powyższe założenie jest tylko naszą konwencją, wystarczającą w tym przykładzie. Opis pluginu mógłby być równie dobrze w manifeście, jak również mółby zawierać dodatkowe informacje, jak np. opis funkcjonalności pluginu, wymagania co do wersji itd.
Tworzenie pluginów Problem: Nie chcemy udostępniać kodu źródłowego naszej aplikacji głównej, nie chcemy również czynić wszystkich pól publicznymi. Rozwiązanie: Nasza aplikacja główna implementuje interfejs, za pomocą którego udostępnia przewidziane do dostępu przez pluginy pola.
Tworzenie pluginów Problem: Skąd aplikacja główna ma wiedzieć, jak tworzyć, przechowywać, aktywować i dezaktywować pluginy? Rozwiązanie: Każdy plugin implementuje interfejs i jego metody, za pomocą których aplikacja główna będzie go obsługiwać. Pluginy są przechowywane w tablicy. Tworzenie odbywa się z wykorzystaniem mechanizmu odzwierciedlania (Reflection API).
Interfejs pośredni dla pluginu PluginInterface umożliwia aplikacji głównej przechowywanie i komunikacje z pluginami. W naszym przypadku komunikacja ogranicza się do aktywacji pluginu i jego zatrzymania. implementuje Plugin_1 Aplikacja główna pośredniczy PluginInterface implementuje implementuje Plugin_2 Plugin_3 posiada tablice PluginInterfejs plugins[];
Interfejs pośredni dla aplikacji głównej AppInterface umożliwia pluginom dostęp do wybranych (wyszczególnionych przez ten interfejs) pól aplikacji głównej. korzysta Plugin_1 Aplikacja główna implementuje AppInterface Posiada pole AppInterface mainapp;
Tworzenie Interfejsów
Tworzenie Interfejsów
Tworzenie Interfejsów
Tworzenie Interfejsów
Tworzenie Interfejsów package inters; public interface PluginInterface { public void startplugin(); } public void stopplugin(); Aplikacja główna bedzie jedynie uruchamiać lub zatrzymywać wtyczkę.
Tworzenie Interfejsów package inters; public interface AppInterface { public javax.swing.jlabel getdatelabel(); public javax.swing.jtextarea gettextarea(); } Aplikacja główna udostępnia wtyczkom etykietę, na której wyświetla datę oraz pole tekstowe notatnika. http://torus.uck.pk.edu.pl/~beretam/just_java/plugins/interfaces.zip
Pierwszy plugin: okno informacyjne Ta wtyczka nie robi nic szczególnego, tylko pokazuje okno informacyjne na ekranie. Będzie to zrobione bez ingerencji w aplikację główną nie licząc ogólnego kodu do obsługi wtyczek.
Pierwszy plugin: okno informacyjne
Pierwszy plugin: okno informacyjne
Pierwszy plugin: okno informacyjne Nasze okno informacyjne dziedziczy z klasy Jframe. W javie klasy mogą dziedziczyć bezpośrednio tylko po jednej klasie. Dlatego komunikacja poprzez interfejsy jest korzystna. W javie klasy mogą implementować dowolną liczbę interfejsów.
Pierwszy plugin: okno informacyjne Wygenerowany kod.
Pierwszy plugin: okno informacyjne Zaplanuj okno lub pobierz gotowy projekt. http://torus.uck.pk.edu.pl/~beretam/just_java/plugins/infwindow.zip
Pierwszy plugin: okno informacyjne Dodajemy informację, że ten projekt bedzie pluginem zgodnie z naszą konwencją.
Pierwszy plugin: okno informacyjne Dodajemy informację, że ten projekt bedzie pluginem zgodnie z naszą konwencją.
Pierwszy plugin: okno informacyjne Podpinamy bibliotekę z potrzebnymi interjesami.
Pierwszy plugin: okno informacyjne Implementujemy PluginInterface poprzez dodanie koniecznych metod. W tym przypadku uruchomienie pluginu polega na jego pokazaniu, a zatrzymanie na jego ukryciu.
Rozbudowa aplikacji głównej Potrzebne interfejsy podpinamy również do aplikacji głównej.
Rozbudowa aplikacji głównej Aplikacja główna implementuje interfejs AppInterface. Jego metody:
Rozbudowa aplikacji głównej class MyListModel extends javax.swing.abstractlistmodel{ String strings[] = null; public void setstrings(string[] _strings){ strings = _strings; } public int getsize() { return strings.length; } public Object getelementat(int i) { return strings[i]; } } Na końcu pliku MojaAplikacja.java dodajemy pomocniczą klasę.
Rozbudowa aplikacji głównej private final static String pluginsdir = "plugins/"; private final static String plugindesc = "plugin_desc/plugin_desc.txt"; private static ArrayList pluginsnames = new ArrayList();//nazwy klas glownych pluginow private static ArrayList pluginsjarpaths = new ArrayList(); private static ArrayList pluginswindows = new ArrayList();//konretne obiekty pluginow private static URLClassLoader urlclassloader = null; Dodajemy pomocnicze pola...
Rozbudowa aplikacji głównej
Rozbudowa aplikacji głównej W katalogu dist\plugins umieść InfWindow.jar oraz FalsePlugin.jar Pozostałe wtyczki dodamy później. http://torus.uck.pk.edu.pl/~beretam/just_java/plugins/falseplugin.zip http://torus.uck.pk.edu.pl/~beretam/just_java/plugins/infwindow.zip
Rozbudowa aplikacji głównej Dodajemy funkcję ladujpluginy(); --> patrz kod źródłowy Ważniejsze momenty: JarFile jarfile = null; jarfile = new JarFile(files[i].getPath() ); Enumeration entries = jarfile.entries(); JarEntry entry = null; JarEntry to jakikolwiek plik lub katalog w danym pliku JAR URL urls[] = new URL[pluginsNames.size()]; urls[i] = new File((String)pluginsJarPaths.get(i) ).tourl(); Tablica ta będzie potrzebna później, podczas tworzenia obiektów pluginów. Przechowywanie adresów miejsc, gdzie znajdują się pliki binarne klas (z kodem bajtowym) jest konieczne, gdyż podczas uruchamiania aplikacji głównej, maszyna wirtualna javy nie ma pojęcia o tych lokacjach. Tablice z adresami tych lokacji wykorzystamy do tworzenia obiektu klasy URLClassLoader.
Rozbudowa aplikacji głównej Pobierz projekt po modyfikacji: http://torus.uck.pk.edu.pl/~beretam/just_java/plugins/mojaaplikacja_2_dostep_do_jar.zip
Rozbudowa aplikacji głównej Jeśli uruchamiasz projekt spod środowiska NetBeans, zmień miejsce wykonywania aplikacji.
Rozbudowa aplikacji głównej Jeszcze nie działa... Po uruchomieniu
Rozbudowa aplikacji głównej Po uruchomieniu
Rozbudowa aplikacji głównej. Uruchamianie i zatrzymywanie wtyczek. The Reflection API The reflection API represents, or reflects, the classes, interfaces, and objects in the current Java Virtual Machine. You'll want to use the reflection API if you are writing development tools such as debuggers, class browsers, and GUI builders. With the reflection API you can: Determine the class of an object. Get information about a class's modifiers, fields, methods, constructors, and superclasses. Find out what constants and method declarations belong to an interface. Create an instance of a class whose name is not known until runtime. Get and set the value of an object's field, even if the field name is unknown to your program until runtime. Invoke a method on an object, even if the method is not known until runtime. Create a new array, whose size and component type are not known until runtime, and then modify the array's components. http://java.sun.com/docs/books/tutorial/reflect/index.html
Rozbudowa aplikacji głównej. Uruchamianie i zatrzymywanie wtyczek. Class classobj = null; Przechowuje informacje o klasie. np. Rectangle rect = new Rectangle(); Trzy sposoby stworzenie obiektu Class: classobj = rect.class; classobj = Rectangle.class; classobj = Class.forName( java.awt.rectangle ); Skorzystamy z ostatniego sposobu nazwa klasy bedzie odczytana z pliku plugin_desc.txt
Rozbudowa aplikacji głównej. Uruchamianie i zatrzymywanie wtyczek. W naszym przypadku: Class classobj = null; inters.plugininterface pluginwindow = null; Obiekt urlclassloader jest konieczny, jak wcześniej wspomniano: classobj = Class.forName((String) pluginsnames.get(index), true, urlclassloader); Metoda newinstance() jest możliwa w przypadku istnienia konstruktora bezargumentowego: pluginwindow = (inters.plugininterface)classobj.newinstance();
Rozbudowa aplikacji głównej. Uruchamianie i zatrzymywanie wtyczek. Do głownej klasy naszej aplikacji dodajemy metodę: private void uruchomplugin(); --> patrz kod źródłowy http://torus.uck.pk.edu.pl/~beretam/just_java/plugins/mojaaplikacja_3_konstr_bezarg.zip
Rozbudowa aplikacji głównej. Uruchamianie i zatrzymywanie wtyczek. Teraz działa
Rozbudowa aplikacji głównej. Uruchamianie i zatrzymywanie wtyczek. Wtyczka SettingsPlug. Umożliwia zmianę koloru czcionki edytora http://torus.uck.pk.edu.pl/~beretam/just_java/plugins/settingsplug.zip
Rozbudowa aplikacji głównej. Uruchamianie i zatrzymywanie wtyczek. Wymagana implementacja PluginInterface Jedynie konstruktor jednoargumentowy, zatem nie można skorzystać z funkcji newinstance()
Rozbudowa aplikacji głównej. Uruchamianie i zatrzymywanie wtyczek. Zmiana koloru w edytorze głównej aplikacji wszystko robi wtyczka, żadne zmiany nie są wprowadzane w kodzie aplikacji głównej.
Rozbudowa aplikacji głównej. Uruchamianie i zatrzymywanie wtyczek. Zmiana w kodzie metody uruchomplugin(); Constructor constructor = null; Class paramstypes[] = new Class[]{ inters.appinterface.class }; Object arguments[] = new Object[] {this}; constructor = classobj.getconstructor(paramstypes); if(constructor!= null) { pluginwindow = (inters.plugininterface)createobject(constructor, arguments); else pluginwindow = (inters.plugininterface)classobj.newinstance(); Jeśli dana klasa posiada konstruktor jednoargumentowy, korzystamy z niego, w przeciwnym przypadku próbujemy stworzyć obiekt za pomocą konstruktora bezargumentowego.
Rozbudowa aplikacji głównej. Uruchamianie i zatrzymywanie wtyczek. private Object createobject(constructor constructor, Object[] arguments) { } Object object = null; try { object = constructor.newinstance(arguments); return object; } catch (InstantiationException e) { System.out.println(e); } catch (IllegalAccessException e) { System.out.println(e); } catch (IllegalArgumentException e) { System.out.println(e); } catch (InvocationTargetException e) { System.out.println(e); } return object; http://torus.uck.pk.edu.pl/~beretam/just_java/plugins/mojaaplikacja_final.zip
Uwagi odnośnie przyjętego rozwiązania Korzystamy jedynie w niewielkim stopniu z możliwości, jakie daje Reflection API. By nie zaciemniać kodu, nie rozpatrujemy przypadku, gdy plik JAR zadeklarowany jako plugin, nie dostarcza poprawnych konstruktorów. Porządna aplikacja powinna być przygotowana na takie i inne możliwe błędy w klasach potencjalnych pluginów. W przypadku bardziej rozwiniętych interfejsów do komunikacji miedzy aplikacją a wtyczkami lub też między samymi wtyczkami, Reflection API może być użyte by sprawdzić, jakiego rodzaju interfejsy implementuje dany plugin, tzn. jakiego jest typu, jakiego przeznaczenia, jakie ma metody, z jakimi argumentami, itd.
Wtyczka do uaktualniania daty http://torus.uck.pk.edu.pl/~beretam/just_java/plugins/dateupdate.zip Dodatkowy interfejs nowy plugin będzie jednocześnie nowym wątkiem.
Wtyczka do uaktualniania daty Funkcje z interfejsu PluginInterface
Wtyczka do uaktualniania daty Metoda z interfejsu Runnable
Wtyczka do uaktualniania daty Po uruchomieniu, wtyczka DateUpdate na bieżąco uaktualnia datę i czas.
Wtyczka do automatycznej obsługi słów kluczowych. EditorHelper - automatycznie zamienia słowa kluczowe na pisane dużą czcionką.
Wtyczka do automatycznej obsługi słów kluczowych. http://torus.uck.pk.edu.pl/~beretam/just_java/plugins/editorhelper.zip
Wtyczka do automatycznej obsługi słów kluczowych.
Dziekuję za uwagę Wszystkie materiały do pobrania ze strony http://torus.uck.pk.edu.pl/~beretam Wypełnij anonimową ankietę! Napisz co było dobrze, a co źle! http://torus.uck.pk.edu.pl/~beretam/dydaktyka/ankieta.html Z góry dziękuję!