bennidi / mbassador

Powerful event-bus optimized for high throughput in multi-threaded applications. Features: Sync and Async event publication, weak/strong references, event filtering, annotation driven
MIT License
956 stars 146 forks source link

Send a message to a specific listener #72

Closed vrudikov closed 10 years ago

vrudikov commented 10 years ago

Hello! is there a way to post a message to a specific listener. e.g: Listener1 - with receiverId field value = 1067 Listener2 - with receiverId field value = 2098 both listeners are handling same SuperMessage class

bus.post(message, 1067);

And only Listener1 will receive a message as a result

P.S: Maybe it can be implemented as a Filter

volgar1x commented 10 years ago

A cleaner way would be to subclass SuperMessage and make Listener2 handle it.

vrudikov commented 10 years ago

Are you kitten me :) your suggestions is to subclass message for each receiverId receiverId is dynamic data - each listener will have own processID

volgar1x commented 10 years ago

My suggestion is to design a clear and concise event model.

vrudikov commented 10 years ago

my suggestion is to implement methods which will take a list of subscriptions by message type and then will choose a specific listener from that list by receiverId

volgar1x commented 10 years ago

This isn't often a good idea. Take a look at this article (wikipedia) to understand why.

bennidi commented 10 years ago

@Blackrush How does the referenced article relate to vrudikovs problem?

@vrudikov I think that there is currently no other solution than implementing the receiverId filter in a common base class handler handle(SuperMessage message) which checks the receiverId of its instance and either calls the doHandle(SuperMessage message) or not. I was thinking about implementing listener scoped filters but I didn't see much of a point in doing so because having base class methods to implement listener specific filtering logic seems a very straight-forward technique.

vrudikov commented 10 years ago

@bennidi common base class handler - it's sounds good, but you will have a lot of messages types and a lot of message handlers methods I think Filter concept is more flexible. and as i understand in Filter class we have HandlerMetadata, so maybe i can grab receiverId from that metadata will do more complex analysis

arnonmoscona commented 10 years ago

I totally agree with @bennidi - from where I stand, the whole point of messaging is to decouple senders from receivers. If the sender knows who the receiver is (in the same process) then there is no architectural difference between a direct method call from the sender to the receiver. Why do you need messaging then? Just because you don't maintain a direct reference to the instance? that's pretty weak.

Filters on the other hand, leave the control at the receiver side, which is the right model for messaging.

The simple guiding principle is "senders do not know receivers and receivers do not know senders". Messages can be classified by type, and when that is not feasible, then a filter can help.

bennidi commented 10 years ago

@vrudikov The existing filter concept is subcription scoped, i.e. a filter is evaluated once for all listeners of a subscription. it also does not have access to a concrete listener instance. What you would need is a listener scoped filter. This would be implemented as an IHandlerInvocation, very similar to how FilteredDispatcher is implemented. You could have a go and implement a FilteredHandlerInvocation. Have a look at how SubscriptionFactory does its job. It should be fairly easy to add this new FilteredInvocation. Submit a pull request when your done, I would be happy to include the code into the next release.

volgar1x commented 10 years ago

Reference transparency exactly describes the concept of "decoupling senders from receivers".

bennidi commented 10 years ago

@Blackrush I think I tend to disagree. As the article states, referential transparency means that the same expression always evaluates to the same value (i.e. it can be replaced by its value). This is a primary goal of functional programming where immutable data types (like tries or the common linked lists) are used a lot to make sure that functions operating on data will return the same value when provided with the same references to data structures.

In non-functional programming there is a lot of (shared) mutable state such that you are not guaranteed at all that invoking the same method on the same object with the same object references as parameters yields the same output. Most OO programs are not referentially transparent at all, which is why reasoning about behaviour is very difficult. So the real question is not whether you decouple your senders and receivers or not (decoupling has nothing to do with referential transparency) but whether you design them to be mutable or immutable.

volgar1x commented 10 years ago

