0
votes

Gary Russell kindly answered a previous question of mine about Spring Integration udp flows. Moving from there, I have stumbled upon an issue with ports.

The Spring Integration documentation says that you can put 0 to the inbound channel adapter port, and the OS will select an available port for the adapter, which can be retrieved at runtime invoking getPort() on the adapter object. The problem is that at runtime I just get a 0 if I try to retrieve the port programmatically. Here's "my" code (i.e. a slightly modified version of Russel's answer to my previous question for Spring Integration 4.3.12, which I am currently using).

@SpringBootApplication
public class  TestApp  {

private final Map<Integer, IntegrationFlowRegistration> registrations = new HashMap<>();

@Autowired
private IntegrationFlowContext flowContext;

public static void main(String[] args) {
    SpringApplication.run(TestApp.class, args);
}

@Bean
public PublishSubscribeChannel channel() {
    return new PublishSubscribeChannel();
}

@Bean
public TestData test() {
    return new TestData();
}

@Bean
public ApplicationRunner runner() {     
    return args -> {
        UnicastReceivingChannelAdapter source;
        source = makeANewUdpInbound(0);
        makeANewUdpOutbound(source.getPort());
         Thread.sleep(5_000);
         channel().send(MessageBuilder.withPayload("foo\n").build());
         this.registrations.values().forEach(r -> {
           r.stop();
           r.destroy();
         });
         this.registrations.clear();

         makeANewUdpInbound(1235);
         makeANewUdpOutbound(1235);
         Thread.sleep(5_000);
         channel().send(MessageBuilder.withPayload("bar\n").build());
         this.registrations.values().forEach(r -> {
           r.stop();
           r.destroy();
         });
         this.registrations.clear();
    };
}

public UnicastSendingMessageHandler makeANewUdpOutbound(int port) {
    System.out.println("Creating an adapter to send to port " + port);
    UnicastSendingMessageHandler adapter = new UnicastSendingMessageHandler("localhost", port);
    IntegrationFlow flow = IntegrationFlows.from(channel())
            .handle(adapter)
            .get();
    IntegrationFlowRegistration registration = flowContext.registration(flow).register();
    registrations.put(port, registration);
    return adapter;
}

public UnicastReceivingChannelAdapter makeANewUdpInbound(int port) {
    System.out.println("Creating an adapter to receive from port " + port);
    UnicastReceivingChannelAdapter source = new UnicastReceivingChannelAdapter(port);
    IntegrationFlow flow = IntegrationFlows.from(source)
            .<byte[], String>transform(String::new)
            .handle(System.out::println)
            .get();
    IntegrationFlowRegistration registration = flowContext.registration(flow).register();
    registrations.put(port, registration);
    return source;
}
}

The output I read is

Creating an adapter to receive from port 0
Creating an adapter to send to port 0
Creating an adapter to receive from port 1235
Creating an adapter to send to port 1235
GenericMessage [payload=bar, headers={ip_packetAddress=127.0.0.1/127.0.0.1:54374, ip_address=127.0.0.1, id=c95d6255-e63a-433d-3723-c389fe66b060, ip_port=54374, ip_hostname=127.0.0.1, timestamp=1517220716983}]

I suspect the library did create adapters on OS-chosen free ports, but I am unable to retrieve the assigned port.

1

1 Answers

0
votes

The port is assigned asynchronously; you need to wait until the port is actually assigned. Something like...

int n = 0;
while (n++ < 100 && ! source.isListening()) {
    Thread.sleep(100;
}
if (!source.isListening()) {
    // failed to start in 10 seconds.
}

We should probably enhance the adapter to emit an event when the port is ready. Feel free to open an 'Improvement' JIRA Issue.