「WebSocket」タグアーカイブWebSocketにてJSON形式のデータをJavaFXとやり取りしてみる。送信ボタンからSingleController#handleSendActionが呼ばれる。SingleController#handleSendActionから@ClientEndpointのsendメソッド(自分で作った普通のメソッド)を呼び出す。sendメソッド内でSession.getAsyncRemote().sendObject(dataObj)を呼び出すencodersに指定してあるClientEncoderクラスのencodeメソッドが呼び出される。encodeメソッドでエンコードされたデータ(JSON形式の文字列)がServerEndpointに送信される。エコープログラムなのでServerEndpointで受信されたデータは(若干加工され)ClientEndpointに戻される。そのため、続けて受信処理が走る。受信処理サーバからのデータがPushされる。@ClientEndpointのdecodersに指定してあるClientDecoderクラスのwillDecodeメソッドが呼び出され入力値がチェックされる。willDecodeの結果がtrueならば続けてdecodeメソッドが呼び出されてジェネリクスで指定した型(ClientData)にデコードされる。デコードされたデータを引数に@ClientEndpointの@OnMessageが呼び出される。ここはJavaFXのスレッド配下ではなく、WebSocketのスレッドに移っている。そのため、JavaFXのコントロールを直接操作することはできない。@OnMessageの中でPlatform.runLaterを呼び、JavaFXの画面操作(接続時処理ViewObj#write)の呼び出しを登録(?)する。JavaFXスレッドに戻り、JavaFXの画面操作が実行される。wpepeaWebSocketにてJSON形式のデータをJavaFXとやり取りしてみる。画面の開始ボタンからSingleController#handleStartButtonActionが呼ばれる。handleStartButtonAction内でClientManager#connectToServerを呼びWebSocketの起動を開始する。WebSocketの@OnOpenが呼ばれる。ここはJavaFXのスレッド配下ではなく、WebSocketのスレッドに移っている。そのため、JavaFXのコントロールを直接操作することはできない。@OnOpenの中でPlatform.runLaterを呼び、JavaFXの画面操作(接続時処理ViewObj#open)の呼び出しを登録(?)する。JavaFXスレッドに戻り、JavaFXの画面操作が実行される。切断処理1画面の終了ボタンからSingleController#handleStoptButtonActionが呼ばれる。Session#closeを呼び出し、WebSocketの終了処理を開始する。切断処理2に続く切断処理2「切断処理1」または「ServerEndpointからの切断」等により、WebSocketの@OnCloseが呼ばれる。ここはJavaFXのスレッド配下ではなく、WebSocketのスレッドに移っている。そのため、JavaFXのコントロールを直接操作することはできない。@OnCloseの中でPlatform.runLaterを呼び、JavaFXの画面操作(接続時処理ViewObj#close)の呼び出しを登録(?)する。JavaFXスレッドに戻り、JavaFXの画面操作が実行される。wpepeaWebSocketにてJSON形式のデータをJavaFXとやり取りしてみる。2コメントを残す先ほどの続き前提条件JavaFXのコントロール(ラベルとかボタンとか)はJavaFXのスレッド配下からでないと原則(※1)操作できない(と思う)。WebSocketはプログラム本体とは別のスレッドでデータの送受信を行う。WebSocketのスレッド内からPlatform#runLaterを呼ぶことで、JavaFXのスレッド配下で行わせたい処理を登録できる。今の実装だと、接続状況の排他制御がきちんとされていないので、「サーバからの切断」と「クライアントからの起動」のシーケンスが重なったときとかにうまく動かないケースがでてくる。こちらにあるようにコントロールに結びついたObservableListを別スレッドから操作した場合は、その操作が画面に反映される。ラベルとかボタンとかのコントロールで試した範囲ではJavaFXのスレッドかどうかチェックにかかってうまくいかなかったがやりようはあるかもしれない。ApplicationクラスとFXMLApplicationクラスは起動するだけ。}}FXMLはfx:idふって、onActionを紐付けている程度。</children></AnchorPane>起動処理WSJsonSingle#mainから。画面項目の初期化と、ClientEndpointのインスタンス化(まだ接続しない)。wpepeaWebSocketにてJSON形式のデータをJavaFXとやり取りしてみるコメントを残すGlassfish4とJavaFX間でJSON形式のデータをWebsocketにてやり取りしてみたのでメモ。何ページかに分かれているけど実際にJSONのハンドリングをしている箇所はこちらサーバサイドはこの前HTMLとやり取りしたものとほぼ同じ。表示はリスト形式ではなくてラベルの中身を置換していく形式(表示以外はやっていることはHTMLの時とほぼ一緒)HTMLからJSONで入力テキストと現在日時を送信。GlassfishのWebsocketで受信(&Decode)受け取ったデータに情報(とりあえずユーザ情報代わりのセッションID)を付与し繋がっているクライアント(JavaFX&この前作ったHTML)全体にJSON形式で返信JavaFX(またはHTML)で受け取ったデータを表示データ形式はこの前と同じ。(なので、クライアントはこの前のHTMLとJavaFXで併用できる。)(JavaFX->glassfishのデータ形式){"message":"メッセージ","messageTime":時間(ミリ秒)}(glassfish->JavaFXのデータ形式){"message":"メッセージ","sessionId":"セッションID","messageTime":"HH:mm:ss"}斜体は変数Maven必要な依存関係を追加。開始ボタンしか押せない状態。裏ではWebSocket用のClientEndPointをインスタンス化している。開始ボタンクリック後(の入力欄に文字入力した状態)入力欄や送信ボタン・終了ボタンが使えるようになる。入力メッセージと時間がサーバに送信される。データを受信したサーバにて加工(といってもセッションID文字列を付加しただけ)されたデータがクライアントに送られ、そのデータがラベルに表示される。開始ボタンしか押せない状態になる。github:https://github.com/epea/test01/tree/WF_JSON_FX_SINGLE続き。wpepeaDecoder.TextのwillDecodeが2度呼ばれる(TYRUS-210)コメントを残すWebSocketのServerEndpointとClientEndpointでDecoder.Text<T>を使っていたらwillDecodeが2度呼ばれていた。Tyrusのバグ(TYRUS-210)とのこと。TestDecoder#decode(ClientEndpoint)ClientDecoder#initClientDecoder#willDecodeClientDecoder#willDecodeClientDecoder#decodeWSJsonSingleClient#onMessageクライアントサイドは新しいバージョンをpomに指定して修正されたことを確認。</dependency>ClientDecoder#initClientDecoder#willDecodeClientDecoder#decodeWSJsonSingleClient#onMessageサーバサイドのGlassfishも下のように対応されているとのこと。1.2は既にGlassFishに統合・組み込まれています。Buildのダウンロードもしくは手作業で新しいTyrusにアップグレードすることができます(手作業の場合、全てのTyrusのjarファイルを置き換えて下さい)。サーバサイドのMavenで対応されているものはぱっとみ見つからなかった。wpepeaWebSocketのエンコード・デコード時のエラーコメントを残す先日書いたWebsocketでJSONデータをやり取りしたブログだけど、エラー出力をきちんとしていなくてはまった。Decoder.Text<T>のdecodeメソッド内でExceptionが発生した場合に、明示的にログ・スタックトレースの出力をしておかないと特にスタックトレース等が出力されないでdecodeメソッド(とそれから呼ばれる@OnMessage)が終了してしまう。(コンソールにエラーがつらつら出るようなことは無い。)そのため、下みたいな感じで明示的にエラーあったことを出力してやらないとわかりにくい。}Encoder.Text<T>のencodeメソッドも似たような話がある。4)コメントを残すGlassfish4とHTML間でJSON形式のデータをWebsocketにてやり取りしてみたのでメモ。大まかな動きは以下HTMLからJSONで入力テキストと現在日時を送信。GlassfishのWebsocketで受信(&Decode)受け取ったデータに情報(とりあえずユーザ情報代わりのセッションID)を付与し繋がっているクライアント(HTML)全体にJSON形式で返信HTMLで受け取ったデータを表示(html->glassfishのデータ形式){"message":"メッセージ","messageTime":時間(ミリ秒)}(glassfish->htmlのデータ形式){"message":"メッセージ","sessionId":"セッションID","messageTime":"HH:mm:ss"}斜体は変数Maven必要な依存関係を追加。WebSocket(arg)のargで接続先指定をしてWebSocketオブジェクトを取得する接続先は@ServerEndpointをつけたサーバサイドのPOJOに指定したもの。WebSocketのsendで送信、onmessageで受信Websocketで送られてきたデータはsocket.onmessageで渡される引数(今回はmessage)のdataに入っている。取り出してからの扱いは普通のJSON。id="send">送信</button></body></html>WebSocket用のPOJO(GlassFish側)@ServerEndpointをつけたPOJOでWebSocket用クラス(サーバエンドポイント)を作るパスはvalueで指定。自分の環境ではコンテキストがfirstなのでfirst/hellojson/がサーバエンドポイントのアドレス@onMessageが繋がっている(クライアントサイドの)エンドポイントから呼ばれたときに動く個所decodersでクラスを指定すると、やってきたメッセージが指定されたクラスでデコードされて渡される。encodersでクラスを指定すると、指定したクラスでエンコードしたデータを、繋がっているエンドポイントに渡せる。}}decodersのクラスjavax.websocket.DecoderのText<T>をimplementsする。(Text形式であるJSONの場合)<T>に入るのはエンドポイントのOnMessageに渡したい型willDecodeメソッドがデコードできるかどうか判定。バリデーションとかはwillDecodeでなくOnMessageでやるべきがどうかは未調査。(未実装)decodeメソッドで返却するオブジェクトを生成しreturnする。false;}}}encodersのクラスjavax.websocket.EncoderのText<T>をimplementsする。(Text形式であるJSONの場合)<T>に入るのはエンドポイントのOnMessageで呼ぶsendObjectの引数(返す元ネタ)encodeメソッドで返却するStringを生成しreturnする。‘/first/hello/’.とわかりやすいメッセージがでる。その4コメントを残すWebSocketで受け取ったデータをJavaFXのラベルに設定する方法とりあえず動いた。}}まだ、確認できていないのがこの実装で順序の保障できているかどうか。(実装的にはWebSocketボタン押した回数立ち上がったりと適当な部分はありますが。その3コメントを残す時系列のままに書いている間が漂ってきましたが。最初はボタンイベント(handleButtonAction)からjavafx.concurrent.Serviceをキックしていた。Grizzlyの中でWebsocket用のスレッドが立てられているので、「Websoketスレッド起動の処理」をスレッド化しても多分意味がないことに気づいた。thread」というやつの対策にはならなそう。ボタンイベントから直接Websocket用のスレッド起動を行うように変更。}}予定通りWebsocket(のスレッド)はキックされている。@OnMessageでLabelの値に反映させる方法は調査中。動いたそれを皮切りに”ObservableList経由で反映しているTableView”以外もやっつけるつもりだったけどそもそもできるか不安になってきた。案件募集中です店が開店休業状態なので、店番をしながらできるシステムの仕事を募集中です。@YoshitakeKitamuもしくは店へのご連絡お待ちしています。