Change streams->stream

This commit is contained in:
Dave Syer
2015-07-15 08:25:44 +01:00
parent 09c0cf5573
commit dafa0773b5
168 changed files with 195 additions and 195 deletions

View File

@@ -0,0 +1,2 @@
Spring XD Test Support
======================

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binding-test</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-cloud-stream-binding-test</name>
<description>Test support for binding implementations</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-bindings-parent</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-amqp.version>1.4.5.RELEASE</spring-amqp.version>
</properties>
<dependencies>
<!-- There's no comparison with gradle's syntax, but I think we'll be happy sticking with
maven for the superior dependency and project management -->
<!--
compile ("org.springframework.integration:spring-integration-test"){
exclude group: 'org.apache.avro', module: 'avro-compiler'
}
compile "org.springframework.integration:spring-integration-amqp"
compile "org.springframework.integration:spring-integration-redis"
compile "org.springframework.integration:spring-integration-mqtt"
compile ("org.springframework.integration:spring-integration-kafka:$springIntegrationKafkaVersion") {
exclude group: 'org.apache.avro', module: 'avro-compiler'
}
-->
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-test</artifactId>
<version>${spring-integration.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.avro</groupId>
<artifactId>avro-compiler</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--<dependency>-->
<!--<groupId>org.springframework.integration</groupId>-->
<!--<artifactId>spring-integration-redis</artifactId>-->
<!--<version>${spring-integration.version}</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.integration</groupId>-->
<!--<artifactId>spring-integration-mqtt</artifactId>-->
<!--<version>${spring-integration.version}</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.integration</groupId>-->
<!--<artifactId>spring-integration-kafka</artifactId>-->
<!--<version>1.1.2.RELEASE</version>-->
<!--<exclusions>-->
<!--<exclusion>-->
<!--<groupId>org.apache.avro</groupId>-->
<!--<artifactId>avro-compiler</artifactId>-->
<!--</exclusion>-->
<!--</exclusions>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.apache.kafka</groupId>-->
<!--<artifactId>kafka_2.10</artifactId>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.apache.kafka</groupId>-->
<!--<artifactId>kafka_2.10</artifactId>-->
<!--<classifier>test</classifier>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.apache.kafka</groupId>-->
<!--<artifactId>kafka-clients</artifactId>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.apache.curator</groupId>-->
<!--<artifactId>curator-test</artifactId>-->
<!--</dependency>-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<!--<dependency>-->
<!--<groupId>org.springframework.amqp</groupId>-->
<!--<artifactId>spring-rabbit</artifactId>-->
<!--<version>${spring-amqp.version}</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.springframework.xd</groupId>
<artifactId>spring-xd-tuple</artifactId>
<version>${spring-xd.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.xd</groupId>
<artifactId>spring-xd-codec</artifactId>
</exclusion>
</exclusions>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binding-spi</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-codec</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,311 @@
/*
* Copyright 2013-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.xd.dirt.integration.bus;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.http.MediaType;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.channel.interceptor.WireTap;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.xd.dirt.integration.bus.MessageBus.Capability;
import org.springframework.xd.dirt.integration.bus.serializer.MultiTypeCodec;
import org.springframework.xd.dirt.integration.bus.serializer.kryo.PojoCodec;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author Gary Russell
* @author Ilayaperumal Gopinathan
* @author David Turanski
*/
public abstract class AbstractMessageBusTests {
protected static final Collection<MediaType> ALL = Collections.singletonList(MediaType.ALL);
protected AbstractTestMessageBus<?> testMessageBus;
@Test
public void testClean() throws Exception {
MessageBus messageBus = getMessageBus();
messageBus.bindProducer("foo.0", new DirectChannel(), null);
messageBus.bindConsumer("foo.0", new DirectChannel(), null);
messageBus.bindProducer("foo.1", new DirectChannel(), null);
messageBus.bindConsumer("foo.1", new DirectChannel(), null);
messageBus.bindProducer("foo.2", new DirectChannel(), null);
Collection<?> bindings = getBindings(messageBus);
assertEquals(5, bindings.size());
messageBus.unbindProducers("foo.0");
assertEquals(4, bindings.size());
messageBus.unbindConsumers("foo.0");
messageBus.unbindProducers("foo.1");
assertEquals(2, bindings.size());
messageBus.unbindConsumers("foo.1");
messageBus.unbindProducers("foo.2");
assertTrue(bindings.isEmpty());
}
@Test
public void testSendAndReceive() throws Exception {
MessageBus messageBus = getMessageBus();
DirectChannel moduleOutputChannel = new DirectChannel();
QueueChannel moduleInputChannel = new QueueChannel();
messageBus.bindProducer("foo.0", moduleOutputChannel, null);
messageBus.bindConsumer("foo.0", moduleInputChannel, null);
Message<?> message = MessageBuilder.withPayload("foo").setHeader(MessageHeaders.CONTENT_TYPE,
"foo/bar").build();
// Let the consumer actually bind to the producer before sending a msg
busBindUnbindLatency();
moduleOutputChannel.send(message);
Message<?> inbound = moduleInputChannel.receive(5000);
assertNotNull(inbound);
assertEquals("foo", inbound.getPayload());
assertNull(inbound.getHeaders().get(XdHeaders.XD_ORIGINAL_CONTENT_TYPE));
assertEquals("foo/bar", inbound.getHeaders().get(MessageHeaders.CONTENT_TYPE));
messageBus.unbindProducers("foo.0");
messageBus.unbindConsumers("foo.0");
}
@Test
public void testSendAndReceiveNoOriginalContentType() throws Exception {
MessageBus messageBus = getMessageBus();
DirectChannel moduleOutputChannel = new DirectChannel();
QueueChannel moduleInputChannel = new QueueChannel();
messageBus.bindProducer("bar.0", moduleOutputChannel, null);
messageBus.bindConsumer("bar.0", moduleInputChannel, null);
busBindUnbindLatency();
Message<?> message = MessageBuilder.withPayload("foo").build();
moduleOutputChannel.send(message);
Message<?> inbound = moduleInputChannel.receive(5000);
assertNotNull(inbound);
assertEquals("foo", inbound.getPayload());
assertNull(inbound.getHeaders().get(XdHeaders.XD_ORIGINAL_CONTENT_TYPE));
assertNull(inbound.getHeaders().get(MessageHeaders.CONTENT_TYPE));
messageBus.unbindProducers("bar.0");
messageBus.unbindConsumers("bar.0");
}
@Test
public void testSendAndReceivePubSub() throws Exception {
MessageBus messageBus = getMessageBus();
DirectChannel moduleOutputChannel = new DirectChannel();
// Test pub/sub by emulating how StreamPlugin handles taps
DirectChannel tapChannel = new DirectChannel();
QueueChannel moduleInputChannel = new QueueChannel();
QueueChannel module2InputChannel = new QueueChannel();
QueueChannel module3InputChannel = new QueueChannel();
messageBus.bindProducer("baz.0", moduleOutputChannel, null);
messageBus.bindConsumer("baz.0", moduleInputChannel, null);
moduleOutputChannel.addInterceptor(new WireTap(tapChannel));
messageBus.bindPubSubProducer("tap:baz.http", tapChannel, null);
// A new module is using the tap as an input channel
String fooTapName = messageBus.isCapable(Capability.DURABLE_PUBSUB) ? "foo.tap:baz.http" : "tap:baz.http";
messageBus.bindPubSubConsumer(fooTapName, module2InputChannel, null);
// Another new module is using tap as an input channel
String barTapName = messageBus.isCapable(Capability.DURABLE_PUBSUB) ? "bar.tap:baz.http" : "tap:baz.http";
messageBus.bindPubSubConsumer(barTapName, module3InputChannel, null);
Message<?> message = MessageBuilder.withPayload("foo").setHeader(MessageHeaders.CONTENT_TYPE,
"foo/bar").build();
boolean success = false;
boolean retried = false;
while (!success) {
moduleOutputChannel.send(message);
Message<?> inbound = moduleInputChannel.receive(5000);
assertNotNull(inbound);
assertEquals("foo", inbound.getPayload());
assertNull(inbound.getHeaders().get(XdHeaders.XD_ORIGINAL_CONTENT_TYPE));
assertEquals("foo/bar", inbound.getHeaders().get(MessageHeaders.CONTENT_TYPE));
Message<?> tapped1 = module2InputChannel.receive(5000);
Message<?> tapped2 = module3InputChannel.receive(5000);
if (tapped1 == null || tapped2 == null) {
// listener may not have started
assertFalse("Failed to receive tap after retry", retried);
retried = true;
continue;
}
success = true;
assertEquals("foo", tapped1.getPayload());
assertNull(tapped1.getHeaders().get(XdHeaders.XD_ORIGINAL_CONTENT_TYPE));
assertEquals("foo/bar", tapped1.getHeaders().get(MessageHeaders.CONTENT_TYPE));
assertEquals("foo", tapped2.getPayload());
assertNull(tapped2.getHeaders().get(XdHeaders.XD_ORIGINAL_CONTENT_TYPE));
assertEquals("foo/bar", tapped2.getHeaders().get(MessageHeaders.CONTENT_TYPE));
}
// delete one tap stream is deleted
messageBus.unbindConsumer(barTapName, module3InputChannel);
Message<?> message2 = MessageBuilder.withPayload("bar").setHeader(MessageHeaders.CONTENT_TYPE,
"foo/bar").build();
moduleOutputChannel.send(message2);
// other tap still receives messages
Message<?> tapped = module2InputChannel.receive(5000);
assertNotNull(tapped);
// Removed tap does not
assertNull(module3InputChannel.receive(1000));
// when other tap stream is deleted
messageBus.unbindConsumer(fooTapName, module2InputChannel);
// Clean up as StreamPlugin would
messageBus.unbindConsumer("baz.0", moduleInputChannel);
messageBus.unbindProducer("baz.0", moduleOutputChannel);
messageBus.unbindProducers("tap:baz.http");
assertTrue(getBindings(messageBus).isEmpty());
}
@Test
public void createInboundPubSubBeforeOutboundPubSub() throws Exception {
MessageBus messageBus = getMessageBus();
DirectChannel moduleOutputChannel = new DirectChannel();
// Test pub/sub by emulating how StreamPlugin handles taps
DirectChannel tapChannel = new DirectChannel();
QueueChannel moduleInputChannel = new QueueChannel();
QueueChannel module2InputChannel = new QueueChannel();
QueueChannel module3InputChannel = new QueueChannel();
// Create the tap first
String fooTapName = messageBus.isCapable(Capability.DURABLE_PUBSUB) ? "foo.tap:baz.http" : "tap:baz.http";
messageBus.bindPubSubConsumer(fooTapName, module2InputChannel, null);
// Then create the stream
messageBus.bindProducer("baz.0", moduleOutputChannel, null);
messageBus.bindConsumer("baz.0", moduleInputChannel, null);
moduleOutputChannel.addInterceptor(new WireTap(tapChannel));
messageBus.bindPubSubProducer("tap:baz.http", tapChannel, null);
// Another new module is using tap as an input channel
String barTapName = messageBus.isCapable(Capability.DURABLE_PUBSUB) ? "bar.tap:baz.http" : "tap:baz.http";
messageBus.bindPubSubConsumer(barTapName, module3InputChannel, null);
Message<?> message = MessageBuilder.withPayload("foo").setHeader(MessageHeaders.CONTENT_TYPE,
"foo/bar").build();
boolean success = false;
boolean retried = false;
while (!success) {
moduleOutputChannel.send(message);
Message<?> inbound = moduleInputChannel.receive(5000);
assertNotNull(inbound);
assertEquals("foo", inbound.getPayload());
assertNull(inbound.getHeaders().get(XdHeaders.XD_ORIGINAL_CONTENT_TYPE));
assertEquals("foo/bar", inbound.getHeaders().get(MessageHeaders.CONTENT_TYPE));
Message<?> tapped1 = module2InputChannel.receive(5000);
Message<?> tapped2 = module3InputChannel.receive(5000);
if (tapped1 == null || tapped2 == null) {
// listener may not have started
assertFalse("Failed to receive tap after retry", retried);
retried = true;
continue;
}
success = true;
assertEquals("foo", tapped1.getPayload());
assertNull(tapped1.getHeaders().get(XdHeaders.XD_ORIGINAL_CONTENT_TYPE));
assertEquals("foo/bar", tapped1.getHeaders().get(MessageHeaders.CONTENT_TYPE));
assertEquals("foo", tapped2.getPayload());
assertNull(tapped2.getHeaders().get(XdHeaders.XD_ORIGINAL_CONTENT_TYPE));
assertEquals("foo/bar", tapped2.getHeaders().get(MessageHeaders.CONTENT_TYPE));
}
// delete one tap stream is deleted
messageBus.unbindConsumer(barTapName, module3InputChannel);
Message<?> message2 = MessageBuilder.withPayload("bar").setHeader(MessageHeaders.CONTENT_TYPE,
"foo/bar").build();
moduleOutputChannel.send(message2);
// other tap still receives messages
Message<?> tapped = module2InputChannel.receive(5000);
assertNotNull(tapped);
// Removed tap does not
assertNull(module3InputChannel.receive(1000));
// when other tap stream is deleted
messageBus.unbindConsumer(fooTapName, module2InputChannel);
// Clean up as StreamPlugin would
messageBus.unbindConsumer("baz.0", moduleInputChannel);
messageBus.unbindProducer("baz.0", moduleOutputChannel);
messageBus.unbindProducers("tap:baz.http");
assertTrue(getBindings(messageBus).isEmpty());
}
@Test
public void testBadDynamic() throws Exception {
Properties properties = new Properties();
properties.setProperty(BusProperties.PARTITION_KEY_EXPRESSION, "'foo'");
MessageBus messageBus = getMessageBus();
try {
messageBus.bindDynamicProducer("queue:foo", properties);
fail("Exception expected");
}
catch (MessageBusException mbe) {
Assert.assertEquals("Failed to bind dynamic channel 'queue:foo' with properties " +
"{partitionKeyExpression='foo'}",
mbe.getMessage());
if (messageBus instanceof AbstractTestMessageBus) {
messageBus = ((AbstractTestMessageBus) messageBus).getCoreMessageBus();
}
assertFalse(((MessageBusSupport) messageBus).getApplicationContext().containsBean("queue:foo"));
}
}
protected Collection<?> getBindings(MessageBus testMessageBus) {
if (testMessageBus instanceof AbstractTestMessageBus) {
return getBindingsFromMsgBus(((AbstractTestMessageBus) testMessageBus).getCoreMessageBus());
}
return Collections.EMPTY_LIST;
}
protected Collection<?> getBindingsFromMsgBus(MessageBus messageBus) {
DirectFieldAccessor accessor = new DirectFieldAccessor(messageBus);
return (List<?>) accessor.getPropertyValue("bindings");
}
@SuppressWarnings({"unchecked", "rawtypes"})
protected MultiTypeCodec<Object> getCodec() {
return new PojoCodec();
}
protected abstract MessageBus getMessageBus() throws Exception;
@After
public void cleanup() {
if (testMessageBus != null) {
testMessageBus.cleanup();
}
}
/**
* If appropriate, let the bus middleware settle down a bit while binding/unbinding actually happens.
*/
protected void busBindUnbindLatency() throws InterruptedException {
// default none
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2014-2015 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.xd.dirt.integration.bus;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.messaging.MessageChannel;
/**
* Abstract class that adds test support for {@link MessageBus}.
*
* @author Ilayaperumal Gopinathan
* @author Gary Russell
*/
public abstract class AbstractTestMessageBus<C extends MessageBusSupport> implements MessageBus {
protected Set<String> queues = new HashSet<String>();
protected Set<String> topics = new HashSet<String>();
private C messageBus;
public void setMessageBus(C messageBus) {
messageBus.setIntegrationEvaluationContext(new StandardEvaluationContext());
try {
messageBus.afterPropertiesSet();
}
catch (Exception e) {
throw new RuntimeException("Failed to initialize message bus", e);
}
this.messageBus = messageBus;
}
@Override
public void bindConsumer(String name, MessageChannel moduleInputChannel, Properties properties) {
messageBus.bindConsumer(name, moduleInputChannel, properties);
queues.add(name);
}
@Override
public void bindPubSubConsumer(String name, MessageChannel inputChannel, Properties properties) {
messageBus.bindPubSubConsumer(name, inputChannel, properties);
addTopic(name);
}
@Override
public void bindProducer(String name, MessageChannel moduleOutputChannel, Properties properties) {
messageBus.bindProducer(name, moduleOutputChannel, properties);
queues.add(name);
}
@Override
public void bindPubSubProducer(String name, MessageChannel outputChannel, Properties properties) {
messageBus.bindPubSubProducer(name, outputChannel, properties);
addTopic(name);
}
@Override
public void bindRequestor(String name, MessageChannel requests, MessageChannel replies,
Properties properties) {
messageBus.bindRequestor(name, requests, replies, properties);
queues.add(name + ".requests");
}
@Override
public void bindReplier(String name, MessageChannel requests, MessageChannel replies,
Properties properties) {
messageBus.bindReplier(name, requests, replies, properties);
queues.add(name + ".requests");
}
private void addTopic(String topicName) {
topics.add("topic." + topicName);
}
public C getCoreMessageBus() {
return messageBus;
}
public abstract void cleanup();
@Override
public void unbindConsumers(String name) {
messageBus.unbindConsumers(name);
}
@Override
public void unbindProducers(String name) {
messageBus.unbindProducers(name);
}
@Override
public void unbindConsumer(String name, MessageChannel channel) {
messageBus.unbindConsumer(name, channel);
}
@Override
public void unbindProducer(String name, MessageChannel channel) {
messageBus.unbindProducer(name, channel);
}
@Override
public MessageChannel bindDynamicProducer(String name, Properties properties) {
this.queues.add(name);
return this.messageBus.bindDynamicProducer(name, properties);
}
@Override
public MessageChannel bindDynamicPubSubProducer(String name, Properties properties) {
this.topics.add(name);
return this.messageBus.bindDynamicPubSubProducer(name, properties);
}
@Override
public boolean isCapable(Capability capability) {
return this.messageBus.isCapable(capability);
}
public MessageBus getMessageBus() {
return this.messageBus;
}
}

View File

@@ -0,0 +1,114 @@
/*
* 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.xd.dirt.integration.bus;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.support.GenericMessage;
/**
* Tests for buses that use an external broker.
*
* @author Gary Russell
*/
public abstract class BrokerBusTests extends
AbstractMessageBusTests {
@Test
public void testDirectBinding() throws Exception {
MessageBus bus = getMessageBus();
Properties properties = new Properties();
properties.setProperty(BusProperties.DIRECT_BINDING_ALLOWED, "true");
DirectChannel moduleInputChannel = new DirectChannel();
moduleInputChannel.setBeanName("direct.input");
DirectChannel moduleOutputChannel = new DirectChannel();
moduleOutputChannel.setBeanName("direct.output");
bus.bindConsumer("direct.0", moduleInputChannel, null);
bus.bindProducer("direct.0", moduleOutputChannel, properties);
final AtomicReference<Thread> caller = new AtomicReference<Thread>();
final AtomicInteger count = new AtomicInteger();
moduleInputChannel.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
caller.set(Thread.currentThread());
count.incrementAndGet();
}
});
moduleOutputChannel.send(new GenericMessage<String>("foo"));
moduleOutputChannel.send(new GenericMessage<String>("foo"));
assertNotNull(caller.get());
assertSame(Thread.currentThread(), caller.get());
assertEquals(2, count.get());
assertNull(spyOn("direct.0").receive(true));
// Remove direct binding and bind producer to the bus
bus.unbindConsumers("direct.0");
busBindUnbindLatency();
Spy spy = spyOn("direct.0");
count.set(0);
moduleOutputChannel.send(new GenericMessage<String>("bar"));
moduleOutputChannel.send(new GenericMessage<String>("baz"));
Object bar = spy.receive(false);
assertEquals("bar", bar);
Object baz = spy.receive(false);
assertEquals("baz", baz);
assertEquals(0, count.get());
// Unbind producer from bus and bind directly again
caller.set(null);
bus.bindConsumer("direct.0", moduleInputChannel, null);
moduleOutputChannel.send(new GenericMessage<String>("foo"));
moduleOutputChannel.send(new GenericMessage<String>("foo"));
assertNotNull(caller.get());
assertSame(Thread.currentThread(), caller.get());
assertEquals(2, count.get());
assertNull(spy.receive(true));
bus.unbindProducers("direct.0");
bus.unbindConsumers("direct.0");
}
/**
* Create a new spy on the given 'queue'. This allows de-correlating the creation of
* the 'connection' from its actual usage, which may be needed by some implementations to
* see messages sent after connection creation.
*/
public abstract Spy spyOn(final String name);
}

View File

@@ -0,0 +1,49 @@
/*
* 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.xd.dirt.integration.bus;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.integration.support.DefaultMessageBuilderFactory;
import org.springframework.integration.support.MessageBuilderFactory;
import org.springframework.integration.support.utils.IntegrationUtils;
/**
*
* @author Gary Russell
*/
public class BusTestUtils {
private static final MessageBuilderFactory mbf = new DefaultMessageBuilderFactory();
public static final AbstractApplicationContext MOCK_AC = mock(AbstractApplicationContext.class);
public static final ConfigurableListableBeanFactory MOCK_BF = mock(ConfigurableListableBeanFactory.class);
static {
when(MOCK_BF.getBean(IntegrationUtils.INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME,
MessageBuilderFactory.class)).thenReturn(mbf);
when(MOCK_AC.getBean(IntegrationUtils.INTEGRATION_MESSAGE_BUILDER_FACTORY_BEAN_NAME,
MessageBuilderFactory.class)).thenReturn(mbf);
when(MOCK_AC.getBeanFactory()).thenReturn(MOCK_BF);
}
}

View File

@@ -0,0 +1,271 @@
/*
* 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.xd.dirt.integration.bus;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasProperty;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import org.hamcrest.CustomMatcher;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.springframework.integration.IntegrationMessageHeaderAccessor;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.xd.test.TestUtils;
/**
* Tests for buses that support partitioning.
*
* @author Gary Russell
*/
abstract public class PartitionCapableBusTests extends BrokerBusTests {
@Test
public void testBadProperties() throws Exception {
MessageBus bus = getMessageBus();
Properties properties = new Properties();
properties.put("foo", "bar");
properties.put("baz", "qux");
DirectChannel output = new DirectChannel();
try {
bus.bindProducer("badprops.0", output, properties);
}
catch (IllegalArgumentException e) {
assertThat(e.getMessage(), allOf(Matchers.containsString(bus.getClass().getSimpleName().replace("Test", "")
+ " does not support producer "),
containsString("foo"),
containsString("baz"),
containsString(" for badprops.0.")));
}
properties.remove("baz");
try {
bus.bindConsumer("badprops.0", output, properties);
}
catch (IllegalArgumentException e) {
assertThat(e.getMessage(), equalTo(bus.getClass().getSimpleName().replace("Test", "")
+ " does not support consumer property: foo for badprops.0."));
}
}
@Test
public void testPartitionedModuleSpEL() throws Exception {
MessageBus bus = getMessageBus();
Properties properties = new Properties();
properties.put("partitionKeyExpression", "payload");
properties.put("partitionSelectorExpression", "hashCode()");
properties.put(BusProperties.NEXT_MODULE_COUNT, "3");
properties.put(BusProperties.NEXT_MODULE_CONCURRENCY, "2");
DirectChannel output = new DirectChannel();
output.setBeanName("test.output");
bus.bindProducer("part.0", output, properties);
@SuppressWarnings("unchecked")
List<Binding> bindings = TestUtils.getPropertyValue(bus, "messageBus.bindings", List.class);
assertEquals(1, bindings.size());
try {
AbstractEndpoint endpoint = bindings.get(0).getEndpoint();
assertThat(getEndpointRouting(endpoint), containsString("part.0-' + headers['partition']"));
}
catch (UnsupportedOperationException ignored) {
}
properties.clear();
properties.put("concurrency", "2");
properties.put("partitionIndex", "0");
properties.put("count","3");
QueueChannel input0 = new QueueChannel();
input0.setBeanName("test.input0S");
bus.bindConsumer("part.0", input0, properties);
properties.put("partitionIndex", "1");
QueueChannel input1 = new QueueChannel();
input1.setBeanName("test.input1S");
bus.bindConsumer("part.0", input1, properties);
properties.put("partitionIndex", "2");
QueueChannel input2 = new QueueChannel();
input2.setBeanName("test.input2S");
bus.bindConsumer("part.0", input2, properties);
Message<Integer> message2 = MessageBuilder.withPayload(2)
.setHeader(IntegrationMessageHeaderAccessor.CORRELATION_ID, "foo")
.setHeader(IntegrationMessageHeaderAccessor.SEQUENCE_NUMBER, 42)
.setHeader(IntegrationMessageHeaderAccessor.SEQUENCE_SIZE, 43)
.setHeader("xdReplyChannel", "bar")
.build();
output.send(message2);
output.send(new GenericMessage<Integer>(1));
output.send(new GenericMessage<Integer>(0));
Message<?> receive0 = input0.receive(1000);
assertNotNull(receive0);
Message<?> receive1 = input1.receive(1000);
assertNotNull(receive1);
Message<?> receive2 = input2.receive(1000);
assertNotNull(receive2);
Matcher<Message<?>> fooMatcher = new CustomMatcher<Message<?>>("the message with 'foo' as its correlationId") {
@Override
public boolean matches(Object item) {
IntegrationMessageHeaderAccessor accessor = new IntegrationMessageHeaderAccessor((Message<?>) item);
boolean result = "foo".equals(accessor.getCorrelationId()) &&
42 == accessor.getSequenceNumber() &&
43 == accessor.getSequenceSize() &&
"bar".equals(accessor.getHeader("xdReplyChannel"));
return result;
}
};
if (usesExplicitRouting()) {
assertEquals(0, receive0.getPayload());
assertEquals(1, receive1.getPayload());
assertEquals(2, receive2.getPayload());
assertThat(receive2, fooMatcher);
}
else {
assertThat(Arrays.asList(
(Integer) receive0.getPayload(),
(Integer) receive1.getPayload(),
(Integer) receive2.getPayload()),
containsInAnyOrder(0, 1, 2));
@SuppressWarnings("unchecked")
Matcher<Iterable<? extends Message<?>>> containsOur3Messages = containsInAnyOrder(
fooMatcher,
hasProperty("payload", equalTo(0)),
hasProperty("payload", equalTo(1))
);
assertThat(
Arrays.asList(receive0, receive1, receive2),
containsOur3Messages);
}
bus.unbindConsumers("part.0");
bus.unbindProducers("part.0");
}
@Test
public void testPartitionedModuleJava() throws Exception {
MessageBus bus = getMessageBus();
Properties properties = new Properties();
properties.put("partitionKeyExtractorClass", "org.springframework.xd.dirt.integration.bus.PartitionTestSupport");
properties.put("partitionSelectorClass", "org.springframework.xd.dirt.integration.bus.PartitionTestSupport");
properties.put(BusProperties.NEXT_MODULE_COUNT, "3");
properties.put(BusProperties.NEXT_MODULE_CONCURRENCY, "2");
DirectChannel output = new DirectChannel();
output.setBeanName("test.output");
bus.bindProducer("partJ.0", output, properties);
@SuppressWarnings("unchecked")
List<Binding> bindings = TestUtils.getPropertyValue(bus, "messageBus.bindings", List.class);
assertEquals(1, bindings.size());
if (usesExplicitRouting()) {
AbstractEndpoint endpoint = bindings.get(0).getEndpoint();
assertThat(getEndpointRouting(endpoint), containsString("partJ.0-' + headers['partition']"));
}
properties.clear();
properties.put("concurrency", "2");
properties.put("count","3");
properties.put("partitionIndex", "0");
QueueChannel input0 = new QueueChannel();
input0.setBeanName("test.input0J");
bus.bindConsumer("partJ.0", input0, properties);
properties.put("partitionIndex", "1");
QueueChannel input1 = new QueueChannel();
input1.setBeanName("test.input1J");
bus.bindConsumer("partJ.0", input1, properties);
properties.put("partitionIndex", "2");
QueueChannel input2 = new QueueChannel();
input2.setBeanName("test.input2J");
bus.bindConsumer("partJ.0", input2, properties);
output.send(new GenericMessage<Integer>(2));
output.send(new GenericMessage<Integer>(1));
output.send(new GenericMessage<Integer>(0));
Message<?> receive0 = input0.receive(1000);
assertNotNull(receive0);
Message<?> receive1 = input1.receive(1000);
assertNotNull(receive1);
Message<?> receive2 = input2.receive(1000);
assertNotNull(receive2);
if (usesExplicitRouting()) {
assertEquals(0, receive0.getPayload());
assertEquals(1, receive1.getPayload());
assertEquals(2, receive2.getPayload());
}
else {
assertThat(Arrays.asList(
(Integer) receive0.getPayload(),
(Integer) receive1.getPayload(),
(Integer) receive2.getPayload()),
containsInAnyOrder(0, 1, 2));
}
bus.unbindConsumers("partJ.0");
bus.unbindProducers("partJ.0");
}
/**
* Implementations should return whether the bus under test uses "explicit" routing (e.g. Rabbit)
* whereby XD is responsible for assigning a partition and knows which exact consumer will receive the
* message (i.e. honor "partitionIndex") or "implicit" routing (e.g. Kafka) whereby the only guarantee
* is that messages will be spread, but we don't control exactly which consumer gets which message.
*/
protected abstract boolean usesExplicitRouting();
/**
* For implementations that rely on explicit routing, return the routing expression.
*/
protected String getEndpointRouting(AbstractEndpoint endpoint) {
throw new UnsupportedOperationException();
}
/**
* For implementations that rely on explicit routing, return the routing expression.
*/
protected String getPubSubEndpointRouting(AbstractEndpoint endpoint) {
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.xd.dirt.integration.bus;
import org.springframework.messaging.Message;
/**
*
* @author Gary Russell
*/
public class PartitionTestSupport implements PartitionKeyExtractorStrategy, PartitionSelectorStrategy {
@Override
public int selectPartition(Object key, int divisor) {
return key.hashCode() % divisor;
}
@Override
public Object extractKey(Message<?> message) {
return message.getPayload();
}
}

View File

@@ -0,0 +1,13 @@
package org.springframework.xd.dirt.integration.bus;
/**
* Represents an out-of-band connection to the underlying middleware,
* so that tests can check that some messages actually do (or do not)
* transit through it.
*
* @author Eric Bottard
*/
public interface Spy {
public Object receive(boolean expectNull) throws Exception;
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xd.test;
import static org.junit.Assert.fail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.junit.Assume;
import org.junit.Rule;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.springframework.util.Assert;
/**
* Abstract base class for JUnit {@link Rule}s that detect the presence of some external resource. If the resource is
* indeed present, it will be available during the test lifecycle through {@link #getResource()}. If it is not, tests
* will either fail or be skipped, depending on the value of system property {@value #XD_EXTERNAL_SERVERS_REQUIRED}.
*
* @author Eric Bottard
* @author Gary Russell
*/
public abstract class AbstractExternalResourceTestSupport<R> implements TestRule {
public static final String XD_EXTERNAL_SERVERS_REQUIRED = "XD_EXTERNAL_SERVERS_REQUIRED";
protected R resource;
private String resourceDescription;
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
protected AbstractExternalResourceTestSupport(String resourceDescription) {
Assert.hasText(resourceDescription, "resourceDescription is required");
this.resourceDescription = resourceDescription;
}
@Override
public Statement apply(final Statement base, Description description) {
try {
obtainResource();
}
catch (Exception e) {
maybeCleanup();
return failOrSkip(e);
}
return new Statement() {
@Override
public void evaluate() throws Throwable {
try {
base.evaluate();
}
finally {
try {
cleanupResource();
}
catch (Exception ignored) {
logger.warn("Exception while trying to cleanup proper resource", ignored);
}
}
}
};
}
private Statement failOrSkip(final Exception e) {
String serversRequired = System.getenv(XD_EXTERNAL_SERVERS_REQUIRED);
if ("true".equalsIgnoreCase(serversRequired)) {
logger.error(resourceDescription + " IS REQUIRED BUT NOT AVAILABLE", e);
fail(resourceDescription + " IS NOT AVAILABLE");
// Never reached, here to satisfy method signature
return null;
}
else {
logger.error(resourceDescription + " IS NOT AVAILABLE, SKIPPING TESTS", e);
return new Statement() {
@Override
public void evaluate() throws Throwable {
Assume.assumeTrue("Skipping test due to " + resourceDescription + " not being available " + e, false);
}
};
}
}
private void maybeCleanup() {
if (resource != null) {
try {
cleanupResource();
}
catch (Exception ignored) {
logger.warn("Exception while trying to cleanup failed resource", ignored);
}
}
}
public R getResource() {
return resource;
}
/**
* Perform cleanup of the {@link #resource} field, which is guaranteed to be non null.
*
* @throws Exception any exception thrown by this method will be logged and swallowed
*/
protected abstract void cleanupResource() throws Exception;
/**
* Try to obtain and validate a resource. Implementors should either set the {@link #resource} field with a valid
* resource and return normally, or throw an exception.
*/
protected abstract void obtainResource() throws Exception;
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2015 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.xd.test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.util.Assert;
/**
* Copy of class in org.springframework.amqp.utils.test to avoid dependency on spring-amqp
*/
public class TestUtils {
/**
* Uses nested {@link DirectFieldAccessor}s to obtain a property using dotted notation to traverse fields; e.g.
* "foo.bar.baz" will obtain a reference to the baz field of the bar field of foo. Adopted from Spring Integration.
* @param root The object.
* @param propertyPath The path.
* @return The field.
*/
public static Object getPropertyValue(Object root, String propertyPath) {
Object value = null;
DirectFieldAccessor accessor = new DirectFieldAccessor(root);
String[] tokens = propertyPath.split("\\.");
for (int i = 0; i < tokens.length; i++) {
value = accessor.getPropertyValue(tokens[i]);
if (value != null) {
accessor = new DirectFieldAccessor(value);
}
else if (i == tokens.length - 1) {
return null;
}
else {
throw new IllegalArgumentException("intermediate property '" + tokens[i] + "' is null");
}
}
return value;
}
@SuppressWarnings("unchecked")
public static <T> T getPropertyValue(Object root, String propertyPath, Class<T> type) {
Object value = getPropertyValue(root, propertyPath);
if (value != null) {
Assert.isAssignable(type, value.getClass());
}
return (T) value;
}
}

View File

@@ -0,0 +1,302 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xd.dirt.integration.bus;
import java.io.IOException;
import java.util.Collections;
import java.util.Properties;
import org.junit.Before;
import org.junit.Test;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.ContentTypeResolver;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
import org.springframework.xd.dirt.integration.bus.MessageBusSupport.JavaClassMimeTypeConversion;
import org.springframework.xd.dirt.integration.bus.serializer.kryo.PojoCodec;
import org.springframework.xd.tuple.DefaultTuple;
import org.springframework.xd.tuple.Tuple;
import org.springframework.xd.tuple.TupleBuilder;
import org.springframework.xd.tuple.serializer.kryo.TupleKryoRegistrar;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
/**
* @author Gary Russell
* @author David Turanski
*/
public class MessageBusSupportTests {
private ContentTypeResolver contentTypeResolver = new StringConvertingContentTypeResolver();
private final TestMessageBus messageBus = new TestMessageBus();
@SuppressWarnings({"unchecked", "rawtypes"})
@Before
public void setUp() {
messageBus.setCodec(new PojoCodec(new TupleKryoRegistrar()));
}
@Test
public void testBytesPassThru() {
byte[] payload = "foo".getBytes();
Message<byte[]> message = MessageBuilder.withPayload(payload).build();
MessageValues converted = messageBus.serializePayloadIfNecessary(message
);
assertSame(payload, converted.getPayload());
Message<?> convertedMessage = converted.toMessage();
assertSame(payload, convertedMessage.getPayload());
assertEquals(MimeTypeUtils.APPLICATION_OCTET_STREAM,
contentTypeResolver.resolve(convertedMessage.getHeaders()));
MessageValues reconstructed = messageBus.deserializePayloadIfNecessary(convertedMessage);
payload = (byte[]) reconstructed.getPayload();
assertSame(converted.getPayload(), payload);
assertNull(reconstructed.get(XdHeaders.XD_ORIGINAL_CONTENT_TYPE));
}
@Test
public void testBytesPassThruContentType() {
byte[] payload = "foo".getBytes();
Message<byte[]> message = MessageBuilder.withPayload(payload)
.setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE)
.build();
MessageValues messageValues = messageBus.serializePayloadIfNecessary(message
);
Message<?> converted = messageValues.toMessage();
assertSame(payload, converted.getPayload());
assertEquals(MimeTypeUtils.APPLICATION_OCTET_STREAM,
contentTypeResolver.resolve(converted.getHeaders()));
MessageValues reconstructed = messageBus.deserializePayloadIfNecessary(converted);
payload = (byte[]) reconstructed.getPayload();
assertSame(converted.getPayload(), payload);
assertEquals(MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE,
reconstructed.get(MessageHeaders.CONTENT_TYPE));
assertNull(reconstructed.get(XdHeaders.XD_ORIGINAL_CONTENT_TYPE));
}
@Test
public void testString() throws IOException {
MessageValues convertedValues = messageBus.serializePayloadIfNecessary(
new GenericMessage<String>("foo"));
Message<?> converted = convertedValues.toMessage();
assertEquals(MimeTypeUtils.TEXT_PLAIN,
contentTypeResolver.resolve(converted.getHeaders()));
MessageValues reconstructed = messageBus.deserializePayloadIfNecessary(converted);
assertEquals("foo", reconstructed.getPayload());
assertNull(reconstructed.get(MessageHeaders.CONTENT_TYPE));
}
@Test
public void testContentTypePreserved() throws IOException {
Message<String> inbound = MessageBuilder.withPayload("{\"foo\":\"foo\"}")
.copyHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON))
.build();
MessageValues convertedValues = messageBus.serializePayloadIfNecessary(
inbound);
Message<?> converted = convertedValues.toMessage();
assertEquals(MimeTypeUtils.TEXT_PLAIN,
contentTypeResolver.resolve(converted.getHeaders()));
assertEquals(MimeTypeUtils.APPLICATION_JSON,
converted.getHeaders().get(XdHeaders.XD_ORIGINAL_CONTENT_TYPE));
MessageValues reconstructed = messageBus.deserializePayloadIfNecessary(converted);
assertEquals("{\"foo\":\"foo\"}", reconstructed.getPayload());
assertEquals(MimeTypeUtils.APPLICATION_JSON, reconstructed.get(MessageHeaders.CONTENT_TYPE));
}
@Test
public void testPojoSerialization() {
MessageValues convertedValues = messageBus.serializePayloadIfNecessary(
new GenericMessage<Foo>(new Foo("bar"))
);
Message<?> converted = convertedValues.toMessage();
MimeType mimeType = contentTypeResolver.resolve(converted.getHeaders());
assertEquals("application", mimeType.getType());
assertEquals("x-java-object", mimeType.getSubtype());
assertEquals(Foo.class.getName(), mimeType.getParameter("type"));
MessageValues reconstructed = messageBus.deserializePayloadIfNecessary(converted);
assertEquals("bar", ((Foo) reconstructed.getPayload()).getBar());
assertNull(reconstructed.get(MessageHeaders.CONTENT_TYPE));
}
@Test
public void testPojoWithXJavaObjectMimeTypeNoType() {
MessageValues convertedValues = messageBus.serializePayloadIfNecessary(
new GenericMessage<Foo>(new Foo("bar"))
);
Message<?> converted = convertedValues.toMessage();
MimeType mimeType = contentTypeResolver.resolve(converted.getHeaders());
assertEquals("application", mimeType.getType());
assertEquals("x-java-object", mimeType.getSubtype());
assertEquals(Foo.class.getName(), mimeType.getParameter("type"));
MessageValues reconstructed = messageBus.deserializePayloadIfNecessary(converted);
assertEquals("bar", ((Foo) reconstructed.getPayload()).getBar());
assertNull(reconstructed.get(MessageHeaders.CONTENT_TYPE));
}
@Test
public void testPojoWithXJavaObjectMimeTypeExplicitType() {
MessageValues convertedValues = messageBus.serializePayloadIfNecessary(
new GenericMessage<Foo>(new Foo("bar"))
);
Message<?> converted = convertedValues.toMessage();
MimeType mimeType = contentTypeResolver.resolve(converted.getHeaders());
assertEquals("application", mimeType.getType());
assertEquals("x-java-object", mimeType.getSubtype());
assertEquals(Foo.class.getName(), mimeType.getParameter("type"));
MessageValues reconstructed = messageBus.deserializePayloadIfNecessary(converted);
assertEquals("bar", ((Foo) reconstructed.getPayload()).getBar());
assertNull(reconstructed.get(MessageHeaders.CONTENT_TYPE));
}
@Test
public void testTupleSerialization() {
Tuple payload = TupleBuilder.tuple().of("foo", "bar");
MessageValues convertedValues = messageBus.serializePayloadIfNecessary(new GenericMessage<Tuple>(payload)
);
Message<?> converted = convertedValues.toMessage();
MimeType mimeType = contentTypeResolver.resolve(converted.getHeaders());
assertEquals("application", mimeType.getType());
assertEquals("x-java-object", mimeType.getSubtype());
assertEquals(DefaultTuple.class.getName(), mimeType.getParameter("type"));
MessageValues reconstructed = messageBus.deserializePayloadIfNecessary(converted);
assertEquals("bar", ((Tuple) reconstructed.getPayload()).getString("foo"));
assertNull(reconstructed.get(MessageHeaders.CONTENT_TYPE));
}
@Test
public void mimeTypeIsSimpleObject() throws ClassNotFoundException {
MimeType mt = JavaClassMimeTypeConversion.mimeTypeFromObject(new Object());
String className = JavaClassMimeTypeConversion.classNameFromMimeType(mt);
assertEquals(Object.class, Class.forName(className));
}
@Test
public void mimeTypeIsObjectArray() throws ClassNotFoundException {
MimeType mt = JavaClassMimeTypeConversion.mimeTypeFromObject(new String[0]);
String className = JavaClassMimeTypeConversion.classNameFromMimeType(mt);
assertEquals(String[].class, Class.forName(className));
}
@Test
public void mimeTypeIsMultiDimensionalObjectArray() throws ClassNotFoundException {
MimeType mt = JavaClassMimeTypeConversion.mimeTypeFromObject(new String[0][0][0]);
String className = JavaClassMimeTypeConversion.classNameFromMimeType(mt);
assertEquals(String[][][].class, Class.forName(className));
}
@Test
public void mimeTypeIsPrimitiveArray() throws ClassNotFoundException {
MimeType mt = JavaClassMimeTypeConversion.mimeTypeFromObject(new int[0]);
String className = JavaClassMimeTypeConversion.classNameFromMimeType(mt);
assertEquals(int[].class, Class.forName(className));
}
@Test
public void mimeTypeIsMultiDimensionalPrimitiveArray() throws ClassNotFoundException {
MimeType mt = JavaClassMimeTypeConversion.mimeTypeFromObject(new int[0][0][0]);
String className = JavaClassMimeTypeConversion.classNameFromMimeType(mt);
assertEquals(int[][][].class, Class.forName(className));
}
public static class Foo {
private String bar;
public Foo() {
}
public Foo(String bar) {
this.bar = bar;
}
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
public static class Bar {
private String foo;
public Bar() {
}
public Bar(String foo) {
this.foo = foo;
}
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
}
public class TestMessageBus extends MessageBusSupport {
@Override
public void bindConsumer(String name, MessageChannel channel, Properties properties) {
}
@Override
public void bindPubSubConsumer(String name, MessageChannel moduleInputChannel,
Properties properties) {
}
@Override
public void bindPubSubProducer(String name, MessageChannel moduleOutputChannel,
Properties properties) {
}
@Override
public void bindProducer(String name, MessageChannel channel, Properties properties) {
}
@Override
public void bindRequestor(String name, MessageChannel requests, MessageChannel replies,
Properties properties) {
}
@Override
public void bindReplier(String name, MessageChannel requests, MessageChannel replies,
Properties properties) {
}
}
}