0

I am trying to create a Integration flow :

Connection A : [ ActiveMq (queue1) ---> TCP Server (1111) ](spring boot Application) ---> [ExternalApplication (client connected to Server running on port (1111))] (Application on different Technology (VB))

Connection B : [ ActiveMq (queue2) ---> TCP Server (2222) ](spring boot Application) ---> [ExternalApplication (client connected to Server running on port (2222))] (Application on different Technology (VB))

So Above flow depicts following scenario:

  1. I have a spring boot application (say springUtility) which connect with external application(say externalutility) on some other technology.

  2. The connection between springutility and externalUtility is through TCP protocol.

  3. There will be more than one connection between both the utilities as in above flow diagram connection A and connection B.

  4. The flow of message will be from springUtilty to externalutility (One way)

  5. Now, the externalutility can run in client Mode or server mode. so, if the externalUtility run in server Mode then my springutility will be in client mode (Done) also to create more than one connection I run this below code in for loop code :

Code:

flow = IntegrationFlows
            .from(Jms.inboundAdapter(ConnectionFactory())
                    .destination("rec"))
                    .channel(directChannel()).handle(Tcp.outboundAdapter(Tcp.nioClient("172.18.11.22",5555)
                             .serializer(customSerializer)
                             .deserializer(customSerializer)
                             .id(hostConnection.getConnectionNumber()).soTimeout(10000)))
                            .get();

    flowRegistration = this.flowContext.registration(flow).id("in.flow").register();
  1. Issue : When the externalutility run in client mode then spring utility should able to create a server on which the external utility will get connected and then it start receiving messsages from spring utility: which I tried to implement in a wrong way.

  2. So, finally I have to Implement the above connection as a test case to validate the flow, means spring utility has opened server port (1111 and 2222) the external application connect with these two port and start receiving the message from spring utility from queue1 and queue2 repectively. Both the connection will have there seperate flow (Once a single flow will be created then we can use a for loop to create no of connection we need).

Code:

flow = IntegrationFlows
            .from(Jms.inboundAdapter(ConnectionFactory())
                    .destination("rec"))
                    .channel(directChannel()).handle(Tcp.outboundAdapter(Tcp.netServer(5555)
                             .serializer(customSerializer)
                             .deserializer(customSerializer)
                             .id(hostConnection.getConnectionNumber()).soTimeout(10000)))
                            .get();

    flowRegistration = this.flowContext.registration(flow).id("in.flow").register();

