Spring 4+tomcat 8+jdk 8アプリケーションwebsocket

8994 ワード

環境JDK 8.0 + Spring4.2.3 + Tomcat8.0 
websocketはhtml 5の特性の一つであり、既存の主流のブラウザはすでにwebsocketをサポートしており、websocketもJava EE 7の規範の一つとなっている.現在jetty、tomcatなどの主流のアプリケーションサーバはwebsocketプロトコルをサポートしている.tomcatは7以上でwebsocketプロトコルをサポートしていますが、tomcat 7とtomcat 8は少し違います.tomcat 8はwebsocketに対してもっと良い支持とサンプルがあって、webappexamplesの下にwebsocketのサンプルがあって、直接実行することができて、javaファイルはexampleのWEB-INFディレクトリの下で、classと一緒に置いて、とても便利です.
Spring 4もwebsocketに対するAPIサポートを追加し、現在一部のコンテナはJSR-356仕様に対してまだ実現されていないが、異なるバージョンのコンテナ実現方式も異なり、springが提供するAPIを使用してwebsocketの開発を統一することができる.
一.JAVA API for websocket JSR-356
1.非常に簡単で、サーバーセグメントは@ServerEndpoint IDを使用するだけで、具体的な使用方法:
Websocketサービスに設定する必要があるクラスに@Server Endpoint(value='abcd')を追加し、対応するメソッドを実装すればよい.
import javax.websocket.OnClose;
import javax.websocket.OnOpen;
import javax.websocket.server.ServerEndpoint;

import org.springframework.context.annotation.Configuration;

@Configuration
@ServerEndpoint(value = "/abcd")
public class RealEndpoint {
	@OnOpen
	public void onOpen() {
		System.out.println("Client connected");
	}

	@OnClose
	public void onClose() {
		System.out.println("Connection closed");
	}
}

2.クライアントインタフェースでの使用
var host = 'ws://' + window.location.host + '/THSCADAWEB/collectionList';
var webSocket;
if ('WebSocket' in window) {
	webSocket = new WebSocket(host);
} else if ('MozWebSocket' in window) {
	webSocket = new MozWebSocket(host);
} else {
	Ext.Msg.alert('  ', '        websocket,      ');
    return;
}

webSocket.onerror = function(event) {
	console.log('error');
};

webSocket.onopen = function(event) {
	console.log('open');
};

webSocket.onmessage = function(event) {
	var data = event.data;
	console.log('onmessage');
	var address = data.split(';')[0];
	var status = data.split(';')[1];
	var collectionStore = Ext.getCmp('deployCollectionGrid').getStore();
	var rec = collectionStore.findExact('remote_address', address);
	if(rec > -1){
		unit = collectionStore.getAt(rec).set('collection_connStatus',status);
	}else{
		collectionStore.add({
			'remote_address' : address,
			'collection_connStatus' : status
		});
	}	
};

もちろん複雑な点でもブラウザがwebsocketをサポートしているかどうかを判断することができ、tomcat 8のサンプルを参考にすることができます.
3.使用するパッケージ
JAVA APIのみを使用する場合はtomcatの下のwebsocket-apiをインポートする必要がある.JArは、これまでtomcatとの実装を比較する、直接インポートはまだだめであり、tomcatの下のwebsocket-apiを直接インポートする.jar、配備が実行されると、ずっとエラーが報告されています.Error during WebSocket handshake : Unexpected response code : 404.これはjarパケットが衝突したためだそうです.
解決方法:既存のプロジェクトのjava ee 7を削除する.JArパッケージ、tomcatの中のwebsocket-api.JArパッケージは1つのディレクトリに個別にコピーされ、tomcat-websocketと名付けられ、プロジェクトで右クリック->propertiesJava->Build Path->Libraries->Add Library->User Library->User Libraries->New->User library name tomcat-websocketを入力し、下のsystem library->ok->先ほど追加したtomcat-websocketを選択し、Add External JARsをクリックします.さっきコピーしたwebsocket-apiに参加します.JArのディレクトリtomcat-websocket->作成したUser library->Finishをチェックします.
作成してからインポートするとjavax-websocketのパッケージが現れ、tomcat 8に配備して実行できます.
二、Spring API for WebSocket
Javaのapiを使うのは面倒ではありませんが、Springでは上記の手順を処理する必要はありません.Springは4からWebSocket APIを発売し、現在はSpring 4を使用している.2.3、比較的新しいバージョンです.もちろん上記JAVA API for websocket'はSpringエンジニアリングでも使用できますが、アプリケーションサーバjarパッケージとの競合に注意が必要です. 
1.  WebSocketConfig
Springは1つの構成クラスを通じてwebsoceketサービスを登録し管理する
package com.th.scada.websocket;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

