Wicket
  1. Wicket
  2. WICKET-4879

Implementing channels in wicket-atmosphere

    Details

    • Type: New Feature New Feature
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 6.3.0
    • Fix Version/s: 6.4.0
    • Component/s: wicket-atmosphere
    • Labels:
      None
    • Environment:
      Tomcat / Jetty

      Description

      Good evening,

      Now that both Tomcat & Jetty support native WebSockets, it would be a killer-feature to implement Comet / WebSockets channels. i.e. being able to have a channel id and to post a message to one client only or to all the clients who subscribed to a certain channel.

      For the moment, it is possible to do it, but to the (expensive) price of having a PerRequestBroadcastFilter and tweaking it if we manage to do it. But it would be great to have a link to the Broadcaster API from Atmosphere, which I think, supports this kind of things.That would probably be way more efficient than PerRequestBroadcastFilter tweaking.

      As of today, when posting a message to the event bus, all pages who have the @Subscribed-decorated method suitable to process this message will do it, which is not always desirable.

      I'm looking forward to hear from you.

      Regards

        Activity

        Hide
        Zala Goupil added a comment -

        I've attached a quickstart demonstrating the fact that when posting to the event bus, all clients registered to that resource receive the message. But, it is visually obvious in this example that it is not desired behaviour.

        FYI, it's based on Emond's own quickstart.

        Show
        Zala Goupil added a comment - I've attached a quickstart demonstrating the fact that when posting to the event bus, all clients registered to that resource receive the message. But, it is visually obvious in this example that it is not desired behaviour. FYI, it's based on Emond's own quickstart.
        Hide
        vineet semwal added a comment -

        doesn't adding custom predicate in @Subscribe solves the problem?

        Show
        vineet semwal added a comment - doesn't adding custom predicate in @Subscribe solves the problem?
        Hide
        Zala Goupil added a comment -

        Not really, because if you post on the event bus with two clients registered, if I understand things correctly, the predicate which returns true sends the response to both clients, even if the other one returns false.

        Let me explain a little more what is the current behaviour. Let's say that two clients A & B have the right @Subscribe method. Let's say that you send an event e to the event bus for that method. Then A processes e and sends the same response to both A & B. Then B processes the same event and sends a response to both A & B, too. So we have two responses received by both A & B. I have an app where it's visually obvious: I send a small notification in my @Subscribe method. The notification displays two times in each browser A & B. Which is what make me think it works like that. Plus, it looks consistent with what I know from Atmosphere's Meteor API (which is called under the hood). Hence the request for a Broadcaster-based solution.

        If I use a predicate which returns true for A and false for B, here is what apparently occurs: A sends its response to both A & B since the predicate returns true. And B doesn't send anything since the predicate returns false. But the * same * response returns to both A & B since it comes from A.

        What I would like to be able to do is to have a response computed by B's AtmosphereBehavior instance sent to B and a response computed by A's AtmosphereBehavior instance sent to A. Because those 2 responses can potentially be a little bit different from each other.

        Does it make sense? Am I doing assumptions that I should not?

        One more thing. Quoting wicket-atmosphere's JavaDoc: "The filter cannot rely on any context. For example, the RequestCycle may not be available". This seems to me a little bit limited. I fully understand why this is like this for the predicate. But if we have a way to dispatch the right event to the right @Subscribe method (or a channel implementation of any sort), maybe we will be able to filter things with more context, so that would be potentially easier.

        OK, any thoughts about that?

        Kind regards,

        Pierre

        Show
        Zala Goupil added a comment - Not really, because if you post on the event bus with two clients registered, if I understand things correctly, the predicate which returns true sends the response to both clients, even if the other one returns false. Let me explain a little more what is the current behaviour. Let's say that two clients A & B have the right @Subscribe method. Let's say that you send an event e to the event bus for that method. Then A processes e and sends the same response to both A & B. Then B processes the same event and sends a response to both A & B, too. So we have two responses received by both A & B. I have an app where it's visually obvious: I send a small notification in my @Subscribe method. The notification displays two times in each browser A & B. Which is what make me think it works like that. Plus, it looks consistent with what I know from Atmosphere's Meteor API (which is called under the hood). Hence the request for a Broadcaster-based solution. If I use a predicate which returns true for A and false for B, here is what apparently occurs: A sends its response to both A & B since the predicate returns true. And B doesn't send anything since the predicate returns false. But the * same * response returns to both A & B since it comes from A. What I would like to be able to do is to have a response computed by B's AtmosphereBehavior instance sent to B and a response computed by A's AtmosphereBehavior instance sent to A. Because those 2 responses can potentially be a little bit different from each other. Does it make sense? Am I doing assumptions that I should not? One more thing. Quoting wicket-atmosphere's JavaDoc: "The filter cannot rely on any context. For example, the RequestCycle may not be available". This seems to me a little bit limited. I fully understand why this is like this for the predicate. But if we have a way to dispatch the right event to the right @Subscribe method (or a channel implementation of any sort), maybe we will be able to filter things with more context, so that would be potentially easier. OK, any thoughts about that? Kind regards, Pierre
        Hide
        vineet semwal added a comment - - edited

        see code below,i mean something like this,you can change/improve according to your usecase

        public class InputWrapper<T> {
        /**
        *

        • @param input
        • @param id id is sessionid of sender
          */
          public InputWrapper(T input, String id) { this.input=input; this.sender=id; }

        public T getInput()

        { return input; }

        public String getSender()

        { return sender; }

        T input;
        String sender;
        }

        /**

        • compare sessionid and return true if session id of sender is same as receiver
          */
          public class SomePredicate implements Predicate<InputWrapper>
          Unknown macro: { @Override public boolean apply(@Nullable InputWrapper wrapper) { return wrapper.getSender().equals(Session.get().getId()); } }
        Show
        vineet semwal added a comment - - edited see code below,i mean something like this,you can change/improve according to your usecase public class InputWrapper<T> { /** * @param input @param id id is sessionid of sender */ public InputWrapper(T input, String id) { this.input=input; this.sender=id; } public T getInput() { return input; } public String getSender() { return sender; } T input; String sender; } /** compare sessionid and return true if session id of sender is same as receiver */ public class SomePredicate implements Predicate<InputWrapper> Unknown macro: { @Override public boolean apply(@Nullable InputWrapper wrapper) { return wrapper.getSender().equals(Session.get().getId()); } }
        Hide
        Zala Goupil added a comment - - edited

        Yes, that's totally it! But unfortunately, my arguments below still apply.

        Plus, it gives me strange exceptions to use this kind of things: UnknwonBehaviorIdException, NullPointerException because the appended component can't be found, etc. I tried using this kind of code but was forced to strip it because of these exceptions.

        Show
        Zala Goupil added a comment - - edited Yes, that's totally it! But unfortunately, my arguments below still apply. Plus, it gives me strange exceptions to use this kind of things: UnknwonBehaviorIdException, NullPointerException because the appended component can't be found, etc. I tried using this kind of code but was forced to strip it because of these exceptions.
        Hide
        vineet semwal added a comment -

        afaik what wicket-atmosphere does is it filters the overall subscribers based on if their corresponding predicate returns true or not when event is posted ,if a predicate of subscriber returns true then the method/subscriber gets invoked else not so in above case with "SomePredicate" if client A has sent response then predicate returns true only if A is receiver and hence only A gets response.
        i can't reproduce your exception...

        Show
        vineet semwal added a comment - afaik what wicket-atmosphere does is it filters the overall subscribers based on if their corresponding predicate returns true or not when event is posted ,if a predicate of subscriber returns true then the method/subscriber gets invoked else not so in above case with "SomePredicate" if client A has sent response then predicate returns true only if A is receiver and hence only A gets response. i can't reproduce your exception...
        Hide
        Zala Goupil added a comment -

        Unfortunately, my experience is that if a predicate matches for a client and doesn't matches for the others, the matching predicate sends the response to all clients. So in our case the message is sent only once, and not as many times as there are connected clients, but to all the connected clients. Which I find pretty annoying.

        Regarding my exception, it only happens when using complex HTML, in one of my projects, it is totally absent. But in another one, it happens on a regular basis.

        Show
        Zala Goupil added a comment - Unfortunately, my experience is that if a predicate matches for a client and doesn't matches for the others, the matching predicate sends the response to all clients. So in our case the message is sent only once, and not as many times as there are connected clients, but to all the connected clients. Which I find pretty annoying. Regarding my exception, it only happens when using complex HTML, in one of my projects, it is totally absent. But in another one, it happens on a regular basis.
        Hide
        Emond Papegaaij added a comment -

        I agree that the filter construction is a bit weak. It can only be used to filter on properties of the event itself, not on properties of the receiver. The reason for this is that it is expensive to setup the entire context, such as the session and a request cycle. I think it should be possible to add a second filter that runs inside the AtmosphereRequestHandler, at which point the session, request cylcle and even the page are available.

        Show
        Emond Papegaaij added a comment - I agree that the filter construction is a bit weak. It can only be used to filter on properties of the event itself, not on properties of the receiver. The reason for this is that it is expensive to setup the entire context, such as the session and a request cycle. I think it should be possible to add a second filter that runs inside the AtmosphereRequestHandler, at which point the session, request cylcle and even the page are available.
        Hide
        Emond Papegaaij added a comment -

        I've just pushed the implementation of a context aware filter. I've also updated the example to show how this works. Be advised though that filtering within the context of a Wicket request cyle is expensive. If you can implement the desired behavior via the Atmosphere API, I suggest you do so, because that should be much less expensive.

        Wrapping the Atmosphere api is not what Wicket-Atmosphere is for, it is a bridge not a wrapper.

        Show
        Emond Papegaaij added a comment - I've just pushed the implementation of a context aware filter. I've also updated the example to show how this works. Be advised though that filtering within the context of a Wicket request cyle is expensive. If you can implement the desired behavior via the Atmosphere API, I suggest you do so, because that should be much less expensive. Wrapping the Atmosphere api is not what Wicket-Atmosphere is for, it is a bridge not a wrapper.
        Hide
        Zala Goupil added a comment -

        Good evening,

        I've just tried it. The behaviour is the same: if three clients are connected and that only one's filter returns true, then the same message goes to all the three clients. If two of them return true, then those two send the message to the three clients, and so on.

        Personally, I'd like to be able to have the message sent by one client broadcasted to only this client. Of course, the use case of "one client sends to all" is useful, I just wish it was not the only one possible.

        Anyway, your "contextAwareFilter" is useful, thanks for that !

        I've tried myself to implement a Broadcaster-based message-sending in your code, but for the moment, I failed. Too bad. The one thing I miss is how to provide an AtmosphereResource to my Broadcaster.

        Anyway, keep up the good work!

        Show
        Zala Goupil added a comment - Good evening, I've just tried it. The behaviour is the same: if three clients are connected and that only one's filter returns true, then the same message goes to all the three clients. If two of them return true, then those two send the message to the three clients, and so on. Personally, I'd like to be able to have the message sent by one client broadcasted to only this client. Of course, the use case of "one client sends to all" is useful, I just wish it was not the only one possible. Anyway, your "contextAwareFilter" is useful, thanks for that ! I've tried myself to implement a Broadcaster-based message-sending in your code, but for the moment, I failed. Too bad. The one thing I miss is how to provide an AtmosphereResource to my Broadcaster. Anyway, keep up the good work!
        Hide
        Emond Papegaaij added a comment -

        I don't see how you could return true for one client and false for another (other than using something like random). filter() is used with no receiver context at all. It is meant to be used to filter certain types of events (other than the actual instance type). For example, if you have an event that contains progress of a job and you only want your component to receive the events if the progress is 80% or higher, that's when you use filter(). The same filter should always return true for the same events.

        Can you take a look at commit a5421ec488020a3835d49962e1f772dc1435e230? I've added methods to post an event to a single resource, either with a resource instance or via its uuid (which I think makes more sense). You can get the UUID with AtmosphereResource.getUUID(page).

        Show
        Emond Papegaaij added a comment - I don't see how you could return true for one client and false for another (other than using something like random). filter() is used with no receiver context at all. It is meant to be used to filter certain types of events (other than the actual instance type). For example, if you have an event that contains progress of a job and you only want your component to receive the events if the progress is 80% or higher, that's when you use filter(). The same filter should always return true for the same events. Can you take a look at commit a5421ec488020a3835d49962e1f772dc1435e230? I've added methods to post an event to a single resource, either with a resource instance or via its uuid (which I think makes more sense). You can get the UUID with AtmosphereResource.getUUID(page).
        Hide
        Zala Goupil added a comment -

        Thanks again! I've tried this new code, but unfortunately the situation is the same: all the connected resources are refreshed when issuing: Application.get().getEventBus().post(MyObject, pageUuid), not just the one that I'm expecting.

        Sorry about that, I don't know what could be wrong.

        Show
        Zala Goupil added a comment - Thanks again! I've tried this new code, but unfortunately the situation is the same: all the connected resources are refreshed when issuing: Application.get().getEventBus().post(MyObject, pageUuid), not just the one that I'm expecting. Sorry about that, I don't know what could be wrong.
        Hide
        Emond Papegaaij added a comment -

        Do you think you can make a quickstart for that? If so, please create a new ticket. However, I suspect the problem lies somewhere else, because the code in EventBus does not write anything to the other resources.

        Show
        Emond Papegaaij added a comment - Do you think you can make a quickstart for that? If so, please create a new ticket. However, I suspect the problem lies somewhere else, because the code in EventBus does not write anything to the other resources.

          People

          • Assignee:
            Emond Papegaaij
            Reporter:
            Zala Goupil
          • Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development