Uploaded image for project: 'CDI Specification Issues'
  1. CDI Specification Issues
  2. CDI-4

Need a way to provide ordering for Event observers

      There needs to be a way to specify some kind of ordering for Event observers.

      Understandably, this is somewhat counter-intuitive to the general concept of observing an event, but there is going to be need for this in an upcoming JBoss project. While it can be done manually, it might be nice to have a built-in API.

            [CDI-4] Need a way to provide ordering for Event observers

            Jozef Hartinger added a comment - Proposed changes to @Priority and ObserverMethod SPI: https://github.com/weld/api/blob/3.0.Alpha1/weld/src/main/java/org/jboss/weld/experimental/ExperimentalObserverMethod.java https://github.com/weld/api/blob/3.0.Alpha1/weld/src/main/java/org/jboss/weld/experimental/Priority.java

            Martin Kouba added a comment - - edited

            Actually, javax.annotation.Priority is not a part of the Java SE (only EE 7+), so it shouldn't be a big problem.

            Martin Kouba added a comment - - edited Actually, javax.annotation.Priority is not a part of the Java SE (only EE 7+), so it shouldn't be a big problem.

            Yes, that reminds me of javax.annotation.Resource. It got updated in Java EE 6 (it introduced the lookup() method) and we had to endorse the library for Java SE 6. So yes, there is a bit of tuning when you touch common code like Commons Annotation.

            Antonio Goncalves (Inactive) added a comment - Yes, that reminds me of javax.annotation.Resource . It got updated in Java EE 6 (it introduced the lookup() method) and we had to endorse the library for Java SE 6. So yes, there is a bit of tuning when you touch common code like Commons Annotation .

            agoncal Hm, so it woudn't work for Java 8 SE without some dependency tuning, right? I'm just pointing out there is a little complication...

            Martin Kouba added a comment - agoncal Hm, so it woudn't work for Java 8 SE without some dependency tuning, right? I'm just pointing out there is a little complication...

            martinkouba_jira There was a maintenance release during Java EE 7 for the JSR 250, it can be updated again on Java EE 8 if needed.

            Antonio Goncalves (Inactive) added a comment - martinkouba_jira There was a maintenance release during Java EE 7 for the JSR 250 , it can be updated again on Java EE 8 if needed.

            agoncal javax.annotation.Priority has @Target(value=TYPE) so we can't use it for observer methods

            Martin Kouba added a comment - agoncal javax.annotation.Priority has @Target(value=TYPE) so we can't use it for observer methods

            jporter@redhat.com] same unspecified behaviour as we have today for ALL observers

            Mark Struberg (Inactive) added a comment - jporter@redhat.com ] same unspecified behaviour as we have today for ALL observers

            If we have something at the same priority level, is it just unspecified behavior?

            Jason Porter added a comment - If we have something at the same priority level, is it just unspecified behavior?

            And now that we have @Priority, can't we just use it ?

            public class MyBean
            	public void myFirstObserverInPriority(@Observes @Priority(1000) MyEvent event) { ... }
            }
            
            public class MyBean
            	public void mySecondObserverInPriority(@Observes @Priority(42) MyEvent event) { ... }
            }
            

            Antonio Goncalves (Inactive) added a comment - And now that we have @Priority , can't we just use it ? public class MyBean public void myFirstObserverInPriority(@Observes @Priority(1000) MyEvent event) { ... } } public class MyBean public void mySecondObserverInPriority(@Observes @Priority(42) MyEvent event) { ... } }

            Fix issue title to just be about event observers. Defer

            Pete Muir (Inactive) added a comment - Fix issue title to just be about event observers. Defer

            I'm marking this as a dupe of CDI-18 as we need to consider both issues in parallel.

            Pete Muir (Inactive) added a comment - I'm marking this as a dupe of CDI-18 as we need to consider both issues in parallel.

            If we allow a reordering in a portable extension, Linda suggested that the order from XML should be used as a default.

            Pete Muir (Inactive) added a comment - If we allow a reordering in a portable extension, Linda suggested that the order from XML should be used as a default.

            I'm merging CDI-48 into this issue, whatever solution we arrive at must be the same across all scenarios.

            Furthermore, it's important to offer both SPI support for this, so alternative approaches to ordering can be created by the community, as well as API support, as this is a core concern.

            Pete Muir (Inactive) added a comment - I'm merging CDI-48 into this issue, whatever solution we arrive at must be the same across all scenarios. Furthermore, it's important to offer both SPI support for this, so alternative approaches to ordering can be created by the community, as well as API support, as this is a core concern.

            Will think about this on the trip back tomorrow and comment on all this.

            Mike Brock (Inactive) added a comment - Will think about this on the trip back tomorrow and comment on all this.

            I have a comment about the syntax.I quite like the idea of using @Before and @After coupled with qualifiers for ordering event handlers and IMO partial ordering can go a pretty long way in solving the issues that we have.

            But, rather than using @Before and @After with the event qualifiers, I think that qualifiers should be placed on event handlers themselves.

            So, this may work:

            public void myFirstObserver(@Observes @After(Initialization.class) MyEvent event) {
            ...
            }
            
            public void mySecondObserver(@Observes @Initialization MyEvent event) {
            ...
            } 
            

            but what if I want to order two observers for @Initialization MyEvent ? I can do:

            public void myFirstObserver(@Observes @Initialization @After(Initialization.class) MyEvent event) {
            ...
            }
            
            public void mySecondObserver(@Observes @Initialization MyEvent event) {
            ...
            } 
            

            but that does not do a great favour to readability. On the other hand, what do we do with observers that do not have any qualifier at all? Mike suggested an @OrderingQualifier, but mixing it up with the rest of the qualifiers does not improve readability. On the other hand, ordering is relative to the method not to the event being observed, so why not qualify observer methods themselves (note how I stole the idea of placing the @Before annotation from Christian)?

            @Initialization
            public void myFirstObserver(@Observes MyEvent event) {
            ...
            }
            
            @Before(Initialization.class)
            public void mySecondObserver(@Observes  MyEvent event) {
            ...
            } 
            

            @Initialization can be a special type of annotation, or can be a simple @Qualifer. I also think that the idea can be reused in scenarios such as interceptor/decorator ordering, if we ever consider automatic interceptor/decorator enablement.

            Marius Bogoevici (Inactive) added a comment - I have a comment about the syntax.I quite like the idea of using @Before and @After coupled with qualifiers for ordering event handlers and IMO partial ordering can go a pretty long way in solving the issues that we have. But, rather than using @Before and @After with the event qualifiers, I think that qualifiers should be placed on event handlers themselves. So, this may work: public void myFirstObserver(@Observes @After(Initialization.class) MyEvent event) { ... } public void mySecondObserver(@Observes @Initialization MyEvent event) { ... } but what if I want to order two observers for @Initialization MyEvent ? I can do: public void myFirstObserver(@Observes @Initialization @After(Initialization.class) MyEvent event) { ... } public void mySecondObserver(@Observes @Initialization MyEvent event) { ... } but that does not do a great favour to readability. On the other hand, what do we do with observers that do not have any qualifier at all? Mike suggested an @OrderingQualifier, but mixing it up with the rest of the qualifiers does not improve readability. On the other hand, ordering is relative to the method not to the event being observed, so why not qualify observer methods themselves (note how I stole the idea of placing the @Before annotation from Christian)? @Initialization public void myFirstObserver(@Observes MyEvent event) { ... } @Before(Initialization.class) public void mySecondObserver(@Observes MyEvent event) { ... } @Initialization can be a special type of annotation, or can be a simple @Qualifer. I also think that the idea can be reused in scenarios such as interceptor/decorator ordering, if we ever consider automatic interceptor/decorator enablement.

            If we add this API, I'd also like to see an SPI that can be used in tandem, so that Event firing may additionally be controlled using custom annotations and an Event firing controller.

            For instance:

            @EventBinding
            public annotation Controlled { ... values ...}
            @Controlled(Type.DELTA)
            public class EventInstance {...}

            Where the annotation is bound to the controller just like we do for Interceptors and Decorators (which also need an ordering solution if I am not mistaken.)

            public class Bean {
            
               @Inject BeanManager manager;
               public void fireEvent()
               {
                  manager.fire(new EventInstance());
               }
            
            }

            The controller is then passed control of the event firing:

            @EventBinding
            @Controlled
            public class CustomController implements EventController
            {
               @Override
               public void performEvent(EventInstance<?> event, Controlled anno, Set<ObserverInstance> observers) {
                  // this method can sort through the list of observers and fire the event for each observer in order as it sees fit.
                  for( ObserverInstance o : observers )
                  {
                     if(Type.DELTA.equals(anno.type()))
                        o.fire(event.getEvent())
                     else
                        o.fire(new EventInstance("foo"));
                  }
               }
            }
            

            This would effectively enable custom ordering and fire-control, without additional coupling or implementation specific details. However, I don't believe that @Before and @After could be implemented using this SPI; this would be an additional feature that could take advantage of the @Before or @After hints if desired.

            Lincoln Baxter III (Inactive) added a comment - - edited If we add this API, I'd also like to see an SPI that can be used in tandem, so that Event firing may additionally be controlled using custom annotations and an Event firing controller. For instance: @EventBinding public annotation Controlled { ... values ...} @Controlled(Type.DELTA) public class EventInstance {...} Where the annotation is bound to the controller just like we do for Interceptors and Decorators (which also need an ordering solution if I am not mistaken.) public class Bean { @Inject BeanManager manager; public void fireEvent() { manager.fire( new EventInstance()); } } The controller is then passed control of the event firing: @EventBinding @Controlled public class CustomController implements EventController { @Override public void performEvent(EventInstance<?> event, Controlled anno, Set<ObserverInstance> observers) { // this method can sort through the list of observers and fire the event for each observer in order as it sees fit. for ( ObserverInstance o : observers ) { if (Type.DELTA.equals(anno.type())) o.fire(event.getEvent()) else o.fire( new EventInstance( "foo" )); } } } This would effectively enable custom ordering and fire-control, without additional coupling or implementation specific details. However, I don't believe that @Before and @After could be implemented using this SPI; this would be an additional feature that could take advantage of the @Before or @After hints if desired.

            Mike Brock (Inactive) added a comment - - edited

            Mike, can you impl your solution on top of Jason's proposal?

            Most likely. However, it's probably not something we could target in our own client CDI stuff. So if the @Before @After stuff was not standardized, we'd be forced to couple our stuff to a specific extension. That's the only downside for us.

            It seems to me that, while not necessarily a problem for the majority, we should strongly consider if this is a minority we should ignore. My opinion is that, while at any given time this is a minority problem, the majority will probably run into it at some point or another.

            My personal preference would be for both an SPI and an standard for ordering in the programming model.

            Mike Brock (Inactive) added a comment - - edited Mike, can you impl your solution on top of Jason's proposal? Most likely. However, it's probably not something we could target in our own client CDI stuff. So if the @Before @After stuff was not standardized, we'd be forced to couple our stuff to a specific extension. That's the only downside for us. It seems to me that, while not necessarily a problem for the majority, we should strongly consider if this is a minority we should ignore. My opinion is that, while at any given time this is a minority problem, the majority will probably run into it at some point or another. My personal preference would be for both an SPI and an standard for ordering in the programming model.

            This problem is very acute for a few people, but doesn't affect a majority.

            Jason's solution may well be the sweetspot as it allows custom solutions to be implemented. Mike, can you impl your solution on top of Jason's proposal?

            Pete Muir (Inactive) added a comment - This problem is very acute for a few people, but doesn't affect a majority. Jason's solution may well be the sweetspot as it allows custom solutions to be implemented. Mike, can you impl your solution on top of Jason's proposal?

            "Let's go back to the Portland Pattern Repository or the GoF Design Patterns book and check the Observer/Observable pattern. The whole idea/mechanism is built based on the paradigma that all the involved parties know NOTHNING about each other.

            They don't. That's why I'm relying purely on qualifiers for ordering."

            A magic number based solution introduces tight coupling between observers and is non-type-safe. The approach proposed by Mike does introduce an (optional) level of coupling, but not as strong as magic numbers and is type safe. To interleave an observer in a magic number scheme I must know the magic number of all other observers I must come before. To interleave in Mike's scheme I simply need to know the qualifer they are using (which is itself reasonably decoupled from the observer method).

            In general this notion that a non-strongly-typed link between objects is gives looser coupling inherently is extremely flawed.

            Pete Muir (Inactive) added a comment - "Let's go back to the Portland Pattern Repository or the GoF Design Patterns book and check the Observer/Observable pattern. The whole idea/mechanism is built based on the paradigma that all the involved parties know NOTHNING about each other. They don't. That's why I'm relying purely on qualifiers for ordering." A magic number based solution introduces tight coupling between observers and is non-type-safe. The approach proposed by Mike does introduce an (optional) level of coupling, but not as strong as magic numbers and is type safe. To interleave an observer in a magic number scheme I must know the magic number of all other observers I must come before. To interleave in Mike's scheme I simply need to know the qualifer they are using (which is itself reasonably decoupled from the observer method). In general this notion that a non-strongly-typed link between objects is gives looser coupling inherently is extremely flawed.

            In Arquillian Core we had the same issue.

            Previously multiple Observers were observing the BeforeClass, and the ordered mattered:

            • ContextCreator (sets up the context so the TestClass can be found)
              • Listen to BeforeClass
            • DeploymentGenerator (use the TestClass to generate the Archive)
              • Listen to BeforeClass
            • ArchiveExporter (Export the Archive)
              • Listen to BeforeClass
            • Deployer (Deploy the Archive)
              • Listen to BeforeClass

            From my perspective, if Order matters, they have a coupling so why not show it directly.

            Two features were added to help with this:

            • Event interceptor
              If you need to setup/process a event indirectly, e.g. setup some evn, then you can Intercept the event in a AroundInvoke style interceptor. You can then Observe EventContext<BeforeClass>, setup your 'stuff' and continue the chain with event.proceed() to invoke the next Interceptors or Observers.
            • Nested Events
              Instead of all coupled Observers, based on order, being registered on the same Event, use 'sub events' to limit the scope and show the coupling. DeploymentGenerator fires a DeploymentGenerated event, Deployer fires BeforeDeploy and AfterDeploy events etc.

            The new observer chain then becomes:

            • ContextCreator
              • Listen to EventContext<BeforeClass> - Around Invoke interceptor of the whole Event
            • DeploymentGenerator
              • Listen to BeforeClass -> fire DeploymentGenerated
            • Deployer
              • Listen to DeploymentGenerated -> fire BeforeDeploy & AfterDeploy
            • ArchiveExporter
              • Listen to BeforeDeploy

            This has the added bonus that 'others' can reuse some of the chains logic by firing more concrete events, e.g. Trigger only a deployment by firing a DeploymentGenerated event instead of having the other side effects of the more coarse grained BeforeClass event.

            Just wanted to throw the idea of Intercepting a Event and not the Observer out there, just my 2 cents.

            Aslak Knutsen added a comment - In Arquillian Core we had the same issue. Previously multiple Observers were observing the BeforeClass, and the ordered mattered: ContextCreator (sets up the context so the TestClass can be found) Listen to BeforeClass DeploymentGenerator (use the TestClass to generate the Archive) Listen to BeforeClass ArchiveExporter (Export the Archive) Listen to BeforeClass Deployer (Deploy the Archive) Listen to BeforeClass From my perspective, if Order matters, they have a coupling so why not show it directly. Two features were added to help with this: Event interceptor If you need to setup/process a event indirectly, e.g. setup some evn, then you can Intercept the event in a AroundInvoke style interceptor. You can then Observe EventContext<BeforeClass>, setup your 'stuff' and continue the chain with event.proceed() to invoke the next Interceptors or Observers. Nested Events Instead of all coupled Observers, based on order, being registered on the same Event, use 'sub events' to limit the scope and show the coupling. DeploymentGenerator fires a DeploymentGenerated event, Deployer fires BeforeDeploy and AfterDeploy events etc. The new observer chain then becomes: ContextCreator Listen to EventContext<BeforeClass> - Around Invoke interceptor of the whole Event DeploymentGenerator Listen to BeforeClass -> fire DeploymentGenerated Deployer Listen to DeploymentGenerated -> fire BeforeDeploy & AfterDeploy ArchiveExporter Listen to BeforeDeploy This has the added bonus that 'others' can reuse some of the chains logic by firing more concrete events, e.g. Trigger only a deployment by firing a DeploymentGenerated event instead of having the other side effects of the more coarse grained BeforeClass event. Just wanted to throw the idea of Intercepting a Event and not the Observer out there, just my 2 cents.

            Jason Porter added a comment - - edited

            Simply having say, a framework like Forge implement comparators, doesn't help readability of the model at all. In fact, it sort of suffers from the same problem that putting the configuration in an XML file suffers from: you can't get any sense of the ordering just by looking at any given observer.

            Yeah, that is one of the side effects of this idea.

            Seems two issues we're be dealing with are

            1. adding to the signature of the observer, possibly making it harder to read with other annotations
            2. externalizing ordering and not having any idea about ordering simply by looking at an observer.

            With a comparator, you could do what you wanted with the extra annotations, they simply wouldn't be qualifying annotations, you could do the ordering in the comparator based on those. Another option I just thought of (which is similar to the comparator idea) is to make handling observers pluggable. Call it an EventingBus or something, it would be another SPI that people could tie into and have (full ?) control over how eventing happens, ordering, injection points, etc. This idea would allow us to do what we wanted in Seam JMS (not sure if this has come up in CODI) where we need the metadata of the event that was fired. It would be more work for the end user should they decide to take it, but it really gives them control over the application, and the environment should they need it.

            Sorry for the first post which wasn't complete, Save and preview were a little too close

            Jason Porter added a comment - - edited Simply having say, a framework like Forge implement comparators, doesn't help readability of the model at all. In fact, it sort of suffers from the same problem that putting the configuration in an XML file suffers from: you can't get any sense of the ordering just by looking at any given observer. Yeah, that is one of the side effects of this idea. Seems two issues we're be dealing with are adding to the signature of the observer, possibly making it harder to read with other annotations externalizing ordering and not having any idea about ordering simply by looking at an observer. With a comparator, you could do what you wanted with the extra annotations, they simply wouldn't be qualifying annotations, you could do the ordering in the comparator based on those. Another option I just thought of (which is similar to the comparator idea) is to make handling observers pluggable. Call it an EventingBus or something, it would be another SPI that people could tie into and have (full ?) control over how eventing happens, ordering, injection points, etc. This idea would allow us to do what we wanted in Seam JMS (not sure if this has come up in CODI) where we need the metadata of the event that was fired. It would be more work for the end user should they decide to take it, but it really gives them control over the application, and the environment should they need it. Sorry for the first post which wasn't complete, Save and preview were a little too close

            Let's go back to the Portland Pattern Repository or the GoF Design Patterns book and check the Observer/Observable pattern. The whole idea/mechanism is built based on the paradigma that all the involved parties know NOTHNING about each other.

            They don't. That's why I'm relying purely on qualifiers for ordering.

            Mike Brock (Inactive) added a comment - Let's go back to the Portland Pattern Repository or the GoF Design Patterns book and check the Observer/Observable pattern. The whole idea/mechanism is built based on the paradigma that all the involved parties know NOTHNING about each other. They don't. That's why I'm relying purely on qualifiers for ordering.

            Mark Struberg (Inactive) added a comment - - edited

            > I'm struggling to see how my proposal introduces coupling.
            If I understood correctly, then your proposal relies that you have to know the other/all the other observer(s) in your 'before' Observers. If you don't call this coupling, well...
            edit: ah I see now, your @After and @Before references a qualifier and not the other obserer class. Well then there might be no direct coupling, but this most probably wont work with the currently defined qualifier pattern (depending how we fix CDI-7).

            > If I add a jar in the future which
            and what if you add another jar with another bean having another @Observes BusEvent? How to make sure your beforeInit also runs before this new observer?

            Let's go back to the Portland Pattern Repository or the GoF Design Patterns book and check the Observer/Observable pattern. The whole idea/mechanism is built based on the paradigma that all the involved parties know NOTHNING about each other.

            Mark Struberg (Inactive) added a comment - - edited > I'm struggling to see how my proposal introduces coupling. If I understood correctly, then your proposal relies that you have to know the other/all the other observer(s) in your 'before' Observers. If you don't call this coupling, well... edit : ah I see now, your @After and @Before references a qualifier and not the other obserer class. Well then there might be no direct coupling, but this most probably wont work with the currently defined qualifier pattern (depending how we fix CDI-7 ). > If I add a jar in the future which and what if you add another jar with another bean having another @Observes BusEvent? How to make sure your beforeInit also runs before this new observer? Let's go back to the Portland Pattern Repository or the GoF Design Patterns book and check the Observer/Observable pattern. The whole idea/mechanism is built based on the paradigma that all the involved parties know NOTHNING about each other.

            Mike Brock (Inactive) added a comment - - edited

            A few points of contention here:

            "the @Before doesn't scale out in practice. You cannot always change your event if someone adds a jar into the classpath which needs that event"

            Can you give a solid example of this? I mean, one the reasons why I am proposing this, is that the sorting can deal with the addition of new beans which have ordered observers.

            The solution that you're proposing – which is to use a sort of "marathon pattern" – seems to suffer greatly from a coupling problem, because you're relying there being a discretely defined event type for each step. That, in itself, almost entirely precludes the adding of new beans to the container that change the ordering – at all.

            I'm struggling to see how my proposal introduces coupling.

            Having both @Before and @After is, for the very reason of dealing with potentially unseen model, very necessary.

            Say I have an event BusEvent. And it has qualifiers like @Initialize and @PostInitialize.

            If I add a jar in the future which contains a bean which has say, something like this:

            public void beforeInit(@Observes @Before(Initialize.class) BusEvent event) { 
               ...
            }
            

            ... I would get exactly the kind of behavior I would expect. So I fail to see the problem you're referring to. There'd be no guarantee of ordering for all the @Before(Initialize.class) events, but I somehow doubt that would be any trouble in such a use case.

            "The service could be something like javax.enterprise.inject.spi.ObserverComparator, applied after the ProcessObserverMethod event."

            I think this is actually a really good idea. But it's really orthogonal to the problem I'm trying to solve. For the very example I point out above.

            It seems to me that event ordering is not something which violates decoupling of events, and is a concept which is purely a component-container contract issue.

            Simply having say, a framework like Forge implement comparators, doesn't help readability of the model at all. In fact, it sort of suffers from the same problem that putting the configuration in an XML file suffers from: you can't get any sense of the ordering just by looking at any given observer.

            Mike Brock (Inactive) added a comment - - edited A few points of contention here: "the @Before doesn't scale out in practice. You cannot always change your event if someone adds a jar into the classpath which needs that event" Can you give a solid example of this? I mean, one the reasons why I am proposing this, is that the sorting can deal with the addition of new beans which have ordered observers. The solution that you're proposing – which is to use a sort of "marathon pattern" – seems to suffer greatly from a coupling problem, because you're relying there being a discretely defined event type for each step. That, in itself, almost entirely precludes the adding of new beans to the container that change the ordering – at all. I'm struggling to see how my proposal introduces coupling. Having both @Before and @After is, for the very reason of dealing with potentially unseen model, very necessary. Say I have an event BusEvent . And it has qualifiers like @Initialize and @PostInitialize . If I add a jar in the future which contains a bean which has say, something like this: public void beforeInit(@Observes @Before(Initialize.class) BusEvent event) { ... } ... I would get exactly the kind of behavior I would expect. So I fail to see the problem you're referring to. There'd be no guarantee of ordering for all the @Before(Initialize.class) events, but I somehow doubt that would be any trouble in such a use case. — "The service could be something like javax.enterprise.inject.spi.ObserverComparator, applied after the ProcessObserverMethod event." I think this is actually a really good idea. But it's really orthogonal to the problem I'm trying to solve. For the very example I point out above. It seems to me that event ordering is not something which violates decoupling of events, and is a concept which is purely a component-container contract issue. Simply having say, a framework like Forge implement comparators, doesn't help readability of the model at all. In fact, it sort of suffers from the same problem that putting the configuration in an XML file suffers from: you can't get any sense of the ordering just by looking at any given observer.

            I mentioned InjectionPoint because ObserverMethod doesn't give you metadata of the containing bean, but we could add that if feel we need it. Otherwise ObserverMethod has the payload type, qualifiers, transaction phase, reception, etc. Seems like everything one would need to make a decision about ordering observers.

            I know the spec doesn't call this out explicitly, it's more of an implementation detail, but I'm pretty sure each implementation is going to store the ObeserverMethods in some sort of Iterable (it's actually somewhat implied via the JavaDocs for BeanManager it will be a Set). Adding additional metadata or global explicit ordering seems like overkill when we have easy solutions in the JDK, we just need to expose it.

            The service could be something like javax.enterprise.inject.spi.ObserverComparator, applied after the ProcessObserverMethod event. We could actually apply this idea globally to all areas we need ordering (little late for Interceptors though, unless we define a comparator that orders via the XML as a default).

            Jason Porter added a comment - I mentioned InjectionPoint because ObserverMethod doesn't give you metadata of the containing bean, but we could add that if feel we need it. Otherwise ObserverMethod has the payload type, qualifiers, transaction phase, reception, etc. Seems like everything one would need to make a decision about ordering observers. I know the spec doesn't call this out explicitly, it's more of an implementation detail, but I'm pretty sure each implementation is going to store the ObeserverMethods in some sort of Iterable (it's actually somewhat implied via the JavaDocs for BeanManager it will be a Set). Adding additional metadata or global explicit ordering seems like overkill when we have easy solutions in the JDK, we just need to expose it. The service could be something like javax.enterprise.inject.spi.ObserverComparator, applied after the ProcessObserverMethod event. We could actually apply this idea globally to all areas we need ordering (little late for Interceptors though, unless we define a comparator that orders via the XML as a default).

            Interesting idea, tell us more.

            Anything which is 'outside' the observers is a welcome bonus. If we create a model where the observers need to know about other observers, then 90% of the benefits of the observer/observable pattern is lost imo.

            Mark Struberg (Inactive) added a comment - Interesting idea, tell us more. Anything which is 'outside' the observers is a welcome bonus. If we create a model where the observers need to know about other observers, then 90% of the benefits of the observer/observable pattern is lost imo.

            Maybe I'm coming at this from an odd angle, but why not allow them to register, via the same service provider API, a Comparator<InjectionPoint> or Comparator<ObserverMethod>? We could leave everything the way it is or create a default one that just returns 0. This allows the users complete control over where each observer falls in the set of observers, they don't need to add more XML, it's a very simply, widely used pattern for ordering. Not sure why we're trying to reinvent an ordering solution when one has already been provided by the language.

            Jason Porter added a comment - Maybe I'm coming at this from an odd angle, but why not allow them to register, via the same service provider API, a Comparator<InjectionPoint> or Comparator<ObserverMethod>? We could leave everything the way it is or create a default one that just returns 0. This allows the users complete control over where each observer falls in the set of observers, they don't need to add more XML, it's a very simply, widely used pattern for ordering. Not sure why we're trying to reinvent an ordering solution when one has already been provided by the language.

            Mark Struberg (Inactive) added a comment - - edited

            huh, just realized that I could do the 2nd code block even more elegantly:

            public void checkSecurity(@Observes MyEvent me, @Secured Event<MyEvent> securedEventSource) {
              checkSecurity();
              securedEventSource.fire(me);
            }
            

            The trick here is that any observer method can have additional injection points as parameters which will get resolved by the container automatically ...

            Mark Struberg (Inactive) added a comment - - edited huh, just realized that I could do the 2nd code block even more elegantly: public void checkSecurity(@Observes MyEvent me, @Secured Event<MyEvent> securedEventSource) { checkSecurity(); securedEventSource.fire(me); } The trick here is that any observer method can have additional injection points as parameters which will get resolved by the container automatically ...

            John, think you mean the discussion over in CDI-7, right?

            Btw, good discussion right now. Think we are moving forward.

            I understand that magic numbers is not an optimal idea neither. So lets step back a bit and look at the problem from the distance.

            • the @Before doesn't scale out in practice. You cannot always change your event if someone adds a jar into the classpath which needs that event
            • the @After is not necessary. The problems you explained can also easily get solved with the current spec. See my example below

            The original code just throws the original Event:

            BeanManager.fire(new MyEvent());
            

            The 'before' observes this event, does some security checks and rethrows it with a qualifier @Secured

            public void checkSecurity(@Observes MyEvent me) {
              checkSecurity();
              bm.fire(me, SecuredAnnotationLiteral);
            }
            

            The 'after' then just observes the @Secured version of the event

            public void doThaRealThing(@Observes @Secured MyEvent me) {
              doTheActualWork();
            }
            

            I'm coming always closer to the firm conviction that we should pull out the observer ordering into an external config, as we did with interceptor ordering.

            Mark Struberg (Inactive) added a comment - John, think you mean the discussion over in CDI-7 , right? Btw, good discussion right now. Think we are moving forward. I understand that magic numbers is not an optimal idea neither. So lets step back a bit and look at the problem from the distance. the @Before doesn't scale out in practice. You cannot always change your event if someone adds a jar into the classpath which needs that event the @After is not necessary. The problems you explained can also easily get solved with the current spec. See my example below The original code just throws the original Event: BeanManager.fire( new MyEvent()); The 'before' observes this event, does some security checks and rethrows it with a qualifier @Secured public void checkSecurity(@Observes MyEvent me) { checkSecurity(); bm.fire(me, SecuredAnnotationLiteral); } The 'after' then just observes the @Secured version of the event public void doThaRealThing(@Observes @Secured MyEvent me) { doTheActualWork(); } I'm coming always closer to the firm conviction that we should pull out the observer ordering into an external config, as we did with interceptor ordering.

            One of the things Pete and I spoke about was observer resolution with multiple qualifiers. I believe the 1.0 spec has an issue with terminology (another inconsistency in it). I would recommend against a solution to this that relied on the idea of multiple qualifiers resolving to similar observers.

            John Ament (Inactive) added a comment - One of the things Pete and I spoke about was observer resolution with multiple qualifiers. I believe the 1.0 spec has an issue with terminology (another inconsistency in it). I would recommend against a solution to this that relied on the idea of multiple qualifiers resolving to similar observers.

            I'd prefer the more expressive approach using @Before and @After over the simpler approach using salience/priority values.

            However, I'd like to suggest having the ordering specified on the observer methods rather than on the event parameters. That is to have a separation of concerns between event qualifiers and observer ordering. I think this would further increase readability:

            public class MyBean
            
            	public void myFirstObserver(@Observes @A @B MyEvent event) {
            	   ...
            	}
            
            	@After({A.class, B.class})
            	public void mySecondObserver(@Observes @A @B @C MyEvent event) {
            	   ...
            	} 
            }
            

            Christian Sadilek (Inactive) added a comment - I'd prefer the more expressive approach using @Before and @After over the simpler approach using salience/priority values. However, I'd like to suggest having the ordering specified on the observer methods rather than on the event parameters. That is to have a separation of concerns between event qualifiers and observer ordering. I think this would further increase readability: public class MyBean public void myFirstObserver(@Observes @A @B MyEvent event) { ... } @After({A.class, B.class}) public void mySecondObserver(@Observes @A @B @C MyEvent event) { ... } }

            Mike Brock (Inactive) added a comment - - edited

            I do, of course. And I've stewed about this problem for quite a long time. Ever since we needed the ability to control event ordering in Forge and now in some cases, in Errai.

            The problem is that when you're trying to keep it simple you can't discount the possibility that you're merely exporting the complexity of a problem. And I think that while this does add complexity to CDI, it does so in a way that balances the complexity with programmatic intuitiveness, maintains a modicum of readability, leverages qualifiers, and provides frameworks like say, Forge or Errai, with the ability to offer expressive – and more importantly, documentable – ways of controlling event-firing across decoupled components.

            The truth is, and I know Dan Allen can attest to this, that my initial suggestion was to use magic numbers for salience, as well. And while it's an undeniably simple thing to implement, it will more than likely just produce convoluted code.

            I mean, to further my example from earlier – and not to put to fine a point on it – but I would cringe to write something like this in my documentation:

              In order to ensure that your changes are picked up by the persistence plugin, you must ensure that the CodeChange event observer has a salience of less than 0.7f.
            

            Mike Brock (Inactive) added a comment - - edited I do, of course. And I've stewed about this problem for quite a long time. Ever since we needed the ability to control event ordering in Forge and now in some cases, in Errai. The problem is that when you're trying to keep it simple you can't discount the possibility that you're merely exporting the complexity of a problem. And I think that while this does add complexity to CDI, it does so in a way that balances the complexity with programmatic intuitiveness, maintains a modicum of readability, leverages qualifiers, and provides frameworks like say, Forge or Errai, with the ability to offer expressive – and more importantly, documentable – ways of controlling event-firing across decoupled components. The truth is, and I know Dan Allen can attest to this, that my initial suggestion was to use magic numbers for salience, as well. And while it's an undeniably simple thing to implement, it will more than likely just produce convoluted code. I mean, to further my example from earlier – and not to put to fine a point on it – but I would cringe to write something like this in my documentation: In order to ensure that your changes are picked up by the persistence plugin, you must ensure that the CodeChange event observer has a salience of less than 0.7f.

            you know the KISS pattern? This would be a pretty heavy change for 1.1 imo. From my gut feeling I'd say we should think a few days/weeks about this.

            Mark Struberg (Inactive) added a comment - you know the KISS pattern? This would be a pretty heavy change for 1.1 imo. From my gut feeling I'd say we should think a few days/weeks about this.

            Mike Brock (Inactive) added a comment - - edited

            This solution might introduce cycles in more complex scenarios

            True. But they're also extremely easy to detect by the container at bootstrap time. That is to say, that you can easily detect a conflict in the ordering and blow up with a fairly informative message. CDI-aware IDEs will also have no problem detecting this, too.

            This solution has the same basic effect as a magic number solution to define salience. But the problem with magic numbers is that it suffers from an even worse readability problem than this. Especially if you have salience values for common events being declared in dynamically loaded modules – say, like Seam Forge.

            So in a Forge plugin, the plugin author would need to know the basal value, and then if coordinating with other plugins (a likely possibility in Forge) know their basal values as well.

            When you look at this solution in that scenario, it's much easier to resolve the issue.

            All you need to do is provide qualifiers that can then be referenced with @Before and @After from the plugins.

            So, @After(BeanLoading.class) @Before(BeanDeploy.class) is a heck of a lot easier to document for and read than say something like @Salience(1.30f).

            Mike Brock (Inactive) added a comment - - edited This solution might introduce cycles in more complex scenarios True. But they're also extremely easy to detect by the container at bootstrap time. That is to say, that you can easily detect a conflict in the ordering and blow up with a fairly informative message. CDI-aware IDEs will also have no problem detecting this, too. This solution has the same basic effect as a magic number solution to define salience. But the problem with magic numbers is that it suffers from an even worse readability problem than this. Especially if you have salience values for common events being declared in dynamically loaded modules – say, like Seam Forge. So in a Forge plugin, the plugin author would need to know the basal value, and then if coordinating with other plugins (a likely possibility in Forge) know their basal values as well. When you look at this solution in that scenario, it's much easier to resolve the issue. All you need to do is provide qualifiers that can then be referenced with @Before and @After from the plugins. So, @After(BeanLoading.class) @Before(BeanDeploy.class) is a heck of a lot easier to document for and read than say something like @Salience(1.30f) .

            Mark Struberg (Inactive) added a comment - - edited

            As with every solution there are pros and cons. Your solution is very neat to read, but there are also a few cons which should not be unmentioned:

            a) This solution might introduce cycles in more complex scenarios
            b) In a more modular and thus more complex situation, you don't always know your other observers.
            c) Having to know other observers is a bit (edit: should better say totally?) against the basic idea of the observer design pattern.
            d) Again: if I know my other observers, then just fu***n simply call them directly
            e) If I don't know the other observers (e.g. they are in another jar which is not compile time scoped but only added at runtime), then there's now way to do it.

            I know: a Float with a default value of 1.0f is really stone age technology. But it works. But sure, this also has cons so lets discuss this further.

            Mark Struberg (Inactive) added a comment - - edited As with every solution there are pros and cons. Your solution is very neat to read, but there are also a few cons which should not be unmentioned: a) This solution might introduce cycles in more complex scenarios b) In a more modular and thus more complex situation, you don't always know your other observers. c) Having to know other observers is a bit ( edit : should better say totally?) against the basic idea of the observer design pattern. d) Again: if I know my other observers, then just fu***n simply call them directly e) If I don't know the other observers (e.g. they are in another jar which is not compile time scoped but only added at runtime), then there's now way to do it. I know: a Float with a default value of 1.0f is really stone age technology. But it works. But sure, this also has cons so lets discuss this further.

            Mike Brock (Inactive) added a comment - - edited

            After having a conversation with Dan Allen about solving this problem, this is what we came up with.

            We propose the addition of two new annotations, @Before and @After which both accept Class<? extends Annotation>[] to reference qualifiers (one or more).

            For example:

            Case A:

            public class MyBean
            	public void myFirstObserver(@Observes @Before(Initialization.class) MyEvent event) {
            	   ...
            	}
            
            	public void mySecondObserver(@Observes @Initialization MyEvent event) {
            	   ...
            	} 
            }
            
            

            Case B:

            public class MyBean
            	public void myFirstObserver(@Observes @After(Initialization.class) MyEvent event) {
            	   ...
            	}
            
            	public void mySecondObserver(@Observes @Initialization MyEvent event) {
            	   ...
            	} 
            }
            

            In Case A, the container would guarantee that when a MyEvent is fired, that myFirstObserver() is called before mySecondObserver(). In Case B, the container would guarantee that myFirstObserver() is called only after mySecondObserver() has been called.

            In a situation where multiple observers are observing the event with the referenced qualifier, @Before will indicate the annotated observer shall execute before any of those thus qualified execute. The converse will be true for @After.

            Where multiple qualifiers are specified in @Before or @After the matching behavior will be identical to the dispatch matching behavior of the CDI specification. For instance:

            Case C

            public void firstObserver(@Observes @A @B @C MyEvent event) { 
            }
            
            public void secondObserver(@Observes @A @B @D MyEvent event) { 
            }
            
            public void thirdObserver(@Observes @A @E @F @After({A.class, D.class}) MyEvent event) { 
            }
            
            

            In Case C, thirdObserver() will only be called after secondObserver() is called. However, firstObserver() may be called before or after; the contract with the container does not guarantee any firing order for firstObserver() relative to the other two observers.

            Also, the specification may benefit from the addition of an @OrderingQualifier which permits the creation of qualifiers used only for the purpose of ordering, but are otherwise ignored by the container for dispatching of events. This would be optional, of course. Any regular qualifier can be used for ordering.

            Example:

            @OrderingQualifier
            @Retention(RetentionPolicy.RUNTIME)
            @Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
            public @interface MyQualifier {
            }
            

            The presence of ordering qualifiers may muddy the water a little bit in terms of readability. However, I could foresee situations where the use of ordering qualifiers – when used properly – could make resolving some complex ordering situations less difficult. At the very least, it would allow for dispatch ordering that could not be circumvented accidentally. And by that I mean, ordering qualifiers cannot be used to qualify event consumers; they are used by the container merely for resolving the dispatch ordering – which is done when the container bootstraps – and otherwise have no runtime side-effects.

            Mike Brock (Inactive) added a comment - - edited After having a conversation with Dan Allen about solving this problem, this is what we came up with. We propose the addition of two new annotations, @Before and @After which both accept Class<? extends Annotation>[] to reference qualifiers (one or more). For example: Case A: public class MyBean public void myFirstObserver(@Observes @Before(Initialization.class) MyEvent event) { ... } public void mySecondObserver(@Observes @Initialization MyEvent event) { ... } } Case B: public class MyBean public void myFirstObserver(@Observes @After(Initialization.class) MyEvent event) { ... } public void mySecondObserver(@Observes @Initialization MyEvent event) { ... } } In Case A, the container would guarantee that when a MyEvent is fired, that myFirstObserver() is called before mySecondObserver() . In Case B, the container would guarantee that myFirstObserver() is called only after mySecondObserver() has been called. In a situation where multiple observers are observing the event with the referenced qualifier, @Before will indicate the annotated observer shall execute before any of those thus qualified execute. The converse will be true for @After . Where multiple qualifiers are specified in @Before or @After the matching behavior will be identical to the dispatch matching behavior of the CDI specification. For instance: Case C public void firstObserver(@Observes @A @B @C MyEvent event) { } public void secondObserver(@Observes @A @B @D MyEvent event) { } public void thirdObserver(@Observes @A @E @F @After({A.class, D.class}) MyEvent event) { } In Case C, thirdObserver() will only be called after secondObserver() is called. However, firstObserver() may be called before or after; the contract with the container does not guarantee any firing order for firstObserver() relative to the other two observers. — Also, the specification may benefit from the addition of an @OrderingQualifier which permits the creation of qualifiers used only for the purpose of ordering, but are otherwise ignored by the container for dispatching of events. This would be optional, of course. Any regular qualifier can be used for ordering. Example: @OrderingQualifier @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD}) public @ interface MyQualifier { } The presence of ordering qualifiers may muddy the water a little bit in terms of readability. However, I could foresee situations where the use of ordering qualifiers – when used properly – could make resolving some complex ordering situations less difficult. At the very least, it would allow for dispatch ordering that could not be circumvented accidentally. And by that I mean, ordering qualifiers cannot be used to qualify event consumers; they are used by the container merely for resolving the dispatch ordering – which is done when the container bootstraps – and otherwise have no runtime side-effects.

            I don't really think it's counter intuitive. A special circumstance, sure. But not counter intuitive. In a stateful system, there are times when one observer is going to need to affect the state of the system in such a way that it needs to come before other observers of the same event. We have this in Servlet, we have this in JSF and I think we need to have it in CDI too.

            One concrete use case is an observer that responds to the Servlet Initialized Event (assuming we had such an integration) to prepare the request and initialize other context variables so that all other observers of the same event can use these prepared resources. The inverse is needed on destroy.

            Dan Allen (Inactive) added a comment - I don't really think it's counter intuitive. A special circumstance, sure. But not counter intuitive. In a stateful system, there are times when one observer is going to need to affect the state of the system in such a way that it needs to come before other observers of the same event. We have this in Servlet, we have this in JSF and I think we need to have it in CDI too. One concrete use case is an observer that responds to the Servlet Initialized Event (assuming we had such an integration) to prepare the request and initialize other context variables so that all other observers of the same event can use these prepared resources. The inverse is needed on destroy.

            One thing we could pretty easily do is to add a Float value representing an ordinal. But that really doesn't fit well to the general observer/observable pattern. What is your exact use case? From a quick think-through I don't see anything which cannot be done in a different way also.

            Mark Struberg (Inactive) added a comment - One thing we could pretty easily do is to add a Float value representing an ordinal. But that really doesn't fit well to the general observer/observable pattern. What is your exact use case? From a quick think-through I don't see anything which cannot be done in a different way also.

              asabotdu@redhat.com Antoine Sabot-Durand (Inactive)
              lincolnthree Lincoln Baxter III (Inactive)
              Votes:
              7 Vote for this issue
              Watchers:
              20 Start watching this issue

                Created:
                Updated:
                Resolved: