SI4.0.x: Create a new SI4.0.x maint branch
JIRA: https://jira.spring.io/browse/INTSAMPLES-138 Revert usage of SI 4.0 Remove those samples, which are based on SI 4.1
This commit is contained in:
@@ -1,50 +0,0 @@
|
||||
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.
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,217 +0,0 @@
|
||||
<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>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-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.
|
||||
@@ -13,10 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.integration.samples.mongodb.util;
|
||||
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mongodb.MongoDbFactory;
|
||||
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
|
||||
@@ -24,6 +26,7 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Oleg Zhurakousky
|
||||
@@ -33,8 +36,9 @@ public class StringConverter extends MappingMongoConverter {
|
||||
public StringConverter(
|
||||
MongoDbFactory mongoDbFactory,
|
||||
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
|
||||
super(mongoDbFactory, mappingContext);
|
||||
super(new DefaultDbRefResolver(mongoDbFactory), mappingContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Object source, DBObject target) {
|
||||
String strPerson = (String) source;
|
||||
@@ -52,14 +56,12 @@ public class StringConverter extends MappingMongoConverter {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <S> S read(Class<S> clazz, DBObject source) {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append(source.get("fname") + ", ");
|
||||
buffer.append(source.get("lname") + ", ");
|
||||
buffer.append(source.get("city") + ", ");
|
||||
buffer.append(source.get("street") + ", ");
|
||||
buffer.append(source.get("zip") + ", ");
|
||||
buffer.append(source.get("state") + ", ");
|
||||
return (S) buffer.toString();
|
||||
return (S) ((source.get("fname") + ", ")
|
||||
+ source.get("lname") + ", "
|
||||
+ source.get("city") + ", "
|
||||
+ source.get("street") + ", "
|
||||
+ source.get("zip") + ", "
|
||||
+ source.get("state") + ", ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
WebSockets Sample
|
||||
==============
|
||||
|
||||
This example demonstrates the Standard WebSocket protocol (without any sub-protocols) with Spring Integration Adapters.
|
||||
It just sends current time from the server to all connected clients.
|
||||
|
||||
## 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 Java & Annotation configuration.
|
||||
It is a `main` and starts an embedded Tomcat server on the default `8080` port. The WebSocket endpoint is mapped to the `/time` path.
|
||||
|
||||
The server also can be run from Gradle `gradlew :web-sockets:run`
|
||||
|
||||
## Java Client
|
||||
|
||||
The `org.springframework.integration.samples.websocket.standard.client.Application` is a simple Java application,
|
||||
which starts an integration flow (`client-context.xml`), connects to the WebSocket server and prints `Message`s to the
|
||||
logs, which are received over WebSocket.
|
||||
|
||||
## Browser Client
|
||||
|
||||
The `index.html` in the `src/main/resources/static` directory of this project demonstrates a JavaScript `SockJS` client, which connects
|
||||
to our server and just prints its messages in the middle of page.
|
||||
|
||||
## Test Case
|
||||
|
||||
The `org.springframework.integration.samples.websocket.standard.ApplicationTests` demonstrates the Spring Boot test
|
||||
framework and starts Server & Client to check, that the client receives correct data.
|
||||
@@ -1,123 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
</parent>
|
||||
<groupId>org.springframework.integration.samples</groupId>
|
||||
<artifactId>web-sockets</artifactId>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
<name>Web Sockets Basic Sample</name>
|
||||
<description>Web Sockets Basic Sample</description>
|
||||
<url>http://projects.spring.io/spring-integration</url>
|
||||
<organization>
|
||||
<name>SpringIO</name>
|
||||
<url>https://spring.io</url>
|
||||
</organization>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>garyrussell</id>
|
||||
<name>Gary Russell</name>
|
||||
<email>grussell@pivotal.io</email>
|
||||
<roles>
|
||||
<role>project lead</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>markfisher</id>
|
||||
<name>Mark Fisher</name>
|
||||
<email>mfisher@pivotal.io</email>
|
||||
<roles>
|
||||
<role>project founder and lead emeritus</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>ghillert</id>
|
||||
<name>Gunnar Hillert</name>
|
||||
<email>ghillert@pivotal.io</email>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>abilan</id>
|
||||
<name>Artem Bilan</name>
|
||||
<email>abilan@pivotal.io</email>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:scm:git:git://github.com/spring-projects/spring-integration-samples.git</connection>
|
||||
<developerConnection>scm:git:scm:git:ssh://git@github.com:spring-projects/spring-integration-samples.git</developerConnection>
|
||||
<url>https://github.com/spring-projects/spring-integration-samples</url>
|
||||
</scm>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>repo.spring.io.milestone</id>
|
||||
<name>Spring Framework Maven Milestone Repository</name>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-all</artifactId>
|
||||
<version>1.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<version>4.0.7.RELEASE</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.17</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-websocket</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* 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.websocket.standard.client;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*/
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("client-context.xml", Application.class);
|
||||
System.in.read();
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* 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.websocket.standard.server;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.annotation.InboundChannelAdapter;
|
||||
import org.springframework.integration.annotation.Poller;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.annotation.Transformer;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.ExecutorChannel;
|
||||
import org.springframework.integration.channel.PublishSubscribeChannel;
|
||||
import org.springframework.integration.core.MessageSource;
|
||||
import org.springframework.integration.handler.LoggingHandler;
|
||||
import org.springframework.integration.splitter.DefaultMessageSplitter;
|
||||
import org.springframework.integration.transformer.AbstractPayloadTransformer;
|
||||
import org.springframework.integration.transformer.HeaderEnricher;
|
||||
import org.springframework.integration.transformer.support.ExpressionEvaluatingHeaderValueMessageProcessor;
|
||||
import org.springframework.integration.websocket.ServerWebSocketContainer;
|
||||
import org.springframework.integration.websocket.outbound.WebSocketOutboundMessageHandler;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
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();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServerWebSocketContainer serverWebSocketContainer() {
|
||||
return new ServerWebSocketContainer("/time").withSockJs();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@InboundChannelAdapter(value = "splitChannel", poller = @Poller(fixedDelay = "1000", maxMessagesPerPoll = "1"))
|
||||
public MessageSource<?> webSocketSessionsMessageSource() {
|
||||
return new MessageSource<Iterator<String>>() {
|
||||
|
||||
@Override
|
||||
public Message<Iterator<String>> receive() {
|
||||
return new GenericMessage<Iterator<String>>(serverWebSocketContainer().getSessions().keySet().iterator());
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageChannel splitChannel() {
|
||||
return new DirectChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "splitChannel")
|
||||
public MessageHandler splitter() {
|
||||
DefaultMessageSplitter splitter = new DefaultMessageSplitter();
|
||||
splitter.setOutputChannelName("headerEnricherChannel");
|
||||
return splitter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageChannel headerEnricherChannel() {
|
||||
return new ExecutorChannel(Executors.newCachedThreadPool());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Transformer(inputChannel = "headerEnricherChannel", outputChannel = "transformChannel")
|
||||
public HeaderEnricher headerEnricher() {
|
||||
return new HeaderEnricher(Collections.singletonMap(SimpMessageHeaderAccessor.SESSION_ID_HEADER,
|
||||
new ExpressionEvaluatingHeaderValueMessageProcessor<Object>("payload", null)));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Transformer(inputChannel = "transformChannel", outputChannel = "sendTimeChannel")
|
||||
public AbstractPayloadTransformer<?, ?> transformer() {
|
||||
return new AbstractPayloadTransformer<Object, Object>() {
|
||||
@Override
|
||||
protected Object transformPayload(Object payload) throws Exception {
|
||||
return DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.DEFAULT).format(new Date());
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public MessageChannel sendTimeChannel() {
|
||||
return new PublishSubscribeChannel();
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "sendTimeChannel")
|
||||
public MessageHandler webSocketOutboundAdapter() {
|
||||
return new WebSocketOutboundMessageHandler(serverWebSocketContainer());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "sendTimeChannel")
|
||||
public MessageHandler loggingChannelAdapter() {
|
||||
LoggingHandler loggingHandler = new LoggingHandler("info");
|
||||
loggingHandler.setExpression("'The time ' + payload + ' has been sent to the WebSocketSession ' + headers.simpSessionId");
|
||||
return loggingHandler;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<?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"
|
||||
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/websocket
|
||||
http://www.springframework.org/schema/integration/websocket/spring-integration-websocket.xsd">
|
||||
|
||||
<bean id="webSocketClient" class="org.springframework.web.socket.client.standard.StandardWebSocketClient"/>
|
||||
|
||||
<int-websocket:client-container id="clientWebSocketContainer"
|
||||
client="webSocketClient"
|
||||
uri="ws://localhost:8080/time/websocket"/>
|
||||
|
||||
<int-websocket:inbound-channel-adapter container="clientWebSocketContainer"
|
||||
channel="webSocketInputChannel"/>
|
||||
|
||||
<int:logging-channel-adapter id="webSocketInputChannel"/>
|
||||
|
||||
</beans>
|
||||
@@ -1,34 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Time over WebSocket</title>
|
||||
<script src="http://localhost:8080/sockjs.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var sock = new SockJS('http://localhost:8080/time');
|
||||
sock.onopen = function () {
|
||||
document.getElementById('time').innerHTML = 'Connecting...';
|
||||
};
|
||||
sock.onmessage = function (e) {
|
||||
document.getElementById('time').innerHTML = e.data;
|
||||
};
|
||||
sock.onclose = function () {
|
||||
document.getElementById('time').innerHTML = "Server closed connection or hasn't been started";
|
||||
};
|
||||
</script>
|
||||
</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="time"
|
||||
style="position: absolute;
|
||||
bottom: 0;
|
||||
font-size: 800%;
|
||||
height: 200px;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
top: 0;
|
||||
width: 100%;">Starting...</div>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* 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.websocket.standard;
|
||||
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.samples.websocket.standard.server.Application;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.support.ChannelInterceptorAdapter;
|
||||
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 {
|
||||
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("client-context.xml",
|
||||
org.springframework.integration.samples.websocket.standard.client.Application.class);
|
||||
DirectChannel webSocketInputChannel = ctx.getBean("webSocketInputChannel", DirectChannel.class);
|
||||
|
||||
final CountDownLatch stopLatch = new CountDownLatch(2);
|
||||
|
||||
webSocketInputChannel.addInterceptor(new ChannelInterceptorAdapter() {
|
||||
@Override
|
||||
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
|
||||
Object payload = message.getPayload();
|
||||
assertThat(payload, instanceOf(String.class));
|
||||
Date date = null;
|
||||
try {
|
||||
date = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.DEFAULT).parse((String) payload);
|
||||
}
|
||||
catch (ParseException e) {
|
||||
fail("fail to parse date");
|
||||
}
|
||||
assertThat(new Date().compareTo(date), greaterThanOrEqualTo(0));
|
||||
stopLatch.countDown();
|
||||
}
|
||||
|
||||
});
|
||||
assertTrue(stopLatch.await(10, TimeUnit.SECONDS));
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
}
|
||||
60
build.gradle
60
build.gradle
@@ -192,7 +192,6 @@ subprojects { subproject ->
|
||||
subethasmtpVersion = '1.2'
|
||||
slf4jVersion = '1.7.6'
|
||||
springIntegrationVersion = '4.0.4.RELEASE'
|
||||
springIntegration41Version = '4.1.0.BUILD-SNAPSHOT'
|
||||
springIntegrationDslVersion = '1.0.0.M3'
|
||||
springVersion = '4.0.7.RELEASE'
|
||||
springSecurityVersion = '3.2.4.RELEASE'
|
||||
@@ -520,8 +519,8 @@ project('si4demo') {
|
||||
dependencies {
|
||||
compile 'org.springframework.boot:spring-boot-starter-web'
|
||||
compile 'org.springframework.boot:spring-boot-starter-integration'
|
||||
compile "org.springframework.integration:spring-integration-mail:$springIntegration41Version"
|
||||
compile "org.springframework.integration:spring-integration-twitter:$springIntegration41Version"
|
||||
compile "org.springframework.integration:spring-integration-mail"
|
||||
compile "org.springframework.integration:spring-integration-twitter"
|
||||
compile "org.springframework.integration:spring-integration-java-dsl:$springIntegrationDslVersion"
|
||||
compile "javax.mail:javax.mail-api:$javaxMailVersion"
|
||||
compile "com.sun.mail:javax.mail:$javaxMailVersion"
|
||||
@@ -550,7 +549,7 @@ project('cafe-dsl') {
|
||||
dependencies {
|
||||
compile project(":cafe-si")
|
||||
compile 'org.springframework.boot:spring-boot-starter-integration'
|
||||
compile "org.springframework.integration:spring-integration-core:$springIntegration41Version"
|
||||
compile "org.springframework.integration:spring-integration-core"
|
||||
compile "org.springframework.integration:spring-integration-java-dsl:$springIntegrationDslVersion"
|
||||
|
||||
testCompile 'org.springframework.boot:spring-boot-starter-test'
|
||||
@@ -637,7 +636,6 @@ project('mail') {
|
||||
|
||||
dependencies {
|
||||
compile "org.springframework.integration:spring-integration-mail:$springIntegrationVersion"
|
||||
compile "org.springframework:spring-context:$springVersion"
|
||||
compile "javax.mail:javax.mail-api:$javaxMailVersion"
|
||||
compile "com.sun.mail:mailapi:$javaxMailVersion"
|
||||
}
|
||||
@@ -819,17 +817,6 @@ project('xmpp') {
|
||||
}
|
||||
}
|
||||
|
||||
project('async-gateway') {
|
||||
description = 'Async Gateway Sample'
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
dependencies {
|
||||
compile "org.springframework.integration:spring-integration-core:$springIntegration41Version"
|
||||
compile "org.projectreactor.spring:reactor-spring-context:$reactorSpringVersion"
|
||||
}
|
||||
}
|
||||
|
||||
project('dynamic-poller') {
|
||||
description = 'Dynamic Poller Sample'
|
||||
|
||||
@@ -923,7 +910,6 @@ project('rest-http') {
|
||||
compile "org.springframework:spring-oxm:$springVersion"
|
||||
compile "org.springframework:spring-tx:$springVersion"
|
||||
compile "org.springframework:spring-jdbc:$springVersion"
|
||||
compile "org.springframework:spring-context:$springVersion"
|
||||
compile "org.springframework:spring-aop:$springVersion"
|
||||
compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
|
||||
compile "org.springframework.security:spring-security-web:$springSecurityVersion"
|
||||
@@ -1097,46 +1083,6 @@ project('tx-synch') {
|
||||
}
|
||||
}
|
||||
|
||||
project('web-sockets') {
|
||||
description = 'Web Sockets Basic Sample'
|
||||
|
||||
apply plugin: 'spring-boot'
|
||||
|
||||
dependencies {
|
||||
compile 'org.springframework.boot:spring-boot-starter-websocket'
|
||||
compile "org.springframework.integration:spring-integration-websocket:$springIntegration41Version"
|
||||
|
||||
testCompile 'org.springframework.boot:spring-boot-starter-test'
|
||||
}
|
||||
|
||||
mainClassName = 'org.springframework.integration.samples.websocket.standard.server.Application'
|
||||
|
||||
tasks.withType(JavaExec) {
|
||||
standardInput = System.in
|
||||
}
|
||||
}
|
||||
|
||||
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'
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
version=3.0.0.BUILD-SNAPSHOT
|
||||
springBootVersion=1.1.7.RELEASE
|
||||
springBootVersion=1.1.8.RELEASE
|
||||
|
||||
1
intermediate/async-gateway/.gitignore
vendored
1
intermediate/async-gateway/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/bin/
|
||||
@@ -1,33 +0,0 @@
|
||||
Async Gateway Sample
|
||||
====================
|
||||
|
||||
Gateways provide a convenient way to expose a Proxy over a service-interface thus giving you POJO-based access to a messaging system (based on objects in your own domain, or primitives/Strings, etc). However, when you invoke a method, you expect the method to return. A gateway's method call represents a contract with the messaging system, which states that for each request, there will always be a reply. Therefore you must always guarantee that your message flow is in compliance with such a contract.
|
||||
|
||||
But what about the cases where you can't (e.g, message was filtered out and discarded or routed into a unidirectional sub-flow)?
|
||||
|
||||
Starting with Spring Integration 2.0, we introduced support for an Asynchronous Gateway, which is a convenient way to initiate flows, where you may not know, if a reply is expected or how long will it take for it to arrive. A natural way to handle these types of scenarios in Java would be to rely upon **java.util.concurrent.Future** instances. That is exactly what Spring Integration uses to support Asynchronous Gateways.
|
||||
|
||||
This example demonstrates how you can apply an Asynchronous Gateway based on the following simple use case:
|
||||
|
||||
We are sending a request to a **MathService** to multiply random numbers by 2. As you can see from the configuration there is a filter that discards any request for the number that is less then a 100. This means that there will be no replies coming for the requests with numbers less then 100. Typically, when using the regular Gateway, the gateway method would lock until a timeout occurs. In this example, however, the responses are coming back right away as Java Futures which we evaluate.
|
||||
|
||||
To run this sample, simply execute **org.springframework.integration.samples.async.gateway.AsyncGatewayTest**.
|
||||
|
||||
You should see the following output:
|
||||
|
||||
INFO : org.springframework.integration.samples.async.gateway.AsyncGatewayTest - Result of multiplication of 107 by 2 is 214
|
||||
INFO : org.springframework.integration.samples.async.gateway.AsyncGatewayTest - Result of multiplication of 146 by 2 is 292
|
||||
INFO : org.springframework.integration.samples.async.gateway.AsyncGatewayTest - Result of multiplication of 189 by 2 is 378
|
||||
INFO : org.springframework.integration.samples.async.gateway.AsyncGatewayTest - Result of multiplication of 130 by 2 is 260
|
||||
. . . . .
|
||||
INFO : org.springframework.integration.samples.async.gateway.AsyncGatewayTest - Multiplication of 38 by 2 is can not be accomplished in 20 seconds
|
||||
INFO : org.springframework.integration.samples.async.gateway.AsyncGatewayTest - Multiplication of 39 by 2 is can not be accomplished in 20 seconds
|
||||
INFO : org.springframework.integration.samples.async.gateway.AsyncGatewayTest - Multiplication of 36 by 2 is can not be accomplished in 20 seconds
|
||||
INFO : org.springframework.integration.samples.async.gateway.AsyncGatewayTest - Multiplication of 37 by 2 is can not be accomplished in 20 seconds
|
||||
|
||||
|
||||
Spring Integration 4.0 provided the capability to more easily configure Messaging Gateways with Java configuration.
|
||||
|
||||
Spring Integration 4.1 added support for **ListenableFuture** and **Promise** (from project reactor) return types.
|
||||
|
||||
The **ListenableFutureTest** and **PromiseTest** test classes replicate the above test case, using those return types, and showing the use of **@MessagingGateway** java configuration.
|
||||
@@ -1,106 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.integration.samples</groupId>
|
||||
<artifactId>async-gateway</artifactId>
|
||||
<version>3.0.0.BUILD-SNAPSHOT</version>
|
||||
<name>Async Gateway Sample</name>
|
||||
<description>Async Gateway Sample</description>
|
||||
<url>http://projects.spring.io/spring-integration</url>
|
||||
<organization>
|
||||
<name>SpringIO</name>
|
||||
<url>https://spring.io</url>
|
||||
</organization>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>garyrussell</id>
|
||||
<name>Gary Russell</name>
|
||||
<email>grussell@pivotal.io</email>
|
||||
<roles>
|
||||
<role>project lead</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>markfisher</id>
|
||||
<name>Mark Fisher</name>
|
||||
<email>mfisher@pivotal.io</email>
|
||||
<roles>
|
||||
<role>project founder and lead emeritus</role>
|
||||
</roles>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>ghillert</id>
|
||||
<name>Gunnar Hillert</name>
|
||||
<email>ghillert@pivotal.io</email>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>abilan</id>
|
||||
<name>Artem Bilan</name>
|
||||
<email>abilan@pivotal.io</email>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:scm:git:git://github.com/spring-projects/spring-integration-samples.git</connection>
|
||||
<developerConnection>scm:git:scm:git:ssh://git@github.com:spring-projects/spring-integration-samples.git</developerConnection>
|
||||
<url>https://github.com/spring-projects/spring-integration-samples</url>
|
||||
</scm>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>repo.spring.io.milestone</id>
|
||||
<name>Spring Framework Maven Milestone Repository</name>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectreactor.spring</groupId>
|
||||
<artifactId>reactor-spring-context</artifactId>
|
||||
<version>1.1.3.RELEASE</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-all</artifactId>
|
||||
<version>1.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<version>4.0.7.RELEASE</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-core</artifactId>
|
||||
<version>4.1.0.BUILD-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.17</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.async.gateway;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
*
|
||||
*/
|
||||
public class MathService {
|
||||
private final Random random = new Random();
|
||||
|
||||
public int multiplyByTwo(int i) throws Exception{
|
||||
long sleep = random.nextInt(10) * 500;
|
||||
Thread.sleep(sleep);
|
||||
return i*2;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.async.gateway;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
*
|
||||
*/
|
||||
public interface MathServiceGateway {
|
||||
|
||||
Future<Integer> multiplyByTwo(int i);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
##
|
||||
# Dispatcher configuration
|
||||
#
|
||||
# Each dispatcher must be configured with a type:
|
||||
#
|
||||
# reactor.dispatchers.<name>.type = <type>
|
||||
#
|
||||
# Legal values for <type> are eventLoop, ringBuffer, synchronous, and threadPoolExecutor.
|
||||
|
||||
# Depending on the type, further configuration is be possible:
|
||||
#
|
||||
# reactor.dispatchers.<name>.size: eventLoop and threadPoolExecutor Dispatchers
|
||||
# reactor.dispatchers.<name>.backlog: eventLoop, ringBuffer, and threadPoolExecutor Dispatchers
|
||||
#
|
||||
# A size less than 1 may be specified to indicate that the size should be the same as the number
|
||||
# of CPUs.
|
||||
|
||||
# A thread pool executor dispatcher, named threadPoolExecutor
|
||||
reactor.dispatchers.threadPoolExecutor.type = threadPoolExecutor
|
||||
reactor.dispatchers.threadPoolExecutor.size = 100
|
||||
# Backlog is how many Task objects to warm up internally
|
||||
reactor.dispatchers.threadPoolExecutor.backlog = 100
|
||||
|
||||
# An event loop dispatcher, named eventLoop
|
||||
reactor.dispatchers.eventLoop.type =
|
||||
reactor.dispatchers.eventLoop.size = 0
|
||||
reactor.dispatchers.eventLoop.backlog = 2048
|
||||
|
||||
# A ring buffer dispatcher, named ringBuffer
|
||||
reactor.dispatchers.ringBuffer.type =
|
||||
reactor.dispatchers.ringBuffer.backlog = 2048
|
||||
|
||||
# A work queue dispatcher, named workQueue
|
||||
reactor.dispatchers.workQueue.type =
|
||||
reactor.dispatchers.workQueue.size = 0
|
||||
reactor.dispatchers.workQueue.backlog = 2048
|
||||
|
||||
# The dispatcher named ringBuffer should be the default dispatcher
|
||||
reactor.dispatchers.default = threadPoolExecutor
|
||||
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:task="http://www.springframework.org/schema/task">
|
||||
|
||||
<int:gateway id="mathService"
|
||||
service-interface="org.springframework.integration.samples.async.gateway.MathServiceGateway"
|
||||
default-request-channel="requestChannel"
|
||||
async-executor="executor" default-reply-timeout="0"/>
|
||||
|
||||
<int:channel id="requestChannel" />
|
||||
|
||||
<int:filter input-channel="requestChannel" output-channel="calculatingChannel" expression="payload gt 100" />
|
||||
|
||||
<int:channel id="calculatingChannel" />
|
||||
|
||||
<int:service-activator input-channel="calculatingChannel">
|
||||
<bean class="org.springframework.integration.samples.async.gateway.MathService"/>
|
||||
</int:service-activator>
|
||||
|
||||
<task:executor id="executor" pool-size="100" />
|
||||
</beans>
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-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.async.gateway;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.FileSystemXmlApplicationContext;
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
public class AsyncGatewayTest {
|
||||
private static Logger logger = Logger.getLogger(AsyncGatewayTest.class);
|
||||
private static ExecutorService executor = Executors.newFixedThreadPool(100);
|
||||
private static int timeout = 20;
|
||||
|
||||
@Test
|
||||
public void testAsyncGateway() throws Exception{
|
||||
ConfigurableApplicationContext ac =
|
||||
new FileSystemXmlApplicationContext("src/main/resources/META-INF/spring/integration/*.xml");
|
||||
MathServiceGateway mathService = ac.getBean("mathService", MathServiceGateway.class);
|
||||
Map<Integer, Future<Integer>> results = new HashMap<Integer, Future<Integer>>();
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
int number = random.nextInt(200);
|
||||
Future<Integer> result = mathService.multiplyByTwo(number);
|
||||
results.put(number, result);
|
||||
}
|
||||
for (final Map.Entry<Integer, Future<Integer>> resultEntry : results.entrySet()) {
|
||||
executor.execute(() -> {
|
||||
int[] result = processFuture(resultEntry);
|
||||
|
||||
if (result[1] == -1){
|
||||
logger.info("Multiplying " + result[0] + " should be easy. You should be able to multiply any number < 100 by 2 in your head");
|
||||
} else if (result[1] == -2){
|
||||
logger.info("Multiplication of " + result[0] + " by 2 is can not be accomplished in " + timeout + " seconds");
|
||||
} else {
|
||||
logger.info("Result of multiplication of " + result[0] + " by 2 is " + result[1]);
|
||||
}
|
||||
});
|
||||
}
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(60, TimeUnit.SECONDS);
|
||||
logger.info("Finished");
|
||||
ac.close();
|
||||
}
|
||||
|
||||
public static int[] processFuture(Map.Entry<Integer, Future<Integer>> resultEntry){
|
||||
int originalNumber = resultEntry.getKey();
|
||||
Future<Integer> result = resultEntry.getValue();
|
||||
try {
|
||||
int finalResult = result.get(timeout, TimeUnit.SECONDS);
|
||||
return new int[]{originalNumber, finalResult};
|
||||
} catch (ExecutionException e) {
|
||||
return new int[]{originalNumber, -1};
|
||||
} catch (TimeoutException tex){
|
||||
return new int[]{originalNumber, -2};
|
||||
} catch (Exception ex){
|
||||
System.out.println();
|
||||
// ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-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.async.gateway;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.integration.annotation.Filter;
|
||||
import org.springframework.integration.annotation.Gateway;
|
||||
import org.springframework.integration.annotation.IntegrationComponentScan;
|
||||
import org.springframework.integration.annotation.MessageEndpoint;
|
||||
import org.springframework.integration.annotation.MessagingGateway;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.ListenableFutureCallback;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
@ContextConfiguration(classes = ListenableFutureTest.TestConfig.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext
|
||||
public class ListenableFutureTest {
|
||||
|
||||
private static Logger logger = Logger.getLogger(ListenableFutureTest.class);
|
||||
|
||||
@Autowired
|
||||
private MathGateway gateway;
|
||||
|
||||
@Test
|
||||
public void testAsyncGateway() throws Exception{
|
||||
Random random = new Random();
|
||||
int[] numbers = new int[100];
|
||||
int expectedResults = 0;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
numbers[i] = random.nextInt(200);
|
||||
if (numbers[i] > 100) {
|
||||
expectedResults++;
|
||||
}
|
||||
}
|
||||
final CountDownLatch latch = new CountDownLatch(expectedResults);
|
||||
final AtomicInteger failures = new AtomicInteger();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
final int number = numbers[i];
|
||||
ListenableFuture<Integer> result = gateway.multiplyByTwo(number);
|
||||
ListenableFutureCallback<Integer> callback = new ListenableFutureCallback<Integer>() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(Integer result) {
|
||||
logger.info("Result of multiplication of " + number + " by 2 is " + result);
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
failures.incrementAndGet();
|
||||
logger.error("Unexpected exception for " + number, t);
|
||||
latch.countDown();
|
||||
}
|
||||
};
|
||||
result.addCallback(callback);
|
||||
}
|
||||
assertTrue(latch.await(60, TimeUnit.SECONDS));
|
||||
assertEquals(0, failures.get());
|
||||
logger.info("Finished");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@EnableIntegration
|
||||
@IntegrationComponentScan
|
||||
public static class TestConfig {
|
||||
|
||||
@Bean
|
||||
public MessageChannel gatewayChannel() {
|
||||
return new DirectChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel="mathServiceChannel")
|
||||
public MathService mathService() {
|
||||
return new MathService();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AsyncTaskExecutor exec() {
|
||||
SimpleAsyncTaskExecutor simpleAsyncTaskExecutor = new SimpleAsyncTaskExecutor();
|
||||
simpleAsyncTaskExecutor.setThreadNamePrefix("exec-");
|
||||
return simpleAsyncTaskExecutor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@MessagingGateway(defaultReplyTimeout = 0)
|
||||
public interface MathGateway {
|
||||
|
||||
@Gateway(requestChannel = "gatewayChannel")
|
||||
ListenableFuture<Integer> multiplyByTwo(int number);
|
||||
|
||||
}
|
||||
|
||||
@MessageEndpoint
|
||||
public static class Gt100Filter {
|
||||
|
||||
@Filter(inputChannel="gatewayChannel", outputChannel="mathServiceChannel")
|
||||
public boolean filter(int i) {
|
||||
return i > 100;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-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.async.gateway;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import reactor.core.composable.Promise;
|
||||
import reactor.spring.context.config.EnableReactor;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.integration.annotation.Filter;
|
||||
import org.springframework.integration.annotation.Gateway;
|
||||
import org.springframework.integration.annotation.IntegrationComponentScan;
|
||||
import org.springframework.integration.annotation.MessageEndpoint;
|
||||
import org.springframework.integration.annotation.MessagingGateway;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
*
|
||||
*/
|
||||
@ContextConfiguration(classes = PromiseTest.TestConfig.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext
|
||||
public class PromiseTest {
|
||||
|
||||
private static Logger logger = Logger.getLogger(PromiseTest.class);
|
||||
|
||||
@Autowired
|
||||
private MathGateway gateway;
|
||||
|
||||
@Test
|
||||
public void testPromiseGateway() throws Exception {
|
||||
Random random = new Random();
|
||||
int[] numbers = new int[100];
|
||||
int expectedResults = 0;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
numbers[i] = random.nextInt(200);
|
||||
if (numbers[i] > 100) {
|
||||
expectedResults++;
|
||||
}
|
||||
}
|
||||
final CountDownLatch latch = new CountDownLatch(expectedResults);
|
||||
final AtomicInteger failures = new AtomicInteger();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
final int number = numbers[i];
|
||||
gateway.multiplyByTwo(number)
|
||||
.onSuccess(result1 -> {
|
||||
logger.info("Result of multiplication of " + number + " by 2 is " + result1);
|
||||
latch.countDown();
|
||||
})
|
||||
.onError(t -> {
|
||||
failures.incrementAndGet();
|
||||
logger.error("Unexpected exception for " + number, t);
|
||||
latch.countDown();
|
||||
})
|
||||
.flush();
|
||||
}
|
||||
assertTrue(latch.await(60, TimeUnit.SECONDS));
|
||||
assertEquals(0, failures.get());
|
||||
logger.info("Finished");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
@EnableReactor
|
||||
@ComponentScan
|
||||
@IntegrationComponentScan
|
||||
public static class TestConfig {
|
||||
|
||||
@Bean
|
||||
public MessageChannel gatewayChannel() {
|
||||
return new DirectChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ServiceActivator(inputChannel = "mathServiceChannel")
|
||||
public MathService mathService() {
|
||||
return new MathService();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AsyncTaskExecutor exec() {
|
||||
SimpleAsyncTaskExecutor simpleAsyncTaskExecutor = new SimpleAsyncTaskExecutor();
|
||||
simpleAsyncTaskExecutor.setThreadNamePrefix("exec-");
|
||||
return simpleAsyncTaskExecutor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@MessagingGateway(defaultReplyTimeout = 0, reactorEnvironment = "reactorEnv")
|
||||
public interface MathGateway {
|
||||
|
||||
@Gateway(requestChannel = "gatewayChannel")
|
||||
Promise<Integer> multiplyByTwo(int number);
|
||||
|
||||
}
|
||||
|
||||
@MessageEndpoint
|
||||
public static class Gt100Filter {
|
||||
|
||||
@Filter(inputChannel = "gatewayChannel", outputChannel = "mathServiceChannel")
|
||||
public boolean filter(int i) {
|
||||
return i > 100;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
||||
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
|
||||
|
||||
<!-- Appenders -->
|
||||
<appender name="console" class="org.apache.log4j.ConsoleAppender">
|
||||
<param name="Target" value="System.out" />
|
||||
<layout class="org.apache.log4j.PatternLayout">
|
||||
<param name="ConversionPattern" value="%d{HH:mm:ss.SSS} %-5p [%t][%c] %m%n" />
|
||||
</layout>
|
||||
</appender>
|
||||
<logger name="org.springframework.integration">
|
||||
<level value="warn" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.integration.samples.async.gateway">
|
||||
<level value="debug" />
|
||||
</logger>
|
||||
|
||||
<!-- Root Logger -->
|
||||
<root>
|
||||
<priority value="warn" />
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
</log4j:configuration>
|
||||
Reference in New Issue
Block a user