Some weeks ago I published how views can communicate using the EventAdmin-Service. To get things working in an 3.x application one has to write some glue code but more importantly one has to know about all those nifty things about event publishing, getting access to OSGi-Services, … .php
One of the main topics of the Eclipse 4 Application Platform was to make easy things easy by removing the need to know about all the concepts by hiding them using DI. The service responsible to deliver events in application built on top the Eclipse 4 Application Platform is the EventBroker-Service:java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package
org.eclipse.e4.core.services.events;
public
interface
IEventBroker {
public
String DATA =
"org.eclipse.e4.data"
;
//$NON-NLS-1$
public
boolean
send(String topic, Object data);
public
boolean
post(String topic, Object data);
public
boolean
subscribe(String topic, EventHandler eventHandler);
public
boolean
subscribe(String topic, String filter, EventHandler eventHandler,
boolean
headless);
public
boolean
unsubscribe(EventHandler eventHandler);
}
|
This is all we need to know (assuming we have already understood how DI-works) to implement our sender and receiver views:app
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
|
public
class
SenderView {
@Inject
private
IEventBroker eventBroker;
private
Button b;
@PostConstruct
void
init(Composte parent) {
parent.setLayout(
new
GridLayout());
b =
new
Button(parent, SWT.PUSH);
b.setText(
"Send Event"
);
b.addSelectionListener(
new
SelectionAdapter() {
@Override
public
void
widgetSelected(SelectionEvent e) {
Date d =
new
Date();
eventBroker.send(
"viewcommunication/syncEvent"
,d);
eventBroker.post(
"viewcommunication/asyncEvent"
, d);
}
});
}
@Focus
void
focus() {
b.setFocus();
}
}
|
These are some fewer lines of code which is good but IMHO the more important fact is that you are independent from OSGi running now so the code you have there is much easier testable by simply mocking IEventBroker!less
Let’s look now at the receiver side of the story. If you take a look at the IEventBroker you see the subscribe methods which allows us to subscribe to various topics so we could as a first step implement it like this:eclipse
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
|
public
class
ReceiverView {
@Inject
private
IEventBroker eventBroker;
private
TableViewer viewer;
@PostConstruct
public
void
init(
final
Composite parent) {
parent.setLayout(
new
FillLayout());
viewer =
new
TableViewer(parent);
viewer.getTable().setHeaderVisible(
true
);
viewer.getTable().setLinesVisible(
true
);
viewer.setLabelProvider(
new
ColumnLabelProvider() {
@Override
public
String getText(Object element) {
return
DateFormat.getDateTimeInstance().format(element);
}
});
EventHandler handler =
new
EventHandler() {
public
void
handleEvent(
final
Event event) {
if
( parent.getDisplay().getThread() == Thread.currentThread() ) {
viewer.add(event.getProperty(IEventBroker.DATA));
}
else
{
parent.getDisplay().syncExec(
new
Runnable() {
public
void
run() {
viewer.add(event.getProperty(IEventBroker.DATA));
}
});
}
}
};
eventBroker.subscribe(
"viewcommunication/*"
,handler);
}
@Focus
void
focus() {
viewer.getTable().setFocus();
}
}
|
This is once more making your code looser coupled because you are independent from OSGi running but we can do even better by fully leveraging the Eclipse DI-Framework. By default Eclipse DI injects data it has stored in its IEclipseContext (you can think of the IEclipseContext as a Map where the value is thing that gets injected into you 「POJO」). async
The important thing for us is that one can extend Eclipse DI to consult other resources like e.g. Preferences (@Preference) and e.g. Event-Data (@EventTopic) – a blog post explaining how this works will follow hopefully soon.ide
So we can rewrite our receiver code like this:oop
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
|
public
class
ReceiverView {
private
TableViewer viewer;
@PostConstruct
public
void
init(
final
Composite parent) {
parent.setLayout(
new
FillLayout());
viewer =
new
TableViewer(parent);
viewer.getTable().setHeaderVisible(
true
);
viewer.getTable().setLinesVisible(
true
);
viewer.setLabelProvider(
new
ColumnLabelProvider() {
@Override
public
String getText(Object element) {
return
DateFormat.getDateTimeInstance().format(element);
}
});
}
@Inject
void
eventReceived(
@EventTopic
(
"viewcommunication/*"
) Date date) {
Display d = viewer.getControl().getDisplay();
if
( d.getThread() == Thread.currentThread() ) {
viewer.add(date);
}
else
{
parent.getDisplay().syncExec(
new
Runnable() {
public
void
run() {
viewer.add(date);
}
});
}
}
@Focus
void
focus() {
viewer.getTable().setFocus();
}
}
|
[Update 2011-02-07] – Start
But we can do even better so that we don’t need to take of the UI-Thread syncing all we need to do is use another annotation:post
1
2
3
4
|
@Inject
void
eventReceived(
@UIEventTopic
(
"viewcommunication/*"
) Date date) {
viewer.add(date);
}
|
Now the DI-Framework takes care of the Event loop syncing. Thanks to Brian de Alwis for pointing this out.
[Update 2011-02-07] – Endui
If you are interested in how you can use things like this in your Eclipse 3.x applications and you are going to be at EclipseCon you should come to my talk about 「Singlesourcing for Eclipse 4.x and Eclipse 3.x」