/*
* Copyright 2017 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.dsl.transformers;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.MessageRejectedException;
import org.springframework.integration.annotation.Transformer;
import org.springframework.integration.channel.FixedSubscriberChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.codec.Codec;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.Transformers;
import org.springframework.integration.dsl.channel.MessageChannels;
import org.springframework.integration.handler.advice.IdempotentReceiverInterceptor;
import org.springframework.integration.selector.MetadataStoreSelector;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.transformer.DecodingTransformer;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author Artem Bilan
* @author Ian Bondoc
* @author Gary Russell
*
* @since 5.0
*/
@RunWith(SpringRunner.class)
@DirtiesContext
public class TransformerTests {
@Autowired
@Qualifier("enricherInput")
private FixedSubscriberChannel enricherInput;
@Autowired
@Qualifier("enricherInput2")
private FixedSubscriberChannel enricherInput2;
@Autowired
@Qualifier("enricherInput3")
private FixedSubscriberChannel enricherInput3;
@Test
public void testContentEnricher() {
QueueChannel replyChannel = new QueueChannel();
Message<?> message = MessageBuilder.withPayload(new TestPojo("Bar"))
.setHeader(MessageHeaders.REPLY_CHANNEL, replyChannel)
.build();
this.enricherInput.send(message);
Message<?> receive = replyChannel.receive(5000);
assertNotNull(receive);
assertEquals("Bar Bar", receive.getHeaders().get("foo"));
Object payload = receive.getPayload();
assertThat(payload, instanceOf(TestPojo.class));
TestPojo result = (TestPojo) payload;
assertEquals("Bar Bar", result.getName());
assertNotNull(result.getDate());
assertThat(new Date(), Matchers.greaterThanOrEqualTo(result.getDate()));
}
@Test
public void testContentEnricher2() {
QueueChannel replyChannel = new QueueChannel();
Message<?> message = MessageBuilder.withPayload(new TestPojo("Bar"))
.setHeader(MessageHeaders.REPLY_CHANNEL, replyChannel)
.build();
this.enricherInput2.send(message);
Message<?> receive = replyChannel.receive(5000);
assertNotNull(receive);
assertNull(receive.getHeaders().get("foo"));
Object payload = receive.getPayload();
assertThat(payload, instanceOf(TestPojo.class));
TestPojo result = (TestPojo) payload;
assertEquals("Bar Bar", result.getName());
assertNotNull(result.getDate());
assertThat(new Date(), Matchers.greaterThanOrEqualTo(result.getDate()));
}
@Test
public void testContentEnricher3() {
QueueChannel replyChannel = new QueueChannel();
Message<?> message = MessageBuilder.withPayload(new TestPojo("Bar"))
.setHeader(MessageHeaders.REPLY_CHANNEL, replyChannel)
.build();
this.enricherInput3.send(message);
Message<?> receive = replyChannel.receive(5000);
assertNotNull(receive);
assertEquals("Bar Bar", receive.getHeaders().get("foo"));
Object payload = receive.getPayload();
assertThat(payload, instanceOf(TestPojo.class));
TestPojo result = (TestPojo) payload;
assertEquals("Bar", result.getName());
assertNull(result.getDate());
}
@Autowired
@Qualifier("replyProducingSubFlowEnricher.input")
private MessageChannel replyProducingSubFlowEnricherInput;
@Autowired
@Qualifier("terminatingSubFlowEnricher.input")
private MessageChannel terminatingSubFlowEnricherInput;
@Autowired
@Qualifier("subFlowTestReplyChannel")
private PollableChannel subFlowTestReplyChannel;
@Test
public void testSubFlowContentEnricher() {
this.replyProducingSubFlowEnricherInput.send(MessageBuilder.withPayload(new TestPojo("Bar")).build());
Message<?> receive = this.subFlowTestReplyChannel.receive(5000);
assertNotNull(receive);
assertEquals("Foo Bar (Reply Producing)", receive.getHeaders().get("foo"));
Object payload = receive.getPayload();
assertThat(payload, instanceOf(TestPojo.class));
TestPojo result = (TestPojo) payload;
assertThat(result.getName(), is("Foo Bar (Reply Producing)"));
this.terminatingSubFlowEnricherInput.send(MessageBuilder.withPayload(new TestPojo("Bar")).build());
receive = this.subFlowTestReplyChannel.receive(5000);
assertNotNull(receive);
assertEquals("Foo Bar (Terminating)", receive.getHeaders().get("foo"));
payload = receive.getPayload();
assertThat(payload, instanceOf(TestPojo.class));
result = (TestPojo) payload;
assertThat(result.getName(), is("Foo Bar (Terminating)"));
}
@Autowired
@Qualifier("encodingFlow.input")
private MessageChannel encodingFlowInput;
@Autowired
@Qualifier("decodingFlow.input")
private MessageChannel decodingFlowInput;
@Autowired
@Qualifier("codecReplyChannel")
private PollableChannel codecReplyChannel;
@Test
public void testCodec() throws Exception {
this.encodingFlowInput.send(new GenericMessage<>("bar"));
Message<?> receive = this.codecReplyChannel.receive(10000);
assertNotNull(receive);
assertThat(receive.getPayload(), instanceOf(byte[].class));
byte[] transformed = (byte[]) receive.getPayload();
assertArrayEquals("foo".getBytes(), transformed);
this.decodingFlowInput.send(new GenericMessage<>(transformed));
receive = this.codecReplyChannel.receive(10000);
assertNotNull(receive);
assertEquals(42, receive.getPayload());
}
@Autowired
@Qualifier("pojoTransformFlow.input")
private MessageChannel pojoTransformFlowInput;
@Autowired
private PollableChannel idempotentDiscardChannel;
@Test
public void transformWithHeader() {
QueueChannel replyChannel = new QueueChannel();
Message<?> message = MessageBuilder.withPayload("Foo")
.setReplyChannel(replyChannel)
.build();
this.pojoTransformFlowInput.send(message);
Message<?> receive = replyChannel.receive(10000);
assertNotNull(receive);
assertEquals("FooBar", receive.getPayload());
try {
this.pojoTransformFlowInput.send(message);
fail("MessageRejectedException expected");
}
catch (Exception e) {
assertThat(e, instanceOf(MessageRejectedException.class));
assertThat(e.getMessage(), containsString("IdempotentReceiver"));
assertThat(e.getMessage(), containsString("rejected duplicate Message"));
}
assertNotNull(this.idempotentDiscardChannel.receive(10000));
}
@Configuration
@EnableIntegration
public static class ContextConfiguration {
@Bean
public IntegrationFlow enricherFlow() {
return IntegrationFlows.from("enricherInput", true)
.enrich(e -> e.requestChannel("enrichChannel")
.requestPayloadExpression("payload")
.shouldClonePayload(false)
.propertyExpression("name", "payload['name']")
.propertyFunction("date", m -> new Date())
.headerExpression("foo", "payload['name']")
)
.get();
}
@Bean
public IntegrationFlow enricherFlow2() {
return IntegrationFlows.from("enricherInput2", true)
.enrich(e -> e.requestChannel("enrichChannel")
.requestPayloadExpression("payload")
.shouldClonePayload(false)
.propertyExpression("name", "payload['name']")
.propertyExpression("date", "new java.util.Date()")
)
.get();
}
@Bean
public IntegrationFlow enricherFlow3() {
return IntegrationFlows.from("enricherInput3", true)
.enrich(e -> e.requestChannel("enrichChannel")
.requestPayload(Message::getPayload)
.shouldClonePayload(false)
.<Map<String, String>>headerFunction("foo", m -> m.getPayload().get("name")))
.get();
}
@Bean
public IntegrationFlow enrichFlow() {
return IntegrationFlows.from("enrichChannel")
.<TestPojo, Map<?, ?>>transform(p -> Collections.singletonMap("name", p.getName() + " Bar"))
.get();
}
@Bean
public PollableChannel receivedChannel() {
return new QueueChannel();
}
@Bean
public PollableChannel codecReplyChannel() {
return new QueueChannel();
}
@Bean
public IntegrationFlow encodingFlow() {
return f -> f
.transform(Transformers.encoding(new MyCodec()))
.channel("codecReplyChannel");
}
@Bean
public IntegrationFlow decodingFlow() {
// TODO: Stored in an unnecessary variable to work around an eclipse type inference issue.
DecodingTransformer<Integer> transformer = Transformers.decoding(new MyCodec(), m -> Integer.class);
return f -> f
.transform(transformer)
.channel("codecReplyChannel");
}
@Bean
public IntegrationFlow pojoTransformFlow() {
return f -> f
.enrichHeaders(h -> h
.header("Foo", "Bar")
.advice(idempotentReceiverInterceptor()))
.transform(new PojoTransformer());
}
@Bean
public PollableChannel idempotentDiscardChannel() {
return new QueueChannel();
}
@Bean
public IdempotentReceiverInterceptor idempotentReceiverInterceptor() {
IdempotentReceiverInterceptor idempotentReceiverInterceptor =
new IdempotentReceiverInterceptor(new MetadataStoreSelector(m -> m.getPayload().toString()));
idempotentReceiverInterceptor.setDiscardChannel(idempotentDiscardChannel());
idempotentReceiverInterceptor.setThrowExceptionOnRejection(true);
return idempotentReceiverInterceptor;
}
@Bean
public PollableChannel subFlowTestReplyChannel() {
return new QueueChannel();
}
@Bean
public IntegrationFlow replyProducingSubFlowEnricher(SomeService someService) {
return f -> f
.enrich(e -> e.<TestPojo>requestPayload(p -> p.getPayload().getName())
.requestSubFlow(sf -> sf
.<String>handle((p, h) -> someService.someServiceMethod(p)))
.<String>headerFunction("foo", Message::getPayload)
.propertyFunction("name", Message::getPayload))
.channel("subFlowTestReplyChannel");
}
@Bean
public MessageChannel enricherReplyChannel() {
return MessageChannels.direct().get();
}
@Bean
public IntegrationFlow terminatingSubFlowEnricher(SomeService someService) {
return f -> f
.enrich(e -> e.<TestPojo>requestPayload(p -> p.getPayload().getName())
.requestSubFlow(sf -> sf
.handle(someService::aTerminatingServiceMethod))
.replyChannel("enricherReplyChannel")
.<String>headerFunction("foo", Message::getPayload)
.propertyFunction("name", Message::getPayload))
.channel("subFlowTestReplyChannel");
}
@Bean
public SomeService someService() {
return new SomeService();
}
}
private static final class TestPojo {
private String name;
private Date date;
private TestPojo(String name) {
this.name = name;
}
public String getName() {
return name;
}
@SuppressWarnings("unused")
public void setName(String name) {
this.name = name;
}
public Date getDate() {
return date;
}
@SuppressWarnings("unused")
public void setDate(Date date) {
this.date = date;
}
}
public static class MyCodec implements Codec {
@Override
public void encode(Object object, OutputStream outputStream) throws IOException {
}
@Override
public byte[] encode(Object object) throws IOException {
return "foo".getBytes();
}
@Override
public <T> T decode(InputStream inputStream, Class<T> type) throws IOException {
return null;
}
@SuppressWarnings("unchecked")
@Override
public <T> T decode(byte[] bytes, Class<T> type) throws IOException {
return (T) (type.equals(String.class) ? new String(bytes) :
type.equals(Integer.class) ? Integer.valueOf(42) : Integer.valueOf(43));
}
}
public static class PojoTransformer {
@Transformer
public String transform(String payload, @Header("Foo") String header) {
return payload + header;
}
}
public static class SomeService {
@Autowired
@Qualifier("enricherReplyChannel")
public MessageChannel enricherReplyChannel;
public String someServiceMethod(String value) {
return "Foo ".concat(value).concat(" (Reply Producing)");
}
public void aTerminatingServiceMethod(Message<?> message) {
String payload = "Foo ".concat(message.getPayload().toString()).concat(" (Terminating)");
enricherReplyChannel.send(MessageBuilder.withPayload(payload).copyHeaders(message.getHeaders()).build());
}
}
}