0

I am using base code of dynamic tcp routing and extend it with help of Gary Russell,

Sub Flow code : In here i can receive arbitrary messages with extending TcpOutboundGateway. If message is coming with arbitrary without request from client, it handles by direct message channel of ExtendedTcpOutBoundGateway

private MessageChannel createNewSubflow(Message<?> message) {
    String host = (String) message.getHeaders().get("host");
    Integer port = (Integer) message.getHeaders().get("port");

    boolean hasThisConnectionIrregularChannel = message.getHeaders().containsKey("irregularMessageChannelName");

    Assert.state(host != null && port != null, "host and/or port header missing");
    String flowRegisterKey;

    if (hasThisConnectionIrregularChannel) {
        flowRegisterKey = host + port + ".extended";
    } else {
        flowRegisterKey = host + port;
    }

    TcpNetClientConnectionFactory cf = new TcpNetClientConnectionFactory(host, port);
    cf.setSoTimeout(0);
    cf.setSoKeepAlive(true);

    ByteArrayCrLfSerializer byteArrayCrLfSerializer = new ByteArrayCrLfSerializer();
    byteArrayCrLfSerializer.setMaxMessageSize(1048576);

    cf.setSerializer(byteArrayCrLfSerializer);
    cf.setDeserializer(byteArrayCrLfSerializer);

    TcpOutboundGateway tcpOutboundGateway;
    if (hasThisConnectionIrregularChannel) {
        log.info("TcpRouter # createNewSubflow extended TcpOutboundGateway will be created");
        String unsolicitedMessageChannelName = (String) message.getHeaders().get("irregularMessageChannelName");
        DirectChannel directChannel = getBeanFactory().getBean(unsolicitedMessageChannelName, DirectChannel.class);
        tcpOutboundGateway = new ExtendedTcpOutboundGateway(directChannel);
    } else {
        log.info("TcpRouter # createNewSubflow extended TcpOutboundGateway will be created");
        tcpOutboundGateway = new TcpOutboundGateway();
    }

    tcpOutboundGateway.setConnectionFactory(cf);

    tcpOutboundGateway.setAdviceChain(Arrays.asList(new Advice[]{tcpRetryAdvice()}));

    IntegrationFlow flow = f -> f.handle(tcpOutboundGateway);

    IntegrationFlowContext.IntegrationFlowRegistration flowRegistration =
        this.flowContext.registration(flow)
            .addBean(cf)
            .id(flowRegisterKey + ".flow")
            .register();

    MessageChannel inputChannel = flowRegistration.getInputChannel();

    this.subFlows.put(flowRegisterKey, inputChannel);
    return inputChannel;
}

In here, i can handle retry advice to setup connection again.

@Bean
public RequestHandlerRetryAdvice tcpRetryAdvice() {
    SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
    retryPolicy.setMaxAttempts(2);

    ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
    backOffPolicy.setInitialInterval(100);
    backOffPolicy.setMaxInterval(1000);
    backOffPolicy.setMultiplier(2);

    RetryTemplate retryTemplate = new RetryTemplate();
    retryTemplate.setRetryPolicy(retryPolicy);
    retryTemplate.setBackOffPolicy(backOffPolicy);

    RequestHandlerRetryAdvice tcpRetryAdvice = new RequestHandlerRetryAdvice();
    tcpRetryAdvice.setRetryTemplate(retryTemplate);

    // This allows fail-controlling
    tcpRetryAdvice.setRecoveryCallback(new ErrorMessageSendingRecoverer(failMessageChannel()));

    return tcpRetryAdvice;
}

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

@ServiceActivator(inputChannel = "failMessageChannel")
public void messageAggregation(String in) {
    log.error("TcpRouter # connection retry failed with message : " + in);
}

but now i need to handle another scenario;

What if during receiving arbitrary messages from server connection closed ? Somehow connection can be closed from server side, I need to handle connection closed event and setup connection again to continue receiving arbitrary events.

How should i catch that event with spring integration and re-establish connection again ?

