Community
Participate
Working Groups
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.
Created attachment 221229 [details] Example E4 project
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
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.
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
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 ;-).
(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
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.
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.
(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
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.
Would it make sense, as a workaround, to (un-)register the listeners ourselves in a @PostConstruce/@Predestroy?
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.
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.
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).
By the way, an even easier way to reproduce the problem: - startup - close Part1 - send event 2
Runtime part of the fix: http://git.eclipse.org/c/platform/eclipse.platform.runtime.git/commit/?h=R4_2_maintenance&id=4fb085bbddd9e72a75662c3daef38a9836da871d UI part of the fix: http://git.eclipse.org/c/platform/eclipse.platform.ui.git/commit/?h=R4_2_maintenance&id=80e45e434c97a1cb41957270603b7f79f479ac8c
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.
Verified using I20121030-0800 and M20121024-1600.