diff --git a/common/source/java/ch/systemsx/cisd/common/concurrent/MonitoringProxy.java b/common/source/java/ch/systemsx/cisd/common/concurrent/MonitoringProxy.java index da17857d9df94540df612511bfa29cc8a9f1e6fa..9f5800482bc640c46a4c599cbf3840d4a2a7f70b 100644 --- a/common/source/java/ch/systemsx/cisd/common/concurrent/MonitoringProxy.java +++ b/common/source/java/ch/systemsx/cisd/common/concurrent/MonitoringProxy.java @@ -26,11 +26,13 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; +import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel; import ch.systemsx.cisd.base.exceptions.InterruptedExceptionUnchecked; import ch.systemsx.cisd.base.exceptions.TimeoutExceptionUnchecked; import ch.systemsx.cisd.base.namedthread.NamedCallable; @@ -106,6 +108,12 @@ public class MonitoringProxy<T> private final Map<Method, Object> errorMethodValueMap; + private final Map<Class<?>, Object> asyncOkTypeValueMap; + + private final Map<Method, Object> asyncOkMethodValueMap; + + private final Set<Method> asyncMethodSet; + private final Set<Class<? extends Exception>> exceptionClassesSuitableForRetrying; private final MonitoringInvocationHandler handler; @@ -122,10 +130,12 @@ public class MonitoringProxy<T> private ISimpleLogger loggerOrNull; + private LogLevel logLevelForSuccessfulCalls; + private IMonitoringProxyLogger invocationLoggerOrNull; private IActivitySensor sensorOrNull; - + private ExecutorService executorService = defaultExecutorService; private Set<MonitorCommunicator> currentOperations = @@ -192,6 +202,22 @@ public class MonitoringProxy<T> result.put(Short.TYPE, (short) 0); result.put(Integer.TYPE, 0); result.put(Long.TYPE, 0L); + result.put(Float.TYPE, 0f); + result.put(Double.TYPE, 0.0); + return result; + } + + private static Map<Class<?>, Object> createDefaultOKTypeValueMap() + { + final Map<Class<?>, Object> result = new HashMap<Class<?>, Object>(); + result.put(Void.TYPE, Void.TYPE.cast(null)); + result.put(Boolean.TYPE, true); + result.put(Byte.TYPE, (byte) 1); + result.put(Short.TYPE, (short) 1); + result.put(Integer.TYPE, 1); + result.put(Long.TYPE, 1L); + result.put(Float.TYPE, 1f); + result.put(Double.TYPE, 1.0); return result; } @@ -227,42 +253,72 @@ public class MonitoringProxy<T> private ExecutionResult<Object> retryingExecuteInThread(final Object myProxy, final Method method, final Object[] args) { - int counter = 0; - ExecutionResult<Object> result = null; - boolean willRetry; - do + final Callable<ExecutionResult<Object>> callable = + new Callable<ExecutionResult<Object>>() + { + @Override + public ExecutionResult<Object> call() throws Exception + { + int counter = 0; + ExecutionResult<Object> result = null; + boolean willRetry; + do + { + result = executeInThread(myProxy, method, args); + if (result.getStatus() == ExecutionStatus.COMPLETE + || result.getStatus() == ExecutionStatus.INTERRUPTED + || exceptionStatusUnsuitableForRetry(result)) + { + willRetry = false; + } else + { + willRetry = + (counter++ < timingParameters + .getMaxRetriesOnFailure()); + } + if (invocationLoggerOrNull != null) + { + invocationLoggerOrNull.log(method, result, willRetry); + } + if (willRetry + && timingParameters + .getIntervalToWaitAfterFailureMillis() > 0) + { + try + { + Thread.sleep(timingParameters + .getIntervalToWaitAfterFailureMillis()); + } catch (InterruptedException ex) + { + result = ExecutionResult.createInterrupted(); + if (invocationLoggerOrNull != null) + { + invocationLoggerOrNull.log(method, result, false); + } + return result; + } + } + } while (willRetry); + return result; + } + }; + try { - result = executeInThread(myProxy, method, args); - if (result.getStatus() == ExecutionStatus.COMPLETE - || result.getStatus() == ExecutionStatus.INTERRUPTED - || exceptionStatusUnsuitableForRetry(result)) + if (asyncMethodSet.contains(method)) { - willRetry = false; + final Object returnValue = + asyncOkMethodValueMap.containsKey(method) ? asyncOkMethodValueMap + .get(method) : asyncOkTypeValueMap.get(method.getReturnType()); + executorService.submit(callable); + return ExecutionResult.create(returnValue); } else { - willRetry = (counter++ < timingParameters.getMaxRetriesOnFailure()); - } - if (invocationLoggerOrNull != null) - { - invocationLoggerOrNull.log(method, result, willRetry); - } - if (willRetry && timingParameters.getIntervalToWaitAfterFailureMillis() > 0) - { - try - { - Thread.sleep(timingParameters.getIntervalToWaitAfterFailureMillis()); - } catch (InterruptedException ex) - { - result = ExecutionResult.createInterrupted(); - if (invocationLoggerOrNull != null) - { - invocationLoggerOrNull.log(method, result, false); - } - return result; - } + return callable.call(); } - } while (willRetry); - return result; + } catch (Exception ex) + { + throw CheckedExceptionTunnel.wrapIfNecessary(ex); + } } private boolean exceptionStatusUnsuitableForRetry(ExecutionResult<Object> result) @@ -349,6 +405,12 @@ public class MonitoringProxy<T> return describe(method); } } + + @Override + public LogLevel getLogLevelForSuccess() + { + return logLevelForSuccessfulCalls; + } }; final ExecutionResult<Object> result = ConcurrencyUtilities.getResult(future, timingParameters.getTimeoutMillis(), @@ -387,7 +449,11 @@ public class MonitoringProxy<T> assert interfaceClass.isInterface(); this.errorTypeValueMap = createDefaultErrorTypeValueMap(); + this.asyncOkTypeValueMap = createDefaultOKTypeValueMap(); this.errorMethodValueMap = new HashMap<Method, Object>(); + this.asyncOkMethodValueMap = new HashMap<Method, Object>(); + this.asyncMethodSet = new HashSet<Method>(); + this.logLevelForSuccessfulCalls = LogLevel.OFF; this.exceptionClassesSuitableForRetrying = new HashSet<Class<? extends Exception>>(); this.timingParameters = TimingParameters.getNoTimeoutNoRetriesParameters(); this.delegate = new DelegatingInvocationHandler<T>(objectToProxyFor); @@ -610,7 +676,7 @@ public class MonitoringProxy<T> this.loggerOrNull = newLogger; return this; } - + /** * Set the <var>newExecutorService</var> to use calls to this monitoring proxy. * <p> @@ -658,6 +724,45 @@ public class MonitoringProxy<T> return this; } + /** + * The monitoring proxy can call some methods in a simple asynchronous way. This method is to + * state that the given <var>method</var> should be called asynchronously. + * <p> + * <i>Note that asynchronously called methods cannot be monitored for success but will return a + * value signing "OK" to the caller.<i> + */ + public MonitoringProxy<T> callAsynchronously(Method method) + { + asyncMethodSet.add(method); + return this; + } + + /** + * Sets a return <var>value</var> that signals "OK" for the type <var>clazz</var>. + * <p> + * <i>A value set by this method is only relevant if the proxy is configured to run a method + * asynchronously.</i> + */ + public <V> MonitoringProxy<T> asyncOkTypeValueMapping(Class<V> clazz, V value) + { + asyncOkTypeValueMap.put(clazz, value); + return this; + } + + /** + * Sets a return <var>value</var> that signals "OK" for the given <var>method</var>. This + * <var>value</var> takes precedence over the ok type mapping for methods with the same return + * type. + * <p> + * <i>A value set by this method is only relevant if the proxy is configured to run a method + * asynchronously.</i> + */ + public MonitoringProxy<T> asyncOkMethodValueMapping(Method method, Object value) + { + asyncOkMethodValueMap.put(method, value); + return this; + } + /** * Add an {@link Exception} class that is suitable for retrying the operation. */ @@ -677,6 +782,17 @@ public class MonitoringProxy<T> return this; } + /** + * Sets a log level for successful calls to the proxy. + * <p> + * Default: {@link LogLevel#OFF}. + */ + public MonitoringProxy<T> logLevelForSuccessfulCalls(LogLevel newLogLevelForSuccessfulCalls) + { + this.logLevelForSuccessfulCalls = newLogLevelForSuccessfulCalls; + return this; + } + /** * Sets a sensor for detecting activity during a monitored method call. Activity on this sensor * can prevent the monitored method invocation from timing out. diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/concurrent/MonitoringProxyTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/concurrent/MonitoringProxyTest.java index 931b3987886c536bb2a76cbac1435160f2767ddd..b448812726d99fd5e187cfcb584ef39e7c0f36f7 100644 --- a/common/sourceTest/java/ch/systemsx/cisd/common/concurrent/MonitoringProxyTest.java +++ b/common/sourceTest/java/ch/systemsx/cisd/common/concurrent/MonitoringProxyTest.java @@ -42,8 +42,10 @@ import ch.systemsx.cisd.base.namedthread.NamingThreadPoolExecutor; import ch.systemsx.cisd.base.tests.Retry50; import ch.systemsx.cisd.common.TimingParameters; import ch.systemsx.cisd.common.concurrent.MonitoringProxy.IMonitorCommunicator; +import ch.systemsx.cisd.common.logging.AssertingLogger; import ch.systemsx.cisd.common.logging.ConsoleLogger; import ch.systemsx.cisd.common.logging.ISimpleLogger; +import ch.systemsx.cisd.common.logging.LogLevel; /** * Test cases for the {@link MonitoringProxy}. @@ -74,10 +76,14 @@ public class MonitoringProxyTest private ITest exceptionThrowingProxy; + private AssertingLogger asyncLogger; + + private ITest asynchronouslyExecutingProxy; + private ITest retryingOnceExceptionThrowingProxy; private ITest retryingTwiceExceptionThrowingProxy; - + private ITest nonDefaultExecutorServiceProxy; private static class SignalException extends RuntimeException @@ -99,12 +105,14 @@ public class MonitoringProxyTest { void idle(boolean hang); + void idle(long[] idleTimes); + void busyUpdatingActivity(); void busyUpdatingActivity(IMonitorCommunicator communicator); String getString(boolean hang); - + String getThreadName(); boolean getBoolean(boolean hang); @@ -139,9 +147,12 @@ public class MonitoringProxyTest { private final IActivityObserver observer; - TestImpl(IActivityObserver observer) + private final boolean checkThreadName; + + TestImpl(IActivityObserver observer, boolean checkThreadName) { this.observer = observer; + this.checkThreadName = checkThreadName; } private void hang(boolean hang) @@ -149,14 +160,24 @@ public class MonitoringProxyTest if (hang) { threadToStop = Thread.currentThread(); - while (true) + try + { + while (true) + { + } + } finally { + threadToStop = null; } } } private void checkThreadName() { + if (checkThreadName == false) + { + return; + } final String name = Thread.currentThread().getName(); assertTrue(name, THREAD_NAME_PATTERN.matcher(name).matches()); } @@ -168,16 +189,31 @@ public class MonitoringProxyTest hang(hang); } + int invocationCount4 = 0; + + @Override + public void idle(long[] idleTimes) + { + checkThreadName(); + ConcurrencyUtilities.sleep(idleTimes[invocationCount4++]); + } + @Override public void busyUpdatingActivity() { checkThreadName(); threadToStop = Thread.currentThread(); - final long timeToHangMillis = (long) (TIMEOUT_MILLIS * 1.5); - final long start = System.currentTimeMillis(); - while (System.currentTimeMillis() - start < timeToHangMillis) + try + { + final long timeToHangMillis = (long) (TIMEOUT_MILLIS * 1.5); + final long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < timeToHangMillis) + { + observer.update(); + } + } finally { - observer.update(); + threadToStop = null; } } @@ -186,15 +222,21 @@ public class MonitoringProxyTest { checkThreadName(); threadToStop = Thread.currentThread(); - final long timeToHangMillis = (long) (TIMEOUT_MILLIS * 1.5); - final long start = System.currentTimeMillis(); - while (System.currentTimeMillis() - start < timeToHangMillis) + try { - if (communicator.isCancelled()) + final long timeToHangMillis = (long) (TIMEOUT_MILLIS * 1.5); + final long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < timeToHangMillis) { - return; + if (communicator.isCancelled()) + { + return; + } + communicator.update(); } - communicator.update(); + } finally + { + threadToStop = null; } } @@ -320,7 +362,7 @@ public class MonitoringProxyTest observerSensor = new RecordingActivityObserverSensor(); defaultReturningProxy = MonitoringProxy - .create(ITest.class, new TestImpl(observerSensor)) + .create(ITest.class, new TestImpl(observerSensor, true)) .timing(TimingParameters.createNoRetries(TIMEOUT_MILLIS)) .errorValueOnTimeout() .name(THREAD_NAME) @@ -333,31 +375,48 @@ public class MonitoringProxyTest new NamingThreadPoolExecutor("My Special Monitoring Proxy").corePoolSize(1) .daemonize(); - nonDefaultExecutorServiceProxy = + nonDefaultExecutorServiceProxy = MonitoringProxy - .create(ITest.class, new TestImpl(observerSensor)) - .timing(TimingParameters.createNoRetries(TIMEOUT_MILLIS)) - .errorValueOnTimeout() - .name(THREAD_NAME) - .errorTypeValueMapping(Status.class, Status.UUUPS) - .errorMethodValueMapping( - ITest.class.getMethod("getSpecialStatus", new Class<?>[] - { Boolean.TYPE }), Status.SPECIAL_UUUPS).sensor(observerSensor) - .errorLog(logger) - .executorService(executorService) - .get(); + .create(ITest.class, new TestImpl(observerSensor, true)) + .timing(TimingParameters.createNoRetries(TIMEOUT_MILLIS)) + .errorValueOnTimeout() + .name(THREAD_NAME) + .errorTypeValueMapping(Status.class, Status.UUUPS) + .errorMethodValueMapping( + ITest.class.getMethod("getSpecialStatus", new Class<?>[] + { Boolean.TYPE }), Status.SPECIAL_UUUPS).sensor(observerSensor) + .errorLog(logger) + .executorService(executorService) + .get(); exceptionThrowingProxy = - MonitoringProxy.create(ITest.class, new TestImpl(observerSensor)) + MonitoringProxy.create(ITest.class, new TestImpl(observerSensor, true)) .timing(TimingParameters.createNoRetries(TIMEOUT_MILLIS)).name(THREAD_NAME) - .sensor(observerSensor).errorLog(logger).get(); + .sensor(observerSensor).errorLog(logger) + .get(); + asyncLogger = new AssertingLogger(); + asynchronouslyExecutingProxy = + MonitoringProxy.create(ITest.class, new TestImpl(observerSensor, false)) + .name(THREAD_NAME) + .sensor(observerSensor) + .errorLog(asyncLogger) + .logLevelForSuccessfulCalls(LogLevel.INFO) + .timing(TimingParameters.create(TIMEOUT_MILLIS, 1, 0L)) + .exceptionClassSuitableForRetrying(RetryItException.class) + .callAsynchronously(ITest.class.getMethod("idle", new Class<?>[] + { long[].class })) + .callAsynchronously(ITest.class.getMethod("throwSignalException")) + .callAsynchronously(ITest.class.getMethod("worksOnSecondInvocation")) + .callAsynchronously(ITest.class.getMethod("getInteger", new Class<?>[] + { Boolean.TYPE })) + .get(); retryingOnceExceptionThrowingProxy = - MonitoringProxy.create(ITest.class, new TestImpl(observerSensor)) + MonitoringProxy.create(ITest.class, new TestImpl(observerSensor, true)) .timing(TimingParameters.create(TIMEOUT_MILLIS, 1, 0L)).name(THREAD_NAME) .sensor(observerSensor) .exceptionClassSuitableForRetrying(RetryItException.class).errorLog(logger) .get(); retryingTwiceExceptionThrowingProxy = - MonitoringProxy.create(ITest.class, new TestImpl(observerSensor)) + MonitoringProxy.create(ITest.class, new TestImpl(observerSensor, true)) .timing(TimingParameters.create(TIMEOUT_MILLIS, 2, 0L)).name(THREAD_NAME) .sensor(observerSensor) .exceptionClassSuitableForRetrying(RetryItException.class).errorLog(logger) @@ -400,32 +459,120 @@ public class MonitoringProxyTest final String threadName = nonDefaultExecutorServiceProxy.getThreadName(); assertTrue(threadName, MY_SPECIAL_THREAD_NAME_PATTERN.matcher(threadName).matches()); } - + @Test(expectedExceptions = SignalException.class, retryAnalyzer = Retry50.class) public void testThrowExceptionNullReturningPolicy() { defaultReturningProxy.throwSignalException(); } - @Test(expectedExceptions = SignalException.class, retryAnalyzer = Retry50.class) + @Test + public void testWorksOnSecondInvocationOnAsynchronouslyCalledMethod() + { + asyncLogger.reset(); + asynchronouslyExecutingProxy.worksOnSecondInvocation(); + for (int i = 0; i < 10; ++i) + { + if (asyncLogger.getNumberOfRecords() > 1) + { + break; + } + ConcurrencyUtilities.sleep(100L); + } + asyncLogger.assertNumberOfMessage(2); + asyncLogger + .assertEq( + 0, + LogLevel.ERROR, + "Call to method 'ITest.worksOnSecondInvocation()'[Some Flaky Stuff]: exception: <no message> [RetryItException]."); + asyncLogger + .assertEq( + 1, + LogLevel.INFO, + "Call to method 'ITest.worksOnSecondInvocation()'[Some Flaky Stuff]: call returns null."); + } + + @Test(retryAnalyzer = Retry50.class, successPercentage = 2) + public void testGetIntegerAsynchronouslyCalledMethod() + { + asyncLogger.reset(); + assertEquals(1, asynchronouslyExecutingProxy.getInteger(false)); + for (int i = 0; i < 20; ++i) + { + if (asyncLogger.getNumberOfRecords() > 0) + { + break; + } + ConcurrencyUtilities.sleep(100L); + } + asyncLogger.assertNumberOfMessage(1); + asyncLogger.assertEq(0, LogLevel.INFO, + "Call to method 'ITest.getInteger(boolean)'[Some Flaky Stuff]: call returns 17."); + } + + @Test(groups = "slow", retryAnalyzer = Retry50.class, successPercentage = 2) + public void testTimeoutOnAsynchronouslyCalledMethod() + { + asyncLogger.reset(); + final long start = System.currentTimeMillis(); + asynchronouslyExecutingProxy.idle(new long[] + { 3000L, 0L }); + final long stop = System.currentTimeMillis(); + assertTrue(Long.toString(stop - start), (stop - start) < 20); + for (int i = 0; i < 30; ++i) + { + if (asyncLogger.getNumberOfRecords() > 1) + { + break; + } + ConcurrencyUtilities.sleep(100L); + } + asyncLogger.assertNumberOfMessage(2); + asyncLogger + .assertEq(0, LogLevel.ERROR, + "Call to method 'ITest.idle(long[])'[Some Flaky Stuff]: timeout of 2.00 s exceeded, cancelled."); + asyncLogger.assertEq(1, LogLevel.INFO, + "Call to method 'ITest.idle(long[])'[Some Flaky Stuff]: call returns null."); + } + + @Test(groups = "slow", retryAnalyzer = Retry50.class, successPercentage = 2) + public void testThrowExceptionOnAsynchronouslyCalledMethod() + { + asyncLogger.reset(); + asynchronouslyExecutingProxy.throwSignalException(); + for (int i = 0; i < 30; ++i) + { + if (asyncLogger.getNumberOfRecords() > 0) + { + break; + } + ConcurrencyUtilities.sleep(100L); + } + asyncLogger.assertNumberOfMessage(1); + asyncLogger.assertEq(0, LogLevel.ERROR, + "Call to method 'ITest.throwSignalException()'[Some Flaky Stuff]:" + + " exception: <no message> [SignalException]."); + } + + @Test(expectedExceptions = SignalException.class, retryAnalyzer = Retry50.class, successPercentage = 2) public void testThrowExceptionExceptionThrowsPolicy() { exceptionThrowingProxy.throwSignalException(); } - @Test(groups = "slow", expectedExceptions = TimeoutExceptionUnchecked.class, retryAnalyzer = Retry50.class) + @Test(groups = "slow", expectedExceptions = TimeoutExceptionUnchecked.class, retryAnalyzer = Retry50.class, successPercentage = 2) public void testVoidTimeoutWithException() { exceptionThrowingProxy.idle(true); } - @Test(groups = "slow", retryAnalyzer = Retry50.class) + @Test(groups = "slow", retryAnalyzer = Retry50.class, successPercentage = 2) public void testNoTimeoutDueToSensorUpdate() { exceptionThrowingProxy.busyUpdatingActivity(); } - @Test(groups = "slow", retryAnalyzer = Retry50.class) + @Test(groups = "slow", retryAnalyzer = Retry50.class, successPercentage = 2) public void testNoTimeoutDueToCommunicatorUpdate() { exceptionThrowingProxy.busyUpdatingActivity(null); @@ -449,65 +596,65 @@ public class MonitoringProxyTest assertNull(defaultReturningProxy.getString(true)); } - @Test(groups = "slow", expectedExceptions = TimeoutExceptionUnchecked.class, retryAnalyzer = Retry50.class) + @Test(groups = "slow", expectedExceptions = TimeoutExceptionUnchecked.class, retryAnalyzer = Retry50.class, successPercentage = 2) public void testGetStringTimeoutWithException() { exceptionThrowingProxy.getString(true); } - @Test(retryAnalyzer = Retry50.class) + @Test(retryAnalyzer = Retry50.class, successPercentage = 2) public void testGetIntNullReturningPolicy() { assertEquals(THE_INTEGER, defaultReturningProxy.getInteger(false)); } - @Test(retryAnalyzer = Retry50.class) + @Test(retryAnalyzer = Retry50.class, successPercentage = 2) public void testGetIntExceptionThrowingPolicy() { assertEquals(THE_INTEGER, exceptionThrowingProxy.getInteger(false)); } - @Test(groups = "slow", retryAnalyzer = Retry50.class) + @Test(groups = "slow", retryAnalyzer = Retry50.class, successPercentage = 2) public void testGetBoolTimeoutReturnsDefault() { assertEquals(false, defaultReturningProxy.getBoolean(true)); } - @Test(retryAnalyzer = Retry50.class) + @Test(retryAnalyzer = Retry50.class, successPercentage = 2) public void testGetStatus() { assertEquals(THE_STATUS, defaultReturningProxy.getStatus(false)); } - @Test(groups = "slow", retryAnalyzer = Retry50.class) + @Test(groups = "slow", retryAnalyzer = Retry50.class, successPercentage = 2) public void testGetStatusTimeoutReturnsDefault() { assertEquals(Status.UUUPS, defaultReturningProxy.getStatus(true)); } - @Test(groups = "slow", retryAnalyzer = Retry50.class) + @Test(groups = "slow", retryAnalyzer = Retry50.class, successPercentage = 2) public void testGetSpecialStatusTimeoutReturnsMethodDefault() { assertEquals(Status.SPECIAL_UUUPS, defaultReturningProxy.getSpecialStatus(true)); } - @Test(groups = "slow", retryAnalyzer = Retry50.class) + @Test(groups = "slow", retryAnalyzer = Retry50.class, successPercentage = 2) public void testGetIntTimeoutReturnsDefault() { assertEquals(0, defaultReturningProxy.getInteger(true)); } - @Test(groups = "slow", expectedExceptions = TimeoutExceptionUnchecked.class, retryAnalyzer = Retry50.class) + @Test(groups = "slow", expectedExceptions = TimeoutExceptionUnchecked.class, retryAnalyzer = Retry50.class, successPercentage = 2) public void testGetIntTimeoutWithException() { exceptionThrowingProxy.getInteger(true); } - @Test(groups = "slow", expectedExceptions = InterruptedExceptionUnchecked.class, retryAnalyzer = Retry50.class) + @Test(groups = "slow", expectedExceptions = InterruptedExceptionUnchecked.class, retryAnalyzer = Retry50.class, successPercentage = 2) public void testInterruptTheUninterruptableThrowsException() { final ITest proxy = - MonitoringProxy.create(ITest.class, new TestImpl(observerSensor)) + MonitoringProxy.create(ITest.class, new TestImpl(observerSensor, true)) .timing(TimingParameters.create(1000L)).name(THREAD_NAME).get(); final Thread currentThread = Thread.currentThread(); final Timer timer = new Timer(); @@ -525,12 +672,12 @@ public class MonitoringProxyTest timer.cancel(); } - @Test(groups = "slow", retryAnalyzer = Retry50.class) + @Test(groups = "slow", retryAnalyzer = Retry50.class, successPercentage = 2) public void testInterruptTheUninterruptableReturnsDefaultValue() { final String defaultReturnValue = "That's the default return value."; final ITest proxy = - MonitoringProxy.create(ITest.class, new TestImpl(observerSensor)) + MonitoringProxy.create(ITest.class, new TestImpl(observerSensor, true)) .timing(TimingParameters.create(1000L)).name(THREAD_NAME) .errorValueOnInterrupt() .errorTypeValueMapping(String.class, defaultReturnValue).get(); @@ -556,14 +703,14 @@ public class MonitoringProxyTest exceptionThrowingProxy.worksOnSecondInvocation(); } - @Test(retryAnalyzer = Retry50.class) + @Test(retryAnalyzer = Retry50.class, successPercentage = 2) public void testRetryOnceFailOnce() { retryingOnceExceptionThrowingProxy.worksOnSecondInvocation(); } @Test(groups = - { "slow" }, retryAnalyzer = Retry50.class) + { "slow" }, retryAnalyzer = Retry50.class, successPercentage = 2) public void testRetryOnceFailOnceWithCommunicator() { retryingOnceExceptionThrowingProxy.resetInvocationsCancelled(); @@ -579,19 +726,19 @@ public class MonitoringProxyTest retryingOnceExceptionThrowingProxy.worksOnThirdInvocation(); } - @Test(groups = "slow", retryAnalyzer = Retry50.class) + @Test(groups = "slow", retryAnalyzer = Retry50.class, successPercentage = 2) public void testRetryTwiceFailTwice() { retryingTwiceExceptionThrowingProxy.worksOnThirdInvocation(); } - @Test(retryAnalyzer = Retry50.class) + @Test(retryAnalyzer = Retry50.class, successPercentage = 2) public void testInvocationLog() { final List<ExecutionResult<Object>> results = new ArrayList<ExecutionResult<Object>>(); final List<Boolean> retryFlags = new ArrayList<Boolean>(); final ITest proxy = - MonitoringProxy.create(ITest.class, new TestImpl(observerSensor)) + MonitoringProxy.create(ITest.class, new TestImpl(observerSensor, true)) .timing(TimingParameters.create(TIMEOUT_MILLIS, 2, 0L)).name(THREAD_NAME) .sensor(observerSensor) .exceptionClassSuitableForRetrying(RetryItException.class) diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/logging/AssertingLogger.java b/common/sourceTest/java/ch/systemsx/cisd/common/logging/AssertingLogger.java index 3896220960a29691c253df1a62b2a33acea6596e..525227879dba85dc81d244507eb2ea1c97c2a3ea 100644 --- a/common/sourceTest/java/ch/systemsx/cisd/common/logging/AssertingLogger.java +++ b/common/sourceTest/java/ch/systemsx/cisd/common/logging/AssertingLogger.java @@ -20,6 +20,7 @@ import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertTrue; +import java.io.PrintStream; import java.util.ArrayList; import java.util.List; @@ -44,6 +45,11 @@ public class AssertingLogger implements ISimpleLogger records.add(new LogRecord(level, message, throwableOrNull)); } + public void reset() + { + records.clear(); + } + public void assertNumberOfMessage(final int expectedNumberOfMessages) { assertEquals(expectedNumberOfMessages, records.size()); @@ -79,6 +85,19 @@ public class AssertingLogger implements ISimpleLogger assertTrue(assertError, message.matches(throwableMessagePattern)); } + public int getNumberOfRecords() + { + return records.size(); + } + + public void print(PrintStream out) + { + for (LogRecord record : records) + { + out.println(record); + } + } + private static class LogRecord { final LogLevel level; @@ -93,5 +112,19 @@ public class AssertingLogger implements ISimpleLogger this.message = message; this.throwableOrNull = throwableOrNull; } + + @Override + public String toString() + { + if (throwableOrNull != null) + { + return "LogRecord [level=" + level + ", message=" + message + ", throwable=" + + throwableOrNull + "]"; + + } else + { + return "LogRecord [level=" + level + ", message=" + message + "]"; + } + } } } \ No newline at end of file