/*
* 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.sling.ide.test.impl.helpers;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.io.IOUtils;
import org.junit.rules.ExternalResource;
import com.google.gson.stream.JsonReader;
import junit.framework.AssertionFailedError;
public class ExternalSlingLaunchpad extends ExternalResource {
private static final Pattern STARTLEVEL_JSON_SNIPPET = Pattern.compile("\"systemStartLevel\":(\\d+)");
private static final int EXPECTED_START_LEVEL = 30;
private static final long MAX_WAIT_TIME_MS = TimeUnit.MINUTES.toMillis(1);
private final LaunchpadConfig config;
public ExternalSlingLaunchpad(LaunchpadConfig config) {
this.config = config;
}
@Override
protected void before() throws Throwable {
Credentials creds = new UsernamePasswordCredentials(config.getUsername(), config.getPassword());
HttpClient client = new HttpClient();
client.getState().setCredentials(new AuthScope(config.getHostname(), config.getPort()), creds);
long cutoff = System.currentTimeMillis() + MAX_WAIT_TIME_MS;
List<SlingReadyRule> rules = new ArrayList<>();
rules.add(new StartLevelSlingReadyRule(client));
rules.add(new ActiveBundlesSlingReadyRule(client));
for (SlingReadyRule rule : rules) {
while (true) {
if (rule.evaluate()) {
break;
}
assertTimeout(cutoff);
Thread.sleep(100);
}
}
}
private void assertTimeout(long cutoff) throws AssertionFailedError {
if (System.currentTimeMillis() > cutoff) {
throw new AssertionFailedError("Sling launchpad did not start within " + MAX_WAIT_TIME_MS + " milliseconds");
}
}
private void debug(String string) {
if (System.getProperty("sling.ide.it.debug") != null) {
System.out.println("[" + new Date() + "] " + string);
}
}
private interface SlingReadyRule {
boolean evaluate() throws Exception;
}
private class StartLevelSlingReadyRule implements SlingReadyRule {
private final HttpClient client;
private final GetMethod httpMethod;
public StartLevelSlingReadyRule(HttpClient client) {
this.client = client;
httpMethod = new GetMethod(config.getUrl() + "system/console/vmstat");
}
@Override
public boolean evaluate() throws Exception {
int status = client.executeMethod(httpMethod);
debug("vmstat http call got return code " + status);
if (status == 200) {
String responseBody = IOUtils.toString(httpMethod.getResponseBodyAsStream(),
httpMethod.getResponseCharSet());
Matcher m = STARTLEVEL_JSON_SNIPPET.matcher(responseBody);
if (m.find()) {
int startLevel = Integer.parseInt(m.group(1));
debug("vmstat http call got startLevel " + startLevel);
if (startLevel >= EXPECTED_START_LEVEL) {
debug("current startLevel " + startLevel + " >= " + EXPECTED_START_LEVEL
+ ", we are done here");
return true;
}
}
}
return false;
}
}
private class ActiveBundlesSlingReadyRule implements SlingReadyRule {
private final HttpClient client;
private final GetMethod httpMethod;
public ActiveBundlesSlingReadyRule(HttpClient client) {
this.client = client;
httpMethod = new GetMethod(config.getUrl() + "system/console/bundles.json");
}
@Override
public boolean evaluate() throws Exception {
int status = client.executeMethod(httpMethod);
debug("bundles http call got return code " + status);
if ( status != 200) {
return false;
}
try (JsonReader jsonReader = new JsonReader(
new InputStreamReader(httpMethod.getResponseBodyAsStream(), httpMethod.getResponseCharSet()))) {
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
if (name.equals("s")) {
jsonReader.beginArray();
int total = jsonReader.nextInt();
int active = jsonReader.nextInt();
int fragment = jsonReader.nextInt();
debug("bundle http call status: total = " + total + ", active = " + active + ", fragment = " + fragment);
if (total == active + fragment) {
debug("All bundles are started, we are done here");
return true;
} else {
return false;
}
} else {
jsonReader.skipValue();
}
}
}
return false;
}
}
}