/******************************************************************************
* Copyright (c) 2006, 2010 VMware Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
* is available at http://www.opensource.org/licenses/apache2.0.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* VMware Inc.
*****************************************************************************/
package org.eclipse.gemini.blueprint.internal.service.interceptor;
import junit.framework.TestCase;
import org.eclipse.gemini.blueprint.service.importer.support.internal.support.DefaultRetryCallback;
import org.eclipse.gemini.blueprint.service.importer.support.internal.support.RetryCallback;
import org.eclipse.gemini.blueprint.service.importer.support.internal.support.RetryTemplate;
/**
*
* @author Costin Leau
*
*/
public class RetryTemplateTest extends TestCase {
private static class EventRecorderRetryTemplate extends RetryTemplate {
private long missingTarget = 0;
private long missingTargetInvocation = 0;
private long successfulStop = 0;
private long failedStop = 0;
/**
* Constructs a new <code>EventRecorderRetryTemplate</code> instance.
*
* @param waitTime
* @param notificationLock
*/
public EventRecorderRetryTemplate(long waitTime, Object notificationLock) {
super(waitTime, notificationLock);
}
/**
* Constructs a new <code>EventRecorderRetryTemplate</code> instance.
*
* @param notificationLock
*/
public EventRecorderRetryTemplate(Object notificationLock) {
super(notificationLock);
}
protected void callbackFailed(long stop) {
setFailedStop(stop);
}
protected void callbackSucceeded(long stop) {
setSuccessfulStop(stop);
}
protected synchronized void onMissingTarget() {
missingTargetInvocation++;
setMissingTarget(System.currentTimeMillis());
}
/**
* Returns the missingTarget.
*
* @return Returns the missingTarget
*/
public synchronized long getMissingTarget() {
return missingTarget;
}
public synchronized long getMissingTargetInvocation() {
return missingTarget;
}
/**
* @param missingTarget The missingTarget to set.
*/
public synchronized void setMissingTarget(long missingTarget) {
this.missingTarget = missingTarget;
}
/**
* Returns the successfulStop.
*
* @return Returns the successfulStop
*/
public synchronized long getSuccessfulStop() {
return successfulStop;
}
/**
* @param successfulStop The successfulStop to set.
*/
public synchronized void setSuccessfulStop(long successfulStop) {
this.successfulStop = successfulStop;
}
/**
* Returns the failedStop.
*
* @return Returns the failedStop
*/
public synchronized long getFailedStop() {
return failedStop;
}
/**
* @param failedStop The failedStop to set.
*/
public synchronized void setFailedStop(long failedStop) {
this.failedStop = failedStop;
}
}
private EventRecorderRetryTemplate template;
private RetryCallback callback;
private Object monitor;
private static class CountingCallback implements RetryCallback<Object> {
private int count = 0;
public final static int WAKES_THRESHOLD = 7;
public synchronized Object doWithRetry() {
count++;
return null;
}
public boolean isComplete(Object result) {
// postpone completion X times
if (getCount() == WAKES_THRESHOLD)
return true;
return false;
}
public synchronized int getCount() {
return count;
}
}
private static class FailingCallback implements RetryCallback<Object> {
private static Object VALUE = new Object();
public Object doWithRetry() {
return VALUE;
}
public boolean isComplete(Object result) {
return false;
}
}
protected void setUp() throws Exception {
monitor = new Object();
callback = new DefaultRetryCallback<Object>() {
public Object doWithRetry() {
return null;
}
};
}
protected void tearDown() throws Exception {
template = null;
}
// reset test - a separate thread reset a template that waits for a long time
public void testTemplateReset() throws Exception {
long initialWaitTime = 20 * 1000;
template = new EventRecorderRetryTemplate(initialWaitTime, monitor);
long start = System.currentTimeMillis();
Runnable shutdownTask = new Runnable() {
public void run() {
// wait a bit
try {
Thread.sleep(3 * 1000);
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("About to reset template...");
template.reset(0);
System.out.println("Resetted template...");
}
};
Thread th = new Thread(shutdownTask, "shutdown-thread");
th.start();
assertNull(template.execute(callback));
long stop = System.currentTimeMillis();
long waitingTime = stop - start;
assertTrue("Template not stopped in time", waitingTime < initialWaitTime);
}
// simple test that keeps waking up the template for a number of times
// the callback counts the invocations and then returns nicely
public void testSpuriousWakeup() throws Exception {
// wait 20s
long initialWaitTime = 20 * 1000;
final CountingCallback callback = new CountingCallback();
template = new EventRecorderRetryTemplate(initialWaitTime, monitor);
Runnable spuriousTask = new Runnable() {
public void run() {
try {
// start sending notifications to the monitor
do {
// sleep for a while
Thread.sleep(50);
synchronized (monitor) {
monitor.notifyAll();
}
} while (!callback.isComplete(null));
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
};
Thread th = new Thread(spuriousTask, "wake-up thread");
th.start();
long start = System.currentTimeMillis();
template.execute(callback);
long stop = System.currentTimeMillis();
long waited = stop - start;
assertTrue(template.getMissingTarget() >= start);
assertEquals(1, template.missingTargetInvocation);
assertEquals("successful callback does not end in failure", 0, template.failedStop);
assertEquals(CountingCallback.WAKES_THRESHOLD, callback.getCount());
assertTrue(waited < initialWaitTime);
}
// test that checks the template keeps waiting until the waiting period elapses
// if the callback returns falls even if there are (plenty) of wakeups
public void testFailingCallbackWithSpuriousWakeups() throws Exception {
// wait 10 secs
long initialWaitTime = 10 * 1000;
template = new EventRecorderRetryTemplate(initialWaitTime, monitor);
Runnable spuriousTask = new Runnable() {
public void run() {
try {
// start sending notifications to the monitor
do {
// sleep for a while
Thread.sleep(50);
synchronized (monitor) {
monitor.notifyAll();
}
} while (!callback.isComplete(null));
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
};
Thread th = new Thread(spuriousTask, "wake-up thread");
th.start();
callback = new FailingCallback();
long start = System.currentTimeMillis();
assertNull("failing callback should always return null", template.execute(callback));
long stop = System.currentTimeMillis();
assertEquals("failed callback does not end succesful", 0, template.successfulStop);
long waited = stop - start;
assertTrue(waited >= template.getFailedStop());
assertTrue(template.getMissingTarget() >= start);
assertEquals(1, template.missingTargetInvocation);
assertTrue(waited >= initialWaitTime - 3);
}
// test the retry with a thread that keeps waking up the template
// then does a reset
// the test checks the event method
public void testSpuriousWakeupWithReset() throws Exception {
long initialWaitTime = 30 * 1000;
template = new EventRecorderRetryTemplate(initialWaitTime, monitor);
Runnable spuriousAndResetTask = new Runnable() {
public void run() {
try {
// wait a bit
int count = 0;
// start sending notifications to the monitor
do {
// sleep for a while
Thread.sleep(50);
synchronized (monitor) {
monitor.notifyAll();
}
count++;
} while (count > 100);
// sent enough,
System.out.println("About to reset template...");
template.reset(0);
System.out.println("Resetted template...");
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
};
Thread th = new Thread(spuriousAndResetTask, "spurious_reset-thread");
th.start();
long start = System.currentTimeMillis();
assertNull(template.execute(callback));
long stop = System.currentTimeMillis();
long waitingTime = stop - start;
assertTrue("Template not stopped in time", waitingTime < initialWaitTime);
assertTrue(template.getMissingTarget() >= start);
assertEquals(1, template.missingTargetInvocation);
assertEquals("failed callback does not end succesful", 0, template.successfulStop);
assertTrue(waitingTime >= template.getFailedStop());
}
}