Spring-Integration: Cannot Consume from custom message adapter using IntegrationFlow - spring

I have created a MessageAdapter by extending MessageProducerSupport. I produce message to output channel by calling MessageProducerSupport.sendMessage. Then I defined an IntegrationFlow to consume messages from the message adapter, and send it to a channel. But I don't receive any messages on the channel.
This is my configuration
#Bean
public MyAdapter myAdapter() {
MyAdapter myAdapter = new MyAdapter();
myAdapter.setOutputChannel(new QueueChannel());
return myAdapter;
}
#Bean
public IntegrationFlow integrationFlow() {
return IntegrationFlows
.from(myAdapter())
.channel("myChannel")
.get();
}
and message endpoint for "myChannel":
#MessageEndpoint
public class MyConsumer {
#ServiceActivator(inputChannel = "myChannel")
public void parseMessage(Message message) {
System.out.println(message.getPayload().toString());
}
}
Is there something I am missing to configure?
Thank you

Related

TCP Gateway with Spring Integration

I need to implement a tcp gateway that sends a message to a server and receive synchronously the response.
The server is up and running on a specific port so following the examples I have found on the web I have configured it like this:
#Configuration
public class TcpClientConfig {
#Value("${tcp.server.host}")
private String host;
#Value("${tcp.server.port}")
private int port;
private static final Logger LOGGER = LoggerFactory.getLogger(TcpClientConfig.class);
#Component
#MessagingGateway
public interface TcpClientGateway {
#Gateway(requestChannel="outboundChannel")
byte[] sendByClient(byte[] message);
}
#Bean
public AbstractClientConnectionFactory clientConnectionFactory() {
TcpNioClientConnectionFactory tcpNioClientConnectionFactory = new TcpNioClientConnectionFactory(host, port);
tcpNioClientConnectionFactory.setUsingDirectBuffers(true);
tcpNioClientConnectionFactory.setSingleUse(false);
Bytearraylengthheaderserializer serDeser = new Bytearraylengthheaderserializer(2);
tcpNioClientConnectionFactory.setSerializer(serDeser);
tcpNioClientConnectionFactory.setDeserializer(serDeser);
return tcpNioClientConnectionFactory;
}
#Bean
public MessageChannel outboundChannel() {
return new DirectChannel();
}
#Bean
public IntegrationFlow incomingClient(final TcpReceivingChannelAdapter tcpReceivingChannelAdapter,
TcpServerEndpoint tcpServerEndpoint) {
return IntegrationFlows
.from(tcpReceivingChannelAdapter)
.handle(message -> { LOGGER.info("RECEIVING ON CLIENT: {}", tcpServerEndpoint.processMessage((byte[]) message.getPayload()));})
.get();
}
#Bean
public IntegrationFlow outgoingClient(final MessageChannel outboundChannel, final TcpSendingMessageHandler tcpSendingClientMessageHandler) {
return IntegrationFlows
.from(outboundChannel)
.handle(tcpSendingClientMessageHandler)
.get();
}
#Bean
public TcpSendingMessageHandler tcpSendingClientMessageHandler(AbstractClientConnectionFactory clientConnectionFactory) {
TcpSendingMessageHandler tcpSendingMessageHandler = new TcpSendingMessageHandler();
tcpSendingMessageHandler.setConnectionFactory(clientConnectionFactory);
tcpSendingMessageHandler.setClientMode(true);
tcpSendingMessageHandler.setRetryInterval(5000);
tcpSendingMessageHandler.setLoggingEnabled(true);
return tcpSendingMessageHandler;
}
#Bean
public TcpReceivingChannelAdapter tcpReceivingChannelAdapter(AbstractClientConnectionFactory clientConnectionFactory) {
TcpReceivingChannelAdapter tcpReceivingChannelAdapter = new TcpReceivingChannelAdapter();
tcpReceivingChannelAdapter.setConnectionFactory(clientConnectionFactory);
tcpReceivingChannelAdapter.setAutoStartup(true);
tcpReceivingChannelAdapter.setClientMode(true);
tcpReceivingChannelAdapter.setRetryInterval(5000);
return tcpReceivingChannelAdapter;
}
#EventListener
public void open(TcpConnectionOpenEvent event)
throws InterruptedException, IOException {
LOGGER.info("Open new connection to Router {}", event.getConnectionId());
clientConnectionFactory().getConnection().getSocketInfo().getChannel()
.setOption(ExtendedSocketOptions.TCP_KEEPIDLE, 60);
}
#EventListener
public void close(TcpConnectionCloseEvent event)
throws InterruptedException, IOException {
LOGGER.info("Close connection to Router {}", event.getConnectionId());
}
}
The idea is to have a Gateway that send the message and wait synchronously for the response. But when I call the method of the gateway
byte[] sendByClient(byte[] message)
the call get hanged and there's no response back to the gateway.
The TcpSendingMessageHandler is a one-way component. It just does not wait for any replies to produce them to the replyChannel header for that TcpClientGateway in the beginning of the flow.
Consider to use a TcpOutboundGateway instead: https://docs.spring.io/spring-integration/docs/current/reference/html/ip.html#tcp-gateways
This samples has some basic ideas what and how to do with the gateway: https://github.com/spring-projects/spring-integration-samples/tree/main/basic/tcp-client-server

