Beispiel zum Zugriff auf einen Service und eine Extension durch ein anderes Plugin: Unterschied zwischen den Versionen

Aus Eclipse
Wechseln zu: Navigation, Suche
(Die Seite wurde neu angelegt: „=Zugriff auf einen Service und eine Extension durch ein anderes Plugin= Das folgende Beispiel zeigt, wie auf einen Service bzw. eine Extension zugegriffen werden …“)
 
K (hat „Beisiel 2“ nach „Beispiel zum Zugriff auf einen Service und eine Extension durch ein anderes Plugin“ verschoben: Nichtssagender Seitentitel)
(kein Unterschied)

Version vom 15. Juli 2010, 13:58 Uhr

Zugriff auf einen Service und eine Extension durch ein anderes Plugin

Das folgende Beispiel zeigt, wie auf einen Service bzw. eine Extension zugegriffen werden kann. Sowohl der Service als auch die Extension werden je von einem Plugin implementiert. Ihre Dienste sind dabei ähnlich. Sie geben beide eine Frase aus. Wie das Beispiel zeigt, wird die Serviceklasse vom Framework instanziiert, während das im Falle der Extension der Nutzer erledigen muss. Hierdurch mutet der Code zur Nutzung einer Extension komplizierter an. Allerdings ist diese dafür sofort verfügbar, sobald das implementierende Plugin installiert ist. Der Service dagegen ist erst dann nutzbar, wenn das implementierende Plugin gestartet wurde.

Zugriff auf den Service:

Damit auf den Service zugegriffen werden kann, muss dieser vom implementierenden Plugin am Framework registriert werden. Dies kann im Activator in der start Methode geschehen.

	/*
	 * (non-Javadoc)
	 * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
	 */
	public void start(BundleContext context) throws Exception {
		super.start(context);
		plugin = this;
		// hier wird das neue Plugin registriert
		context.registerService(
				IFrasenProvider.class.getName(),
				new SpiellaengeFrase(),null); 	
	}


Möchte ein Bundle diesen Service nutzen, kann es sich vom Framework benachrichtigen lassen, sobald der Service verfügbar ist.Zu diesem Zweck kann das Bundle einen ServiceTracker bei der Service Registry anmelden. Ein Servicetracker ist eine Instanz der Klasse ServiceTracker, die das Interface ServiceTrackerCustomizer implementiert. Die Interface Methoden werden von der Service Registry aufgerufen, wenn ein entsprechendes Ereignis (Service hinzugefügt, Service entfernt, Service modifiziert) eingetreten ist. Durch Überschreiben dieser Methoden kann auf die Ereignisse reagiert werden. Der Konstruktor eines Service Trackers nimmt als Parameter einen Klassennamen entgegen, der als Filter dient. Ereignisse nur bezgl. Services, die diesem Filter entsprechen, werden an diese Instanz des Service Trackers geliefert.
Der Servicetracker kann zB. in der Start-Methode des Activators des servicenutzenden Bundles registriert werden.
Im folgendem Beispiel registriert ein Bundle einen ServiceTracker, der auf Ereignisse eines Bundles namens IFrasenBundle lauscht. Der ServiceTrackerCustomizer wird im folgendem Beispiel als anonyme Instanz an den ServiceTracker übergeben. Sobald sich ein entsprechendes Bundle am Framework anmeldet, wird auf der Console die von diesem Bundle erzeugte Frase ausgegeben:

/*
	 * (non-Javadoc)
	 * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
	 */
	public void start(final BundleContext context) throws Exception {
		super.start(context);
		plugin = this;
 
		frasenPluginTracker = new ServiceTracker(context, IFrasenProvider.class.getName(), new ServiceTrackerCustomizer() 
			{
			 public Object addingService(final ServiceReference reference) 
			 {
				 final IFrasenProvider frasenProvider = (IFrasenProvider) context.getService(reference);
				 System.out.println(frasenProvider.frase());
				 return frasenProvider;
			 }
 
			 public void modifiedService(final ServiceReference reference,
			 final Object service) 
			 { // Nothing to be done!
			 }
 
			 public void removedService(final ServiceReference reference,
			 final Object service) 
			 { // Nothing to be done!
			 }
			});
		frasenPluginTracker.open(); 		
 
	}


