3 changed files with 249 additions and 296 deletions
@ -1,100 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2002-2013 the original author or authors. |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package org.springframework.web.messaging.stomp.socket; |
|
||||||
|
|
||||||
import java.nio.charset.Charset; |
|
||||||
import java.util.Map; |
|
||||||
import java.util.concurrent.ConcurrentHashMap; |
|
||||||
|
|
||||||
import org.springframework.messaging.GenericMessage; |
|
||||||
import org.springframework.messaging.Message; |
|
||||||
import org.springframework.web.messaging.stomp.StompCommand; |
|
||||||
import org.springframework.web.messaging.stomp.StompHeaders; |
|
||||||
import org.springframework.web.messaging.stomp.support.StompMessageConverter; |
|
||||||
import org.springframework.web.socket.CloseStatus; |
|
||||||
import org.springframework.web.socket.TextMessage; |
|
||||||
import org.springframework.web.socket.WebSocketSession; |
|
||||||
import org.springframework.web.socket.adapter.TextWebSocketHandlerAdapter; |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* @author Rossen Stoyanchev |
|
||||||
* @since 4.0 |
|
||||||
*/ |
|
||||||
public abstract class AbstractStompWebSocketHandler extends TextWebSocketHandlerAdapter { |
|
||||||
|
|
||||||
private final StompMessageConverter stompMessageConverter = new StompMessageConverter(); |
|
||||||
|
|
||||||
private final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<String, WebSocketSession>(); |
|
||||||
|
|
||||||
|
|
||||||
public StompMessageConverter getStompMessageConverter() { |
|
||||||
return this.stompMessageConverter; |
|
||||||
} |
|
||||||
|
|
||||||
protected WebSocketSession getWebSocketSession(String sessionId) { |
|
||||||
return this.sessions.get(sessionId); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void afterConnectionEstablished(WebSocketSession session) throws Exception { |
|
||||||
this.sessions.put(session.getId(), session); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected void handleTextMessage(WebSocketSession session, TextMessage textMessage) { |
|
||||||
try { |
|
||||||
String payload = textMessage.getPayload(); |
|
||||||
Message<byte[]> message = this.stompMessageConverter.toMessage(payload, session.getId()); |
|
||||||
|
|
||||||
// TODO: validate size limits
|
|
||||||
// http://stomp.github.io/stomp-specification-1.2.html#Size_Limits
|
|
||||||
|
|
||||||
handleStompMessage(session, message); |
|
||||||
|
|
||||||
// TODO: send RECEIPT message if incoming message has "receipt" header
|
|
||||||
// http://stomp.github.io/stomp-specification-1.2.html#Header_receipt
|
|
||||||
|
|
||||||
} |
|
||||||
catch (Throwable error) { |
|
||||||
sendErrorMessage(session, error); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
protected void sendErrorMessage(WebSocketSession session, Throwable error) { |
|
||||||
|
|
||||||
StompHeaders stompHeaders = StompHeaders.create(StompCommand.ERROR); |
|
||||||
stompHeaders.setMessage(error.getMessage()); |
|
||||||
|
|
||||||
Message<byte[]> errorMessage = new GenericMessage<byte[]>(new byte[0], stompHeaders.toMessageHeaders()); |
|
||||||
byte[] bytes = this.stompMessageConverter.fromMessage(errorMessage); |
|
||||||
|
|
||||||
try { |
|
||||||
session.sendMessage(new TextMessage(new String(bytes, Charset.forName("UTF-8")))); |
|
||||||
} |
|
||||||
catch (Throwable t) { |
|
||||||
// ignore
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
protected abstract void handleStompMessage(WebSocketSession session, Message<byte[]> message); |
|
||||||
|
|
||||||
@Override |
|
||||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { |
|
||||||
this.sessions.remove(session.getId()); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,196 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2002-2013 the original author or authors. |
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package org.springframework.web.messaging.stomp.socket; |
|
||||||
|
|
||||||
import java.io.IOException; |
|
||||||
import java.nio.charset.Charset; |
|
||||||
import java.util.List; |
|
||||||
import java.util.Map; |
|
||||||
import java.util.Set; |
|
||||||
|
|
||||||
import org.apache.commons.logging.Log; |
|
||||||
import org.apache.commons.logging.LogFactory; |
|
||||||
import org.springframework.http.MediaType; |
|
||||||
import org.springframework.messaging.GenericMessage; |
|
||||||
import org.springframework.messaging.Message; |
|
||||||
import org.springframework.web.messaging.MessageType; |
|
||||||
import org.springframework.web.messaging.converter.CompositeMessageConverter; |
|
||||||
import org.springframework.web.messaging.converter.MessageConverter; |
|
||||||
import org.springframework.web.messaging.event.EventBus; |
|
||||||
import org.springframework.web.messaging.event.EventConsumer; |
|
||||||
import org.springframework.web.messaging.service.AbstractMessageService; |
|
||||||
import org.springframework.web.messaging.stomp.StompCommand; |
|
||||||
import org.springframework.web.messaging.stomp.StompConversionException; |
|
||||||
import org.springframework.web.messaging.stomp.StompHeaders; |
|
||||||
import org.springframework.web.socket.CloseStatus; |
|
||||||
import org.springframework.web.socket.TextMessage; |
|
||||||
import org.springframework.web.socket.WebSocketSession; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author Gary Russell |
|
||||||
* @author Rossen Stoyanchev |
|
||||||
* @since 4.0 |
|
||||||
*/ |
|
||||||
public class DefaultStompWebSocketHandler extends AbstractStompWebSocketHandler { |
|
||||||
|
|
||||||
private static Log logger = LogFactory.getLog(DefaultStompWebSocketHandler.class); |
|
||||||
|
|
||||||
|
|
||||||
private final EventBus eventBus; |
|
||||||
|
|
||||||
private MessageConverter payloadConverter = new CompositeMessageConverter(null); |
|
||||||
|
|
||||||
|
|
||||||
public DefaultStompWebSocketHandler(EventBus eventBus) { |
|
||||||
|
|
||||||
this.eventBus = eventBus; |
|
||||||
|
|
||||||
this.eventBus.registerConsumer(AbstractMessageService.SERVER_TO_CLIENT_MESSAGE_KEY, |
|
||||||
new EventConsumer<Message<?>>() { |
|
||||||
@Override |
|
||||||
public void accept(Message<?> message) { |
|
||||||
|
|
||||||
StompHeaders stompHeaders = StompHeaders.fromMessageHeaders(message.getHeaders()); |
|
||||||
stompHeaders.setStompCommandIfNotSet(StompCommand.MESSAGE); |
|
||||||
|
|
||||||
if (StompCommand.CONNECTED.equals(stompHeaders.getStompCommand())) { |
|
||||||
// Ignore for now since we already sent it
|
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
String sessionId = stompHeaders.getSessionId(); |
|
||||||
if (sessionId == null) { |
|
||||||
logger.error("Cannot send message without a session id: " + message); |
|
||||||
} |
|
||||||
WebSocketSession session = getWebSocketSession(sessionId); |
|
||||||
|
|
||||||
byte[] payload; |
|
||||||
try { |
|
||||||
MediaType contentType = stompHeaders.getContentType(); |
|
||||||
payload = payloadConverter.convertToPayload(message.getPayload(), contentType); |
|
||||||
} |
|
||||||
catch (Exception e) { |
|
||||||
logger.error("Failed to send " + message, e); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
try { |
|
||||||
Map<String, Object> messageHeaders = stompHeaders.toMessageHeaders(); |
|
||||||
Message<byte[]> byteMessage = new GenericMessage<byte[]>(payload, messageHeaders); |
|
||||||
byte[] bytes = getStompMessageConverter().fromMessage(byteMessage); |
|
||||||
session.sendMessage(new TextMessage(new String(bytes, Charset.forName("UTF-8")))); |
|
||||||
} |
|
||||||
catch (Throwable t) { |
|
||||||
sendErrorMessage(session, t); |
|
||||||
} |
|
||||||
finally { |
|
||||||
if (StompCommand.ERROR.equals(stompHeaders.getStompCommand())) { |
|
||||||
try { |
|
||||||
session.close(CloseStatus.PROTOCOL_ERROR); |
|
||||||
} |
|
||||||
catch (IOException e) { |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
public void setMessageConverters(List<MessageConverter> converters) { |
|
||||||
this.payloadConverter = new CompositeMessageConverter(converters); |
|
||||||
} |
|
||||||
|
|
||||||
public void handleStompMessage(final WebSocketSession session, Message<byte[]> message) { |
|
||||||
|
|
||||||
if (logger.isTraceEnabled()) { |
|
||||||
logger.trace("Processing STOMP message: " + message); |
|
||||||
} |
|
||||||
|
|
||||||
try { |
|
||||||
StompHeaders stompHeaders = StompHeaders.fromMessageHeaders(message.getHeaders()); |
|
||||||
MessageType messageType = stompHeaders.getMessageType(); |
|
||||||
if (MessageType.CONNECT.equals(messageType)) { |
|
||||||
handleConnect(session, message); |
|
||||||
} |
|
||||||
else if (MessageType.MESSAGE.equals(messageType)) { |
|
||||||
handleMessage(message); |
|
||||||
} |
|
||||||
else if (MessageType.SUBSCRIBE.equals(messageType)) { |
|
||||||
handleSubscribe(message); |
|
||||||
} |
|
||||||
else if (MessageType.UNSUBSCRIBE.equals(messageType)) { |
|
||||||
handleUnsubscribe(message); |
|
||||||
} |
|
||||||
else if (MessageType.DISCONNECT.equals(messageType)) { |
|
||||||
handleDisconnect(message); |
|
||||||
} |
|
||||||
this.eventBus.send(AbstractMessageService.CLIENT_TO_SERVER_MESSAGE_KEY, message); |
|
||||||
} |
|
||||||
catch (Throwable t) { |
|
||||||
logger.error("Terminating STOMP session due to failure to send message: ", t); |
|
||||||
sendErrorMessage(session, t); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
protected void handleConnect(final WebSocketSession session, Message<byte[]> message) throws IOException { |
|
||||||
|
|
||||||
StompHeaders connectStompHeaders = StompHeaders.fromMessageHeaders(message.getHeaders()); |
|
||||||
StompHeaders connectedStompHeaders = StompHeaders.create(StompCommand.CONNECTED); |
|
||||||
|
|
||||||
Set<String> acceptVersions = connectStompHeaders.getAcceptVersion(); |
|
||||||
if (acceptVersions.contains("1.2")) { |
|
||||||
connectedStompHeaders.setAcceptVersion("1.2"); |
|
||||||
} |
|
||||||
else if (acceptVersions.contains("1.1")) { |
|
||||||
connectedStompHeaders.setAcceptVersion("1.1"); |
|
||||||
} |
|
||||||
else if (acceptVersions.isEmpty()) { |
|
||||||
// 1.0
|
|
||||||
} |
|
||||||
else { |
|
||||||
throw new StompConversionException("Unsupported version '" + acceptVersions + "'"); |
|
||||||
} |
|
||||||
connectedStompHeaders.setHeartbeat(0,0); // TODO
|
|
||||||
|
|
||||||
// TODO: security
|
|
||||||
|
|
||||||
Message<byte[]> connectedMessage = new GenericMessage<byte[]>(new byte[0], connectedStompHeaders.toMessageHeaders()); |
|
||||||
byte[] bytes = getStompMessageConverter().fromMessage(connectedMessage); |
|
||||||
session.sendMessage(new TextMessage(new String(bytes, Charset.forName("UTF-8")))); |
|
||||||
} |
|
||||||
|
|
||||||
protected void handleSubscribe(Message<byte[]> message) { |
|
||||||
// TODO: need a way to communicate back if subscription was successfully created or
|
|
||||||
// not in which case an ERROR should be sent back and close the connection
|
|
||||||
// http://stomp.github.io/stomp-specification-1.2.html#SUBSCRIBE
|
|
||||||
} |
|
||||||
|
|
||||||
protected void handleUnsubscribe(Message<byte[]> message) { |
|
||||||
} |
|
||||||
|
|
||||||
protected void handleMessage(Message<byte[]> stompMessage) { |
|
||||||
} |
|
||||||
|
|
||||||
protected void handleDisconnect(Message<byte[]> stompMessage) { |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { |
|
||||||
eventBus.send(AbstractMessageService.CLIENT_CONNECTION_CLOSED_KEY, session.getId()); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,249 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2002-2013 the original author or authors. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package org.springframework.web.messaging.stomp.support; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.charset.Charset; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Set; |
||||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||||
|
|
||||||
|
import org.apache.commons.logging.Log; |
||||||
|
import org.apache.commons.logging.LogFactory; |
||||||
|
import org.springframework.http.MediaType; |
||||||
|
import org.springframework.messaging.GenericMessage; |
||||||
|
import org.springframework.messaging.Message; |
||||||
|
import org.springframework.web.messaging.MessageType; |
||||||
|
import org.springframework.web.messaging.converter.CompositeMessageConverter; |
||||||
|
import org.springframework.web.messaging.converter.MessageConverter; |
||||||
|
import org.springframework.web.messaging.event.EventBus; |
||||||
|
import org.springframework.web.messaging.event.EventConsumer; |
||||||
|
import org.springframework.web.messaging.service.AbstractMessageService; |
||||||
|
import org.springframework.web.messaging.stomp.StompCommand; |
||||||
|
import org.springframework.web.messaging.stomp.StompConversionException; |
||||||
|
import org.springframework.web.messaging.stomp.StompHeaders; |
||||||
|
import org.springframework.web.socket.CloseStatus; |
||||||
|
import org.springframework.web.socket.TextMessage; |
||||||
|
import org.springframework.web.socket.WebSocketSession; |
||||||
|
import org.springframework.web.socket.adapter.TextWebSocketHandlerAdapter; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @author Rossen Stoyanchev |
||||||
|
* @since 4.0 |
||||||
|
*/ |
||||||
|
public class StompWebSocketHandler extends TextWebSocketHandlerAdapter { |
||||||
|
|
||||||
|
private static Log logger = LogFactory.getLog(StompWebSocketHandler.class); |
||||||
|
|
||||||
|
private final StompMessageConverter stompMessageConverter = new StompMessageConverter(); |
||||||
|
|
||||||
|
private final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<String, WebSocketSession>(); |
||||||
|
|
||||||
|
private final EventBus eventBus; |
||||||
|
|
||||||
|
private MessageConverter payloadConverter = new CompositeMessageConverter(null); |
||||||
|
|
||||||
|
|
||||||
|
public StompWebSocketHandler(EventBus eventBus) { |
||||||
|
this.eventBus = eventBus; |
||||||
|
this.eventBus.registerConsumer(AbstractMessageService.SERVER_TO_CLIENT_MESSAGE_KEY, |
||||||
|
new ClientMessageConsumer()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void setMessageConverters(List<MessageConverter> converters) { |
||||||
|
this.payloadConverter = new CompositeMessageConverter(converters); |
||||||
|
} |
||||||
|
|
||||||
|
public StompMessageConverter getStompMessageConverter() { |
||||||
|
return this.stompMessageConverter; |
||||||
|
} |
||||||
|
|
||||||
|
protected WebSocketSession getWebSocketSession(String sessionId) { |
||||||
|
return this.sessions.get(sessionId); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void afterConnectionEstablished(WebSocketSession session) throws Exception { |
||||||
|
this.sessions.put(session.getId(), session); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void handleTextMessage(WebSocketSession session, TextMessage textMessage) { |
||||||
|
try { |
||||||
|
String payload = textMessage.getPayload(); |
||||||
|
Message<byte[]> message = this.stompMessageConverter.toMessage(payload, session.getId()); |
||||||
|
|
||||||
|
// TODO: validate size limits
|
||||||
|
// http://stomp.github.io/stomp-specification-1.2.html#Size_Limits
|
||||||
|
|
||||||
|
if (logger.isTraceEnabled()) { |
||||||
|
logger.trace("Processing STOMP message: " + message); |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
StompHeaders stompHeaders = StompHeaders.fromMessageHeaders(message.getHeaders()); |
||||||
|
MessageType messageType = stompHeaders.getMessageType(); |
||||||
|
if (MessageType.CONNECT.equals(messageType)) { |
||||||
|
handleConnect(session, message); |
||||||
|
} |
||||||
|
else if (MessageType.MESSAGE.equals(messageType)) { |
||||||
|
handleMessage(message); |
||||||
|
} |
||||||
|
else if (MessageType.SUBSCRIBE.equals(messageType)) { |
||||||
|
handleSubscribe(message); |
||||||
|
} |
||||||
|
else if (MessageType.UNSUBSCRIBE.equals(messageType)) { |
||||||
|
handleUnsubscribe(message); |
||||||
|
} |
||||||
|
else if (MessageType.DISCONNECT.equals(messageType)) { |
||||||
|
handleDisconnect(message); |
||||||
|
} |
||||||
|
this.eventBus.send(AbstractMessageService.CLIENT_TO_SERVER_MESSAGE_KEY, message); |
||||||
|
} |
||||||
|
catch (Throwable t) { |
||||||
|
logger.error("Terminating STOMP session due to failure to send message: ", t); |
||||||
|
sendErrorMessage(session, t); |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: send RECEIPT message if incoming message has "receipt" header
|
||||||
|
// http://stomp.github.io/stomp-specification-1.2.html#Header_receipt
|
||||||
|
|
||||||
|
} |
||||||
|
catch (Throwable error) { |
||||||
|
sendErrorMessage(session, error); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void handleConnect(final WebSocketSession session, Message<byte[]> message) throws IOException { |
||||||
|
|
||||||
|
StompHeaders connectStompHeaders = StompHeaders.fromMessageHeaders(message.getHeaders()); |
||||||
|
StompHeaders connectedStompHeaders = StompHeaders.create(StompCommand.CONNECTED); |
||||||
|
|
||||||
|
Set<String> acceptVersions = connectStompHeaders.getAcceptVersion(); |
||||||
|
if (acceptVersions.contains("1.2")) { |
||||||
|
connectedStompHeaders.setAcceptVersion("1.2"); |
||||||
|
} |
||||||
|
else if (acceptVersions.contains("1.1")) { |
||||||
|
connectedStompHeaders.setAcceptVersion("1.1"); |
||||||
|
} |
||||||
|
else if (acceptVersions.isEmpty()) { |
||||||
|
// 1.0
|
||||||
|
} |
||||||
|
else { |
||||||
|
throw new StompConversionException("Unsupported version '" + acceptVersions + "'"); |
||||||
|
} |
||||||
|
connectedStompHeaders.setHeartbeat(0,0); // TODO
|
||||||
|
|
||||||
|
// TODO: security
|
||||||
|
|
||||||
|
Message<byte[]> connectedMessage = new GenericMessage<byte[]>(new byte[0], connectedStompHeaders.toMessageHeaders()); |
||||||
|
byte[] bytes = getStompMessageConverter().fromMessage(connectedMessage); |
||||||
|
session.sendMessage(new TextMessage(new String(bytes, Charset.forName("UTF-8")))); |
||||||
|
} |
||||||
|
|
||||||
|
protected void handleSubscribe(Message<byte[]> message) { |
||||||
|
// TODO: need a way to communicate back if subscription was successfully created or
|
||||||
|
// not in which case an ERROR should be sent back and close the connection
|
||||||
|
// http://stomp.github.io/stomp-specification-1.2.html#SUBSCRIBE
|
||||||
|
} |
||||||
|
|
||||||
|
protected void handleUnsubscribe(Message<byte[]> message) { |
||||||
|
} |
||||||
|
|
||||||
|
protected void handleMessage(Message<byte[]> stompMessage) { |
||||||
|
} |
||||||
|
|
||||||
|
protected void handleDisconnect(Message<byte[]> stompMessage) { |
||||||
|
} |
||||||
|
|
||||||
|
protected void sendErrorMessage(WebSocketSession session, Throwable error) { |
||||||
|
|
||||||
|
StompHeaders stompHeaders = StompHeaders.create(StompCommand.ERROR); |
||||||
|
stompHeaders.setMessage(error.getMessage()); |
||||||
|
|
||||||
|
Message<byte[]> errorMessage = new GenericMessage<byte[]>(new byte[0], stompHeaders.toMessageHeaders()); |
||||||
|
byte[] bytes = this.stompMessageConverter.fromMessage(errorMessage); |
||||||
|
|
||||||
|
try { |
||||||
|
session.sendMessage(new TextMessage(new String(bytes, Charset.forName("UTF-8")))); |
||||||
|
} |
||||||
|
catch (Throwable t) { |
||||||
|
// ignore
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { |
||||||
|
this.sessions.remove(session.getId()); |
||||||
|
eventBus.send(AbstractMessageService.CLIENT_CONNECTION_CLOSED_KEY, session.getId()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private final class ClientMessageConsumer implements EventConsumer<Message<?>> { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void accept(Message<?> message) { |
||||||
|
|
||||||
|
StompHeaders stompHeaders = StompHeaders.fromMessageHeaders(message.getHeaders()); |
||||||
|
stompHeaders.setStompCommandIfNotSet(StompCommand.MESSAGE); |
||||||
|
|
||||||
|
if (StompCommand.CONNECTED.equals(stompHeaders.getStompCommand())) { |
||||||
|
// Ignore for now since we already sent it
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
String sessionId = stompHeaders.getSessionId(); |
||||||
|
if (sessionId == null) { |
||||||
|
logger.error("Cannot send message without a session id: " + message); |
||||||
|
} |
||||||
|
WebSocketSession session = getWebSocketSession(sessionId); |
||||||
|
|
||||||
|
byte[] payload; |
||||||
|
try { |
||||||
|
MediaType contentType = stompHeaders.getContentType(); |
||||||
|
payload = payloadConverter.convertToPayload(message.getPayload(), contentType); |
||||||
|
} |
||||||
|
catch (Exception e) { |
||||||
|
logger.error("Failed to send " + message, e); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
Map<String, Object> messageHeaders = stompHeaders.toMessageHeaders(); |
||||||
|
Message<byte[]> byteMessage = new GenericMessage<byte[]>(payload, messageHeaders); |
||||||
|
byte[] bytes = getStompMessageConverter().fromMessage(byteMessage); |
||||||
|
session.sendMessage(new TextMessage(new String(bytes, Charset.forName("UTF-8")))); |
||||||
|
} |
||||||
|
catch (Throwable t) { |
||||||
|
sendErrorMessage(session, t); |
||||||
|
} |
||||||
|
finally { |
||||||
|
if (StompCommand.ERROR.equals(stompHeaders.getStompCommand())) { |
||||||
|
try { |
||||||
|
session.close(CloseStatus.PROTOCOL_ERROR); |
||||||
|
} |
||||||
|
catch (IOException e) { |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue