INTS-137: Add STOMP Chat Application
JIRA: https://jira.spring.io/browse/INTSAMPLES-137 * Upgrade to Boot 1.1.7 * Fix several Boot apps to get deal with `args`, e.g. `--debug` option can be useful for Boot * Provide a loca copy for JavaScript files (`sock.js` and `stomp.js`) to avoid extra Internet connection on testing INTS-137-2: Make JavaScript cross-browser Some additional polishing to the server config, like `logging-channel-adapter` for server message flow
This commit is contained in:
committed by
Gary Russell
parent
a5c12343c6
commit
63232c2974
50
applications/stomp-chat/README.md
Normal file
50
applications/stomp-chat/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
WebSockets Stomp Chat Sample
|
||||
==============
|
||||
|
||||
This application demonstrates the Web chat based on STOMP WebSocket sub-protocol with Spring Integration Adapters.
|
||||
## Server
|
||||
|
||||
The server is presented only with a single `org.springframework.integration.samples.websocket.standard.server.Application`
|
||||
class, which is based on the Spring Boot AutoConfiguration and Spring Integration xml configuration `@ImportResource`.
|
||||
It is a `main` and starts an embedded Tomcat server on the default `8080` port.
|
||||
The WebSocket endpoint is mapped to the `/chat` path.
|
||||
|
||||
The server also can be run from Gradle `gradlew :stomp-chat:run`
|
||||
|
||||
The server application demonstrates how Spring Integration can be used as a STOMP Broker.
|
||||
|
||||
1. `webSocketSessionStore` - the `SimpleMetadataStore` to keep track of `WebSocketSession` and its `user`.
|
||||
2. `chatMessagesStore` - the `SimpleMessageStore` to store messages for chat rooms.
|
||||
3. `chatRoomSessions` - the `Map<String, Tuple2<String, String>>` to keep track of `WebSocketSession` `subscriptions`
|
||||
to the concrete chat room - STOMP `destination`.
|
||||
4. `<int-event:inbound-channel-adapter channel="routeStompEvents">` is subscribed to the `AbstractSubProtocolEvent`
|
||||
type to handle STOMP sub-protocol events.
|
||||
5. `<int:payload-type-router input-channel="routeStompEvents">` is mapped to the appropriate `AbstractSubProtocolEvent`
|
||||
type to provide the specific integration flow for each event type.
|
||||
6. `<int-websocket:inbound-channel-adapter>` receives STOMP messages, store them to the appropriate `messageGroup`
|
||||
(according to the STOMP `destination`) and forward to the `<int-websocket:outbound-channel-adapter>` to send to
|
||||
each `WebSocketSession` subscribed to that STOMP `destination` - chat room.
|
||||
|
||||
## Client
|
||||
|
||||
The `index.html` in the `src/main/resources/static` directory of this project demonstrates a JavaScript `STOMP` client
|
||||
over `SockJS` client.
|
||||
|
||||
This application covers classical STOMP scenario:
|
||||
|
||||
- `connect` - requirement to enter the `user name` - chat member;
|
||||
- `subscribe` - the `Join` operation on the one of chat rooms and receiving messages to that subscription for the
|
||||
destination;
|
||||
- `send` and `receive` - just chat messages;
|
||||
- `unsubscribe` - the `Leave` operation on the chat room: the current web socket session stops receiving messages for
|
||||
the destination;
|
||||
- `disconnect` - close current web socket session and unsubscribe from all its subscriptions.
|
||||
|
||||
To get real chat interaction it's just enough to open several tabs in browser.
|
||||
When the user joins to the chat room, his subscription receives all messages, sent by other users to that room,
|
||||
immediately.
|
||||
|
||||
## Test Case
|
||||
|
||||
The `org.springframework.integration.samples.chat.stomp.server.ApplicationTests` demonstrates the Spring Boot test
|
||||
framework and just starts Server on the random port to be sure that this application is run correctly.
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2014 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.integration.samples.chat.stomp.server;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportResource;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@ImportResource("classpath:org/springframework/integration/samples/chat/stomp/server/stomp-server.xml")
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
|
||||
System.out.println("Hit 'Enter' to terminate");
|
||||
System.in.read();
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-websocket="http://www.springframework.org/schema/integration/websocket"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int-event="http://www.springframework.org/schema/integration/event"
|
||||
xmlns:task="http://www.springframework.org/schema/task" xmlns:util="http://www.springframework.org/schema/util"
|
||||
xmlns:int-groovy="http://www.springframework.org/schema/integration/groovy"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration
|
||||
http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/event
|
||||
http://www.springframework.org/schema/integration/event/spring-integration-event.xsd
|
||||
http://www.springframework.org/schema/integration/websocket
|
||||
http://www.springframework.org/schema/integration/websocket/spring-integration-websocket.xsd
|
||||
http://www.springframework.org/schema/task
|
||||
http://www.springframework.org/schema/task/spring-task.xsd
|
||||
http://www.springframework.org/schema/util
|
||||
http://www.springframework.org/schema/util/spring-util.xsd
|
||||
http://www.springframework.org/schema/integration/groovy
|
||||
http://www.springframework.org/schema/integration/groovy/spring-integration-groovy.xsd">
|
||||
|
||||
<int:wire-tap channel="logger"/>
|
||||
|
||||
<int:logging-channel-adapter id="logger" level="INFO" log-full-message="true"/>
|
||||
|
||||
<task:executor id="executor"/>
|
||||
|
||||
<bean id="webSocketSessionStore" class="org.springframework.integration.metadata.SimpleMetadataStore"/>
|
||||
|
||||
<bean id="chatMessagesStore" class="org.springframework.integration.store.SimpleMessageStore"/>
|
||||
|
||||
<util:map id="chatRoomSessions" value-type="java.util.List">
|
||||
<entry key="room1" value="#{new java.util.ArrayList()}"/>
|
||||
<entry key="room2" value="#{new java.util.ArrayList()}"/>
|
||||
</util:map>
|
||||
|
||||
<bean id="stompSubProtocolHandler" class="org.springframework.web.socket.messaging.StompSubProtocolHandler"/>
|
||||
|
||||
<int-websocket:server-container id="serverWebSocketContainer" path="/chat">
|
||||
<int-websocket:sockjs/>
|
||||
</int-websocket:server-container>
|
||||
|
||||
<int-event:inbound-channel-adapter event-types="org.springframework.web.socket.messaging.AbstractSubProtocolEvent"
|
||||
payload-expression="message"
|
||||
channel="routeStompEvents"/>
|
||||
|
||||
<int:header-value-router input-channel="routeStompEvents"
|
||||
header-name="simpMessageType"
|
||||
resolution-required="false"
|
||||
default-output-channel="nullChannel">
|
||||
<int:mapping value="#{T(org.springframework.messaging.simp.SimpMessageType).CONNECT.name()}"
|
||||
channel="connectAck"/>
|
||||
<int:mapping value="#{T(org.springframework.messaging.simp.SimpMessageType).SUBSCRIBE.name()}"
|
||||
channel="subscribe"/>
|
||||
<int:mapping value="#{T(org.springframework.messaging.simp.SimpMessageType).UNSUBSCRIBE.name()}"
|
||||
channel="unsubscribe"/>
|
||||
<int:mapping value="#{T(org.springframework.messaging.simp.SimpMessageType).DISCONNECT.name()}"
|
||||
channel="disconnect"/>
|
||||
</int:header-value-router>
|
||||
|
||||
<int:outbound-channel-adapter id="connectAck"
|
||||
expression="@webSocketSessionStore.put(headers.simpSessionId, headers.nativeHeaders.login)"/>
|
||||
|
||||
<int:publish-subscribe-channel id="subscribe"/>
|
||||
|
||||
<int:service-activator input-channel="subscribe" output-channel="nullChannel"
|
||||
expression="@chatRoomSessions[headers.simpDestination]
|
||||
.add(T(reactor.tuple.Tuple).of(headers.simpSessionId, headers.simpSubscriptionId))"/>
|
||||
|
||||
<int:chain input-channel="subscribe" output-channel="sendMessage">
|
||||
<int:header-enricher>
|
||||
<int:header name="sessionToSend" expression="headers.simpSessionId"/>
|
||||
<int:header name="subscriptionToSend" expression="headers.simpSubscriptionId"/>
|
||||
</int:header-enricher>
|
||||
<int:service-activator
|
||||
expression="@chatMessagesStore.getMessageGroup(headers.simpDestination).messages"/>
|
||||
<int:filter expression="!payload.empty"/>
|
||||
<int:header-enricher default-overwrite="true">
|
||||
<int:header name="#{T(org.springframework.messaging.simp.stomp.StompHeaderAccessor).SESSION_ID_HEADER}"
|
||||
expression="headers.sessionToSend"/>
|
||||
<int:header
|
||||
name="#{T(org.springframework.messaging.simp.stomp.StompHeaderAccessor).STOMP_SUBSCRIPTION_HEADER}"
|
||||
expression="headers.subscriptionToSend"/>
|
||||
</int:header-enricher>
|
||||
<int:splitter apply-sequence="false"/>
|
||||
</int:chain>
|
||||
|
||||
|
||||
<int:outbound-channel-adapter id="unsubscribe">
|
||||
<int-groovy:script>
|
||||
chatRoomSessions.each { k, v ->
|
||||
v.remove(reactor.tuple.Tuple.of(headers.simpSessionId, headers.simpSubscriptionId))
|
||||
}
|
||||
null
|
||||
</int-groovy:script>
|
||||
</int:outbound-channel-adapter>
|
||||
|
||||
|
||||
<int:channel id="receiveMessage"/>
|
||||
|
||||
<int-websocket:inbound-channel-adapter channel="receiveMessage" container="serverWebSocketContainer"
|
||||
default-protocol-handler="stompSubProtocolHandler"/>
|
||||
|
||||
<int:transformer input-channel="receiveMessage" output-channel="storeMessageAndPublish"
|
||||
expression="{user: @webSocketSessionStore.get(headers.simpSessionId), message: payload, date: new java.util.Date()}"/>
|
||||
|
||||
<int:publish-subscribe-channel id="storeMessageAndPublish"/>
|
||||
|
||||
<int:service-activator input-channel="storeMessageAndPublish" output-channel="nullChannel"
|
||||
expression="@chatMessagesStore.addMessageToGroup(headers.simpDestination, #root)"/>
|
||||
|
||||
|
||||
<int:splitter input-channel="storeMessageAndPublish" output-channel="sendMessage" apply-sequence="false">
|
||||
<int-groovy:script>
|
||||
chatRoomSessions[headers.simpDestination].collect {
|
||||
org.springframework.integration.support.MessageBuilder.withPayload(payload)
|
||||
.copyHeaders(headers)
|
||||
.setHeader('simpSessionId', it.t1)
|
||||
.setHeader('simpSubscriptionId', it.t2)
|
||||
.build()
|
||||
}
|
||||
</int-groovy:script>
|
||||
</int:splitter>
|
||||
|
||||
<int:channel id="sendMessage">
|
||||
<int:dispatcher task-executor="executor"/>
|
||||
</int:channel>
|
||||
|
||||
<int-websocket:outbound-channel-adapter channel="sendMessage" container="serverWebSocketContainer"
|
||||
default-protocol-handler="stompSubProtocolHandler"/>
|
||||
|
||||
<int:outbound-channel-adapter id="disconnect">
|
||||
<int-groovy:script>
|
||||
webSocketSessionStore.remove(headers.simpSessionId)
|
||||
chatRoomSessions.each { k, v -> v.removeAll { it.t1 == headers.simpSessionId } }
|
||||
null
|
||||
</int-groovy:script>
|
||||
</int:outbound-channel-adapter>
|
||||
|
||||
</beans>
|
||||
217
applications/stomp-chat/src/main/resources/static/index.html
Normal file
217
applications/stomp-chat/src/main/resources/static/index.html
Normal file
@@ -0,0 +1,217 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>WebSocket Chat</title>
|
||||
<script src="http://localhost:8080/sockjs.js"></script>
|
||||
<script src="http://localhost:8080/stomp.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var sock, stompClient, currentUser, subscriptions = {};
|
||||
|
||||
function connect() {
|
||||
var userValue = document.getElementById('user');
|
||||
if (userValue.value != "") {
|
||||
currentUser = userValue.value;
|
||||
sock = new SockJS('http://localhost:8080/chat');
|
||||
stompClient = Stomp.over(sock);
|
||||
stompClient.connect({login: currentUser}, function (frame) {
|
||||
document.getElementById("welcome").style.display = "none";
|
||||
document.getElementById("chat").style.display = "";
|
||||
document.getElementById("currentUser").innerHTML = currentUser;
|
||||
userValue.value = "";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function subscribeToRoom(room) {
|
||||
subscriptions[room] =
|
||||
stompClient.subscribe('room' + room, function (message) {
|
||||
var m = JSON.parse(message.body);
|
||||
var tr = document.createElement('tr');
|
||||
tr.innerHTML = "<td width='100'><div class='messageUser'>" +
|
||||
m.user +
|
||||
"</div><div class='messageDate'>" +
|
||||
new Date(m.date).toLocaleTimeString(navigator.userLanguage,
|
||||
{month: "short", day: "numeric", hour: "2-digit", minute: "2-digit"}) +
|
||||
"</div></td><td>" +
|
||||
m.message +
|
||||
"</td>";
|
||||
var messages = document.getElementById("messages" + room);
|
||||
messages.appendChild(tr);
|
||||
messages.scrollIntoView(false);
|
||||
});
|
||||
document.getElementById("join" + room).style.display = "none";
|
||||
document.getElementById("leave" + room).style.display = "";
|
||||
document.getElementById("message" + room).disabled = false;
|
||||
document.getElementById("send" + room).disabled = false;
|
||||
}
|
||||
|
||||
function sendMessage(room) {
|
||||
var messageValue = document.getElementById("message" + room);
|
||||
if (messageValue.value != "") {
|
||||
stompClient.send('room' + room, {subscription: subscriptions[room].id}, messageValue.value);
|
||||
messageValue.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
function unsubscribeFromRoom(room) {
|
||||
subscriptions[room].unsubscribe();
|
||||
document.getElementById("join" + room).style.display = "";
|
||||
document.getElementById("leave" + room).style.display = "none";
|
||||
document.getElementById("message" + room).disabled = true;
|
||||
document.getElementById("send" + room).disabled = true;
|
||||
document.getElementById("messages" + room).innerHTML = "";
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
for (var key in subscriptions) {
|
||||
if (subscriptions.hasOwnProperty(key)) {
|
||||
unsubscribeFromRoom(key)
|
||||
}
|
||||
}
|
||||
stompClient.disconnect(function (frame) {
|
||||
document.getElementById("welcome").style.display = "";
|
||||
document.getElementById("chat").style.display = "none";
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.messageUser {
|
||||
width: 100px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.messageDate {
|
||||
text-align: center;
|
||||
margin-top: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body style="margin: 0">
|
||||
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript!
|
||||
WebSocket relies on Javascript being enabled. Please enable Javascript and reload this page!</h2></noscript>
|
||||
<div id="welcome"
|
||||
style="position: absolute;
|
||||
bottom: 0;
|
||||
font-size: 200%;
|
||||
height: 200px;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
top: 0;
|
||||
width: 100%;">
|
||||
Welcome to the Simple WebSocket Stomp Chat!
|
||||
<br/>
|
||||
Enter your name to connect:
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<div align="center">
|
||||
<form onsubmit="connect();return false;">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<input id="user" type="text"
|
||||
style="font-size: 24pt; width: 200px; font-weight: bold; margin-top: 2px;"/>
|
||||
</td>
|
||||
<td>
|
||||
<input type="submit" value="Connect"
|
||||
style="height: 43px; width: 200px; font-size: 24pt; font-weight: bold;"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="chat" align="center" style="font-size: 200%; padding-top: 50px; display: none;">
|
||||
Welcome, <span id="currentUser"></span>!
|
||||
<br/>
|
||||
Please, join to chat rooms to send and receive messages to/from other users:
|
||||
<br/>
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
<td style="padding-right: 20px;">
|
||||
<div align="center" style="font-size: 25pt;">
|
||||
Room 1
|
||||
</div>
|
||||
<div align="center">
|
||||
<input id="join1" type="button" value="Join"
|
||||
onclick="subscribeToRoom(1)"
|
||||
style="height: 43px; width: 150px; font-size: 20pt; font-weight: bold;"/>
|
||||
|
||||
<input id="leave1" type="button" value="Leave"
|
||||
onclick="unsubscribeFromRoom(1)"
|
||||
style="height: 43px; width: 150px; font-size: 20pt; font-weight: bold;display: none"/>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
<div style="height: 300px; border: 5px groove; overflow: auto; width: 500px;">
|
||||
<table width="100%" border="1" cellspacing="0" style="border: 0">
|
||||
<tbody id="messages1" valign="top">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
<form onsubmit="sendMessage(1);return false;" style="margin-left: -4px;">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<input id="message1" type="text" disabled
|
||||
style="font-size: 20pt; width: 429px; margin-top: 2px;"/>
|
||||
</td>
|
||||
<td>
|
||||
<input id="send1" type="submit" value="Send" disabled
|
||||
style="height: 40px; width: 77px; font-size: 20pt; font-weight: bold;"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</td>
|
||||
<td style="padding-left: 20px;">
|
||||
<div align="center" style="font-size: 25pt;">
|
||||
Room 2
|
||||
</div>
|
||||
<div align="center">
|
||||
<input id="join2" type="button" value="Join"
|
||||
onclick="subscribeToRoom(2)"
|
||||
style="height: 43px; width: 150px; font-size: 20pt; font-weight: bold;"/>
|
||||
|
||||
<input id="leave2" type="button" value="Leave"
|
||||
onclick="unsubscribeFromRoom(2)"
|
||||
style="height: 43px; width: 150px; font-size: 20pt; font-weight: bold; display: none"/>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
<div style="height: 300px; border: 5px groove; overflow: auto; width: 500px;">
|
||||
<table width="100%" border="1" cellspacing="0" style="border: 0">
|
||||
<tbody id="messages2" valign="top">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
<form onsubmit="sendMessage(2);return false;" style="margin-left: -4px;">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<input id="message2" type="text" disabled
|
||||
style="font-size: 20pt; width: 429px; margin-top: 2px;"/>
|
||||
</td>
|
||||
<td>
|
||||
<input id="send2" type="submit" value="Send" disabled
|
||||
style="height: 40px; width: 77px; font-size: 20pt; font-weight: bold;"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/><br/>
|
||||
<input type="button" value="Disconnect" onclick="disconnect()"
|
||||
style="height: 43px; width: 220px; font-size: 24pt; font-weight: bold;"/>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
27
applications/stomp-chat/src/main/resources/static/sockjs.js
Normal file
27
applications/stomp-chat/src/main/resources/static/sockjs.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2014 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.integration.samples.chat.stomp.server;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = Application.class)
|
||||
@WebAppConfiguration
|
||||
@IntegrationTest
|
||||
public class ApplicationTests {
|
||||
|
||||
@Test
|
||||
public void testWebSockets() throws InterruptedException {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -57,7 +57,7 @@ import org.springframework.messaging.support.GenericMessage;
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ConfigurableApplicationContext ctx = SpringApplication.run(Application.class);
|
||||
ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
|
||||
System.out.println("Hit 'Enter' to terminate");
|
||||
System.in.read();
|
||||
ctx.close();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Time over WebSocket</title>
|
||||
<script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>
|
||||
<script src="http://localhost:8080/sockjs.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var sock = new SockJS('http://localhost:8080/time');
|
||||
|
||||
27
basic/web-sockets/src/main/resources/static/sockjs.js
Normal file
27
basic/web-sockets/src/main/resources/static/sockjs.js
Normal file
File diff suppressed because one or more lines are too long
22
build.gradle
22
build.gradle
@@ -138,7 +138,6 @@ subprojects { subproject ->
|
||||
}
|
||||
|
||||
repositories {
|
||||
//TODO on release
|
||||
// mavenLocal()
|
||||
maven { url 'http://repo.spring.io/libs-snapshot' }
|
||||
maven { url 'http://repo.spring.io/libs-milestone' }
|
||||
@@ -1117,6 +1116,27 @@ project('web-sockets') {
|
||||
}
|
||||
}
|
||||
|
||||
project('stomp-chat') {
|
||||
description = 'Web Sockets Stomp Chat Sample'
|
||||
|
||||
apply plugin: 'spring-boot'
|
||||
|
||||
dependencies {
|
||||
compile 'org.springframework.boot:spring-boot-starter-websocket'
|
||||
compile "org.springframework.integration:spring-integration-websocket:$springIntegration41Version"
|
||||
compile "org.springframework.integration:spring-integration-event:$springIntegration41Version"
|
||||
compile "org.springframework.integration:spring-integration-groovy:$springIntegration41Version"
|
||||
|
||||
testCompile 'org.springframework.boot:spring-boot-starter-test'
|
||||
}
|
||||
|
||||
mainClassName = 'org.springframework.integration.samples.chat.stomp.server.Application'
|
||||
|
||||
tasks.withType(JavaExec) {
|
||||
standardInput = System.in
|
||||
}
|
||||
}
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
description = 'Generates gradlew[.bat] scripts'
|
||||
gradleVersion = '1.12'
|
||||
|
||||
@@ -68,7 +68,7 @@ import org.springframework.social.twitter.api.impl.TwitterTemplate;
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ConfigurableApplicationContext ctx = SpringApplication.run(Application.class);
|
||||
ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
|
||||
System.in.read();
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ import org.springframework.social.twitter.api.impl.TwitterTemplate;
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ConfigurableApplicationContext ctx = SpringApplication.run(Application.class);
|
||||
ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
String hashTag = scanner.nextLine();
|
||||
System.out.println(ctx.getBean(Gateway.class).sendReceive(hashTag));
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
version=3.0.0.BUILD-SNAPSHOT
|
||||
springBootVersion=1.1.6.RELEASE
|
||||
springBootVersion=1.1.7.RELEASE
|
||||
|
||||
Reference in New Issue
Block a user