001/* 002 * Copyright 2002-2018 the original author or authors. 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 * https://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, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.socket.adapter.jetty; 018 019import java.nio.ByteBuffer; 020 021import org.apache.commons.logging.Log; 022import org.apache.commons.logging.LogFactory; 023import org.eclipse.jetty.websocket.api.Session; 024import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; 025import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; 026import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; 027import org.eclipse.jetty.websocket.api.annotations.OnWebSocketFrame; 028import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; 029import org.eclipse.jetty.websocket.api.annotations.WebSocket; 030import org.eclipse.jetty.websocket.api.extensions.Frame; 031import org.eclipse.jetty.websocket.common.OpCode; 032 033import org.springframework.util.Assert; 034import org.springframework.web.socket.BinaryMessage; 035import org.springframework.web.socket.CloseStatus; 036import org.springframework.web.socket.PongMessage; 037import org.springframework.web.socket.TextMessage; 038import org.springframework.web.socket.WebSocketHandler; 039import org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator; 040 041/** 042 * Adapts {@link WebSocketHandler} to the Jetty 9 WebSocket API. 043 * 044 * @author Rossen Stoyanchev 045 * @since 4.0 046 */ 047@WebSocket 048public class JettyWebSocketHandlerAdapter { 049 050 private static final ByteBuffer EMPTY_PAYLOAD = ByteBuffer.wrap(new byte[0]); 051 052 private static final Log logger = LogFactory.getLog(JettyWebSocketHandlerAdapter.class); 053 054 055 private final WebSocketHandler webSocketHandler; 056 057 private final JettyWebSocketSession wsSession; 058 059 060 public JettyWebSocketHandlerAdapter(WebSocketHandler webSocketHandler, JettyWebSocketSession wsSession) { 061 Assert.notNull(webSocketHandler, "WebSocketHandler must not be null"); 062 Assert.notNull(wsSession, "WebSocketSession must not be null"); 063 this.webSocketHandler = webSocketHandler; 064 this.wsSession = wsSession; 065 } 066 067 068 @OnWebSocketConnect 069 public void onWebSocketConnect(Session session) { 070 try { 071 this.wsSession.initializeNativeSession(session); 072 this.webSocketHandler.afterConnectionEstablished(this.wsSession); 073 } 074 catch (Throwable ex) { 075 ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger); 076 } 077 } 078 079 @OnWebSocketMessage 080 public void onWebSocketText(String payload) { 081 TextMessage message = new TextMessage(payload); 082 try { 083 this.webSocketHandler.handleMessage(this.wsSession, message); 084 } 085 catch (Throwable ex) { 086 ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger); 087 } 088 } 089 090 @OnWebSocketMessage 091 public void onWebSocketBinary(byte[] payload, int offset, int length) { 092 BinaryMessage message = new BinaryMessage(payload, offset, length, true); 093 try { 094 this.webSocketHandler.handleMessage(this.wsSession, message); 095 } 096 catch (Throwable ex) { 097 ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger); 098 } 099 } 100 101 @OnWebSocketFrame 102 public void onWebSocketFrame(Frame frame) { 103 if (OpCode.PONG == frame.getOpCode()) { 104 ByteBuffer payload = frame.getPayload() != null ? frame.getPayload() : EMPTY_PAYLOAD; 105 PongMessage message = new PongMessage(payload); 106 try { 107 this.webSocketHandler.handleMessage(this.wsSession, message); 108 } 109 catch (Throwable ex) { 110 ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger); 111 } 112 } 113 } 114 115 @OnWebSocketClose 116 public void onWebSocketClose(int statusCode, String reason) { 117 CloseStatus closeStatus = new CloseStatus(statusCode, reason); 118 try { 119 this.webSocketHandler.afterConnectionClosed(this.wsSession, closeStatus); 120 } 121 catch (Throwable ex) { 122 if (logger.isWarnEnabled()) { 123 logger.warn("Unhandled exception after connection closed for " + this, ex); 124 } 125 } 126 } 127 128 @OnWebSocketError 129 public void onWebSocketError(Throwable cause) { 130 try { 131 this.webSocketHandler.handleTransportError(this.wsSession, cause); 132 } 133 catch (Throwable ex) { 134 ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger); 135 } 136 } 137 138}