ERLEDIGT
NEIN
NEIN
ANTWORTEN
4
4
ZUGRIFFE
578
578
EMPFEHLEN
-
29.06.11 00:23 #1
- Registriert seit
- Jun 2002
- Ort
- Saarbrücken (Saarland)
- Beiträge
- 9.886
- Blog-Einträge
- 29
Hallo,
hier mal wieder ein kleines einfaches Beispiel für einen flexiblen Erweiterungsmechanismus auf Basis des ServiceLoader APIs (http://download.oracle.com/javase/6/...iceLoader.html).
Dazu definieren wir uns ein Marker-Interface womit wir "Erweiterungsklassen" in unserer Anwendung markieren könnnen.
Unser Marker-Interface Extension:
Code java:1 2 3 4
package de.tutorials.extensibility; public interface Extension { }
Unser Interface ExtensionRegistry:
Code java:1 2 3 4 5 6 7 8
package de.tutorials.extensibility; import java.util.List; public interface ExtensionRegistry { <TExtension extends Extension> List<TExtension> getExtensions(Class<TExtension> extensionClass); }
Hier unsere Standard ExtensionRegistry Implementierung ServiceLoaderExtensionRegistry :
Code java:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
package de.tutorials.extensibility; import java.io.File; import java.io.FilenameFilter; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ServiceLoader; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; public class ServiceLoaderExtensionRegistry implements ExtensionRegistry{ private final static Logger logger = Logger.getLogger(ServiceLoaderExtensionRegistry.class.getName()); private ClassLoader extensionClassLoader; private AtomicBoolean initialized = new AtomicBoolean(); protected String extensionLookupPath = System.getProperty("de.tutorials.extensibility.extension.lookupPath", "./ext/"); protected String extensionFileExtension = ".ext.jar"; protected FilenameFilter extensionJarFilter = createExtensionJarFilter(); public void init() { this.extensionClassLoader = createExtensionClassLoader(lookupExtensionUrls()); this.initialized.set(true); } @Override public <TExtension extends Extension> List<TExtension> getExtensions(Class<TExtension> extensionClass){ if(!this.initialized.get()){ init(); } ServiceLoader<TExtension> extensionLoader = ServiceLoader.load(extensionClass, getExtensionClassLoader()); List<TExtension> extensions = new ArrayList<TExtension>(); for(Iterator<TExtension> iter = extensionLoader.iterator();iter.hasNext();){ TExtension extension = iter.next(); logger.info(String.format("Found extension for %s: %s from URL: %s",extensionClass.getName(), extension, extension.getClass().getProtectionDomain().getCodeSource().getLocation())); extensions.add(extension); } if(extensions.isEmpty()){ logger.info(String.format("No extensions were found for %s", extensionClass.getName())); } return extensions; } private URLClassLoader createExtensionClassLoader(List<URL> extensionJars) { return new URLClassLoader(extensionJars.toArray(new URL[extensionJars.size()])); } private List<URL> lookupExtensionUrls() { File extensionsFolder = new File(getExtensionLookupPath()); logger.info("Using extension folder: " + extensionsFolder.getAbsolutePath()); List<URL> extensionUrlList = new ArrayList<URL>(); File[] extensionJars = extensionsFolder.listFiles(getExtensionJarFilter()); if(extensionJars == null || extensionJars.length == 0){ logger.info("Found 0 extension jars"); }else{ logger.info(String.format("Found %s extensions",extensionJars.length)); for(File extensionJar : extensionJars){ try { URL extensionJarUrl = extensionJar.toURI().toURL(); logger.info("Found extension jar: " + extensionJarUrl); extensionUrlList.add(extensionJarUrl); } catch (MalformedURLException e) { e.printStackTrace(); } } } return extensionUrlList; } protected FilenameFilter createExtensionJarFilter() { return new FilenameFilter() { public boolean accept(File dir, String name) { return name.endsWith(extensionFileExtension); } }; } public String getExtensionLookupPath() { return extensionLookupPath; } public void setExtensionLookupPath(String extensionLookupPath) { this.extensionLookupPath = extensionLookupPath; } public String getExtensionFileExtension() { return extensionFileExtension; } public void setExtensionFileExtension(String extensionFileExtension) { this.extensionFileExtension = extensionFileExtension; } public FilenameFilter getExtensionJarFilter() { return extensionJarFilter; } public void setExtensionJarFilter(FilenameFilter extensionJarFilter) { this.extensionJarFilter = extensionJarFilter; } public ClassLoader getExtensionClassLoader() { return extensionClassLoader; } public void setExtensionClassLoader(ClassLoader extensionClassLoader) { this.extensionClassLoader = extensionClassLoader; } }
Unsere Anwendung:
Code java:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
package de.tutorials.app; import de.tutorials.app.components.Component; import de.tutorials.extensibility.ExtensionRegistry; import de.tutorials.extensibility.ServiceLoaderExtensionRegistry; public class Application { protected ExtensionRegistry extensionRegistry; public static void main(String[] args) { new Application().boot(); } protected void boot() { init(); start(); } protected void init() { setExtensionRegistry(new ServiceLoaderExtensionRegistry()); } protected void start() { for (Component component : getExtensionRegistry().getExtensions(Component.class)) { component.start(); } } public ExtensionRegistry getExtensionRegistry() { return extensionRegistry; } public void setExtensionRegistry(ExtensionRegistry extensionRegistry) { this.extensionRegistry = extensionRegistry; } }
In unserer Anwendung kann man Komponenten hinzufügen welche "gestartet" werden können.
Deshalb definieren wir ein interface Component das von unserem Extension-Marker Interface erbt:
Code java:1 2 3 4 5 6 7
package de.tutorials.app.components; import de.tutorials.extensibility.Extension; public interface Component extends Extension{ void start(); }
Für das Beispiel definieren wir noch eine Abstrakte Klasse AbstractComponent
damit wir schnell viele kleine Component Implementierungen definieren können, die beim start() ihren Namen ausgeben:
Code java:1 2 3 4 5 6 7 8 9 10 11
package de.tutorials.app.components; import java.util.logging.Logger; public abstract class AbstractComponent implements Component { @Override public void start() { Logger.getLogger(getClass().getName()).info( "Started " + getClass().getSimpleName()); } }
Zusätzlich definieren wir eine StandardComponent, schließlich muss die Anwendung ja auch ein wenig Standard-Funktionalität mitbringen:
Code java:1 2 3 4
package de.tutorials.app.components; public class StandardComponent extends AbstractComponent { }
Damit diese Standard Implementierung vom ServiceLoader gefunden werden kann, müssen wir die Verzeichnisse META-INF/services/ im Classpath Root (im Eclipse zbsp. im src Folder) definieren. Dort erzeugen wir eine Datei mit dem Namen des Interfaces "de.tutorials.app.components.Component" (das ist die Konvention des ServiceLoaders).
In diese Datei schreiben wir nun den voll qualifizierten Klassennamen (fqcn) unserer Standardimplementierung:
Code :1
de.tutorials.app.components.StandardComponent
Lassen wir nun unsere Anwendung laufen, so sehen wir folgende Ausgabe:
Code :1 2 3 4 5 6 7 8
29.06.2011 00:08:19 de.tutorials.extensibility.ServiceLoaderExtensionRegistry lookupExtensionUrls INFO: Using extension folder: C:\development\java\workspaces\2.6.1\de.tutorials.training.app\.\ext 29.06.2011 00:08:19 de.tutorials.extensibility.ServiceLoaderExtensionRegistry lookupExtensionUrls INFO: Found 0 extension jars 29.06.2011 00:08:19 de.tutorials.extensibility.ServiceLoaderExtensionRegistry getExtensions INFO: Found extension for de.tutorials.app.components.Component: de.tutorials.app.components.StandardComponent@b61d36b from URL: file:/C:/development/java/workspaces/2.6.1/de.tutorials.training.app/bin/ 29.06.2011 00:08:19 de.tutorials.app.components.AbstractComponent start INFO: Started StandardComponent
Wie man sieht wurde unsere StandardComponent gefunden, jedoch keine zusätztlichen Erweiterungen. Das ist auch richtig so, denn wir haben ja noch keine definiert.
Wir erstellen nun eine solche Erweiterung. Dazu erzeugen wir ein neues java-Projekt: de.tutorials.training.app.extension1 und erstellen dort folgende zwei Klassen:
Code java:1 2 3 4
package de.tutorials.app.components; public class Component1A extends AbstractComponent { }
und
Code java:1 2 3 4
package de.tutorials.app.components; public class Component1B extends AbstractComponent { }
wieder erzeugen wir eine Datei: /de.tutorials.training.app.extension1/src/META-INF/services/de.tutorials.app.components.Component ; nun mit folgendem Inhalt:
Code :1 2
de.tutorials.app.components.Component1A de.tutorials.app.components.Component1B
Diesmal möchten wir über unseren Erweiterungsmechanismus gleich zwei Component Implementierungen bereitstellen.
Nun erzeugen wir für unsere Anwendung (app.ext.jar als runnable jar!) und das Erweiterungs Projekt (extension1.ext.jar) jeweils ein jar.
Das das app.jar welches unsere Hauptanwendung enthält erzeugen wir zum test in ein Verzeichnis c:\temp\app
Dazu erstellen wir nun ein Verzeichnis c:\temp\app\ext. Dieses Verzeichnis wird unsere Erweiterungs-jars aufnehmen.
Ich habe die Erweiterungs-Jars durch die Endung .ext.jar explizit als solche gekennzeichnet.
Starten wir nun unsere Anwendung über die Konsole via:
java -jar app.jar
... so sehen wir folgende Ausgabe:
Code :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
C:\temp\app>java -jar app.jar 29.06.2011 00:16:04 de.tutorials.extensibility.ServiceLoaderExtensionRegistry lookupExtensionUrls INFO: Using extension folder: C:\temp\app\.\ext 29.06.2011 00:16:04 de.tutorials.extensibility.ServiceLoaderExtensionRegistry lookupExtensionUrls INFO: Found 1 extensions 29.06.2011 00:16:04 de.tutorials.extensibility.ServiceLoaderExtensionRegistry lookupExtensionUrls INFO: Found extension jar: file:/C:/temp/app/./ext/extension1.ext.jar 29.06.2011 00:16:04 de.tutorials.extensibility.ServiceLoaderExtensionRegistry getExtensions INFO: Found extension for de.tutorials.app.components.Component: de.tutorials.app.components.StandardComponent@38503429 from URL: file:/C:/temp/app/app.jar 29.06.2011 00:16:04 de.tutorials.extensibility.ServiceLoaderExtensionRegistry getExtensions INFO: Found extension for de.tutorials.app.components.Component: de.tutorials.app.components.Component1A@6bdd46f7 from URL: file:/C:/temp/app/./ext/extension1.ext.jar 29.06.2011 00:16:04 de.tutorials.extensibility.ServiceLoaderExtensionRegistry getExtensions INFO: Found extension for de.tutorials.app.components.Component: de.tutorials.app.components.Component1B@7e0df503 from URL: file:/C:/temp/app/./ext/extension1.ext.jar 29.06.2011 00:16:04 de.tutorials.app.components.AbstractComponent start INFO: Started StandardComponent 29.06.2011 00:16:04 de.tutorials.app.components.AbstractComponent start INFO: Started Component1A 29.06.2011 00:16:04 de.tutorials.app.components.AbstractComponent start INFO: Started Component1B
Wie wir sehen hat unsere Erweiterungsmechanismus nun auch die Erweiterungen aus unserem extension1.ext.jar erkannt (Component1A, Component1B).
Hier die Ausgabe mit einem weiteren extension-jar:
Code :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
C:\temp\app>java -jar app.jar 29.06.2011 00:19:59 de.tutorials.extensibility.ServiceLoaderExtensionRegistry lookupExtensionUrls INFO: Using extension folder: C:\temp\app\.\ext 29.06.2011 00:19:59 de.tutorials.extensibility.ServiceLoaderExtensionRegistry lookupExtensionUrls INFO: Found 2 extensions 29.06.2011 00:19:59 de.tutorials.extensibility.ServiceLoaderExtensionRegistry lookupExtensionUrls INFO: Found extension jar: file:/C:/temp/app/./ext/extension1.ext.jar 29.06.2011 00:19:59 de.tutorials.extensibility.ServiceLoaderExtensionRegistry lookupExtensionUrls INFO: Found extension jar: file:/C:/temp/app/./ext/extension2.ext.jar 29.06.2011 00:19:59 de.tutorials.extensibility.ServiceLoaderExtensionRegistry getExtensions INFO: Found extension for de.tutorials.app.components.Component: de.tutorials.app.components.StandardComponent@b815859 from URL: file:/C:/temp/app/app.jar 29.06.2011 00:19:59 de.tutorials.extensibility.ServiceLoaderExtensionRegistry getExtensions INFO: Found extension for de.tutorials.app.components.Component: de.tutorials.app.components.Component1A@6100ab23 from URL: file:/C:/temp/app/./ext/extension1.ext.jar 29.06.2011 00:19:59 de.tutorials.extensibility.ServiceLoaderExtensionRegistry getExtensions INFO: Found extension for de.tutorials.app.components.Component: de.tutorials.app.components.Component1B@446b7920 from URL: file:/C:/temp/app/./ext/extension1.ext.jar 29.06.2011 00:19:59 de.tutorials.extensibility.ServiceLoaderExtensionRegistry getExtensions INFO: Found extension for de.tutorials.app.components.Component: de.tutorials.app.components.Component2XXX@65bd0dd4 from URL: file:/C:/temp/app/./ext/extension2.ext.jar 29.06.2011 00:19:59 de.tutorials.app.components.AbstractComponent start INFO: Started StandardComponent 29.06.2011 00:19:59 de.tutorials.app.components.AbstractComponent start INFO: Started Component1A 29.06.2011 00:19:59 de.tutorials.app.components.AbstractComponent start INFO: Started Component1B 29.06.2011 00:19:59 de.tutorials.app.components.AbstractComponent start INFO: Started Component2XXX
Ich hoffe dieses Beispiel hat klar gemacht wie einfach man sehr flexible Erweiterungsmechanismen auf Basis des ServiceLoader APIs definieren kann.
Hier ein paar weitere Beispiele zum ServiceLoader API:
http://www.tutorials.de/java/357126-...ml#post1850187
http://www.tutorials.de/java/358931-...ml#post1859684
Gruß TomJava rocks!
How to become a good Java Programmer?
Does IT in Java and .Net
The only valid measurement of code quality: WTFs / minute
Blog
Xing
Twitter
-
29.06.11 00:25 #2SE Tutorials.de Gastzugang
Anmerkung aus Erfahrung:
Dieses Prinzip lässt sich NICHT in ein externes Jar auslagern. Die gesamte ServiceLoader-Einheit muss zum Startzeitpunkt der VM im CLASSPATH bekannt sein !
-
29.06.11 00:30 #3
- Registriert seit
- Jun 2002
- Ort
- Saarbrücken (Saarland)
- Beiträge
- 9.886
- Blog-Einträge
- 29
Hallo,
Ich denke das stimmt so nicht.... das Interface mit dem du nach Erweiterungen suchst muss natürlich dem Modul bekannt sein, das Erweiterungen dazu sucht...Dieses Prinzip lässt sich NICHT in ein externes Jar auslagern. Die gesamte ServiceLoader-Einheit muss zum Startzeitpunkt der VM im CLASSPATH bekannt sein !
Weitere jars kannst du der Applikation auch einfach hinzufügen, du musst nur in der Anwendung erkennen, dass es neue Erweiterungen gibt und einen neuen ClassLoader erzeugen welche dann die neuen Erweiterungen berücksichtigt.
Gruß TomJava rocks!
How to become a good Java Programmer?
Does IT in Java and .Net
The only valid measurement of code quality: WTFs / minute
Blog
Xing
Twitter
-
29.06.11 00:43 #4SE Tutorials.de Gastzugang
Mein Post zielte darauf ab das du immer nur Lösungen und "Tricks" postest an denen man ganz schön rumtricksen muss ... nämlich das manuelle angeben des CP. Genau das ist aber irgendwie nicht der Sinn eines Plug-In basierten Systems da sich der Plug-In-Handler in aller regel seine Plug-In's selbst suchen können sollte. Das wirklich Problem was hierbei auftritt ist wenn man einen solchen Plug-In-Handler ebenfalls als art Plug-In oder Extension der Applikation hinzufügen möchte. Es kracht an genau dem Punkt an dem das gesamte Plug-In System zur Runtime nachgeladen wird und nicht schon zur Laufzeit bekannt ist.
Hier sollte man wirklich auf diese Besonderheit hinweisen da einige User *unter anderem auch ich* sonst daran verzweifeln das es nicht läuft weil man es als neue Modul seiner Applikation hinzugefügt hat. Es besteht schon ein gewisser Aufwand in einen bestehende Anwendung soetwas im nachhinein zu integrieren.
-
29.06.11 00:54 #5
- Registriert seit
- Jun 2002
- Ort
- Saarbrücken (Saarland)
- Beiträge
- 9.886
- Blog-Einträge
- 29
Hallo,
Wo ist denn im obigen Beispiel ein manueller Classpath gesetzt?Mein Post zielte darauf ab das du immer nur Lösungen und "Tricks" postest an denen man ganz schön rumtricksen muss ... nämlich das manuelle angeben des CP
Gruß TomJava rocks!
How to become a good Java Programmer?
Does IT in Java and .Net
The only valid measurement of code quality: WTFs / minute
Blog
Xing
Twitter
Ähnliche Themen
-
Moderner Login-Mechanismus?
Von Trash im Forum PHPAntworten: 3Letzter Beitrag: 16.12.10, 11:10 -
ServiceLoader API
Von dadom110 im Forum JavaAntworten: 6Letzter Beitrag: 21.09.10, 13:27 -
Tomcat Login Mechanismus
Von Thomas Darimont im Forum Enterprise Java (JEE, J2EE, Spring & Co.)Antworten: 29Letzter Beitrag: 27.05.09, 10:41 -
Mechanismus für Virtuelle Methodenaufrufe umgehen.
Von Thomas Darimont im Forum C/C++Antworten: 4Letzter Beitrag: 31.10.04, 16:45 -
zahnrad mechanismus
Von easybrother im Forum 3D Studio MaxAntworten: 6Letzter Beitrag: 24.02.03, 23:31






Zitieren
Login





