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}