springspring-bootspockspring-boot-actuator

Get Spring Boot management port at runtime when management.port=0


I'm looking for advice on how to get the port that was assigned to the embedded Tomcat that is serving the actuator endpoint when setting management.port property to 0 in integration tests.

I'm using Spring Boot 1.3.2 with the following application.yml configuration:

server.port: 8080
server.contextPath: /my-app-context-path

management.port: 8081
management.context-path: /manage

...

My integration tests are then annotated with @WebIntegrationTest, setting the ports shown above to 0:

@WebIntegrationTest({ "server.port=0", "management.port=0" })

And the following utility class should be used to get access to the application configuration when doing full integration tests:

@Component
@Profile("testing")
class TestserverInfo {
    
    @Value( '${server.contextPath:}' )
    private String contextPath;

    @Autowired
    private EmbeddedWebApplicationContext server;
    
    @Autowired
    private ManagementServerProperties managementServerProperties
    
    
    public String getBasePath() {
        final int serverPort = server.embeddedServletContainer.port
        
        return "http://localhost:${serverPort}${contextPath}"
    }
    
    public String getManagementPath() {
        // The following wont work here:
        // server.embeddedServletContainer.port -> regular server port
        // management.port -> is zero just as server.port as i want random ports

        final int managementPort = // how can i get this one ?
        final String managementPath = managementServerProperties.getContextPath()
        
        return "http://localhost:${managementPort}${managementPath}"
    }
}

I already know the standard port can be get by using the local.server.port and there seems to be some equivalent for the management endpoint named local.management.port. But that one seems to have a different meaning. The official documentation doesn't mention a way to do this.

Is there currently any undocumented way to get a hand on that management port?


Solution Edit

As I am using the Spock Framework and spock-spring module for testing my Spring Boot application, I have to initialize the application like this:

@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MyApplication.class)

Somehow spock-spring or the test-initialization seems to affect the evaluation of the @Value Annotation so that @Value("${local.management.port}") resulted in

java.lang.IllegalArgumentException: Could not resolve placeholder 'local.management.port' in string value "${local.management.port}"

With your solution I knew the property existed, so I simply use the Spring Environment directly to retrieve the property-value at test runtime:

@Autowired
ManagementServerProperties managementServerProperties

@Autowired
Environment environment

public String getManagementPath() {
    final int managementPort = environment.getProperty('local.management.port', Integer.class)
    final String managementPath = managementServerProperties.getContextPath()
    
    return "http://localhost:${managementPort}${managementPath}"
}

Solution

  • This is how I've done it, copied straight from my test class (I use RestAssured for assertions):

    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.test.SpringApplicationConfiguration;
    import org.springframework.boot.test.WebIntegrationTest;
    
    import org.springframework.test.annotation.DirtiesContext;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import static com.jayway.restassured.RestAssured.get;
    import static org.hamcrest.CoreMatchers.equalTo;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(Application.class)
    @WebIntegrationTest(randomPort = true, value = {"management.port=0", "management.context-path=/admin"})
    @DirtiesContext
    public class ActuatorEndpointTest {
    
        @Value("${local.management.port}")
        private int localManagementPort;
    
        @Test
        public void actuatorHealthEndpointIsAvailable() throws Exception {
    
            String healthUrl = "http://localhost:" + localManagementPort + "/admin/health";
            get(healthUrl)
                    .then()
                    .assertThat().body("status", equalTo("UP"));
        }
    
    
    
    }