NOTE:
1. this code works for me where ONLY 1 browser per device connects to server.
2. Below, I provided CDI and JSF-managed-bean versions of ChatUsers.java and ChatView.java
3. As I mentioned in other/related forum topics, the code I am sharing is for MULTIPLE devices and MULTIPLE devices ONLY!
4. The screen capture below shows that I logged into the web app via my phone, tablet, and desktop (in that order), and I entered messages, and the messages showed up on all 'devices'.
chat.xhtml
Code: Select all
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>PrimePush - Chat</title>
<style>
.chatlogs {
width: 400px;
height: 200px;
overflow: auto;
padding: 0.5em 1em 0.5em 0.5em;
}
.messageInput {
width: 400px;
}
.usersList {
height: 200px;
overflow: auto;
}
</style>
</h:head>
<h:body>
<p:growl id="growl" showDetail="true" />
<h:form id="form">
<p:remoteCommand name="updateUserList" update="users"/>
<p:commandLink value="Return to PrimeFaces Push - Examples"
action="index" ajax="false" immediate="true"/>
<p:fieldset id="container" legend="PrimeChat" toggleable="true">
<h:panelGroup rendered="#{chatView.loggedIn}">
<h:panelGrid columns="2" columnClasses="publicColumn,usersColumn" style="width:100%">
<p:outputPanel id="public" layout="block" styleClass="ui-corner-all ui-widget-content chatlogs"/>
<p:dataList id="users" var="user" value="#{chatUsers.users}" styleClass="usersList">
<f:facet name="header">
Users
</f:facet>
<p:commandButton title="Chat" icon="ui-icon-comment" oncomplete="pChat.show()" update=":form:privateChatContainer">
<f:setPropertyActionListener value="#{user}" target="#{chatView.privateUser}" />
</p:commandButton>
#{user}
</p:dataList>
</h:panelGrid>
<p:separator />
<p:inputText value="#{chatView.globalMessage}" styleClass="messageInput" />
<p:spacer width="5" />
<p:commandButton value="Send" actionListener="#{chatView.sendGlobal}" oncomplete="$('.messageInput').val('').focus()"/>
<p:spacer width="5" />
<p:commandButton value="Disconnect" actionListener="#{chatView.disconnect}" global="false" update="container" />
</h:panelGroup>
<h:panelGroup rendered="#{not chatView.loggedIn}" >
Username: <p:inputText value="#{chatView.username}" />
<p:spacer width="5" />
<p:commandButton value="Login" actionListener="#{chatView.login}" update="container"
icon="ui-icon-person" />
</h:panelGroup>
</p:fieldset>
<p:dialog widgetVar="pChat" header="Private Chat" modal="true" showEffect="fade" hideEffect="fade">
<h:panelGrid id="privateChatContainer" columns="2" columnClasses="vtop,vtop">
<p:outputLabel for="pChatInput" value="To: #{chatView.privateUser}" />
<p:inputTextarea id="pChatInput" value="#{chatView.privateMessage}" rows="5" cols="30" />
<p:spacer />
<p:commandButton value="Send" actionListener="#{chatView.sendPrivate}" oncomplete="pChat.hide()" />
</h:panelGrid>
</p:dialog>
</h:form>
<p:socket onMessage="handleMessage" channel="/chat" autoConnect="false" widgetVar="subscriber"/>
<script type="text/javascript">
function handleMessage(data) {
if (data.indexOf("updateUserList()") !== -1) {
updateUserList();
}
else {
var chatContent = $(PrimeFaces.escapeClientId('form:public'));
chatContent.append(data + '<br />');
//keep scroll
chatContent.scrollTop(chatContent.height());
}
}
</script>
</h:body>
</html>
Code: Select all
/*
* Copyright 2009-2012 Prime Teknoloji.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.primepush;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
@Named
@ApplicationScoped
public class ChatUsers implements Serializable {
private List<String> users;
public ChatUsers() {
}
@PostConstruct
public void init() {
this.users = new ArrayList<String>();
}
public List<String> getUsers() {
return users;
}
public void setUsers(List<String> users) {
this.users = users;
}
public void addUser(String user) {
this.users.add(user);
}
public void removeUser(String user) {
this.users.remove(user);
}
public boolean contains(String user) {
return this.users.contains(user);
}
}
Code: Select all
package com.primepush;
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import org.primefaces.context.RequestContext;
import org.primefaces.push.PushContext;
import org.primefaces.push.PushContextFactory;
@Named
@SessionScoped
public class ChatView implements Serializable {
private PushContext pushContext = PushContextFactory.getDefault().getPushContext();
@Inject
private ChatUsers users;
private String privateMessage;
private String globalMessage;
private String username;
private boolean loggedIn;
private String privateUser;
private final static String CHANNEL = "/chat/";
public ChatView() {
}
public void setUsers(ChatUsers users) {
this.users = users;
}
public String getPrivateUser() {
return privateUser;
}
public void setPrivateUser(String privateUser) {
this.privateUser = privateUser;
}
public String getGlobalMessage() {
return globalMessage;
}
public void setGlobalMessage(String globalMessage) {
this.globalMessage = globalMessage;
}
public String getPrivateMessage() {
return privateMessage;
}
public void setPrivateMessage(String privateMessage) {
this.privateMessage = privateMessage;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public boolean isLoggedIn() {
return loggedIn;
}
public void setLoggedIn(boolean loggedIn) {
this.loggedIn = loggedIn;
}
public void sendGlobal() {
pushContext.push(CHANNEL + "*", username + ": " + globalMessage);
globalMessage = null;
}
public void sendPrivate() {
pushContext.push(CHANNEL + privateUser, "[PM] " + username + ": " + privateMessage);
privateMessage = null;
}
public void login() {
RequestContext requestContext = RequestContext.getCurrentInstance();
if(users.contains(username)) {
loggedIn = false;
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Username taken", "Try with another username."));
requestContext.update("growl");
}
else {
users.addUser(username);
pushContext.push(CHANNEL + "*", username + " joined the channel.");
pushContext.push(CHANNEL + "*", "updateUserList()");
requestContext.execute("subscriber.connect('/" + username + "')");
loggedIn = true;
}
}
public void disconnect() {
RequestContext requestContext = RequestContext.getCurrentInstance();
//remove user and update ui
users.removeUser(username);
requestContext.update("form:users");
requestContext.execute("subscriber.disconnect()");
//push leave information
pushContext.push(CHANNEL + "*", "updateUserList()");
pushContext.push(CHANNEL + "*", username + " left the channel.");
//reset state
loggedIn = false;
username = null;
}
}
Code: Select all
/*
* Copyright 2009-2012 Prime Teknoloji.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.primepush;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@ApplicationScoped
public class ChatUsers implements Serializable {
private List<String> users;
public ChatUsers() {
}
@PostConstruct
public void init() {
this.users = new ArrayList<String>();
}
public List<String> getUsers() {
return users;
}
public void setUsers(List<String> users) {
this.users = users;
}
public void addUser(String user) {
this.users.add(user);
}
public void removeUser(String user) {
this.users.remove(user);
}
public boolean contains(String user) {
return this.users.contains(user);
}
}
Code: Select all
package com.primepush;
import java.io.Serializable;
import javax.faces.application.FacesMessage;
import javax.faces.bean.SessionScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.context.FacesContext;
import org.primefaces.context.RequestContext;
import org.primefaces.push.PushContext;
import org.primefaces.push.PushContextFactory;
@ManagedBean
@SessionScoped
public class ChatView implements Serializable {
private PushContext pushContext = PushContextFactory.getDefault().getPushContext();
/*
* Injecting managed beans in each other
* http://balusc.blogspot.com/2011/09/communication-in-jsf-20.html#InjectingManagedBeansInEachOther
*/
@ManagedProperty("#{chatUsers}")
private ChatUsers users;
private String privateMessage;
private String globalMessage;
private String username;
private boolean loggedIn;
private String privateUser;
private final static String CHANNEL = "/chat/";
public ChatView() {
}
/*
* Injecting managed beans in each other
* http://balusc.blogspot.com/2011/09/communication-in-jsf-20.html#InjectingManagedBeansInEachOther
*/
public void setUsers(ChatUsers users) {
this.users = users;
}
public String getPrivateUser() {
return privateUser;
}
public void setPrivateUser(String privateUser) {
this.privateUser = privateUser;
}
public String getGlobalMessage() {
return globalMessage;
}
public void setGlobalMessage(String globalMessage) {
this.globalMessage = globalMessage;
}
public String getPrivateMessage() {
return privateMessage;
}
public void setPrivateMessage(String privateMessage) {
this.privateMessage = privateMessage;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public boolean isLoggedIn() {
return loggedIn;
}
public void setLoggedIn(boolean loggedIn) {
this.loggedIn = loggedIn;
}
public void sendGlobal() {
pushContext.push(CHANNEL + "*", username + ": " + globalMessage);
globalMessage = null;
}
public void sendPrivate() {
pushContext.push(CHANNEL + privateUser, "[PM] " + username + ": " + privateMessage);
privateMessage = null;
}
public void login() {
RequestContext requestContext = RequestContext.getCurrentInstance();
if(users.contains(username)) {
loggedIn = false;
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Username taken", "Try with another username."));
requestContext.update("growl");
}
else {
users.addUser(username);
pushContext.push(CHANNEL + "*", username + " joined the channel.");
pushContext.push(CHANNEL + "*", "updateUserList()");
requestContext.execute("subscriber.connect('/" + username + "')");
loggedIn = true;
}
}
public void disconnect() {
RequestContext requestContext = RequestContext.getCurrentInstance();
//remove user and update ui
users.removeUser(username);
requestContext.update("form:users");
requestContext.execute("subscriber.disconnect()");
//push leave information
pushContext.push(CHANNEL + "*", "updateUserList()");
pushContext.push(CHANNEL + "*", username + " left the channel.");
//reset state
loggedIn = false;
username = null;
}
}
Code: Select all
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Production</param-value>
</context-param>
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>server</param-value>
</context-param>
<context-param>
<param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>org.apache.myfaces.EL_RESOLVER_COMPARATOR</param-name>
<param-value>org.apache.myfaces.el.unified.OpenWebBeansELResolverComparator</param-value>
</context-param>
<context-param>
<param-name>org.apache.myfaces.CACHE_EL_EXPRESSIONS</param-name>
<param-value>always</param-value>
</context-param>
<context-param>
<param-name>org.apache.myfaces.COMPRESS_STATE_IN_SESSION</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<param-name>org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION</param-name>
<param-value>10</param-value>
</context-param>
<context-param>
<param-name>org.apache.myfaces.SERIALIZE_STATE_IN_SESSION</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name>
<param-value>-1</param-value>
</context-param>
<context-param>
<param-name>org.apache.myfaces.CHECK_ID_PRODUCTION_MODE</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<param-name>org.apache.myfaces.VIEW_UNIQUE_IDS_CACHE_ENABLED</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>org.apache.myfaces.SAVE_STATE_WITH_VISIT_TREE_ON_PSS</param-name>
<param-value>false</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>Push Servlet</servlet-name>
<servlet-class>org.primefaces.push.PushServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>org.atmosphere.cpr.broadcasterCacheClass</param-name>
<param-value>org.atmosphere.cache.HeaderBroadcasterCache</param-value>
</init-param>
<init-param>
<param-name>org.atmosphere.cpr.broadcasterClass</param-name>
<param-value>org.atmosphere.cpr.DefaultBroadcaster</param-value>
</init-param>
<init-param>
<param-name>org.atmosphere.cpr.broadcastFilterClasses</param-name>
<param-value>org.atmosphere.client.TrackMessageSizeFilter</param-value>
</init-param>
<init-param>
<param-name>org.atmosphere.cpr.sessionSupport</param-name>
<param-value>false</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Push Servlet</servlet-name>
<url-pattern>/primepush/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
</web-app>