I poorly choose the article. I mean "referential transparency" in the Erlang way.

bennidi commented 10 years ago

I am quite sure that Erlang, as a functional language, doesn't define referential transparency differently. Or can you provide any resource where this is clearly stated? If you are referring to the actor model (which are decoupled senders and receivers) then it is still true that the main point of actors is immutable communication and local mutable state. Again, referential transparency has nothing to do with decoupling of objects.

bennidi commented 10 years ago

@arnonmoscona Thanks for your support :) But I want to make my personal understanding of coupling/decoupling clear. Objects are coupled if they know each other, i.e. have a (typed) reference to each other, accept instances of one another as a parameter, do call each others methods etc. In statically typed languages an import kind of indicates this dependency. So for me, establishing an indirect relation between sender and receiver by a commonly shared context (like a process id) does not violate decoupling of objects. They still don't know about one another's existence and new receiver types can be added without any change to the sender. The difference rather lies in the distribution of messages. What vrudikov suggests may seem like a point-to-point communication but there may be many different senders and receivers sharing the same context (process id, whatever). I think this can make sense in many cases. The question is rather whether mbassador supports this out of the box or not. @vrudikov Have you managed to take a look at the code I was telling you about?

arnonmoscona commented 10 years ago

While you do have a point that the coupling is weaker, you must realize that you still have logical coupling there. In a fully decoupled design the sender would have no knowledge of any aspect of the receiver.

Yes. There are use cases where the type of weak coupling you suggest may be needed, but they are rather rare. I think this pattern should be generally discouraged. Once you enable it on a framework level you encourage antipatterns in uses of the framework. Very bad.

If you have a truly legitimate use case, and think hard about it, then it may be worth your effort to wrap the framework to achieve the functionality you want. It can be done using the "pure" facilities that are already there with little performance impact. In my mind it would be better than "contaminating" the framework...

Maybe users who want to create a sort of "to address" should lay out their use cases more elaborately. Maybe some deeper pattern will be uncovered that has a cleaner solution. I am hard pressed to think that it could not be achieved with some IMessageFilter implementation. But detailed use cases could lead to some small improvements...

Just my 2c

bennidi commented 10 years ago

@arnonmoscona Got a fair point there. So the question rather seems to be about the "acceptable" amount of coupling. But then the handlers signature is some sort of logical coupling, too. The structure of the message defines the contract between senders and receivers, it is what they have in common and what logically couples them together. This is inevitable, so some logical coupling seems to be inherent to the pub/sub pattern.

Now, where do you then draw the line? If you design a message such that it will always include an id and that it is the messages' responsibility to choose this id, let's say from a ThreadLocal. Does this violate decoupling? Does it violate decoupling if the messages' contract is that an id needs to be supplied to its constructor (i.e. by the sender) such that whoever listens may or may not include that id in its processing decisions? Tough puzzle...

BTW, I saw you have some CSV code in one of your repos. Maybe you want to take a look at my project BloX, which is an event based CSV reader/writer library :)

vrudikov commented 10 years ago

To all of you - i don't understand why is that become such a problem we have a sender - and we are have receivers(which are subscribed for this message type) sender will send a message and using common language it will say - "ok, receivers! all of you are subscribed for me, because we are using pub/sub pattern. but this time i will send a message which some of you will process and someone will not, because of some secured thing or because many of you is parents at age 50. etc...Just think about it as a common language which you use while speaking

bennidi commented 10 years ago

@vrudikov As I said earlier: For me it is about who is responsible for establishing the common context/filtering (in your case the process id) and whether it is the responsibility of the framework to support this kind of use cases. And I think you should try to advance your arguments more clearly and maybe propose concrete solutions rather than throwing half baked opinions into the room. Also, if you want a feature in an open source project it is always possible to write (good) code and submit a pull request. After all, this is what github is all about.

bennidi commented 10 years ago

By the way: It occurred to me that you could use a custom HandlerInvocation to solve your problem. Have a look at the invocation property of @Listener