Walk through using build-helper:reserve-network-port to make integration tests safe to run in parallel. Why is it needed?
answer
- free OS ephemeral port
- portNames -> properties
- reserve before server start
- process-test-resources / pre-integration-test
- feed via failsafe systemPropertyVariables
- tiny TOCTOU window
basics
~20 sreserve-network-port finds free TCP ports and stores them in Maven properties. You bind it before your test server starts, then point the server and tests at those properties instead of a hardcoded port, so concurrent builds don't clash.
solid answer
~40 sHardcoding a port (say 8080) for an embedded server in integration tests breaks when two builds run on the same machine/CI agent — they fight for the port. build-helper:reserve-network-port asks the OS for currently-free ephemeral ports and assigns each to a named Maven property via `<portNames>`. You bind the execution to a phase that runs *before* the server starts — commonly `process-test-resources` or `pre-integration-test` — then reference `${it.http.port}` when configuring the embedded server (e.g. failsafe system properties, a Spring profile, or the maven-surefire/failsafe `<systemPropertyVariables>`). There's an inherent TOCTOU gap (reserved then later bound), but it's small and good enough in practice. This pattern is what lets the maven-failsafe-plugin run module integration tests concurrently across a multi-module reactor without port collisions.
code
xml · 11 lines<execution>
<id>reserve-it-ports</id>
<phase>process-test-resources</phase>
<goals><goal>reserve-network-port</goal></goals>
<configuration>
<portNames>
<portName>it.http.port</portName>
<portName>it.admin.port</portName>
</portNames>
</configuration>
</execution>go deeper
Know it picks a free port and stores it in a property.
Bind it before server startup and wire the property into failsafe systemPropertyVariables and server config.
Use it to enable -T parallel reactor ITs; understand the TOCTOU window and phase ordering.
Standardize the reserve+failsafe pattern repo-wide so all modules' ITs are parallel-safe by default.
## Why hardcoded ports fail Integration tests often spin up an embedded server (Tomcat, Jetty, an in-memory broker, a stub HTTP server). If the port is hardcoded, two problems appear: 1. **Parallel builds collide** — CI runs multiple jobs on one agent, or `mvn -T` builds modules concurrently; both try to bind the same port and one fails with 'address already in use'. 2. **Dev-machine clashes** — a developer already has something on 8080. ## What reserve-network-port does The goal asks the operating system for one or more **free** TCP ports (the OS hands out an unused ephemeral port) and writes each into a Maven property you name. Those properties then flow into your server config and test config. ```xml <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> <version>3.6.0</version> <executions> <execution> <id>reserve-it-ports</id> <phase>process-test-resources</phase> <goals><goal>reserve-network-port</goal></goals> <configuration> <portNames> <portName>it.http.port</portName> <portName>it.admin.port</portName> </portNames> </configuration> </execution> </executions> </plugin> ``` After it runs, `${it.http.port}` and `${it.admin.port}` hold free port numbers. ## Feeding the ports to the test Bind the reservation **before** the server is started, then pass the properties through. With failsafe: ```xml <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <configuration> <systemPropertyVariables> <it.http.port>${it.http.port}</it.http.port> </systemPropertyVariables> </configuration> </plugin> ``` The test reads `System.getProperty("it.http.port")`; the embedded server is configured with the same value. ## Phase ordering matters Reserve must run **before** anything that needs the port. `process-test-resources` runs before `test`; `pre-integration-test` runs before `integration-test`. Pick whichever sits ahead of your server startup. If you reserve after starting the server, the property is empty. ## The TOCTOU caveat There is a 'time-of-check / time-of-use' window: the port is free when reserved but technically could be taken by another process before your server actually binds. In practice the window is tiny and this pattern is the standard solution. For extra safety some setups reserve and immediately have the server bind. ## Where it shines Multi-module reactors built with `mvn -T 1C` (one thread per core) run each module's failsafe ITs simultaneously; per-module reserved ports keep them isolated.
- Why must the execution be bound before the integration-test phase?The port property is only set when the goal runs. If you start the server before reserve-network-port executes, the property is unresolved/empty and the server binds to nothing useful. Bind to process-test-resources or pre-integration-test.
- Is there any race condition risk?Yes, a small TOCTOU gap: the port is free at reservation but could be grabbed before the server binds. The window is tiny and the pattern is the accepted standard; reserving immediately before binding minimizes it.
Like the restaurant host handing out a buzzer with a free table number instead of everyone fighting for table 12 — each party gets whatever table is actually open right now.
saying these in an interview costs you the question
- Hardcoding ports for parallel ITs and assuming it's fine
- Binding reserve-network-port after the server starts
- Claiming it guarantees the port can never be taken (ignoring TOCTOU)