Ensure that all actuator endpoint accessors work for Boot 2.0

This commit is contained in:
Kris De Volder
2017-10-20 11:09:39 -07:00
parent 6d854f1f80
commit 413b90716b
5 changed files with 161 additions and 13 deletions

View File

@@ -26,6 +26,8 @@ import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -292,10 +294,33 @@ public class SpringBootApp {
if (environment != null) {
JSONObject env = new JSONObject(environment);
if (env != null) {
JSONObject portsObject = env.getJSONObject("server.ports");
JSONObject portsObject = env.optJSONObject("server.ports");
if (portsObject != null) {
String portValue = portsObject.get("local.server.port").toString();
return portValue;
String portValue = portsObject.optString("local.server.port");
if (portValue!=null) {
return portValue;
}
}
//Not found as direct property value... in Boot 2.0 we must look inside the 'propertySources'.
//Similar... but structure is more complex.
JSONArray propertySources = env.optJSONArray("propertySources");
if (propertySources!=null) {
for (Object _source : propertySources) {
if (_source instanceof JSONObject) {
JSONObject source = (JSONObject) _source;
String sourceName = source.optString("name");
if ("server.ports".equals(sourceName)) {
JSONObject props = source.optJSONObject("properties");
JSONObject valueObject = props.optJSONObject("local.server.port");
if (valueObject!=null) {
String portValue = valueObject.optString("value");
if (portValue!=null) {
return portValue;
}
}
}
}
}
}
}
}
@@ -317,7 +342,6 @@ public class SpringBootApp {
}
catch (InstanceNotFoundException e) {
}
return null;
}

View File

@@ -11,42 +11,59 @@
package org.springframework.ide.vscode.commons.boot.app.cli;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.net.URL;
import java.time.Duration;
import java.util.Map;
import java.util.Optional;
import org.json.JSONObject;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.ide.vscode.commons.util.AsyncProcess;
import org.springframework.ide.vscode.commons.util.ExternalCommand;
import org.springframework.ide.vscode.commons.util.StringUtil;
import org.springframework.ide.vscode.commons.util.test.ACondition;
public class SpringBootAppTest {
private static final String appName = "actuator-client-test-subject";
// private static final String appName = "actuator-client-15-test-subject"; // Boot 1.5 test app
private static final String appName = "actuator-client-20-test-subject"; //Boot 2.0 test app
private static final Duration TIMEOUT = Duration.ofSeconds(10);
private static AsyncProcess testAppRunner;
private static SpringBootApp testApp;
@BeforeClass
public static void setupClass() throws Exception {
testAppRunner = startTestApplication(SpringBootAppTest.class.getResource("/"+appName+"-0.0.1-SNAPSHOT.jar"));
//TODO: add some wait here until the boot app is 'ready'. Otherwise some of our test will just fail trying to read stuff that's not there yet.
testApp = getAppContaining(appName);
assertNotNull(testApp);
}
private static AsyncProcess startTestApplication(URL jarUrl) throws Exception {
File jarFile = new File(jarUrl.toURI());
return new AsyncProcess(new File("."), new ExternalCommand(
"java",
"-Dserver.port=0", //let spring boot pick randomized free port
"-jar",
jarFile.getAbsolutePath()
));
return new AsyncProcess(
new File("."),
new ExternalCommand(
"java",
"-Dserver.port=0", //let spring boot pick randomized free port
"-jar",
jarFile.getAbsolutePath()
),
false
);
}
private static SpringBootApp getAppContaining(String nameFragment) throws Exception {
return SpringBootApp.getAllRunningJavaApps().values().stream().filter(app -> app.getProcessName().contains(nameFragment)).findAny().get();
}
@AfterClass
public static void tearDownClass() throws Exception {
testAppRunner.kill();
@@ -65,5 +82,64 @@ public class SpringBootAppTest {
Optional<SpringBootApp> myProcess = allApps.values().stream().filter(app -> app.getProcessName().contains(appName)).findAny();
assertTrue(myProcess.isPresent());
}
@Test
public void getPort() throws Exception {
ACondition.waitFor(TIMEOUT, () -> {
int port = Integer.parseInt(testApp.getPort());
assertTrue(port > 0);
System.out.println("port = "+port);
});
}
@Test
public void getHost() throws Exception {
ACondition.waitFor(TIMEOUT, () -> {
String host = testApp.getHost();
assertTrue(StringUtil.hasText(host));
System.out.println("host = "+host);
});
}
@Test
public void getEnvironment() throws Exception {
ACondition.waitFor(TIMEOUT, () -> {
String env = testApp.getEnvironment();
assertNonEmptyJsonObject(env);
System.out.println("env = "+env);
});
}
@Test
public void getBeans() throws Exception {
ACondition.waitFor(TIMEOUT, () -> {
String beans = testApp.getBeans();
assertNonEmptyJsonObject(beans);
System.out.println("beans = "+beans);
});
}
@Test
public void getRequestMappings() throws Exception {
ACondition.waitFor(TIMEOUT, () -> {
String result = testApp.getRequestMappings();
assertNonEmptyJsonObject(result);
System.out.println("requestMappings = "+result);
});
}
@Test
public void getAutoConfigReport() throws Exception {
ACondition.waitFor(TIMEOUT, () -> {
String result = testApp.getAutoConfigReport();
assertNonEmptyJsonObject(result);
System.out.println("autoconfreport = "+result);
});
}
private void assertNonEmptyJsonObject(String jsonData) {
JSONObject parsed = new JSONObject(jsonData);
assertFalse(parsed.keySet().isEmpty());
}
}

View File

@@ -0,0 +1,48 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.util.test;
import java.time.Duration;
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
/**
* Utility for async condition checking, mostly intended for testing code.
* <p>
* Uses a polling loop to repeatedly evaluate some code that represents a condition
* of some kind. The test passes as soon as the condition passes, or fails if
* the condition does not pass within a given time limit.
*/
public class ACondition {
public interface Asserter {
void doAsserts() throws Exception;
}
public static void waitFor(Duration timeout, Asserter asserter) throws Exception {
long startTime = System.currentTimeMillis();
long timeout_millis = timeout.toMillis();
Throwable lastException = null;
do {
try {
asserter.doAsserts();
return;
} catch (Throwable e) {
lastException = e;
if (System.currentTimeMillis()-startTime < timeout_millis) {
Thread.sleep(300);
}
}
} while (System.currentTimeMillis()-startTime < timeout_millis);
throw ExceptionUtil.exception(lastException);
}
}