001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.hayabusa.servlet; 017 018import javax.websocket.OnOpen; 019import javax.websocket.OnClose; 020import javax.websocket.OnMessage; 021import javax.websocket.OnError; 022import javax.websocket.Session; 023import javax.websocket.EndpointConfig; 024import javax.websocket.CloseReason; 025import javax.websocket.server.ServerEndpoint; 026import java.io.IOException; 027import java.io.ByteArrayInputStream; 028import java.io.ByteArrayOutputStream; 029import java.nio.ByteBuffer; 030import java.awt.image.BufferedImage; 031import javax.imageio.ImageIO; 032import java.util.Set; 033import java.util.Collections; 034import java.util.concurrent.ConcurrentHashMap; 035 036/** 037 * http://enterprisegeeks.hatenablog.com/entry/2015/12/17/104815 038 * 039 * WebSocketBasicEndpoint.java のソースを参照しています。 040 * 041 * 設定が、いくつか必要です。 042 * ① /wsdemo をサーバーエンドポイントのURLにしているため 043 * WEB-INF/web.xml の security-constraint の web-resource-collection の 044 * url-pattern に、/wsdemo を追加する必要がある。 045 * <url-pattern>/wsdemo/*</url-pattern> 046 * 047 * ② コンパイル時(build.xml)のクラスパスの設定に、 048 * <pathelement path="${env.CATALINA_HOME}/lib/websocket-api.jar" /> 049 * を追加する必要がある。 050 */ 051@ServerEndpoint(value="/wsdemo") 052public class WebSocketDemo { 053 054 /** クライアントからの全接続を保持するセット */ 055 /** 056 * クライアントからの全接続を保持するセット 057 * 058 * @og.rev 6.8.5.0 (2018/01/09) PMD Variables that are final and static should be all capitals。sessSET → SESS_SET 059 */ 060 private static final Set<Session> SESS_SET = Collections.newSetFromMap( new ConcurrentHashMap<Session, Boolean>() ); 061 062 /** 063 * デフォルトコンストラクター 064 * 065 */ 066 public WebSocketDemo() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 067 068 /** 069 * 2.クライアントからの接続時にコールされる。 070 * 071 * 引数は以下が設定可能だが、メソッド内で使用しないなら省略できる。 072 * 073 * @og.rev 6.8.5.0 (2018/01/09) PMD Variables that are final and static should be all capitals。sessSET → SESS_SET 074 * 075 * @param client クライアントの接続情報 076 * @param config 設定情報 077 */ 078 @OnOpen 079 public void onOpen( final Session client, final EndpointConfig config ) { 080 System.out.println( client.getId() + " was connected." ); 081 printSession( client ); 082 083 SESS_SET.add( client ); 084 } 085 086 /** 087 * 3.クライアントの切断時にコールされる 088 * 089 * 引数は使用しなければ省略可能。 090 * 091 * @og.rev 6.8.5.0 (2018/01/09) PMD Variables that are final and static should be all capitals。sessSET → SESS_SET 092 * 093 * @param client 接続 094 * @param reason 切断理由 095 */ 096 @OnClose 097 public void onClose( final Session client, final CloseReason reason ) { 098 System.out.println( client.getId() + " was closed by " 099 + reason.getCloseCode() 100 + "[" + reason.getCloseCode().getCode()+"]" ); 101 printSession( client ); 102 103 SESS_SET.remove( client ); 104 } 105 106 /** 107 * 4.エラー時にコールされる。 108 * 109 * 引数は使用しなければ省略可能。 110 * @param client クライアント接続 111 * @param error エラー 112 */ 113 @OnError 114 public void onError( final Session client, final Throwable error ) { 115 System.out.println( client.getId() + " was error." ); 116 error.printStackTrace(); 117 } 118 119 /** 120 * 5.テキストメッセージ受信時の処理 121 * 122 * 全クライアントにメッセージを送信する。(サンプル) 123 * 124 * 引数は使用しなければ省略可能。 125 * @param text クライアントから送信されたテキスト 126 * @param client 接続情報 127 * @throws IOException なんらかの入出力例外の発生を通知するシグナルを発生させます。 128 */ 129 @OnMessage 130 public void onMessage( final String text, final Session client ) throws IOException { 131 System.out.println( client.getId() + " was message." + text ); 132 133 // // sessionが持つendPointに紐付く session を取り出す。 134 // for( final Session other : client.getOpenSessions() ) { 135 // other.getBasicRemote().sendText( text ); 136 // } 137 broadCast( text ); // Setで管理しているopenされたsession (元とかぶる) 138 } 139 140 /** 141 * すべてのクライアントにメッセージを送信する。 142 * 143 * @og.rev 6.8.5.0 (2018/01/09) PMD Variables that are final and static should be all capitals。sessSET → SESS_SET 144 * 145 * @param text クライアントに送信するテキスト 146 */ 147 private static void broadCast( final String text ) { 148 for( final Session client : SESS_SET) { 149 client.getOpenSessions().forEach( sess -> sess.getAsyncRemote().sendText( text ) ); 150 } 151 } 152 153 /** 154 * 6.バイナリ受信時の処理 155 * 156 * 送信元に画像を変換して送り返す。 157 * 158 * 引数は使用しなければ省略可能。 159 * @param buf クライアントから送信されたバイナリ 160 * @param client 接続情報 161 * @throws IOException なんらかの入出力例外の発生を通知するシグナルを発生させます。 162 */ 163 @OnMessage 164 public void onMessage( final ByteBuffer buf, final Session client ) throws IOException { 165 client.getBasicRemote().sendBinary( grayScall( buf ) ); 166 } 167 168 /** 169 * 7.画像をグレースケールに変換する。本筋とは関係ない。 170 * 171 * @param input 引数のバイトバッファー 172 * @return グレースケールに変換されたバイトバッファー 173 */ 174 private ByteBuffer grayScall( final ByteBuffer input ) throws IOException { 175 final BufferedImage img = ImageIO.read( new ByteArrayInputStream( input.array() ) ); 176 final BufferedImage glay = new BufferedImage( img.getWidth(), img.getHeight(), BufferedImage.TYPE_BYTE_GRAY ); 177 glay.getGraphics().drawImage( img, 0, 0, null ); 178 final ByteArrayOutputStream bos = new ByteArrayOutputStream(); 179 ImageIO.write( glay, "png", bos ); 180 181 return ByteBuffer.wrap( bos.toByteArray() ); 182 } 183 184 /** 185 * Session のセッションを標準出力に表示します。 186 * 187 * デバッグ用の仮メソッドです。 188 * 189 * @param session セッションオブジェクト 190 */ 191 private void printSession( final Session session ) { 192 System.out.println( " ID = " + session.getId() ); 193 // System.out.println( " Query = " + session.getQueryString() ); // 引数部分のみ 194 System.out.println( " URI = " + session.getRequestURI() ); // URI すべて 195 196 System.out.println( " RequestParameter : " ); // 引数部分を、展開して、Map化 197 session.getRequestParameterMap().forEach( (k,v) -> { 198 System.out.println( " Key=" + k ); 199 System.out.println( " Val=" + java.util.Arrays.toString( v.toArray( new String[v.size()] ) ) ); 200 } 201 ); 202 } 203}