4

We have a case where in our spring integration splitter logic where it could return an empty ArrayList thus providing no data to be routed. How should we handle this scenario and reroute it to a different channel when there is an empty ArrayList returned from the splitter.

UPDATE :

As shown by Artem I have created a custom advice and have got the rerouting to be done successfully however there are still two issues, where the second one describe is the blocker issue listed

The current class that I have is as follows

public class EmptyListRequestHandlerAdvice extends AbstractRequestHandlerAdvice {

    private final MessagingTemplate messagingTemplate = new MessagingTemplate();

    public EmptyListRequestHandlerAdvice(MessageChannel emptyListChannel) {
        this.messagingTemplate.setDefaultChannel(emptyListChannel);
    }

    @Override
    protected Object doInvoke(ExecutionCallback callback, Object target,
            Message<?> message) throws Exception {
        Object result = callback.execute();

        if (result==null) {
            this.messagingTemplate.convertAndSend(new ArrayList<InventoryHotel>());
        }

        return result;
    }

Whenever the splitter returns a new empty array list the result of "callback.execute() is null. However whenever the returned array list from the splitter is not empty the returned result is of type org.springframework.integration.util.FunctionIterator and not a collection type. Any idea on if it is possible to get a collection returned in both cases instead of null or function iterator? Atleast avoid getting a function iterator and get an array list.

Additionally when a valid channel name is set to the constructor and a message is sent through that channel to the transformer method the transformer method is successfully executed however when it returned a value the following error occurs

org.springframework.messaging.MessagingException: org.springframework.messaging.core.DestinationResolutionException: no output-channel or replyChannel header available
    at org.springframework.integration.dispatcher.AbstractDispatcher.wrapExceptionIfNecessary(AbstractDispatcher.java:133)
    at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:120)
    at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:101)
    at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:97)
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77)
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:255)

Any suggestions on why this occurs and how to resolve it.

Regards, Milinda

MilindaD
  • 7,533
  • 9
  • 43
  • 63

1 Answers1

3

Well, you can overcome it with <request-handler-advice-chain> for the <splitter> and of course some custom AbstractRequestHandlerAdvice:

public class EmptyListRequestHandlerAdvice extends AbstractRequestHandlerAdvice {

    private final MessagingTemplate messagingTemplate = new MessagingTemplate();

    public EmptyListRequestHandlerAdvice(MessageChannel emptyListChannel) {
         this.messagingTemplate.setDefaultChannel(emptyListChannel);
    }

    @Override
    protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
          Collection<?> result = (Collection<?>) callback.execute();
          if (result.isEmpty) {
             this.messagingTemplate.convertAndSend(result);
          }
          return result;
     }

} 

UPDATE

Having that class you can use it like this:

<splitter>
  <request-handler-advice-chain>
      <beans:bean class="com.my.proj.int.EmptyListRequestHandlerAdvice">
          <beans:constructor-arg ref="emptyListChannel"/>
      </beans:bean>
  </request-handler-advice-chain>
</splitter>

<channel id="emptyListChannel"/>

UPDATE2

Any idea on if it is possible to get a collection returned in both cases instead of null or function iterator?

No, it isn't possible. It is an internal logic of the splitter, and of cause its handleRequestMessage produces an Iterator to have a streaming split process gain. It's isn't clear what is the reason for you to have Collection, since splitter is alwasy produces several messages and an iteration logic is hidden from you.

From your first requirement, you asked for the solution to send an empty list to different channel. So, I don't see why the iterator logic should impact you.

no output-channel or replyChannel header available

You really should do this:

this.messagingTemplate.send(MessageBuilder.withPayload(new ArrayList<InventoryHotel>())
      .copyHeaders(message.getHeaders().build());

Having a new message based on that requestMessage you'll copy all its header, so the replyChannel header is there too.

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • Hi Artem, is it also possible for you to provide an xml configuration sample for the above example as well especially which then shows how the given MessageChannel is set to the MessagingTemplate from the xml configuration, thanks – MilindaD Oct 20 '14 at 19:01
  • Added XML configuration for that `EmptyListRequestHandlerAdvice` – Artem Bilan Oct 20 '14 at 19:33
  • Hi artem, also this might be a bit off topic, but is it logical to load data from the db itself in the splitter and then do the splitting logic there or is the best practice to load the data in the service, and pass it to the service for splitting and sending – MilindaD Oct 21 '14 at 04:32
  • Actually there is no restrictions. Since it is your code and even if you use here a POJO method invocation you don't have any impact to the EIP pattern and sometime it might be better to do that in the single place rather than divide it to several steps. I've recently added `JdbcSplitter` sample: https://github.com/spring-projects/spring-integration-extensions/pull/118/files#diff-48ce48be23dd54f80bd15d8867b8e782R52. – Artem Bilan Oct 21 '14 at 07:07
  • Hi Artem, There are two new issues, where one is a blocker issue. The two issues have been listed in the main questions update section. – MilindaD Oct 21 '14 at 13:54
  • Hi Artem, did the following and copied the headers however upon inspecting the header of the message that was built in the Advice class, all the headers were present however AFTER the message was passed through the default channel to the next transformer the headers only contained "id" and "timestamp" and none of the copied headers, any idea Why? Also in the spring integration configuration the transformer that is called through the "emptyListChannel" has an "output-channel" configured in the XML configuration, what is the reasoning that it is not called by default – MilindaD Oct 22 '14 at 04:54
  • Need to see you flow, of course, but I can say, that the `` is that component, which doesn't copy `requestHeader` if you return `Message>` for the underlying POJO. Maybe that is your issue? – Artem Bilan Oct 22 '14 at 07:35
  • Well I checked the when the transformer is reached through the normal flow all the headers set earlier during the flow seem to be there – MilindaD Oct 22 '14 at 08:04
  • The issue was the incorrect constructor was getting called in the MessagingTemplate send method, when this was fixed everything worked as required – MilindaD Nov 10 '14 at 05:37