Zugriff auf Extensions:

Ganz anders ist der Workflow bei einer Extension. Hier muss das Bundle, das die Extension zur Verfügung stellt, zur Laufzeit nichts machen. Die Extension ist verfügbar, sobald das Bundle installiert ist. Das nutzende Bundle muss jedoch zunächst die implementierende Klasse am Besitzerbundle erfragen. Der Klassloader des Bundles, der die Extension implementiert, stellt dann dem anfragenden Bundle die fragliche Klasse zur Verfügung.

	/*
	 * (non-Javadoc)
	 * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
	 */
	public void start(BundleContext context) throws Exception {
                // zunächst den ExtensionPoint ermitteln zu dem die Frase Extensions passen
                IExtensionPoint ep = RegistryFactory.getRegistry().getExtensionPoint("PluginSucher.getFrase");
		IExtension[] extensions = ep.getExtensions();
		// herausfinden welches Element das Client Element ist (dieses enthält den Klassennamen der FraseProviders
		// über alle Extensions iterieren
		for (int i = 0; i < extensions.length; i++) {
			IConfigurationElement[] extensionElements =  extensions[i].getConfigurationElements();
			// über alle Elemente der Extension iterieren
			for( int j = 0; j < extensionElements.length ; j++){
				IConfigurationElement configurationElement = extensionElements[j];
					//wenn der Elementname Client ist dann Klasse instanziieren und Frase ausgeben
				if(configurationElement.getName().equalsIgnoreCase("Client")){
					// jetzt haben wir endlich den Frasenprovider!
					try {
						IFrasenProvider frasenProvider = (IFrasenProvider)configurationElement.createExecutableExtension("class");
						// Frase ausgeben
						System.out.println(frasenProvider.frase());
					} catch (CoreException e) {
						e.printStackTrace();
					}
				}
			}
	}

Besondere Aufmerksamkeit muss hier folgendem Aufruf geschenkt werden:
IFrasenProvider frasenProvider = (IFrasenProvider)configurationElement.createExecutableExtension("class");
Die Funktion IConfigurationElement.createExecutableExtension("class") erledigt zwei Dinge:

  1. instanziieren der im Configurationselement enthaltenen Klasse mittels des Defaulkonstuktors. Der fullQualifiedName der Klasse wird dabei aus dem classPropertyName Argument des configurationElements bezogen.
  2. initialisieren des zuvor instanziierten Objektes. Dies erfolgt aber nur, wenn das Objekt das Interface IExecutableExtension implementiert.

Das Interface IFrasenProvider fungiert hier also als Callback Interface, über welches die Funktionalität der Extension abgerufen werden kann.
Übrigens folgender Code funktioniert nicht:
Class.forName( configurationElement.getAttribute("class")).newInstance();
um eine Extensionklasse zu erzeugen. Hier kommt es zu einer ClassNotFound Exception. Denn diese Instruktion würde den Plugin eigenem Classloader anweisen, die Klasse zu laden. Der kann diese aber nicht finden, da die entsprechende klasse ja zuvor nicht importiert wurde. Diese Klasse kann nur vom Klassloader der implementierende Klasse geladen werden.
Bleibt noch zu erwähnen, dass der Aufruf der Methode IConfigurationElement.createExecutableExtension("class") natürlich bewirkt das ein Plugin das noch nicht aktiviert worden ist aktiviert wird. Wie unter Lebenszyklus diskutiert ist dies ein teurer Prozess. Diese Methode sollte also nur dann aufgerufen werden unmittelbar bevor eine Klasse benötigt wird. Insbesondere sollte im Activator darauf verzichtet werden ConfigurationElement.createExecutableExtension("class") auf anderen Plugins aufzurufen, da hierdurch die Startzeit des eigenen Plugins erheblich verzögert wird.