OSGi services are used to provide functionality via separate bundles. They are also used to decouple functionality, so it is possible to exchange the implementation at runtime. With the introduction of OSGi declarative services and Eclipse 4 it became more popular to use OSGi services in Eclipse RCP applications.
The communication system in Eclipse 4 is the global event bus. It supports communication between application components and is also used for decoupling, since bundles only need to know the event bus for communication, not the bundles they need to communicate with.
This recipe shows how to use the event bus in an Eclipse 4 based application, how to create a simple service using OSGi declarative services, and how to communicate via event bus in an OSGi service. For this, a part will be added to the application that shows log messages which are sent via event bus.
Ingredients
This recipe is based on the Eclipse RCP Cookbook – Basic Recipe . To get started fast with this recipe, we have prepared the basic recipe for you on GitHub .
To use the prepared basic recipe, import the project by cloning the Git repository:
- File → Import → Git → Projects from Git
- Click Next
- Select Clone URI
- Enter URI https://github.com/fipro78/e4-cookbook-basic-recipe.git
- Click Next
- Select the master branch
- Click Next
- Choose a directory where you want to store the checked out sources
- Click Next
- Select Import existing projects
- Click Next
- Click Finish
Note: With exception to the part implementation, this recipe does not depend on the used UI toolkit. So you can also use the JavaFX version of the basic recipe. If you want to follow this recipe in the JavaFX version, use the following URI to clone the prepared basic recipe: https://github.com/fipro78/e4-cookbook-basic-recipe-fx.git.
Preparation
Step 1: Sending events to the event bus
The Eclipse event service can be used to send events to the event bus. It is implemented via the IEventBroker
interface and can get injected. In this step the application is modified to send log events on specific actions.
- Update the bundle dependencies
- Open the file MANIFEST.MF in the project de.codecentric.eclipse.tutorial.inverter
- Switch to the Dependencies tab
- Add the following packages to the Required Plug-ins
- org.eclipse.e4.core.services
- Open the
InverterPart
in the project de.codecentric.eclipse.tutorial.inverter- Get the
IEventBroker
injected - Modify the listeners on the button and the input field to post an event that contains a String with the log message for the topic “TOPIC_LOGGING”.
1@Inject 2IEventBroker broker; 3 4@PostConstruct 5public void postConstruct(Composite parent) { 6 ... 7 button.addSelectionListener(new SelectionAdapter() { 8 @Override 9 public void widgetSelected(SelectionEvent e) { 10 ... 11 broker.post("TOPIC_LOGGING", "triggered via button"); 12 } 13 }); 14}
- For the JavaFX version this means to add the posting of the event to the onAction
EventHandler
.1@Inject 2IEventBroker broker; 3 4@PostConstruct 5public void postConstruct(Composite parent) { 6 ... 7 button.setOnAction(event -> { 8 ... 9 broker.post("TOPIC_LOGGING", "triggered via button"); 10 }); 11}
- Get the
Note: via IEventBroker#post()
the event is sent asynchronously. If you need to send the event synchronously, use IEventBroker#send().
Step 2: Receiving events from the event bus
The recommended way of receiving events from the event bus is to use dependency injection. Using the annotations @EventTopic
and @UIEventTopic
for method parameters will cause method execution if an event for the specified topic is fired on the event bus. The difference between the two annotations is that using @UIEventTopic
will execute the method in the UI thread.
In this step a log view is added to the application to show the log messages that were sent to the event bus.
- Create the package de.codecentric.eclipse.tutorial.app.part in the project de.codecentric.eclipse.tutorial.app
- Create a part that shows the log messages
- Open the file Application.e4xmi in the project de.codecentric.eclipse.tutorial.app
- Add a container for the part to the window
- Application → Windows and Dialogs → Trimmed Window → Controls → Part Sash Container → Add Part Stack
- Add a part to the container
- Application → Windows and Dialogs → Trimmed Window → Controls → Part Sash Container → Part Stack → Add Part
- Set the Label to Log View
- Create the part implementation
- Click the Class URI link in the part detail view
- Set the following values in the opened dialog
- Package: de.codecentric.eclipse.tutorial.app.part
- Name: LogViewPart
- Create a viewer that is used to show the log messages
- Create a method that is executed/notified when an event for the topic “TOPIC_LOGGING” is send
The following is an example of a part using SWT:
1public class LogViewPart {
2
3 ListViewer viewer;
4
5 @PostConstruct
6 public void postConstruct(Composite parent) {
7 viewer = new ListViewer(parent);
8 }
9
10 @Inject
11 @Optional
12 void logging(@UIEventTopic("TOPIC_LOGGING") String message) {
13 viewer.add(message);
14 }
15
16}
The following is an example of a part using JavaFX:
1public class LogViewPart {
2
3 ListView viewer;
4
5 @PostConstruct
6 public void postConstruct(BorderPane parent) {
7 viewer = new ListView();
8 parent.setCenter(viewer);
9 }
10
11 @Inject
12 @Optional
13 void logging(@UIEventTopic("TOPIC_LOGGING") String message) {
14 viewer.getItems().add(message);
15 }
16
17}
You can also subscribe for events by registering an org.osgi.service.event.EventHandler
for a topic to the IEventBroker
. In such a case you also need to take care of unregistering the handler again.
Step 3: Create an OSGi declarative service
An OSGi service is a java object instance, registered into an OSGi framework. Any java object can be registered as a service, but typically it implements a well-known interface. Via OSGi declarative services it is possible to define and implement an OSGi service without implementing or extending OSGi framework classes.
The basic recipe uses a static helper class to implement the functionality of inverting a String. In this step a new plug-in is created that contains an OSGi declarative service for that purpose. This way it will be possible to exchange the implementation at runtime or mock the implementation for testing.
- Create a new plug-in project
- Main Menu → File → New → Plug-in Project
- Set name to de.codecentric.eclipse.tutorial.service.inverter
- Click Next
- Select Execution Environment JavaSE-1.8
- Ensure that Generate an Activator and This plug-in will make contributions to the UI are disabled
- Click Finish
- Create an interface for the service definition
- Main Menu → File → New → Interface
- Source Folder: de.codecentric.eclipse.tutorial.service.inverter/src
- Package: de.codecentric.eclipse.tutorial.service.inverter
- Name: InverterService
- Add the method definition
String invert(String value);
- Main Menu → File → New → Interface
- Create the service implementation
- Main Menu → File → New → Class
- Source Folder: de.codecentric.eclipse.tutorial.service.inverter/src
- Package: de.codecentric.eclipse.tutorial.service.inverter.impl
- Name: InverterServiceImpl
- Interfaces: de.codecentric.eclipse.tutorial.service.inverter.InverterService
- Implement the method String invert(String);
- Main Menu → File → New → Class
- Configure the bundle via MANIFEST.MF
- Open the file META-INF/MANIFEST.MF in the project de.codecentric.eclipse.tutorial.service.inverter
- Switch to the Overview tab
- Activate Activate this plug-in when one of its classes is loaded
- Switch to the Dependencies tab
- Add the plug-in org.eclipse.osgi.services to the Required Plug-ins
- Switch to the Runtime tab
- Add de.codecentric.eclipse.tutorial.service.inverter to the list of Exported Packages
- Configure the OSGi declarative service
- Create the folder OSGI-INF in the project de.codecentric.eclipse.tutorial.service.inverter
- Create a Component Definition
- File → New → Component Definition
- Parent folder: de.codecentric.eclipse.tutorial.service.inverter/OSGI-INF
- File name: inverter.xml
- Component Definition Name: de.codecentric.eclipse.tutorial.service.inverter
- Component Definition Class: de.codecentric.eclipse.tutorial.service.inverter.impl.InverterServiceImpl
- File → New → Component Definition
- Switch to the Services tab
- Add de.codecentric.eclipse.tutorial.service.inverter.InverterService to the Provided Services
- Ensure the Service-Component entry pointing to OSGI-INF/inverter.xml is added to the MANIFEST.MF file
- Open the build.properties file in the project de.codecentric.eclipse.tutorial.service.inverter
- Add the folder OSGI-INF to the Binary Build
- Use the created
InverterService
in theInverterPart
- Open the file META-INF/MANIFEST.MF in the project de.codecentric.eclipse.tutorial.inverter
- Switch to the Dependencies tab
- Add the plug-in de.codecentric.eclipse.tutorial.service.inverter to the Required Plug-ins
- Open the
InverterPart
- Inject the
InverterService
as instance field - Replace the usage of the
StringInverter
helper class with using theInverterService
- Inject the
- Update the feature
- Open the file feature.xml in the project de.codecentric.eclipse.tutorial.feature
- Switch to the Plug-ins tab
- Add the plug-in de.codecentric.eclipse.tutorial.service.inverter to the list of Plug-ins and Fragments
Step 4: Send events via OSGi declarative service
The IEventBroker
is not available in the OSGi context, which allows us, for example, to have multiple instances in one application. This also means that it cannot be referenced in an OSGi declarative service. But as the IEventBroker
makes use of the OSGi EventAdmin
service, it is possible to send events to the event bus from an OSGi declarative service by directly using the EventAdmin
.
- Open the file META-INF/MANIFEST.MF in the project de.codecentric.eclipse.tutorial.service.inverter
- Switch to the Dependencies tab
- Add the plug-in org.eclipse.e4.core.services to the Required Plug-ins
- Open the file OSGI-INF/inverter.xml in the project de.codecentric.eclipse.tutorial.service.inverter
- Open the
InverterServiceImpl
- Add an instance field of type
EventAdmin
- Add the methods for binding and unbinding the
EventAdmin
- Use the
EventAdmin
ininvertString(String)
- Create an instance of
java.util.Dictionary
- Put the event topic value to the
Dictionary
for the keyEventConstants.EVENT_TOPIC
- Put the event value to the
Dictionary
for the keyIEventBroker.DATA
- Create an instance of type
org.osgi.service.event.Event
using the topic and theDictionary
- Post the event via the
EventAdmin
- Create an instance of
- Add an instance field of type
The finished InverterServiceImpl
should look similar to the following snippet:
1package de.codecentric.eclipse.tutorial.service.inverter.impl;
2
3import java.util.Dictionary;
4import java.util.Hashtable;
5
6import org.eclipse.e4.core.services.events.IEventBroker;
7import org.osgi.service.event.Event;
8import org.osgi.service.event.EventAdmin;
9import org.osgi.service.event.EventConstants;
10
11import de.codecentric.eclipse.tutorial.service.inverter.InverterService;
12
13public class InverterServiceImpl implements InverterService {
14
15 EventAdmin eventAdmin;
16
17 @Override
18 public String invert(String value) {
19 String result = new StringBuilder(value).reverse().toString();
20
21 String topic = "TOPIC_LOGGING";
22 Dictionary<String, Object> data = new Hashtable<String, Object>(2);
23 data.put(EventConstants.EVENT_TOPIC, topic);
24 data.put(IEventBroker.DATA, "Inverted " + value + " to " + result);
25 Event event = new Event(topic, data);
26
27 eventAdmin.postEvent(event);
28
29 return result;
30 }
31
32 void registerEventAdmin(EventAdmin admin) {
33 this.eventAdmin = admin;
34 }
35
36 void unregisterEventAdmin(EventAdmin admin) {
37 this.eventAdmin = null;
38 }
39}
Step 5: Taste
- Start the application from within the IDE
- Open the Product Configuration in the de.codecentric.eclipse.tutorial.product project
- Select the Overview tab
- Click Launch an Eclipse Application in the Testing section
The started application should look similar to one of the following screenshots.
Further information:
- http://www.knopflerfish.org/osgi_service_tutorial.html
- http://www.vogella.com/tutorials/OSGiServices/article.html
- http://www.vogella.com/tutorials/Eclipse4EventSystem/article.html
- https://wiki.eclipse.org/Eclipse4/RCP/Event_Model
More articles
fromDirk Fauth
Your job at codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog author
Dirk Fauth
Do you still have questions? Just send me a message.
Do you still have questions? Just send me a message.