Bug 389863 - EventHandlers are not called on part after closing other part
Summary: EventHandlers are not called on part after closing other part
Status: VERIFIED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 4.2   Edit
Hardware: PC Windows 7
: P3 blocker (vote)
Target Milestone: 4.2.2   Edit
Assignee: Oleg Besedin CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on: 390346
Blocks: 392977
  Show dependency tree
 
Reported: 2012-09-19 03:26 EDT by Thomas M??der CLA
Modified: 2012-10-30 13:30 EDT (History)
6 users (show)

See Also:


Attachments
Example E4 project (20.08 KB, application/zip)
2012-09-19 03:27 EDT, Thomas M??der CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Thomas M??der CLA 2012-09-19 03:26:04 EDT
I have a simple example with 2 parts in a stack with both parts having an event listener method. 
When I start up and then close the first part, the second part does not receive events anymore.
Comment 1 Thomas M??der CLA 2012-09-19 03:27:53 EDT
Created attachment 221229 [details]
Example E4 project
Comment 2 Thomas M??der CLA 2012-09-19 03:33:24 EDT
To reproduce, import the attached example project, then:

1) Start the application (clear the workspace)
2) To see the event listeners in action, select "Send Event 1" from the file menu
3) Observe: you get the message "event 1 received" in the log
4) bring the view labeled "Part 2" to the front
3) exit
4) restart (do NOT clear the workspace)
5) observe: the app starts up with "Part 2" visible
6) click the close box on the part labeled "Part 2"
7) Select "Send Event 1" from the file menu
8) Observe: you get not console output
Comment 3 Thomas M??der CLA 2012-09-19 03:35:02 EDT
Setting importance to blocker, because we rely on listners being called; can't ship if this doesn't work. We don't have a workaround.
Comment 4 Paul Webster CLA 2012-09-19 08:49:04 EDT
You can't use @Inject on handlers as they are instantiated in a framework context that you might not have control over.  Instead:

@Execute
public void execute(EventAdmin eventAdmin) {...}

