No publisher available to publish TcpConnectionOpenEvent / TcpConnectionCloseEvent - spring-integration

I configured a TCP Client with the Java DSL of Spring Integration. It looks like this
#Bean
public TcpSendingMessageHandler tcpClient()
{
return Tcp
.outboundAdapter(
Tcp.nioClient("localhost", 9060)
.deserializer(new ByteArrayLfSerializer())
.soKeepAlive(false)
.leaveOpen(false)
.taskExecutor(Executors.newSingleThreadExecutor())
.get()
)
.clientMode(false)
.get();
}
And I am using it in a Service to send messages to the TCP socket the client is connected to:
#Slf4j
#Service
public class TcpClientConnectionService
{
private final TcpSendingMessageHandler messageHandler;
#Autowired
public TcpClientConnectionService(final TcpSendingMessageHandler messageHandler)
{
this.messageHandler = messageHandler;
this.messageHandler.start();
}
public void sendMessage(final String message)
{
messageHandler.handleMessage(new GenericMessage<>(message));
log.debug("Message: " + message + " send");
}
}
But in production I am getting the follwing warning rather regulary and I do not know what the issue is and how to fix it.
o.s.i.i.tcp.connection.TcpNioConnection : No publisher available to
publish TcpConnectionOpenEvent
o.s.i.i.tcp.connection.TcpNioConnection : No publisher available to
publish TcpConnectionCloseEvent
It would be great if somebody could help me out since I was not able to find anything by googling.

The nested factory is not initialized properly because you are incorrectly calling .get() on the spec, which subverts Spring initialization.
I configured a TCP Client with the Java DSL of Spring Integration. It looks like this
#Bean
public TcpSendingMessageHandler tcpClient()
{
return Tcp
.outboundAdapter(
Tcp.nioClient("localhost", 9060)
.deserializer(new ByteArrayLfSerializer())
.soKeepAlive(false)
.leaveOpen(false)
.taskExecutor(Executors.newSingleThreadExecutor()))
.clientMode(false)
.get();
}
Or move the factory definition to a top level #Bean.

Related

Spring Integration aws Kinesis , message aggregator, Release Strategy

this is a follow-up question to Spring Integration AWS RabbitMQ Kinesis
I have the following configuration. I am noticing that when I send a message to the input channel named kinesisSendChannel for the first time, the aggregator and release strategy is getting invoked and messages are sent to Kinesis Streams. I put debug breakpoints at different places and could verify this behavior. But when I again publish messages to the same input channel the release strategy and the outbound processor are not getting invoked and messages are not sent to the Kinesis. I am not sure why the aggregator flow is getting invoked only the first time and not for subsequent messages. For testing purpose , the TimeoutCountSequenceSizeReleaseStrategy is set with count as 1 & time as 60 seconds. There is no specific MessageStore used. Could you help identify the issue?
#Bean(name = "kinesisSendChannel")
public MessageChannel kinesisSendChannel() {
return MessageChannels.direct().get();
}
#Bean(name = "resultChannel")
public MessageChannel resultChannel() {
return MessageChannels.direct().get();
}
#Bean
#ServiceActivator(inputChannel = "kinesisSendChannel")
public MessageHandler aggregator(TestMessageProcessor messageProcessor,
MessageChannel resultChannel,
TimeoutCountSequenceSizeReleaseStrategy timeoutCountSequenceSizeReleaseStrategy) {
AggregatingMessageHandler handler = new AggregatingMessageHandler(messageProcessor);
handler.setCorrelationStrategy(new ExpressionEvaluatingCorrelationStrategy("headers['foo']"));
handler.setReleaseStrategy(timeoutCountSequenceSizeReleaseStrategy);
handler.setOutputProcessor(messageProcessor);
handler.setOutputChannel(resultChannel);
return handler;
}
#Bean
#ServiceActivator(inputChannel = "resultChannel")
public MessageHandler kinesisMessageHandler1(#Qualifier("successChannel") MessageChannel successChannel,
#Qualifier("errorChannel") MessageChannel errorChannel, final AmazonKinesisAsync amazonKinesis) {
KinesisMessageHandler kinesisMessageHandler = new KinesisMessageHandler(amazonKinesis);
kinesisMessageHandler.setSync(true);
kinesisMessageHandler.setOutputChannel(successChannel);
kinesisMessageHandler.setFailureChannel(errorChannel);
return kinesisMessageHandler;
}
public class TestMessageProcessor extends AbstractAggregatingMessageGroupProcessor {
#Override
protected Object aggregatePayloads(MessageGroup group, Map<String, Object> defaultHeaders) {
final PutRecordsRequest putRecordsRequest = new PutRecordsRequest().withStreamName("test-stream");
final List<PutRecordsRequestEntry> putRecordsRequestEntry = group.getMessages().stream()
.map(message -> (PutRecordsRequestEntry) message.getPayload()).collect(Collectors.toList());
putRecordsRequest.withRecords(putRecordsRequestEntry);
return putRecordsRequestEntry;
}
}
I believe the problem is here handler.setCorrelationStrategy(new ExpressionEvaluatingCorrelationStrategy("headers['foo']"));. All your messages come with the same foo header. So, all of them form the same message group. As long as you release group and don’t remove it, all the new messages are going to be discarded.
Please, revise aggregator documentation to make yourself familiar with all the possible behavior : https://docs.spring.io/spring-integration/docs/current/reference/html/message-routing.html#aggregator

