001/* 002 * Copyright 2002-2020 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.standard; 018 019import java.nio.ByteBuffer; 020 021import javax.websocket.CloseReason; 022import javax.websocket.Endpoint; 023import javax.websocket.EndpointConfig; 024import javax.websocket.MessageHandler; 025 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028 029import org.springframework.util.Assert; 030import org.springframework.web.socket.BinaryMessage; 031import org.springframework.web.socket.CloseStatus; 032import org.springframework.web.socket.PongMessage; 033import org.springframework.web.socket.TextMessage; 034import org.springframework.web.socket.WebSocketHandler; 035import org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator; 036 037/** 038 * Adapts a {@link WebSocketHandler} to the standard WebSocket for Java API. 039 * 040 * @author Rossen Stoyanchev 041 * @since 4.0 042 */ 043public class StandardWebSocketHandlerAdapter extends Endpoint { 044 045 private final Log logger = LogFactory.getLog(StandardWebSocketHandlerAdapter.class); 046 047 private final WebSocketHandler handler; 048 049 private final StandardWebSocketSession wsSession; 050 051 052 public StandardWebSocketHandlerAdapter(WebSocketHandler handler, StandardWebSocketSession wsSession) { 053 Assert.notNull(handler, "WebSocketHandler must not be null"); 054 Assert.notNull(wsSession, "WebSocketSession must not be null"); 055 this.handler = handler; 056 this.wsSession = wsSession; 057 } 058 059 060 @Override 061 public void onOpen(final javax.websocket.Session session, EndpointConfig config) { 062 this.wsSession.initializeNativeSession(session); 063 064 // The following inner classes need to remain since lambdas would not retain their 065 // declared generic types (which need to be seen by the underlying WebSocket engine) 066 067 if (this.handler.supportsPartialMessages()) { 068 session.addMessageHandler(new MessageHandler.Partial<String>() { 069 @Override 070 public void onMessage(String message, boolean isLast) { 071 handleTextMessage(session, message, isLast); 072 } 073 }); 074 session.addMessageHandler(new MessageHandler.Partial<ByteBuffer>() { 075 @Override 076 public void onMessage(ByteBuffer message, boolean isLast) { 077 handleBinaryMessage(session, message, isLast); 078 } 079 }); 080 } 081 else { 082 session.addMessageHandler(new MessageHandler.Whole<String>() { 083 @Override 084 public void onMessage(String message) { 085 handleTextMessage(session, message, true); 086 } 087 }); 088 session.addMessageHandler(new MessageHandler.Whole<ByteBuffer>() { 089 @Override 090 public void onMessage(ByteBuffer message) { 091 handleBinaryMessage(session, message, true); 092 } 093 }); 094 } 095 096 session.addMessageHandler(new MessageHandler.Whole<javax.websocket.PongMessage>() { 097 @Override 098 public void onMessage(javax.websocket.PongMessage message) { 099 handlePongMessage(session, message.getApplicationData()); 100 } 101 }); 102 103 try { 104 this.handler.afterConnectionEstablished(this.wsSession); 105 } 106 catch (Exception ex) { 107 ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger); 108 } 109 } 110 111 private void handleTextMessage(javax.websocket.Session session, String payload, boolean isLast) { 112 TextMessage textMessage = new TextMessage(payload, isLast); 113 try { 114 this.handler.handleMessage(this.wsSession, textMessage); 115 } 116 catch (Exception ex) { 117 ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger); 118 } 119 } 120 121 private void handleBinaryMessage(javax.websocket.Session session, ByteBuffer payload, boolean isLast) { 122 BinaryMessage binaryMessage = new BinaryMessage(payload, isLast); 123 try { 124 this.handler.handleMessage(this.wsSession, binaryMessage); 125 } 126 catch (Exception ex) { 127 ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger); 128 } 129 } 130 131 private void handlePongMessage(javax.websocket.Session session, ByteBuffer payload) { 132 PongMessage pongMessage = new PongMessage(payload); 133 try { 134 this.handler.handleMessage(this.wsSession, pongMessage); 135 } 136 catch (Exception ex) { 137 ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger); 138 } 139 } 140 141 @Override 142 public void onClose(javax.websocket.Session session, CloseReason reason) { 143 CloseStatus closeStatus = new CloseStatus(reason.getCloseCode().getCode(), reason.getReasonPhrase()); 144 try { 145 this.handler.afterConnectionClosed(this.wsSession, closeStatus); 146 } 147 catch (Exception ex) { 148 if (logger.isWarnEnabled()) { 149 logger.warn("Unhandled on-close exception for " + this.wsSession, ex); 150 } 151 } 152 } 153 154 @Override 155 public void onError(javax.websocket.Session session, Throwable exception) { 156 try { 157 this.handler.handleTransportError(this.wsSession, exception); 158 } 159 catch (Exception ex) { 160 ExceptionWebSocketHandlerDecorator.tryCloseWithError(this.wsSession, ex, logger); 161 } 162 } 163 164}