This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 7727e198bb26 Avoid port number in use test error
7727e198bb26 is described below

commit 7727e198bb26a89596e3636d10652e15ecf59acc
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Oct 16 10:08:02 2025 +0200

    Avoid port number in use test error
---
 .../apache/camel/tooling/maven/AvailablePort.java  |  53 ++++++
 .../camel/tooling/maven/AvailablePortFinder.java   | 203 +++++++++++++++++++++
 .../camel/tooling/maven/MavenResolverTest.java     |   2 +-
 .../src/test/resources/log4j2.properties           |   2 +-
 4 files changed, 258 insertions(+), 2 deletions(-)

diff --git 
a/tooling/camel-tooling-maven/src/test/java/org/apache/camel/tooling/maven/AvailablePort.java
 
b/tooling/camel-tooling-maven/src/test/java/org/apache/camel/tooling/maven/AvailablePort.java
new file mode 100644
index 000000000000..f60312851c2e
--- /dev/null
+++ 
b/tooling/camel-tooling-maven/src/test/java/org/apache/camel/tooling/maven/AvailablePort.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.tooling.maven;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AvailablePort {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(AvailablePort.class);
+
+    /**
+     * Probe a port to see if it is free
+     *
+     * @param  port                  an integer port number to be tested. If 
port is 0, then the next available port is
+     *                               returned.
+     * @throws IllegalStateException if the port is not free or, in case of 
port 0, if there are no ports available at
+     *                               all.
+     * @return                       the port number itself if the port is 
free or, in case of port 0, the first
+     *                               available port number.
+     */
+    public static int probePort(InetAddress address, int port) {
+
+        try (ServerSocket ss = new ServerSocket()) {
+            ss.setReuseAddress(true);
+            ss.bind(new InetSocketAddress(address, port), 1);
+            int probedPort = ss.getLocalPort();
+            LOG.info("Available port is -> {}", probedPort);
+            return probedPort;
+        } catch (IOException e) {
+            throw new IllegalStateException("Cannot find free port", e);
+        }
+    }
+}
diff --git 
a/tooling/camel-tooling-maven/src/test/java/org/apache/camel/tooling/maven/AvailablePortFinder.java
 