PW
Comment 5 Thomas M??der CLA 2012-09-19 09:01:01 EDT
To quote the eclipse wiki: (http://wiki.eclipse.org/Eclipse4/RCP/Event_Model)

@Inject @Optional
void closeHandler(@UIEventTopic(''TOPIC_STRING'') foo.Bar payload) {

And furthermore Lars Vogels tutorial here: http://www.vogella.com/articles/Eclipse4EventSystem/article.html#eventsystem_usagereceive

Also, there can only by one @Execute method on a class. Does that mean a part can only handle one event topic?

If I'm mistaken feel free to point me at the correct documentation ;-).
Comment 6 Paul Webster CLA 2012-09-19 09:14:30 EDT
(In reply to comment #5)
> To quote the eclipse wiki: (http://wiki.eclipse.org/Eclipse4/RCP/Event_Model)
> 
> @Inject @Optional
> void closeHandler(@UIEventTopic(''TOPIC_STRING'') foo.Bar payload) {

This is the code on your part, and is fine AFAIK.


> Also, there can only by one @Execute method on a class. Does that mean a
> part can only handle one event topic?

Only one execute method on a handler, in your case your handlers are firing the different events.  It's your handler that can't depend on @Inject EventAdmin eventAdmin, I mean, the handler must get eventadmin in the execute(*) method.

AFAIK your part can include multiple methods injected with UIEventTopics.

PW
Comment 7 Nobody - feel free to take it CLA 2012-09-19 09:18:52 EDT
When you inject an object as a field, the injection is performed on class creation (specifically right after the constructor returns). The fact is that injection is not "global", it is made in relation to a context. Object A injected in context 1 is not the same as Object A injected in context 2.

Handlers might run (the @Execute method) in different contexts. Thus if you inject it on-the-fly when the method runs (injecting event admin as a method parameter) you are sure to be making the injection in the right "running" context. If you inject in the field, the injection is made just once in the handler creation and it becomes "stale" meaning that the @Execute method runs in different contexts but the EventAdmin keeps staying the one from the initial creation context.

Here we are not talking about the events injection but about the EventAdmin (IEventBroker)injection.

I'm not sure if this fixes your issue though as I haven't had a chance to test it.
Comment 8 Thomas M??der CLA 2012-09-19 10:51:21 EDT
I don't believe that is a problem here: the real code this example is derived from consumes the EventAdmin OSGI service view declarative services, not E4 DI and the problem is exactly the same.
Comment 9 Paul Webster CLA 2012-09-19 11:59:54 EDT
(In reply to comment #8)
> I don't believe that is a problem here: the real code this example is
> derived from consumes the EventAdmin OSGI service view declarative services,
> not E4 DI and the problem is exactly the same.

So your real scenario doesn't involve the handlers, just the methods on the part?  ex:

@Inject @Optional
void closeHandler(@UIEventTopic(''TOPIC_STRING'') foo.Bar payload) { ... }

Oleg, it appears that closing that one part means the other part will no longer get UIEventTopic notifications.

PW
Comment 10 Thomas Schindl CLA 2012-09-26 03:31:40 EDT
What you see here is quite likely because of what I described in bug 390346, the problem is if you have:
* Part1: registering for topic a
* Part2: registering for topic b

created and activated to receive events. If Part1 is the first one to ever create the UIEventObjectSupplier (it is then bound to the IEclipseContext of Part1) and you close it the IEclipseContext goes away and the suppliers @PreDestroy removes *all* registrations.

IMHO this objectsupplier stuff is a major design problem but I fear we can't fix it for 4.2.2 (the only way I found was to a) fix the UIEventObjectSupplier to check if the requestor is still valid and b) always execute a System.gc() in front of the isValid check) because it needs new API.
Comment 11 Thomas M??der CLA 2012-09-26 03:36:23 EDT
Would it make sense, as a workaround, to (un-)register the listeners ourselves in a @PostConstruce/@Predestroy?
Comment 12 Thomas Schindl CLA 2012-09-26 04:41:26 EDT
well the work-around is:

a) Use the IEventBroker and subscribe to events manually, the eventbroker is 
created through a IContextFunction so you don't need to take care of 
unregistering because the IEventBroker is disposed when the context goes away - I'd go with this and delegate to internal methods and once we fixed the supplier switch back to it

b) 1. do not use @UIEventTopic but only @EventTopic (it has the 
      requestor.isValid() check) else you can't work around 390346
   2. create an addon and in there get access to the MApplication-IEclipseContext 
      and create an instance of a dummy bean which uses @EventTopic => this way 
      the supplier will be bound to the application context and you won't run in 
      the unregistering all handler
   3. run gc before sending events

=> I have an idea how to fix the @EventTopic & @UIEventTopic in 4.2.2 so maybe you can remove all the points from b) in 4.2.2 but I can't guarantee.
Comment 13 Thomas Schindl CLA 2012-09-26 05:59:45 EDT
I can't reproduce this when applying the proposed fix in bug 390346 - but I haven't tried without it but I'm sure the reason for not seeing the events is because of the description I gave in comment 12.

So if we solve bug 390346 like I propose this one will be fixed also. I'll mark this one as a child of.
Comment 14 Oleg Besedin CLA 2012-10-23 10:26:55 EDT
As Thomas described in the comment 10, the bug is in using an arbitrary context to inject extended suppliers. Solution: use workbench context for this purpose.

In more details:

When we create an internal piece to connect event bus with our DI mechanism, we use "a" context to provide it with some nice functionality, such as logging and synchronization. 

In this case it happened that we used context of Part1 to inject that internal linking piece. As Part1 gets destroyed, so does the linking piece.

Solution: 

I don't want to eliminate injection for extended suppliers, neither I am a fan of creating separate supplier for every injection. (Performance, anyone, please?) 

The logical and simple solution is to use a more specific context that is both known to be long-lived and contains data of interest to the application. The "workbench context" fits the bill (it is the context sometime referred to an "app context" that is a child of the top-level OSGi context).
Comment 15 Oleg Besedin CLA 2012-10-23 10:28:13 EDT
By the way, an even easier way to reproduce the problem:

- startup
- close Part1
- send event 2
Comment 17 Thomas Schindl CLA 2012-10-24 04:03:49 EDT
I can understand and accept this patch in 4.2 but for 4.3 we'd have to have another solution! The problem is with multi-instance frameworks like RAP - bringing context (workbench) lifecycle to OSGi-Services is simply wrong.

In a RAP scenario when you have 2 users and "user A" closes the workbench you have the same problem you see with the pre-patch situation in a single user env! The same is true for the UISynchronizer

On more instance, ... Please please take a look my patch there is not a single more instance created (they are cached!), the difference although is that the listener instances are created through DI so they are bound to the correct context and are cleaned when the context is disposed.
Comment 18 Oleg Besedin CLA 2012-10-30 13:30:56 EDT
Verified using I20121030-0800 and M20121024-1600.