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