Kleines Beispiel zum EventAdmin Service unter OSGi / Equinox

Thomas Darimont

Erfahrenes Mitglied
Hallo,

hier mal ein Beispiel für den EventAdmin Service unter Equinox OSGi.
Normalerweise verwendet man diesen Dienst um Events über Bundlegrenzen hinweg / zwischen Bundles auszutauschen. In diesem Beispiel ist mein Bundle der Einfachheit halber Sender und Empfänger gleichzeitig.
Java:
package de.tutorials.osgi.events;

import java.util.Dictionary;
import java.util.Hashtable;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;

public class Activator implements BundleActivator {

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext
     * )
     */
    public void start(BundleContext context) throws Exception {

        ServiceReference eventAdminServiceReference = context
                .getServiceReference(EventAdmin.class.getName());

        //EventAdmin Service lookup
        final EventAdmin eventAdmin = (EventAdmin) context
                .getService(eventAdminServiceReference);

        //wir sind an Events interessiert die im de/tutorials Namespace (topic) und "darunter" gesendet werden
        String[] topics = new String[] { EventConstants.EVENT_TOPIC,
                "de/tutorials/*" };
        
        Dictionary<String, Object> ht = new Hashtable<String, Object>();
        ht.put(EventConstants.EVENT_TOPIC, topics);

        //hier registrieren wir einen entsprechenden EventHandler
        context.registerService(EventHandler.class.getName(),
                new EventHandler() {
                    @Override
                    public void handleEvent(Event event) {
                        System.out.println("handleEvent: " + event);
                        try {
                            TimeUnit.SECONDS.sleep(2L);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("finished handleEvent: " + event);
                    }
                }, ht);

        Executors.newSingleThreadExecutor().execute(new Runnable() {

            @Override
            public void run() {
                int count = 0;
                while (true) {

                    //hier senden wir 2 Events an unterschiedliche Topics
                    if (count++ % 5 != 0) {
                        
                        //postEvent sendet events asynchron -> Methode kehrt sofort zurück
                        eventAdmin.postEvent(new Event("de/tutorials/BUBU"
                                + System.currentTimeMillis(), null));
                    } else {
                        //sendEvent sendet events synchron -> Methode wartet bis alle Listener feritg sind.
                        eventAdmin.sendEvent(new Event("de/tutorials/a/b/c/d/e/XXXX"
                                + System.currentTimeMillis(), null));
                    }
                    try {
                        TimeUnit.SECONDS.sleep(1L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        });
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
     */
    public void stop(BundleContext context) throws Exception {
    }

}

Ausgabe:
Code:
osgi> start de.tutorials.osgi.events

osgi> handleEvent: org.osgi.service.event.Event [topic=de/tutorials/a/b/c/d/e/XXXX1220304000057]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/a/b/c/d/e/XXXX1220304000057]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304003058]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304003058]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304004058]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304004058]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/a/b/c/d/e/XXXX1220304007058]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304005058]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304005058]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/a/b/c/d/e/XXXX1220304007058]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304006058]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304006058]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304010058]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304010058]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304011058]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/a/b/c/d/e/XXXX1220304014058]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304011058]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304012058]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/a/b/c/d/e/XXXX1220304014058]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304012058]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304013058]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304013058]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304017058]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304017058]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304018058]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/a/b/c/d/e/XXXX1220304021058]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/a/b/c/d/e/XXXX1220304021058]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304018058]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1220304019058]


Mittlerweile gibts für equinox eine ganze Reihe von Referenzimplementierungen von Diensten der OSGi Spezifikation siehe:
http://www.eclipse.org/equinox/bundles
Downloaden kann man das ganze via:
http://download.eclipse.org/eclipse/equinox/
http://download.eclipse.org/eclipse/equinox/drops/S-3.5M1-200808071402/index.php

Gruß Tom
 
Hallo,

ich möchte den EventAdmin Service in einer RCP Anwendung nutzen. Wenn ich so vorgehe wie beschrieben erhalte ich bei
Code:
ServiceReference eventAdminServiceReference = context
                .getServiceReference(EventAdmin.class.getName());
immer nur NULL statt der Referenz. Welche osgi Bundles muss ich aktivieren und muss der EventAdmin Service zusätzlich noch irgendwo registriert werden?
 
Hallo,

du brauchst dazu eine Implementierung vom EventAdminService.

Diese findest du beispielsweise im aktuellen Equinox 3.5 SDK:

http://download.eclipse.org/equinox/drops/R-3.5-200906111540/index.php

Equinox 3.5 SDK
http://www.eclipse.org/downloads/do.../drops/R-3.5-200906111540/equinox-SDK-3.5.zip


im plugins-Verzeichnis als:
org.eclipse.equinox.event_1.1.100.v20090520-1800.jar
vor.

für das kleine Beispiel benötige ich folgende Bundles:
Code:
osgi> ss

Framework is launched.

id    State       Bundle
0    ACTIVE      org.eclipse.osgi_3.5.0.v20090520
1    ACTIVE      org.eclipse.osgi.services_3.2.0.v20090520-1800
2    ACTIVE      javax.servlet_2.5.0.v200806031605
3    ACTIVE      de.tutorials.equinox.events_1.0.0.qualifier
4    ACTIVE      org.eclipse.equinox.event_1.1.100.v20090520-1800

Im Manifest habe ich bei required plugins:
org.eclipse.osgi.services angegeben.

Du musst dafür sorgen, dass das Bundle org.eclipse.equinox.event_XXXX vor deinem Anwendungsbundle gestartet wird.

Das geht Beispielsweise über die start Level Konfiguration der entsprechenden Launch Configuration.

Hier eine Version die wunderbar mit Eclipse / Equniox 3.5 funktioniert
Java:
package de.tutorials.equinox.events;

import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;

public class Activator implements BundleActivator {

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext
     * )
     */
    public void start(BundleContext context) throws Exception {

        ServiceReference eventAdminServiceReference = context
                .getServiceReference(EventAdmin.class.getName());

        // EventAdmin Service lookup
        final EventAdmin eventAdmin = (EventAdmin) context
                .getService(eventAdminServiceReference);

        // wir sind an Events interessiert die im de/tutorials Namespace (topic)
        // und "darunter" gesendet werden
        String[] topics = new String[] { EventConstants.EVENT_TOPIC,
                "de/tutorials/*" };

        Dictionary<String, Object> ht = new Hashtable<String, Object>();
        ht.put(EventConstants.EVENT_TOPIC, topics);

        // hier registrieren wir einen entsprechenden EventHandler
        context.registerService(EventHandler.class.getName(),
                new EventHandler() {
                    @Override
                    public void handleEvent(Event event) {
                        System.out.println("handleEvent: " + event);
                        try {
                            TimeUnit.SECONDS.sleep(2L);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("finished handleEvent: " + event);
                    }
                }, ht);

        Executors.newSingleThreadExecutor().execute(new Runnable() {

            @Override
            public void run() {
                int count = 0;
                while (true) {

                    // hier senden wir 2 Events an unterschiedliche Topics
                    if (count++ % 5 != 0) {

                        // postEvent sendet events asynchron -> Methode kehrt
                        // sofort zurück
                        eventAdmin.postEvent(new Event("de/tutorials/BUBU"
                                + System.currentTimeMillis(),
                                new HashMap<String, String>()));
                    } else {
                        // sendEvent sendet events synchron -> Methode wartet
                        // bis alle Listener feritg sind.
                        eventAdmin.sendEvent(new Event(
                                "de/tutorials/a/b/c/d/e/XXXX"
                                        + System.currentTimeMillis(),
                                new HashMap<String, String>()));
                    }
                    try {
                        TimeUnit.SECONDS.sleep(1L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        });
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
     */
    public void stop(BundleContext context) throws Exception {
    }

}

Ausgabe:
Code:
osgi> handleEvent: org.osgi.service.event.Event [topic=de/tutorials/a/b/c/d/e/XXXX1246391235526]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/a/b/c/d/e/XXXX1246391235526]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1246391238531]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1246391238531]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1246391239533]
finished handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1246391239533]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/BUBU1246391240533]
handleEvent: org.osgi.service.event.Event [topic=de/tutorials/a/b/c/d/e/XXXX1246391242535]
...

Gruß Tom
 

Anhänge

  • de.tutorials.equinox.events.zip
    7,5 KB · Aufrufe: 91
Hallo Tom,

danke für deine ausführliche Antwort! Mir hat nur der org.eclipse.equinox.event_1.1.100.v20090520-1800 gefehlt, da dieser nicht im Eclipse SDk dabei ist.

Code:
javax.servlet_2.5.0.v200806031605
benötigt man übrigens nicht ;-)

Eine Frage habe ich noch:

Wozu dient das Dictionary Object im Event Object
Code:
new Event( "de/tutorials/a/b/c/d/e/XXXX",dictionary)
? Ich nehme an um darin beliebige Objekte zu kapseln?
 
Zuletzt bearbeitet:
Hallo,

javax.servlet_2.5.0.v200806031605 benötigt man übrigens nicht ;-)
Ja, schon klar. Trotzdem ist irgendwo eine Abhängigkeit bei den anderen Bundles noch falsch definiert die bei einem klick auf add required Bundles in der Launchconfiguration javax.servlet hinzufügt... zumindest im eclipse 3.5 SDK.

Wozu dient das Dictionary Object im Event Object Code: new Event( "de/tutorials/a/b/c/d/e/XXXX",dictionary)
? Ich nehme an um darin beliebige Objekte zu kapseln?
Jep. Das ist der "Payload" für das Event.


Gruß Tom
 
Noch eine Frage, wie kann ich den EventHandler wieder entfernen ? Die context.removeServiceListener() Methode benötigt ein ServiceListener Objekt als Parameter und nicht ein EventHandler Objekt ..
 
Hallo.

Noch eine Frage zu den Events bzw. zum handleEvent des EventHandler:
Wie können Events weiter verarbeitet werden? Sprich nicht nur auf der Konsole ausgegeben werden, sondern von anderen Services verwendet werden?

O.g. Programm wurde so erweitert, dass die Events in der run() Methode via EventAdmin (als Declarative Service) "versendet" werden:

<reference name="eventAdmin"
interface="org.osgi.service.event.EventAdmin"
bind="setEventAdmin"
unbind="unsetEventAdmin"
/>

Event event = new Event("generator/event", eventProperties);
eventAdmin.sendEvent(event);




Eine andere Komponente Namens "InGate" fängt diese dann auch auf:

<?xml version="1.0"?>
<component name="InGateComponent">
<implementation class="InGate"/>
<property name="event.topics" value="generator/event"/>
<service>
<provide interface="org.osgi.service.event.EventHandler"/>
</service>
<reference name="logService"
interface="org.osgi.service.log.LogService"
bind="setLogService"
unbind="unsetLogService"
/>
</component>


public class InGate implements EventHandler{

[...]

public void handleEvent(Event event) {

// geht
String[] allProps = event.getPropertyNames();
for (String s: allProps){
System.out.println("Props: " + s + " = " + event.getProperty(s));

// geht nicht
doJob(event);

}

So weit so gut. sysout funktioniert auch hier. Das Problem sind Aufrufe von anderen Services innerhalb der handelEvent Methode des EventAdmins.
Sowohl bei der Verwendung eines logServices (dieser funktioniert z.B. in der Activate Methode von InGate) als auch bei eigenen Services (mit ServiceTracker XOR Declarative Services gebunden) kommt folgender Output mit Fehlermeldung:

Props: event.topics = generator/event
Props: unit = km/h
Props: velocity = 141
Props: type = velocity
[INFO] 20:36:55 - generator --> BundleEvent STARTED
[ERROR] 20:36:55 - org.eclipse.equinox.event --> Exception while dispatching event org.osgi.service.event.Event [generator/event] to handler InGate@1f8c6df
java.lang.NullPointerException
[ERROR] 20:36:55 - org.eclipse.equinox.event --> Exception while dispatching event org.osgi.service.event.Event [topic=generator/event] to handler InGate@1f8c6df

at InGate.doJob(InGate.java:60)
at InGate.handleEvent(InGate.java:96)
at org.eclipse.equinox.internal.event.EventHandlerWrapper.handleEvent(EventHandlerWrapper.java:177)
at org.eclipse.equinox.internal.event.EventHandlerTracker.dispatchEvent(EventHandlerTracker.java:198)
at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:220)
at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:149)
at org.eclipse.equinox.internal.event.EventAdminImpl.dispatchEvent(EventAdminImpl.java:139)
at org.eclipse.equinox.internal.event.EventAdminImpl.sendEvent(EventAdminImpl.java:78)
at VelocityComponent.run(VelocityComponent.java:77)
at java.util.TimerThread.mainLoop(Timer.java:512)
at java.util.TimerThread.run(Timer.java:462)
java.lang.NullPointerException



Warum legt sich die handleEvent Methode wegen der Thread run() Methode --> Zeile 77 (= eventAdmin.sendEvent(event)) auf die Nase?
 
Zurück