PrimeFaces WebSocket Ajaxタグを使用した簡易チャットプログラミング

#最初に
PrimeFacesのタグを使用して簡易チャットを作成してみました。
NetBeansインストール方法は[こちら](http://qiita.com/yu_naka0607/items/c0dca687a6659f7e9ac4)を参照ください。
Projectの作成、PrimeFaces設定方法は[こちら](http://qiita.com/BlueMoonAZ/items/b48abba5b5d665a33c05)を参照下さい。
#環境
NetBeans 8.0.2
JavaSDK 1.8.0.25
PrimeFaces 5.0
GlassFish 4.1
#プログラムの作成
非同期表示が可能なチャットプログラムです。

- チャットメッセージはBeanで保持し、ArrayListに追加します。
- ページはDataTableタグでArrayListの内容を表示します。
- メッセージの投稿を契機にWebSocketによるPush通知でDataTable表示を更新します。

WebSocketではメッセージの送受信は行わず、画面の更新契機通知のみに使用しています。これによりメッセージの規定やハンドリングは不要となり、シンプルなコードとなっています。

今回作成したコードは、JSFページが1ページとJavaクラスが４つになります。Javaクラスは以下の構成です。

| クラス名      | 内容 | Scope |
|:-------------|:-------------|:-------|
| Bb           |jsfページとのインターフェイス |RequestScoped|
| ChatText     |1回の投稿情報を格納する||
| ChatApp      |投稿情報をArrayListに保存する|AplicationScoped|
| ChatEndPoint |Pushのエンドポイント||


JSFページのコードです。

```html:index.xhtml
<?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:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui">
    
    <h:head>
        <title>Chat</title>
    </h:head>
    <h:body>
        <h:form id="form">
            <h:panelGrid columns="3">
                <h:outputLabel value="お名前" />
                <h:inputText id="inputName" value="#{bb.mname}" />
                <h:outputLabel value="" />
                <h:outputLabel value="メッセージ" />
                <h:inputText id="inputMessage" value="#{bb.mtext}" />
                <h:commandButton value="Send" action="#{bb.sendText}" />
                
            </h:panelGrid>
            
            <h:dataTable id="dttable" value="#{chatApp.MChat}" var="row">
                <h:column>
                    <h:outputText value="#{row.MCal}" />
                </h:column>
                 <h:column>
                     <h:outputText value="#{row.MName}" />
                </h:column>
                 <h:column>
                     <h:outputText value="#{row.MText}" />
                </h:column>                
            </h:dataTable>
           
        </h:form>

        <p:socket channel="/chat">
            <p:ajax event="message" update=":form:dttable" />
        </p:socket>

    </h:body>
</html>

```

入力欄はPanelタグにまとめています。
socketタブでWebSocketのチャネルを指定しています。チャネルはEndpointBeanと一致させる必要が有ります。ajaxタグでメッセージ受信時にdatatableのみ再描画する定義をしています。

以下はJavaのコードです。

```java:Bb.java
package beans;

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import org.primefaces.push.EventBus;
import org.primefaces.push.EventBusFactory; 

@Named
@RequestScoped
public class Bb {

    private String Mtext;    

    public String getMtext() {
        return Mtext;
    }

    public void setMtext(String Mtext) {
        this.Mtext = Mtext;
    }
    
    private String Mname;

    public String getMname() {
        return Mname;
    }

    public void setMname(String Mname) {
        this.Mname = Mname;
    }
    
    @Inject
    private ChatApp cha; //ApricationScoped bean にアクセスするためDIします。 

    public void sendText(){
        
        cha.setChat(Mtext, Mname);

        EventBus eventBus = EventBusFactory.getDefault().eventBus();
        eventBus.publish("/chat", "Notify");
        Mtext="";
    }
    
}

```
actionメソッドのsendTextでは入力データをAplicationScopeのChatAppに設定した後、WebSocket通信を行います。具体的にはFactoryからeventBusを取得してpublishメソッドを使用します。

```java:ChatText.java
package beans;

import java.text.SimpleDateFormat;
import java.util.Calendar;

public class ChatText {

    String MText;
    String MCal;
    String MName;

    public ChatText(String MText, String MName) {
        this.MText = MText;
        this.MName = MName;
        
        Calendar cal;
        SimpleDateFormat sdf = new SimpleDateFormat("MM/dd hh:mm:ss SSS");        
        cal=Calendar.getInstance();
        
        this.MCal=sdf.format(cal.getTime());
        
    }

    public String getMText() {
        return MText;
    }


    public String getMCal() {
        return MCal;
    }


    public String getMName() {
        return MName;
    }

    
} 

```

１メッセージを格納するBeanです。メッセージ発信時刻をコンストラクタで設定しています。

```java:ChatApp.java
package beans;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;

@Named
@ApplicationScoped
public class ChatApp implements Serializable{
   
    private final List<ChatText> MChat = new ArrayList();
    
    public List<ChatText> getMChat() {
        return MChat;
    }  
    
    public void setChat(String pText,String pName){        
        MChat.add(0,new ChatText( pText,pName ));
    }
       
}
```

AplicationScopeのbeanですが、ArrayListを保持しているだけです。
ArrayListのゲッターと、メッセージをセットするメソッドのみです。

```java:ChatEndPoint.java
package beans;

import org.primefaces.push.annotation.OnMessage;
import org.primefaces.push.annotation.PushEndpoint;
import org.primefaces.push.impl.JSONEncoder;

@PushEndpoint("/chat")
public class ChatEndPoint {
    
    @OnMessage(encoders = {JSONEncoder.class})
    public String onMessage(String data) {
        return data;
    }
    
}
```

EndPointのbeanです。ここでWebSocketの終端で起こるイベントハンドラを実装します。
アノテーションでチャネル名を指定しており、OnMessageをオーバーライドしてencodersを指定しています。今回はWebSocket電文はダミーなのでencodersは省略できるかと思いましたが、OnMessageイベントが発火しませんでした。エラーにはなりませんでしたが必須のようです。

#実行画面
![image](https://qiita-image-store.s3.amazonaws.com/0/94957/a9cb25bf-c574-7d2e-d489-aee188f22977.png)

2枚のブラウザで相互表示が確認できます。

#おしまい
プログラムとしてはすっきりしたコードに仕上がりましたがJSF（CDIも含めですが）には未だ慣れず、製造には３日程度かかってしまいました。コンパイル、デプロイができた後に引っかかることが結構あります。時間の有る今のうちに慣れておきたいと思ってきます。