Sending messages to different topics using spring integration gateway

I am trying to use spring integration for send mqtt messages to a broker and I am trying to use the gateway interface.
#Bean
public MqttPahoClientFactory mqttClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
//set the factory details
return factory:
}
#Bean
#ServiceActivator(inputChannel = "mqttOutboundChannel")
public MessageHandler mqttOutbound() {
MqttPahoMessageHandler messageHandler =
new MqttPahoMessageHandler("randomString", mqttClientFactory());
//set handler details
messageHandler.setDefaultTopic(topic);
return messageHandler;
}
#Bean
public MessageChannel mqttOutboundChannel() {
return new DirectChannel();
}
#MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
private interface MyGateway {
void sendToMqtt(String data);
}
My question is: If I want to use the gateway handler to send messages to different topics how would I do that without having to create an adapter for each topic ?
Thanks.
Hope I formulated my question clearly and the code is properly formatted.
You need to set the target topic in a message header.
Here is one way to do that...
void sendToMqtt(String data, #Header(MqttHeaders.TOPIC) String topic);
The gateway proxy will assemble the message with the header, which is then used by the outbound adapter.

#Transformer for ObjectToJson Not Working in Spring Integration

A POJO Message.java is to be Converted to JSON(JSON is to be sent to pubsub Topic,using Spring Integration MessageChannels.),using following:
#Bean
#Transformer(inputChannel = "pubsubOutputChannel", outputChannel = "handleOutChannel")
public ObjectToJsonTransformer transformOut() {
return new ObjectToJsonTransformer();
}
#MessagingGateway(defaultRequestChannel = "pubsubOutputChannel")
public interface PubsubOutboundGateway {
void sendToPubsub(Messages msg);
}
#Bean
#ServiceActivator(inputChannel = "handleOutChannel")
public MessageHandler messageSender(PubSubOperations pubsubTemplate) {
return new PubSubMessageHandler(pubsubTemplate, "TestTopic");
}
When i call sendToPubsub() with an instance of Message.java with required properties set,i get an error "Null".
Is serviceActivator not able to receive the required data?
Any suggestions to fix this?.
Yes, it can't do that because you just don't tell it to do that.
Your gateway is configured for this:
#MessagingGateway(defaultRequestChannel = "handleOutChannel")
But that is not an input channel for the ObjectToJsonTransformer. So, whatever you send over that gateway is going directly to the messageSender service activator.
Try to configure your gateway like this:
#MessagingGateway(defaultRequestChannel = "pubsubOutputChannel")

Spring Integration TCP: How to get local IP address

I'm new to Spring Integration and experimenting with its TCP support. I have implemented a very basic TCP server that accepts connections and just logs the messages from the clients. This works fine, but I'm stuck in finding out how to get the local IP address to which the client has connected (the server machine has multiple IP addresses). It seems that the MessageHeaders contain only the remote address.
Here's my configuration:
#EnableIntegration
#Configuration
public class SpringConfig {
#Bean
public MessageChannel requestChannel() {
return new DirectChannel();
}
#Bean
public AbstractServerConnectionFactory serverConnectionFactory() {
TcpNetServerConnectionFactory connectionFactory =
new TcpNetServerConnectionFactory(2000);
return connectionFactory;
}
#Bean
public TcpReceivingChannelAdapter tcpReceivingChannelAdapter() {
TcpReceivingChannelAdapter adapter = new TcpReceivingChannelAdapter();
adapter.setOutputChannel(requestChannel());
adapter.setConnectionFactory(serverConnectionFactory());
return adapter;
}
#Bean
public PrintService printService() {
return new PrintService();
}
}
And my PrintService:
#MessageEndpoint
public class PrintService {
#ServiceActivator(inputChannel = "requestChannel")
public String print(Message<byte[]> message) {
// How can I get the local IP address here?
}
}
Edit 1
Basically, I would like to have the result from java.net.Socket.getLocalAddress() available in my print(...) function. Maybe like this: message.getHeaders().get(IpHeaders.LOCAL_IP_ADDRESS)
I also tried to add a custom message header by subclassing TcpMessageMapper and overriding supplyCustomHeaders(...), but I couldn't find out how to access the underlying socket.
Basically, I would like to have the result from java.net.Socket.getLocalAddress() available in my print(...) function. Maybe like this: message.getHeaders().get(IpHeaders.LOCAL_IP_ADDRESS)
Yeah, I'd better expose something like T getTarget() on the TcpConnection to allow to get access to underlying socket from the TcpMessageMapper.supplyCustomHeaders() implementation...
Feel free to raise a JIRA on the matter and we'll try to figure out what to do from the Framework perspective.
Meanwhile, as a workaround: I guess you should extend the TcpNetServerConnectionFactory a bit and override its initializeConnection(TcpConnectionSupport connection, Socket socket) to store the connectionId <-> socket relationship in some global map. Maybe even in your custom TcpMessageMapper? Having connectionId in the supplyCustomHeaders() you will be able to restore the particular socket for your purpose.

Spring Integration 4 asynchronous request/response

I am trying to write a simple message flow using Spring Integration v4's DSL APIs which would look like this:
-> in.ch -> Processing -> JmsGatewayOut -> JMS_OUT_QUEUE
Gateway
<- out.ch <- Processing <- JmsGatewayIn <- JMS_IN_QUEUE
With the request/response being asynchronous, when I inject a message via the initial Gateway, the message goes all the way to JMS_OUT_QUEUE. Beyond this message flow, a reply message is put back into JMS_IN_QUEUE which it is then picked up by JmsGatewayIn. At this point, the message is Processed and placed into out.ch (I know the response gets to out.ch because I have a logger interceptor there which logs the message being placed there) but, the Gateway never receives the response.
Instead of a response, the system outside of this message flow which picked up the message from JMS_OUT_QUEUE and placed the response in JMS_IN_QUEUE, receives a javax.jms.MessageFormatException: MQJMS1061: Unable to deserialize object on its own JmsOutboundgateway (I think it is failing to deserialize a jms reply object from looking at the logs).
I have clearly not got something configured correctly but I don't know exactly what. Does anyone know what I am missing?
Working with spring-integration-core-4.0.3.RELEASE, spring-integration-jms-4.0.3.RELEASE, spring-integration-java-dsl-1.0.0.M2, spring-jms-4.0.6.RELEASE.
My Gateway is configured as follows:
#MessagingGateway
public interface WsGateway {
#Gateway(requestChannel = "in.ch", replyChannel = "out.ch",
replyTimeout = 45000)
AResponse process(ARequest request);
}
My Integration flow is configured as follows:
#Configuration
#EnableIntegration
#IntegrationComponentScan
#ComponentScan
public class IntegrationConfig {
#Bean(name = "in.ch")
public DirectChannel inCh() {
return new DirectChannel();
}
#Bean(name = "out.ch")
public DirectChannel outCh() {
return new DirectChannel();
}
#Autowired
private MQQueueConnectionFactory mqConnectionFactory;
#Bean
public IntegrationFlow requestFlow() {
return IntegrationFlows.from("in.ch")
.handle("processor", "processARequest")
.handle(Jms.outboundGateway(mqConnectionFactory)
.requestDestination("JMS_OUT_QUEUE")
.correlationKey("JMSCorrelationID")
.get();
}
#Bean
public IntegrationFlow responseFlow() {
return IntegrationFlows.from(Jms.inboundGateway(mqConnectionFactory)
.destination("JMS_IN_QUEUE"))
.handle("processor", "processAResponse")
.channel("out.ch")
.get();
}
}
Thanks for any help on this,
PM.
First of all your configuration is bad:
Since you start the flow from WsGateway#process you really should wait reply there.
The gateway's request/reply capability is based on TemporaryReplyChannel, which is placed to the headers as non-serializable value.
As long as you wait rely on that gateway, actually there is no reason to provide the replyChannel, if you aren't going to do some publish-subscribe logic on the reply.
As you send message to the JMS queue, you should understand that consumer part might be a separete remote application. And the last one might know nothing about your out.ch.
The JMS request/reply capability is really based on JMSCorrelationID, but it isn't enough. The one more thing here is a ReplyTo JMS header. Hence, if you are going to send reply from the consumer you should really just rely on the JmsGatewayIn stuff.
So I'd change your code to this:
#MessagingGateway
public interface WsGateway {
#Gateway(requestChannel = "in.ch", replyTimeout = 45000)
AResponse process(ARequest request);
}
#Configuration
#EnableIntegration
#IntegrationComponentScan
#ComponentScan
public class IntegrationConfig {
#Bean(name = "in.ch")
public DirectChannel inCh() {
return new DirectChannel();
}
#Autowired
private MQQueueConnectionFactory mqConnectionFactory;
#Bean
public IntegrationFlow requestFlow() {
return IntegrationFlows.from("in.ch")
.handle("processor", "processARequest")
.handle(Jms.outboundGateway(mqConnectionFactory)
.requestDestination("JMS_OUT_QUEUE")
.replyDestination("JMS_IN_QUEUE"))
.handle("processor", "processAResponse")
.get();
}
}
Let me know, if it is appropriate for you or try to explian why you use two-way gateways for one one-way cases. Maybe Jms.outboundAdapter() and Jms.inboundAdapter() are more good for you?
UPDATE
How to use <header-channels-to-string> from Java DSL:
.enrichHeaders(e -> e.headerChannelsToString())

Resources