Hallo,
hier mal just 4 fun ein kleines Beispiel für einen Plugin Mechanismus der den ServiceLoader verwendet. Hier sieht man schön wie einfach man mit dem ServiceLoader eigene Erweiterungsmechanismen aufbauen kann ohne sich mit ClassLoader spielereien herumschlagen zu müssen.
Wir haben 3 Module:
Unsere Platform: de.tutorials.platform.training.jar
Unsere Anwendung: de.tutorials.platform.application.helloworld.jar
Erwieterungen zur Anwendung: de.tutorials.platform.application.helloworld.extensions.jar
Wir haben folgende Strutkur:
/deploy/de.tutorials.platform.training.jar
/deploy/applications/de.tutorials.platform.application.helloworld.jar
/deploy/extensions/de.tutorials.platform.application.helloworld.extensions.jar
Unsere Platform bietet die Möglichkeit Anwendungsfunktionalität mit Extensions (Erweiterungen) an ExtensionPoints (Erweiterungspunkten) über konkrete Contributions(Beiträge) zu erweitern. Ähnlichkeiten mit dem Eclipse Plugin-Mechanismus
sind natürlich nur rein zufällig ;-)
Die Plugin Systeme von Eclipse und Netbeans sind natürlich sehr viel umfangreicher als der hier vorgestellte Ansatz.
Modul: de.tutorials.platform.training.jar
Java:
package de.tutorials;
public interface IApplication {
void start();
}
Java:
package de.tutorials;
public interface IPlatform {
void launch();
IContribution<IApplication>[] getContributionsFor(IApplication application, String target);
}
Java:
package de.tutorials;
public interface IContribution<TApplication extends IApplication> {
void contribute(TApplication application);
}
Java:
package de.tutorials;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.TYPE})
public @interface Extension {
String target();
}
Java:
package de.tutorials.internal;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import de.tutorials.Extension;
import de.tutorials.IApplication;
import de.tutorials.IContribution;
import de.tutorials.IPlatform;
public class Platform implements IPlatform {
ServiceLoader<IApplication> applicationServiceLoader;
ServiceLoader<IContribution<IApplication>> contributionServiceLoader;
Map<String, IApplication> applicationNameToApplicationMap = new HashMap<String, IApplication>();
Map<String, IContribution<IApplication>> contributionNameToContributionMap = new HashMap<String, IContribution<IApplication>>();
public final static IPlatform INSTANCE = new Platform();
private boolean launched;
private Platform(){}
@Override
public void launch() {
if (!launched) {
System.out.println("Launching");
init();
loadApplications();
loadExtensions();
startApplications();
launched = true;
}
}
@SuppressWarnings("unchecked")
private void init() {
this.applicationServiceLoader = ServiceLoader.load(IApplication.class);
this.contributionServiceLoader = (ServiceLoader<IContribution<IApplication>>) (Object) ServiceLoader
.load(IContribution.class);
}
private void loadApplications() {
for (IApplication application : applicationServiceLoader) {
register(application);
}
}
private void loadExtensions() {
for (IContribution<IApplication> contribution : contributionServiceLoader) {
register(contribution);
}
}
private void register(IApplication application) {
Class<?> applicationInterface = getSpecificApplicationInterface(application);
System.out.printf("Register Application: %s from %s\n",
applicationInterface.getName(), applicationInterface
.getProtectionDomain().getCodeSource().getLocation());
applicationNameToApplicationMap.put(applicationInterface.getName(),
application);
}
private Class<?> getSpecificApplicationInterface(IApplication application) {
Class<?> applicationInterface = null;
for (Class<?> iface : application.getClass().getInterfaces()) {
for (Class<?> parentIterface : iface.getInterfaces()) {
if (parentIterface.equals(IApplication.class)) {
applicationInterface = iface;
break;
}
}
}
return applicationInterface;
}
@SuppressWarnings("unchecked")
private void register(IContribution<IApplication> contribution) {
Class<? extends IContribution<IApplication>> contributionClazz = (Class<? extends IContribution<IApplication>>) (Object) contribution
.getClass();
String target = getExtensionTargetOf(contributionClazz);
Class<IApplication> contributionTargetApplication = getTargetApplicationOf(contributionClazz);
if (null != contributionTargetApplication) {
System.out
.printf(
"Register Contribution: %s for target: %s application: %s from: %s\n",
contribution.getClass().getName(), target,
contributionTargetApplication.getName(),
contributionClazz.getProtectionDomain()
.getCodeSource().getLocation());
contributionNameToContributionMap.put(contribution.getClass()
.getName(), contribution);
}
}
private String getExtensionTargetOf(
Class<? extends IContribution<IApplication>> contributionClazz) {
Extension extension = contributionClazz.getAnnotation(Extension.class);
return extension.target();
}
@SuppressWarnings("unchecked")
private Class<IApplication> getTargetApplicationOf(
Class<? extends IContribution<IApplication>> contributionClazz) {
Class<IApplication> contributionTargetApplication = null;
try {
for (Method method : contributionClazz.getDeclaredMethods()) {
Class[] parameterTypes = method.getParameterTypes();
if (!method.isBridge()
&& parameterTypes.length == 1
&& IApplication.class
.isAssignableFrom(parameterTypes[0])) {
contributionTargetApplication = parameterTypes[0];
break;
}
}
} catch (Exception exception) {
exception.printStackTrace();
}
return contributionTargetApplication;
}
private void startApplications() {
for (String applicationName : applicationNameToApplicationMap.keySet()) {
System.out.println("Starting application: " + applicationName);
applicationNameToApplicationMap.get(applicationName).start();
}
}
@Override
@SuppressWarnings("unchecked")
public IContribution<IApplication>[] getContributionsFor(
IApplication application, String target) {
Class<?> applicationInterface = getSpecificApplicationInterface(application);
List<IContribution<IApplication>> applicationSpecificContributionsForGivenTarget = new ArrayList<IContribution<IApplication>>();
for (String contributionName : contributionNameToContributionMap
.keySet()) {
IContribution<IApplication> contribution = contributionNameToContributionMap
.get(contributionName);
Class<IApplication> contributionTargetApplicationType = getTargetApplicationOf((Class<? extends IContribution<IApplication>>) (Object) contribution
.getClass());
if (contributionTargetApplicationType.equals(applicationInterface)) {
if (null == target || target.length() == 0
|| target.equals(target)) {
applicationSpecificContributionsForGivenTarget
.add(contribution);
}
}
}
return applicationSpecificContributionsForGivenTarget
.toArray(new IContribution[0]);
}
}
Java:
package de.tutorials;
import de.tutorials.internal.Platform;
public class ExtensiblePlatformExample {
public static void main(String[] args) {
IPlatform platform = Platform.INSTANCE;
platform.launch();
}
}
Modul:de.tutorials.platform.application.helloworld.jar
Java:
package de.tutorials;
public interface IExtensionPoints{
public final static String MESSAGES = "de.tutorials.IExtensionPoints#MESSAGES";
}
Java:
package de.tutorials;
import java.util.List;
public interface IHelloWorldApplication extends IApplication{
public List<String> getMessages();
}
Java:
package de.tutorials.internal;
import java.util.ArrayList;
import java.util.List;
import de.tutorials.IApplication;
import de.tutorials.IContribution;
import de.tutorials.IExtensionPoints;
import de.tutorials.IHelloWorldApplication;
public class HelloWorldApplication implements IHelloWorldApplication {
private List<String> messages = new ArrayList<String>();
private void init() {
for (IContribution<IApplication> messageContribution : Platform.INSTANCE.getContributionsFor(this,IExtensionPoints.MESSAGES)) {
messageContribution.contribute(this);
}
}
@Override
public void start() {
init();
for (String message : messages) {
System.out.println(message);
}
}
public List<String> getMessages() {
return messages;
}
}
Java:
package de.tutorials.contribution.internal;
import de.tutorials.Extension;
import de.tutorials.IContribution;
import de.tutorials.IExtensionPoints;
import de.tutorials.IHelloWorldApplication;
@Extension(target = IExtensionPoints.MESSAGES)
public class AnotherHelloWorldMessagesContribution implements
IContribution<IHelloWorldApplication>{
@Override
public void contribute(IHelloWorldApplication application) {
application.getMessages().add("Huhu");
application.getMessages().add("Moin");
application.getMessages().add("Tach");
}
}
Java:
package de.tutorials.contribution.internal;
import de.tutorials.Extension;
import de.tutorials.IContribution;
import de.tutorials.IExtensionPoints;
import de.tutorials.IHelloWorldApplication;
@Extension(target = IExtensionPoints.MESSAGES)
public class HelloWorldMessagesContribution implements
IContribution<IHelloWorldApplication> {
@Override
public void contribute(IHelloWorldApplication application) {
application.getMessages().add("Hey");
application.getMessages().add("Hello");
application.getMessages().add("Hi");
}
}
Unter META-INF/services liegt nun die Datei de.tutorials.IApplication
Code:
de.tutorials.internal.HelloWorldApplication
Unter META-INF/services liegt nun die Datei de.tutorials.IContribution
Code:
de.tutorials.contribution.internal.HelloWorldMessagesContribution
de.tutorials.contribution.internal.AnotherHelloWorldMessagesContribution
In diesem Fall erweitert sich die Anwendung per default selbst mit zwei Contributions.
Modul: de.tutorials.platform.application.helloworld.extensions.jar
Java:
package de.tutorials.contribution.internal;
import de.tutorials.Extension;
import de.tutorials.IContribution;
import de.tutorials.IExtensionPoints;
import de.tutorials.IHelloWorldApplication;
@Extension(target = IExtensionPoints.MESSAGES)
public class YetAnotherHelloWorldMessagesContribution implements
IContribution<IHelloWorldApplication> {
@Override
public void contribute(IHelloWorldApplication application) {
application.getMessages().add("Foo");
application.getMessages().add("Bar");
application.getMessages().add("Buu");
}
}
Unter META-INF/services liegt nun die Datei de.tutorials.IContribution
Code:
de.tutorials.contribution.internal.YetAnotherHelloWorldMessagesContribution
Nun kann man via:
Code:
java
-cp
deploy/de.tutorials.platform.training.jar;deploy/applications/*;deploy/extensions/*
de.tutorials.ExtensiblePlatformExample
Die Platform mit den darin befindlichen Anwendungen und den entsprechenden Plugins
starten. Seit Java 6 kann man mit -cp xxx/* alle Jars die im Verzeichnis xxx liegen
automatisch in den classpath aufnehmen ohne sie einzeln auflisten zu müssen.
Ausgabe:
Code:
D:\eclipse\workspaces\workspace-3.4M5\de.tutorials.platform.training>java -cp deploy/de.tutorials.platform.training.jar;deploy/applications/*;deploy/extensions/* de.tutorials.ExtensiblePlatformExample
Launching
Register Application: de.tutorials.IHelloWorldApplication from file:/D:/eclipse/workspaces/workspace-3.4M5/de.tutorials.platform.training/deploy/applications/de.tutorials.platform.application.helloworld.jar
Register Contribution: de.tutorials.contribution.internal.HelloWorldMessagesContribution for target: MESSAGES application: de.tutorials.IHelloWorldApplication from: file:/D:/eclipse/workspaces/workspace-3.4M5/de.tutorials.platform.training/deploy/applications/de.tutorials.platform.application.helloworld.jar
Register Contribution:de.tutorials.contribution.internal.AnotherHelloWorldMessagesContribution for target:MESSAGES application:de.tutorials.IHelloWorldApplication from:file:/D:/eclipse/workspaces/workspace-3.4M5/de.tutorials.platform.training/deploy/applications/de.tutorials.platform.application.helloworld.jar
Register Contribution:de.tutorials.contribution.internal.YetAnotherHelloWorldMessagesContribution for target: MESSAGES application: de.tutorials.IHelloWorldApplication from: file:/D:/eclipse/workspaces/workspace-3.4M5/de.tutorials.platform.training/deploy/extensions/de.tutorials.platform.application.helloworld.extensions.jar
Starting application: de.tutorials.IHelloWorldApplication
Hey
Hello
Hi
Foo
Bar
Buu
Huhu
Moin
Tach
Gruß Tom