EDIT 1

I googled some same questions and found that EventListener from this link EventListener topic but in that link as i understood there is only one connection so in my case i am client side and using dynamic tcp routing this is why i need to implement logic to handle different connections.

EDIT 2

Here is determine target channel

@Override
protected Collection<MessageChannel> determineTargetChannels(Message<?> message) {
    MessageChannel channel;
    boolean hasThisConnectionIrregularChannel = message.getHeaders().containsKey("irregularMessageChannelName");
    if (hasThisConnectionIrregularChannel) {
        channel = this.subFlows.get(message.getHeaders().get("host", String.class) + message.getHeaders().get("port") + ".extended");
    } else {
        channel = this.subFlows.get(message.getHeaders().get("host", String.class) + message.getHeaders().get("port"));
    }

    if (channel == null) {
        channel = createNewSubflow(message);
    }
    return Collections.singletonList(channel);
}

Here is ToTCP interface

@Component
@MessagingGateway(defaultRequestChannel = "toTcp.input")
public interface ToTCP {
    byte[] send(String data, @Header("host") String host, @Header("port") int port, @Header("irregularMessageChannelName") String channelName);

byte[] send(String data, @Header("host") String host, @Header("port") int port);

}

baki hayat
  • 27
  • 8

1 Answers1

1

.addBean(cf)

If you use .addBean("someBeanName", cf) you can check the TcpConnectionClosedEvent's connectionFactoryName (which will be the id you gave the cf in addBean()).

You can then route a new message to the flow that has that factory to re-establish the connection.

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • Let me try and provide my feedback, Thank you – baki hayat Jul 21 '20 at 05:31
  • One more question Gary, ı am using here [link](https://github.com/spring-projects/spring-integration-samples/blob/master/advanced/dynamic-tcp-client/src/main/java/org/springframework/integration/samples/dynamictcp/DynamicTcpClientApplication.java) from github. As i understood when i catch connection closed event, i need to send message again then connection will be established, am i right ? – baki hayat Jul 21 '20 at 07:33
  • If yes then should i autowire ToTcp in TcpRouter class or should i inject it in constructor of TcpRouter ? Because if i inject in constructor then also i need to change it in ` @Bean public IntegrationFlow toTcp() { return f -> f.route(new TcpRouter()); } `. so i am a bit confused about routing a new message... – baki hayat Jul 21 '20 at 07:34
  • You must already have logic that sends a message to the router with appropriate headers; otherwise you would never have established a question in the first place. Simply send an appropriate message to the router with headers set up so it will be routed to the flow where the connection just closed. – Gary Russell Jul 21 '20 at 13:06
  • Ok thank you, i think i did some arrangement in my flow and it seems working now. In case some additional recommendation, i will open another ticket. Thanks for your support – baki hayat Jul 22 '20 at 05:40
  • Hi Again, my TcpRouter extends AbstractMessageRouter like in your example so ı noticed that there is an method called "determineTargetChannels(Message> message)" so when i handle connection closed i can setup message and call determineTargetChannel... also there is a handleMessageInternal method too, am i doing it correct way ? Or call directly `send`method (`byte[] send(String data, @Header("host") String host, @Header("port") int port)`) in event listener then also i have to deal with return result. sorry but i am confused about it. – baki hayat Jul 22 '20 at 12:45
  • I have added EDIT in to question that shows `determineTargetChannels` and `@Component @MessagingGateway(defaultRequestChannel = "toTcp.input")` to be more clear – baki hayat Jul 22 '20 at 12:49
  • It's not clear what you are unsure about. Perhaps its time to open a new question? – Gary Russell Jul 22 '20 at 13:19
  • Ok then i will open another question. Thank you – baki hayat Jul 22 '20 at 15:21
  • here is more detailed question [new question](https://stackoverflow.com/questions/63038187/spring-integration-handle-connection-close-event-with-event-listener-and-re-esta) – baki hayat Jul 22 '20 at 15:47