#最初に
PrimeFacesのタグを使用して簡易チャットを作成してみました。
NetBeansインストール方法はこちらを参照ください。
Projectの作成、PrimeFaces設定方法はこちらを参照下さい。
#環境
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クラスが4つになります。Javaクラスは以下の構成です。
クラス名 | 内容 | Scope |
---|---|---|
Bb | jsfページとのインターフェイス | RequestScoped |
ChatText | 1回の投稿情報を格納する | |
ChatApp | 投稿情報をArrayListに保存する | AplicationScoped |
ChatEndPoint | Pushのエンドポイント |
JSFページのコードです。
<?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のコードです。
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メソッドを使用します。
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;
}
}
1メッセージを格納するBeanです。メッセージ発信時刻をコンストラクタで設定しています。
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のゲッターと、メッセージをセットするメソッドのみです。
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イベントが発火しませんでした。エラーにはなりませんでしたが必須のようです。
2枚のブラウザで相互表示が確認できます。
#おしまい
プログラムとしてはすっきりしたコードに仕上がりましたがJSF(CDIも含めですが)には未だ慣れず、製造には3日程度かかってしまいました。コンパイル、デプロイができた後に引っかかることが結構あります。時間の有る今のうちに慣れておきたいと思ってきます。