SLING-5681 Verify that a bundle is started correctly after deployment
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1784084 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index 335bf31..e102590 100644
--- a/pom.xml
+++ b/pom.xml
@@ -84,6 +84,18 @@
<artifactId>org.apache.sling.junit.core</artifactId>
<version>1.0.24-SNAPSHOT</version>
</dependency>
+ <!-- JSR 353 (JSON-P 1.0) implementation -->
+ <dependency>
+ <groupId>org.apache.johnzon</groupId>
+ <artifactId>johnzon-core</artifactId>
+ <version>1.0.0</version>
+ </dependency>
+ <!-- JSR 353 API -->
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-json_1.0_spec</artifactId>
+ <version>1.0-alpha-1</version>
+ </dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
diff --git a/src/main/java/org/apache/sling/testing/teleporter/client/TeleporterHttpClient.java b/src/main/java/org/apache/sling/testing/teleporter/client/TeleporterHttpClient.java
index 7aa1810..e06617d 100644
--- a/src/main/java/org/apache/sling/testing/teleporter/client/TeleporterHttpClient.java
+++ b/src/main/java/org/apache/sling/testing/teleporter/client/TeleporterHttpClient.java
@@ -21,17 +21,29 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
+import java.io.StringReader;
+import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonException;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import javax.json.JsonValue.ValueType;
import javax.xml.bind.DatatypeConverter;
+import org.apache.commons.io.IOUtils;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.junit.runners.model.MultipleFailureException;
@@ -46,6 +58,23 @@
private String credentials = null;
private final String testServletPath;
+ static final class SimpleHttpResponse {
+ private final int status;
+ private final String body;
+
+ public SimpleHttpResponse(int status, String body) {
+ super();
+ this.status = status;
+ this.body = body;
+ }
+ public int getStatus() {
+ return status;
+ }
+ public String getBody() {
+ return body;
+ }
+ }
+
TeleporterHttpClient(String baseUrl, String testServletPath) {
this.baseUrl = baseUrl;
if(!testServletPath.endsWith("/")) {
@@ -66,16 +95,16 @@
}
/** Wait until specified URL returns specified status */
- public void waitForStatus(String url, int expectedStatus, int timeoutMsec) throws IOException {
+ public String waitForStatus(String url, int expectedStatus, int timeoutMsec) throws IOException {
final long end = System.currentTimeMillis() + timeoutMsec;
final Set<Integer> statusSet = new HashSet<Integer>();
final ExponentialBackoffDelay d = new ExponentialBackoffDelay(50, 250);
while(System.currentTimeMillis() < end) {
try {
- final int status = getHttpGetStatus(url);
- statusSet.add(status);
- if(status == expectedStatus) {
- return;
+ final SimpleHttpResponse response = getHttpGetStatus(url);
+ statusSet.add(response.getStatus());
+ if(response.getStatus() == expectedStatus) {
+ return response.getBody();
}
d.waitNextDelay();
} catch(Exception ignore) {
@@ -108,6 +137,52 @@
cleanup(c);
}
}
+
+ void verifyCorrectBundleState(String bundleSymbolicName, int webConsoleReadyTimeoutSeconds) throws IOException {
+ final String url = baseUrl + "/system/console/bundles/" + bundleSymbolicName + ".json";
+
+ String jsonBody = waitForStatus(url, 200, webConsoleReadyTimeoutSeconds * 1000);
+ // deserialize json (https://issues.apache.org/jira/browse/SLING-6536)
+ try {
+ JsonReader jsonReader = Json.createReader(new StringReader(jsonBody));
+ // extract state
+ JsonArray jsonArray = jsonReader.readObject().getJsonArray("data");
+ if (jsonArray == null) {
+ throw new JsonException("Could not find 'data' array");
+ }
+ JsonObject bundleObject = jsonArray.getJsonObject(0);
+ String state = bundleObject.getString("state");
+ if ("Active".equals(state)) {
+ return;
+ }
+ // otherwise evaluate the import section
+ JsonArray propsArray = bundleObject.getJsonArray("props");
+ if (propsArray == null) {
+ throw new JsonException("Could not find 'props' object");
+ }
+ // iterate through all of them until key="Imported Packages" is found
+ for (JsonValue propValue : propsArray) {
+ if (propValue.getValueType().equals(ValueType.OBJECT)) {
+ JsonObject propObject = (JsonObject)propValue;
+ if ("Imported Packages".equals(propObject.getString("key"))) {
+ JsonArray importedPackagesArray = propObject.getJsonArray("value");
+ String reason = "Unknown";
+ for (JsonValue importedPackageValue : importedPackagesArray) {
+ if (importedPackageValue.getValueType().equals(ValueType.STRING)) {
+ String importedPackage = ((JsonString)importedPackageValue).getString();
+ if (importedPackage.startsWith("ERROR:")) {
+ reason = importedPackage;
+ }
+ }
+ }
+ throw new IllegalStateException("The test bundle is in state " + state +". Most probably this is due to unresolved import-packages: " + reason);
+ }
+ }
+ }
+ } catch (JsonException|IndexOutOfBoundsException e) {
+ throw new IllegalArgumentException("Test bundle '" + bundleSymbolicName +"' not correctly installed. Could not parse JSON response though to expose further information: " + jsonBody, e);
+ }
+ }
void uninstallBundle(String bundleSymbolicName, int webConsoleReadyTimeoutSeconds) throws MalformedURLException, IOException {
// equivalent of
@@ -131,7 +206,7 @@
}
}
- public int getHttpGetStatus(String url) throws MalformedURLException, IOException {
+ public SimpleHttpResponse getHttpGetStatus(String url) throws MalformedURLException, IOException {
final HttpURLConnection c = (HttpURLConnection)new URL(url).openConnection();
setConnectionCredentials(c);
c.setUseCaches(false);
@@ -139,16 +214,22 @@
c.setDoInput(true);
c.setInstanceFollowRedirects(false);
boolean gotStatus = false;
- int result = 0;
+ int status = 0;
try {
- result = c.getResponseCode();
+ status = c.getResponseCode();
gotStatus = true;
+ //4xx: client error, 5xx: server error. See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.
+ boolean isError = status >= 400;
+ //In HTTP error cases, HttpURLConnection only gives you the input stream via #getErrorStream().
+ InputStream is = isError ? c.getErrorStream() : c.getInputStream();
+ StringWriter writer = new StringWriter();
+ IOUtils.copy(is, writer, StandardCharsets.UTF_8);
+ return new SimpleHttpResponse(status, writer.toString());
} finally {
// If we didn't get a status, do not attempt
// to get input streams as this would retry connecting
cleanup(c, gotStatus);
}
- return result;
}
void runTests(String testSelectionPath, int testReadyTimeoutSeconds) throws MalformedURLException, IOException, MultipleFailureException {
@@ -158,7 +239,7 @@
final long timeout = System.currentTimeMillis() + (testReadyTimeoutSeconds * 1000L);
final ExponentialBackoffDelay delay = new ExponentialBackoffDelay(25, 1000);
while(true) {
- if(getHttpGetStatus(testUrl) == 200) {
+ if(getHttpGetStatus(testUrl).getStatus() == 200) {
break;
}
if(System.currentTimeMillis() > timeout) {
diff --git a/src/test/java/org/apache/sling/testing/teleporter/client/TeleporterHttpClientTest.java b/src/test/java/org/apache/sling/testing/teleporter/client/TeleporterHttpClientTest.java
index d40981f..1cdcb48 100644
--- a/src/test/java/org/apache/sling/testing/teleporter/client/TeleporterHttpClientTest.java
+++ b/src/test/java/org/apache/sling/testing/teleporter/client/TeleporterHttpClientTest.java
@@ -23,12 +23,17 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import java.io.FileReader;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.net.MalformedURLException;
+import java.nio.charset.StandardCharsets;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
+import org.apache.commons.io.IOUtils;
import org.apache.sling.testing.clients.util.TimeoutsProvider;
import org.junit.Rule;
import org.junit.Test;
@@ -58,10 +63,10 @@
final TeleporterHttpClient client = new TeleporterHttpClient(baseUrl, TEST_PATH);
final String testUrl = baseUrl + TEST_PATH;
- assertEquals(404, client.getHttpGetStatus(baseUrl + TEST_PATH));
+ assertEquals(404, client.getHttpGetStatus(baseUrl + TEST_PATH).getStatus());
activateLater(TEST_PATH, 1000);
client.waitForStatus(testUrl, 200, TimeoutsProvider.getInstance().getTimeout(2000));
- assertEquals(200, client.getHttpGetStatus(baseUrl + TEST_PATH));
+ assertEquals(200, client.getHttpGetStatus(baseUrl + TEST_PATH).getStatus());
}
@Test
@@ -69,7 +74,7 @@
final TeleporterHttpClient client = new TeleporterHttpClient(baseUrl, TEST_PATH);
final String testUrl = baseUrl + TEST_PATH;
- assertEquals(404, client.getHttpGetStatus(baseUrl + TEST_PATH));
+ assertEquals(404, client.getHttpGetStatus(baseUrl + TEST_PATH).getStatus());
activateLater(TEST_PATH, 1000);
try {
@@ -91,11 +96,35 @@
int status = 0;
for(int i=0; i < N; i++) {
try {
- status = client.getHttpGetStatus(testUrl);
+ status = client.getHttpGetStatus(testUrl).getStatus();
} catch(Exception e) {
fail("Exception at index " + i + ":" + e);
}
assertEquals("Expecting status 200 at index " + i, 200, status);
}
}
+
+ @Test(expected=IllegalStateException.class)
+ public void testVerifyCorrectBundleStateForInactiveBundle() throws IOException {
+ final TeleporterHttpClient client = new TeleporterHttpClient(baseUrl, "invalid");
+ String bundleSymbolicName = "testBundle1";
+ // open resource
+ try (InputStream inputStream = this.getClass().getResourceAsStream("/bundle-not-active.json")) {
+ String body = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
+ givenThat(get(urlEqualTo("/system/console/bundles/" + bundleSymbolicName + ".json")).willReturn(aResponse().withStatus(200).withBody(body)));
+ }
+ client.verifyCorrectBundleState(bundleSymbolicName, 1);
+ }
+
+ @Test
+ public void testVerifyCorrectBundleStateForActiveBundle() throws IOException {
+ final TeleporterHttpClient client = new TeleporterHttpClient(baseUrl, "invalid");
+ String bundleSymbolicName = "testBundle2";
+ // open resource
+ try (InputStream inputStream = this.getClass().getResourceAsStream("/bundle-active.json")) {
+ String body = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
+ givenThat(get(urlEqualTo("/system/console/bundles/" + bundleSymbolicName + ".json")).willReturn(aResponse().withStatus(200).withBody(body)));
+ }
+ client.verifyCorrectBundleState(bundleSymbolicName, 1);
+ }
}
\ No newline at end of file
diff --git a/src/test/resources/bundle-active.json b/src/test/resources/bundle-active.json
new file mode 100644
index 0000000..5072973
--- /dev/null
+++ b/src/test/resources/bundle-active.json
@@ -0,0 +1 @@
+{"status":"Bundle information: 148 bundles in total - all 148 bundles active.","s":[148,144,4,0,0],"data":[{"id":147,"name":"ClientSideTeleporter.ValidationServiceIT.18-27-36.cd5a3250-766b-47a0-af9a-b5e56c0079c0","fragment":false,"stateRaw":32,"state":"Active","version":"0","symbolicName":"ClientSideTeleporter.ValidationServiceIT.18-27-36.cd5a3250-766b-47a0-af9a-b5e56c0079c0","category":"","props":[{"key":"Symbolic Name","value":"ClientSideTeleporter.ValidationServiceIT.18-27-36.cd5a3250-766b-47a0-af9a-b5e56c0079c0"},{"key":"Version","value":"0"},{"key":"Bundle Location","value":"inputstream:ClientSideTeleporter.ValidationServiceIT.18-27-36.cd5a3250-766b-47a0-af9a-b5e56c0079c0.jar"},{"key":"Last Modification","value":"Wed Feb 22 18:27:37 CET 2017"},{"key":"Start Level","value":20},{"key":"Exported Packages","value":"---"},{"key":"Imported Packages","value":["org.apache.sling.api.resource,version=2.10.0 from <a href='/system/console/bundles/94'>org.apache.sling.api (94)<\/a>","org.apache.sling.api.wrappers,version=2.6.0 from <a href='/system/console/bundles/94'>org.apache.sling.api (94)<\/a>","org.apache.sling.commons.json,version=2.0.4 from <a href='/system/console/bundles/38'>org.apache.sling.commons.json (38)<\/a>","org.apache.sling.junit.rules,version=1.3.0 from <a href='/system/console/bundles/122'>org.apache.sling.junit.core (122)<\/a>","org.apache.sling.validation,version=1.0.0 from <a href='/system/console/bundles/145'>org.apache.sling.validation.api (145)<\/a>","org.apache.sling.validation.model,version=1.0.0 from <a href='/system/console/bundles/145'>org.apache.sling.validation.api (145)<\/a>","org.junit,version=4.12.0 from <a href='/system/console/bundles/122'>org.apache.sling.junit.core (122)<\/a>"]},{"key":"Manifest Headers","value":["Bnd-LastModified: 1487784456896","Built-By: konradwindszus","Bundle-ManifestVersion: 2","Bundle-Name: ClientSideTeleporter.ValidationServiceIT.18-27-36.cd5a3250-766b-47a0-af9a-b5e56c0079c0","Bundle-SymbolicName: ClientSideTeleporter.ValidationServiceIT.18-27-36.cd5a3250-766b-47a0-af9a-b5e56c0079c0","Bundle-Version: 0","Created-By: 1.8.0_66 (Oracle Corporation)","Import-Package: org.apache.sling.api.resource, org.apache.sling.api.wrappers, org.apache.sling.commons.json, org.apache.sling.junit.rules, org.apache.sling.validation, org.apache.sling.validation.model, org.junit","Manifest-Version: 1.0","Originally-Created-By: pax-tinybundles-2.0.0","Private-Package: SLING-CONTENT.apps.sling.validation, SLING-CONTENT.apps.sling.validation.models, org.apache.sling.validation.impl","Sling-Test-Regexp: org.apache.sling.validation.impl.ValidationServiceIT.*","Sling-Test-WaitForService-Timeout: 10","TinybundlesVersion: pax-tinybundles-2.0.0","Tool: Bnd-2.1.0.20130426-122213"]},{"key":"nfo","value":{}}]}]}
\ No newline at end of file
diff --git a/src/test/resources/bundle-not-active.json b/src/test/resources/bundle-not-active.json
new file mode 100644
index 0000000..17027bd
--- /dev/null
+++ b/src/test/resources/bundle-not-active.json
@@ -0,0 +1 @@
+{"status":"Bundle information: 147 bundles in total, 142 bundles active, 4 bundles active fragments, 1 bundle installed.","s":[147,142,4,0,1],"data":[{"id":147,"name":"ClientSideTeleporter.ValidationServiceIT.12-39-11.de72ad9c-a053-4767-80b1-5e3c25ed4cee","fragment":false,"stateRaw":2,"state":"Installed","version":"0","symbolicName":"ClientSideTeleporter.ValidationServiceIT.12-39-11.de72ad9c-a053-4767-80b1-5e3c25ed4cee","category":"","props":[{"key":"Symbolic Name","value":"ClientSideTeleporter.ValidationServiceIT.12-39-11.de72ad9c-a053-4767-80b1-5e3c25ed4cee"},{"key":"Version","value":"0"},{"key":"Bundle Location","value":"inputstream:ClientSideTeleporter.ValidationServiceIT.12-39-11.de72ad9c-a053-4767-80b1-5e3c25ed4cee.jar"},{"key":"Last Modification","value":"Wed Feb 22 12:45:51 CET 2017"},{"key":"Start Level","value":20},{"key":"Imported Packages","value":["org.apache.sling.api.resource from <a href='/system/console/bundles/94'>org.apache.sling.api (94)<\/a>","org.apache.sling.api.wrappers from <a href='/system/console/bundles/94'>org.apache.sling.api (94)<\/a>","org.apache.sling.commons.json from <a href='/system/console/bundles/38'>org.apache.sling.commons.json (38)<\/a>","org.apache.sling.junit.rules from <a href='/system/console/bundles/122'>org.apache.sling.junit.core (122)<\/a>","ERROR: org.apache.sling.validation.model -- Cannot be resolved","org.junit from <a href='/system/console/bundles/122'>org.apache.sling.junit.core (122)<\/a>"]},{"key":"Manifest Headers","value":["Bnd-LastModified: 1487763552010","Built-By: konradwindszus","Bundle-ManifestVersion: 2","Bundle-Name: ClientSideTeleporter.ValidationServiceIT.12-39-11.de72ad9c-a053-4767-80b1-5e3c25ed4cee","Bundle-SymbolicName: ClientSideTeleporter.ValidationServiceIT.12-39-11.de72ad9c-a053-4767-80b1-5e3c25ed4cee","Bundle-Version: 0","Created-By: 1.8.0_66 (Oracle Corporation)","Import-Package: org.apache.sling.api.resource, org.apache.sling.api.wrappers, org.apache.sling.commons.json, org.apache.sling.junit.rules, org.apache.sling.validation, org.apache.sling.validation.model, org.junit","Manifest-Version: 1.0","Originally-Created-By: pax-tinybundles-2.0.0","Private-Package: SLING-CONTENT.apps.sling.validation, SLING-CONTENT.apps.sling.validation.models, org.apache.sling.validation.impl","Sling-Test-Regexp: org.apache.sling.validation.impl.ValidationServiceIT.*","Sling-Test-WaitForService-Timeout: 10","TinybundlesVersion: pax-tinybundles-2.0.0","Tool: Bnd-2.1.0.20130426-122213"]},{"key":"nfo","value":{}}]}]}
\ No newline at end of file