1

Let's say I have a custom module in Spring XD (I'm using Spring XD + Spring Integration + hibernate). The module basically gets something from the DB (let's say I store it using an hibernate entity, so I use an objet called "DataFromDB"). DataFromDB is a list, then I get each element from the list and I want to send it using something like:

String payload = convertDataFromDBToJson(record);
return MessageBuilder.createMessage(payload, message.getHeaders());
  • The problem is every time I have to send a message I have to return a Message.
  • So I would like to loop over the DataFromDB list and send each element as a message.

Is there a way to send multiple messages?

Edit:

I just created a little example according to the comments trying to replicate the scenario. This is what I have:

my transformer class:

public class TransformerClass {
public Collection<Message<?>> transformerMethod(Message<?> message) {
    List<Message<?>> messages = new ArrayList<Message<?>>();
    messages.add(new GenericMessage<>("foo"));
    messages.add(new GenericMessage<>("bar"));
    return messages;
}

My xml configuration:

    <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:int="http://www.springframework.org/schema/integration"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/tx  http://www.springframework.org/schema/tx/spring-tx.xsd">

    <tx:annotation-driven />
    <int:channel id="input" />

    <bean id="transFormerClass" class="myModule.TransformerClass">
    </bean>

    <int:transformer input-channel="input" output-channel="output" ref="transFormerClass" method="transformerMethod"/>

    <int:channel id="output"/>

</beans>

My test class:

    @ModuleName(value = "someModule", type = ModuleType.processor)
public class TransformerClassTest extends xDTest {
    private String streamName = "myStream";
    private String chainTest = "someModule";

    @SuppressWarnings("unchecked")
    @Test
    public void testPartnerNotification() throws IOException {

        this.chain = SingleNodeProcessingChainSupport.chain(application, streamName, chainTest);
        //Just to send something to the module as a input.
        Message<String> input = MessageBuilder.createMessage("hello world", buildHeaders());
        this.chain.send(input);
        //Receiving a Single message
        Message<String> result = (Message<String>) chain.receive(5000);
        System.out.println("Result: " + result);
    }

    private MessageHeaders buildHeaders() {
        Map<String, Object> hashMap = new HashMap<String,Object>();
        hashMap.put("test", "testing");
        MessageHeaders headers = new MessageHeaders(hashMap);
        return headers;
    }    
}

Output is:

Result: GenericMessage [payload=[GenericMessage [payload=foo, headers={timestamp=1475072951345, id=7b6c79a2-db85-563e-c238-262a31141456}], GenericMessage [payload=bar, headers={timestamp=1475072951345, id=31c8ef0e-3513-b95e-3a25-4fd3550f2fea}]], headers={timestamp=1475072951347, id=f90d94c4-e323-70ed-62ee-4b8bce64814d, test=testing}]

I'm using Spring Integration 4.2.2.RELEASE.

Columb1a
  • 463
  • 2
  • 11
  • 25
  • Have you tried *just using a loop*? – chrylis -cautiouslyoptimistic- Sep 26 '16 at 17:19
  • Not yet to be honest. But doesn't make sense for me to do that, since in order to send the message I have to return `MessageBuilder.createMessage(payload, message.getHeaders());` Therefore in that case, just one message (then only one element in the lis)t would be sent. – Columb1a Sep 26 '16 at 18:08

1 Answers1

5

If you return a Collection<Message<?>>, they will be sent as separate messages.

EDIT

@EnableIntegration
@Configuration
public class So39708474Application {

    @Bean
    public DirectChannel input() {
        return new DirectChannel();
    }

    @Bean
    public DirectChannel output() {
        return new DirectChannel();
    }

    @Bean
    public Foo foo() {
        return new Foo();
    }

    public static class Foo {

        @ServiceActivator(inputChannel = "input", outputChannel = "output")
        public Collection<Message<?>> handle(Message<?> in) {
            List<Message<?>> messages = new ArrayList<Message<?>>();
            messages.add(new GenericMessage<>("foo"));
            messages.add(new GenericMessage<>("bar"));
            return messages;
        }

    }

}

and

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = So39708474Application.class)
public class So39708474ApplicationTests {

    @Autowired
    private MessageChannel input;

    @Autowired
    private SubscribableChannel output;

    @Test
    public void contextLoads() {
        AtomicInteger count = new AtomicInteger();
        this.output.subscribe(m -> {
            count.incrementAndGet();
            System.out.println(m);
        });
        this.input.send(new GenericMessage<>("test"));
        assertEquals(2, count.get());
    }

}
Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • So, after having returned a `Collection>` I think I will have to create a new SpringXD module to split the messages that I'm receiving. After that I will be able to do whatever I want with each of those messages. – Columb1a Sep 26 '16 at 19:37
  • 2
    I am confused - you said you already have a custom module and want to create multiple output messages for one input message. That's exactly what will happen; you don't need a splitter. If your return `Collection` you will, but `Collection>` tells the framework you want to send multiple outputs. – Gary Russell Sep 26 '16 at 21:09
  • Oh, Awesome!.Many thanks for the clarification. That is exactly what I was looking for. So then, I just have to use `Collection>`, and any module that receives as an input the output of my current module, will receive a single message. – Columb1a Sep 26 '16 at 23:06
  • Hey @Gary Russell. I just created a Junit test to check the output related to what I implemented. I hardocoded my logic to populate the collection that I'm returning with 2 messages. I noticed that I'm receiving one message which contains 2 messages instead of 2 messages separately. `Message input = MessageBuilder.createMessage(alertDetail, buildHeaders()); this.chain.send(input); Message> result = chain.receive(5000); System.out.println("Result: " + result);` – Columb1a Sep 27 '16 at 19:47
  • You need to show the code (including the test) - edit your question. Also what version of Spring Integration are you using? I tested with 4.3, 4.2 and 4.1. But the logic to emit a collection of messages individually has been there pretty much for ever. – Gary Russell Sep 27 '16 at 20:12
  • I don't need to see any logic, I just need to see the class and method signatures and exactly how you are testing it. And, how you are configuring the application (the XML or @Bean definitions). I edited my answer with an example that shows 2 messages output per input. – Gary Russell Sep 27 '16 at 20:45
  • I've just added an example with what I just did. When I perform `chain.receive(5000)` I'm expecting to receive one message, but actually I'm receiving one message and inside of it, two more messages, which basically is the list that was sent to the output. – Columb1a Sep 28 '16 at 14:49
  • 1
    Ah - transformers are "special" - the framework assumes that a transformer takes complete responsibility for what is output. Change it to a `` and it will do what you want. In this case you are not transforming a message, you are invoking some service, so a service activator is the correct item to use. – Gary Russell Sep 28 '16 at 15:05