` code:

2018-05-30 17:39:39.171  INFO 15776 --- [nio-8080-exec-1] o.s.i.endpoint.EventDrivenConsumer       : Adding {jms:outbound-channel-adapter} as a subscriber to the 'Conn1311out.flow.channel#0' channel
2018-05-30 17:39:39.172  INFO 15776 --- [nio-8080-exec-1] o.s.integration.channel.DirectChannel    : Channel 'application.Conn1311out.flow.channel#0' has 1 subscriber(s).
2018-05-30 17:39:39.172  INFO 15776 --- [nio-8080-exec-1] o.s.i.endpoint.EventDrivenConsumer       : started org.springframework.integration.config.ConsumerEndpointFactoryBean#3
2018-05-30 17:39:41.965  INFO 15776 --- [nio-8080-exec-1] o.s.i.endpoint.EventDrivenConsumer       : Adding {ip:tcp-outbound-channel-adapter} as a subscriber to the 'Conn1311in.flow.channel#0' channel
2018-05-30 17:39:41.965  INFO 15776 --- [nio-8080-exec-1] o.s.integration.channel.DirectChannel    : Channel 'application.Conn1311in.flow.channel#0' has 1 subscriber(s).
2018-05-30 17:39:41.966  INFO 15776 --- [nio-8080-exec-1] .s.i.i.t.c.TcpNetServerConnectionFactory : started Conn1311, port=3333
2018-05-30 17:39:41.966  INFO 15776 --- [nio-8080-exec-1] o.s.i.endpoint.EventDrivenConsumer       : started org.springframework.integration.config.ConsumerEndpointFactoryBean#4
2018-05-30 17:39:41.966  INFO 15776 --- [pool-1-thread-1] .s.i.i.t.c.TcpNetServerConnectionFactory : Conn1311, port=3333 No listener bound to server connection factory; will not read; exiting...
2018-05-30 17:39:41.971  INFO 15776 --- [nio-8080-exec-1] o.s.i.e.SourcePollingChannelAdapter      : started org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean#0

It first create the server on the provided port but exit with following message

No listener bound to server connection factory; will not read; exiting...

When I am creating the client it is working fine but in case of creating server it is giving error

EDIT:

I have use the following approach to create dynamically different server and send message to connected client from its respective activemq client

  1. I have created an inbound adapter as below :

    IntegrationFlow flow;
    CustomSerializer customSerializer = getCustomSerializer(String.valueOf(hostConnection.getMaxMessageLength()),
            hostConnection.getTerminatorChar());
    
    TcpSendingMessageHandler handler = new TcpSendingMessageHandler();
    TcpNetServerConnectionFactory cf = new TcpNetServerConnectionFactory(1234));
    cf.setSerializer(customSerializer);
    cf.setDeserializer(customSerializer);
    handler.setConnectionFactory(cf);
    
         flow = IntegrationFlows
            .from(Tcp.inboundAdapter(cf).id("adapter")).handle(handler)
            .get();
    
         this.flowContext.registration(flow).id("inflow")
         .addBean(hostConnection.getConnectionNumber(),cf)
         .addBean(hostConnection.getConnectionNumber()+"handler",handler)
         .register();
    
  2. I have created a Jmsflow in which the message from the queue is send to a router as below:

        flow = IntegrationFlows
            .from(Jms.inboundAdapter(activeMQConnectionFactory)
            .destination(hostConnection.getConnectionNumber() +"rec")) 
            .channel(directChannel()).route(new ServerRouter())
            .get(); 
    
  3. when the client got connected to my server then I create a flow from the listener handleTcpConnectionCloseEvent(TcpConnectionOpenEvent event) and create a flow as:

    void createServerFlow(TcpConnectionOpenEvent event) {
        String connectionNumber = event.getConnectionFactoryName();
        TcpNetConnection server = (TcpNetConnection) event.getSource();
        TcpSendingMessageHandler handler = (TcpSendingMessageHandler) ac.getBean(connectionNumber + "handler");
    
        IntegrationFlow flow = f -> f.enrichHeaders(e -> e.header(IpHeaders.CONNECTION_ID, event.getConnectionId()))
                .handle(handler);
        IntegrationFlowRegistration flowregister = this.flowContext.registration(flow).id("outclient").register();
    
        MessageChannel channel = flowregister.getInputChannel();
    
        this.subFlows.put(connectionNumber + "server", channel);
    }
    
  4. My ServerRouter find the channel and send to the target channel

    protected Collection<MessageChannel> determineTargetChannels(Message<?> message) {
    String serverChannel = (String) message.getHeaders().get("connectionnumber");
    MessageChannel channel = HostConnectionRepository.subFlows
            .get(serverChannel+"server");
    
    return Collections.singletonList(channel);
    

    }

  5. when I got disconnected then I remove the flow from flowContext created in step 3.

Issue : When I remove the flow the bean associated with the flow also get removed as its written in the docs that related beans will also discard.

This remove my handler bean which close the TCP adapter also the connectionfactory hence once it get disconnected it can't connect again

How can i discard the outclientflow without discarding the handler bean?

2 Answers2

0

The Tcp.outboundAdapter() is not a TcpListener. It is just sender to the TCP socket. That's why you get that exception.

If we talk about TCP server, it must be a listener, an Inbound Endpoint.

It is fully unclear why would one create a TCP server dynamically on the send operation...

You need to re-think your logic and have such a TCP server for listening really outside of this dynamic logic. And here would use only a client variant to send TCP packets to the required TCP port.

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • There is some external application which can either run in client mode or in server mode if the external application is running in server mode the I create the following flow by giving connection factory details of client, but If the external application behave as a client the I have to configure a Server so that the external application can connect to it – shivam tiwari May 30 '18 at 13:47
  • Correct. So, this TCP server has to be configured in advance, before you start creating flows dynamically. Currently there is no any TCP listeners in your solution. – Artem Bilan May 30 '18 at 14:09
  • Limitation in creating TCP Server in advance that in my application I don't know how many servers I have to create it should be create on runtime – shivam tiwari May 30 '18 at 14:20
  • That's something wrong... If it is *client*, then it has to know where to connect and you share with it your server port. The client can't dictate what to do on the server. You have to re-think an architecture. You can create servers dynamically, but that's already not a `Tcp.outboundAdapter()` responsibility. You need to start a separate `IntegrationFlow` for server listener and reuse the `ConnectionFactory` from there. – Artem Bilan May 30 '18 at 14:26
  • I think i am not able to explain my issue clearly, please see the below link [link](http://forum.spring.io/forum/spring-projects/integration/746570-send-message-from-server-connection-factory-to-the-client) this is what i want but I want to do this at runtime with above flow i.e the mesage received from activemq is send to the client connected to my server created dynamically – shivam tiwari Jun 06 '18 at 11:43
  • I have updated the question Hope this time you can understand what is my requirement. – shivam tiwari Jun 06 '18 at 20:05
  • Well, as I said you before: you need to create a single server connection factory and all the clients have t oconnect to the same server port. For that purpose you need to configure a `Tcp.inboundAdapter()` based on the `Tcp.netServer()`. When you receive a message via this channel adapter, you get a `IpHeaders.CONNECTION_ID` header to be able to distinguish your clients connected. At this point you really can create a dynamic flow for currently connected client and perform send via `TcpSendingMessageHandler` and that server connection factory reference. – Artem Bilan Jun 06 '18 at 20:40
  • I have used an approach for this but there is an issue please see the issue i have edited the question – shivam tiwari Jun 27 '18 at 13:35
  • You found answer in the Docs by yourself: once an `IntegrationFlow` is destroyed, all the associated beans are destroyed as well. There is no other way. What is the question? That's how things work that's all. Once you reconnect you have to recreate the whole `IntegrationFlow`. I don't say that your approach is right, but since you insist on your solution we can't do anything for you. Absolutely not clear why would one did such a solution using dynamic structures: everything sounds like some stable singleton beans. – Artem Bilan Jun 27 '18 at 13:51
  • Maybe would be better if you start from scratch and rethink an approach? Or even consider do not use Spring Integration on the matter... – Artem Bilan Jun 27 '18 at 13:54
0

It's not clear what you are trying to do; clients normally initiate connections, not offer a port for others to connect to.

Even if somebody connects to that port, the client won't know which socket to send the message to. So you will get errors at runtime.

No listener bound to server connection factory; will not read; exiting...

That message is simply saying there is no component registered with the factory to receive incoming messages.

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • Sorry if I am not able to clearly explain the question `Activemq ---> TCP server ----> External Application(Client Mode responsible to connect to my TCP server)` , hence I have to create a server and when the client got connected to my server then I have to send the message This link might help you to understand [question](http://forum.spring.io/forum/spring-projects/integration/746570-send-message-from-server-connection-factory-to-the-client) if you want I can edit the question – shivam tiwari Jun 06 '18 at 11:53
  • Your current flow is incorrect for that scenario; it will try to send data regardless of whether a client is connected and, in any case, even if there is a client, you need to set the connection id header so the outbound adapter knows where to send it. You need something like `tcpInbound->customService->tcpoutbound`. – Gary Russell Jun 06 '18 at 14:20
  • thanks for the response Do I have to create a seperate integrationfIow for `tcpInbound ->customservice-->outbound' both the adapter will have server cf and how I will add this configuration in my above flow . – shivam tiwari Jun 06 '18 at 17:17
  • You need a new flow; you can't add it to that flow; as I said, it is invalid. – Gary Russell Jun 06 '18 at 18:04
  • how do I link these two different flow ? Is there any resource or code snippet where I can take an idea how to implement the required flow – shivam tiwari Jun 06 '18 at 18:48
  • Your requirements are incomplete. For example, what do you want to do if 2 clients connect? Is it a queue or a topic? Perhaps you can edit your question to show your **actual** requirements and then somebody might be able to help you move forward. I might be able to take a look tomorrow. – Gary Russell Jun 06 '18 at 18:51
  • updated the question with actual requirement hope you understand my requirement. – shivam tiwari Jun 06 '18 at 20:06