blob: 88b542332ca167fe2e9c17a5ef3bbf1677a210f5 [file] [log] [blame]
/*
* 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 SF 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.felix.hc.core.it;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import javax.inject.Inject;
import org.apache.felix.hc.api.HealthCheck;
import org.apache.felix.hc.api.Result;
import org.apache.felix.hc.api.execution.HealthCheckExecutor;
import org.apache.felix.hc.api.execution.HealthCheckSelector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.ops4j.pax.exam.Configuration;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.PaxExam;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
@RunWith(PaxExam.class)
public class AsyncHealthCheckIT {
@Inject
private HealthCheckExecutor executor;
@Inject
private BundleContext bundleContext;
@Configuration
public Option[] config() {
return U.config();
}
final AtomicInteger counter = new AtomicInteger(Integer.MIN_VALUE);
final static int MAX_VALUE = 12345678;
class TestHC implements HealthCheck {
@Override
public Result execute() {
final int v = counter.incrementAndGet();
return new Result(v > MAX_VALUE ? Result.Status.WARN : Result.Status.OK, "counter is now " + v);
}
}
private ServiceRegistration registerAsyncHc(HealthCheck hc, String id, Object async, int stickySec) {
final Dictionary<String, Object> props = new Hashtable<String, Object>();
props.put(HealthCheck.NAME, "async_HC_" + id);
props.put(HealthCheck.TAGS, id);
if (async instanceof String) {
props.put(HealthCheck.ASYNC_CRON_EXPRESSION, async);
} else if (async instanceof Long) {
props.put(HealthCheck.ASYNC_INTERVAL_IN_SEC, async);
}
if (stickySec > 0) {
props.put(HealthCheck.KEEP_NON_OK_RESULTS_STICKY_FOR_SEC, stickySec);
}
final ServiceRegistration result = bundleContext.registerService(HealthCheck.class.getName(), hc, props);
// Wait for HC to be registered
U.expectHealthChecks(1, executor, id);
return result;
}
private void assertStatus(String id, Result.Status expected, long maxMsec, String msg) throws InterruptedException {
final long timeout = System.currentTimeMillis() + 5000L;
while (System.currentTimeMillis() < timeout) {
final Result.Status actual = executor.execute(HealthCheckSelector.tags(id)).get(0).getHealthCheckResult().getStatus();
if (actual == expected) {
return;
}
Thread.sleep(100L);
}
fail("Did not get status " + expected + " after " + maxMsec + " msec " + msg);
}
@Test
public void testAsyncHealthCheckExecution() throws InterruptedException {
final String id = UUID.randomUUID().toString();
final HealthCheck hc = new TestHC();
final ServiceRegistration reg = registerAsyncHc(hc, id, "*/1 * * * * ?", 0);
final long maxMsec = 5000L;
try {
// Reset the counter and check that HC increments it without explicitly calling the executor
{
counter.set(0);
final long timeout = System.currentTimeMillis() + maxMsec;
while (System.currentTimeMillis() < timeout) {
int currentVal = counter.get();
if (currentVal > 0) {
break;
}
Thread.sleep(100L);
}
assertTrue("Expecting counter to be incremented", counter.get() > 0);
}
// Verify that we get the right log
final String msg = executor.execute(HealthCheckSelector.tags(id)).get(0).getHealthCheckResult().iterator().next().getMessage();
assertTrue("Expecting the right message: " + msg, msg.contains("counter is now"));
// And verify that calling executor lots of times doesn't increment as much
final int previous = counter.get();
final int n = 100;
for (int i = 0; i < n; i++) {
executor.execute(HealthCheckSelector.tags(id));
}
assertTrue("Expecting counter to increment asynchronously", counter.get() < previous + n);
// Verify that results are not sticky
assertStatus(id, Result.Status.OK, maxMsec, "before WARN");
counter.set(MAX_VALUE + 1);
assertStatus(id, Result.Status.WARN, maxMsec, "right after WARN");
counter.set(0);
assertStatus(id, Result.Status.OK, maxMsec, "after resetting counter");
} finally {
reg.unregister();
}
}
@Test
public void testAsyncHealthCheckExecutionWithInterval() throws InterruptedException {
final String id = UUID.randomUUID().toString();
final HealthCheck hc = new TestHC();
final ServiceRegistration reg = registerAsyncHc(hc, id, new Long(2), 0);
final long maxMsec = 5000L;
try {
// Reset the counter and check that HC increments it without explicitly calling the executor
{
counter.set(0);
final long timeout = System.currentTimeMillis() + maxMsec;
while (System.currentTimeMillis() < timeout) {
int currentVal = counter.get();
if (currentVal > 0) {
break;
}
Thread.sleep(100L);
}
assertTrue("Expecting counter to be incremented", counter.get() > 0);
}
// Verify that we get the right log
final String msg = executor.execute(HealthCheckSelector.tags(id)).get(0).getHealthCheckResult().iterator().next().getMessage();
assertTrue("Expecting the right message: " + msg, msg.contains("counter is now"));
} finally {
reg.unregister();
}
}
@Test
public void testAsyncHealthCheckWithStickyResults() throws InterruptedException {
final String id = UUID.randomUUID().toString();
final HealthCheck hc = new TestHC();
final long maxMsec = 5000L;
final int stickySeconds = 60;
final ServiceRegistration reg = registerAsyncHc(hc, id, "*/1 * * * * ?", stickySeconds);
try {
assertStatus(id, Result.Status.OK, maxMsec, "before WARN");
counter.set(MAX_VALUE + 1);
assertStatus(id, Result.Status.WARN, maxMsec, "right after WARN");
counter.set(0);
// Counter should be incremented after a while, and in range, but with sticky WARN result
final long timeout = System.currentTimeMillis() + maxMsec;
boolean ok = false;
while (System.currentTimeMillis() < timeout) {
if (counter.get() > 0 && counter.get() < MAX_VALUE) {
ok = true;
break;
}
Thread.sleep(100L);
}
assertTrue("expecting counter to be incremented", ok);
assertStatus(id, Result.Status.WARN, maxMsec, "after resetting counter, expecting sticky result");
} finally {
reg.unregister();
}
}
}