Bu yazıda Primefaces 6.2 de Web Socket ile sunucudan istemciye socket açıp veri push nasıl edildiği basit bir örnekle gösterilecektir. Tomcat 8 kullanılacaktır. Pimefaces Web Socket kütüphanesi olan Atmosphere'i ile çalışmaktadır.
Öncelikle primefaces ve CDI desteği eklemek gerekiyor. Bunun için aşağıdaki bağımlılıklar eklenmeli:
<dependencies>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.2.17</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.2.17</version>
</dependency>
<dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>6.2</version>
</dependency>
<dependency>
<groupId>org.jboss.weld.servlet</groupId>
<artifactId>weld-servlet-shaded</artifactId>
<version>3.0.4.Final</version>
</dependency>
<dependency>
<groupId>org.atmosphere</groupId>
<artifactId>atmosphere-runtime</artifactId>
<version>2.4.6</version>
</dependency>
<dependency>
<groupId>org.atmosphere</groupId>
<artifactId>atmosphere-runtime-native</artifactId>
<version>2.4.6</version>
</dependency>
<dependency>
<groupId>org.atmosphere</groupId>
<artifactId>atmosphere-cdi</artifactId>
<version>2.4.21</version>
</dependency>
</dependencies>
bu şekilde JSF 2, Primefaces 6.2, CDI için Weld servlet ve atmosphere projesi için gerekli kütüphaneler eklenmiş olacaktır. CDI kurulumu için bakınız :
www.fibiler.com/Tomcat-ile-CDI-Kullanimi_Ipucu_26969
Web.xml aşağıdaki gibi gerekli şeyler eklenmelidir :
<context-param>
<param-name>primefaces.PUSH_SERVER_URL</param-name>
<param-value>http://localhost:8080/pfpush</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-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Push Servlet</servlet-name>
<servlet-class>org.primefaces.push.PushServlet</servlet-class>
<init-param>
<param-name>org.atmosphere.cpr.broadcasterCacheClass</param-name>
<param-value>org.atmosphere.cache.UUIDBroadcasterCache</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>Push Servlet</servlet-name>
<url-pattern>/primepush/*</url-pattern>
</servlet-mapping>
Öncelikle Push Servlet servlet tanımlanmıştır. Eğer local'de bir context içinde çalışıyorsa web projesi primefaces.PUSH_SERVER_URL değerinin girilmesi gereklidir. pfpush web projenizin adıdır.
Bir değeri artıran bir sayfa yapalım. Bu sayfada bir düğme tıklandığında değer bir artsın ve değişiklikler tüm ilgili istemcilere gönderilsin. Sayfa aşağıdaki gibi olabilir:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head></h:head>
<h:body>
<h:form>
<h:outputText value="#{counterView.count}" styleClass="display" />
<p:commandButton value="Click" actionListener="#{counterView.increment}" />
</h:form>
<p:socket onMessage="handleMessage" channel="/counter" />
<script type="text/javascript">
function handleMessage(data) {
$('.display').text(data);
}
</script>
</h:body>
</html>
Düğme tıklandığında counterView bean'indeki değer bir artırılmaktadır. p:socket ile bir web socket yaratılmıştır ve channel ile kanal adı /counter olarak verilmiştir. Kanala yeni veri geldiğinde handleMessage yöntemi çağrılacaktır. Bu yöntem de .display style class'lı nesnenin içeriğini değiştirecektir. CounterView bean'i aşağıdaki gibidir :
import javax.annotation.PiostConstruct;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import org.primefaces.push.EventBus;
import org.primefaces.push.EventBusFactory;
@ManagedBean
@ApplicationScoped
public class CounterView {
private volatile int count;
private EventBus eventBus;
@PostConstruct
public void init() {
eventBus = EventBusFactory.getDefault().eventBus();
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public void increment() {
count++;
if(eventBus!=null) {
eventBus.publish("/counter", String.valueOf(count));
}
}
}
Düğmeye tıklandığında increment() yöntemi çağrılacaktır. Bu yöntemde counter değerini bir artıracak ve yeni veri girildiğini publish edecektir. Bu şekilde /counter kanalına bağlanmış tüm socket'ler veriyi alacaktır.
CounterResource adında bir kaynak aşağıdaki gibi yaratılmalıdır:
import org.primefaces.push.annotation.OnMessage;
import org.primefaces.push.annotation.PushEndpoint;
import org.primefaces.push.impl.JSONEncoder;
@PushEndpoint("/counter")
public class CounterResource {
@OnMessage(encoders = { JSONEncoder.class })
public String onMessage(String count) {
return count;
}
}
Bu kaynak bir mesaj geldiğinde hangi verinin socket'e yazılacağını belirtir.
İkinci bir sayfa yaparak socket'in farklı sayfalarda da çalışacağını test edelim:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head></h:head>
<h:body>
<h:form>
<h:outputText value="" styleClass="display" />
</h:form>
<p:socket onMessage="handleMessage" channel="/counter" />
<script type="text/javascript">
function handleMessage(data) {
$('.display').text(data);
}
</script>
</h:body>
</html>
Bu sayfada da bir socket yaratıyoruz. Socket'e mesaj geldiğinde handleMessage çağrılıyor ve değeri gösteren bölüme yeni değer yazılıyor.
counter.jsf sayfasını çalıştırınca ve düğmeye basınca değerin hem bu sayfada hem de diğer sayfada değiştiğini görebilirsiniz.
Developer aracında WS sekmesi ile socket bağlantıları görülebilir.