b/tooling/camel-tooling-maven/src/test/java/org/apache/camel/tooling/maven/AvailablePortFinder.java
new file mode 100644
index 000000000000..7f2896e95fdc
--- /dev/null
+++ 
b/tooling/camel-tooling-maven/src/test/java/org/apache/camel/tooling/maven/AvailablePortFinder.java
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.tooling.maven;
+
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Finds currently available server ports.
+ */
+public final class AvailablePortFinder {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(AvailablePortFinder.class);
+
+    private static final AvailablePortFinder INSTANCE = new 
AvailablePortFinder();
+
+    public class Port implements BeforeEachCallback, AfterAllCallback, 
AutoCloseable {
+        final int port;
+        String testClass;
+        final Throwable creation;
+
+        public Port(int port) {
+            this.port = port;
+            this.creation = new Throwable();
+        }
+
+        public int getPort() {
+            return port;
+        }
+
+        public void release() {
+            AvailablePortFinder.this.release(this);
+        }
+
+        @Override
+        public String toString() {
+            return Integer.toString(port);
+        }
+
+        @Override
+        public void beforeEach(ExtensionContext context) throws Exception {
+            testClass = 
context.getTestClass().map(Class::getName).orElse(null);
+            LOG.info("Registering port {} for test {}", port, testClass);
+        }
+
+        @Override
+        public void afterAll(ExtensionContext context) throws Exception {
+            release();
+        }
+
+        @Override
+        public void close() {
+            release();
+        }
+    }
+
+    private final Map<Integer, Port> portMapping = new ConcurrentHashMap<>();
+
+    /**
+     * Creates a new instance.
+     */
+    private AvailablePortFinder() {
+        // Do nothing
+    }
+
+    public static Port find() {
+        return INSTANCE.findPort();
+    }
+
+    synchronized Port findPort() {
+        while (true) {
+            final int port = probePort(0);
+            Port p = new Port(port);
+            Port prv = INSTANCE.portMapping.putIfAbsent(port, p);
+            if (prv == null) {
+                return p;
+            }
+        }
+    }
+
+    synchronized Port findPort(int fromPort, int toPort) {
+        for (int i = fromPort; i <= toPort; i++) {
+            try {
+                final int port = probePort(i);
+                Port p = new Port(port);
+                Port prv = INSTANCE.portMapping.putIfAbsent(port, p);
+                if (prv == null) {
+                    return p;
+                }
+            } catch (IllegalStateException e) {
+                // do nothing, let's try the next port
+            }
+        }
+        throw new IllegalStateException("Cannot find free port");
+    }
+
+    synchronized void release(Port port) {
+        INSTANCE.portMapping.remove(port.getPort(), port);
+    }
+
+    /**
+     * Gets the next available port.
+     *
+     * @throws IllegalStateException if there are no ports available
+     * @return                       the available port
+     */
+    public static int getNextAvailable() {
+        try (Port port = INSTANCE.findPort()) {
+            return port.getPort();
+        }
+    }
+
+    /**
+     * Gets the next available port.
+     *
+     * @throws IllegalStateException if there are no ports available
+     * @return                       the available port
+     */
+    public static int getNextRandomAvailable() {
+        Random random = new Random();
+        int fromPort = random.nextInt(10000, 65500);
+        int toPort = random.nextInt(fromPort, 65500);
+        try (Port port = INSTANCE.findPort(fromPort, toPort)) {
+            return port.getPort();
+        }
+    }
+
+    /**
+     * Gets the next available port in the given range.
+     *
+     * @param  fromPort              port number start range.
+     * @param  toPort                port number end range.
+     *
+     * @throws IllegalStateException if there are no ports available
+     * @return                       the available port
+     */
+    public static int getNextAvailable(int fromPort, int toPort) {
+        try (Port port = INSTANCE.findPort(fromPort, toPort)) {
+            return port.getPort();
+        }
+    }
+
+    /**
+     * Gets the next available port in the given range.
+     *
+     * @param  portNumber            port number start range.
+     * @param  failurePayload        handover data in case port allocation 
fails (i.e.: a default one to use)
+     * @param  failureHandler        a handler in case the requested port is 
not available
+     *
+     * @throws IllegalStateException if there are no ports available
+     * @return                       the available port
+     */
+    public static <T> int getSpecificPort(int portNumber, T failurePayload, 
Function<T, Integer> failureHandler) {
+        try (Port port = INSTANCE.findPort(portNumber, portNumber)) {
+            return port.getPort();
+        } catch (IllegalStateException e) {
+            if (LOG.isTraceEnabled()) {
+                LOG.trace("Unable to obtain the requested TCP port {}: {}", 
portNumber, e.getMessage(), e);
+            } else {
+                LOG.warn("Unable to obtain the requested TCP port {}: {}", 
portNumber, e.getMessage());
+            }
+
+            return failureHandler.apply(failurePayload);
+        }
+    }
+
+    /**
+     * Probe a port to see if it is free
+     *
+     * @param  port                  an integer port number to be tested. If 
port is 0, then the next available port is
+     *                               returned.
+     * @throws IllegalStateException if the port is not free or, in case of 
port 0, if there are no ports available at
+     *                               all.
+     * @return                       the port number itself if the port is 
free or, in case of port 0, the first
+     *                               available port number.
+     */
+    public static int probePort(int port) {
+        return AvailablePort.probePort(null, port);
+    }
+
+}
diff --git 
a/tooling/camel-tooling-maven/src/test/java/org/apache/camel/tooling/maven/MavenResolverTest.java
 
b/tooling/camel-tooling-maven/src/test/java/org/apache/camel/tooling/maven/MavenResolverTest.java
index 55e772b8f6e2..b6f2dec2adf9 100644
--- 
a/tooling/camel-tooling-maven/src/test/java/org/apache/camel/tooling/maven/MavenResolverTest.java
+++ 
b/tooling/camel-tooling-maven/src/test/java/org/apache/camel/tooling/maven/MavenResolverTest.java
@@ -60,7 +60,7 @@ public class MavenResolverTest {
     @BeforeAll
     public static void startMavenMirror() throws Exception {
         localServer = ServerBootstrap.bootstrap()
-                .setListenerPort(8888)
+                .setListenerPort(AvailablePortFinder.getNextAvailable(9234, 
10000))
                 .registerHandler("/maven/*", (req, res, context) -> {
                     Header authz = req.getFirstHeader("Authorization");
                     if (authz == null) {
diff --git a/tooling/camel-tooling-maven/src/test/resources/log4j2.properties 
b/tooling/camel-tooling-maven/src/test/resources/log4j2.properties
index 9e703f7435a9..8a561896f668 100644
--- a/tooling/camel-tooling-maven/src/test/resources/log4j2.properties
+++ b/tooling/camel-tooling-maven/src/test/resources/log4j2.properties
@@ -17,7 +17,7 @@
 
 appender.file.type = File
 appender.file.name = file
-appender.file.fileName = target/camel-tooling-maven.log
+appender.file.fileName = target/camel-tooling-maven-test.log
 appender.file.layout.type = PatternLayout
 appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
 appender.out.type = Console

Reply via email to