3

I would like to create a module which will filter messages from the input channel and transform them into something else on the output. I know I can seperate this in two modules (I prefer Java code over scripts) like:

@Filter(inputChannel = "input", outputChannel = "output")
public boolean accept(final Message<?> message) {
    final MyObject payload = (MyObject) message.getPayload();
    return payload.getName().equals("test");
}


@Transformer(inputChannel = "input", outputChannel = "output")
public OtherObject transform(final MyObject data) {
    return convert(data);
}

but I would like to do this in a single module. If I move the filtering logic to the transfomer module and I return null values on non-acceptable payloads, I start getting spring-xd runtime exceptions. What would be the correct approach to this?

--EDIT--

Configuration:

@Configuration
@EnableIntegration
public class ModuleConfiguration {

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

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

@Bean
public MessageChannel myChannel() {
    return new DirectChannel();
}

@Bean
public MyFilter filter() {
    return new MyFilter();
}

@Bean
public MyTransformer transformer() {
    return new MyTransformer();
}
}

Filter:

@Filter(inputChannel = "input", outputChannel = "myChannel")
public boolean accept(final Message<?> message) 

Transformer:

@Transformer(inputChannel = "myChannel", outputChannel = "output")
public OtherObject transform(final MyObject payload)

Exception:

2016-05-13T11:17:59+0200 1.3.1.RELEASE WARN xdbus.tt.0-1 listener.ConditionalRejectingErrorHandler - Execution of Rabbit message listener failed.
org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener threw exception
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:865) ~[spring-rabbit-1.5.4.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:760) ~[spring-rabbit-1.5.4.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:680) ~[spring-rabbit-1.5.4.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:93) [spring-rabbit-1.5.4.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:183) ~[spring-rabbit-1.5.4.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1358) [spring-rabbit-1.5.4.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:661) ~[spring-rabbit-1.5.4.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1102) [spring-rabbit-1.5.4.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:1086) [spring-rabbit-1.5.4.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1100(SimpleMessageListenerContainer.java:93) [spring-rabbit-1.5.4.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1203) [spring-rabbit-1.5.4.RELEASE.jar:na]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_73]
Caused by: org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'JavaConfiguredModule [name=myFilter, type=processor, group=tt, index=1 @7d48b140]:default,admin,singlenode,hsqldbServer:9393.input'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:81) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:442) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:392) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115) ~[spring-messaging-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45) ~[spring-messaging-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105) ~[spring-messaging-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:231) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:154) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:102) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:105) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:147) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:120) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:442) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:392) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115) ~[spring-messaging-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45) ~[spring-messaging-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105) ~[spring-messaging-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.integration.endpoint.MessageProducerSupport.sendMessage(MessageProducerSupport.java:105) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter.access$400(AmqpInboundChannelAdapter.java:45) ~[spring-integration-amqp-4.2.5.RELEASE.jar:na]
    at org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter$1.onMessage(AmqpInboundChannelAdapter.java:93) ~[spring-integration-amqp-4.2.5.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:757) ~[spring-rabbit-1.5.4.RELEASE.jar:na]
    ... 10 common frames omitted
Caused by: org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers
    at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:153) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:120) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:77) ~[spring-integration-core-4.2.5.RELEASE.jar:na]
    ... 33 common frames omitted
alturkovic
  • 990
  • 8
  • 31

1 Answers1

2

As you have found, transformers must return something.

Please refer to the Spring Integration documentation (each XD processor module is a small Spring Integration application with an input and output channel - sources just have an output, sinks just have an input).

The way you have it now, you have two consumers on input - messages will be round-robin distributed to them.

You need to wire the two components into a message flow (via a third message channel)...

@Filter(inputChannel = "input", outputChannel = "transformerChannel")
public boolean accept(final Message<?> message) {
    final MyObject payload = (MyObject) message.getPayload();
    return payload.getName().equals("test");
}


@Transformer(inputChannel = "transformerChannel", outputChannel = "output")
public OtherObject transform(final MyObject data) {
    return convert(data);
}

Notice the channel configuration.

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • Hi, thanks for your help. These modules are deployed with seperate configurations, I believe messages are distributed correctly but I will have to check. But the point of my question was is it possible to filter AND transform a payload in a single module? Without having to even put anything to the message bus? Thanks... – alturkovic May 05 '16 at 12:41
  • Either approach will work, in the above scenario they are wired together directly, via `transformerChannel` in a single module. If they are already configured and uploaded as separate modules, you can compose them into a single module and they will be wired together avoiding the bus. See [the docs](http://docs.spring.io/spring-xd/docs/1.3.1.RELEASE/reference/html/#composing-modules) `module compose foo --definition "filter | transfomer"` You can then use `foo` as a module in a stream definition. – Gary Russell May 05 '16 at 12:47
  • "They will be wired together avoiding the bus" -> this means they communicate directly? Or via some Java queue? Thanks again... – alturkovic May 05 '16 at 12:51
  • 1
    Yes, the bus thread that invokes the filter will (if the filter passes the message) invoke the transformer directly; they are wired together using a `DirectChannel`, no queues involved. It is effectively the same as doing what I suggested above (putting the two components in a single module and wire them together with a `DirectChannel`. – Gary Russell May 05 '16 at 13:20
  • I have just tried implementing this solution and I keep getting "Dispatcher has no subscribers" exception on deploy. I want to package these 2 modules as one. I edited my question and put all my setup there. Have any idea where I messed up? Thanks... – alturkovic May 13 '16 at 09:29
  • 1
    `Dispatcher has no subscribers for channel ... input` so it's the filter that's not subscribed. Have you annotated the class that contains that method with `@MessageEndpoint` (or `@Component`) ? BTW, both methods can be in the same class, and then you only need one `@Bean`. – Gary Russell May 13 '16 at 13:50
  • That was the issue. I thought implementing MessageSelector and annotating a method will be enough. I moved everything into one bean and added @MessageEndpoint. Works now. Thanks! – alturkovic May 13 '16 at 13:56
  • 1
    Cool. FYI, in Spring Integration 4.3 (out soon), we have removed the requirement for annotated methods to have the class-level annotation. – Gary Russell May 13 '16 at 14:54