12

I've tried to implement the basic notification system for a basic social network with p:poll on view layer and a simple NotificationService class which gets the new notifications from DB and refreshes the notifications list of NotificationBean which is viewscoped for each user. Process flow similar to this:

-Poll calls NotificationBean.getNewNotifications for example every 15 sec.
--getNewNotifications calls NotificationService and DAO methods
---notificationList of user is refreshed
----DataTable on view layer shows new notifications

But the concern of p:poll is about it's performance because it sends a query at every interval expiration.

PrimeFaces has PrimePush which based on Atmosphere Framework, it opens web-sockets and seems like more suitable for creating notifications system.

But I don't know which components and which properties of them should be used. It has p:socket component with channel property. Should I use usernames as a channel values? Below code coming from PrimeFaces showcase and summarizes the last sentences:

<p:socket onMessage="handleMessage" channel="/notifications" /> 

As far as I understood from this showcase example this p:socket listens notifications channel. And pusher code snippet is:

PushContext pushContext = PushContextFactory.getDefault().getPushContext();       
pushContext.push("/notifications", new FacesMessage(summary, detail));

But this will notify all user pages, I need a pusher which notifies specific user. Let say there are 2 users and assume that User1 adds User2 as a friend. There must be sth. like that:

pushContext.push("User2/notifications", new FacesMessage("friendship request", "from User1"));

But I am not sure this is the correct usage for this kind of functional requirement or not. Considering scalability of the app there can be expensive cost of opening so many channels per a process.

Thanks for helping.

Ömer Faruk Almalı
  • 3,792
  • 6
  • 37
  • 63
  • You want to one user send immediate message to other user ? – Rong Nguyen Apr 12 '13 at 07:03
  • @RongNK that depends on how you understand `message`. In the question, I've mentioned about lists which includes notifications. If user1 adds user2 as a friend or comment to his/her status, a notification should be appended into user2's notification list which can be a plain String list or a model class list which is more appropriate for my implementation. So yes, that must be immediate. – Ömer Faruk Almalı Apr 12 '13 at 07:20
  • (sorry for my english)ok, i think you don't want to use poll, so server will broadcast massage and each client will filter, or server must store client's address and send to specific address, that i think. Let a pro to solve your issue :) – Rong Nguyen Apr 12 '13 at 07:55

1 Answers1

8

PrimeFaces push supports one or more channels to push. To be able to create private channels for specific reasons; for example per user like in your case, you can create more than one channels. I had used unique ids for this purpose.

Basically, I've implemented a managed bean which is application scoped that handles user channel matching which should be considered. You can maintain it in different ways.

@ManagedBean
@ApplicationScoped
public class ChannelsBean {

    Map<String, String> channels = new HashMap<String, String>();

    public void addChannel(String user, String channel) {
        channels.put(user, channel);
    }

    public String getChannel(String user) {
        return channels.get(user);
    }

}

Then inject this bean in your backing bean which sends notifications.

@ManagedBean
@SessionScoped
public class GrowlBean {

    private String channel;

    @ManagedProperty(value = "#{channelsBean}")
    private ChannelsBean channels;

    private String sendMessageUser;

    private String user;

    @PostConstruct
    public void doPostConstruction() {
        channel = "/" + UUID.randomUUID().toString();
        channels.addChannel(user, channel);
    }

    public void send() {
        PushContext pushContext = PushContextFactory.getDefault().getPushContext();

        pushContext.push(channels.getChannel(sendMessageUser), new FacesMessage("Hi ", user));
    }

    //Getter Setters
}

You should give the channel value to p:socket. Here is the kickoff example of the page;

<p:growl widgetVar="growl" showDetail="true" />  

<h:form>  
    <p:panel header="Growl">  
        <h:panelGrid columns="2">  
            <p:outputLabel for="user" value="User: " />   
            <p:inputText id="user" value="#{growlBean.sendMessageUser}" required="true" />  


        </h:panelGrid>  

        <p:commandButton value="Send" actionListener="#{growlBean.send}" />  
    </p:panel>  
</h:form>  

<p:socket onMessage="handleMessage" channel="#{growlBean.channel}" />  

<script type="text/javascript">  
function handleMessage(facesmessage) {  
    facesmessage.severity = 'info';  

    growl.show([facesmessage]);  
}  
</script>  

For scalability issues, you should maintain the active or inactive channels. You can remove the one which is not in session or inactive for some time. Remove channels by using @PreDestroy annotation when beans are destroying. There is one channel for one user session in my solution.

My suggestion is; do not use usernames explicitly on the pages. It is not good for security reasons.

Ömer Faruk Almalı
  • 3,792
  • 6
  • 37
  • 63
erencan
  • 3,725
  • 5
  • 32
  • 50
  • It seems correct, however I've still few concerns about it's performance, is there any other precautions? By the way thanks for answering, as you might imagine, I am going to wait for bounty expiration, than it will take the cake, surely. – Ömer Faruk Almalı Apr 17 '13 at 09:53
  • I am aware of performance concerns of JSF's itself, lot's of people biased about storing view-state of users decreases the performance. But it provides lot's of features, actually I want to taste it myself and when I do this, it should be implemented neatly to be able to get max performance. That's why I've asked this question and that's why I wanted to get rid of `p:poll`. – Ömer Faruk Almalı Apr 17 '13 at 10:20
  • @erencan is this application scoped bean _thread safe_ ? due to multiple concurrent users, you should be aware of this. – oko Apr 17 '13 at 10:28
  • @oko application scoped beans are not thread safe. Furthermore, hashmap put method is not atomic. So there may occur some problems about it. as i suggested, it is an implementation detail. you can keep channels in databases by using transactions or make the addChannel method synchronized. – erencan Apr 17 '13 at 10:54
  • @ÖmerFarukAlmalı as far as i trace the source code of atmosphere, there is a lookup table which maintains ip and channel matching as you imagine. the only possibility is overloading this lookup table. the table is in org.atmosphere.cpr.DefaultBroadcasterFactory and they are stored in the store hashmap. there should need some implementation to atmosphere to refresh this table. – erencan Apr 17 '13 at 11:22
  • @erencan congurations you got the 50 rep!(helal hacı kaptin repleri :D ) – Ömer Faruk Almalı Apr 18 '13 at 11:17
  • Eyvallah :) I hope this helped ! :) – erencan Apr 18 '13 at 11:35