import com.th.scada.real.controller.RealSocketHandler;

/**
 * Spring  WebSocket     
 * @author guoyankun
 */
@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements
		WebSocketConfigurer {
	/**
	 *   websocket
	 */
	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		//     websocket  ,addHandler      websocket        ,     collectionList   endpoint
		registry.addHandler(RealSocketHandler(), "/collectionList").addInterceptors(new WebSocketHandshakeInterceptor());
		//       websocket endpoint
	}
	@Bean
	public WebSocketHandler RealSocketHandler() {
		return new RealSocketHandler();
	}
}

2.Interceptor、その名の通り、インターセプタ、HandshakeInterceptorインタフェースを実現し、websocket握手開始と握手終了のイベントを処理する.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import com.th.scada.util.Constants;

public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {

    private static Logger logger = LoggerFactory.getLogger(HandshakeInterceptor.class);
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws Exception {
        if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
            HttpSession session = servletRequest.getServletRequest().getSession(false);
            if (session != null) {
                //  userName  WebSocketHandler,        
                String userName = (String) session.getAttribute(Constants.SESSION_USERNAME);
                if(userName != null){
                	attributes.put(Constants.WEBSOCKET_USERNAME,userName);
                }
            }
        }
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {

    }
}

3. 
RealSocketHandlerは、WebSocketHandlerインタフェースを実現し、具体的なwebsocket情報インタラクティブコンテンツを処理する.
import java.io.IOException;
import java.util.ArrayList;

import org.apache.log4j.Logger;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;

public class RealSocketHandler implements WebSocketHandler {
	private static final ArrayList users = new ArrayList();
	private static Logger logger = Logger.getLogger(RealSocketHandler.class);

	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus arg1) throws Exception {
		logger.debug("websocket connection closed");
		System.out.println("afterConnectionClosed");
		users.remove(session);
	}

	@Override
	public void afterConnectionEstablished(WebSocketSession session)
			throws Exception {
		logger.debug("connect to the websocket succcess ... ...");
		System.out.println("afterConnectionEstablished");
		users.add(session);
	}

	@Override
	public void handleMessage(WebSocketSession session, WebSocketMessage> arg1) throws Exception {
		System.out.println("handleMessage");
	}

	@Override
	public void handleTransportError(WebSocketSession session, Throwable arg1) throws Exception {
		System.out.println("handleTransportError");
		logger.debug("websocket connection closed");
		users.remove(session);
	}

	@Override
	public boolean supportsPartialMessages() {
		return false;
	}

	/**
	 *            
	 *
	 * @param message
	 *            
	 */
	public void sendMessageToUsers(TextMessage message) {
		for (WebSocketSession user : users) {
			try {
				if (user.isOpen()) {
					user.sendMessage(message);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 *          
	 *
	 * @param userName
	 * @param message
	 */
	public void sendMessageToUser(String userName, TextMessage message) {
		for (WebSocketSession user : users) {
			if (user.getAttributes().get("websocket_username").equals(userName)) {
				try {
					if (user.isOpen()) {
						user.sendMessage(message);
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
				break;
			}
		}
	}
}
4. Spring API for WebSocketを使用するとtomcatのwebsocket-apiは必要ありません.JArパッケージはspring-websocket-4.2.3に代わります.RELEASE.jar.