После вчерашнего поста о реализации передачи сообщений между 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.
Комментариев нет:
Отправить комментарий