Kafka Consumer Invalid Payload Error Handler

I have the below configuration. When the message is invalid I want to send an email and for errors I want to save it in database. How can I handle this in errorHandler() ?
#Configuration
#EnableKafka
public class KafkaConsumerConfig implements KafkaListenerConfigurer{
#Bean
ErrorHandler errorHandler() {
return new SeekToCurrentErrorHandler((rec, ex) ->
{ dbService.saveErrorMsg(rec); }
,new FixedBackOff(5000, 3)) ;
}
#Override
public void configureKafkaListeners(KafkaListenerEndpointRegistrar registrar) {
registrar.setValidator(this.validator);
}
#KafkaListener(topics = "mytopic", concurrency = "3", groupId = "mytopic-1-groupid")
public void consumeFromTopic1(#Payload #Valid ValidatedClass val, ConsumerRecordMetadata meta) throws Exception
{
dbservice.callDB(val,"t");
}
I presume your emai code is in dbService.saveErrorMsg.
Spring Boot should automatically detect the ErrorHandler #Bean and wire it into the container factory.
See Boot's KafkaAnnotationDrivenConfiguration class and ConcurrentKafkaListenerContainerFactoryConfigurer.

Spring Integration - Convert Service Activator with Java Configuration

I try to convert the "Hello World example" from Spring Integration samples (https://github.com/spring-projects/spring-integration-samples/tree/master/basic/helloworld) from XML, to Java Configuration, (so with the #Configuration annotation).
The configuration class looks like this :
#Configuration
#EnableIntegration
public class BasicIntegrationConfig{
#Bean
public DirectChannel inputCHannel() {
return new DirectChannel();
}
#Bean
public QueueChannel outputChannel() {
return new QueueChannel();
}
#Bean
#ServiceActivator(inputChannel= "inputChannel", outputChannel= "outputChannel" )
public MessageHandler fileWritingMessageHandler() {
MessageHandler mh = new MessageHandler() {
#Override
public void handleMessage(Message<?> message) throws MessagingException {
System.out.println("Message payload: " + message.getPayload());
}
};
return mh;
}
}
To test it, I use the main() supplied from sample project :
DirectChannel fileChannel = applicationContext.getBean("inputChannel", DirectChannel.class);
QueueChannel outputChannel = applicationContext.getBean("outputChannel", QueueChannel.class);
System.out.println("********** SENDING MESSAGE");
fileChannel.send(new GenericMessage<>("test"));
System.out.println(outputChannel.receive(0).getPayload());
I see in the console "Message payload: test", but unfortunately, I don't receive the message on the outputchannel (I have a NullPointerException on outputChannel.receive(0).
Do you have an idea why the Service Activator does not send the message to the output channel?
Your MessageHandler returns void.
You need to subclass AbstractReplyProducingMessageHandler instead.
Thank you Gary, it works perfectly after switching to :
#Bean
#ServiceActivator(inputChannel= "inputChannel")
public AbstractReplyProducingMessageHandler fileWritingMessageHandler() {
AbstractReplyProducingMessageHandler mh = new AbstractReplyProducingMessageHandler() {
#Override
protected Object handleRequestMessage(Message<?> message) {
String payload= (String)message.getPayload();
return "Message Payload : ".concat(payload);
}
};
mh.setOutputChannelName("outputChannel");
return mh;
}
As a side note, I had to remove the output channel attribute in #ServiceActivator annotation, and put it in method body instead (Bean Validation Exception if not).

Spring-Rabbitmq MessageConverter - not invoking custom object handleMessage

I am implementing a consumer class that binds to fanout exchange in RabbitMQ and receives the message published as json. For some reason, the handleMessage within the Consumer class is not being invoked when its argument is a custom object. Same code works when the handleMessage is changed to take Object. Would appreciate your help in identity the missing piece.
Here is the configuration and consumer classes. This is not a SpringBoot application. My Configuration class has #Configuration annotation and not #SpringBootApplication.
#Bean
public SimpleMessageListenerContainer messageListenerContainer() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(rabbitConnectionFactory());
container.setQueueNames(QUEUE_NAME);
container.setMessageListener(listenerAdapter());
container.setMessageConverter(new Jackson2JsonMessageConverter());
container.setMissingQueuesFatal(false);
return container;
}
#Bean
public AmqpAdmin amqpAdmin() {
return new RabbitAdmin(rabbitConnectionFactory());
}
#Bean
public Queue queue() {
return new Queue(QUEUE_NAME, false, false, false);
}
#Bean
public FanoutExchange exchange() {
return new FanoutExchange(EXCHANGE_NAME, false, false);
}
#Bean
public Binding inboundEmailExchangeBinding() {
return BindingBuilder.bind(queue()).to(exchange());
}
#Bean
public ConnectionFactory rabbitConnectionFactory() {
return new CachingConnectionFactory("localhost");
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory());
rabbitTemplate.setExchange(EXCHANGE_NAME);
return rabbitTemplate;
}
#Bean
MessageListenerAdapter listenerAdapter() {
return new MessageListenerAdapter(new Consumer(), "receiveMessage");
}
Here is the consumer ...
public class Consumer {
// This works
/*
public void receiveMessage(Object message) {
System.out.println("Received <" + message + ">");
}
*/
// This does not works, whereas I expect this to work.
public void receiveMessage(CustomObject message) {
System.out.println("Received <" + message + ">");
}
}
where CustomObject class is a plain POJO.
Here is an example of what is being published in RabbitMQ.
{
"state": "stable",
"ip": "1.2.3.4"
}
Its being published as json content-type
exchange.publish(message_json, :content_type => "application/json")
Appreciate all your help in making me understand the problem. Thanks.
The Jackson2JsonMessageConverter needs to be told what object to map the json to.
This can be provided via information in a __TypeId__ header (which would be the case if Spring was on the sending side); the header can either contain the full class name, or a token that is configured to map to the class name.
Or, you need to configure the converter with a class mapper.
For convenience there is a DefaultClassMapper that be configured with your target class:
ClassMapper classMapper = new DefaultClassMapper();
classMapper.setDefaultType(CustomObject.class);
converter.setClassMapper(classMapper);

Spring MQTT integration: loss of messages

I'm trying use spring integration to receive a big amount of MQTT messages, process them and then store in a db.
Here is the code:
#Bean
public MqttPahoClientFactory mqttClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
factory.setServerURIs("tcp://localhost:1883");
return factory;
}
#Bean
public DefaultPahoMessageConverter messageConverter(){
DefaultPahoMessageConverter converter = new DefaultPahoMessageConverter();
converter.setPayloadAsBytes(true);
return converter;
}
#Bean
public MessageChannel mqttInputChannel() {
return new DirectChannel();
}
#Bean
public MessageProducer mqttInbound() {
MqttPahoMessageDrivenChannelAdapter adapter =
new MqttPahoMessageDrivenChannelAdapter("clientID", mqttClientFactory(), "topic1");
adapter.setCompletionTimeout(5000);
adapter.setConverter(messageConverter());
adapter.setQos(2);
adapter.setOutputChannel(mqttInputChannel());
return adapter;
}
#Bean
#ServiceActivator(inputChannel = "mqttInputChannel")
public MessageHandler handler(){
return new MessageHandler() {
#Override
public void handleMessage(Message<?> arg0) throws MessagingException {
//process messages and storing operations
}
};
}
My problem is that I'm not able to receive all messages and losing some of them, probably because I spend a lot of resources and time inside the handler method. I've tried also to use a QueueChannel instead of the DirectChannel, but when the queue is full the problem remains.
A possible solution could be this, stop the reception till the message is completely handled and then restarts it, but I don't know how. Any advice?
Thanks in advance.

Resources