После вчерашнего поста о реализации передачи сообщений между JMS и WebSocket силами Java EE я решил сообразить аналогичную реализацию при помощи Spring Framework. Для этого потребуется сервер приложений, реализующий JSR 356 (Apache Tomcat версии 7.0.47 и выше, GlassFish 4, WildFly 8 или любой другой), JMS-брокер, в качестве которого я выбрал Apache ActiveMQ, и браузер, реализующий RFC 6455.
Spring-beans XML
Этого достаточно, что бы отправлять сообщения из JMS клиентам WebSocket.
Сообщения из очереди websocket.out будут отображаться в консоли веб-браузера.
Для отправки сообщений в обратном направлении, из WebSocket в JMS понадобится ещё один класс.
JmsMessageSender.java
pom.xml
Для нашего проекта потребуются следующие зависимости:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>4.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>
<version>5.10.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>4.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
Если используется Tomcat, то понадобится реализация Java API for JSON Processing:
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.0.4</version>
</dependency>
Реализация серверной стороны
Как я уже упомянул в предыдущем посте, реализовать передачу сообщений между JMS и WebSocket при помощи Spring очень просто. Для этого понадобится класс, обрабатывающий события WebSocket (хэндлер), а так же JMS-листнер, в который хэндлер будет внедрён.
WebSocket-хэндлер (ChatHandler.java):
public class ChatHandler extends TextWebSocketHandler {
private final static Logger logger = LoggerFactory.getLogger(ChatHandler.class);
private final static Set<WebSocketSession> sessions = Collections.synchronizedSet(new HashSet<WebSocketSession>());
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
logger.info("User has left the chat");
super.afterConnectionClosed(session, status);
sessions.remove(session);
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
logger.info("New user has joined the chat");
super.afterConnectionEstablished(session);
sessions.add(session);
}
public void sendMessage(final String message) throws IOException {
logger.info("Sending message to all participants");
for (WebSocketSession session : sessions) {
if (session.isOpen()) {
session.sendMessage(new TextMessage(message));
} else {
sessions.remove(session);
}
}
}
}
JMS-листнер (WebSocketOutMessageListener.java):
public class WebSocketOutMessageListener implements MessageListener {
private static final Logger logger = LoggerFactory.getLogger(WebSocketOutMessageListener.class);
private ChatHandler webSocketHandler;
public void setWebSocketHandler(ChatHandler webSocketHandler) {
this.webSocketHandler = webSocketHandler;
}
@Override
public void onMessage(Message message) {
if (message instanceof TextMessage) {
TextMessage textMessage = (TextMessage) message;
try {
webSocketHandler.sendMessage(textMessage.getText());
} catch (JMSException | IOException ex) {
logger.error(ex.getMessage(), ex);
}
}
}
}
Spring-beans XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
http://www.springframework.org/schema/websocket/spring-websocket.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms.xsd">
<bean class="org.apache.activemq.ActiveMQConnectionFactory"
id="activeMQConnectionFactory"
p:brokerURL="tcp://localhost:61616"/>
<websocket:handlers>
<websocket:mapping path="/chat" handler="chatHandler"/>
</websocket:handlers>
<bean class="org.acruxsource.sandbox.spring.jmstows.websocket.ChatHandler"
id="chatHandler"/>
<bean class="org.acruxsource.sandbox.spring.jmstows.jms.WebSocketOutMessageListener"
id="webSocketOutMessageListener"
p:webSocketHandler-ref="chatHandler"
lazy-init="false"/>
<jms:listener-container connection-factory="activeMQConnectionFactory" container-type="default">
<jms:listener destination="websocket.out" ref="webSocketOutMessageListener" method="onMessage"/>
</jms:listener-container>
</beans>
Этого достаточно, что бы отправлять сообщения из JMS клиентам WebSocket.
Реализация клиентской стороны
Для клиентской стороны достаточно небольшой странички:
index.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>WebSocket test</title>
</head>
<body>
<script type="text/javascript">
var ws;
if ('WebSocket' in window) {
console.log('This browser supports websocket');
ws = new WebSocket('ws://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/sandbox/chat');
ws.onmessage = function(message) {
console.log(message);
};
}
</script>
</body>
</html>
Сообщения из очереди websocket.out будут отображаться в консоли веб-браузера.
Для отправки сообщений в обратном направлении, из WebSocket в JMS понадобится ещё один класс.
JmsMessageSender.java
public class JmsMessageSender {
private JmsTemplate jmsTemplate;
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public void sendMessage(String destination, final String message) {
jmsTemplate.send(destination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
TextMessage textMessage = session.createTextMessage();
textMessage.setText(message);
return textMessage;
}
});
}
}
Объект этого класса нужно внедрить в WebSocket-хэндлер и использовать его для отправки сообщений в очередь
ChatHandler.java
ChatHandler.java
private JmsMessageSender jmsMessageSender;
public void setJmsMessageSender(JmsMessageSender jmsMessageSender) {
this.jmsMessageSender = jmsMessageSender;
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
logger.info("New incoming message: " + message.getPayload());
jmsMessageSender.sendMessage("websocket.in", message.getPayload());
}
Остаётся изменить spring-beans:
<bean class="org.springframework.jms.connection.SingleConnectionFactory"
id="jmsProducerConnectionFactory"
p:targetConnectionFactory-ref="activeMQConnectionFactory"/>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"
p:connectionFactory-ref="jmsProducerConnectionFactory"/>
<bean class="org.acruxsource.sandbox.spring.jmstows.websocket.ChatHandler"
id="chatHandler"
p:jmsMessageSender-ref="jmsMessageSender"/>
<bean class="org.acruxsource.sandbox.spring.jmstows.jms.JmsMessageSender"
id="jmsMessageSender"
p:jmsTemplate-ref="jmsTemplate"/>
Теперь для теста из консоли браузера можно отправить сообщение: ws.send("Some test message").
В следующих постах я более основательно опишу работу с WebSocket.
В следующих постах я более основательно опишу работу с WebSocket.
Комментариев нет:
Отправить комментарий