diff --git a/jrugged-aspects/pom.xml b/jrugged-aspects/pom.xml index 73de31dd..56b33b17 100644 --- a/jrugged-aspects/pom.xml +++ b/jrugged-aspects/pom.xml @@ -31,12 +31,12 @@ org.aspectj aspectjrt - 1.7.2 + 1.9.2 org.aspectj aspectjweaver - 1.7.2 + 1.9.2 org.fishwife diff --git a/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/CircuitBreaker.java b/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/CircuitBreaker.java index e2f3333a..559fa7fd 100644 --- a/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/CircuitBreaker.java +++ b/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/CircuitBreaker.java @@ -27,43 +27,45 @@ @Target(ElementType.METHOD) public @interface CircuitBreaker { - /** - * Name of the circuit. Each annotation with a shared value shares - * the same CircuitBreaker. - * @return the value - */ - String name(); + /** + * Name of the circuit. Each annotation with a shared value shares the same + * CircuitBreaker. + * + * @return the value + */ + String name(); - /** - * Exception types that the {@link - * org.fishwife.jrugged.CircuitBreaker} will ignore (pass through - * transparently without tripping). - * @return the Exception types. - */ - Class[] ignore() default {}; + /** + * Exception types that the {@link org.fishwife.jrugged.CircuitBreaker} will + * ignore (pass through transparently without tripping). + * + * @return the Exception types. + */ + Class[] ignore() default {}; - /** - * Specifies the length of the measurement window for failure - * tolerances in milliseconds. i.e. if limit - * failures occur within windowMillis milliseconds, - * the breaker will trip. - * @return the length of the measurement window. - */ - long windowMillis() default -1; + /** + * Specifies the length of the measurement window for failure tolerances in + * milliseconds. i.e. if limit failures occur within + * windowMillis milliseconds, the breaker will trip. + * + * @return the length of the measurement window. + */ + long windowMillis() default -1; - /** - * Specifies the number of failures that must occur within a - * configured time window in order to trip the circuit breaker. - * @return the number of failures. - */ - int limit() default -1; + /** + * Specifies the number of failures that must occur within a configured time + * window in order to trip the circuit breaker. + * + * @return the number of failures. + */ + int limit() default -1; - - /** - * Amount of time in milliseconds after tripping after which the - * {@link org.fishwife.jrugged.CircuitBreaker} is reset and will - * allow a test request through. - * @return the amount of time in milliseconds. - */ - long resetMillis() default -1; + /** + * Amount of time in milliseconds after tripping after which the + * {@link org.fishwife.jrugged.CircuitBreaker} is reset and will allow a test + * request through. + * + * @return the amount of time in milliseconds. + */ + long resetMillis() default -1; } diff --git a/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/CircuitBreakerAspect.java b/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/CircuitBreakerAspect.java index 63a45c56..67bb6c0a 100644 --- a/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/CircuitBreakerAspect.java +++ b/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/CircuitBreakerAspect.java @@ -33,95 +33,90 @@ @Aspect public class CircuitBreakerAspect { - private static final Logger logger = - LoggerFactory.getLogger(CircuitBreakerAspect.class); - - /** - * Maps names to CircuitBreakers. - */ - private CircuitBreakerFactory circuitBreakerFactory; - - /** Default constructor. */ - public CircuitBreakerAspect() { - circuitBreakerFactory = new CircuitBreakerFactory(); - } - - /** - * Sets the {@link org.fishwife.jrugged.CircuitBreakerFactory} to use when creating new - * {@link org.fishwife.jrugged.CircuitBreaker} instances. - * @param circuitBreakerFactory the {@link org.fishwife.jrugged.CircuitBreakerFactory} to - * use. - */ - public void setCircuitBreakerFactory( - CircuitBreakerFactory circuitBreakerFactory) { - this.circuitBreakerFactory = circuitBreakerFactory; - } - - /** - * Get the {@link org.fishwife.jrugged.CircuitBreakerFactory} that is being used to create - * new {@link org.fishwife.jrugged.CircuitBreaker} instances. - * @return the {@link org.fishwife.jrugged.CircuitBreakerFactory}. - */ - public CircuitBreakerFactory getCircuitBreakerFactory() { - return circuitBreakerFactory; - } - - /** Runs a method call through the configured - * {@link org.fishwife.jrugged.CircuitBreaker}. - * @param pjp a {@link ProceedingJoinPoint} representing an annotated - * method call. - * @param circuitBreakerAnnotation the {@link org.fishwife.jrugged.CircuitBreaker} annotation - * that wrapped the method. - * @throws Throwable if the method invocation itself or the wrapping - * {@link org.fishwife.jrugged.CircuitBreaker} throws one during execution. - * @return The return value from the method call. - */ - @Around("@annotation(circuitBreakerAnnotation)") - public Object monitor(final ProceedingJoinPoint pjp, - CircuitBreaker circuitBreakerAnnotation) throws Throwable { - final String name = circuitBreakerAnnotation.name(); - - org.fishwife.jrugged.CircuitBreaker circuitBreaker = - circuitBreakerFactory.findCircuitBreaker(name); - - if (circuitBreaker == null) { - DefaultFailureInterpreter dfi = - new DefaultFailureInterpreter( - circuitBreakerAnnotation.ignore(), - circuitBreakerAnnotation.limit(), - circuitBreakerAnnotation.windowMillis()); - - CircuitBreakerConfig config = new CircuitBreakerConfig( - circuitBreakerAnnotation.resetMillis(), dfi); - - circuitBreaker = - circuitBreakerFactory.createCircuitBreaker(name, config); - } - - if (logger.isDebugEnabled()) { - logger.debug("Have @CircuitBreaker method with breaker name {}, " + - "wrapping call on method {} of target object {} with status {}", - new Object[]{ - name, - pjp.getSignature().getName(), - pjp.getTarget(), - circuitBreaker.getStatus()}); - } - - return circuitBreaker.invoke(new Callable() { - public Object call() throws Exception { - try { - return pjp.proceed(); - } catch (Throwable e) { - if (e instanceof Exception) { - throw (Exception) e; - } else if (e instanceof Error) { - throw (Error) e; - } else { - throw new RuntimeException(e); - } - } - } - }); - } + private static final Logger logger = LoggerFactory.getLogger(CircuitBreakerAspect.class); + + /** + * Maps names to CircuitBreakers. + */ + private CircuitBreakerFactory circuitBreakerFactory; + + /** Default constructor. */ + public CircuitBreakerAspect() { + circuitBreakerFactory = new CircuitBreakerFactory(); + } + + /** + * Sets the {@link org.fishwife.jrugged.CircuitBreakerFactory} to use when + * creating new {@link org.fishwife.jrugged.CircuitBreaker} instances. + * + * @param circuitBreakerFactory the + * {@link org.fishwife.jrugged.CircuitBreakerFactory} + * to use. + */ + public void setCircuitBreakerFactory(CircuitBreakerFactory circuitBreakerFactory) { + this.circuitBreakerFactory = circuitBreakerFactory; + } + + /** + * Get the {@link org.fishwife.jrugged.CircuitBreakerFactory} that is being used + * to create new {@link org.fishwife.jrugged.CircuitBreaker} instances. + * + * @return the {@link org.fishwife.jrugged.CircuitBreakerFactory}. + */ + public CircuitBreakerFactory getCircuitBreakerFactory() { + return circuitBreakerFactory; + } + + /** + * Runs a method call through the configured + * {@link org.fishwife.jrugged.CircuitBreaker}. + * + * @param pjp a {@link ProceedingJoinPoint} representing an + * annotated method call. + * @param circuitBreakerAnnotation the + * {@link org.fishwife.jrugged.CircuitBreaker} + * annotation that wrapped the method. + * @throws Throwable if the method invocation itself or the wrapping + * {@link org.fishwife.jrugged.CircuitBreaker} throws one + * during execution. + * @return The return value from the method call. + */ + @Around("@annotation(circuitBreakerAnnotation)") + public Object monitor(final ProceedingJoinPoint pjp, CircuitBreaker circuitBreakerAnnotation) throws Throwable { + final String name = circuitBreakerAnnotation.name(); + + org.fishwife.jrugged.CircuitBreaker circuitBreaker = circuitBreakerFactory.findCircuitBreaker(name); + + if (circuitBreaker == null) { + DefaultFailureInterpreter dfi = new DefaultFailureInterpreter(circuitBreakerAnnotation.ignore(), + circuitBreakerAnnotation.limit(), circuitBreakerAnnotation.windowMillis()); + + CircuitBreakerConfig config = new CircuitBreakerConfig(circuitBreakerAnnotation.resetMillis(), dfi); + + circuitBreaker = circuitBreakerFactory.createCircuitBreaker(name, config); + } + + if (logger.isDebugEnabled()) { + logger.debug( + "Have @CircuitBreaker method with breaker name {}, " + + "wrapping call on method {} of target object {} with status {}", + new Object[] { name, pjp.getSignature().getName(), pjp.getTarget(), circuitBreaker.getStatus() }); + } + + return circuitBreaker.invoke(new Callable() { + public Object call() throws Exception { + try { + return pjp.proceed(); + } catch (Throwable e) { + if (e instanceof Exception) { + throw (Exception) e; + } else if (e instanceof Error) { + throw (Error) e; + } else { + throw new RuntimeException(e); + } + } + } + }); + } } diff --git a/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/PerformanceMonitor.java b/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/PerformanceMonitor.java index 430c2a71..ac30ad7a 100644 --- a/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/PerformanceMonitor.java +++ b/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/PerformanceMonitor.java @@ -20,21 +20,21 @@ import java.lang.annotation.Target; /** - * Annotation that is used to indicate that a method should be - * wrapped by a {@link org.fishwife.jrugged.PerformanceMonitor}. The value - * passed to the annotation serves as a key for a PerformanceMonitor instance. - * You may have a unique PerformanceMonitor for individual methods by using - * unique key names in the PerformanceMonitor annotation for the method, or you - * may share a PerformanceMonitor across classes and methods by using the - * same key value for many PerformanceMonitor annotations. + * Annotation that is used to indicate that a method should be wrapped by a + * {@link org.fishwife.jrugged.PerformanceMonitor}. The value passed to the + * annotation serves as a key for a PerformanceMonitor instance. You may have a + * unique PerformanceMonitor for individual methods by using unique key names in + * the PerformanceMonitor annotation for the method, or you may share a + * PerformanceMonitor across classes and methods by using the same key value for + * many PerformanceMonitor annotations. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface PerformanceMonitor { - /** - * The value for the configured - * {@link org.fishwife.jrugged.PerformanceMonitor}. - * @return the value. - */ - String value(); + /** + * The value for the configured {@link org.fishwife.jrugged.PerformanceMonitor}. + * + * @return the value. + */ + String value(); } diff --git a/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/PerformanceMonitorAspect.java b/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/PerformanceMonitorAspect.java index a40c3608..05a3040b 100644 --- a/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/PerformanceMonitorAspect.java +++ b/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/PerformanceMonitorAspect.java @@ -25,98 +25,94 @@ import java.util.concurrent.Callable; /** - * Aspect that wraps methods annotated with {@link org.fishwife.jrugged.PerformanceMonitor} with a - * {@link org.fishwife.jrugged.PerformanceMonitor}. The value given to the + * Aspect that wraps methods annotated with + * {@link org.fishwife.jrugged.PerformanceMonitor} with a + * {@link org.fishwife.jrugged.PerformanceMonitor}. The value given to the * PerformanceMonitor annotation serves as a key for a PerformanceMonitor - * instance. Thus it is possible to have a PerformanceMonitor per method. + * instance. Thus it is possible to have a PerformanceMonitor per method. * Alternatively, PerformanceMonitor can be shared across methods and classes by * using the same value for the monitor key. */ @Aspect public class PerformanceMonitorAspect { - private static final Logger logger = - LoggerFactory.getLogger(PerformanceMonitorAspect.class); - - private volatile PerformanceMonitorFactory performanceMonitorFactory; - - /** Default constructor. */ - public PerformanceMonitorAspect() { - performanceMonitorFactory = new PerformanceMonitorFactory(); - } - - /** - * Sets the {@link org.fishwife.jrugged.PerformanceMonitorFactory} to use when creating new - * {@link org.fishwife.jrugged.PerformanceMonitor} instances. - * @param performanceMonitorFactory the {@link org.fishwife.jrugged.PerformanceMonitorFactory} to - * use. - */ - public void setPerformanceMonitorFactory( - PerformanceMonitorFactory performanceMonitorFactory) { - this.performanceMonitorFactory = performanceMonitorFactory; - } - - - /** - * Get the {@link org.fishwife.jrugged.PerformanceMonitorFactory} that is being used to create - * new {@link org.fishwife.jrugged.PerformanceMonitor} instances. - * @return the {@link org.fishwife.jrugged.PerformanceMonitorFactory}. - */ - public PerformanceMonitorFactory getPerformanceMonitorFactory() { - return performanceMonitorFactory; - } - - /** - * Wraps a method annotated with the {@link org.fishwife.jrugged.PerformanceMonitor} annotation - * with a {@link org.fishwife.jrugged.PerformanceMonitor}. - * - * @param pjp Represents the method that is being executed. - * @param performanceMonitorAnnotation The PerformanceMonitor annotation - * associated with the method being execute. - * @return Value returned by the method that is being wrapped. - * @throws Throwable Whatever the wrapped method throws will be thrown by - * this method. - */ - @Around("@annotation(performanceMonitorAnnotation)") - public Object monitor(final ProceedingJoinPoint pjp, - PerformanceMonitor performanceMonitorAnnotation) throws Throwable { - String monitorName = performanceMonitorAnnotation.value(); - - if (logger.isDebugEnabled()) { - logger.debug("Have @PerformanceMonitor method with monitor name {}, " + - "wrapping call on method {} of target object {}", - new Object[]{ - monitorName, - pjp.getSignature().getName(), - pjp.getTarget()}); - } - - org.fishwife.jrugged.PerformanceMonitor performanceMonitor = - performanceMonitorFactory.findPerformanceMonitor( - monitorName); - - if (performanceMonitor == null) { - performanceMonitor = - performanceMonitorFactory.createPerformanceMonitor( - monitorName); - } - - return performanceMonitor.invoke( - new Callable() { - public Object call() throws Exception { - Object retval; - try { - retval = pjp.proceed(); - } catch (Throwable e) { - if (e instanceof Exception) { - throw (Exception) e; - } else { - throw (Error) e; - } - } - return retval; - } - } - ); - } + private static final Logger logger = LoggerFactory.getLogger(PerformanceMonitorAspect.class); + + private volatile PerformanceMonitorFactory performanceMonitorFactory; + + /** Default constructor. */ + public PerformanceMonitorAspect() { + performanceMonitorFactory = new PerformanceMonitorFactory(); + } + + /** + * Sets the {@link org.fishwife.jrugged.PerformanceMonitorFactory} to use when + * creating new {@link org.fishwife.jrugged.PerformanceMonitor} instances. + * + * @param performanceMonitorFactory the + * {@link org.fishwife.jrugged.PerformanceMonitorFactory} + * to use. + */ + public void setPerformanceMonitorFactory(PerformanceMonitorFactory performanceMonitorFactory) { + this.performanceMonitorFactory = performanceMonitorFactory; + } + + /** + * Get the {@link org.fishwife.jrugged.PerformanceMonitorFactory} that is being + * used to create new {@link org.fishwife.jrugged.PerformanceMonitor} instances. + * + * @return the {@link org.fishwife.jrugged.PerformanceMonitorFactory}. + */ + public PerformanceMonitorFactory getPerformanceMonitorFactory() { + return performanceMonitorFactory; + } + + /** + * Wraps a method annotated with the + * {@link org.fishwife.jrugged.PerformanceMonitor} annotation with a + * {@link org.fishwife.jrugged.PerformanceMonitor}. + * + * @param pjp Represents the method that is being + * executed. + * @param performanceMonitorAnnotation The PerformanceMonitor annotation + * associated with the method being execute. + * @return Value returned by the method that is being wrapped. + * @throws Throwable Whatever the wrapped method throws will be thrown by this + * method. + */ + @Around("@annotation(performanceMonitorAnnotation)") + public Object monitor(final ProceedingJoinPoint pjp, PerformanceMonitor performanceMonitorAnnotation) + throws Throwable { + String monitorName = performanceMonitorAnnotation.value(); + + if (logger.isDebugEnabled()) { + logger.debug( + "Have @PerformanceMonitor method with monitor name {}, " + + "wrapping call on method {} of target object {}", + new Object[] { monitorName, pjp.getSignature().getName(), pjp.getTarget() }); + } + + org.fishwife.jrugged.PerformanceMonitor performanceMonitor = performanceMonitorFactory + .findPerformanceMonitor(monitorName); + + if (performanceMonitor == null) { + performanceMonitor = performanceMonitorFactory.createPerformanceMonitor(monitorName); + } + + return performanceMonitor.invoke(new Callable() { + public Object call() throws Exception { + Object retval; + try { + retval = pjp.proceed(); + } catch (Throwable e) { + if (e instanceof Exception) { + throw (Exception) e; + } else { + throw (Error) e; + } + } + return retval; + } + }); + } } diff --git a/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/Retryable.java b/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/Retryable.java index 7a0cd017..3b79f015 100644 --- a/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/Retryable.java +++ b/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/Retryable.java @@ -27,38 +27,43 @@ @Target(ElementType.METHOD) public @interface Retryable { - /** - * Throwable types that the {@link org.fishwife.jrugged.aspects.RetryableAspect} - * will retry on. An empty list indicates that the method call will be - * retried on ANY Throwable. - * @return the Throwable types. - */ - Class[] retryOn() default {}; + /** + * Throwable types that the {@link org.fishwife.jrugged.aspects.RetryableAspect} + * will retry on. An empty list indicates that the method call will be retried + * on ANY Throwable. + * + * @return the Throwable types. + */ + Class[] retryOn() default {}; - /** - * Specifies the maximum number of tries. - * @return the maximum number of tries. - */ - int maxTries() default 2; + /** + * Specifies the maximum number of tries. + * + * @return the maximum number of tries. + */ + int maxTries() default 2; - /** - * Amount of time in milliseconds between retries. - * @return the amount of time in milliseconds. - */ - int retryDelayMillis() default 0; + /** + * Amount of time in milliseconds between retries. + * + * @return the amount of time in milliseconds. + */ + int retryDelayMillis() default 0; - /** - * Whether the delay should be doubled between tries. The delay will reset - * to the original 'retryDelayMillis' values after a successful call or - * after 'maxTries' is reached. - * @return whether the delay should be doubled between tries. - */ - boolean doubleDelay() default false; + /** + * Whether the delay should be doubled between tries. The delay will reset to + * the original 'retryDelayMillis' values after a successful call or after + * 'maxTries' is reached. + * + * @return whether the delay should be doubled between tries. + */ + boolean doubleDelay() default false; - /** - * Whether the root cause Exception should be thrown, or whether a generic - * Exception("Call failed n times") should be thrown. - * @return if the root cause Exception should be thrown. - */ - boolean throwCauseException() default true; + /** + * Whether the root cause Exception should be thrown, or whether a generic + * Exception("Call failed n times") should be thrown. + * + * @return if the root cause Exception should be thrown. + */ + boolean throwCauseException() default true; } diff --git a/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/RetryableAspect.java b/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/RetryableAspect.java index 71bb7545..44fc8dc2 100644 --- a/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/RetryableAspect.java +++ b/jrugged-aspects/src/main/java/org/fishwife/jrugged/aspects/RetryableAspect.java @@ -24,64 +24,58 @@ import java.util.concurrent.Callable; /** - * Surrounds methods decorated with the {@link org.fishwife.jrugged.aspects.Retryable} annotation - * with logic to retry the method call. + * Surrounds methods decorated with the + * {@link org.fishwife.jrugged.aspects.Retryable} annotation with logic to retry + * the method call. */ @Aspect public class RetryableAspect { - private static final Logger logger = - LoggerFactory.getLogger(RetryableAspect.class); + private static final Logger logger = LoggerFactory.getLogger(RetryableAspect.class); - /** Default constructor. */ - public RetryableAspect() { - } + /** Default constructor. */ + public RetryableAspect() { + } - /** - * Runs a method call with retries. - * @param pjp a {@link ProceedingJoinPoint} representing an annotated - * method call. - * @param retryableAnnotation the {@link org.fishwife.jrugged.aspects.Retryable} - * annotation that wrapped the method. - * @throws Throwable if the method invocation itself throws one during execution. - * @return The return value from the method call. - */ - @Around("@annotation(retryableAnnotation)") - public Object call(final ProceedingJoinPoint pjp, Retryable retryableAnnotation) throws Throwable { - final int maxTries = retryableAnnotation.maxTries(); - final int retryDelayMillies = retryableAnnotation.retryDelayMillis(); - final Class[] retryOn = retryableAnnotation.retryOn(); - final boolean doubleDelay = retryableAnnotation.doubleDelay(); - final boolean throwCauseException = retryableAnnotation.throwCauseException(); + /** + * Runs a method call with retries. + * + * @param pjp a {@link ProceedingJoinPoint} representing an + * annotated method call. + * @param retryableAnnotation the {@link org.fishwife.jrugged.aspects.Retryable} + * annotation that wrapped the method. + * @throws Throwable if the method invocation itself throws one during + * execution. + * @return The return value from the method call. + */ + @Around("@annotation(retryableAnnotation)") + public Object call(final ProceedingJoinPoint pjp, Retryable retryableAnnotation) throws Throwable { + final int maxTries = retryableAnnotation.maxTries(); + final int retryDelayMillies = retryableAnnotation.retryDelayMillis(); + final Class[] retryOn = retryableAnnotation.retryOn(); + final boolean doubleDelay = retryableAnnotation.doubleDelay(); + final boolean throwCauseException = retryableAnnotation.throwCauseException(); - if (logger.isDebugEnabled()) { - logger.debug("Have @Retryable method wrapping call on method {} of target object {}", - new Object[] { - pjp.getSignature().getName(), - pjp.getTarget() - }); - } + if (logger.isDebugEnabled()) { + logger.debug("Have @Retryable method wrapping call on method {} of target object {}", + new Object[] { pjp.getSignature().getName(), pjp.getTarget() }); + } - ServiceRetrier serviceRetrier = - new ServiceRetrier(retryDelayMillies, maxTries, doubleDelay, throwCauseException, retryOn); + ServiceRetrier serviceRetrier = new ServiceRetrier(retryDelayMillies, maxTries, doubleDelay, + throwCauseException, retryOn); - return serviceRetrier.invoke( - new Callable() { - public Object call() throws Exception { - try { - return pjp.proceed(); - } - catch (Exception e) { - throw e; - } - catch (Error e) { - throw e; - } - catch (Throwable t) { - throw new RuntimeException(t); - } - } - } - ); - } + return serviceRetrier.invoke(new Callable() { + public Object call() throws Exception { + try { + return pjp.proceed(); + } catch (Exception e) { + throw e; + } catch (Error e) { + throw e; + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + }); + } } diff --git a/jrugged-aspects/src/test/java/org/fishwife/jrugged/aspects/TestCircuitBreakerAspect.java b/jrugged-aspects/src/test/java/org/fishwife/jrugged/aspects/TestCircuitBreakerAspect.java index 5464a505..4369b21d 100644 --- a/jrugged-aspects/src/test/java/org/fishwife/jrugged/aspects/TestCircuitBreakerAspect.java +++ b/jrugged-aspects/src/test/java/org/fishwife/jrugged/aspects/TestCircuitBreakerAspect.java @@ -31,200 +31,197 @@ public class TestCircuitBreakerAspect { - private CircuitBreakerAspect aspect; - - CircuitBreaker mockAnnotation; - - Signature mockSignature; - - private static final String TEST_CIRCUIT_BREAKER = "TestCircuitBreaker"; - - @Before - public void setUp() { - aspect = new CircuitBreakerAspect(); - - mockAnnotation = createMock(CircuitBreaker.class); - mockSignature = createMock(Signature.class); - - expect(mockSignature.getName()).andReturn("Signature").anyTimes(); - expect(mockAnnotation.name()).andReturn(TEST_CIRCUIT_BREAKER).anyTimes(); - expect(mockAnnotation.limit()).andReturn(5).anyTimes(); - expect(mockAnnotation.resetMillis()).andReturn(30000L).anyTimes(); - expect(mockAnnotation.windowMillis()).andReturn(10000L).anyTimes(); - @SuppressWarnings("unchecked") - Class[] ignores = new Class[0]; - expect(mockAnnotation.ignore()).andReturn(ignores); - - replay(mockAnnotation); - replay(mockSignature); - } - - @Test - public void testMonitor() throws Throwable { - ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 2); - expect(mockPjp.proceed()).andReturn(null).times(2); - replay(mockPjp); - - // Test monitor without pre-existing circuit breaker. - aspect.monitor(mockPjp, mockAnnotation); - - // Test monitor with pre-existing circuit breaker. - aspect.monitor(mockPjp, mockAnnotation); - - String otherName = "OtherMonitor"; - ProceedingJoinPoint otherMockPjp = createPjpMock(mockSignature, 1); - expect(otherMockPjp.proceed()).andReturn(null).times(1); - replay(otherMockPjp); - - CircuitBreaker otherMockAnnotation = createMock(CircuitBreaker.class); - expect(otherMockAnnotation.name()).andReturn(otherName).anyTimes(); - expect(otherMockAnnotation.limit()).andReturn(5).anyTimes(); - expect(otherMockAnnotation.resetMillis()).andReturn(30000L).anyTimes(); - expect(otherMockAnnotation.windowMillis()).andReturn(10000L).anyTimes(); - @SuppressWarnings("unchecked") - Class[] ignores = new Class[0]; - expect(otherMockAnnotation.ignore()).andReturn(ignores); - replay(otherMockAnnotation); - - // Test monitor with another circuit breaker. - aspect.monitor(otherMockPjp, otherMockAnnotation); - verifyBreakerExists(TEST_CIRCUIT_BREAKER); - verifyBreakerExists(otherName); - - verify(mockPjp); - verify(mockAnnotation); - verify(mockSignature); - verify(otherMockPjp); - verify(otherMockAnnotation); - } - - @Test - public void testSetCircuitBreakerFactory() throws Throwable { - ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 1); - expect(mockPjp.proceed()).andReturn(null); - replay(mockPjp); - - CircuitBreakerFactory factory = new CircuitBreakerFactory(); - aspect.setCircuitBreakerFactory(factory); - - aspect.monitor(mockPjp, mockAnnotation); - - assertSame(factory, aspect.getCircuitBreakerFactory()); - verifyBreakerExists(TEST_CIRCUIT_BREAKER); - - verify(mockPjp); - verify(mockAnnotation); - verify(mockSignature); - } - - @Test - public void testMonitorWithError() throws Throwable { - Error e = new Error(); - ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 1); - expect(mockPjp.proceed()).andThrow(e); - replay(mockPjp); - - callMonitorCatchThrowable(mockPjp, e); - verifyBreakerExists(TEST_CIRCUIT_BREAKER); - - verify(mockPjp); - verify(mockAnnotation); - verify(mockSignature); - } - - @Test - public void testMonitorWithRunTimeException() throws Throwable { - ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 1); - expect(mockPjp.proceed()).andThrow(new Throwable()); - replay(mockPjp); - - callMonitorCatchThrowable(mockPjp, new RuntimeException()); - verifyBreakerExists(TEST_CIRCUIT_BREAKER); - - verify(mockPjp); - verify(mockAnnotation); - verify(mockSignature); - } - - @Test - public void testMonitorWithException() throws Throwable { - Exception e = new Exception(); - ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 1); - expect(mockPjp.proceed()).andThrow(e); - replay(mockPjp); - - callMonitorCatchThrowable(mockPjp, e); - verifyBreakerExists(TEST_CIRCUIT_BREAKER); - - verify(mockPjp); - verify(mockAnnotation); - verify(mockSignature); - } - - @Test - public void testGetCircuitBreakerFactory() throws Throwable { - - ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 1); - expect(mockPjp.proceed()).andReturn(null); - replay(mockPjp); - - aspect.monitor(mockPjp, mockAnnotation); - CircuitBreakerFactory circuitBreakerFactory = - aspect.getCircuitBreakerFactory(); - - assertNotNull(circuitBreakerFactory); - verifyBreakerExists(TEST_CIRCUIT_BREAKER); - - verify(mockPjp); - verify(mockAnnotation); - verify(mockSignature); - } - - @Test - public void testTripBreaker() throws Throwable { - int pjpCallCount = 7; - int callCount = pjpCallCount - 1; - - ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, pjpCallCount); - - expect(mockPjp.proceed()).andThrow(new Exception()).times(callCount); - replay(mockPjp); - - Exception e = new Exception(); - - for (int i = 0; i < callCount; i++) { - callMonitorCatchThrowable(mockPjp, e); - } - - CircuitBreakerException cbe = new CircuitBreakerException(); - callMonitorCatchThrowable(mockPjp, cbe); - verifyBreakerExists(TEST_CIRCUIT_BREAKER); - - verify(mockPjp); - verify(mockAnnotation); - verify(mockSignature); - } - - private static ProceedingJoinPoint createPjpMock(Signature mockSignature, int times) { - ProceedingJoinPoint mockPjp = createMock(ProceedingJoinPoint.class); - // XXX: the following two interactions are for logging, so they may happen - // 0 or n times, pending logging configuration - expect(mockPjp.getTarget()).andReturn("Target").times(0, times); - expect(mockPjp.getSignature()).andReturn(mockSignature).times(0, times); - return mockPjp; - } - - private void callMonitorCatchThrowable( - ProceedingJoinPoint pjp, Throwable expected) { - try { - aspect.monitor(pjp, mockAnnotation); - } - catch (Throwable thrown) { - assertEquals(expected.getClass(), thrown.getClass()); - } - } - - private void verifyBreakerExists(String name) { - assertNotNull(aspect.getCircuitBreakerFactory().findCircuitBreaker(name)); - } + private CircuitBreakerAspect aspect; + + CircuitBreaker mockAnnotation; + + Signature mockSignature; + + private static final String TEST_CIRCUIT_BREAKER = "TestCircuitBreaker"; + + @Before + public void setUp() { + aspect = new CircuitBreakerAspect(); + + mockAnnotation = createMock(CircuitBreaker.class); + mockSignature = createMock(Signature.class); + + expect(mockSignature.getName()).andReturn("Signature").anyTimes(); + expect(mockAnnotation.name()).andReturn(TEST_CIRCUIT_BREAKER).anyTimes(); + expect(mockAnnotation.limit()).andReturn(5).anyTimes(); + expect(mockAnnotation.resetMillis()).andReturn(30000L).anyTimes(); + expect(mockAnnotation.windowMillis()).andReturn(10000L).anyTimes(); + @SuppressWarnings("unchecked") + Class[] ignores = new Class[0]; + expect(mockAnnotation.ignore()).andReturn(ignores); + + replay(mockAnnotation); + replay(mockSignature); + } + + @Test + public void testMonitor() throws Throwable { + ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 2); + expect(mockPjp.proceed()).andReturn(null).times(2); + replay(mockPjp); + + // Test monitor without pre-existing circuit breaker. + aspect.monitor(mockPjp, mockAnnotation); + + // Test monitor with pre-existing circuit breaker. + aspect.monitor(mockPjp, mockAnnotation); + + String otherName = "OtherMonitor"; + ProceedingJoinPoint otherMockPjp = createPjpMock(mockSignature, 1); + expect(otherMockPjp.proceed()).andReturn(null).times(1); + replay(otherMockPjp); + + CircuitBreaker otherMockAnnotation = createMock(CircuitBreaker.class); + expect(otherMockAnnotation.name()).andReturn(otherName).anyTimes(); + expect(otherMockAnnotation.limit()).andReturn(5).anyTimes(); + expect(otherMockAnnotation.resetMillis()).andReturn(30000L).anyTimes(); + expect(otherMockAnnotation.windowMillis()).andReturn(10000L).anyTimes(); + @SuppressWarnings("unchecked") + Class[] ignores = new Class[0]; + expect(otherMockAnnotation.ignore()).andReturn(ignores); + replay(otherMockAnnotation); + + // Test monitor with another circuit breaker. + aspect.monitor(otherMockPjp, otherMockAnnotation); + verifyBreakerExists(TEST_CIRCUIT_BREAKER); + verifyBreakerExists(otherName); + + verify(mockPjp); + verify(mockAnnotation); + verify(mockSignature); + verify(otherMockPjp); + verify(otherMockAnnotation); + } + + @Test + public void testSetCircuitBreakerFactory() throws Throwable { + ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 1); + expect(mockPjp.proceed()).andReturn(null); + replay(mockPjp); + + CircuitBreakerFactory factory = new CircuitBreakerFactory(); + aspect.setCircuitBreakerFactory(factory); + + aspect.monitor(mockPjp, mockAnnotation); + + assertSame(factory, aspect.getCircuitBreakerFactory()); + verifyBreakerExists(TEST_CIRCUIT_BREAKER); + + verify(mockPjp); + verify(mockAnnotation); + verify(mockSignature); + } + + @Test + public void testMonitorWithError() throws Throwable { + Error e = new Error(); + ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 1); + expect(mockPjp.proceed()).andThrow(e); + replay(mockPjp); + + callMonitorCatchThrowable(mockPjp, e); + verifyBreakerExists(TEST_CIRCUIT_BREAKER); + + verify(mockPjp); + verify(mockAnnotation); + verify(mockSignature); + } + + @Test + public void testMonitorWithRunTimeException() throws Throwable { + ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 1); + expect(mockPjp.proceed()).andThrow(new Throwable()); + replay(mockPjp); + + callMonitorCatchThrowable(mockPjp, new RuntimeException()); + verifyBreakerExists(TEST_CIRCUIT_BREAKER); + + verify(mockPjp); + verify(mockAnnotation); + verify(mockSignature); + } + + @Test + public void testMonitorWithException() throws Throwable { + Exception e = new Exception(); + ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 1); + expect(mockPjp.proceed()).andThrow(e); + replay(mockPjp); + + callMonitorCatchThrowable(mockPjp, e); + verifyBreakerExists(TEST_CIRCUIT_BREAKER); + + verify(mockPjp); + verify(mockAnnotation); + verify(mockSignature); + } + + @Test + public void testGetCircuitBreakerFactory() throws Throwable { + + ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 1); + expect(mockPjp.proceed()).andReturn(null); + replay(mockPjp); + + aspect.monitor(mockPjp, mockAnnotation); + CircuitBreakerFactory circuitBreakerFactory = aspect.getCircuitBreakerFactory(); + + assertNotNull(circuitBreakerFactory); + verifyBreakerExists(TEST_CIRCUIT_BREAKER); + + verify(mockPjp); + verify(mockAnnotation); + verify(mockSignature); + } + + @Test + public void testTripBreaker() throws Throwable { + int pjpCallCount = 7; + int callCount = pjpCallCount - 1; + + ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, pjpCallCount); + + expect(mockPjp.proceed()).andThrow(new Exception()).times(callCount); + replay(mockPjp); + + Exception e = new Exception(); + + for (int i = 0; i < callCount; i++) { + callMonitorCatchThrowable(mockPjp, e); + } + + CircuitBreakerException cbe = new CircuitBreakerException(); + callMonitorCatchThrowable(mockPjp, cbe); + verifyBreakerExists(TEST_CIRCUIT_BREAKER); + + verify(mockPjp); + verify(mockAnnotation); + verify(mockSignature); + } + + private static ProceedingJoinPoint createPjpMock(Signature mockSignature, int times) { + ProceedingJoinPoint mockPjp = createMock(ProceedingJoinPoint.class); + // XXX: the following two interactions are for logging, so they may happen + // 0 or n times, pending logging configuration + expect(mockPjp.getTarget()).andReturn("Target").times(0, times); + expect(mockPjp.getSignature()).andReturn(mockSignature).times(0, times); + return mockPjp; + } + + private void callMonitorCatchThrowable(ProceedingJoinPoint pjp, Throwable expected) { + try { + aspect.monitor(pjp, mockAnnotation); + } catch (Throwable thrown) { + assertEquals(expected.getClass(), thrown.getClass()); + } + } + + private void verifyBreakerExists(String name) { + assertNotNull(aspect.getCircuitBreakerFactory().findCircuitBreaker(name)); + } } diff --git a/jrugged-aspects/src/test/java/org/fishwife/jrugged/aspects/TestPerformanceMonitorAspect.java b/jrugged-aspects/src/test/java/org/fishwife/jrugged/aspects/TestPerformanceMonitorAspect.java index f5e8f025..1731365b 100644 --- a/jrugged-aspects/src/test/java/org/fishwife/jrugged/aspects/TestPerformanceMonitorAspect.java +++ b/jrugged-aspects/src/test/java/org/fishwife/jrugged/aspects/TestPerformanceMonitorAspect.java @@ -30,126 +30,125 @@ public class TestPerformanceMonitorAspect { - private PerformanceMonitorAspect aspect; + private PerformanceMonitorAspect aspect; - PerformanceMonitor mockAnnotation; + PerformanceMonitor mockAnnotation; - Signature mockSignature; + Signature mockSignature; - private static final String TEST_MONITOR = "TestMonitor"; + private static final String TEST_MONITOR = "TestMonitor"; - @Before - public void setUp() { - aspect = new PerformanceMonitorAspect(); + @Before + public void setUp() { + aspect = new PerformanceMonitorAspect(); - mockSignature = createMock(Signature.class); - mockAnnotation = createMock(PerformanceMonitor.class); + mockSignature = createMock(Signature.class); + mockAnnotation = createMock(PerformanceMonitor.class); - expect(mockSignature.getName()).andReturn("Signature").anyTimes(); - expect(mockAnnotation.value()).andReturn(TEST_MONITOR).anyTimes(); + expect(mockSignature.getName()).andReturn("Signature").anyTimes(); + expect(mockAnnotation.value()).andReturn(TEST_MONITOR).anyTimes(); - replay(mockAnnotation); - replay(mockSignature); - } + replay(mockAnnotation); + replay(mockSignature); + } - private static ProceedingJoinPoint createPjpMock(Signature mockSignature, - int times) { - ProceedingJoinPoint mockPjp = createMock(ProceedingJoinPoint.class); - // XXX: the following two interactions are for logging, so they may happen - // 0 or n times, pending logging configuration - expect(mockPjp.getTarget()).andReturn("Target").times(0, times); - expect(mockPjp.getSignature()).andReturn(mockSignature).times(0, times); - return mockPjp; - } + private static ProceedingJoinPoint createPjpMock(Signature mockSignature, int times) { + ProceedingJoinPoint mockPjp = createMock(ProceedingJoinPoint.class); + // XXX: the following two interactions are for logging, so they may happen + // 0 or n times, pending logging configuration + expect(mockPjp.getTarget()).andReturn("Target").times(0, times); + expect(mockPjp.getSignature()).andReturn(mockSignature).times(0, times); + return mockPjp; + } - @Test - public void testMonitor() throws Throwable { - ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 2); - expect(mockPjp.proceed()).andReturn(null).times(2); - replay(mockPjp); + @Test + public void testMonitor() throws Throwable { + ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 2); + expect(mockPjp.proceed()).andReturn(null).times(2); + replay(mockPjp); - // Test monitor without pre-existing perf monitor. - aspect.monitor(mockPjp, mockAnnotation); + // Test monitor without pre-existing perf monitor. + aspect.monitor(mockPjp, mockAnnotation); - // Test monitor with pre-existing perf monitor. - aspect.monitor(mockPjp, mockAnnotation); + // Test monitor with pre-existing perf monitor. + aspect.monitor(mockPjp, mockAnnotation); - String otherMonitor = "OtherMonitor"; + String otherMonitor = "OtherMonitor"; - ProceedingJoinPoint otherMockPjp = createPjpMock(mockSignature, 1); - expect(otherMockPjp.proceed()).andReturn(null).times(1); - replay(otherMockPjp); + ProceedingJoinPoint otherMockPjp = createPjpMock(mockSignature, 1); + expect(otherMockPjp.proceed()).andReturn(null).times(1); + replay(otherMockPjp); - PerformanceMonitor otherMockAnnotation = createMock(PerformanceMonitor.class); - expect(otherMockAnnotation.value()).andReturn(otherMonitor); - replay(otherMockAnnotation); + PerformanceMonitor otherMockAnnotation = createMock(PerformanceMonitor.class); + expect(otherMockAnnotation.value()).andReturn(otherMonitor); + replay(otherMockAnnotation); - // Test monitor with another perf monitor. - aspect.monitor(otherMockPjp, otherMockAnnotation); + // Test monitor with another perf monitor. + aspect.monitor(otherMockPjp, otherMockAnnotation); - verifyMonitor(TEST_MONITOR, 2, 0); - verifyMonitor(otherMonitor, 1, 0); + verifyMonitor(TEST_MONITOR, 2, 0); + verifyMonitor(otherMonitor, 1, 0); - verify(mockPjp); - verify(mockAnnotation); - verify(mockSignature); - verify(otherMockPjp); - verify(otherMockAnnotation); - } + verify(mockPjp); + verify(mockAnnotation); + verify(mockSignature); + verify(otherMockPjp); + verify(otherMockAnnotation); + } - @Test - public void testSetPerformanceMonitorFactory() throws Throwable { - ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 1); - expect(mockPjp.proceed()).andReturn(null); - replay(mockPjp); + @Test + public void testSetPerformanceMonitorFactory() throws Throwable { + ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 1); + expect(mockPjp.proceed()).andReturn(null); + replay(mockPjp); - PerformanceMonitorFactory factory = new PerformanceMonitorFactory(); - aspect.setPerformanceMonitorFactory(factory); + PerformanceMonitorFactory factory = new PerformanceMonitorFactory(); + aspect.setPerformanceMonitorFactory(factory); - aspect.monitor(mockPjp, mockAnnotation); + aspect.monitor(mockPjp, mockAnnotation); - assertSame(factory, aspect.getPerformanceMonitorFactory()); - verifyMonitor(TEST_MONITOR, 1, 0); + assertSame(factory, aspect.getPerformanceMonitorFactory()); + verifyMonitor(TEST_MONITOR, 1, 0); - verify(mockPjp); - verify(mockAnnotation); - verify(mockSignature); - } + verify(mockPjp); + verify(mockAnnotation); + verify(mockSignature); + } - @Test(expected = Throwable.class) - public void testMonitorWithThrowable() throws Throwable { + @Test(expected = Throwable.class) + public void testMonitorWithThrowable() throws Throwable { - ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 1); - expect(mockPjp.proceed()).andThrow(new Throwable()); - replay(mockPjp); + ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 1); + expect(mockPjp.proceed()).andThrow(new Throwable()); + replay(mockPjp); - aspect.monitor(mockPjp, mockAnnotation); - verifyMonitor(TEST_MONITOR, 0, 1); + aspect.monitor(mockPjp, mockAnnotation); + verifyMonitor(TEST_MONITOR, 0, 1); - verify(mockPjp); - verify(mockAnnotation); - verify(mockSignature); - } + verify(mockPjp); + verify(mockAnnotation); + verify(mockSignature); + } - @Test(expected = Exception.class) - public void testMonitorWithException() throws Throwable { - ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 1); - expect(mockPjp.proceed()).andThrow(new Exception()); - replay(mockPjp); - - aspect.monitor(mockPjp, mockAnnotation); - verifyMonitor(TEST_MONITOR, 0, 1); - - verify(mockPjp); - verify(mockAnnotation); - verify(mockSignature); - } - - private void verifyMonitor(String name, int successCount, int failureCount) { - org.fishwife.jrugged.PerformanceMonitor monitor = - aspect.getPerformanceMonitorFactory().findPerformanceMonitor(name); - assertNotNull(monitor); - assertEquals(successCount, monitor.getSuccessCount()); - assertEquals(failureCount, monitor.getFailureCount()); - } + @Test(expected = Exception.class) + public void testMonitorWithException() throws Throwable { + ProceedingJoinPoint mockPjp = createPjpMock(mockSignature, 1); + expect(mockPjp.proceed()).andThrow(new Exception()); + replay(mockPjp); + + aspect.monitor(mockPjp, mockAnnotation); + verifyMonitor(TEST_MONITOR, 0, 1); + + verify(mockPjp); + verify(mockAnnotation); + verify(mockSignature); + } + + private void verifyMonitor(String name, int successCount, int failureCount) { + org.fishwife.jrugged.PerformanceMonitor monitor = aspect.getPerformanceMonitorFactory() + .findPerformanceMonitor(name); + assertNotNull(monitor); + assertEquals(successCount, monitor.getSuccessCount()); + assertEquals(failureCount, monitor.getFailureCount()); + } } diff --git a/jrugged-aspects/src/test/java/org/fishwife/jrugged/aspects/TestRetryableAspect.java b/jrugged-aspects/src/test/java/org/fishwife/jrugged/aspects/TestRetryableAspect.java index dd37604b..f002912c 100644 --- a/jrugged-aspects/src/test/java/org/fishwife/jrugged/aspects/TestRetryableAspect.java +++ b/jrugged-aspects/src/test/java/org/fishwife/jrugged/aspects/TestRetryableAspect.java @@ -27,106 +27,104 @@ public class TestRetryableAspect { - private RetryableAspect aspect; - - private Retryable mockAnnotation; - - private Signature mockSignature; - - - @Before - public void setUp() { - aspect = new RetryableAspect(); - - mockAnnotation = createMock(Retryable.class); - expect(mockAnnotation.maxTries()).andReturn(1).anyTimes(); - expect(mockAnnotation.retryDelayMillis()).andReturn(0).anyTimes(); - @SuppressWarnings("unchecked") - Class[] retryOn = new Class[0]; - expect(mockAnnotation.retryOn()).andReturn(retryOn); - expect(mockAnnotation.doubleDelay()).andReturn(true); - expect(mockAnnotation.throwCauseException()).andReturn(true); - replay(mockAnnotation); - - mockSignature = createMock(Signature.class); - expect(mockSignature.getName()).andReturn("Signature").anyTimes(); - replay(mockSignature); - } - - @Test - public void testCall() throws Throwable { - ProceedingJoinPoint mockPjp = createPjpMock(mockSignature); - expect(mockPjp.proceed()).andReturn(null).times(1); - replay(mockPjp); - - aspect.call(mockPjp, mockAnnotation); - - verify(mockPjp); - verify(mockAnnotation); - verify(mockSignature); - } - - @Test - public void testCall_WithException() throws Throwable { - Exception exception = new Exception(); - - ProceedingJoinPoint mockPjp = createPjpMock(mockSignature); - expect(mockPjp.proceed()).andThrow(exception); - replay(mockPjp); - - callCatchThrowable(mockPjp, exception); - - verify(mockPjp); - verify(mockAnnotation); - verify(mockSignature); - } - - @Test - public void testCall_WithError() throws Throwable { - Error error = new Error(); - - ProceedingJoinPoint mockPjp = createPjpMock(mockSignature); - expect(mockPjp.proceed()).andThrow(error); - replay(mockPjp); - - callCatchThrowable(mockPjp, error); - - verify(mockPjp); - verify(mockAnnotation); - verify(mockSignature); - } - - @Test - public void testCall_WithThrowable() throws Throwable { - Throwable throwable = new Throwable(); - - ProceedingJoinPoint mockPjp = createPjpMock(mockSignature); - expect(mockPjp.proceed()).andThrow(throwable); - replay(mockPjp); - - callCatchThrowable(mockPjp, new RuntimeException(throwable)); - - verify(mockPjp); - verify(mockAnnotation); - verify(mockSignature); - } - - private static ProceedingJoinPoint createPjpMock(Signature mockSignature) { - ProceedingJoinPoint mockPjp = createMock(ProceedingJoinPoint.class); - // XXX: the following two interactions are for logging, so they may happen - // 0 or n times, pending logging configuration - expect(mockPjp.getTarget()).andReturn("Target").times(0, 1); - expect(mockPjp.getSignature()).andReturn(mockSignature).times(0, 1); - return mockPjp; - } - - private void callCatchThrowable(ProceedingJoinPoint pjp, Throwable expected) { - try { - aspect.call(pjp, mockAnnotation); - } - catch (Throwable thrown) { - thrown.printStackTrace(); - assertEquals(expected.getClass(), thrown.getClass()); - } - } + private RetryableAspect aspect; + + private Retryable mockAnnotation; + + private Signature mockSignature; + + @Before + public void setUp() { + aspect = new RetryableAspect(); + + mockAnnotation = createMock(Retryable.class); + expect(mockAnnotation.maxTries()).andReturn(1).anyTimes(); + expect(mockAnnotation.retryDelayMillis()).andReturn(0).anyTimes(); + @SuppressWarnings("unchecked") + Class[] retryOn = new Class[0]; + expect(mockAnnotation.retryOn()).andReturn(retryOn); + expect(mockAnnotation.doubleDelay()).andReturn(true); + expect(mockAnnotation.throwCauseException()).andReturn(true); + replay(mockAnnotation); + + mockSignature = createMock(Signature.class); + expect(mockSignature.getName()).andReturn("Signature").anyTimes(); + replay(mockSignature); + } + + @Test + public void testCall() throws Throwable { + ProceedingJoinPoint mockPjp = createPjpMock(mockSignature); + expect(mockPjp.proceed()).andReturn(null).times(1); + replay(mockPjp); + + aspect.call(mockPjp, mockAnnotation); + + verify(mockPjp); + verify(mockAnnotation); + verify(mockSignature); + } + + @Test + public void testCall_WithException() throws Throwable { + Exception exception = new Exception(); + + ProceedingJoinPoint mockPjp = createPjpMock(mockSignature); + expect(mockPjp.proceed()).andThrow(exception); + replay(mockPjp); + + callCatchThrowable(mockPjp, exception); + + verify(mockPjp); + verify(mockAnnotation); + verify(mockSignature); + } + + @Test + public void testCall_WithError() throws Throwable { + Error error = new Error(); + + ProceedingJoinPoint mockPjp = createPjpMock(mockSignature); + expect(mockPjp.proceed()).andThrow(error); + replay(mockPjp); + + callCatchThrowable(mockPjp, error); + + verify(mockPjp); + verify(mockAnnotation); + verify(mockSignature); + } + + @Test + public void testCall_WithThrowable() throws Throwable { + Throwable throwable = new Throwable(); + + ProceedingJoinPoint mockPjp = createPjpMock(mockSignature); + expect(mockPjp.proceed()).andThrow(throwable); + replay(mockPjp); + + callCatchThrowable(mockPjp, new RuntimeException(throwable)); + + verify(mockPjp); + verify(mockAnnotation); + verify(mockSignature); + } + + private static ProceedingJoinPoint createPjpMock(Signature mockSignature) { + ProceedingJoinPoint mockPjp = createMock(ProceedingJoinPoint.class); + // XXX: the following two interactions are for logging, so they may happen + // 0 or n times, pending logging configuration + expect(mockPjp.getTarget()).andReturn("Target").times(0, 1); + expect(mockPjp.getSignature()).andReturn(mockSignature).times(0, 1); + return mockPjp; + } + + private void callCatchThrowable(ProceedingJoinPoint pjp, Throwable expected) { + try { + aspect.call(pjp, mockAnnotation); + } catch (Throwable thrown) { + thrown.printStackTrace(); + assertEquals(expected.getClass(), thrown.getClass()); + } + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/CallableAdapter.java b/jrugged-core/src/main/java/org/fishwife/jrugged/CallableAdapter.java index 6f2f1f8f..ceda6c6a 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/CallableAdapter.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/CallableAdapter.java @@ -20,25 +20,26 @@ /** * Adapter to expose a Runnable as a Callable, with an optional result. + * * @param What am I building this adaptor for */ public class CallableAdapter implements Callable { - private Runnable runnable; - private R result; + private Runnable runnable; + private R result; - public CallableAdapter(Runnable runnable) { - this(runnable, null); - } + public CallableAdapter(Runnable runnable) { + this(runnable, null); + } - public CallableAdapter(Runnable runnable, R result) { - this.runnable = runnable; - this.result = result; - } + public CallableAdapter(Runnable runnable, R result) { + this.runnable = runnable; + this.result = result; + } - public R call() throws Exception { - runnable.run(); - return result; - } + public R call() throws Exception { + runnable.run(); + return result; + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreaker.java b/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreaker.java index 8a29b721..d6b3cb5d 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreaker.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreaker.java @@ -25,40 +25,650 @@ import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicLong; -/** A {@link CircuitBreaker} can be used with a service to throttle traffic - * to a failed subsystem (particularly one we might not be able to monitor, - * such as a peer system which must be accessed over the network). Service - * calls are wrapped by the CircuitBreaker. - *

- * When everything is operating normally, the CircuitBreaker - * is CLOSED and the calls are allowed through. - *

- * When a call fails, however, the CircuitBreaker "trips" and - * moves to an OPEN state. Client calls are not allowed through while - * the CircuitBreaker is OPEN. - *

- * After a certain "cooldown" period, the CircuitBreaker will - * transition to a HALF_CLOSED state, where one call is allowed to go through - * as a test. If that call succeeds, the CircuitBreaker moves - * back to the CLOSED state; if it fails, it moves back to the OPEN state - * for another cooldown period. - *

- * Sample usage: - *

-    public class Service implements Monitorable {
-        private CircuitBreaker cb = new CircuitBreaker();
-        public String doSomething(final Object arg) throws Exception {
-        return cb.invoke(new Callable<String>() {
-                                 public String call() {
-                                     // make the call ...
-                                 }
-                             });
-        }
-        public Status getStatus() { return cb.getStatus(); }
-    }
+/**
+ * A {@link CircuitBreaker} can be used with a service to throttle traffic to a
+ * failed subsystem (particularly one we might not be able to monitor, such as a
+ * peer system which must be accessed over the network). Service calls are
+ * wrapped by the CircuitBreaker.
+ * 

+ * When everything is operating normally, the CircuitBreaker is + * CLOSED and the calls are allowed through. + *

+ * When a call fails, however, the CircuitBreaker "trips" and moves + * to an OPEN state. Client calls are not allowed through while the + * CircuitBreaker is OPEN. + *

+ * After a certain "cooldown" period, the CircuitBreaker will + * transition to a HALF_CLOSED state, where one call is allowed to go through as + * a test. If that call succeeds, the CircuitBreaker moves back to + * the CLOSED state; if it fails, it moves back to the OPEN state for another + * cooldown period. + *

+ * Sample usage: + * + *

+ * public class Service implements Monitorable {
+ * 	private CircuitBreaker cb = new CircuitBreaker();
+ * 
+ * 	public String doSomething(final Object arg) throws Exception {
+ * 		return cb.invoke(new Callable<String>() {
+ * 			public String call() {
+ * 				// make the call ...
+ * 			}
+ * 		});
+ * 	}
+ * 
+ * 	public Status getStatus() {
+ * 		return cb.getStatus();
+ * 	}
+ * }
  * 
*/ public class CircuitBreaker implements MonitoredService, ServiceWrapper { + /** + * Represents whether a {@link CircuitBreaker} is OPEN, HALF_CLOSED, or CLOSED. + */ + protected enum BreakerState { + /** + * An OPEN breaker has tripped and will not allow requests through. + */ + OPEN, + + /** + * A HALF_CLOSED breaker has completed its cooldown period and will allow one + * request through as a "test request." + */ + HALF_CLOSED, + + /** + * A CLOSED breaker is operating normally and allowing requests through. + */ + CLOSED + } + + private Throwable tripException = null; + + /** + * Returns the last exception that caused the breaker to trip, NULL if never + * tripped. + * + * @return Throwable + */ + public Throwable getTripException() { + return tripException; + } + + /** + * Returns the last exception that caused the breaker to trip, empty + * String if never tripped. + * + * @return Throwable + */ + public String getTripExceptionAsString() { + if (tripException == null) { + return ""; + } else { + return getFullStackTrace(tripException); + + } + } + + /** Current state of the breaker. */ + protected volatile BreakerState state = BreakerState.CLOSED; + + /** + * The time the breaker last tripped, in milliseconds since the epoch. + */ + protected AtomicLong lastFailure = new AtomicLong(0L); + + /** How many times the breaker has tripped during its lifetime. */ + protected AtomicLong openCount = new AtomicLong(0L); + + /** How long the cooldown period is in milliseconds. */ + protected AtomicLong resetMillis = new AtomicLong(15 * 1000L); + + /** + * The {@link FailureInterpreter} to use to determine whether a given failure + * should cause the breaker to trip. + */ + protected FailureInterpreter failureInterpreter = new DefaultFailureInterpreter(); + + /** + * Helper class to allow throwing an application-specific exception rather than + * the default {@link CircuitBreakerException}. + */ + protected CircuitBreakerExceptionMapper exceptionMapper; + + protected List cbNotifyList = Collections + .synchronizedList(new ArrayList()); + + private boolean isHardTrip; + + /** + * Bypass this CircuitBreaker - used for testing, or other operational + * situations where verification of the Break might be required. + */ + protected boolean byPass = false; + + /** + * Whether the "test" attempt permitted in the HALF_CLOSED state is currently + * in-flight. + */ + protected boolean isAttemptLive = false; + + /** The default name if none is provided. */ + private static final String DEFAULT_NAME = "CircuitBreaker"; + + /** The name for the CircuitBreaker. */ + protected String name = DEFAULT_NAME; + + /** + * Creates a {@link CircuitBreaker} with a {@link DefaultFailureInterpreter} and + * the default "tripped" exception behavior (throwing a + * {@link CircuitBreakerException}). + */ + public CircuitBreaker() { + } + + /** + * Creates a {@link CircuitBreaker} with a {@link DefaultFailureInterpreter} and + * the default "tripped" exception behavior (throwing a + * {@link CircuitBreakerException}). + * + * @param name the name for the {@link CircuitBreaker}. + */ + public CircuitBreaker(String name) { + this.name = name; + } + + /** + * Creates a {@link CircuitBreaker} with the specified + * {@link FailureInterpreter} and the default "tripped" exception behavior + * (throwing a {@link CircuitBreakerException}). + * + * @param fi the FailureInterpreter to use when determining whether + * a specific failure ought to cause the breaker to trip + */ + public CircuitBreaker(FailureInterpreter fi) { + failureInterpreter = fi; + } + + /** + * Creates a {@link CircuitBreaker} with the specified + * {@link FailureInterpreter} and the default "tripped" exception behavior + * (throwing a {@link CircuitBreakerException}). + * + * @param name the name for the {@link CircuitBreaker}. + * @param fi the FailureInterpreter to use when determining + * whether a specific failure ought to cause the breaker to trip + */ + public CircuitBreaker(String name, FailureInterpreter fi) { + this.name = name; + failureInterpreter = fi; + } + + /** + * Creates a {@link CircuitBreaker} with a {@link DefaultFailureInterpreter} and + * using the supplied {@link CircuitBreakerExceptionMapper} when client calls + * are made while the breaker is tripped. + * + * @param name the name for the {@link CircuitBreaker}. + * @param mapper helper used to translate a {@link CircuitBreakerException} into + * an application-specific one + */ + public CircuitBreaker(String name, CircuitBreakerExceptionMapper mapper) { + this.name = name; + exceptionMapper = mapper; + } + + /** + * Creates a {@link CircuitBreaker} with the provided {@link FailureInterpreter} + * and using the provided {@link CircuitBreakerExceptionMapper} when client + * calls are made while the breaker is tripped. + * + * @param name the name for the {@link CircuitBreaker}. + * @param fi the FailureInterpreter to use when determining + * whether a specific failure ought to cause the breaker to trip + * @param mapper helper used to translate a {@link CircuitBreakerException} into + * an application-specific one + */ + public CircuitBreaker(String name, FailureInterpreter fi, + CircuitBreakerExceptionMapper mapper) { + this.name = name; + failureInterpreter = fi; + exceptionMapper = mapper; + } + + /** + * Wrap the given service call with the {@link CircuitBreaker} protection logic. + * + * @param c the {@link Callable} to attempt + * @return whatever c would return on success + * @throws CircuitBreakerException if the breaker was OPEN or HALF_CLOSED and + * this attempt wasn't the reset attempt + * @throws Exception if c throws one during execution + */ + public V invoke(Callable c) throws Exception { + if (!byPass) { + if (!allowRequest()) { + throw mapException(new CircuitBreakerException()); + } + + try { + isAttemptLive = true; + V result = c.call(); + close(); + return result; + } catch (Throwable cause) { + handleFailure(cause); + } + throw new IllegalStateException("not possible"); + } else { + return c.call(); + } + } + + /** + * Wrap the given service call with the {@link CircuitBreaker} protection logic. + * + * @param r the {@link Runnable} to attempt + * @throws CircuitBreakerException if the breaker was OPEN or HALF_CLOSED and + * this attempt wasn't the reset attempt + * @throws Exception if c throws one during execution + */ + public void invoke(Runnable r) throws Exception { + if (!byPass) { + if (!allowRequest()) { + throw mapException(new CircuitBreakerException()); + } + + try { + isAttemptLive = true; + r.run(); + close(); + return; + } catch (Throwable cause) { + handleFailure(cause); + } + throw new IllegalStateException("not possible"); + } else { + r.run(); + } + } + + /** + * Wrap the given service call with the {@link CircuitBreaker} protection logic. + * + * @param r the {@link Runnable} to attempt + * @param result what to return after r succeeds + * @return result + * @throws CircuitBreakerException if the breaker was OPEN or HALF_CLOSED and + * this attempt wasn't the reset attempt + * @throws Exception if c throws one during execution + */ + public V invoke(Runnable r, V result) throws Exception { + if (!byPass) { + if (!allowRequest()) { + throw mapException(new CircuitBreakerException()); + } + + try { + isAttemptLive = true; + r.run(); + close(); + return result; + } catch (Throwable cause) { + handleFailure(cause); + } + throw new IllegalStateException("not possible"); + } else { + r.run(); + return result; + } + } + + /** + * When called with true - causes the {@link CircuitBreaker} to byPass its + * functionality allowing requests to be executed unmolested until the + * CircuitBreaker is reset or the byPass is manually set to false. + * + * @param b Set this breaker into bypass mode + */ + public void setByPassState(boolean b) { + byPass = b; + notifyBreakerStateChange(getStatus()); + } + + /** + * Get the current state of the {@link CircuitBreaker} byPass + * + * @return boolean the byPass flag's current value + */ + public boolean getByPassState() { + return byPass; + } + + /** + * Causes the {@link CircuitBreaker} to trip and OPEN; no new requests will be + * allowed until the CircuitBreaker resets. + */ + public void trip() { + if (state != BreakerState.OPEN) { + openCount.getAndIncrement(); + } + state = BreakerState.OPEN; + lastFailure.set(System.currentTimeMillis()); + isAttemptLive = false; + + notifyBreakerStateChange(getStatus()); + } + + /** + * Manually trips the CircuitBreaker until {@link #reset()} is invoked. + */ + public void tripHard() { + this.trip(); + isHardTrip = true; + } + + /** + * Returns the last time the breaker tripped OPEN, measured in milliseconds + * since the Epoch. + * + * @return long the last failure time + */ + public long getLastTripTime() { + return lastFailure.get(); + } + + /** + * Returns the number of times the breaker has tripped OPEN during its lifetime. + * + * @return long the number of times the circuit breaker tripped + */ + public long getTripCount() { + return openCount.get(); + } + + /** + * Manually set the breaker to be reset and ready for use. This is only useful + * after a manual trip otherwise the breaker will trip automatically again if + * the service is still unavailable. Just like a real breaker. WOOT!!! + */ + public void reset() { + state = BreakerState.CLOSED; + isHardTrip = false; + byPass = false; + isAttemptLive = false; + + notifyBreakerStateChange(getStatus()); + } + + /** + * Returns the current {@link org.fishwife.jrugged.Status} of the + * {@link CircuitBreaker}. In this case, it really refers to the status of the + * client service. If the CircuitBreaker is CLOSED, we report that + * the client is UP; if it is HALF_CLOSED, we report that the client is + * DEGRADED; if it is OPEN, we report the client is DOWN. + * + * @return Status the current status of the breaker + */ + public Status getStatus() { + return getServiceStatus().getStatus(); + } + + /** + * Get the current {@link ServiceStatus} of the {@link CircuitBreaker}, + * including the name, {@link org.fishwife.jrugged.Status}, and reason. + * + * @return the {@link ServiceStatus}. + */ + public ServiceStatus getServiceStatus() { + boolean canSendProbeRequest = !isHardTrip && lastFailure.get() > 0 && allowRequest(); + + if (byPass) { + return new ServiceStatus(name, Status.DEGRADED, "Bypassed"); + } + + switch (state) { + case OPEN: + return (canSendProbeRequest ? new ServiceStatus(name, Status.DEGRADED, "Send Probe Request") + : new ServiceStatus(name, Status.DOWN, "Open")); + case HALF_CLOSED: + return new ServiceStatus(name, Status.DEGRADED, "Half Closed"); + case CLOSED: + default: + return new ServiceStatus(name, Status.UP); + } + } + + /** + * Returns the cooldown period in milliseconds. + * + * @return long + */ + public long getResetMillis() { + return resetMillis.get(); + } + + /** + * Sets the reset period to the given number of milliseconds. The default is + * 15,000 (make one retry attempt every 15 seconds). + * + * @param l number of milliseconds to "cool down" after tripping before allowing + * a "test request" through again + */ + public void setResetMillis(long l) { + resetMillis.set(l); + } + + /** + * Returns a {@link String} representation of the breaker's status; potentially + * useful for exposing to monitoring software. + * + * @return String which is "GREEN" if the breaker is + * CLOSED; "YELLOW" if the breaker is HALF_CLOSED; and + * "RED" if the breaker is OPEN (tripped). + */ + public String getHealthCheck() { + return getStatus().getSignal(); + } + + /** + * Specifies the failure tolerance limit for the + * {@link DefaultFailureInterpreter} that comes with a {@link CircuitBreaker} by + * default. + * + * @see DefaultFailureInterpreter + * @param limit the number of tolerated failures in a window + */ + public void setLimit(int limit) { + FailureInterpreter fi = getFailureInterpreter(); + if (!(fi instanceof DefaultFailureInterpreter)) { + throw new IllegalStateException( + "setLimit() not supported: this CircuitBreaker's FailureInterpreter isn't a DefaultFailureInterpreter."); + } + ((DefaultFailureInterpreter) fi).setLimit(limit); + } + + /** + * Specifies a set of {@link Throwable} classes that should not be considered + * failures by the {@link CircuitBreaker}. + * + * @see DefaultFailureInterpreter + * @param ignore a {@link java.util.Collection} of {@link Throwable} classes + */ + public void setIgnore(Collection> ignore) { + FailureInterpreter fi = getFailureInterpreter(); + if (!(fi instanceof DefaultFailureInterpreter)) { + throw new IllegalStateException( + "setIgnore() not supported: this CircuitBreaker's FailureInterpreter isn't a DefaultFailureInterpreter."); + } + + @SuppressWarnings("unchecked") + Class[] classes = new Class[ignore.size()]; + int i = 0; + for (Class c : ignore) { + classes[i] = c; + i++; + } + ((DefaultFailureInterpreter) fi).setIgnore(classes); + } + + /** + * Specifies the tolerance window in milliseconds for the + * {@link DefaultFailureInterpreter} that comes with a {@link CircuitBreaker} by + * default. + * + * @see DefaultFailureInterpreter + * @param windowMillis length of the window in milliseconds + */ + public void setWindowMillis(long windowMillis) { + FailureInterpreter fi = getFailureInterpreter(); + if (!(fi instanceof DefaultFailureInterpreter)) { + throw new IllegalStateException( + "setWindowMillis() not supported: this CircuitBreaker's FailureInterpreter isn't a DefaultFailureInterpreter."); + } + ((DefaultFailureInterpreter) fi).setWindowMillis(windowMillis); + } + + /** + * Specifies a helper that determines whether a given failure will cause the + * breaker to trip or not. + * + * @param failureInterpreter the {@link FailureInterpreter} to use + */ + public void setFailureInterpreter(FailureInterpreter failureInterpreter) { + this.failureInterpreter = failureInterpreter; + } + + /** + * Get the failure interpreter for this instance. The failure interpreter + * provides the configuration for determining which exceptions trip the circuit + * breaker, in what time interval, etc. + * + * @return {@link FailureInterpreter} for this instance or null if no failure + * interpreter was set. + */ + public FailureInterpreter getFailureInterpreter() { + return this.failureInterpreter; + } + + /** + * A helper that converts CircuitBreakerExceptions into a known 'application' + * exception. + * + * @param mapper my converter object + */ + public void setExceptionMapper(CircuitBreakerExceptionMapper mapper) { + this.exceptionMapper = mapper; + } + + /** + * Add an interested party for {@link CircuitBreaker} events, like up, down, + * degraded status state changes. + * + * @param listener an interested party for {@link CircuitBreaker} status events. + */ + public void addListener(CircuitBreakerNotificationCallback listener) { + cbNotifyList.add(listener); + } + + /** + * Set a list of interested parties for {@link CircuitBreaker} events, like up, + * down, degraded status state changes. + * + * @param listeners a list of interested parties for {@link CircuitBreaker} + * status events. + */ + public void setListeners(ArrayList listeners) { + cbNotifyList = Collections.synchronizedList(listeners); + } + + /** + * Get the helper that converts {@link CircuitBreakerException}s into + * application-specific exceptions. + * + * @return {@link CircuitBreakerExceptionMapper} my converter object, or + * null if one is not currently set. + */ + public CircuitBreakerExceptionMapper getExceptionMapper() { + return this.exceptionMapper; + } + + protected Exception mapException(CircuitBreakerException cbe) { + if (exceptionMapper == null) + return cbe; + + return exceptionMapper.map(this, cbe); + } + + protected void handleFailure(Throwable cause) throws Exception { + if (failureInterpreter == null || failureInterpreter.shouldTrip(cause)) { + this.tripException = cause; + trip(); + } else if (isAttemptLive) { + close(); + } + + if (cause instanceof Exception) { + throw (Exception) cause; + } else if (cause instanceof Error) { + throw (Error) cause; + } else { + throw (RuntimeException) cause; + } + } + + /** + * Reports a successful service call to the {@link CircuitBreaker}, putting the + * CircuitBreaker back into the CLOSED state serving requests. + */ + protected void close() { + state = BreakerState.CLOSED; + isAttemptLive = false; + notifyBreakerStateChange(getStatus()); + } + + private synchronized boolean canAttempt() { + if (!(BreakerState.HALF_CLOSED == state) || isAttemptLive) { + return false; + } + return true; + } + + private void notifyBreakerStateChange(Status status) { + if (cbNotifyList != null && cbNotifyList.size() >= 1) { + for (CircuitBreakerNotificationCallback notifyObject : cbNotifyList) { + notifyObject.notify(status); + } + } + } + + /** + * @return boolean whether the breaker will allow a request through or not. + */ + protected boolean allowRequest() { + if (this.isHardTrip) { + return false; + } else if (BreakerState.CLOSED == state) { + return true; + } + + if (BreakerState.OPEN == state && System.currentTimeMillis() - lastFailure.get() >= resetMillis.get()) { + state = BreakerState.HALF_CLOSED; + } + + return canAttempt(); + + } + + private String getFullStackTrace(Throwable t) { + StringWriter sw = new StringWriter(); + t.printStackTrace(new PrintWriter(sw)); + return sw.toString(); + } +======= /** * Represents whether a {@link CircuitBreaker} is OPEN, HALF_CLOSED, * or CLOSED. diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerConfig.java b/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerConfig.java index 4b6c5d86..956b60c3 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerConfig.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerConfig.java @@ -20,20 +20,19 @@ */ public class CircuitBreakerConfig { - private FailureInterpreter failureInterpreter; - private long resetMillis; - - public CircuitBreakerConfig(long resetMillis, - FailureInterpreter failureInterpreter) { - this.resetMillis = resetMillis; - this.failureInterpreter = failureInterpreter; - } - - public long getResetMillis() { - return resetMillis; - } - - public FailureInterpreter getFailureInterpreter() { - return failureInterpreter; - } + private FailureInterpreter failureInterpreter; + private long resetMillis; + + public CircuitBreakerConfig(long resetMillis, FailureInterpreter failureInterpreter) { + this.resetMillis = resetMillis; + this.failureInterpreter = failureInterpreter; + } + + public long getResetMillis() { + return resetMillis; + } + + public FailureInterpreter getFailureInterpreter() { + return failureInterpreter; + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerException.java b/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerException.java index a49eba99..cbdf8fc6 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerException.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerException.java @@ -17,13 +17,14 @@ package org.fishwife.jrugged; /** - * This exception gets thrown by a {@link CircuitBreaker} if a wrapped - * call is disallowed by a tripped (OPEN) breaker. + * This exception gets thrown by a {@link CircuitBreaker} if a wrapped call is + * disallowed by a tripped (OPEN) breaker. */ public class CircuitBreakerException extends RuntimeException { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - /** Default constructor. */ - public CircuitBreakerException() { } + /** Default constructor. */ + public CircuitBreakerException() { + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerExceptionMapper.java b/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerExceptionMapper.java index c9a01d97..560a73e2 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerExceptionMapper.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerExceptionMapper.java @@ -15,21 +15,20 @@ package org.fishwife.jrugged; /** - * Allows the user to map the standard {@link CircuitBreakerException} - * thrown by a tripped {@link CircuitBreaker} into an application - * specific exception. + * Allows the user to map the standard {@link CircuitBreakerException} thrown by + * a tripped {@link CircuitBreaker} into an application specific exception. * * @param is the application specific exception type. */ public interface CircuitBreakerExceptionMapper { - /** - * Turns a {@link CircuitBreakerException} into the desired exception (T) - * - * @param breaker the {@link CircuitBreaker} - * @param e the CircuitBreakerException I get - * @return the {@link Exception} I want thrown instead - */ - public T map(CircuitBreaker breaker, CircuitBreakerException e); + /** + * Turns a {@link CircuitBreakerException} into the desired exception (T) + * + * @param breaker the {@link CircuitBreaker} + * @param e the CircuitBreakerException I get + * @return the {@link Exception} I want thrown instead + */ + public T map(CircuitBreaker breaker, CircuitBreakerException e); } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerFactory.java b/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerFactory.java index fd1cd870..7efdc5bd 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerFactory.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerFactory.java @@ -27,192 +27,177 @@ */ public class CircuitBreakerFactory { - public static final String CONFIG_KEY_PREFIX = "circuit"; - public static final String LIMIT_KEY = "limit"; - public static final String WINDOWMILLIS_KEY = "windowMillis"; - public static final String RESETMILLIS_KEY = "resetMillis"; - - private final ConcurrentHashMap circuitBreakerMap = - new ConcurrentHashMap(); - - private Properties properties; - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - /** - * Create a new {@link CircuitBreaker} and map it to the provided name. - * If the CircuitBreaker already exists, then the existing instance is - * returned. - * @param name the name of the {@link CircuitBreaker} - * @param config the {@link CircuitBreakerConfig} with the configuration - * values. - * @return the created {@link CircuitBreaker} - */ - public synchronized CircuitBreaker createCircuitBreaker(String name, - CircuitBreakerConfig config) { - CircuitBreaker circuitBreaker = findCircuitBreaker(name); - - if (circuitBreaker == null) { - circuitBreaker = new CircuitBreaker(name); - - configureCircuitBreaker(name, circuitBreaker, config); - addCircuitBreakerToMap(name, circuitBreaker); - } - - return circuitBreaker; - } - - /** - * Set the {@link Properties} object to search for {@link CircuitBreaker} - * property override values. The override values can be specified as:
- * circuit.{circuit_name}.limit
- * circuit.{circuit_name}.resetmillis
- * circuit.{circuit_name}.windowmillis - * @param properties the {@link Properties} object to search. - */ - public void setProperties(Properties properties) { - this.properties = properties; - } - - /** - * Find an existing {@link CircuitBreaker} - * @param name the value for the {@link CircuitBreaker} - * @return the found {@link CircuitBreaker}, or null if it is not found. - */ - public CircuitBreaker findCircuitBreaker(String name) { - return circuitBreakerMap.get(name); - } - - /** - * Get the {@link Set} of created {@link CircuitBreaker} names. - * @return the {@link Set} of names. - */ - public Set getCircuitBreakerNames() { - return circuitBreakerMap.keySet(); - } - - protected void configureCircuitBreaker(String name, - CircuitBreaker circuit, - CircuitBreakerConfig config) { - - long resetMillis = config.getResetMillis(); - Long resetMillisOverride = getLongPropertyOverrideValue(name, RESETMILLIS_KEY); - if (resetMillisOverride != null) { - resetMillis = resetMillisOverride; - } - - FailureInterpreter fi = config.getFailureInterpreter(); - circuit.setFailureInterpreter(fi); - - if (resetMillis > 0) { - circuit.setResetMillis(resetMillis); - } - - if (fi instanceof DefaultFailureInterpreter) { - configureDefaultFailureInterpreter(name, resetMillis, circuit); - } - else { - logger.info( - "Created CircuitBreaker '{}', resetMillis={}", - new Object[] { - name, - resetMillis - }); - } - } - - private void configureDefaultFailureInterpreter(String name, long resetMillis, CircuitBreaker circuit) { - DefaultFailureInterpreter fi = (DefaultFailureInterpreter) circuit.getFailureInterpreter(); - - Integer limitOverride = getIntegerPropertyOverrideValue(name, LIMIT_KEY); - - if (limitOverride != null) { - fi.setLimit(limitOverride); - } - - Long windowMillisOverride = getLongPropertyOverrideValue(name, WINDOWMILLIS_KEY); - - if (windowMillisOverride != null) { - fi.setWindowMillis(windowMillisOverride); - } - - logger.info( - "Created CircuitBreaker '{}', limit={}, windowMillis={}, resetMillis={}", - new Object[] { - name, - fi.getLimit(), - fi.getWindowMillis(), - resetMillis - }); - } - - /** - * Add a {@link CircuitBreaker} to the map. - * @param name the name for the {@link CircuitBreaker} - * @param circuitBreaker the {@link CircuitBreaker} to add. - */ - protected void addCircuitBreakerToMap(String name, CircuitBreaker circuitBreaker) { - circuitBreakerMap.put(name, circuitBreaker); - } - - /** - * Get the property name for a circuit name and key. - * @param name the circuit name. - * @param key the property key. - * @return the property name. - */ - private String getPropertyName(String name, String key) { - return CONFIG_KEY_PREFIX + '.' + name + '.' + key; - } - - /** - * Get an integer property override value. - * @param name the {@link CircuitBreaker} name. - * @param key the property override key. - * @return the property override value, or null if it is not found. - */ - private Integer getIntegerPropertyOverrideValue(String name, String key) { - if (properties != null) { - String propertyName = getPropertyName(name, key); - - String propertyOverrideValue = properties.getProperty(propertyName); - - if (propertyOverrideValue != null) { - try { - return Integer.parseInt(propertyOverrideValue); - } - catch (NumberFormatException e) { - logger.error("Could not parse property override key={}, value={}", - key, propertyOverrideValue); - } - } - } - return null; - } - - /** - * Get an {@link Long} property override value. - * @param name the {@link CircuitBreaker} name. - * @param key the property override key. - * @return the property override value, or null if it is not found. - */ - private Long getLongPropertyOverrideValue(String name, String key) { - if (properties != null) { - String propertyName = getPropertyName(name, key); - - String propertyOverrideValue = properties.getProperty(propertyName); - - if (propertyOverrideValue != null) { - try { - return Long.parseLong(propertyOverrideValue); - } - catch (NumberFormatException e) { - logger.error("Could not parse property override key={}, value={}", - key, propertyOverrideValue); - } - } - } - return null; - } + public static final String CONFIG_KEY_PREFIX = "circuit"; + public static final String LIMIT_KEY = "limit"; + public static final String WINDOWMILLIS_KEY = "windowMillis"; + public static final String RESETMILLIS_KEY = "resetMillis"; + + private final ConcurrentHashMap circuitBreakerMap = new ConcurrentHashMap(); + + private Properties properties; + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + /** + * Create a new {@link CircuitBreaker} and map it to the provided name. If the + * CircuitBreaker already exists, then the existing instance is returned. + * + * @param name the name of the {@link CircuitBreaker} + * @param config the {@link CircuitBreakerConfig} with the configuration values. + * @return the created {@link CircuitBreaker} + */ + public synchronized CircuitBreaker createCircuitBreaker(String name, CircuitBreakerConfig config) { + CircuitBreaker circuitBreaker = findCircuitBreaker(name); + + if (circuitBreaker == null) { + circuitBreaker = new CircuitBreaker(name); + + configureCircuitBreaker(name, circuitBreaker, config); + addCircuitBreakerToMap(name, circuitBreaker); + } + + return circuitBreaker; + } + + /** + * Set the {@link Properties} object to search for {@link CircuitBreaker} + * property override values. The override values can be specified as:
+ * circuit.{circuit_name}.limit
+ * circuit.{circuit_name}.resetmillis
+ * circuit.{circuit_name}.windowmillis + * + * @param properties the {@link Properties} object to search. + */ + public void setProperties(Properties properties) { + this.properties = properties; + } + + /** + * Find an existing {@link CircuitBreaker} + * + * @param name the value for the {@link CircuitBreaker} + * @return the found {@link CircuitBreaker}, or null if it is not found. + */ + public CircuitBreaker findCircuitBreaker(String name) { + return circuitBreakerMap.get(name); + } + + /** + * Get the {@link Set} of created {@link CircuitBreaker} names. + * + * @return the {@link Set} of names. + */ + public Set getCircuitBreakerNames() { + return circuitBreakerMap.keySet(); + } + + protected void configureCircuitBreaker(String name, CircuitBreaker circuit, CircuitBreakerConfig config) { + + long resetMillis = config.getResetMillis(); + Long resetMillisOverride = getLongPropertyOverrideValue(name, RESETMILLIS_KEY); + if (resetMillisOverride != null) { + resetMillis = resetMillisOverride; + } + + FailureInterpreter fi = config.getFailureInterpreter(); + circuit.setFailureInterpreter(fi); + + if (resetMillis > 0) { + circuit.setResetMillis(resetMillis); + } + + if (fi instanceof DefaultFailureInterpreter) { + configureDefaultFailureInterpreter(name, resetMillis, circuit); + } else { + logger.info("Created CircuitBreaker '{}', resetMillis={}", new Object[] { name, resetMillis }); + } + } + + private void configureDefaultFailureInterpreter(String name, long resetMillis, CircuitBreaker circuit) { + DefaultFailureInterpreter fi = (DefaultFailureInterpreter) circuit.getFailureInterpreter(); + + Integer limitOverride = getIntegerPropertyOverrideValue(name, LIMIT_KEY); + + if (limitOverride != null) { + fi.setLimit(limitOverride); + } + + Long windowMillisOverride = getLongPropertyOverrideValue(name, WINDOWMILLIS_KEY); + + if (windowMillisOverride != null) { + fi.setWindowMillis(windowMillisOverride); + } + + logger.info("Created CircuitBreaker '{}', limit={}, windowMillis={}, resetMillis={}", + new Object[] { name, fi.getLimit(), fi.getWindowMillis(), resetMillis }); + } + + /** + * Add a {@link CircuitBreaker} to the map. + * + * @param name the name for the {@link CircuitBreaker} + * @param circuitBreaker the {@link CircuitBreaker} to add. + */ + protected void addCircuitBreakerToMap(String name, CircuitBreaker circuitBreaker) { + circuitBreakerMap.put(name, circuitBreaker); + } + + /** + * Get the property name for a circuit name and key. + * + * @param name the circuit name. + * @param key the property key. + * @return the property name. + */ + private String getPropertyName(String name, String key) { + return CONFIG_KEY_PREFIX + '.' + name + '.' + key; + } + + /** + * Get an integer property override value. + * + * @param name the {@link CircuitBreaker} name. + * @param key the property override key. + * @return the property override value, or null if it is not found. + */ + private Integer getIntegerPropertyOverrideValue(String name, String key) { + if (properties != null) { + String propertyName = getPropertyName(name, key); + + String propertyOverrideValue = properties.getProperty(propertyName); + + if (propertyOverrideValue != null) { + try { + return Integer.parseInt(propertyOverrideValue); + } catch (NumberFormatException e) { + logger.error("Could not parse property override key={}, value={}", key, propertyOverrideValue); + } + } + } + return null; + } + + /** + * Get an {@link Long} property override value. + * + * @param name the {@link CircuitBreaker} name. + * @param key the property override key. + * @return the property override value, or null if it is not found. + */ + private Long getLongPropertyOverrideValue(String name, String key) { + if (properties != null) { + String propertyName = getPropertyName(name, key); + + String propertyOverrideValue = properties.getProperty(propertyName); + + if (propertyOverrideValue != null) { + try { + return Long.parseLong(propertyOverrideValue); + } catch (NumberFormatException e) { + logger.error("Could not parse property override key={}, value={}", key, propertyOverrideValue); + } + } + } + return null; + } } - diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerNotificationCallback.java b/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerNotificationCallback.java index 9c9966c2..d2581285 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerNotificationCallback.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/CircuitBreakerNotificationCallback.java @@ -17,18 +17,17 @@ package org.fishwife.jrugged; /** - * Allows outside interested parties to register their interest in - * the state changes of a {@link CircuitBreaker}. The callback allows - * the listeners to then take action based on what the state change - * was. + * Allows outside interested parties to register their interest in the state + * changes of a {@link CircuitBreaker}. The callback allows the listeners to + * then take action based on what the state change was. */ public abstract class CircuitBreakerNotificationCallback { - /** - * The method needing an application specific implementation - * to deal with a change in {@link CircuitBreaker} state. - * - * @param s The new status of the {@link CircuitBreaker} - */ - public abstract void notify(Status s); + /** + * The method needing an application specific implementation to deal with a + * change in {@link CircuitBreaker} state. + * + * @param s The new status of the {@link CircuitBreaker} + */ + public abstract void notify(Status s); } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/Clock.java b/jrugged-core/src/main/java/org/fishwife/jrugged/Clock.java index b2521f67..317be0ea 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/Clock.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/Clock.java @@ -21,11 +21,11 @@ */ public interface Clock { - /** - * Returns the current time in milliseconds. - * - * @return long - */ - long currentTimeMillis(); + /** + * Returns the current time in milliseconds. + * + * @return long + */ + long currentTimeMillis(); } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/ConstantFlowRegulator.java b/jrugged-core/src/main/java/org/fishwife/jrugged/ConstantFlowRegulator.java index 995e8a35..f0ac71a2 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/ConstantFlowRegulator.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/ConstantFlowRegulator.java @@ -20,170 +20,174 @@ public class ConstantFlowRegulator implements ServiceWrapper { - /** - * By default a value of -1 allows all requests to go through - * unmolested. - */ - private int requestPerSecondThreshold = -1; - - /** - * - */ - private long deltaWaitTimeMillis = 0; - - /** - * - */ - private long lastRequestOccurance = 0; - - ConstantFlowRegulatorExceptionMapper exceptionMapper; - - public ConstantFlowRegulator() { - } - - public ConstantFlowRegulator(int requestsPerSecond) { - requestPerSecondThreshold = requestsPerSecond; - calculateDeltaWaitTime(); - } - - public ConstantFlowRegulator(int requestsPerSecond, - ConstantFlowRegulatorExceptionMapper mapper) { - requestPerSecondThreshold = requestsPerSecond; - calculateDeltaWaitTime(); - exceptionMapper = mapper; - } - - /** - * Wrap the given service call with the {@link ConstantFlowRegulator} - * protection logic. - * @param c the {@link Callable} to attempt - * - * @return whatever c would return on success - * - * @throws FlowRateExceededException if the total requests per second - * through the flow regulator exceeds the configured value - * @throws Exception if c throws one during - * execution - */ - public T invoke(Callable c) throws Exception { - if (canProceed()) { - return c.call(); - } - else { - throw mapException(new FlowRateExceededException()); - } - } - - /** Wrap the given service call with the {@link ConstantFlowRegulator} - * protection logic. - * @param r the {@link Runnable} to attempt - * - * @throws FlowRateExceededException if the total requests per second - * through the flow regulator exceeds the configured value - * @throws Exception if c throws one during - * execution - */ - public void invoke(Runnable r) throws Exception { - if (canProceed()) { - r.run(); - } - else { - throw mapException(new FlowRateExceededException()); - } - } - - /** Wrap the given service call with the {@link ConstantFlowRegulator} - * protection logic. - * @param r the {@link Runnable} to attempt - * @param result what to return after r succeeds - * - * @return result - * - * @throws FlowRateExceededException if the total requests per second - * through the flow regulator exceeds the configured value - * @throws Exception if c throws one during - * execution - */ - public T invoke(Runnable r, T result) throws Exception { - if (canProceed()) { - r.run(); - return result; - } - else { - throw mapException(new FlowRateExceededException()); - } - } - - protected synchronized boolean canProceed() { - if (requestPerSecondThreshold == -1) { - return true; - } - - if (lastRequestOccurance == 0) { - lastRequestOccurance = System.currentTimeMillis(); - return true; - } - - if ((System.currentTimeMillis() - lastRequestOccurance) > deltaWaitTimeMillis) { - lastRequestOccurance = System.currentTimeMillis(); - return true; - } - - return false; - } - - /** - * Configures number of requests per second to allow through this flow regulator - * onto a configured back end service. - * - * @param i the requests per second threshold for this flow regulator - */ - public void setRequestPerSecondThreshold(int i) { - this.requestPerSecondThreshold = i; - calculateDeltaWaitTime(); - } - - /** - * Returns the currently configured number of requests per second to allow - * through this flow regulator onto a configured back end service. - * - * @return int the currently configured requests per second threshold - */ - public int getRequestPerSecondThreshold() { - return requestPerSecondThreshold; - } - - /** - * Get the helper that converts {@link FlowRateExceededException}s into - * application-specific exceptions. - * - * @return {@link ConstantFlowRegulatorExceptionMapper} my converter object, or - * null if one is not currently set. - */ - public ConstantFlowRegulatorExceptionMapper getExceptionMapper(){ - return this.exceptionMapper; - } - - /** - * A helper that converts {@link FlowRateExceededException} into a known - * 'application' exception. - * - * @param mapper my converter object - */ - public void setExceptionMapper(ConstantFlowRegulatorExceptionMapper mapper) { - this.exceptionMapper = mapper; - } - - private void calculateDeltaWaitTime() { - if (requestPerSecondThreshold > 0) { - deltaWaitTimeMillis = 1000L / requestPerSecondThreshold; - } - } - - private Exception mapException(FlowRateExceededException e) { - if (exceptionMapper == null) - return e; - - return exceptionMapper.map(this, e); - } + /** + * By default a value of -1 allows all requests to go through unmolested. + */ + private int requestPerSecondThreshold = -1; + + /** + * + */ + private long deltaWaitTimeMillis = 0; + + /** + * + */ + private long lastRequestOccurance = 0; + + ConstantFlowRegulatorExceptionMapper exceptionMapper; + + public ConstantFlowRegulator() { + } + + public ConstantFlowRegulator(int requestsPerSecond) { + requestPerSecondThreshold = requestsPerSecond; + calculateDeltaWaitTime(); + } + + public ConstantFlowRegulator(int requestsPerSecond, + ConstantFlowRegulatorExceptionMapper mapper) { + requestPerSecondThreshold = requestsPerSecond; + calculateDeltaWaitTime(); + exceptionMapper = mapper; + } + + /** + * Wrap the given service call with the {@link ConstantFlowRegulator} protection + * logic. + * + * @param c the {@link Callable} to attempt + * + * @return whatever c would return on success + * + * @throws FlowRateExceededException if the total requests per second through + * the flow regulator exceeds the configured + * value + * @throws Exception if c throws one during + * execution + */ + public T invoke(Callable c) throws Exception { + if (canProceed()) { + return c.call(); + } else { + throw mapException(new FlowRateExceededException()); + } + } + + /** + * Wrap the given service call with the {@link ConstantFlowRegulator} protection + * logic. + * + * @param r the {@link Runnable} to attempt + * + * @throws FlowRateExceededException if the total requests per second through + * the flow regulator exceeds the configured + * value + * @throws Exception if c throws one during + * execution + */ + public void invoke(Runnable r) throws Exception { + if (canProceed()) { + r.run(); + } else { + throw mapException(new FlowRateExceededException()); + } + } + + /** + * Wrap the given service call with the {@link ConstantFlowRegulator} protection + * logic. + * + * @param r the {@link Runnable} to attempt + * @param result what to return after r succeeds + * + * @return result + * + * @throws FlowRateExceededException if the total requests per second through + * the flow regulator exceeds the configured + * value + * @throws Exception if c throws one during + * execution + */ + public T invoke(Runnable r, T result) throws Exception { + if (canProceed()) { + r.run(); + return result; + } else { + throw mapException(new FlowRateExceededException()); + } + } + + protected synchronized boolean canProceed() { + if (requestPerSecondThreshold == -1) { + return true; + } + + if (lastRequestOccurance == 0) { + lastRequestOccurance = System.currentTimeMillis(); + return true; + } + + if ((System.currentTimeMillis() - lastRequestOccurance) > deltaWaitTimeMillis) { + lastRequestOccurance = System.currentTimeMillis(); + return true; + } + + return false; + } + + /** + * Configures number of requests per second to allow through this flow regulator + * onto a configured back end service. + * + * @param i the requests per second threshold for this flow regulator + */ + public void setRequestPerSecondThreshold(int i) { + this.requestPerSecondThreshold = i; + calculateDeltaWaitTime(); + } + + /** + * Returns the currently configured number of requests per second to allow + * through this flow regulator onto a configured back end service. + * + * @return int the currently configured requests per second threshold + */ + public int getRequestPerSecondThreshold() { + return requestPerSecondThreshold; + } + + /** + * Get the helper that converts {@link FlowRateExceededException}s into + * application-specific exceptions. + * + * @return {@link ConstantFlowRegulatorExceptionMapper} my converter object, or + * null if one is not currently set. + */ + public ConstantFlowRegulatorExceptionMapper getExceptionMapper() { + return this.exceptionMapper; + } + + /** + * A helper that converts {@link FlowRateExceededException} into a known + * 'application' exception. + * + * @param mapper my converter object + */ + public void setExceptionMapper(ConstantFlowRegulatorExceptionMapper mapper) { + this.exceptionMapper = mapper; + } + + private void calculateDeltaWaitTime() { + if (requestPerSecondThreshold > 0) { + deltaWaitTimeMillis = 1000L / requestPerSecondThreshold; + } + } + + private Exception mapException(FlowRateExceededException e) { + if (exceptionMapper == null) + return e; + + return exceptionMapper.map(this, e); + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/ConstantFlowRegulatorExceptionMapper.java b/jrugged-core/src/main/java/org/fishwife/jrugged/ConstantFlowRegulatorExceptionMapper.java index 4df46b02..c61da52f 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/ConstantFlowRegulatorExceptionMapper.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/ConstantFlowRegulatorExceptionMapper.java @@ -15,21 +15,23 @@ package org.fishwife.jrugged; /** - * Allows the user to map the standard {@link org.fishwife.jrugged.FlowRateExceededException} - * thrown by a {@link org.fishwife.jrugged.ConstantFlowRegulator} into an application + * Allows the user to map the standard + * {@link org.fishwife.jrugged.FlowRateExceededException} thrown by a + * {@link org.fishwife.jrugged.ConstantFlowRegulator} into an application * specific exception. * * @param is the application specific exception type. */ public interface ConstantFlowRegulatorExceptionMapper { - /** - * Turns a {@link org.fishwife.jrugged.FlowRateExceededException} into the desired exception (T) - * - * @param flowRegulator the {@link org.fishwife.jrugged.ConstantFlowRegulator} - * @param e the FlowRateExceededException I get - * @return the {@link Exception} I want thrown instead - */ - public T map(ConstantFlowRegulator flowRegulator, FlowRateExceededException e); + /** + * Turns a {@link org.fishwife.jrugged.FlowRateExceededException} into the + * desired exception (T) + * + * @param flowRegulator the {@link org.fishwife.jrugged.ConstantFlowRegulator} + * @param e the FlowRateExceededException I get + * @return the {@link Exception} I want thrown instead + */ + public T map(ConstantFlowRegulator flowRegulator, FlowRateExceededException e); } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/DefaultFailureInterpreter.java b/jrugged-core/src/main/java/org/fishwife/jrugged/DefaultFailureInterpreter.java index d89826c9..63c7c31d 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/DefaultFailureInterpreter.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/DefaultFailureInterpreter.java @@ -21,175 +21,179 @@ import java.util.Set; /** - * Trips a {@link CircuitBreaker} if the number of failures in a given - * time window exceed a specified tolerance. By default, all {@link - * Throwable} occurrences will be considered failures. + * Trips a {@link CircuitBreaker} if the number of failures in a given time + * window exceed a specified tolerance. By default, all {@link Throwable} + * occurrences will be considered failures. */ public final class DefaultFailureInterpreter implements FailureInterpreter { - private Set> ignore = new HashSet>(); - private int limit = 0; - private long windowMillis = 0; - - private WindowedEventCounter counter; - - @SuppressWarnings("unchecked") - private static Class[] defaultIgnore = - new Class[0]; - - /** - * Default constructor. Any {@link Throwable} will cause the breaker to trip. - */ - public DefaultFailureInterpreter() { - setIgnore(defaultIgnore); - } - - /** - * Constructor that allows a tolerance for a certain number of - * failures within a given window of time without tripping. - * @param limit the number of failures that will be tolerated - * (i.e. the number of failures has to be strictly greater - * than this number in order to trip the breaker). For - * example, if the limit is 3, the fourth failure during - * the window will cause the breaker to trip. - * @param windowMillis length of the window in milliseconds - */ - public DefaultFailureInterpreter(int limit, long windowMillis) { - setIgnore(defaultIgnore); - setLimit(limit); - setWindowMillis(windowMillis); - initCounter(); - } - - /** - * Constructor where we specify certain {@link Throwable} classes - * that will be ignored by the breaker and not be treated as - * failures (they will be passed through transparently without - * causing the breaker to trip). - * @param ignore an array of {@link Throwable} classes that will - * be ignored. Any given Throwable that is a - * subclass of one of these classes will be ignored. - */ - public DefaultFailureInterpreter(Class[] ignore) { - setIgnore(ignore); - } - - /** - * Constructor where we specify tolerance and a set of ignored failures. - * - * @param ignore an array of {@link Throwable} classes that will - * be ignored. Any given Throwable that is a - * subclass of one of these classes will be ignored. - * @param limit the number of failures that will be tolerated - * (i.e. the number of failures has to be strictly greater - * than this number in order to trip the breaker). For - * example, if the limit is 3, the fourth failure during - * the window will cause the breaker to trip. - * @param windowMillis length of the window in milliseconds - */ - public DefaultFailureInterpreter(Class[] ignore, - int limit, long windowMillis) { - setIgnore(ignore); - setLimit(limit); - setWindowMillis(windowMillis); - initCounter(); - } - - private boolean hasWindowConditions() { - return this.limit > 0 && this.windowMillis > 0; - } - - public boolean shouldTrip(Throwable cause) { - for(Class clazz : ignore) { - if (clazz.isInstance(cause)) { - return false; - } - } - - // if Exception is of specified type, and window conditions exist, - // keep circuit open unless exception threshold has passed - if (hasWindowConditions()) { - counter.mark(); - // Trip if the exception count has passed the limit - return (counter.tally() > limit); - } - - return true; - } - - private void initCounter() { - if (hasWindowConditions()) { - int capacity = limit + 1; - if (counter == null) { - this.counter = new WindowedEventCounter(capacity,windowMillis); - } else { - if (capacity != counter.getCapacity()) { - counter.setCapacity(capacity); - } - - if (windowMillis != counter.getWindowMillis()) { - counter.setWindowMillis(windowMillis); - } - } - } else { - // we're not under windowConditions, no counter needed - counter = null; - } - } - - /** - * Returns the set of currently ignored {@link Throwable} classes. - * @return {@link Set} - */ - public Set> getIgnore(){ - return this.ignore; - } - - /** - * Specifies an array of {@link Throwable} classes to ignore. These will not - * be considered failures. - * @param ignore array of {@link Class} objects - */ - public synchronized void setIgnore(Class[] ignore) { - this.ignore = new HashSet>(Arrays.asList(ignore)); - } - - /** - * Returns the current number of failures within the window that will be tolerated - * without tripping the breaker. - * @return int - */ - public int getLimit(){ - return this.limit; - } - - /** - * Specifies the number of tolerated failures within the - * configured time window. If limit is set to n then the - * (n+1)th failure will trip the breaker. Mutating the - * limit at runtime can reset previous failure counts. - * @param limit int - */ - public void setLimit(int limit) { - this.limit=limit; - initCounter(); - } - - /** - * Returns the length of the currently configured tolerance window - * in milliseconds. - * @return long - */ - public long getWindowMillis(){ - return this.windowMillis; - } - - /** - * Specifies the length of the tolerance window in milliseconds. - * @param windowMillis long - */ - public void setWindowMillis(long windowMillis) { - this.windowMillis=windowMillis; - initCounter(); - } + private Set> ignore = new HashSet>(); + private int limit = 0; + private long windowMillis = 0; + + private WindowedEventCounter counter; + + @SuppressWarnings("unchecked") + private static Class[] defaultIgnore = new Class[0]; + + /** + * Default constructor. Any {@link Throwable} will cause the breaker to trip. + */ + public DefaultFailureInterpreter() { + setIgnore(defaultIgnore); + } + + /** + * Constructor that allows a tolerance for a certain number of failures within a + * given window of time without tripping. + * + * @param limit the number of failures that will be tolerated (i.e. the + * number of failures has to be strictly greater + * than this number in order to trip the breaker). For + * example, if the limit is 3, the fourth failure during the + * window will cause the breaker to trip. + * @param windowMillis length of the window in milliseconds + */ + public DefaultFailureInterpreter(int limit, long windowMillis) { + setIgnore(defaultIgnore); + setLimit(limit); + setWindowMillis(windowMillis); + initCounter(); + } + + /** + * Constructor where we specify certain {@link Throwable} classes that will be + * ignored by the breaker and not be treated as failures (they will be passed + * through transparently without causing the breaker to trip). + * + * @param ignore an array of {@link Throwable} classes that will be ignored. Any + * given Throwable that is a subclass of one of these + * classes will be ignored. + */ + public DefaultFailureInterpreter(Class[] ignore) { + setIgnore(ignore); + } + + /** + * Constructor where we specify tolerance and a set of ignored failures. + * + * @param ignore an array of {@link Throwable} classes that will be + * ignored. Any given Throwable that is a + * subclass of one of these classes will be ignored. + * @param limit the number of failures that will be tolerated (i.e. the + * number of failures has to be strictly greater + * than this number in order to trip the breaker). For + * example, if the limit is 3, the fourth failure during the + * window will cause the breaker to trip. + * @param windowMillis length of the window in milliseconds + */ + public DefaultFailureInterpreter(Class[] ignore, int limit, long windowMillis) { + setIgnore(ignore); + setLimit(limit); + setWindowMillis(windowMillis); + initCounter(); + } + + private boolean hasWindowConditions() { + return this.limit > 0 && this.windowMillis > 0; + } + + public boolean shouldTrip(Throwable cause) { + for (Class clazz : ignore) { + if (clazz.isInstance(cause)) { + return false; + } + } + + // if Exception is of specified type, and window conditions exist, + // keep circuit open unless exception threshold has passed + if (hasWindowConditions()) { + counter.mark(); + // Trip if the exception count has passed the limit + return (counter.tally() > limit); + } + + return true; + } + + private void initCounter() { + if (hasWindowConditions()) { + int capacity = limit + 1; + if (counter == null) { + this.counter = new WindowedEventCounter(capacity, windowMillis); + } else { + if (capacity != counter.getCapacity()) { + counter.setCapacity(capacity); + } + + if (windowMillis != counter.getWindowMillis()) { + counter.setWindowMillis(windowMillis); + } + } + } else { + // we're not under windowConditions, no counter needed + counter = null; + } + } + + /** + * Returns the set of currently ignored {@link Throwable} classes. + * + * @return {@link Set} + */ + public Set> getIgnore() { + return this.ignore; + } + + /** + * Specifies an array of {@link Throwable} classes to ignore. These will not be + * considered failures. + * + * @param ignore array of {@link Class} objects + */ + public synchronized void setIgnore(Class[] ignore) { + this.ignore = new HashSet>(Arrays.asList(ignore)); + } + + /** + * Returns the current number of failures within the window that will be + * tolerated without tripping the breaker. + * + * @return int + */ + public int getLimit() { + return this.limit; + } + + /** + * Specifies the number of tolerated failures within the configured time window. + * If limit is set to n then the (n+1)th failure will trip the + * breaker. Mutating the limit at runtime can reset previous failure counts. + * + * @param limit int + */ + public void setLimit(int limit) { + this.limit = limit; + initCounter(); + } + + /** + * Returns the length of the currently configured tolerance window in + * milliseconds. + * + * @return long + */ + public long getWindowMillis() { + return this.windowMillis; + } + + /** + * Specifies the length of the tolerance window in milliseconds. + * + * @param windowMillis long + */ + public void setWindowMillis(long windowMillis) { + this.windowMillis = windowMillis; + initCounter(); + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/FailureInterpreter.java b/jrugged-core/src/main/java/org/fishwife/jrugged/FailureInterpreter.java index c34c3317..96470aff 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/FailureInterpreter.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/FailureInterpreter.java @@ -15,16 +15,19 @@ package org.fishwife.jrugged; /** - * A {@link FailureInterpreter} is a helper class that can be used by - * a {@link CircuitBreaker} to determine whether a given failure - * should cause the breaker to trip. + * A {@link FailureInterpreter} is a helper class that can be used by a + * {@link CircuitBreaker} to determine whether a given failure should cause the + * breaker to trip. */ public interface FailureInterpreter { - /** Returns whether the governed {@link CircuitBreaker} should - * trip OPEN as a result of this failure. - * @param oops the {@link Throwable} failure that occurred - * @return boolean true iff the circuit should trip */ - boolean shouldTrip(Throwable oops); + /** + * Returns whether the governed {@link CircuitBreaker} should trip OPEN as a + * result of this failure. + * + * @param oops the {@link Throwable} failure that occurred + * @return boolean true iff the circuit should trip + */ + boolean shouldTrip(Throwable oops); } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/FlowMeter.java b/jrugged-core/src/main/java/org/fishwife/jrugged/FlowMeter.java index db406017..40dda8bd 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/FlowMeter.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/FlowMeter.java @@ -16,69 +16,71 @@ */ package org.fishwife.jrugged; -/** This class uses a raw {@link RequestCounter} to compute request rates - * over time for total rate of request, rate of successful requests, and - * rate of failed requests. +/** + * This class uses a raw {@link RequestCounter} to compute request rates over + * time for total rate of request, rate of successful requests, and rate of + * failed requests. */ public class FlowMeter { - private RequestCounter counter; + private RequestCounter counter; - private long lastTotal; - private long lastSuccesses; - private long lastFailures; - private long lastSampleMillis; - private double[] lastKnownRates = new double[3]; + private long lastTotal; + private long lastSuccesses; + private long lastFailures; + private long lastSampleMillis; + private double[] lastKnownRates = new double[3]; - /** - * Constructs a {@link FlowMeter}. - * @param counter the {@link RequestCounter} to calculate request - * rates from - */ - public FlowMeter(RequestCounter counter) { - this.counter = counter; - } + /** + * Constructs a {@link FlowMeter}. + * + * @param counter the {@link RequestCounter} to calculate request rates from + */ + public FlowMeter(RequestCounter counter) { + this.counter = counter; + } - /** - * Calculates requests per second. - * - * @param events how many have occured - * @param t time - * @return double rate - */ - private double rate(long events, long t) { - return ((double)events / (double)t) * 1000.0; - } + /** + * Calculates requests per second. + * + * @param events how many have occured + * @param t time + * @return double rate + */ + private double rate(long events, long t) { + return ((double) events / (double) t) * 1000.0; + } - /** - * Takes a sample of the request rates. Calculations are based on - * differences in request counts since the last call to - * sample(). - * @return an array of three doubles: total requests per - * second, successful requests per second, failed requests per - * second. If this is the first sample, all three rates will be - * reported as zero requests per second. - */ - public synchronized double[] sample() { - long[] currCounts = counter.sample(); - long now = System.currentTimeMillis(); + /** + * Takes a sample of the request rates. Calculations are based on differences in + * request counts since the last call to sample(). + * + * @return an array of three doubles: total requests per second, + * successful requests per second, failed requests per second. If this + * is the first sample, all three rates will be reported as zero + * requests per second. + */ + public synchronized double[] sample() { + long[] currCounts = counter.sample(); + long now = System.currentTimeMillis(); - if (lastSampleMillis != 0) { - long deltaTime = now - lastSampleMillis; + if (lastSampleMillis != 0) { + long deltaTime = now - lastSampleMillis; - if (deltaTime == 0) return lastKnownRates; + if (deltaTime == 0) + return lastKnownRates; - lastKnownRates[0] = rate(currCounts[0] - lastTotal, deltaTime); - lastKnownRates[1] = rate(currCounts[1] - lastSuccesses, deltaTime); - lastKnownRates[2] = rate(currCounts[2] - lastFailures, deltaTime); - } else { - lastKnownRates[0] = lastKnownRates[1] = lastKnownRates[2] = 0.0; - } + lastKnownRates[0] = rate(currCounts[0] - lastTotal, deltaTime); + lastKnownRates[1] = rate(currCounts[1] - lastSuccesses, deltaTime); + lastKnownRates[2] = rate(currCounts[2] - lastFailures, deltaTime); + } else { + lastKnownRates[0] = lastKnownRates[1] = lastKnownRates[2] = 0.0; + } - lastTotal = currCounts[0]; - lastSuccesses = currCounts[1]; - lastFailures = currCounts[2]; - lastSampleMillis = now; + lastTotal = currCounts[0]; + lastSuccesses = currCounts[1]; + lastFailures = currCounts[2]; + lastSampleMillis = now; - return lastKnownRates; - } + return lastKnownRates; + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/FlowRateExceededException.java b/jrugged-core/src/main/java/org/fishwife/jrugged/FlowRateExceededException.java index 52318b78..7e5f0387 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/FlowRateExceededException.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/FlowRateExceededException.java @@ -17,21 +17,21 @@ package org.fishwife.jrugged; public class FlowRateExceededException extends Exception { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private double rate = 0; - private double maximumRate = 0; + private double rate = 0; + private double maximumRate = 0; - public FlowRateExceededException() { - } + public FlowRateExceededException() { + } - public FlowRateExceededException(String message, double rate, double maximumRate) { - super(message); - this.rate = rate; - this.maximumRate = maximumRate; - } + public FlowRateExceededException(String message, double rate, double maximumRate) { + super(message); + this.rate = rate; + this.maximumRate = maximumRate; + } - public FlowRateExceededException(double rate, double maximumRate) { - this("Rate Exceeded (Max=" + maximumRate + ", Current=" + rate + ")", rate, maximumRate); - } + public FlowRateExceededException(double rate, double maximumRate) { + this("Rate Exceeded (Max=" + maximumRate + ", Current=" + rate + ")", rate, maximumRate); + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/Initializable.java b/jrugged-core/src/main/java/org/fishwife/jrugged/Initializable.java index 854fa357..85fc9d33 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/Initializable.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/Initializable.java @@ -16,39 +16,41 @@ */ package org.fishwife.jrugged; -/** Clients who use the {@link Initializer} should implement this interface. - * This lets them do, for example: - *
- *    new Initializer(this).initialize();
- *  
- * and the Initializer will take care of retrying an initialization. +/** + * Clients who use the {@link Initializer} should implement this interface. This + * lets them do, for example: + * + *
+ * new Initializer(this).initialize();
+ * 
+ * + * and the Initializer will take care of retrying an initialization. * - * This pattern is good for subsystems that need to talk to something - * that is currently down at startup time. For example, if your database - * is down when your app server starts up, this can let your app server - * keep trying to connect without getting wedged or needing to restart. + * This pattern is good for subsystems that need to talk to something that is + * currently down at startup time. For example, if your database is down when + * your app server starts up, this can let your app server keep trying to + * connect without getting wedged or needing to restart. */ public interface Initializable { - /** - * Makes an initialization attempt. If an exception is thrown, assume - * attempt failed. - * - * @throws Exception if the attempt to init fails due to wrapped item throwing - */ - public void tryInit() throws Exception; + /** + * Makes an initialization attempt. If an exception is thrown, assume attempt + * failed. + * + * @throws Exception if the attempt to init fails due to wrapped item throwing + */ + public void tryInit() throws Exception; - /** - * Called by the initializer after background initialization succeeds. - * Can be used to mark the client as "ready to serve" or "active", - * etc. - */ - public void afterInit(); + /** + * Called by the initializer after background initialization succeeds. Can be + * used to mark the client as "ready to serve" or "active", etc. + */ + public void afterInit(); - /** - * In the event that I have attempted to initialize, but I fail having - * exceeded my max number of retries to succeed, I will get a callback - * into this method indicating I have met or Exceeded my attempts to start - */ - public void configuredRetriesMetOrExceededWithoutSuccess(); + /** + * In the event that I have attempted to initialize, but I fail having exceeded + * my max number of retries to succeed, I will get a callback into this method + * indicating I have met or Exceeded my attempts to start + */ + public void configuredRetriesMetOrExceededWithoutSuccess(); } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/Initializer.java b/jrugged-core/src/main/java/org/fishwife/jrugged/Initializer.java index 98c0b886..de3c5325 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/Initializer.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/Initializer.java @@ -18,14 +18,15 @@ /** * An {@link Initializer} allows a client to retry failed service - * initializations in the background. For example, the initial - * connection to a remote service may fail; the Initializer will take - * responsibility for continuing to retry that connection in a - * background thread (so that other services can try to initialize - * in the meantime). When initialization succeeds, the background - * thread terminates and the client service can enter normal operation. + * initializations in the background. For example, the initial connection to a + * remote service may fail; the Initializer will take responsibility for + * continuing to retry that connection in a background thread (so that other + * services can try to initialize in the meantime). When initialization + * succeeds, the background thread terminates and the client service can enter + * normal operation. * * Sample usage: + * *
  * public class Service implements Initializable, Monitorable {
  *
@@ -61,106 +62,106 @@
  *       ... // Do something interesting now.
  *    }
  * }
- *  
+ *
*/ public class Initializer implements Runnable { - /** - * By default, keep trying to initialize forever. - */ - private int maxRetries = Integer.MAX_VALUE; - - /** - * Number of initialization attempts we have made. - */ - private int numAttempts = 0; - - /** - * Retry an initialization every 60 seconds by default. - */ - private long retryMillis = 60 * 1000L; - - /** - * This is the guy we're trying to initialize. - */ - private Initializable client; - - /** - * Current status. - */ - private boolean initialized = false; - - /** - * Background initializer thread. - */ - private Thread thread; - - /** - * Set this to true and interrupt thread to cleanly shutdown. - */ - private boolean cancelled = false; - - public Initializer(Initializable client) { - this.client = client; - } - - /** - * Sets up the initialization retry process. - */ - public void initialize() { - thread = new Thread(this); - thread.start(); - } - - /** - * Shuts down the background retry process. If you are using the - * Spring framework, for example, if the client implements - * DisposableBean you can have the destroy() method of the client - * call this method to cleanly shutdown. - */ - public void destroy() { - cancelled = true; - if (thread != null) thread.interrupt(); - } - - public void run() { - while (!initialized && numAttempts < maxRetries && !cancelled) { - try { - numAttempts++; - client.tryInit(); - initialized = true; - client.afterInit(); - } catch (Exception e) { - try { - Thread.sleep(retryMillis); - } catch (InterruptedException ie) { - // nop - } - } - } - - if(!initialized && (numAttempts >= maxRetries) && !cancelled) { - client.configuredRetriesMetOrExceededWithoutSuccess(); - } - } - - public boolean isInitialized() { - return initialized; - } - - public boolean isCancelled() { - return cancelled; - } - - public int getNumAttempts() { - return numAttempts; - } - - public void setMaxRetries(int n) { - maxRetries = n; - } - - public void setRetryMillis(long m) { - retryMillis = m; - } + /** + * By default, keep trying to initialize forever. + */ + private int maxRetries = Integer.MAX_VALUE; + + /** + * Number of initialization attempts we have made. + */ + private int numAttempts = 0; + + /** + * Retry an initialization every 60 seconds by default. + */ + private long retryMillis = 60 * 1000L; + + /** + * This is the guy we're trying to initialize. + */ + private Initializable client; + + /** + * Current status. + */ + private boolean initialized = false; + + /** + * Background initializer thread. + */ + private Thread thread; + + /** + * Set this to true and interrupt thread to cleanly shutdown. + */ + private boolean cancelled = false; + + public Initializer(Initializable client) { + this.client = client; + } + + /** + * Sets up the initialization retry process. + */ + public void initialize() { + thread = new Thread(this); + thread.start(); + } + + /** + * Shuts down the background retry process. If you are using the Spring + * framework, for example, if the client implements DisposableBean you can have + * the destroy() method of the client call this method to cleanly shutdown. + */ + public void destroy() { + cancelled = true; + if (thread != null) + thread.interrupt(); + } + + public void run() { + while (!initialized && numAttempts < maxRetries && !cancelled) { + try { + numAttempts++; + client.tryInit(); + initialized = true; + client.afterInit(); + } catch (Exception e) { + try { + Thread.sleep(retryMillis); + } catch (InterruptedException ie) { + // nop + } + } + } + + if (!initialized && (numAttempts >= maxRetries) && !cancelled) { + client.configuredRetriesMetOrExceededWithoutSuccess(); + } + } + + public boolean isInitialized() { + return initialized; + } + + public boolean isCancelled() { + return cancelled; + } + + public int getNumAttempts() { + return numAttempts; + } + + public void setMaxRetries(int n) { + maxRetries = n; + } + + public void setRetryMillis(long m) { + retryMillis = m; + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/LatencyTracker.java b/jrugged-core/src/main/java/org/fishwife/jrugged/LatencyTracker.java index e2851f3e..89fc5995 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/LatencyTracker.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/LatencyTracker.java @@ -18,55 +18,65 @@ import java.util.concurrent.Callable; -/** This is a statistics wrapper that records the latency of requests, - * both for successes and failures. The most recent measurement is - * available for query, and can be polled periodically to determine - * average latencies. +/** + * This is a statistics wrapper that records the latency of requests, both for + * successes and failures. The most recent measurement is available for query, + * and can be polled periodically to determine average latencies. */ public class LatencyTracker implements ServiceWrapper { - private long lastSuccessMillis; - private long lastFailureMillis; + private long lastSuccessMillis; + private long lastFailureMillis; - public T invoke(Callable c) throws Exception { - long start = System.currentTimeMillis(); - try { - T result = c.call(); - lastSuccessMillis = System.currentTimeMillis() - start; - return result; - } catch (Exception e) { - lastFailureMillis = System.currentTimeMillis() - start; - throw e; - } - } + public T invoke(Callable c) throws Exception { + long start = System.currentTimeMillis(); + try { + T result = c.call(); + lastSuccessMillis = System.currentTimeMillis() - start; + return result; + } catch (Exception e) { + lastFailureMillis = System.currentTimeMillis() - start; + throw e; + } + } - public void invoke(Runnable r) throws Exception { - long start = System.currentTimeMillis(); - try { - r.run(); - lastSuccessMillis = System.currentTimeMillis() - start; - } catch (Exception e) { - lastFailureMillis = System.currentTimeMillis() - start; - throw e; - } - } + public void invoke(Runnable r) throws Exception { + long start = System.currentTimeMillis(); + try { + r.run(); + lastSuccessMillis = System.currentTimeMillis() - start; + } catch (Exception e) { + lastFailureMillis = System.currentTimeMillis() - start; + throw e; + } + } - public T invoke(Runnable r, T result) throws Exception { - long start = System.currentTimeMillis(); - try { - r.run(); - lastSuccessMillis = System.currentTimeMillis() - start; - return result; - } catch (Exception e) { - lastFailureMillis = System.currentTimeMillis() - start; - throw e; - } - } + public T invoke(Runnable r, T result) throws Exception { + long start = System.currentTimeMillis(); + try { + r.run(); + lastSuccessMillis = System.currentTimeMillis() - start; + return result; + } catch (Exception e) { + lastFailureMillis = System.currentTimeMillis() - start; + throw e; + } + } - /** Returns how long the last successful request took. - * @return long request service time in milliseconds */ - public long getLastSuccessMillis() { return lastSuccessMillis; } + /** + * Returns how long the last successful request took. + * + * @return long request service time in milliseconds + */ + public long getLastSuccessMillis() { + return lastSuccessMillis; + } - /** Returns how long the last failed request took. - * @return long request service time in milliseconds. */ - public long getLastFailureMillis() { return lastFailureMillis; } + /** + * Returns how long the last failed request took. + * + * @return long request service time in milliseconds. + */ + public long getLastFailureMillis() { + return lastFailureMillis; + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/MonitoredService.java b/jrugged-core/src/main/java/org/fishwife/jrugged/MonitoredService.java index ce358054..1fa45f72 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/MonitoredService.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/MonitoredService.java @@ -19,5 +19,5 @@ * {@link ServiceStatus}. */ public interface MonitoredService { - ServiceStatus getServiceStatus(); + ServiceStatus getServiceStatus(); } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/MovingAverage.java b/jrugged-core/src/main/java/org/fishwife/jrugged/MovingAverage.java index ace2feb9..ef8c61c4 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/MovingAverage.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/MovingAverage.java @@ -17,46 +17,54 @@ package org.fishwife.jrugged; /** - * This class implements an exponential moving average, using the - * algorithm described at http://en.wikipedia.org/wiki/Moving_average. The average does not - * sample itself; it merely computes the new average when updated with - * a sample by an external mechanism. + * This class implements an exponential moving average, using the algorithm + * described at http://en.wikipedia.org/wiki/Moving_average. + * The average does not sample itself; it merely computes the new average when + * updated with a sample by an external mechanism. */ public class MovingAverage { - private long windowMillis; - private long lastMillis; - private double average; + private long windowMillis; + private long lastMillis; + private double average; - /** Construct a {@link MovingAverage}, providing the time window - * we want the average over. For example, providing a value of - * 3,600,000 provides a moving average over the last hour. - * @param windowMillis the length of the sliding window in - * milliseconds */ - public MovingAverage(long windowMillis) { - this.windowMillis = windowMillis; - } + /** + * Construct a {@link MovingAverage}, providing the time window we want the + * average over. For example, providing a value of 3,600,000 provides a moving + * average over the last hour. + * + * @param windowMillis the length of the sliding window in milliseconds + */ + public MovingAverage(long windowMillis) { + this.windowMillis = windowMillis; + } - /** Updates the average with the latest measurement. - * @param sample the latest measurement in the rolling average */ - public synchronized void update(double sample) { - long now = System.currentTimeMillis(); + /** + * Updates the average with the latest measurement. + * + * @param sample the latest measurement in the rolling average + */ + public synchronized void update(double sample) { + long now = System.currentTimeMillis(); - if (lastMillis == 0) { // first sample - average = sample; - lastMillis = now; - return; - } - long deltaTime = now - lastMillis; - double coeff = Math.exp(-1.0 * ((double)deltaTime / windowMillis)); - average = (1.0 - coeff) * sample + coeff * average; + if (lastMillis == 0) { // first sample + average = sample; + lastMillis = now; + return; + } + long deltaTime = now - lastMillis; + double coeff = Math.exp(-1.0 * ((double) deltaTime / windowMillis)); + average = (1.0 - coeff) * sample + coeff * average; - lastMillis = now; - } + lastMillis = now; + } - /** - * Returns the last computed average value. - * - * @return double The calculated avg. - */ - public double getAverage() { return average; } + /** + * Returns the last computed average value. + * + * @return double The calculated avg. + */ + public double getAverage() { + return average; + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/PercentErrPerTimeFailureInterpreter.java b/jrugged-core/src/main/java/org/fishwife/jrugged/PercentErrPerTimeFailureInterpreter.java index e2b2368d..d355c519 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/PercentErrPerTimeFailureInterpreter.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/PercentErrPerTimeFailureInterpreter.java @@ -21,261 +21,277 @@ import java.util.Set; /** - * Trips a {@link org.fishwife.jrugged.CircuitBreaker} if the percentage of failures in a given - * time window exceed a specified tolerance. By default, all {@link - * Throwable} occurrences will be considered failures. + * Trips a {@link org.fishwife.jrugged.CircuitBreaker} if the percentage of + * failures in a given time window exceed a specified tolerance. By default, all + * {@link Throwable} occurrences will be considered failures. */ public final class PercentErrPerTimeFailureInterpreter implements FailureInterpreter { - private Set> ignore = new HashSet>(); - private int percent = 0; - private long windowMillis = 0; - private int requestThreshold = 0; - private long previousRequestHighWaterMark = 0; - - // tracks times when exceptions occurred - private List errorTimes = new LinkedList(); - - // tracks times when exceptions occurred - private List requestCounts = new LinkedList(); - private final Object modificationLock = new Object(); - - private RequestCounter requestCounter; - - private static Class[] defaultIgnore = { }; - - /** - * Default constructor. Any {@link Throwable} will cause the breaker to trip. - */ - @SuppressWarnings("unchecked") - public PercentErrPerTimeFailureInterpreter() { - setIgnore(defaultIgnore); - requestCounter = new RequestCounter(); - } - - /** - * Constructor that allows a tolerance for a certain number of - * failures within a given window of time without tripping. - * @param rc A {@link RequestCounter} wrapped around the same thing that this - * {@link org.fishwife.jrugged.CircuitBreaker} is protecting. This is - * needed in order to keep track of the total number of requests, enabling a - * percentage calculation to be done. - * @param percent the whole number percentage of failures that will be tolerated - * (i.e. the percentage of failures has to be strictly greater - * than this number in order to trip the breaker). For - * example, if the percentage is 3, any calculated failure percentage - * above that number during the window will cause the breaker to trip. - * @param windowMillis length of the window in milliseconds - */ - @SuppressWarnings("unchecked") - public PercentErrPerTimeFailureInterpreter(RequestCounter rc, - int percent, long windowMillis) { - setIgnore(defaultIgnore); - setPercent(percent); - setWindowMillis(windowMillis); - setRequestCounter(rc); - } - - /** - * Constructor that allows a tolerance for a certain number of - * failures within a given window of time without tripping. - * @param p A {@link PerformanceMonitor} from which we can get an underlying - * {@link RequestCounter} that is wrapped around the same thing that this - * {@link org.fishwife.jrugged.CircuitBreaker} is protecting. This is - * needed in order to keep track of the total number of requests, enabling a - * percentage calculation to be done. - * @param percent the whole number percentage of failures that will be tolerated - * (i.e. the percentage of failures has to be strictly greater - * than this number in order to trip the breaker). For - * example, if the percentage is 3, any calculated failure percentage - * above that number during the window will cause the breaker to trip. - * @param windowMillis length of the window in milliseconds - */ - @SuppressWarnings("unchecked") - public PercentErrPerTimeFailureInterpreter(PerformanceMonitor p, - int percent, long windowMillis) { - setIgnore(defaultIgnore); - setPercent(percent); - setWindowMillis(windowMillis); - setRequestCounter(p.getRequestCounter()); - } - - /** - * Constructor where we specify certain {@link Throwable} classes - * that will be ignored by the breaker and not be treated as - * failures (they will be passed through transparently without - * causing the breaker to trip). - * @param ignore an array of {@link Throwable} classes that will - * be ignored. Any given Throwable that is a - * subclass of one of these classes will be ignored. - */ - public PercentErrPerTimeFailureInterpreter(Class[] ignore) { - setIgnore(ignore); - } - - /** - * Constructor where we specify tolerance and a set of ignored failures. - * - * @param rc A {@link RequestCounter} wrapped around the same thing that this - * {@link org.fishwife.jrugged.CircuitBreaker} is protecting. This is - * needed in order to keep track of the total number of requests, enabling a - * percentage calculation to be done. - * @param ignore an array of {@link Throwable} classes that will - * be ignored. Any given Throwable that is a - * subclass of one of these classes will be ignored. - * @param percent the whole number percentage of failures that will be tolerated - * (i.e. the percentage of failures has to be strictly greater - * than this number in order to trip the breaker). For - * example, if the percentage is 3, any calculated failure percentage - * above that number during the window will cause the breaker to trip. - * @param windowMillis length of the window in milliseconds - */ - public PercentErrPerTimeFailureInterpreter(RequestCounter rc, - Class[] ignore, - int percent, long windowMillis) { - setRequestCounter(rc); - setIgnore(ignore); - setPercent(percent); - setWindowMillis(windowMillis); - } - - private boolean hasWindowConditions() { - return this.percent > 0 && this.windowMillis > 0; - } - - public boolean shouldTrip(Throwable cause) { - if (isExceptionIgnorable(cause)) return false; - - // if Exception is of specified type, and window conditions exist, - // keep circuit open unless exception threshold has passed - if (hasWindowConditions()) { - Long currentRequestCount = -1L; - long numberOfErrorsAfter; - - synchronized (modificationLock) { - errorTimes.add(System.currentTimeMillis()); - requestCounts.add(requestCounter.sample()[0]); - - // calculates time for which we remove any errors before - final long removeTimeBeforeMillis = System.currentTimeMillis() - windowMillis; - final int numberOfErrorsBefore = this.errorTimes.size(); - - removeErrorsPriorToCutoffTime(numberOfErrorsBefore, removeTimeBeforeMillis); - - numberOfErrorsAfter = this.errorTimes.size(); - - currentRequestCount = this.requestCounts.get(requestCounts.size() - 1); - } - - long windowRequests = (currentRequestCount - previousRequestHighWaterMark); - - // Trip if the number of errors over the total of requests over the same period - // is over the percentage limit. - return windowRequests >= requestThreshold && (((double) numberOfErrorsAfter / (double) windowRequests) * 100d >= percent); - } - return true; - } - - private boolean isExceptionIgnorable(Throwable cause) { - for(Class clazz : ignore) { - if (clazz.isInstance(cause)) { - return true; - } - } - return false; - } - - private void removeErrorsPriorToCutoffTime(int numberOfErrorsBefore, long removeTimeBeforeMillis) { - boolean windowRemoval = false; - - // (could we speed this up by using binary search to find the entry point, - // then removing any items before that point?) - for (int j = numberOfErrorsBefore - 1; j >= 0; j--) { - - final Long time = this.errorTimes.get(j); - - if (time < removeTimeBeforeMillis) { - if (!windowRemoval) { - previousRequestHighWaterMark = requestCounts.get(j); - windowRemoval = true; - } - - this.errorTimes.remove(j); - this.requestCounts.remove(j); - } - } - } - - /** - * Returns the set of currently ignored {@link Throwable} classes. - * @return {@link Set} - */ - public Set> getIgnore(){ - return this.ignore; - } - - /** - * Specifies an array of {@link Throwable} classes to ignore. These will not - * be considered failures. - * @param ignore array of {@link Class} objects - */ - public synchronized void setIgnore(Class[] ignore) { - this.ignore = new HashSet>(Arrays.asList(ignore)); - } - - /** - * Returns the current percentage of failures within the window that will be tolerated - * without tripping the breaker. - * @return int - */ - public int getPercent(){ - return this.percent; - } - - /** - * Specifies the percentage of tolerated failures within the - * configured time window. If percentage is set to n then the - * (n.000000000000001)th failure will trip the breaker. - * @param percent int - */ - public void setPercent(int percent) { - this.percent=percent; - } - - /** - * Returns the length of the currently configured tolerance window - * in milliseconds. - * @return long - */ - public long getWindowMillis(){ - return this.windowMillis; - } - - /** - * Specifies the length of the tolerance window in milliseconds. - * @param windowMillis long - */ - public void setWindowMillis(long windowMillis) { - this.windowMillis=windowMillis; - } - - /** - * Specifies the {@link RequestCounter} that will be supplying the "total" requests - * made information for this interpreter. - * @param rc A {@link RequestCounter} - */ - public void setRequestCounter(RequestCounter rc) { - requestCounter = rc; - } - - /** - * Sets the threshold at which the number of requests in the current window must be above in order for the breaker - * to trip. This is intended to prevent the breaker from being opened if the first request into this interpreter is - * a failure. - * - * @param requestThreshold The threshold to set, defaults to 0 - */ - public void setRequestThreshold(int requestThreshold) { - this.requestThreshold = requestThreshold; - } + private Set> ignore = new HashSet>(); + private int percent = 0; + private long windowMillis = 0; + private int requestThreshold = 0; + private long previousRequestHighWaterMark = 0; + + // tracks times when exceptions occurred + private List errorTimes = new LinkedList(); + + // tracks times when exceptions occurred + private List requestCounts = new LinkedList(); + private final Object modificationLock = new Object(); + + private RequestCounter requestCounter; + + private static Class[] defaultIgnore = {}; + + /** + * Default constructor. Any {@link Throwable} will cause the breaker to trip. + */ + @SuppressWarnings("unchecked") + public PercentErrPerTimeFailureInterpreter() { + setIgnore(defaultIgnore); + requestCounter = new RequestCounter(); + } + + /** + * Constructor that allows a tolerance for a certain number of failures within a + * given window of time without tripping. + * + * @param rc A {@link RequestCounter} wrapped around the same thing + * that this {@link org.fishwife.jrugged.CircuitBreaker} is + * protecting. This is needed in order to keep track of the + * total number of requests, enabling a percentage + * calculation to be done. + * @param percent the whole number percentage of failures that will be + * tolerated (i.e. the percentage of failures has to be + * strictly greater than this number in order to + * trip the breaker). For example, if the percentage is 3, + * any calculated failure percentage above that number + * during the window will cause the breaker to trip. + * @param windowMillis length of the window in milliseconds + */ + @SuppressWarnings("unchecked") + public PercentErrPerTimeFailureInterpreter(RequestCounter rc, int percent, long windowMillis) { + setIgnore(defaultIgnore); + setPercent(percent); + setWindowMillis(windowMillis); + setRequestCounter(rc); + } + + /** + * Constructor that allows a tolerance for a certain number of failures within a + * given window of time without tripping. + * + * @param p A {@link PerformanceMonitor} from which we can get an + * underlying {@link RequestCounter} that is wrapped around + * the same thing that this + * {@link org.fishwife.jrugged.CircuitBreaker} is + * protecting. This is needed in order to keep track of the + * total number of requests, enabling a percentage + * calculation to be done. + * @param percent the whole number percentage of failures that will be + * tolerated (i.e. the percentage of failures has to be + * strictly greater than this number in order to + * trip the breaker). For example, if the percentage is 3, + * any calculated failure percentage above that number + * during the window will cause the breaker to trip. + * @param windowMillis length of the window in milliseconds + */ + @SuppressWarnings("unchecked") + public PercentErrPerTimeFailureInterpreter(PerformanceMonitor p, int percent, long windowMillis) { + setIgnore(defaultIgnore); + setPercent(percent); + setWindowMillis(windowMillis); + setRequestCounter(p.getRequestCounter()); + } + + /** + * Constructor where we specify certain {@link Throwable} classes that will be + * ignored by the breaker and not be treated as failures (they will be passed + * through transparently without causing the breaker to trip). + * + * @param ignore an array of {@link Throwable} classes that will be ignored. Any + * given Throwable that is a subclass of one of these + * classes will be ignored. + */ + public PercentErrPerTimeFailureInterpreter(Class[] ignore) { + setIgnore(ignore); + } + + /** + * Constructor where we specify tolerance and a set of ignored failures. + * + * @param rc A {@link RequestCounter} wrapped around the same thing + * that this {@link org.fishwife.jrugged.CircuitBreaker} is + * protecting. This is needed in order to keep track of the + * total number of requests, enabling a percentage + * calculation to be done. + * @param ignore an array of {@link Throwable} classes that will be + * ignored. Any given Throwable that is a + * subclass of one of these classes will be ignored. + * @param percent the whole number percentage of failures that will be + * tolerated (i.e. the percentage of failures has to be + * strictly greater than this number in order to + * trip the breaker). For example, if the percentage is 3, + * any calculated failure percentage above that number + * during the window will cause the breaker to trip. + * @param windowMillis length of the window in milliseconds + */ + public PercentErrPerTimeFailureInterpreter(RequestCounter rc, Class[] ignore, int percent, + long windowMillis) { + setRequestCounter(rc); + setIgnore(ignore); + setPercent(percent); + setWindowMillis(windowMillis); + } + + private boolean hasWindowConditions() { + return this.percent > 0 && this.windowMillis > 0; + } + + public boolean shouldTrip(Throwable cause) { + if (isExceptionIgnorable(cause)) + return false; + + // if Exception is of specified type, and window conditions exist, + // keep circuit open unless exception threshold has passed + if (hasWindowConditions()) { + Long currentRequestCount = -1L; + long numberOfErrorsAfter; + + synchronized (modificationLock) { + errorTimes.add(System.currentTimeMillis()); + requestCounts.add(requestCounter.sample()[0]); + + // calculates time for which we remove any errors before + final long removeTimeBeforeMillis = System.currentTimeMillis() - windowMillis; + final int numberOfErrorsBefore = this.errorTimes.size(); + + removeErrorsPriorToCutoffTime(numberOfErrorsBefore, removeTimeBeforeMillis); + + numberOfErrorsAfter = this.errorTimes.size(); + + currentRequestCount = this.requestCounts.get(requestCounts.size() - 1); + } + + long windowRequests = (currentRequestCount - previousRequestHighWaterMark); + + // Trip if the number of errors over the total of requests over the same period + // is over the percentage limit. + return windowRequests >= requestThreshold + && (((double) numberOfErrorsAfter / (double) windowRequests) * 100d >= percent); + } + return true; + } + + private boolean isExceptionIgnorable(Throwable cause) { + for (Class clazz : ignore) { + if (clazz.isInstance(cause)) { + return true; + } + } + return false; + } + + private void removeErrorsPriorToCutoffTime(int numberOfErrorsBefore, long removeTimeBeforeMillis) { + boolean windowRemoval = false; + + // (could we speed this up by using binary search to find the entry point, + // then removing any items before that point?) + for (int j = numberOfErrorsBefore - 1; j >= 0; j--) { + + final Long time = this.errorTimes.get(j); + + if (time < removeTimeBeforeMillis) { + if (!windowRemoval) { + previousRequestHighWaterMark = requestCounts.get(j); + windowRemoval = true; + } + + this.errorTimes.remove(j); + this.requestCounts.remove(j); + } + } + } + + /** + * Returns the set of currently ignored {@link Throwable} classes. + * + * @return {@link Set} + */ + public Set> getIgnore() { + return this.ignore; + } + + /** + * Specifies an array of {@link Throwable} classes to ignore. These will not be + * considered failures. + * + * @param ignore array of {@link Class} objects + */ + public synchronized void setIgnore(Class[] ignore) { + this.ignore = new HashSet>(Arrays.asList(ignore)); + } + + /** + * Returns the current percentage of failures within the window that will be + * tolerated without tripping the breaker. + * + * @return int + */ + public int getPercent() { + return this.percent; + } + + /** + * Specifies the percentage of tolerated failures within the configured time + * window. If percentage is set to n then the + * (n.000000000000001)th failure will trip the breaker. + * + * @param percent int + */ + public void setPercent(int percent) { + this.percent = percent; + } + + /** + * Returns the length of the currently configured tolerance window in + * milliseconds. + * + * @return long + */ + public long getWindowMillis() { + return this.windowMillis; + } + + /** + * Specifies the length of the tolerance window in milliseconds. + * + * @param windowMillis long + */ + public void setWindowMillis(long windowMillis) { + this.windowMillis = windowMillis; + } + + /** + * Specifies the {@link RequestCounter} that will be supplying the "total" + * requests made information for this interpreter. + * + * @param rc A {@link RequestCounter} + */ + public void setRequestCounter(RequestCounter rc) { + requestCounter = rc; + } + + /** + * Sets the threshold at which the number of requests in the current window must + * be above in order for the breaker to trip. This is intended to prevent the + * breaker from being opened if the first request into this interpreter is a + * failure. + * + * @param requestThreshold The threshold to set, defaults to 0 + */ + public void setRequestThreshold(int requestThreshold) { + this.requestThreshold = requestThreshold; + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/PerformanceMonitor.java b/jrugged-core/src/main/java/org/fishwife/jrugged/PerformanceMonitor.java index 2cbd7f53..809ff06d 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/PerformanceMonitor.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/PerformanceMonitor.java @@ -20,608 +20,656 @@ import java.util.concurrent.TimeUnit; /** - * The {@link PerformanceMonitor} is a convenience wrapper for - * gathering a slew of useful operational metrics about a service, - * including moving averages for latency and request rate over - * various time windows (last minute, last hour, last day). + * The {@link PerformanceMonitor} is a convenience wrapper for gathering a slew + * of useful operational metrics about a service, including moving averages for + * latency and request rate over various time windows (last minute, last hour, + * last day). * - * The intended use is for a client to use the "Decorator" design - * pattern that decorates an existing service with this wrapper. - * Portions of this object can then be exposed via JMX, for example - * to allow for operational polling. + * The intended use is for a client to use the "Decorator" design pattern that + * decorates an existing service with this wrapper. Portions of this object can + * then be exposed via JMX, for example to allow for operational polling. */ public class PerformanceMonitor implements ServiceWrapper { - private static final String WRAP_MSG = - "org.fishwife.jrugged.PerformanceMonitor.WRAPPED"; - - private final long startupMillis = System.currentTimeMillis(); - - private static final long ONE_MINUTE_MILLIS = 60L * 1000L; - private static final long ONE_HOUR_MILLIS = ONE_MINUTE_MILLIS * 60L; - private static final long ONE_DAY_MILLIS = ONE_HOUR_MILLIS * 24L; - - private final RequestCounter requestCounter = new RequestCounter(); - private final FlowMeter flowMeter = new FlowMeter(requestCounter); - - private MovingAverage averageSuccessLatencyLastMinute; - private MovingAverage averageSuccessLatencyLastHour; - private MovingAverage averageSuccessLatencyLastDay; - private MovingAverage averageFailureLatencyLastMinute; - private MovingAverage averageFailureLatencyLastHour; - private MovingAverage averageFailureLatencyLastDay; - private MovingAverage totalRequestsPerSecondLastMinute; - private MovingAverage successRequestsPerSecondLastMinute; - private MovingAverage failureRequestsPerSecondLastMinute; - private MovingAverage totalRequestsPerSecondLastHour; - private MovingAverage successRequestsPerSecondLastHour; - private MovingAverage failureRequestsPerSecondLastHour; - private MovingAverage totalRequestsPerSecondLastDay; - private MovingAverage successRequestsPerSecondLastDay; - private MovingAverage failureRequestsPerSecondLastDay; - - private SampledQuantile lifetimeSuccessLatencyQuantile = new SampledQuantile(); - private SampledQuantile lifetimeFailureLatencyQuantile = new SampledQuantile(); - - private SampledQuantile successLatencyQuantileLastMinute = new SampledQuantile(60L, TimeUnit.SECONDS); - private SampledQuantile successLatencyQuantileLastHour = new SampledQuantile(3600L, TimeUnit.SECONDS); - private SampledQuantile successLatencyQuantileLastDay = new SampledQuantile(86400L, TimeUnit.SECONDS); - - private SampledQuantile failureLatencyQuantileLastMinute = new SampledQuantile(60L, TimeUnit.SECONDS); - private SampledQuantile failureLatencyQuantileLastHour = new SampledQuantile(3600L, TimeUnit.SECONDS); - private SampledQuantile failureLatencyQuantileLastDay = new SampledQuantile(86400L, TimeUnit.SECONDS); - - private long lifetimeMaxSuccessMillis; - private long lifetimeMaxFailureMillis; - - /** Default constructor. */ - public PerformanceMonitor() { - createMovingAverages(); - } - - private void createMovingAverages() { - averageSuccessLatencyLastMinute = new MovingAverage(ONE_MINUTE_MILLIS); - averageSuccessLatencyLastHour = new MovingAverage(ONE_HOUR_MILLIS); - averageSuccessLatencyLastDay = new MovingAverage(ONE_DAY_MILLIS); - averageFailureLatencyLastMinute = new MovingAverage(ONE_MINUTE_MILLIS); - averageFailureLatencyLastHour = new MovingAverage(ONE_HOUR_MILLIS); - averageFailureLatencyLastDay = new MovingAverage(ONE_DAY_MILLIS); - - totalRequestsPerSecondLastMinute = new MovingAverage(ONE_MINUTE_MILLIS); - successRequestsPerSecondLastMinute = new MovingAverage(ONE_MINUTE_MILLIS); - failureRequestsPerSecondLastMinute = new MovingAverage(ONE_MINUTE_MILLIS); - - totalRequestsPerSecondLastHour = new MovingAverage(ONE_HOUR_MILLIS); - successRequestsPerSecondLastHour = new MovingAverage(ONE_HOUR_MILLIS); - failureRequestsPerSecondLastHour = new MovingAverage(ONE_HOUR_MILLIS); - - totalRequestsPerSecondLastDay = new MovingAverage(ONE_DAY_MILLIS); - successRequestsPerSecondLastDay = new MovingAverage(ONE_DAY_MILLIS); - failureRequestsPerSecondLastDay = new MovingAverage(ONE_DAY_MILLIS); - } - - private void recordRequest() { - double[] rates = flowMeter.sample(); - totalRequestsPerSecondLastMinute.update(rates[0]); - totalRequestsPerSecondLastHour.update(rates[0]); - totalRequestsPerSecondLastDay.update(rates[0]); - - successRequestsPerSecondLastMinute.update(rates[1]); - successRequestsPerSecondLastHour.update(rates[1]); - successRequestsPerSecondLastDay.update(rates[1]); - - failureRequestsPerSecondLastMinute.update(rates[2]); - failureRequestsPerSecondLastHour.update(rates[2]); - failureRequestsPerSecondLastDay.update(rates[2]); - } - - private void recordSuccess(LatencyTracker latencyTracker) { - long successMillis = latencyTracker.getLastSuccessMillis(); - averageSuccessLatencyLastMinute.update(successMillis); - averageSuccessLatencyLastHour.update(successMillis); - averageSuccessLatencyLastDay.update(successMillis); - lifetimeSuccessLatencyQuantile.addSample(successMillis); - successLatencyQuantileLastMinute.addSample(successMillis); - successLatencyQuantileLastHour.addSample(successMillis); - successLatencyQuantileLastDay.addSample(successMillis); - lifetimeMaxSuccessMillis = - (successMillis > lifetimeMaxSuccessMillis) ? - successMillis : lifetimeMaxSuccessMillis; - recordRequest(); - } - - private void recordFailure(LatencyTracker latencyTracker) { - long failureMillis = latencyTracker.getLastFailureMillis(); - averageFailureLatencyLastMinute.update(failureMillis); - averageFailureLatencyLastHour.update(failureMillis); - averageFailureLatencyLastDay.update(failureMillis); - lifetimeFailureLatencyQuantile.addSample(failureMillis); - failureLatencyQuantileLastMinute.addSample(failureMillis); - failureLatencyQuantileLastHour.addSample(failureMillis); - failureLatencyQuantileLastDay.addSample(failureMillis); - lifetimeMaxFailureMillis = - (failureMillis > lifetimeMaxFailureMillis) ? - failureMillis : lifetimeMaxFailureMillis; - recordRequest(); - } - - public T invoke(final Callable c) throws Exception { - final LatencyTracker latencyTracker = new LatencyTracker(); - try { - T result = requestCounter.invoke(new Callable() { - public T call() throws Exception { - return latencyTracker.invoke(c); - } - }); - recordSuccess(latencyTracker); - return result; - } catch (Exception e) { - recordFailure(latencyTracker); - if (WRAP_MSG.equals(e.getMessage())) { - throw (Exception)e.getCause(); - } else { - throw e; - } - } - } - - public void invoke(final Runnable r) throws Exception { - final LatencyTracker latencyTracker = new LatencyTracker(); - try { - requestCounter.invoke(new Runnable() { - public void run() { - try { - latencyTracker.invoke(r); - } catch (Exception e) { - throw new RuntimeException(WRAP_MSG, e); - } - } - }); - recordSuccess(latencyTracker); - } catch (RuntimeException re) { - recordFailure(latencyTracker); - if (WRAP_MSG.equals(re.getMessage())) { - throw (Exception)re.getCause(); - } else { - throw re; - } - } - } - - public T invoke(final Runnable r, T result) throws Exception { - this.invoke(r); - return result; - } - - /** - * Returns the average latency in milliseconds of a successful request, - * as measured over the last minute. - * @return double - */ - public double getAverageSuccessLatencyLastMinute() { - return averageSuccessLatencyLastMinute.getAverage(); - } - - /** - * Returns the average latency in milliseconds of a successful request, - * as measured over the last hour. - * @return double - */ - public double getAverageSuccessLatencyLastHour() { - return averageSuccessLatencyLastHour.getAverage(); - } - - /** - * Returns the average latency in milliseconds of a successful request, - * as measured over the last day. - * @return double - */ - public double getAverageSuccessLatencyLastDay() { - return averageSuccessLatencyLastDay.getAverage(); - } - - /** - * Returns the average latency in milliseconds of a failed request, - * as measured over the last minute. - * @return double - */ - public double getAverageFailureLatencyLastMinute() { - return averageFailureLatencyLastMinute.getAverage(); - } - - /** - * Returns the average latency in milliseconds of a failed request, - * as measured over the last hour. - * @return double - */ - public double getAverageFailureLatencyLastHour() { - return averageFailureLatencyLastHour.getAverage(); - } - - /** - * Returns the average latency in milliseconds of a failed request, - * as measured over the last day. - * @return double - */ - public double getAverageFailureLatencyLastDay() { - return averageFailureLatencyLastDay.getAverage(); - } - - /** - * Returns the average request rate in requests per second of - * all requests, as measured over the last minute. - * @return double - */ - public double getTotalRequestsPerSecondLastMinute() { - return totalRequestsPerSecondLastMinute.getAverage(); - } - - /** - * Returns the average request rate in requests per second of - * successful requests, as measured over the last minute. - * @return double - */ - public double getSuccessRequestsPerSecondLastMinute() { - return successRequestsPerSecondLastMinute.getAverage(); - } - - /** - * Returns the average request rate in requests per second of - * failed requests, as measured over the last minute. - * @return double - */ - public double getFailureRequestsPerSecondLastMinute() { - return failureRequestsPerSecondLastMinute.getAverage(); - } - - /** - * Returns the average request rate in requests per second of - * all requests, as measured over the last hour. - * @return double - */ - public double getTotalRequestsPerSecondLastHour() { - return totalRequestsPerSecondLastHour.getAverage(); - } - - /** - * Returns the average request rate in requests per second of - * successful requests, as measured over the last hour. - * @return double - */ - public double getSuccessRequestsPerSecondLastHour() { - return successRequestsPerSecondLastHour.getAverage(); - } - - /** Returns the average request rate in requests per second of - * failed requests, as measured over the last hour. - * @return double - */ - public double getFailureRequestsPerSecondLastHour() { - return failureRequestsPerSecondLastHour.getAverage(); - } - - /** - * Returns the average request rate in requests per second of - * all requests, as measured over the last day. - * @return double - */ - public double getTotalRequestsPerSecondLastDay() { - return totalRequestsPerSecondLastDay.getAverage(); - } - - /** - * Returns the average request rate in requests per second of - * successful requests, as measured over the last day. - * @return double - */ - public double getSuccessRequestsPerSecondLastDay() { - return successRequestsPerSecondLastDay.getAverage(); - } - - /** - * Returns the average request rate in requests per second of - * failed requests, as measured over the last day. - * @return double - */ - public double getFailureRequestsPerSecondLastDay() { - return failureRequestsPerSecondLastDay.getAverage(); - } - - /** - * Returns the average request rate in requests per second of - * all requests, as measured since this object was initialized. - * @return double - */ - public double getTotalRequestsPerSecondLifetime() { - long deltaT = System.currentTimeMillis() - startupMillis; - return (((double)requestCounter.sample()[0])/(double)deltaT) * 1000; - } - - /** - * Returns the average request rate in requests per second of - * successful requests, as measured since this object was - * initialized. - * @return double - */ - public double getSuccessRequestsPerSecondLifetime() { - long deltaT = System.currentTimeMillis() - startupMillis; - return (((double)requestCounter.sample()[1])/(double)deltaT) * 1000; - } - - /** - * Returns the average request rate in requests per second of - * failed requests, as measured since this object was - * initialized. - * @return double - */ - public double getFailureRequestsPerSecondLifetime() { - long deltaT = System.currentTimeMillis() - startupMillis; - return (((double)requestCounter.sample()[2])/(double)deltaT) * 1000; - } - - /** - * Returns the underlying request counter that this performance - * monitor is using. This can be used in conjunction with - * {@link PercentErrPerTimeFailureInterpreter}. - * - * @return RequestCounter the request count tracker class - */ - public RequestCounter getRequestCounter() { - return this.requestCounter; - } - - /** - * Returns the total number of requests seen by this {@link - * PerformanceMonitor}. - * @return long - */ - public long getRequestCount() { - return requestCounter.sample()[0]; - } - - /** - * Returns the number of successful requests seen by this {@link - * PerformanceMonitor}. - * @return long - */ - public long getSuccessCount() { - return requestCounter.sample()[1]; - } - - /** - * Returns the number of failed requests seen by this {@link - * PerformanceMonitor}. - * @return long - */ - public long getFailureCount() { - return requestCounter.sample()[2]; - } - - /** Returns the median latency seen by this {@link - * PerformanceMonitor} for successful requests. - * @return latency in milliseconds - */ - public long getMedianPercentileSuccessLatencyLifetime() { - return lifetimeSuccessLatencyQuantile.getPercentile(50); - } - - /** Returns the 95th-percentile latency seen by this - * {@link PerformanceMonitor} for successful requests. - * @return latency in milliseconds - */ - public long get95thPercentileSuccessLatencyLifetime() { - return lifetimeSuccessLatencyQuantile.getPercentile(95); - } - - /** Returns the 99th-percentile latency seen by this - * {@link PerformanceMonitor} for successful requests. - * @return latency in milliseconds - */ - public long get99thPercentileSuccessLatencyLifetime() { - return lifetimeSuccessLatencyQuantile.getPercentile(99); - } - - /** Returns the maximum latency seen by this - * {@link PerformanceMonitor} for successful requests. - * @return latency in milliseconds - */ - public long getMaxSuccessLatencyLifetime() { - return lifetimeMaxSuccessMillis; - } - - /** Returns the median latency seen by this {@link - * PerformanceMonitor} for successful requests over the - * last minute. - * @return latency in milliseconds - */ - public long getMedianPercentileSuccessLatencyLastMinute() { - return successLatencyQuantileLastMinute.getPercentile(50); - } - - /** Returns the 95th-percentile latency seen by this {@link - * PerformanceMonitor} for successful requests over the last - * minute. - * @return latency in milliseconds - */ - public long get95thPercentileSuccessLatencyLastMinute() { - return successLatencyQuantileLastMinute.getPercentile(95); - } - - /** Returns the 99th-percentile latency seen by this {@link - * PerformanceMonitor} for successful requests over the last - * minute. - * @return latency in milliseconds - */ - public long get99thPercentileSuccessLatencyLastMinute() { - return successLatencyQuantileLastMinute.getPercentile(99); - } - - /** Returns the median latency seen by this {@link - * PerformanceMonitor} for successful requests over the - * last hour. - * @return latency in milliseconds - */ - public long getMedianPercentileSuccessfulLatencyLastHour() { - return successLatencyQuantileLastHour.getPercentile(50); - } - - /** Returns the 95th-percentile latency seen by this {@link - * PerformanceMonitor} for successful requests over the last - * hour. - * @return latency in milliseconds - */ - public long get95thPercentileSuccessLatencyLastHour() { - return successLatencyQuantileLastHour.getPercentile(95); - } - - /** Returns the 99th-percentile latency seen by this {@link - * PerformanceMonitor} for successful requests over the last - * hour. - * @return latency in milliseconds - */ - public long get99thPercentileSuccessLatencyLastHour() { - return successLatencyQuantileLastHour.getPercentile(99); - } - - /** Returns the median latency seen by this {@link - * PerformanceMonitor} for successful requests over the - * last day. - * @return latency in milliseconds - */ - public long getMedianPercentileSuccessLatencyLastDay() { - return successLatencyQuantileLastDay.getPercentile(50); - } - - /** Returns the 95th-percentile latency seen by this {@link - * PerformanceMonitor} for successful requests over the last - * day. - * @return latency in milliseconds - */ - public long get95thPercentileSuccessLatencyLastDay() { - return successLatencyQuantileLastDay.getPercentile(95); - } - - /** Returns the 99th-percentile latency seen by this {@link - * PerformanceMonitor} for successful requests over the last - * hour. - * @return latency in milliseconds - */ - public long get99thPercentileSuccessLatencyLastDay() { - return successLatencyQuantileLastDay.getPercentile(99); - } - - - /** Returns the median latency seen by this {@link - * PerformanceMonitor} for failed requests. - * @return latency in milliseconds - */ - public long getMedianPercentileFailureLatencyLifetime() { - return lifetimeFailureLatencyQuantile.getPercentile(50); - } - - /** Returns the 95th-percentile latency seen by this {@link - * PerformanceMonitor} for failed requests. - * @return latency in milliseconds - */ - public long get95thPercentileFailureLatencyLifetime() { - return lifetimeFailureLatencyQuantile.getPercentile(95); - } - - /** Returns the 99th-percentile latency seen by this {@link - * PerformanceMonitor} for failed requests. - * @return latency in milliseconds - */ - public long get99thPercentileFailureLatencyLifetime() { - return lifetimeFailureLatencyQuantile.getPercentile(99); - } - - /** Returns the maximum latency seen by this {@link - * PerformanceMonitor} for failed requests. - * @return latency in milliseconds - */ - public long getMaxFailureLatencyLifetime() { - return lifetimeMaxFailureMillis; - } - - /** Returns the median latency seen by this {@link - * PerformanceMonitor} for failed requests over the - * last minute. - * @return latency in milliseconds - */ - public long getMedianPercentileFailureLatencyLastMinute() { - return failureLatencyQuantileLastMinute.getPercentile(50); - } - - /** Returns the 95th-percentile latency seen by this {@link - * PerformanceMonitor} for failed requests over the last - * minute. - * @return latency in milliseconds - */ - public long get95thPercentileFailureLatencyLastMinute() { - return failureLatencyQuantileLastMinute.getPercentile(95); - } - - /** Returns the 99th-percentile latency seen by this {@link - * PerformanceMonitor} for failed requests over the last - * minute. - * @return latency in milliseconds - */ - public long get99thPercentileFailureLatencyLastMinute() { - return failureLatencyQuantileLastMinute.getPercentile(99); - } - - /** Returns the median latency seen by this {@link - * PerformanceMonitor} for failed requests over the - * last hour. - * @return latency in milliseconds - */ - public long getMedianPercentileFailureLatencyLastHour() { - return failureLatencyQuantileLastHour.getPercentile(50); - } - - /** Returns the 95th-percentile latency seen by this {@link - * PerformanceMonitor} for failed requests over the last - * hour. - * @return latency in milliseconds - */ - public long get95thPercentileFailureLatencyLastHour() { - return failureLatencyQuantileLastHour.getPercentile(95); - } - - /** Returns the 99th-percentile latency seen by this {@link - * PerformanceMonitor} for failed requests over the last - * hour. - * @return latency in milliseconds - */ - public long get99thPercentileFailureLatencyLastHour() { - return failureLatencyQuantileLastHour.getPercentile(99); - } - - /** Returns the median latency seen by this {@link - * PerformanceMonitor} for failed requests over the - * last day. - * @return latency in milliseconds - */ - public long getMedianPercentileFailureLatencyLastDay() { - return failureLatencyQuantileLastDay.getPercentile(50); - } - - /** Returns the 95th-percentile latency seen by this {@link - * PerformanceMonitor} for failed requests over the last - * day. - * @return latency in milliseconds - */ - public long get95thPercentileFailureLatencyLastDay() { - return failureLatencyQuantileLastDay.getPercentile(95); - } - - /** Returns the 99th-percentile latency seen by this {@link - * PerformanceMonitor} for failed requests over the last - * hour. - * @return latency in milliseconds - */ - public long get99thPercentileFailureLatencyLastDay() { - return failureLatencyQuantileLastDay.getPercentile(99); - } + private static final String WRAP_MSG = "org.fishwife.jrugged.PerformanceMonitor.WRAPPED"; + + private final long startupMillis = System.currentTimeMillis(); + + private static final long ONE_MINUTE_MILLIS = 60L * 1000L; + private static final long ONE_HOUR_MILLIS = ONE_MINUTE_MILLIS * 60L; + private static final long ONE_DAY_MILLIS = ONE_HOUR_MILLIS * 24L; + + private final RequestCounter requestCounter = new RequestCounter(); + private final FlowMeter flowMeter = new FlowMeter(requestCounter); + + private MovingAverage averageSuccessLatencyLastMinute; + private MovingAverage averageSuccessLatencyLastHour; + private MovingAverage averageSuccessLatencyLastDay; + private MovingAverage averageFailureLatencyLastMinute; + private MovingAverage averageFailureLatencyLastHour; + private MovingAverage averageFailureLatencyLastDay; + private MovingAverage totalRequestsPerSecondLastMinute; + private MovingAverage successRequestsPerSecondLastMinute; + private MovingAverage failureRequestsPerSecondLastMinute; + private MovingAverage totalRequestsPerSecondLastHour; + private MovingAverage successRequestsPerSecondLastHour; + private MovingAverage failureRequestsPerSecondLastHour; + private MovingAverage totalRequestsPerSecondLastDay; + private MovingAverage successRequestsPerSecondLastDay; + private MovingAverage failureRequestsPerSecondLastDay; + + private SampledQuantile lifetimeSuccessLatencyQuantile = new SampledQuantile(); + private SampledQuantile lifetimeFailureLatencyQuantile = new SampledQuantile(); + + private SampledQuantile successLatencyQuantileLastMinute = new SampledQuantile(60L, TimeUnit.SECONDS); + private SampledQuantile successLatencyQuantileLastHour = new SampledQuantile(3600L, TimeUnit.SECONDS); + private SampledQuantile successLatencyQuantileLastDay = new SampledQuantile(86400L, TimeUnit.SECONDS); + + private SampledQuantile failureLatencyQuantileLastMinute = new SampledQuantile(60L, TimeUnit.SECONDS); + private SampledQuantile failureLatencyQuantileLastHour = new SampledQuantile(3600L, TimeUnit.SECONDS); + private SampledQuantile failureLatencyQuantileLastDay = new SampledQuantile(86400L, TimeUnit.SECONDS); + + private long lifetimeMaxSuccessMillis; + private long lifetimeMaxFailureMillis; + + /** Default constructor. */ + public PerformanceMonitor() { + createMovingAverages(); + } + + private void createMovingAverages() { + averageSuccessLatencyLastMinute = new MovingAverage(ONE_MINUTE_MILLIS); + averageSuccessLatencyLastHour = new MovingAverage(ONE_HOUR_MILLIS); + averageSuccessLatencyLastDay = new MovingAverage(ONE_DAY_MILLIS); + averageFailureLatencyLastMinute = new MovingAverage(ONE_MINUTE_MILLIS); + averageFailureLatencyLastHour = new MovingAverage(ONE_HOUR_MILLIS); + averageFailureLatencyLastDay = new MovingAverage(ONE_DAY_MILLIS); + + totalRequestsPerSecondLastMinute = new MovingAverage(ONE_MINUTE_MILLIS); + successRequestsPerSecondLastMinute = new MovingAverage(ONE_MINUTE_MILLIS); + failureRequestsPerSecondLastMinute = new MovingAverage(ONE_MINUTE_MILLIS); + + totalRequestsPerSecondLastHour = new MovingAverage(ONE_HOUR_MILLIS); + successRequestsPerSecondLastHour = new MovingAverage(ONE_HOUR_MILLIS); + failureRequestsPerSecondLastHour = new MovingAverage(ONE_HOUR_MILLIS); + + totalRequestsPerSecondLastDay = new MovingAverage(ONE_DAY_MILLIS); + successRequestsPerSecondLastDay = new MovingAverage(ONE_DAY_MILLIS); + failureRequestsPerSecondLastDay = new MovingAverage(ONE_DAY_MILLIS); + } + + private void recordRequest() { + double[] rates = flowMeter.sample(); + totalRequestsPerSecondLastMinute.update(rates[0]); + totalRequestsPerSecondLastHour.update(rates[0]); + totalRequestsPerSecondLastDay.update(rates[0]); + + successRequestsPerSecondLastMinute.update(rates[1]); + successRequestsPerSecondLastHour.update(rates[1]); + successRequestsPerSecondLastDay.update(rates[1]); + + failureRequestsPerSecondLastMinute.update(rates[2]); + failureRequestsPerSecondLastHour.update(rates[2]); + failureRequestsPerSecondLastDay.update(rates[2]); + } + + private void recordSuccess(LatencyTracker latencyTracker) { + long successMillis = latencyTracker.getLastSuccessMillis(); + averageSuccessLatencyLastMinute.update(successMillis); + averageSuccessLatencyLastHour.update(successMillis); + averageSuccessLatencyLastDay.update(successMillis); + lifetimeSuccessLatencyQuantile.addSample(successMillis); + successLatencyQuantileLastMinute.addSample(successMillis); + successLatencyQuantileLastHour.addSample(successMillis); + successLatencyQuantileLastDay.addSample(successMillis); + lifetimeMaxSuccessMillis = (successMillis > lifetimeMaxSuccessMillis) ? successMillis + : lifetimeMaxSuccessMillis; + recordRequest(); + } + + private void recordFailure(LatencyTracker latencyTracker) { + long failureMillis = latencyTracker.getLastFailureMillis(); + averageFailureLatencyLastMinute.update(failureMillis); + averageFailureLatencyLastHour.update(failureMillis); + averageFailureLatencyLastDay.update(failureMillis); + lifetimeFailureLatencyQuantile.addSample(failureMillis); + failureLatencyQuantileLastMinute.addSample(failureMillis); + failureLatencyQuantileLastHour.addSample(failureMillis); + failureLatencyQuantileLastDay.addSample(failureMillis); + lifetimeMaxFailureMillis = (failureMillis > lifetimeMaxFailureMillis) ? failureMillis + : lifetimeMaxFailureMillis; + recordRequest(); + } + + public T invoke(final Callable c) throws Exception { + final LatencyTracker latencyTracker = new LatencyTracker(); + try { + T result = requestCounter.invoke(new Callable() { + public T call() throws Exception { + return latencyTracker.invoke(c); + } + }); + recordSuccess(latencyTracker); + return result; + } catch (Exception e) { + recordFailure(latencyTracker); + if (WRAP_MSG.equals(e.getMessage())) { + throw (Exception) e.getCause(); + } else { + throw e; + } + } + } + + public void invoke(final Runnable r) throws Exception { + final LatencyTracker latencyTracker = new LatencyTracker(); + try { + requestCounter.invoke(new Runnable() { + public void run() { + try { + latencyTracker.invoke(r); + } catch (Exception e) { + throw new RuntimeException(WRAP_MSG, e); + } + } + }); + recordSuccess(latencyTracker); + } catch (RuntimeException re) { + recordFailure(latencyTracker); + if (WRAP_MSG.equals(re.getMessage())) { + throw (Exception) re.getCause(); + } else { + throw re; + } + } + } + + public T invoke(final Runnable r, T result) throws Exception { + this.invoke(r); + return result; + } + + /** + * Returns the average latency in milliseconds of a successful request, as + * measured over the last minute. + * + * @return double + */ + public double getAverageSuccessLatencyLastMinute() { + return averageSuccessLatencyLastMinute.getAverage(); + } + + /** + * Returns the average latency in milliseconds of a successful request, as + * measured over the last hour. + * + * @return double + */ + public double getAverageSuccessLatencyLastHour() { + return averageSuccessLatencyLastHour.getAverage(); + } + + /** + * Returns the average latency in milliseconds of a successful request, as + * measured over the last day. + * + * @return double + */ + public double getAverageSuccessLatencyLastDay() { + return averageSuccessLatencyLastDay.getAverage(); + } + + /** + * Returns the average latency in milliseconds of a failed request, as measured + * over the last minute. + * + * @return double + */ + public double getAverageFailureLatencyLastMinute() { + return averageFailureLatencyLastMinute.getAverage(); + } + + /** + * Returns the average latency in milliseconds of a failed request, as measured + * over the last hour. + * + * @return double + */ + public double getAverageFailureLatencyLastHour() { + return averageFailureLatencyLastHour.getAverage(); + } + + /** + * Returns the average latency in milliseconds of a failed request, as measured + * over the last day. + * + * @return double + */ + public double getAverageFailureLatencyLastDay() { + return averageFailureLatencyLastDay.getAverage(); + } + + /** + * Returns the average request rate in requests per second of all requests, as + * measured over the last minute. + * + * @return double + */ + public double getTotalRequestsPerSecondLastMinute() { + return totalRequestsPerSecondLastMinute.getAverage(); + } + + /** + * Returns the average request rate in requests per second of successful + * requests, as measured over the last minute. + * + * @return double + */ + public double getSuccessRequestsPerSecondLastMinute() { + return successRequestsPerSecondLastMinute.getAverage(); + } + + /** + * Returns the average request rate in requests per second of failed requests, + * as measured over the last minute. + * + * @return double + */ + public double getFailureRequestsPerSecondLastMinute() { + return failureRequestsPerSecondLastMinute.getAverage(); + } + + /** + * Returns the average request rate in requests per second of all requests, as + * measured over the last hour. + * + * @return double + */ + public double getTotalRequestsPerSecondLastHour() { + return totalRequestsPerSecondLastHour.getAverage(); + } + + /** + * Returns the average request rate in requests per second of successful + * requests, as measured over the last hour. + * + * @return double + */ + public double getSuccessRequestsPerSecondLastHour() { + return successRequestsPerSecondLastHour.getAverage(); + } + + /** + * Returns the average request rate in requests per second of failed requests, + * as measured over the last hour. + * + * @return double + */ + public double getFailureRequestsPerSecondLastHour() { + return failureRequestsPerSecondLastHour.getAverage(); + } + + /** + * Returns the average request rate in requests per second of all requests, as + * measured over the last day. + * + * @return double + */ + public double getTotalRequestsPerSecondLastDay() { + return totalRequestsPerSecondLastDay.getAverage(); + } + + /** + * Returns the average request rate in requests per second of successful + * requests, as measured over the last day. + * + * @return double + */ + public double getSuccessRequestsPerSecondLastDay() { + return successRequestsPerSecondLastDay.getAverage(); + } + + /** + * Returns the average request rate in requests per second of failed requests, + * as measured over the last day. + * + * @return double + */ + public double getFailureRequestsPerSecondLastDay() { + return failureRequestsPerSecondLastDay.getAverage(); + } + + /** + * Returns the average request rate in requests per second of all requests, as + * measured since this object was initialized. + * + * @return double + */ + public double getTotalRequestsPerSecondLifetime() { + long deltaT = System.currentTimeMillis() - startupMillis; + return (((double) requestCounter.sample()[0]) / (double) deltaT) * 1000; + } + + /** + * Returns the average request rate in requests per second of successful + * requests, as measured since this object was initialized. + * + * @return double + */ + public double getSuccessRequestsPerSecondLifetime() { + long deltaT = System.currentTimeMillis() - startupMillis; + return (((double) requestCounter.sample()[1]) / (double) deltaT) * 1000; + } + + /** + * Returns the average request rate in requests per second of failed requests, + * as measured since this object was initialized. + * + * @return double + */ + public double getFailureRequestsPerSecondLifetime() { + long deltaT = System.currentTimeMillis() - startupMillis; + return (((double) requestCounter.sample()[2]) / (double) deltaT) * 1000; + } + + /** + * Returns the underlying request counter that this performance monitor is + * using. This can be used in conjunction with + * {@link PercentErrPerTimeFailureInterpreter}. + * + * @return RequestCounter the request count tracker class + */ + public RequestCounter getRequestCounter() { + return this.requestCounter; + } + + /** + * Returns the total number of requests seen by this {@link PerformanceMonitor}. + * + * @return long + */ + public long getRequestCount() { + return requestCounter.sample()[0]; + } + + /** + * Returns the number of successful requests seen by this + * {@link PerformanceMonitor}. + * + * @return long + */ + public long getSuccessCount() { + return requestCounter.sample()[1]; + } + + /** + * Returns the number of failed requests seen by this + * {@link PerformanceMonitor}. + * + * @return long + */ + public long getFailureCount() { + return requestCounter.sample()[2]; + } + + /** + * Returns the median latency seen by this {@link PerformanceMonitor} for + * successful requests. + * + * @return latency in milliseconds + */ + public long getMedianPercentileSuccessLatencyLifetime() { + return lifetimeSuccessLatencyQuantile.getPercentile(50); + } + + /** + * Returns the 95th-percentile latency seen by this {@link PerformanceMonitor} + * for successful requests. + * + * @return latency in milliseconds + */ + public long get95thPercentileSuccessLatencyLifetime() { + return lifetimeSuccessLatencyQuantile.getPercentile(95); + } + + /** + * Returns the 99th-percentile latency seen by this {@link PerformanceMonitor} + * for successful requests. + * + * @return latency in milliseconds + */ + public long get99thPercentileSuccessLatencyLifetime() { + return lifetimeSuccessLatencyQuantile.getPercentile(99); + } + + /** + * Returns the maximum latency seen by this {@link PerformanceMonitor} for + * successful requests. + * + * @return latency in milliseconds + */ + public long getMaxSuccessLatencyLifetime() { + return lifetimeMaxSuccessMillis; + } + + /** + * Returns the median latency seen by this {@link PerformanceMonitor} for + * successful requests over the last minute. + * + * @return latency in milliseconds + */ + public long getMedianPercentileSuccessLatencyLastMinute() { + return successLatencyQuantileLastMinute.getPercentile(50); + } + + /** + * Returns the 95th-percentile latency seen by this {@link PerformanceMonitor} + * for successful requests over the last minute. + * + * @return latency in milliseconds + */ + public long get95thPercentileSuccessLatencyLastMinute() { + return successLatencyQuantileLastMinute.getPercentile(95); + } + + /** + * Returns the 99th-percentile latency seen by this {@link PerformanceMonitor} + * for successful requests over the last minute. + * + * @return latency in milliseconds + */ + public long get99thPercentileSuccessLatencyLastMinute() { + return successLatencyQuantileLastMinute.getPercentile(99); + } + + /** + * Returns the median latency seen by this {@link PerformanceMonitor} for + * successful requests over the last hour. + * + * @return latency in milliseconds + */ + public long getMedianPercentileSuccessfulLatencyLastHour() { + return successLatencyQuantileLastHour.getPercentile(50); + } + + /** + * Returns the 95th-percentile latency seen by this {@link PerformanceMonitor} + * for successful requests over the last hour. + * + * @return latency in milliseconds + */ + public long get95thPercentileSuccessLatencyLastHour() { + return successLatencyQuantileLastHour.getPercentile(95); + } + + /** + * Returns the 99th-percentile latency seen by this {@link PerformanceMonitor} + * for successful requests over the last hour. + * + * @return latency in milliseconds + */ + public long get99thPercentileSuccessLatencyLastHour() { + return successLatencyQuantileLastHour.getPercentile(99); + } + + /** + * Returns the median latency seen by this {@link PerformanceMonitor} for + * successful requests over the last day. + * + * @return latency in milliseconds + */ + public long getMedianPercentileSuccessLatencyLastDay() { + return successLatencyQuantileLastDay.getPercentile(50); + } + + /** + * Returns the 95th-percentile latency seen by this {@link PerformanceMonitor} + * for successful requests over the last day. + * + * @return latency in milliseconds + */ + public long get95thPercentileSuccessLatencyLastDay() { + return successLatencyQuantileLastDay.getPercentile(95); + } + + /** + * Returns the 99th-percentile latency seen by this {@link PerformanceMonitor} + * for successful requests over the last hour. + * + * @return latency in milliseconds + */ + public long get99thPercentileSuccessLatencyLastDay() { + return successLatencyQuantileLastDay.getPercentile(99); + } + + /** + * Returns the median latency seen by this {@link PerformanceMonitor} for failed + * requests. + * + * @return latency in milliseconds + */ + public long getMedianPercentileFailureLatencyLifetime() { + return lifetimeFailureLatencyQuantile.getPercentile(50); + } + + /** + * Returns the 95th-percentile latency seen by this {@link PerformanceMonitor} + * for failed requests. + * + * @return latency in milliseconds + */ + public long get95thPercentileFailureLatencyLifetime() { + return lifetimeFailureLatencyQuantile.getPercentile(95); + } + + /** + * Returns the 99th-percentile latency seen by this {@link PerformanceMonitor} + * for failed requests. + * + * @return latency in milliseconds + */ + public long get99thPercentileFailureLatencyLifetime() { + return lifetimeFailureLatencyQuantile.getPercentile(99); + } + + /** + * Returns the maximum latency seen by this {@link PerformanceMonitor} for + * failed requests. + * + * @return latency in milliseconds + */ + public long getMaxFailureLatencyLifetime() { + return lifetimeMaxFailureMillis; + } + + /** + * Returns the median latency seen by this {@link PerformanceMonitor} for failed + * requests over the last minute. + * + * @return latency in milliseconds + */ + public long getMedianPercentileFailureLatencyLastMinute() { + return failureLatencyQuantileLastMinute.getPercentile(50); + } + + /** + * Returns the 95th-percentile latency seen by this {@link PerformanceMonitor} + * for failed requests over the last minute. + * + * @return latency in milliseconds + */ + public long get95thPercentileFailureLatencyLastMinute() { + return failureLatencyQuantileLastMinute.getPercentile(95); + } + + /** + * Returns the 99th-percentile latency seen by this {@link PerformanceMonitor} + * for failed requests over the last minute. + * + * @return latency in milliseconds + */ + public long get99thPercentileFailureLatencyLastMinute() { + return failureLatencyQuantileLastMinute.getPercentile(99); + } + + /** + * Returns the median latency seen by this {@link PerformanceMonitor} for failed + * requests over the last hour. + * + * @return latency in milliseconds + */ + public long getMedianPercentileFailureLatencyLastHour() { + return failureLatencyQuantileLastHour.getPercentile(50); + } + + /** + * Returns the 95th-percentile latency seen by this {@link PerformanceMonitor} + * for failed requests over the last hour. + * + * @return latency in milliseconds + */ + public long get95thPercentileFailureLatencyLastHour() { + return failureLatencyQuantileLastHour.getPercentile(95); + } + + /** + * Returns the 99th-percentile latency seen by this {@link PerformanceMonitor} + * for failed requests over the last hour. + * + * @return latency in milliseconds + */ + public long get99thPercentileFailureLatencyLastHour() { + return failureLatencyQuantileLastHour.getPercentile(99); + } + + /** + * Returns the median latency seen by this {@link PerformanceMonitor} for failed + * requests over the last day. + * + * @return latency in milliseconds + */ + public long getMedianPercentileFailureLatencyLastDay() { + return failureLatencyQuantileLastDay.getPercentile(50); + } + + /** + * Returns the 95th-percentile latency seen by this {@link PerformanceMonitor} + * for failed requests over the last day. + * + * @return latency in milliseconds + */ + public long get95thPercentileFailureLatencyLastDay() { + return failureLatencyQuantileLastDay.getPercentile(95); + } + + /** + * Returns the 99th-percentile latency seen by this {@link PerformanceMonitor} + * for failed requests over the last hour. + * + * @return latency in milliseconds + */ + public long get99thPercentileFailureLatencyLastDay() { + return failureLatencyQuantileLastDay.getPercentile(99); + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/PerformanceMonitorFactory.java b/jrugged-core/src/main/java/org/fishwife/jrugged/PerformanceMonitorFactory.java index 235c76a9..e6c1ac44 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/PerformanceMonitorFactory.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/PerformanceMonitorFactory.java @@ -24,55 +24,58 @@ */ public class PerformanceMonitorFactory { - private final Map performanceMonitorMap = - new ConcurrentHashMap(); + private final Map performanceMonitorMap = new ConcurrentHashMap(); - /** - * Create an empty PerformanceMonitorFactory. - */ - public PerformanceMonitorFactory() { - } + /** + * Create an empty PerformanceMonitorFactory. + */ + public PerformanceMonitorFactory() { + } - /** - * Create a new {@link PerformanceMonitor} and map it to the provided name. - * If the PerformanceMonitor already exists, then the existing instance is - * returned. - * @param name the name for the {@link PerformanceMonitor} - * @return the created {@link PerformanceMonitor} - */ - public synchronized PerformanceMonitor createPerformanceMonitor(String name) { - PerformanceMonitor performanceMonitor = findPerformanceMonitor(name); + /** + * Create a new {@link PerformanceMonitor} and map it to the provided name. If + * the PerformanceMonitor already exists, then the existing instance is + * returned. + * + * @param name the name for the {@link PerformanceMonitor} + * @return the created {@link PerformanceMonitor} + */ + public synchronized PerformanceMonitor createPerformanceMonitor(String name) { + PerformanceMonitor performanceMonitor = findPerformanceMonitor(name); - if (performanceMonitor == null) { - performanceMonitor = new PerformanceMonitor(); - addPerformanceMonitorToMap(name, performanceMonitor); - } - return performanceMonitor; - } + if (performanceMonitor == null) { + performanceMonitor = new PerformanceMonitor(); + addPerformanceMonitorToMap(name, performanceMonitor); + } + return performanceMonitor; + } - /** - * Find an existing {@link PerformanceMonitor} - * @param name the name for the {@link PerformanceMonitor} - * @return the found {@link PerformanceMonitor}, or null if it is not found. - */ - public PerformanceMonitor findPerformanceMonitor(String name) { - return performanceMonitorMap.get(name); - } + /** + * Find an existing {@link PerformanceMonitor} + * + * @param name the name for the {@link PerformanceMonitor} + * @return the found {@link PerformanceMonitor}, or null if it is not found. + */ + public PerformanceMonitor findPerformanceMonitor(String name) { + return performanceMonitorMap.get(name); + } - /** - * Get the {@link Set} of created performance monitor names. - * @return the {@link Set} of names. - */ - public Set getPerformanceMonitorNames() { - return performanceMonitorMap.keySet(); - } + /** + * Get the {@link Set} of created performance monitor names. + * + * @return the {@link Set} of names. + */ + public Set getPerformanceMonitorNames() { + return performanceMonitorMap.keySet(); + } - /** - * Add a {@link PerformanceMonitor} to the map. - * @param name the name for the {@link PerformanceMonitor} - * @param performanceMonitor the {@link PerformanceMonitor} to add. - */ - protected void addPerformanceMonitorToMap(String name, PerformanceMonitor performanceMonitor) { - performanceMonitorMap.put(name, performanceMonitor); - } + /** + * Add a {@link PerformanceMonitor} to the map. + * + * @param name the name for the {@link PerformanceMonitor} + * @param performanceMonitor the {@link PerformanceMonitor} to add. + */ + protected void addPerformanceMonitorToMap(String name, PerformanceMonitor performanceMonitor) { + performanceMonitorMap.put(name, performanceMonitor); + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/RequestCounter.java b/jrugged-core/src/main/java/org/fishwife/jrugged/RequestCounter.java index 909a495f..1e33da80 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/RequestCounter.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/RequestCounter.java @@ -18,96 +18,99 @@ import java.util.concurrent.Callable; -/** This is a statistics wrapper that counts total requests, as well - * as how many succeed and how many fail. This class can be polled - * periodically to measure request rates, success rates, and failure - * rates. +/** + * This is a statistics wrapper that counts total requests, as well as how many + * succeed and how many fail. This class can be polled periodically to measure + * request rates, success rates, and failure rates. */ public class RequestCounter implements ServiceWrapper { - private long numRequests = 0L; - private long numSuccesses = 0L; - private long numFailures = 0L; + private long numRequests = 0L; + private long numSuccesses = 0L; + private long numFailures = 0L; - /** Default constructor. */ - public RequestCounter() { } + /** Default constructor. */ + public RequestCounter() { + } - protected synchronized void succeed() { - numRequests++; - numSuccesses++; - } + protected synchronized void succeed() { + numRequests++; + numSuccesses++; + } - protected synchronized void fail() { - numRequests++; - numFailures++; - } + protected synchronized void fail() { + numRequests++; + numFailures++; + } - /** Wrap the given service call with the {@link RequestCounter} - * to count the number of calls made. - * @param c the {@link Callable} to attempt - * - * @return whatever c would return on success - * - * @throws Exception if c throws one during - * execution - */ - public T invoke(Callable c) throws Exception { - try { - T result = c.call(); - succeed(); - return result; - } catch (Exception e) { - fail(); - throw e; - } - } + /** + * Wrap the given service call with the {@link RequestCounter} to count the + * number of calls made. + * + * @param c the {@link Callable} to attempt + * + * @return whatever c would return on success + * + * @throws Exception if c throws one during execution + */ + public T invoke(Callable c) throws Exception { + try { + T result = c.call(); + succeed(); + return result; + } catch (Exception e) { + fail(); + throw e; + } + } - /** Wrap the given service call with the {@link RequestCounter} - * to count the number of calls made. - * @param r the {@link Runnable} to attempt - * - * @throws Exception if c throws one during - * execution - */ - public void invoke(Runnable r) throws Exception { - try { - r.run(); - succeed(); - } catch (Exception e) { - fail(); - throw e; - } - } + /** + * Wrap the given service call with the {@link RequestCounter} to count the + * number of calls made. + * + * @param r the {@link Runnable} to attempt + * + * @throws Exception if c throws one during execution + */ + public void invoke(Runnable r) throws Exception { + try { + r.run(); + succeed(); + } catch (Exception e) { + fail(); + throw e; + } + } - /** Wrap the given service call with the {@link RequestCounter} - * to count the number of calls made. - * @param r the {@link Runnable} to attempt - * @param result what to return after r succeeds - * - * @return result - * - * @throws Exception if c throws one during - * execution - */ - public T invoke(Runnable r, T result) throws Exception { - try { - r.run(); - succeed(); - return result; - } catch (Exception e) { - fail(); - throw e; - } - } + /** + * Wrap the given service call with the {@link RequestCounter} to count the + * number of calls made. + * + * @param r the {@link Runnable} to attempt + * @param result what to return after r succeeds + * + * @return result + * + * @throws Exception if c throws one during execution + */ + public T invoke(Runnable r, T result) throws Exception { + try { + r.run(); + succeed(); + return result; + } catch (Exception e) { + fail(); + throw e; + } + } - /** - * Samples the current counts. - * - * @return an array of three longs: the total - * number of requests, the number of successful requests, - * and the number of failed requests. - */ - public synchronized long[] sample() { - return new long[]{numRequests, numSuccesses, numFailures}; - } + /** + * Samples the current counts. + * + * @return an array of three longs: the total number of requests, + * the number of successful requests, and the number of failed requests. + */ + public synchronized long[] sample() { + return new long[] { numRequests, numSuccesses, numFailures }; + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/RolledUpMonitoredService.java b/jrugged-core/src/main/java/org/fishwife/jrugged/RolledUpMonitoredService.java index 5ace4b79..3ccbd694 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/RolledUpMonitoredService.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/RolledUpMonitoredService.java @@ -20,132 +20,134 @@ /** * A {@link RolledUpMonitoredService} provides for grouping - * {@link MonitoredService} instances together into a single system for status - * reporting purposes. Subsystems are divided into critical and - * noncritical groups. The system as a whole will only report UP - * status (GREEN) if all subsystems, critical and noncritical alike, are - * likewise UP. Critical systems are necessary for providing functionality - * within the system, so the system as a whole reports the lowest - * (least functional) status of any critical systems which are not UP. If all - * critical systems are UP, but one or more noncritical systems are not UP, - * then the system as a whole will be in the DEGRADED (YELLOW) state. + * {@link MonitoredService} instances together into a single system for status + * reporting purposes. Subsystems are divided into critical and + * noncritical groups. The system as a whole will only report UP status + * (GREEN) if all subsystems, critical and noncritical alike, are likewise UP. + * Critical systems are necessary for providing functionality within the system, + * so the system as a whole reports the lowest (least functional) status of any + * critical systems which are not UP. If all critical systems are UP, but one or + * more noncritical systems are not UP, then the system as a whole will be in + * the DEGRADED (YELLOW) state. */ public class RolledUpMonitoredService implements MonitoredService { - private String name; - private Collection criticals; - private Collection noncriticals; - - /** Initializes the {@link RolledUpMonitoredService} with its component - * subsystems. - * @param name the name for this rolled up service. - * @param criticals the set of {@link MonitoredService} subsystems - * that are necessary for the system as a whole to be functional - * @param noncriticals the set of MonitoredService subsystems - * that are part of the system as a whole but which are not - * strictly necessary for functionality. - */ - public RolledUpMonitoredService(String name, - Collection criticals, - Collection noncriticals) { - this.name = name; - this.criticals = criticals; - this.noncriticals = noncriticals; - } - - /** - * Get the {@link ServiceStatus} for the rolled up service. The - * name for the ServiceStatus is the name of this rolled up service. - * The {@link Status} is computed based on the status of the critical - * and non-critical subsystems. The {@link List} of reasons contains the - * reasons reported for each system. Each service returns the service - * name in addition to the reason that it has that status (which is - * reported by the service itself) - * - * @return the {@link ServiceStatus}. - */ - public ServiceStatus getServiceStatus() { - List reasons = new ArrayList(); - - Status criticalStatus = Status.UP; - - for (MonitoredService m : criticals) { - ServiceStatus serviceStatus = m.getServiceStatus(); - Status status = serviceStatus.getStatus(); - - if (statusIsNotUp(status)) { - for (String reason : serviceStatus.getReasons()) { - reasons.add(serviceStatus.getName() + ":" + reason); - } - } - - if (status.getValue() < criticalStatus.getValue()) { - criticalStatus = status; - } - } - - Status result = Status.UP; - - for (MonitoredService m : noncriticals) { - ServiceStatus serviceStatus = m.getServiceStatus(); - Status status = serviceStatus.getStatus(); - - if (statusIsNotUp(status)) { - for (String reason : serviceStatus.getReasons()) { - reasons.add(serviceStatus.getName() + ":" + reason); - } - result = Status.DEGRADED; - } - } - - if (criticalStatus.getValue() < result.getValue()) { - result = criticalStatus; - } - - return new ServiceStatus(name, result, reasons); - } - - /** - * Get the {@link List} of {@link ServiceStatus} instances for all - * subsystems. - * @return the {@link List} of {@link ServiceStatus} instances. - */ - public List getAllStatuses() { - List list = getCriticalStatuses(); - list.addAll(getNonCriticalStatuses()); - return list; - } - - /** - * Get the {@link List} of {@link ServiceStatus} instances for all - * critical subsystems. - * @return the {@link List} of {@link ServiceStatus} instances. - */ - public List getCriticalStatuses() { - List list = new ArrayList(); - - for (MonitoredService m : criticals) { - ServiceStatus serviceStatus = m.getServiceStatus(); - list.add(serviceStatus); - } - return list; - } - - /** - * Get the {@link List} of {@link ServiceStatus} instances for all - * non-critical subsystems. - * @return the {@link List} of {@link ServiceStatus} instances. - */ - public List getNonCriticalStatuses() { - List list = new ArrayList(); - - for (MonitoredService m : noncriticals) { - ServiceStatus serviceStatus = m.getServiceStatus(); - list.add(serviceStatus); - } - return list; - } - - private boolean statusIsNotUp(Status status) { - return Status.UP != status; - } + private String name; + private Collection criticals; + private Collection noncriticals; + + /** + * Initializes the {@link RolledUpMonitoredService} with its component + * subsystems. + * + * @param name the name for this rolled up service. + * @param criticals the set of {@link MonitoredService} subsystems that are + * necessary for the system as a whole to be functional + * @param noncriticals the set of MonitoredService subsystems that + * are part of the system as a whole but which are not + * strictly necessary for functionality. + */ + public RolledUpMonitoredService(String name, Collection criticals, + Collection noncriticals) { + this.name = name; + this.criticals = criticals; + this.noncriticals = noncriticals; + } + + /** + * Get the {@link ServiceStatus} for the rolled up service. The name for the + * ServiceStatus is the name of this rolled up service. The {@link Status} is + * computed based on the status of the critical and non-critical subsystems. The + * {@link List} of reasons contains the reasons reported for each system. Each + * service returns the service name in addition to the reason that it has that + * status (which is reported by the service itself) + * + * @return the {@link ServiceStatus}. + */ + public ServiceStatus getServiceStatus() { + List reasons = new ArrayList(); + + Status criticalStatus = Status.UP; + + for (MonitoredService m : criticals) { + ServiceStatus serviceStatus = m.getServiceStatus(); + Status status = serviceStatus.getStatus(); + + if (statusIsNotUp(status)) { + for (String reason : serviceStatus.getReasons()) { + reasons.add(serviceStatus.getName() + ":" + reason); + } + } + + if (status.getValue() < criticalStatus.getValue()) { + criticalStatus = status; + } + } + + Status result = Status.UP; + + for (MonitoredService m : noncriticals) { + ServiceStatus serviceStatus = m.getServiceStatus(); + Status status = serviceStatus.getStatus(); + + if (statusIsNotUp(status)) { + for (String reason : serviceStatus.getReasons()) { + reasons.add(serviceStatus.getName() + ":" + reason); + } + result = Status.DEGRADED; + } + } + + if (criticalStatus.getValue() < result.getValue()) { + result = criticalStatus; + } + + return new ServiceStatus(name, result, reasons); + } + + /** + * Get the {@link List} of {@link ServiceStatus} instances for all subsystems. + * + * @return the {@link List} of {@link ServiceStatus} instances. + */ + public List getAllStatuses() { + List list = getCriticalStatuses(); + list.addAll(getNonCriticalStatuses()); + return list; + } + + /** + * Get the {@link List} of {@link ServiceStatus} instances for all critical + * subsystems. + * + * @return the {@link List} of {@link ServiceStatus} instances. + */ + public List getCriticalStatuses() { + List list = new ArrayList(); + + for (MonitoredService m : criticals) { + ServiceStatus serviceStatus = m.getServiceStatus(); + list.add(serviceStatus); + } + return list; + } + + /** + * Get the {@link List} of {@link ServiceStatus} instances for all non-critical + * subsystems. + * + * @return the {@link List} of {@link ServiceStatus} instances. + */ + public List getNonCriticalStatuses() { + List list = new ArrayList(); + + for (MonitoredService m : noncriticals) { + ServiceStatus serviceStatus = m.getServiceStatus(); + list.add(serviceStatus); + } + return list; + } + + private boolean statusIsNotUp(Status status) { + return Status.UP != status; + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/SampledQuantile.java b/jrugged-core/src/main/java/org/fishwife/jrugged/SampledQuantile.java index 06f8eb25..4a56ec71 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/SampledQuantile.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/SampledQuantile.java @@ -25,242 +25,255 @@ import java.util.concurrent.atomic.AtomicLong; /** - * The {@link SampledQuantile} provides a way to compute - * approximate quantile measurements across a set of samples - * reported to an instance. By default, these samples are - * taken across the instance's lifetime, but a window can be - * configured to keep samples across just across that trailing - * time span (for example, getting a quantile across the last - * minute). We use an algorithm that keeps a fixed maximum number - * of samples that selects uniformly from all reported samples so - * far (thus representing a statistically appropriate sampling - * of the entire population). For the windowed version, we keep - * track of how many samples had been seen over twentieths of the - * window on a rolling basis to ensure that samples are chosen - * appropriately. + * The {@link SampledQuantile} provides a way to compute approximate quantile + * measurements across a set of samples reported to an instance. By default, + * these samples are taken across the instance's lifetime, but a window can be + * configured to keep samples across just across that trailing time span (for + * example, getting a quantile across the last minute). We use an algorithm that + * keeps a fixed maximum number of samples that selects uniformly from all + * reported samples so far (thus representing a statistically appropriate + * sampling of the entire population). For the windowed version, we keep track + * of how many samples had been seen over twentieths of the window on a rolling + * basis to ensure that samples are chosen appropriately. */ public class SampledQuantile { - private static final int NUM_WINDOW_SEGMENTS = 20; - private static final int DEFAULT_MAX_SAMPLES = 200; - - private List samples = new ArrayList(); - - private AtomicLong samplesSeen = new AtomicLong(0L); - private int maxSamples = DEFAULT_MAX_SAMPLES; - private Long windowMillis; - - private LinkedList windowSegments; - Random rand = new Random(); - - /** - * Creates a SampleQuantile that keeps a - * default number of samples across its lifetime. - */ - public SampledQuantile() { - this(DEFAULT_MAX_SAMPLES); - } - - /** - * Creates a SampleQuantile that keeps a - * given maximum number of samples across its lifetime. - * - * @param maxSamples the maximum number of samples to keep - */ - public SampledQuantile(int maxSamples) { - this.maxSamples = maxSamples; - } - - /** - * Creates a SampleQuantile that keeps a - * default number of samples across the specified time - * window. - * - * @param windowLength size of time window to hold onto samples - * @param units indication of what time units windowLength is specified in - */ - public SampledQuantile(long windowLength, TimeUnit units) { - this(DEFAULT_MAX_SAMPLES, windowLength, units); - } - - /** - * Creates a SampleQuantile that keeps a - * given maximum number of samples across the specified time - * window. - * - * @param maxSamples the maximum number of samples to keep inside of windowLength - * @param windowLength size of time window to hold onto samples - * @param units indication of what time units windowLength is specified in - */ - public SampledQuantile(int maxSamples, long windowLength, TimeUnit units) { - this(maxSamples, windowLength, units, System.currentTimeMillis()); - } - - SampledQuantile(int maxSamples, long windowLength, TimeUnit units, long now) { - this.maxSamples = maxSamples; - setWindowMillis(windowLength, units); - windowSegments = new LinkedList(); - windowSegments.offer(new Sample(samplesSeen.get(), now)); - } - - private void setWindowMillis(long windowLength, TimeUnit units) { - switch(units) { - case NANOSECONDS: windowMillis = windowLength / 1000000; break; - case MICROSECONDS: windowMillis = windowLength / 1000; break; - case MILLISECONDS: windowMillis = windowLength; break; - case SECONDS: windowMillis = windowLength * 1000; break; - default: throw new IllegalArgumentException("Unknown TimeUnit specified"); - } - } - - /** - * Returns the median of the samples seen thus far. - * - * @return long The median measurement - */ - public long getMedian() { - return getPercentile(50); - } - - /** - * Returns the ith percentile of the samples seen - * thus far. This is equivalent to getQuantile(i,100). - * - * @param i must be 0 < i < 100 - * @return i-th percentile, or 0 if there is no data - * @throws QuantileOutOfBoundsException if i <= 0 or i >= 100 - */ - public long getPercentile(int i) { - return getPercentile(i, System.currentTimeMillis()); - } - - long getPercentile(int i, long now) { - return getQuantile(i, 100, now); - } - - /** - * Returns the kth q-quantile of the samples - * seen thus far. - * - * @param q must be >= 2 - * @param k must be 0 < k < q - * @return k-th q-quantile, or 0 if there is no data - * @throws QuantileOutOfBoundsException if k <= 0 or k >= q - */ - public long getQuantile(int k, int q) { - return getQuantile(k, q, System.currentTimeMillis()); - } - - long getQuantile(int k, int q, long now) { - if (k <= 0 || k >= q) throw new QuantileOutOfBoundsException(); - - List validSamples = getValidSamples(now); - - if (validSamples.size() == 0) return 0; - - Collections.sort(validSamples); - double targetIndex = (validSamples.size() * k) / (q * 1.0); - - if (validSamples.size() % 2 == 1) { - return validSamples.get((int)Math.ceil(targetIndex) - 1).data; - } - - int i0 = (int)Math.floor(targetIndex) - 1; - return (validSamples.get(i0).data + validSamples.get(i0+1).data) / 2; - } - - private List getValidSamples(long now) { - if (windowMillis == null) return samples; - - long deadline = now - windowMillis; - List validSamples = new ArrayList(); - - for(Sample sample : samples) { - if (sample.timestamp >= deadline) { - validSamples.add(sample); - } - } - return validSamples; - } - - /** - * Reports the number of samples currently held by this - * SampleQuantile. - * - * @return int - */ - public int getNumSamples() { - return samples.size(); - } - - /** - * Reports a sample measurement to be included in the quantile - * calculations. - * - * @param l specific measurement - */ - public void addSample(long l) { - addSample(l, System.currentTimeMillis()); - } - - private synchronized void updateWindowSegments(long now) { - if (windowMillis == null) return; - - long deadline = now - windowMillis; - long segmentSize = windowMillis / NUM_WINDOW_SEGMENTS; - - while(windowSegments.size() > 0 && windowSegments.peek().timestamp < deadline) { - windowSegments.remove(); - } - - long mostRecentSegmentTimestamp = (windowSegments.size() > 0) ? - windowSegments.getLast().timestamp : 0L; - - if (windowSegments.size() == 0 - || now - mostRecentSegmentTimestamp > segmentSize) { - windowSegments.offer(new Sample(samplesSeen.get(), now)); - } - } - - private long getEffectiveSamplesSeen() { - if (windowMillis == null) return samplesSeen.get(); - return (samplesSeen.get() - windowSegments.getFirst().data); - } - - void addSample(long l, long now) { - samplesSeen.getAndIncrement(); - updateWindowSegments(now); - - if (samples.size() < maxSamples) { - samples.add(new Sample(l, now)); - return; - } - - if (rand.nextDouble() < maxSamples * 1.0 / getEffectiveSamplesSeen()) { - int idx = rand.nextInt(maxSamples); - samples.set(idx, new Sample(l, now)); - } - } - - private static class Sample implements Comparable { - public long data; - public long timestamp; - - public Sample(long data, long timestamp) { - this.data = data; - this.timestamp = timestamp; - } - - public int compareTo(Sample other) { - if (other.data > data) return -1; - if (other.data < data) return 1; - if (other.timestamp > timestamp) return -1; - if (other.timestamp < timestamp) return 1; - return 0; - } - } - - public static class QuantileOutOfBoundsException extends RuntimeException { - private static final long serialVersionUID = 1L; - } + private static final int NUM_WINDOW_SEGMENTS = 20; + private static final int DEFAULT_MAX_SAMPLES = 200; + + private List samples = new ArrayList(); + + private AtomicLong samplesSeen = new AtomicLong(0L); + private int maxSamples = DEFAULT_MAX_SAMPLES; + private Long windowMillis; + + private LinkedList windowSegments; + Random rand = new Random(); + + /** + * Creates a SampleQuantile that keeps a default number of samples + * across its lifetime. + */ + public SampledQuantile() { + this(DEFAULT_MAX_SAMPLES); + } + + /** + * Creates a SampleQuantile that keeps a given maximum number of + * samples across its lifetime. + * + * @param maxSamples the maximum number of samples to keep + */ + public SampledQuantile(int maxSamples) { + this.maxSamples = maxSamples; + } + + /** + * Creates a SampleQuantile that keeps a default number of samples + * across the specified time window. + * + * @param windowLength size of time window to hold onto samples + * @param units indication of what time units windowLength is specified + * in + */ + public SampledQuantile(long windowLength, TimeUnit units) { + this(DEFAULT_MAX_SAMPLES, windowLength, units); + } + + /** + * Creates a SampleQuantile that keeps a given maximum number of + * samples across the specified time window. + * + * @param maxSamples the maximum number of samples to keep inside of + * windowLength + * @param windowLength size of time window to hold onto samples + * @param units indication of what time units windowLength is specified + * in + */ + public SampledQuantile(int maxSamples, long windowLength, TimeUnit units) { + this(maxSamples, windowLength, units, System.currentTimeMillis()); + } + + SampledQuantile(int maxSamples, long windowLength, TimeUnit units, long now) { + this.maxSamples = maxSamples; + setWindowMillis(windowLength, units); + windowSegments = new LinkedList(); + windowSegments.offer(new Sample(samplesSeen.get(), now)); + } + + private void setWindowMillis(long windowLength, TimeUnit units) { + switch (units) { + case NANOSECONDS: + windowMillis = windowLength / 1000000; + break; + case MICROSECONDS: + windowMillis = windowLength / 1000; + break; + case MILLISECONDS: + windowMillis = windowLength; + break; + case SECONDS: + windowMillis = windowLength * 1000; + break; + default: + throw new IllegalArgumentException("Unknown TimeUnit specified"); + } + } + + /** + * Returns the median of the samples seen thus far. + * + * @return long The median measurement + */ + public long getMedian() { + return getPercentile(50); + } + + /** + * Returns the ith percentile of the samples seen thus far. This is + * equivalent to getQuantile(i,100). + * + * @param i must be 0 < i < 100 + * @return i-th percentile, or 0 if there is no data + * @throws QuantileOutOfBoundsException if i <= 0 or i >= 100 + */ + public long getPercentile(int i) { + return getPercentile(i, System.currentTimeMillis()); + } + + long getPercentile(int i, long now) { + return getQuantile(i, 100, now); + } + + /** + * Returns the kth q-quantile of the samples seen thus + * far. + * + * @param q must be >= 2 + * @param k must be 0 < k < q + * @return k-th q-quantile, or 0 if there is no data + * @throws QuantileOutOfBoundsException if k <= 0 or k >= q + */ + public long getQuantile(int k, int q) { + return getQuantile(k, q, System.currentTimeMillis()); + } + + long getQuantile(int k, int q, long now) { + if (k <= 0 || k >= q) + throw new QuantileOutOfBoundsException(); + + List validSamples = getValidSamples(now); + + if (validSamples.size() == 0) + return 0; + + Collections.sort(validSamples); + double targetIndex = (validSamples.size() * k) / (q * 1.0); + + if (validSamples.size() % 2 == 1) { + return validSamples.get((int) Math.ceil(targetIndex) - 1).data; + } + + int i0 = (int) Math.floor(targetIndex) - 1; + return (validSamples.get(i0).data + validSamples.get(i0 + 1).data) / 2; + } + + private List getValidSamples(long now) { + if (windowMillis == null) + return samples; + + long deadline = now - windowMillis; + List validSamples = new ArrayList(); + + for (Sample sample : samples) { + if (sample.timestamp >= deadline) { + validSamples.add(sample); + } + } + return validSamples; + } + + /** + * Reports the number of samples currently held by this + * SampleQuantile. + * + * @return int + */ + public int getNumSamples() { + return samples.size(); + } + + /** + * Reports a sample measurement to be included in the quantile calculations. + * + * @param l specific measurement + */ + public void addSample(long l) { + addSample(l, System.currentTimeMillis()); + } + + private synchronized void updateWindowSegments(long now) { + if (windowMillis == null) + return; + + long deadline = now - windowMillis; + long segmentSize = windowMillis / NUM_WINDOW_SEGMENTS; + + while (windowSegments.size() > 0 && windowSegments.peek().timestamp < deadline) { + windowSegments.remove(); + } + + long mostRecentSegmentTimestamp = (windowSegments.size() > 0) ? windowSegments.getLast().timestamp : 0L; + + if (windowSegments.size() == 0 || now - mostRecentSegmentTimestamp > segmentSize) { + windowSegments.offer(new Sample(samplesSeen.get(), now)); + } + } + + private long getEffectiveSamplesSeen() { + if (windowMillis == null) + return samplesSeen.get(); + return (samplesSeen.get() - windowSegments.getFirst().data); + } + + void addSample(long l, long now) { + samplesSeen.getAndIncrement(); + updateWindowSegments(now); + + if (samples.size() < maxSamples) { + samples.add(new Sample(l, now)); + return; + } + + if (rand.nextDouble() < maxSamples * 1.0 / getEffectiveSamplesSeen()) { + int idx = rand.nextInt(maxSamples); + samples.set(idx, new Sample(l, now)); + } + } + + private static class Sample implements Comparable { + public long data; + public long timestamp; + + public Sample(long data, long timestamp) { + this.data = data; + this.timestamp = timestamp; + } + + public int compareTo(Sample other) { + if (other.data > data) + return -1; + if (other.data < data) + return 1; + if (other.timestamp > timestamp) + return -1; + if (other.timestamp < timestamp) + return 1; + return 0; + } + } + + public static class QuantileOutOfBoundsException extends RuntimeException { + private static final long serialVersionUID = 1L; + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceRetrier.java b/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceRetrier.java index 7f6474dc..2a9ca82f 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceRetrier.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceRetrier.java @@ -25,153 +25,151 @@ */ public class ServiceRetrier implements ServiceWrapper { - public static final int DEFAULT_MAX_TRIES = 10; - public static final int DEFAULT_DELAY = 1000; - - private int _delay = DEFAULT_DELAY; - private int _maxTries = DEFAULT_MAX_TRIES; - private boolean _doubleDelay = false; - private boolean _throwCauseException = false; - private Class[] _retryOn = null; - - public ServiceRetrier(int delay, int maxTries) { - setDelay(delay); - setMaxTries(maxTries); - } - - public ServiceRetrier(int delay, int maxTries, - boolean doubleDelay, boolean throwCauseException, Class[] retryOn) { - setDelay(delay); - setMaxTries(maxTries); - setDoubleDelay(doubleDelay); - setThrowCauseException(throwCauseException); - setRetryOn(retryOn); - } - - public ServiceRetrier() { - } - - public V invoke(Callable c) throws Exception { - - int tries = 0; - int delay = _delay; - - while (true) { - try { - return c.call(); - } catch (Exception cause) { - - // If this type of Exception should be retried... - if (shouldRetry(cause)) { - tries++; - - // Don't delay after max tries reached. - if (tries < _maxTries) { - - if (delay > 0) { - sleep(delay); - } - - // Double the next delay if configured to do so. - if (_doubleDelay) { - delay = delay * 2; - } - - // Try again. - continue; - } - } - - if (_throwCauseException) { - throw cause; - } - else { - throw new Exception("Call failed " + tries + " times", cause); - } - } - } - } - - private boolean shouldRetry(Throwable cause) { - if (_retryOn == null || _retryOn.length == 0) { - return true; - } - - for (Class clazz : _retryOn) { - if (clazz.isInstance(cause)) { - return true; - } - } - return false; - } - - public void invoke(Runnable r) throws Exception { - - Callable adapter = new CallableAdapter(r); - invoke(adapter); - } - - public T invoke(Runnable r, T result) throws Exception { - - Callable adapter = new CallableAdapter(r, result); - return invoke(adapter); - } - - public int getDelay() { - return _delay; - } - - public void setDelay(int delay) { - - if (delay < 0) { - throw new IllegalArgumentException("Delay cannot be negative"); - } - - this._delay = delay; - } - - public int getMaxTries() { - return _maxTries; - } - - public void setMaxTries(int maxTries) { - - if (maxTries < 1) - throw new IllegalArgumentException("Maximum number of tries must be greater than zero"); - - this._maxTries = maxTries; - } - - public boolean isDoubleDelay() { - return _doubleDelay; - } - - public void setDoubleDelay(boolean doubleDelay) { - this._doubleDelay = doubleDelay; - } - - public boolean isThrowCauseException() { - return _throwCauseException; - } - - public void setThrowCauseException(boolean throwCauseException) { - this._throwCauseException = throwCauseException; - } - - public Class[] getRetryOn() { - return _retryOn; - } - - public void setRetryOn(Class[] retryOn) { - this._retryOn = retryOn; - } - - protected void sleep(long millis) { - try { - Thread.sleep(millis); - } - catch (InterruptedException e) { - // Nothing much to do here. - } - } + public static final int DEFAULT_MAX_TRIES = 10; + public static final int DEFAULT_DELAY = 1000; + + private int _delay = DEFAULT_DELAY; + private int _maxTries = DEFAULT_MAX_TRIES; + private boolean _doubleDelay = false; + private boolean _throwCauseException = false; + private Class[] _retryOn = null; + + public ServiceRetrier(int delay, int maxTries) { + setDelay(delay); + setMaxTries(maxTries); + } + + public ServiceRetrier(int delay, int maxTries, boolean doubleDelay, boolean throwCauseException, + Class[] retryOn) { + setDelay(delay); + setMaxTries(maxTries); + setDoubleDelay(doubleDelay); + setThrowCauseException(throwCauseException); + setRetryOn(retryOn); + } + + public ServiceRetrier() { + } + + public V invoke(Callable c) throws Exception { + + int tries = 0; + int delay = _delay; + + while (true) { + try { + return c.call(); + } catch (Exception cause) { + + // If this type of Exception should be retried... + if (shouldRetry(cause)) { + tries++; + + // Don't delay after max tries reached. + if (tries < _maxTries) { + + if (delay > 0) { + sleep(delay); + } + + // Double the next delay if configured to do so. + if (_doubleDelay) { + delay = delay * 2; + } + + // Try again. + continue; + } + } + + if (_throwCauseException) { + throw cause; + } else { + throw new Exception("Call failed " + tries + " times", cause); + } + } + } + } + + private boolean shouldRetry(Throwable cause) { + if (_retryOn == null || _retryOn.length == 0) { + return true; + } + + for (Class clazz : _retryOn) { + if (clazz.isInstance(cause)) { + return true; + } + } + return false; + } + + public void invoke(Runnable r) throws Exception { + + Callable adapter = new CallableAdapter(r); + invoke(adapter); + } + + public T invoke(Runnable r, T result) throws Exception { + + Callable adapter = new CallableAdapter(r, result); + return invoke(adapter); + } + + public int getDelay() { + return _delay; + } + + public void setDelay(int delay) { + + if (delay < 0) { + throw new IllegalArgumentException("Delay cannot be negative"); + } + + this._delay = delay; + } + + public int getMaxTries() { + return _maxTries; + } + + public void setMaxTries(int maxTries) { + + if (maxTries < 1) + throw new IllegalArgumentException("Maximum number of tries must be greater than zero"); + + this._maxTries = maxTries; + } + + public boolean isDoubleDelay() { + return _doubleDelay; + } + + public void setDoubleDelay(boolean doubleDelay) { + this._doubleDelay = doubleDelay; + } + + public boolean isThrowCauseException() { + return _throwCauseException; + } + + public void setThrowCauseException(boolean throwCauseException) { + this._throwCauseException = throwCauseException; + } + + public Class[] getRetryOn() { + return _retryOn; + } + + public void setRetryOn(Class[] retryOn) { + this._retryOn = retryOn; + } + + protected void sleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + // Nothing much to do here. + } + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceStatus.java b/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceStatus.java index 99322838..ad82e821 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceStatus.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceStatus.java @@ -25,72 +25,78 @@ * service, and the {@link List} of reasons for the status. */ public class ServiceStatus { - private final String name; - private final Status status; - private final List reasons; + private final String name; + private final Status status; + private final List reasons; - /** - * Constructor with name and {@link Status}. - * @param name the name of the service. - * @param status the {@link Status} of the service. - */ - public ServiceStatus(String name, Status status) { - this.name = name; - this.status = status; - this.reasons = new ArrayList(); - } + /** + * Constructor with name and {@link Status}. + * + * @param name the name of the service. + * @param status the {@link Status} of the service. + */ + public ServiceStatus(String name, Status status) { + this.name = name; + this.status = status; + this.reasons = new ArrayList(); + } - /** - * Constructor with name, {@link Status}, and reason. - * @param name the name of the service. - * @param status the {@link Status} of the service. - * @param reason the reason for the status. - */ - public ServiceStatus(String name, Status status, String reason) { - this.name = name; - this.status = status; - this.reasons = new ArrayList(); - if (reason != null) { - reasons.add(reason); - } - } + /** + * Constructor with name, {@link Status}, and reason. + * + * @param name the name of the service. + * @param status the {@link Status} of the service. + * @param reason the reason for the status. + */ + public ServiceStatus(String name, Status status, String reason) { + this.name = name; + this.status = status; + this.reasons = new ArrayList(); + if (reason != null) { + reasons.add(reason); + } + } - /** - * Constructor with name, {@link Status}, and a {@link List} of reasons. - * @param name the name of the service. - * @param status the {@link Status} of the service. - * @param reasons the {@link List} of reasons for the status. - */ - public ServiceStatus(String name, Status status, List reasons) { - this.name = name; - this.status = status; - this.reasons = new ArrayList(); - if (reasons != null) { - this.reasons.addAll(reasons); - } - } + /** + * Constructor with name, {@link Status}, and a {@link List} of reasons. + * + * @param name the name of the service. + * @param status the {@link Status} of the service. + * @param reasons the {@link List} of reasons for the status. + */ + public ServiceStatus(String name, Status status, List reasons) { + this.name = name; + this.status = status; + this.reasons = new ArrayList(); + if (reasons != null) { + this.reasons.addAll(reasons); + } + } - /** - * Get the name of the service. - * @return the name. - */ - public String getName() { - return name; - } + /** + * Get the name of the service. + * + * @return the name. + */ + public String getName() { + return name; + } - /** - * Get the {@link Status} of the service. - * @return the {@link Status}. - */ - public Status getStatus () { - return status; - } + /** + * Get the {@link Status} of the service. + * + * @return the {@link Status}. + */ + public Status getStatus() { + return status; + } - /** - * Get the {@link List} of reasons for the status. - * @return the {@link List} of reasons. - */ - public List getReasons() { - return new ArrayList(reasons); - } + /** + * Get the {@link List} of reasons for the status. + * + * @return the {@link List} of reasons. + */ + public List getReasons() { + return new ArrayList(reasons); + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceWrapper.java b/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceWrapper.java index f06b41c9..84a5797b 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceWrapper.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceWrapper.java @@ -18,36 +18,43 @@ import java.util.concurrent.Callable; -/** Several of the "rugged" code patterns in this library can be used to - * wrap an existing service in a "decorator" design pattern. This is the - * common interface provided by these wrapping classes that affect normal - * service calls. - *

- * If you are more into aspect-oriented programming (AOP), then you can - * also use the {@link ServiceWrapper} classes in your aspects. +/** + * Several of the "rugged" code patterns in this library can be used to wrap an + * existing service in a "decorator" design pattern. This is the common + * interface provided by these wrapping classes that affect normal service + * calls. + *

+ * If you are more into aspect-oriented programming (AOP), then you can also use + * the {@link ServiceWrapper} classes in your aspects. */ public interface ServiceWrapper { - /** Wraps a {@link Callable} in some fashion. - * @param c the service call to wrap - * @param The callable type I am wrapping - * @return whatever c would normally return - * @throws Exception if c throws one - */ - T invoke(Callable c) throws Exception; + /** + * Wraps a {@link Callable} in some fashion. + * + * @param c the service call to wrap + * @param The callable type I am wrapping + * @return whatever c would normally return + * @throws Exception if c throws one + */ + T invoke(Callable c) throws Exception; - /** Wraps a {@link Runnable} in some fashion. - * @param r the service call/task to wrap - * @throws Exception if r throws one - */ - void invoke(Runnable r) throws Exception; + /** + * Wraps a {@link Runnable} in some fashion. + * + * @param r the service call/task to wrap + * @throws Exception if r throws one + */ + void invoke(Runnable r) throws Exception; - /** Wraps a {@link Runnable} task in some fashion, and returns a - * predetermined result on success. - * @param r the service call/task to wrap - * @param result what to return on success - * @param The return TYPE that should emit - * @return result - * @throws Exception if r throws one - */ - T invoke(Runnable r, T result) throws Exception; + /** + * Wraps a {@link Runnable} task in some fashion, and returns a predetermined + * result on success. + * + * @param r the service call/task to wrap + * @param result what to return on success + * @param The return TYPE that should emit + * @return result + * @throws Exception if r throws one + */ + T invoke(Runnable r, T result) throws Exception; } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceWrapperChain.java b/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceWrapperChain.java index df7f92a2..4fd61acf 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceWrapperChain.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceWrapperChain.java @@ -23,53 +23,53 @@ public class ServiceWrapperChain implements ServiceWrapper { - private List wrappers; + private List wrappers; - public ServiceWrapperChain(Collection wrappers) { - ArrayList rev = new ArrayList(); - for(ServiceWrapper wrapper : wrappers) { - rev.add(0, wrapper); - } - this.wrappers = rev; - } + public ServiceWrapperChain(Collection wrappers) { + ArrayList rev = new ArrayList(); + for (ServiceWrapper wrapper : wrappers) { + rev.add(0, wrapper); + } + this.wrappers = rev; + } - private Callable wrap(final Callable c, final ServiceWrapper wrapper) { - return new Callable() { - public T call() throws Exception { - return wrapper.invoke(c); - } - }; - } + private Callable wrap(final Callable c, final ServiceWrapper wrapper) { + return new Callable() { + public T call() throws Exception { + return wrapper.invoke(c); + } + }; + } - private Runnable wrap(final Runnable r, final ServiceWrapper wrapper) { - return new Runnable() { - public void run() { - try { - wrapper.invoke(r); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }; - } + private Runnable wrap(final Runnable r, final ServiceWrapper wrapper) { + return new Runnable() { + public void run() { + try { + wrapper.invoke(r); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }; + } - public T invoke(Callable c) throws Exception { - for(ServiceWrapper wrapper : wrappers) { - c = wrap(c, wrapper); - } - return c.call(); - } + public T invoke(Callable c) throws Exception { + for (ServiceWrapper wrapper : wrappers) { + c = wrap(c, wrapper); + } + return c.call(); + } - public void invoke(Runnable r) throws Exception { - for(ServiceWrapper wrapper : wrappers) { - r = wrap(r, wrapper); - } - r.run(); - } + public void invoke(Runnable r) throws Exception { + for (ServiceWrapper wrapper : wrappers) { + r = wrap(r, wrapper); + } + r.run(); + } - public T invoke(Runnable r, T result) throws Exception { - invoke(r); - return result; - } + public T invoke(Runnable r, T result) throws Exception { + invoke(r); + return result; + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceWrapperFactory.java b/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceWrapperFactory.java index 8dfca610..33af70bf 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceWrapperFactory.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/ServiceWrapperFactory.java @@ -17,17 +17,18 @@ package org.fishwife.jrugged; /** - * A {@link ServiceWrapperFactory} is a way to create several related - * but distinct {@link ServiceWrapper} instances. + * A {@link ServiceWrapperFactory} is a way to create several related but + * distinct {@link ServiceWrapper} instances. * */ public interface ServiceWrapperFactory { - /** - * Create a new ServiceWrapper using the given - * (presumed unique) identifier. - * @param name to apply to the new ServiceWrapper - * @return ServiceWrapper - */ - ServiceWrapper getWrapperWithName(String name); + /** + * Create a new ServiceWrapper using the given (presumed unique) + * identifier. + * + * @param name to apply to the new ServiceWrapper + * @return ServiceWrapper + */ + ServiceWrapper getWrapperWithName(String name); } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/Status.java b/jrugged-core/src/main/java/org/fishwife/jrugged/Status.java index c8e6f104..226a7ae6 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/Status.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/Status.java @@ -16,78 +16,85 @@ */ package org.fishwife.jrugged; -/** Various components in the system need to report their status. This - * enum standardizes on a set of conventions for reporting - * this. Each {@link Status} has an integer value which can - * be compared; higher values represent higher levels of functionality. - * In addition, each {@link Status} has a {@link String} signal - * which is either GREEN, YELLOW, or RED. - *

- * The general notion is that the whole system (or a subsystem) should - * only report GREEN status (UP) if everything is working as designed. - * When subsystems start to go down, or the current system stops working, - * status should drop to either YELLOW (DEGRADED) or RED (DOWN), depending - * on whether service was still available in some form. For example, if - * a cache subsystem goes down, we might report a YELLOW status because - * we can attempt to serve without a cache. However, if a required database - * goes down, we probably need to report a RED status, unable to serve - * requests. - *

- * A "rugged" system should be able to accurately (and responsively) - * report on its status even if it is unable to perform its main functions. - * This will assist operators in diagnosing the problem; a hung process - * tells no tales. +/** + * Various components in the system need to report their status. This + * enum standardizes on a set of conventions for reporting this. + * Each {@link Status} has an integer value which can be compared; + * higher values represent higher levels of functionality. In addition, each + * {@link Status} has a {@link String} signal which is either + * GREEN, YELLOW, or RED. + *

+ * The general notion is that the whole system (or a subsystem) should only + * report GREEN status (UP) if everything is working as designed. When + * subsystems start to go down, or the current system stops working, status + * should drop to either YELLOW (DEGRADED) or RED (DOWN), depending on whether + * service was still available in some form. For example, if a cache subsystem + * goes down, we might report a YELLOW status because we can attempt to serve + * without a cache. However, if a required database goes down, we probably need + * to report a RED status, unable to serve requests. + *

+ * A "rugged" system should be able to accurately (and responsively) report on + * its status even if it is unable to perform its main functions. This will + * assist operators in diagnosing the problem; a hung process tells no tales. */ public enum Status { - /** Unrecoverable: we're basically dead for good. */ - FAILED(-2,"RED"), + /** Unrecoverable: we're basically dead for good. */ + FAILED(-2, "RED"), - /** Being initialized: not yet ready to serve. */ - INIT(-1,"RED"), + /** Being initialized: not yet ready to serve. */ + INIT(-1, "RED"), - /** Currently unavailable. */ - DOWN(0,"RED"), + /** Currently unavailable. */ + DOWN(0, "RED"), - /** Something's wrong, but we can still serve requests. */ - DEGRADED(1,"YELLOW"), + /** Something's wrong, but we can still serve requests. */ + DEGRADED(1, "YELLOW"), - /** Everything is operating as expected. */ - UP(2,"GREEN"), + /** Everything is operating as expected. */ + UP(2, "GREEN"), - /** - * This status was introduced in a prior version of JRugged and so - * is retained for backwards compatibility, but is specific - * to {@link org.fishwife.jrugged.CircuitBreaker} states - * and should not be used. - */ - @Deprecated - BYPASS(3, "GREEN"); + /** + * This status was introduced in a prior version of JRugged and so is retained + * for backwards compatibility, but is specific to + * {@link org.fishwife.jrugged.CircuitBreaker} states and should not be used. + */ + @Deprecated + BYPASS(3, "GREEN"); - private final int value; - private final String signal; + private final int value; + private final String signal; - Status(int value, String signal) { - this.value = value; - this.signal = signal; - } + Status(int value, String signal) { + this.value = value; + this.signal = signal; + } - /** Returns the current status as an integer. Higher values are for - * states of higher functionality/availability. - * @return int The value of current status - */ - public int getValue() { return value; } + /** + * Returns the current status as an integer. Higher values are for states of + * higher functionality/availability. + * + * @return int The value of current status + */ + public int getValue() { + return value; + } - /** Returns the current GREEN/YELLOW/RED dashboard indicator - * corresponding to this status. - * - * @return String The current signalling, RED, YELLOW, ETC. - */ - public String getSignal() { return signal; } + /** + * Returns the current GREEN/YELLOW/RED dashboard indicator corresponding to + * this status. + * + * @return String The current signalling, RED, YELLOW, ETC. + */ + public String getSignal() { + return signal; + } - /** Returns whether the status indicates a system that is able - * to serve requests. - * - * @return boolean Greater than ZERO settings mean availability - */ - public boolean isAvailable() { return (value > 0); } + /** + * Returns whether the status indicates a system that is able to serve requests. + * + * @return boolean Greater than ZERO settings mean availability + */ + public boolean isAvailable() { + return (value > 0); + } }; diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/SystemClock.java b/jrugged-core/src/main/java/org/fishwife/jrugged/SystemClock.java index 5a36c3ed..20c45d66 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/SystemClock.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/SystemClock.java @@ -21,15 +21,15 @@ */ public class SystemClock implements Clock { - /** - * Returns the current system time in milliseconds. - * - * @return the difference, measured in milliseconds, between the current - * time and midnight, January 1, 1970 UTC. - * @see java.lang.System#currentTimeMillis() - */ - public long currentTimeMillis() { - return System.currentTimeMillis(); - } + /** + * Returns the current system time in milliseconds. + * + * @return the difference, measured in milliseconds, between the current time + * and midnight, January 1, 1970 UTC. + * @see java.lang.System#currentTimeMillis() + */ + public long currentTimeMillis() { + return System.currentTimeMillis(); + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/WindowedEventCounter.java b/jrugged-core/src/main/java/org/fishwife/jrugged/WindowedEventCounter.java index 6d95d17a..51894936 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/WindowedEventCounter.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/WindowedEventCounter.java @@ -24,162 +24,152 @@ */ public class WindowedEventCounter { - /** - * The {@link Clock} to used to determine current time (override for - * testing). - */ - private Clock clock = new SystemClock(); - - /** - * Length of the window in milliseconds. - */ - private long windowMillis; - - /** - * Storage for the internal queue. - */ - private final LinkedList queue = new LinkedList(); - - /** - * The maximum count this WindowedEventCounter can hold. Also, the maximum queue - * size. Immutable. - */ - private int capacity; - - /** - * Sole constructor. - * - * @param capacity - * maximum count this WindowedEventCounter can hold. - * @param windowMillis - * length of the interest window in milliseconds. - * @throws IllegalArgumentException - * if capacity is less than 1 or if windowMillis is less than 1. - */ - public WindowedEventCounter(int capacity, long windowMillis) { - if (capacity <= 0) { - throw new IllegalArgumentException( - "capacity must be greater than 0"); - } - if (windowMillis <= 0) { - throw new IllegalArgumentException( - "windowMillis must be greater than 0"); - } - this.windowMillis = windowMillis; - this.capacity = capacity; - } - - /** - * Record a new event. - */ - public void mark() { - final long currentTimeMillis = clock.currentTimeMillis(); - - synchronized (queue) { - if (queue.size() == capacity) { - /* - * we're all filled up already, let's dequeue the oldest - * timestamp to make room for this new one. - */ - queue.removeFirst(); - } - queue.addLast(currentTimeMillis); - } - } - - /** - * Returns a count of in-window events. - * - * @return the the count of in-window events. - */ - public int tally() { - long currentTimeMillis = clock.currentTimeMillis(); - - // calculates time for which we remove any errors before - final long removeTimesBeforeMillis = currentTimeMillis - windowMillis; - - synchronized (queue) { - // drain out any expired timestamps but don't drain past empty - while (!queue.isEmpty() && queue.peek() < removeTimesBeforeMillis) { - queue.removeFirst(); - } - return queue.size(); - } - } - - /** - * Returns the length of the currently configured event window in - * milliseconds. - * - * @return long - */ - public long getWindowMillis() { - return windowMillis; - } - - /** - * Specifies the maximum capacity of the counter. - * - * @param capacity - * long - * @throws IllegalArgumentException - * if windowMillis is less than 1. - */ - public void setCapacity(int capacity) { - if (capacity <= 0) { - throw new IllegalArgumentException("capacity must be greater than 0"); - } - - synchronized (queue) { - // If the capacity was reduced, we remove oldest elements until the - // queue fits inside the specified capacity - if (capacity < this.capacity) { - while (queue.size() > capacity) { - queue.removeFirst(); - } - } - } - - this.capacity = capacity; - } - - /** - * Returns the maximum capacity this counter can hold. - * - * @return int - */ - public int getCapacity() { - return capacity; - } - - /** - * Specifies the length of the interest window in milliseconds. - * - * @param windowMillis - * long - * @throws IllegalArgumentException - * if windowMillis is less than 1. - */ - public void setWindowMillis(long windowMillis) { - if (windowMillis <= 0) { - throw new IllegalArgumentException("windowMillis must be greater than 0"); - } - - // changing windowMillis while tally() is draining expired events could - // lead to weirdness, let's lock for this. - synchronized (queue) { - this.windowMillis = windowMillis; - } - } - - /** - * Allow the internal {@link Clock} that is used for current time to be - * overridden (for testing). - * - * @param clock Clock - */ - protected void setClock(Clock clock) { - this.clock = clock; - } + /** + * The {@link Clock} to used to determine current time (override for testing). + */ + private Clock clock = new SystemClock(); + + /** + * Length of the window in milliseconds. + */ + private long windowMillis; + + /** + * Storage for the internal queue. + */ + private final LinkedList queue = new LinkedList(); + + /** + * The maximum count this WindowedEventCounter can hold. Also, the maximum queue + * size. Immutable. + */ + private int capacity; + + /** + * Sole constructor. + * + * @param capacity maximum count this WindowedEventCounter can hold. + * @param windowMillis length of the interest window in milliseconds. + * @throws IllegalArgumentException if capacity is less than 1 or if + * windowMillis is less than 1. + */ + public WindowedEventCounter(int capacity, long windowMillis) { + if (capacity <= 0) { + throw new IllegalArgumentException("capacity must be greater than 0"); + } + if (windowMillis <= 0) { + throw new IllegalArgumentException("windowMillis must be greater than 0"); + } + this.windowMillis = windowMillis; + this.capacity = capacity; + } + + /** + * Record a new event. + */ + public void mark() { + final long currentTimeMillis = clock.currentTimeMillis(); + + synchronized (queue) { + if (queue.size() == capacity) { + /* + * we're all filled up already, let's dequeue the oldest timestamp to make room + * for this new one. + */ + queue.removeFirst(); + } + queue.addLast(currentTimeMillis); + } + } + + /** + * Returns a count of in-window events. + * + * @return the the count of in-window events. + */ + public int tally() { + long currentTimeMillis = clock.currentTimeMillis(); + + // calculates time for which we remove any errors before + final long removeTimesBeforeMillis = currentTimeMillis - windowMillis; + + synchronized (queue) { + // drain out any expired timestamps but don't drain past empty + while (!queue.isEmpty() && queue.peek() < removeTimesBeforeMillis) { + queue.removeFirst(); + } + return queue.size(); + } + } + + /** + * Returns the length of the currently configured event window in milliseconds. + * + * @return long + */ + public long getWindowMillis() { + return windowMillis; + } + + /** + * Specifies the maximum capacity of the counter. + * + * @param capacity long + * @throws IllegalArgumentException if windowMillis is less than 1. + */ + public void setCapacity(int capacity) { + if (capacity <= 0) { + throw new IllegalArgumentException("capacity must be greater than 0"); + } + + synchronized (queue) { + // If the capacity was reduced, we remove oldest elements until the + // queue fits inside the specified capacity + if (capacity < this.capacity) { + while (queue.size() > capacity) { + queue.removeFirst(); + } + } + } + + this.capacity = capacity; + } + + /** + * Returns the maximum capacity this counter can hold. + * + * @return int + */ + public int getCapacity() { + return capacity; + } + + /** + * Specifies the length of the interest window in milliseconds. + * + * @param windowMillis long + * @throws IllegalArgumentException if windowMillis is less than 1. + */ + public void setWindowMillis(long windowMillis) { + if (windowMillis <= 0) { + throw new IllegalArgumentException("windowMillis must be greater than 0"); + } + + // changing windowMillis while tally() is draining expired events could + // lead to weirdness, let's lock for this. + synchronized (queue) { + this.windowMillis = windowMillis; + } + } + + /** + * Allow the internal {@link Clock} that is used for current time to be + * overridden (for testing). + * + * @param clock Clock + */ + protected void setClock(Clock clock) { + this.clock = clock; + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/WrappedException.java b/jrugged-core/src/main/java/org/fishwife/jrugged/WrappedException.java index ac1a8c9b..9e08e180 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/WrappedException.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/WrappedException.java @@ -18,9 +18,9 @@ public class WrappedException extends RuntimeException { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - public WrappedException(Throwable cause) { - super(cause); - } + public WrappedException(Throwable cause) { + super(cause); + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/clocks/DefaultHardwareClock.java b/jrugged-core/src/main/java/org/fishwife/jrugged/clocks/DefaultHardwareClock.java index 5cd61f78..3c714ce3 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/clocks/DefaultHardwareClock.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/clocks/DefaultHardwareClock.java @@ -20,129 +20,150 @@ import org.fishwife.jrugged.interval.DiscreteInterval; -/** This class captures the "most accurate system timer" - * available via {@link System#nanoTime}, but also attempts to determine - * the actual granularity of the timer (which might be greater than 1ns) - * and present the measurement error inherent in taking clock measurements. +/** + * This class captures the "most accurate system timer" available via + * {@link System#nanoTime}, but also attempts to determine the actual + * granularity of the timer (which might be greater than 1ns) and present the + * measurement error inherent in taking clock measurements. */ class DefaultHardwareClock implements HardwareClock { - private static long DEFAULT_PERIOD_MILLIS = 100 * 1000L; - private long periodMillis = DEFAULT_PERIOD_MILLIS; - private static int DEFAULT_NUM_SAMPLES = 100; - - /** I sure hope we can count on the clock ticking at least once a - * year. Normalize clock readings so that the returned intervals - * land in reasonable positive ranges. */ - private static long MIN_CLOCK_TIME = 1000000000L * 3600 * 24 * 365; - - private AtomicLong lastSampleTime = new AtomicLong(0L); - private int sampleIndex = 0; - private long maxGranularity; - private long[] samples; - private Long offset; - private Env env; - - /** Default constructor. */ - public DefaultHardwareClock() { - this(new DefaultEnv(), DEFAULT_NUM_SAMPLES, DEFAULT_PERIOD_MILLIS); - } - - /** Constructs a new HardwareClock with an alternative - * implementation for various static system methods. Primarily useful - * for testing. - * @param env - */ - public DefaultHardwareClock(Env env) { - this(env, DEFAULT_NUM_SAMPLES, DEFAULT_PERIOD_MILLIS); - } - - /** Constructs a new HardwareClock with all dependencies - * and/or configuration specified. - * @param env alternative implementation of required static system methods - * @param numSamples how many granularity samples to keep historically (default - * is 100) - * @param periodMillis how often to sample the clock granularity, in milliseconds - * (default is 100,000, or once every 100 seconds) - */ - public DefaultHardwareClock(Env env, int numSamples, long periodMillis) { - this.env = env; - samples = new long[numSamples]; - this.periodMillis = periodMillis; - } - - long elapsedTime(long start, long end) { - if (end > start) return (end - start); - return (Long.MAX_VALUE - start) + 1 + (end - Long.MIN_VALUE); - } - - long sampleGranularity() { - long start = env.nanoTime(); - long end; - while((end = env.nanoTime()) == start) /* loop */; - return elapsedTime(start, end); - } - - /* (non-Javadoc) - * @see org.fishwife.jrugged.clocks.HardwareClock#getGranularity() - */ - public long getGranularity() { - long now = env.currentTimeMillis(); - long curr = lastSampleTime.get(); - if (now - curr > periodMillis - && lastSampleTime.compareAndSet(curr, now)) { - samples[sampleIndex] = sampleGranularity(); - sampleIndex = (sampleIndex + 1) % samples.length; - long max = 0L; - for(long sample : samples) { - if (sample > max) max = sample; - } - maxGranularity = max; - } - return maxGranularity; - } - - /* (non-Javadoc) - * @see org.fishwife.jrugged.clocks.HardwareClock#getNanoTime() - */ - public DiscreteInterval getNanoTime() { - long granularity = getGranularity(); - long err = granularity / 2; - if (granularity % 2 == 1) err += 1; - long now = env.nanoTime() + getOffset(); - return new DiscreteInterval(now - err, now + err); - } - - private long getOffset() { - if (offset != null) return offset; - long now = env.nanoTime(); - offset = (now < MIN_CLOCK_TIME) ? (MIN_CLOCK_TIME - now) : (now - MIN_CLOCK_TIME); - return offset; - } - - /** Interface capturing various static methods that are dependencies - * of this class; this allows us to switch them out for testing and - * generally keep us loosely coupled from underlying platform API. - */ - public static interface Env { - /** @see {@link System#nanotime} */ - long nanoTime(); - /** @see {@link System#currentTimeMillis} */ - long currentTimeMillis(); - } - - /** The default implementation for static dependencies encapsulated - * in the Env interface. - */ - public static class DefaultEnv implements Env { - /** Returns System.nanoTime(). */ - public long nanoTime() { - return System.nanoTime(); - } - /** Returns System.currentTimeMillis(). */ - public long currentTimeMillis() { - return System.currentTimeMillis(); - } - } + private static long DEFAULT_PERIOD_MILLIS = 100 * 1000L; + private long periodMillis = DEFAULT_PERIOD_MILLIS; + private static int DEFAULT_NUM_SAMPLES = 100; + + /** + * I sure hope we can count on the clock ticking at least once a year. Normalize + * clock readings so that the returned intervals land in reasonable positive + * ranges. + */ + private static long MIN_CLOCK_TIME = 1000000000L * 3600 * 24 * 365; + + private AtomicLong lastSampleTime = new AtomicLong(0L); + private int sampleIndex = 0; + private long maxGranularity; + private long[] samples; + private Long offset; + private Env env; + + /** Default constructor. */ + public DefaultHardwareClock() { + this(new DefaultEnv(), DEFAULT_NUM_SAMPLES, DEFAULT_PERIOD_MILLIS); + } + + /** + * Constructs a new HardwareClock with an alternative + * implementation for various static system methods. Primarily useful for + * testing. + * + * @param env + */ + public DefaultHardwareClock(Env env) { + this(env, DEFAULT_NUM_SAMPLES, DEFAULT_PERIOD_MILLIS); + } + + /** + * Constructs a new HardwareClock with all dependencies and/or + * configuration specified. + * + * @param env alternative implementation of required static system + * methods + * @param numSamples how many granularity samples to keep historically + * (default is 100) + * @param periodMillis how often to sample the clock granularity, in + * milliseconds (default is 100,000, or once every 100 + * seconds) + */ + public DefaultHardwareClock(Env env, int numSamples, long periodMillis) { + this.env = env; + samples = new long[numSamples]; + this.periodMillis = periodMillis; + } + + long elapsedTime(long start, long end) { + if (end > start) + return (end - start); + return (Long.MAX_VALUE - start) + 1 + (end - Long.MIN_VALUE); + } + + long sampleGranularity() { + long start = env.nanoTime(); + long end; + while ((end = env.nanoTime()) == start) + /* loop */; + return elapsedTime(start, end); + } + + /* + * (non-Javadoc) + * + * @see org.fishwife.jrugged.clocks.HardwareClock#getGranularity() + */ + public long getGranularity() { + long now = env.currentTimeMillis(); + long curr = lastSampleTime.get(); + if (now - curr > periodMillis && lastSampleTime.compareAndSet(curr, now)) { + samples[sampleIndex] = sampleGranularity(); + sampleIndex = (sampleIndex + 1) % samples.length; + long max = 0L; + for (long sample : samples) { + if (sample > max) + max = sample; + } + maxGranularity = max; + } + return maxGranularity; + } + + /* + * (non-Javadoc) + * + * @see org.fishwife.jrugged.clocks.HardwareClock#getNanoTime() + */ + public DiscreteInterval getNanoTime() { + long granularity = getGranularity(); + long err = granularity / 2; + if (granularity % 2 == 1) + err += 1; + long now = env.nanoTime() + getOffset(); + return new DiscreteInterval(now - err, now + err); + } + + private long getOffset() { + if (offset != null) + return offset; + long now = env.nanoTime(); + offset = (now < MIN_CLOCK_TIME) ? (MIN_CLOCK_TIME - now) : (now - MIN_CLOCK_TIME); + return offset; + } + + /** + * Interface capturing various static methods that are dependencies of this + * class; this allows us to switch them out for testing and generally keep us + * loosely coupled from underlying platform API. + */ + public static interface Env { + /** @see {@link System#nanotime} */ + long nanoTime(); + + /** @see {@link System#currentTimeMillis} */ + long currentTimeMillis(); + } + + /** + * The default implementation for static dependencies encapsulated in the + * Env interface. + */ + public static class DefaultEnv implements Env { + /** Returns System.nanoTime(). */ + public long nanoTime() { + return System.nanoTime(); + } + + /** Returns System.currentTimeMillis(). */ + public long currentTimeMillis() { + return System.currentTimeMillis(); + } + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/clocks/HardwareClock.java b/jrugged-core/src/main/java/org/fishwife/jrugged/clocks/HardwareClock.java index 7223969d..2a967795 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/clocks/HardwareClock.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/clocks/HardwareClock.java @@ -18,29 +18,33 @@ import org.fishwife.jrugged.interval.DiscreteInterval; -/** This interface provides access to the "most accurate system - * timer" available, but also attempts to determine the actual - * granularity of the timer (which might be greater than 1ns) and - * present the measurement error inherent in taking clock measurements. +/** + * This interface provides access to the "most accurate system timer" + * available, but also attempts to determine the actual granularity of the timer + * (which might be greater than 1ns) and present the measurement error inherent + * in taking clock measurements. */ interface HardwareClock { - /** Gets the estimated hardware clock granularity. This is the - * number of nanoseconds that elapse between ticks/updates of - * the underlying hardware clock. - * @return granularity in nanoseconds - */ - long getGranularity(); + /** + * Gets the estimated hardware clock granularity. This is the number of + * nanoseconds that elapse between ticks/updates of the underlying hardware + * clock. + * + * @return granularity in nanoseconds + */ + long getGranularity(); - /** Get an estimate of the current hardware clock reading, - * represented as a range of times. The +/- error should be half - * of the measurement increment, which is the clock granularity - * in our case. Thus, this effectively returns the hardware clock - * reading +/- (half granularity), with conservative rounding. - * @return DiscreteInterval representing the range of values the - * current hardware clock time might fall within, represented in - * nanoseconds. - */ - DiscreteInterval getNanoTime(); + /** + * Get an estimate of the current hardware clock reading, represented as a range + * of times. The +/- error should be half of the measurement increment, which is + * the clock granularity in our case. Thus, this effectively returns the + * hardware clock reading +/- (half granularity), with conservative rounding. + * + * @return DiscreteInterval representing the range of values the + * current hardware clock time might fall within, represented in + * nanoseconds. + */ + DiscreteInterval getNanoTime(); } \ No newline at end of file diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/clocks/Timer.java b/jrugged-core/src/main/java/org/fishwife/jrugged/clocks/Timer.java index 80911f31..b3fb3daf 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/clocks/Timer.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/clocks/Timer.java @@ -20,53 +20,61 @@ public class Timer { - private HardwareClock clock; - private boolean wasSet = false; - private DiscreteInterval targetElapsedTime; - private DiscreteInterval startTime; - private DiscreteInterval targetEndTime; + private HardwareClock clock; + private boolean wasSet = false; + private DiscreteInterval targetElapsedTime; + private DiscreteInterval startTime; + private DiscreteInterval targetEndTime; - public Timer() { - this(new DefaultHardwareClock()); - } + public Timer() { + this(new DefaultHardwareClock()); + } - public Timer(HardwareClock clock) { - this.clock = clock; - } + public Timer(HardwareClock clock) { + this.clock = clock; + } - public boolean set(long duration, long error) { - if (error < clock.getGranularity()) return false; - wasSet = true; - targetElapsedTime = new DiscreteInterval(duration - error, duration + error); - return true; - } + public boolean set(long duration, long error) { + if (error < clock.getGranularity()) + return false; + wasSet = true; + targetElapsedTime = new DiscreteInterval(duration - error, duration + error); + return true; + } - public void start() { - if (!wasSet) throw new IllegalStateException("cannot start a timer that has not been set"); - startTime = clock.getNanoTime(); - targetEndTime = startTime.plus(targetElapsedTime); - } + public void start() { + if (!wasSet) + throw new IllegalStateException("cannot start a timer that has not been set"); + startTime = clock.getNanoTime(); + targetEndTime = startTime.plus(targetElapsedTime); + } - public boolean hasElapsed() { - if (startTime == null) return false; - return (clock.getNanoTime().getMin() >= targetEndTime.getMin()); - } + public boolean hasElapsed() { + if (startTime == null) + return false; + return (clock.getNanoTime().getMin() >= targetEndTime.getMin()); + } - public boolean isLate() { - if (startTime == null) return false; - return (clock.getNanoTime().getMax() > targetEndTime.getMax()); - } + public boolean isLate() { + if (startTime == null) + return false; + return (clock.getNanoTime().getMax() > targetEndTime.getMax()); + } - public DiscreteInterval getTimeRemaining() { - if (!wasSet) throw new IllegalStateException("cannot compute time remaining without having duration set"); - if (startTime == null) return targetElapsedTime; - DiscreteInterval diff = targetEndTime.minus(clock.getNanoTime()); - return (diff.getMin() <= 0L) ? new DiscreteInterval(0L,0L) : diff; - } + public DiscreteInterval getTimeRemaining() { + if (!wasSet) + throw new IllegalStateException("cannot compute time remaining without having duration set"); + if (startTime == null) + return targetElapsedTime; + DiscreteInterval diff = targetEndTime.minus(clock.getNanoTime()); + return (diff.getMin() <= 0L) ? new DiscreteInterval(0L, 0L) : diff; + } - public void waitUntilElapsed() { - if (!wasSet) throw new IllegalStateException("cannot wait until duration has been set"); - while(!hasElapsed()) /*spin*/; - } + public void waitUntilElapsed() { + if (!wasSet) + throw new IllegalStateException("cannot wait until duration has been set"); + while (!hasElapsed()) + /* spin */; + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/control/Action.java b/jrugged-core/src/main/java/org/fishwife/jrugged/control/Action.java index b787ebb5..b9c745f7 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/control/Action.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/control/Action.java @@ -17,5 +17,5 @@ package org.fishwife.jrugged.control; interface Action { - void execute(); + void execute(); } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/control/ClosedLoop.java b/jrugged-core/src/main/java/org/fishwife/jrugged/control/ClosedLoop.java index f4eeb587..f64ba674 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/control/ClosedLoop.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/control/ClosedLoop.java @@ -18,27 +18,27 @@ class ClosedLoop, A extends Action> { - private Controller controller; - private M model; + private Controller controller; + private M model; - ClosedLoop(Controller controller, M model) { - this.controller = controller; - this.model = model; - } + ClosedLoop(Controller controller, M model) { + this.controller = controller; + this.model = model; + } - void addObjective(Objective objective) { - controller.addObjective(objective); - } + void addObjective(Objective objective) { + controller.addObjective(objective); + } - void withdrawObjective(Objective objective) { - controller.withdrawObjective(objective); - } + void withdrawObjective(Objective objective) { + controller.withdrawObjective(objective); + } - void processEvent(E event) { - model.update(event); - controller.assessObjectives(model); - A a = controller.selectAction(model); - a.execute(); - } + void processEvent(E event) { + model.update(event); + controller.assessObjectives(model); + A a = controller.selectAction(model); + a.execute(); + } } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/control/Controller.java b/jrugged-core/src/main/java/org/fishwife/jrugged/control/Controller.java index ea005fd8..400046fe 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/control/Controller.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/control/Controller.java @@ -17,11 +17,11 @@ package org.fishwife.jrugged.control; interface Controller, A extends Action> { - void addObjective(Objective objective); + void addObjective(Objective objective); - void withdrawObjective(Objective objective); + void withdrawObjective(Objective objective); - A selectAction(M model); + A selectAction(M model); - void assessObjectives(M model); + void assessObjectives(M model); } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/control/Model.java b/jrugged-core/src/main/java/org/fishwife/jrugged/control/Model.java index f3df6918..78e36966 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/control/Model.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/control/Model.java @@ -17,5 +17,5 @@ package org.fishwife.jrugged.control; interface Model { - void update(E event); + void update(E event); } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/control/Objective.java b/jrugged-core/src/main/java/org/fishwife/jrugged/control/Objective.java index b24ecf0b..536b2b66 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/control/Objective.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/control/Objective.java @@ -17,8 +17,11 @@ package org.fishwife.jrugged.control; interface Objective> { - boolean isMetBy(M state); - boolean hasFailed(M state); - void succeed(); - void fail(); + boolean isMetBy(M state); + + boolean hasFailed(M state); + + void succeed(); + + void fail(); } diff --git a/jrugged-core/src/main/java/org/fishwife/jrugged/interval/DiscreteInterval.java b/jrugged-core/src/main/java/org/fishwife/jrugged/interval/DiscreteInterval.java index fea57fb5..6ea8367f 100644 --- a/jrugged-core/src/main/java/org/fishwife/jrugged/interval/DiscreteInterval.java +++ b/jrugged-core/src/main/java/org/fishwife/jrugged/interval/DiscreteInterval.java @@ -16,120 +16,130 @@ */ package org.fishwife.jrugged.interval; -/** Representation of integer (long) intervals and associated arithmetic. - * This is useful for representing numbers with inherent measurement error - * or uncertainty; instead of a single value or a single value with a +/- - * error, we represent this as a range of values. One of the interesting - * properties of interval arithmetic is that errors are monotonically - * nondecreasing as you perform additional operations on them. +/** + * Representation of integer (long) intervals and associated arithmetic. This is + * useful for representing numbers with inherent measurement error or + * uncertainty; instead of a single value or a single value with a +/- error, we + * represent this as a range of values. One of the interesting properties of + * interval arithmetic is that errors are monotonically nondecreasing as you + * perform additional operations on them. */ public class DiscreteInterval { - final private long min; - final private long max; + final private long min; + final private long max; - /** Creates an interval spanning a range between the two provided - * value arguments, inclusive. Order matters for the arguments due - * to wraparound overflow of longs; the range is defined as all the - * values crossed by starting at 'min' and then incrementing until - * 'max' is reached. Thus, this is the closed interval [min, max]. - * @param min minimum value of range - * @param max maximum value of range - */ - public DiscreteInterval(long min, long max) { - this.min = min; - this.max = max; - } + /** + * Creates an interval spanning a range between the two provided value + * arguments, inclusive. Order matters for the arguments due to wraparound + * overflow of longs; the range is defined as all the values crossed by starting + * at 'min' and then incrementing until 'max' is reached. Thus, this is the + * closed interval [min, max]. + * + * @param min minimum value of range + * @param max maximum value of range + */ + public DiscreteInterval(long min, long max) { + this.min = min; + this.max = max; + } - /** Returns the minimum value included in the interval. - * - * @return long min value included - */ - public long getMin() { - return min; - } + /** + * Returns the minimum value included in the interval. + * + * @return long min value included + */ + public long getMin() { + return min; + } - /** Returns the maximum value included in the interval. - * - * @return long max value included - */ - public long getMax() { - return max; - } + /** + * Returns the maximum value included in the interval. + * + * @return long max value included + */ + public long getMax() { + return max; + } - /** Returns the number of discrete values covered by this - * range. - * - * @return long how many values are in my range - */ - public long size() { - return max - min + 1; - } + /** + * Returns the number of discrete values covered by this range. + * + * @return long how many values are in my range + */ + public long size() { + return max - min + 1; + } - /** Returns an interval representing the addition of the - * given interval with this one. - * @param other interval to add to this one - * @return interval sum - */ - public DiscreteInterval plus(DiscreteInterval other) { - return new DiscreteInterval(this.min + other.min, this.max + other.max); - } + /** + * Returns an interval representing the addition of the given interval with this + * one. + * + * @param other interval to add to this one + * @return interval sum + */ + public DiscreteInterval plus(DiscreteInterval other) { + return new DiscreteInterval(this.min + other.min, this.max + other.max); + } - /** Returns an interval representing the negation of the - * closed interval [-1,-1] with this one. - * @return interval negation - */ - public DiscreteInterval negate() { - return new DiscreteInterval(-1 * this.max, -1 * this.min); - } + /** + * Returns an interval representing the negation of the closed interval [-1,-1] + * with this one. + * + * @return interval negation + */ + public DiscreteInterval negate() { + return new DiscreteInterval(-1 * this.max, -1 * this.min); + } - /** Returns an interval representing the subtraction of the - * given interval from this one. - * @param other interval to subtract from this one - * @return result of subtraction - */ - public DiscreteInterval minus(DiscreteInterval other) { - return new DiscreteInterval(this.min - other.max, this.max - other.min); - } + /** + * Returns an interval representing the subtraction of the given interval from + * this one. + * + * @param other interval to subtract from this one + * @return result of subtraction + */ + public DiscreteInterval minus(DiscreteInterval other) { + return new DiscreteInterval(this.min - other.max, this.max - other.min); + } - /** Determines whether the given interval is completely - * contained by this one. - * - * @param other Is this interval contained in my current interval - * @return boolean contained or not contained that is the question - */ - public boolean contains(DiscreteInterval other) { - return (min <= other.getMin() && max >= other.getMax()); - } + /** + * Determines whether the given interval is completely contained by this one. + * + * @param other Is this interval contained in my current interval + * @return boolean contained or not contained that is the question + */ + public boolean contains(DiscreteInterval other) { + return (min <= other.getMin() && max >= other.getMax()); + } - @Override - public String toString() { - return "[" + min + "," + max + "]"; - } + @Override + public String toString() { + return "[" + min + "," + max + "]"; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (int) (max ^ (max >>> 32)); - result = prime * result + (int) (min ^ (min >>> 32)); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (max ^ (max >>> 32)); + result = prime * result + (int) (min ^ (min >>> 32)); + return result; + } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - DiscreteInterval other = (DiscreteInterval) obj; - if (max != other.max) - return false; - if (min != other.min) - return false; - return true; - } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DiscreteInterval other = (DiscreteInterval) obj; + if (max != other.max) + return false; + if (min != other.min) + return false; + return true; + } } - diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/TestCircuitBreaker.java b/jrugged-core/src/test/java/org/fishwife/jrugged/TestCircuitBreaker.java index a2a7a739..4d99ef1d 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/TestCircuitBreaker.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/TestCircuitBreaker.java @@ -35,12 +35,15 @@ import static org.junit.Assert.fail; public class TestCircuitBreaker { + private CircuitBreaker impl; private Callable mockCallable; private Runnable mockRunnable; private Clock mockClock; - Status theStatus; + + Status theStatus; + @SuppressWarnings("unchecked") @Before @@ -51,397 +54,392 @@ public void setUp() { mockClock = createMock(Clock.class); } - @Test - public void testInvokeWithRunnableResultAndResultReturnsResult() throws Exception { - final Object result = new Object(); - - mockRunnable.run(); - replay(mockRunnable); - Object theReturned = impl.invoke(mockRunnable, result); + @Test + public void testInvokeWithRunnableResultAndResultReturnsResult() throws Exception { + final Object result = new Object(); - verify(mockRunnable); - assertSame(result, theReturned); - } + mockRunnable.run(); + replay(mockRunnable); - @Test - public void testInvokeWithRunnableResultAndByPassReturnsResult() throws Exception { - final Object result = new Object(); - impl.setByPassState(true); - - mockRunnable.run(); - replay(mockRunnable); - - Object theReturned = impl.invoke(mockRunnable, result); - - verify(mockRunnable); - assertSame(result, theReturned); - } - - @Test(expected = CircuitBreakerException.class) - public void testInvokeWithRunnableResultAndTripHardReturnsException() throws Exception { - final Object result = new Object(); - impl.tripHard(); - - mockRunnable.run(); - replay(mockRunnable); - - impl.invoke(mockRunnable, result); - - verify(mockRunnable); - } - - @Test - public void testInvokeWithRunnableDoesNotError() throws Exception { - mockRunnable.run(); - replay(mockRunnable); - - impl.invoke(mockRunnable); - - verify(mockRunnable); - } + Object theReturned = impl.invoke(mockRunnable, result); - @Test - public void testInvokeWithRunnableAndByPassDoesNotError() throws Exception { - impl.setByPassState(true); - - mockRunnable.run(); - replay(mockRunnable); - - impl.invoke(mockRunnable); - - verify(mockRunnable); - } - - @Test(expected = CircuitBreakerException.class) - public void testInvokeWithRunnableAndTripHardReturnsException() throws Exception { - impl.tripHard(); - - mockRunnable.run(); - replay(mockRunnable); - - impl.invoke(mockRunnable); - - verify(mockRunnable); - } - - @Test - public void testStaysClosedOnSuccess() throws Exception { - impl.state = CircuitBreaker.BreakerState.CLOSED; - final Object obj = new Object(); - expect(mockCallable.call()).andReturn(obj); - replay(mockCallable); - - Object result = impl.invoke(mockCallable); - - verify(mockCallable); - assertSame(obj, result); - assertEquals(CircuitBreaker.BreakerState.CLOSED, impl.state); - } - - @Test - public void testOpensOnFailure() throws Exception { - long start = System.currentTimeMillis(); - impl.state = CircuitBreaker.BreakerState.OPEN; - expect(mockCallable.call()).andThrow(new RuntimeException()); - replay(mockCallable); - - try { - impl.invoke(mockCallable); - fail("should have thrown an exception"); - } catch (RuntimeException expected) { - } - - long end = System.currentTimeMillis(); - - verify(mockCallable); - assertEquals(CircuitBreaker.BreakerState.OPEN, impl.state); - assertTrue(impl.lastFailure.get() >= start); - assertTrue(impl.lastFailure.get() <= end); - } - - @Test - public void testOpenDuringCooldownThrowsCBException() - throws Exception { + verify(mockRunnable); + assertSame(result, theReturned); + } - impl.state = CircuitBreaker.BreakerState.OPEN; - impl.lastFailure.set(System.currentTimeMillis()); - replay(mockCallable); + @Test + public void testInvokeWithRunnableResultAndByPassReturnsResult() throws Exception { + final Object result = new Object(); + impl.setByPassState(true); - try { - impl.invoke(mockCallable); - fail("should have thrown an exception"); - } catch (CircuitBreakerException expected) { - } + mockRunnable.run(); + replay(mockRunnable); - verify(mockCallable); - assertEquals(CircuitBreaker.BreakerState.OPEN, impl.state); - } + Object theReturned = impl.invoke(mockRunnable, result); - @Test - public void testOpenAfterCooldownGoesHalfClosed() - throws Exception { + verify(mockRunnable); + assertSame(result, theReturned); + } - impl.state = CircuitBreaker.BreakerState.OPEN; - impl.resetMillis.set(1000); - impl.lastFailure.set(System.currentTimeMillis() - 2000); + @Test(expected = CircuitBreakerException.class) + public void testInvokeWithRunnableResultAndTripHardReturnsException() throws Exception { + final Object result = new Object(); + impl.tripHard(); - assertEquals(Status.DEGRADED, impl.getStatus()); - assertEquals(CircuitBreaker.BreakerState.HALF_CLOSED, impl.state); - } + mockRunnable.run(); + replay(mockRunnable); - @Test - public void testHalfClosedFailureOpensAgain() - throws Exception { + impl.invoke(mockRunnable, result); - impl.state = CircuitBreaker.BreakerState.HALF_CLOSED; - impl.resetMillis.set(1000); - impl.lastFailure.set(System.currentTimeMillis() - 2000); + verify(mockRunnable); + } - long start = System.currentTimeMillis(); + @Test + public void testInvokeWithRunnableDoesNotError() throws Exception { + mockRunnable.run(); + replay(mockRunnable); - expect(mockCallable.call()).andThrow(new RuntimeException()); - replay(mockCallable); + impl.invoke(mockRunnable); - try { - impl.invoke(mockCallable); - fail("should have thrown exception"); - } catch (RuntimeException expected) { - } + verify(mockRunnable); + } - long end = System.currentTimeMillis(); + @Test + public void testInvokeWithRunnableAndByPassDoesNotError() throws Exception { + impl.setByPassState(true); - verify(mockCallable); - assertEquals(CircuitBreaker.BreakerState.OPEN, impl.state); - assertTrue(impl.lastFailure.get() >= start); - assertTrue(impl.lastFailure.get() <= end); - } + mockRunnable.run(); + replay(mockRunnable); - @Test - public void testGetStatusNotUpdatingIsAttemptLive() throws Exception { + impl.invoke(mockRunnable); - impl.resetMillis.set(50); - impl.trip(); - assertEquals(CircuitBreaker.BreakerState.OPEN, impl.state); - assertEquals(false, impl.isAttemptLive); + verify(mockRunnable); + } - Thread.sleep(200); + @Test(expected = CircuitBreakerException.class) + public void testInvokeWithRunnableAndTripHardReturnsException() throws Exception { + impl.tripHard(); - // The getStatus()->canAttempt() call also updated isAttemptLive to true - assertEquals(Status.DEGRADED.getValue(), impl.getStatus().getValue()); - assertEquals(false, impl.isAttemptLive); - } + mockRunnable.run(); + replay(mockRunnable); - @Test - public void testManualTripAndReset() throws Exception { - impl.state = CircuitBreaker.BreakerState.OPEN; - final Object obj = new Object(); - expect(mockCallable.call()).andReturn(obj); - replay(mockCallable); - - impl.trip(); - try { - impl.invoke(mockCallable); - fail("Manual trip method failed."); - } catch (CircuitBreakerException e) { - } - - impl.reset(); - - Object result = impl.invoke(mockCallable); - - verify(mockCallable); - assertSame(obj, result); - assertEquals(CircuitBreaker.BreakerState.CLOSED, impl.state); - } + impl.invoke(mockRunnable); - @Test - public void testTripHard() throws Exception { - expect(mockCallable.call()).andReturn("hi"); + verify(mockRunnable); + } - replay(mockCallable); + @Test + public void testStaysClosedOnSuccess() throws Exception { + impl.state = CircuitBreaker.BreakerState.CLOSED; + final Object obj = new Object(); + expect(mockCallable.call()).andReturn(obj); + replay(mockCallable); - impl.tripHard(); - try { - impl.invoke(mockCallable); - fail("exception expected after CircuitBreaker.tripHard()"); - } catch (CircuitBreakerException e) { - } - assertEquals(CircuitBreaker.BreakerState.OPEN, impl.state); + Object result = impl.invoke(mockCallable); - impl.reset(); - impl.invoke(mockCallable); - assertEquals(CircuitBreaker.BreakerState.CLOSED, impl.state); + verify(mockCallable); + assertSame(obj, result); + assertEquals(CircuitBreaker.BreakerState.CLOSED, impl.state); + } - verify(mockCallable); - } + @Test + public void testOpensOnFailure() throws Exception { + long start = System.currentTimeMillis(); + impl.state = CircuitBreaker.BreakerState.OPEN; + expect(mockCallable.call()).andThrow(new RuntimeException()); + replay(mockCallable); - @Test - public void testGetTripCount() throws Exception { - long tripCount1 = impl.getTripCount(); + try { + impl.invoke(mockCallable); + fail("should have thrown an exception"); + } catch (RuntimeException expected) { + } - impl.tripHard(); - long tripCount2 = impl.getTripCount(); - assertEquals(tripCount1 + 1, tripCount2); + long end = System.currentTimeMillis(); - impl.tripHard(); - assertEquals(tripCount2, impl.getTripCount()); - } + verify(mockCallable); + assertEquals(CircuitBreaker.BreakerState.OPEN, impl.state); + assertTrue(impl.lastFailure.get() >= start); + assertTrue(impl.lastFailure.get() <= end); + } - @Test - public void testGetStatusWhenOpen() { - impl.state = CircuitBreaker.BreakerState.OPEN; - Assert.assertEquals(Status.DOWN, impl.getStatus()); - } + @Test + public void testOpenDuringCooldownThrowsCBException() throws Exception { - @Test - public void testGetStatusWhenHalfClosed() { - impl.state = CircuitBreaker.BreakerState.HALF_CLOSED; - assertEquals(Status.DEGRADED, impl.getStatus()); - } + impl.state = CircuitBreaker.BreakerState.OPEN; + impl.lastFailure.set(System.currentTimeMillis()); + replay(mockCallable); - @Test - public void testGetStatusWhenOpenBeforeReset() { - impl.state = CircuitBreaker.BreakerState.CLOSED; - impl.resetMillis.set(1000); - impl.lastFailure.set(System.currentTimeMillis() - 50); + try { + impl.invoke(mockCallable); + fail("should have thrown an exception"); + } catch (CircuitBreakerException expected) { + } - assertEquals(Status.UP, impl.getStatus()); - } + verify(mockCallable); + assertEquals(CircuitBreaker.BreakerState.OPEN, impl.state); + } - @Test - public void testGetStatusWhenOpenAfterReset() { - impl.state = CircuitBreaker.BreakerState.OPEN; - impl.resetMillis.set(1000); - impl.lastFailure.set(System.currentTimeMillis() - 2000); + @Test + public void testOpenAfterCooldownGoesHalfClosed() throws Exception { - assertEquals(Status.DEGRADED, impl.getStatus()); - } + impl.state = CircuitBreaker.BreakerState.OPEN; + impl.resetMillis.set(1000); + impl.lastFailure.set(System.currentTimeMillis() - 2000); - @Test - public void testGetStatusAfterHardTrip() { - impl.tripHard(); - impl.resetMillis.set(1000); - impl.lastFailure.set(System.currentTimeMillis() - 2000); + assertEquals(Status.DEGRADED, impl.getStatus()); + assertEquals(CircuitBreaker.BreakerState.HALF_CLOSED, impl.state); + } - assertEquals(Status.DOWN, impl.getStatus()); - } + @Test + public void testHalfClosedFailureOpensAgain() throws Exception { - @Test - public void testStatusIsByPassWhenSet() { - impl.setByPassState(true); - assertEquals(Status.DEGRADED, impl.getStatus()); - } + impl.state = CircuitBreaker.BreakerState.HALF_CLOSED; + impl.resetMillis.set(1000); + impl.lastFailure.set(System.currentTimeMillis() - 2000); - @Test - public void testByPassIgnoresCurrentBreakerStateWhenSet() { - impl.state = CircuitBreaker.BreakerState.OPEN; - assertEquals(Status.DOWN, impl.getStatus()); + long start = System.currentTimeMillis(); - impl.setByPassState(true); - assertEquals(Status.DEGRADED, impl.getStatus()); + expect(mockCallable.call()).andThrow(new RuntimeException()); + replay(mockCallable); + + try { + impl.invoke(mockCallable); + fail("should have thrown exception"); + } catch (RuntimeException expected) { + } + + long end = System.currentTimeMillis(); + + verify(mockCallable); + assertEquals(CircuitBreaker.BreakerState.OPEN, impl.state); + assertTrue(impl.lastFailure.get() >= start); + assertTrue(impl.lastFailure.get() <= end); + } + + @Test + public void testGetStatusNotUpdatingIsAttemptLive() throws Exception { + + impl.resetMillis.set(50); + impl.trip(); + assertEquals(CircuitBreaker.BreakerState.OPEN, impl.state); + assertEquals(false, impl.isAttemptLive); + + Thread.sleep(200); + + // The getStatus()->canAttempt() call also updated isAttemptLive to true + assertEquals(Status.DEGRADED.getValue(), impl.getStatus().getValue()); + assertEquals(false, impl.isAttemptLive); + } + + @Test + public void testManualTripAndReset() throws Exception { + impl.state = CircuitBreaker.BreakerState.OPEN; + final Object obj = new Object(); + expect(mockCallable.call()).andReturn(obj); + replay(mockCallable); + + impl.trip(); + try { + impl.invoke(mockCallable); + fail("Manual trip method failed."); + } catch (CircuitBreakerException e) { + } + + impl.reset(); + + Object result = impl.invoke(mockCallable); + + verify(mockCallable); + assertSame(obj, result); + assertEquals(CircuitBreaker.BreakerState.CLOSED, impl.state); + } + + @Test + public void testTripHard() throws Exception { + expect(mockCallable.call()).andReturn("hi"); + + replay(mockCallable); + + impl.tripHard(); + try { + impl.invoke(mockCallable); + fail("exception expected after CircuitBreaker.tripHard()"); + } catch (CircuitBreakerException e) { + } + assertEquals(CircuitBreaker.BreakerState.OPEN, impl.state); + + impl.reset(); + impl.invoke(mockCallable); + assertEquals(CircuitBreaker.BreakerState.CLOSED, impl.state); + + verify(mockCallable); + } + + @Test + public void testGetTripCount() throws Exception { + long tripCount1 = impl.getTripCount(); + + impl.tripHard(); + long tripCount2 = impl.getTripCount(); + assertEquals(tripCount1 + 1, tripCount2); + + impl.tripHard(); + assertEquals(tripCount2, impl.getTripCount()); + } + + @Test + public void testGetStatusWhenOpen() { + impl.state = CircuitBreaker.BreakerState.OPEN; + Assert.assertEquals(Status.DOWN, impl.getStatus()); + } + + @Test + public void testGetStatusWhenHalfClosed() { + impl.state = CircuitBreaker.BreakerState.HALF_CLOSED; + assertEquals(Status.DEGRADED, impl.getStatus()); + } + + @Test + public void testGetStatusWhenOpenBeforeReset() { + impl.state = CircuitBreaker.BreakerState.CLOSED; + impl.resetMillis.set(1000); + impl.lastFailure.set(System.currentTimeMillis() - 50); + + assertEquals(Status.UP, impl.getStatus()); + } + + @Test + public void testGetStatusWhenOpenAfterReset() { + impl.state = CircuitBreaker.BreakerState.OPEN; + impl.resetMillis.set(1000); + impl.lastFailure.set(System.currentTimeMillis() - 2000); + + assertEquals(Status.DEGRADED, impl.getStatus()); + } + + @Test + public void testGetStatusAfterHardTrip() { + impl.tripHard(); + impl.resetMillis.set(1000); + impl.lastFailure.set(System.currentTimeMillis() - 2000); + + assertEquals(Status.DOWN, impl.getStatus()); + } + + @Test + public void testStatusIsByPassWhenSet() { + impl.setByPassState(true); + assertEquals(Status.DEGRADED, impl.getStatus()); + } + + @Test + public void testByPassIgnoresCurrentBreakerStateWhenSet() { + impl.state = CircuitBreaker.BreakerState.OPEN; + assertEquals(Status.DOWN, impl.getStatus()); - impl.setByPassState(false); - assertEquals(Status.DOWN, impl.getStatus()); - } + impl.setByPassState(true); + assertEquals(Status.DEGRADED, impl.getStatus()); - @Test - public void testByPassIgnoresBreakerStateAndCallsWrappedMethod() throws Exception { - expect(mockCallable.call()).andReturn("hi").anyTimes(); + impl.setByPassState(false); + assertEquals(Status.DOWN, impl.getStatus()); + } - replay(mockCallable); + @Test + public void testByPassIgnoresBreakerStateAndCallsWrappedMethod() throws Exception { + expect(mockCallable.call()).andReturn("hi").anyTimes(); - impl.tripHard(); - impl.setByPassState(true); + replay(mockCallable); - try { - impl.invoke(mockCallable); - } catch (CircuitBreakerException e) { - fail("exception not expected when CircuitBreaker is bypassed."); - } - assertEquals(CircuitBreaker.BreakerState.OPEN, impl.state); - assertEquals(Status.DEGRADED, impl.getStatus()); + impl.tripHard(); + impl.setByPassState(true); - impl.reset(); - impl.setByPassState(false); - impl.invoke(mockCallable); - assertEquals(CircuitBreaker.BreakerState.CLOSED, impl.state); + try { + impl.invoke(mockCallable); + } catch (CircuitBreakerException e) { + fail("exception not expected when CircuitBreaker is bypassed."); + } + assertEquals(CircuitBreaker.BreakerState.OPEN, impl.state); + assertEquals(Status.DEGRADED, impl.getStatus()); - verify(mockCallable); - } + impl.reset(); + impl.setByPassState(false); + impl.invoke(mockCallable); + assertEquals(CircuitBreaker.BreakerState.CLOSED, impl.state); - @Test - public void testNotificationCallback() throws Exception { + verify(mockCallable); + } - CircuitBreakerNotificationCallback cb = new CircuitBreakerNotificationCallback() { - public void notify(Status s) { - theStatus = s; - } - }; + @Test + public void testNotificationCallback() throws Exception { - impl.addListener(cb); - impl.trip(); + CircuitBreakerNotificationCallback cb = new CircuitBreakerNotificationCallback() { + public void notify(Status s) { + theStatus = s; + } + }; - assertNotNull(theStatus); - assertEquals(Status.DOWN, theStatus); - } + impl.addListener(cb); + impl.trip(); - @Test(expected = Throwable.class) - public void circuitBreakerKeepsExceptionThatTrippedIt() throws Throwable { + assertNotNull(theStatus); + assertEquals(Status.DOWN, theStatus); + } - try { - impl.invoke(new FailingCallable("broken")); - } catch (Exception e) { + @Test(expected = Throwable.class) + public void circuitBreakerKeepsExceptionThatTrippedIt() throws Throwable { - } + try { + impl.invoke(new FailingCallable("broken")); + } catch (Exception e) { - Throwable tripException = impl.getTripException(); - assertEquals("broken", tripException.getMessage()); - throw tripException; - } + } - @Test(expected = Throwable.class) - public void resetCircuitBreakerStillHasTripException() throws Throwable { + Throwable tripException = impl.getTripException(); + assertEquals("broken", tripException.getMessage()); + throw tripException; + } - try { - impl.invoke(new FailingCallable("broken")); - } catch (Exception e) { + @Test(expected = Throwable.class) + public void resetCircuitBreakerStillHasTripException() throws Throwable { - } - impl.reset(); + try { + impl.invoke(new FailingCallable("broken")); + } catch (Exception e) { - Throwable tripException = impl.getTripException(); - assertEquals("broken", tripException.getMessage()); - throw tripException; - } + } + impl.reset(); - @Test - public void circuitBreakerReturnsExceptionAsString() { + Throwable tripException = impl.getTripException(); + assertEquals("broken", tripException.getMessage()); + throw tripException; + } - try { - impl.invoke(new FailingCallable("broken")); - } catch (Exception e) { + @Test + public void circuitBreakerReturnsExceptionAsString() { - } + try { + impl.invoke(new FailingCallable("broken")); + } catch (Exception e) { - Throwable tripException = impl.getTripException(); + } - String s = impl.getTripExceptionAsString(); + Throwable tripException = impl.getTripException(); - assertTrue(impl.getTripExceptionAsString().startsWith("java.lang.Exception: broken\n")); - assertTrue(impl.getTripExceptionAsString().contains("at org.fishwife.jrugged.TestCircuitBreaker$FailingCallable.call")); - assertTrue(impl.getTripExceptionAsString().contains("Caused by: java.lang.Exception: The Cause\n")); - } + String s = impl.getTripExceptionAsString(); - @Test - public void neverTrippedCircuitBreakerReturnsNullForTripException() throws Exception { + assertTrue(impl.getTripExceptionAsString().startsWith("java.lang.Exception: broken\n")); + assertTrue(impl.getTripExceptionAsString() + .contains("at org.fishwife.jrugged.TestCircuitBreaker$FailingCallable.call")); + assertTrue(impl.getTripExceptionAsString().contains("Caused by: java.lang.Exception: The Cause\n")); + } - impl.invoke(mockCallable); + @Test + public void neverTrippedCircuitBreakerReturnsNullForTripException() throws Exception { - Throwable tripException = impl.getTripException(); + impl.invoke(mockCallable); - assertNull(tripException); - } @Test public void testSettingClockReturnsSameClockWhenRequested() { @@ -455,17 +453,23 @@ public void testSettingClockReturnsSameClockWhenRequested() { private class FailingCallable implements Callable { - private final String exceptionMessage; - public FailingCallable(String exceptionMessage) { - this.exceptionMessage = exceptionMessage; - } + assertNull(tripException); + } - Exception causeException = new Exception("The Cause"); + private class FailingCallable implements Callable { - public Object call() throws Exception { - throw new Exception(exceptionMessage, causeException); - } - } + private final String exceptionMessage; + + public FailingCallable(String exceptionMessage) { + this.exceptionMessage = exceptionMessage; + } + + Exception causeException = new Exception("The Cause"); + + public Object call() throws Exception { + throw new Exception(exceptionMessage, causeException); + } + } } diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/TestCircuitBreakerFactory.java b/jrugged-core/src/test/java/org/fishwife/jrugged/TestCircuitBreakerFactory.java index cc46a031..eca9db12 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/TestCircuitBreakerFactory.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/TestCircuitBreakerFactory.java @@ -28,147 +28,141 @@ public class TestCircuitBreakerFactory { - private CircuitBreakerFactory factory; - private CircuitBreakerConfig config; - - private static final int TEST_LIMIT = 5; - private static final long TEST_WINDOW_MILLIS = 30000L; - private static final long TEST_RESET_MILLIS = 10000L; - - @Before - public void setUp() { - factory = new CircuitBreakerFactory(); - config = new CircuitBreakerConfig(TEST_RESET_MILLIS, - new DefaultFailureInterpreter(TEST_LIMIT, TEST_WINDOW_MILLIS)); - } - - @Test - public void testCreateCircuitBreaker() { - CircuitBreaker breaker = factory.createCircuitBreaker("testCreate", config); - checkBreaker(breaker, TEST_LIMIT, TEST_WINDOW_MILLIS, TEST_RESET_MILLIS); - } - - @Test - public void testCreateDuplicateCircuitBreaker() { - String name = "testCreate"; - CircuitBreaker createdBreaker = factory.createCircuitBreaker(name, config); - CircuitBreaker secondBreaker = factory.createCircuitBreaker(name, config); - - assertSame(createdBreaker, secondBreaker); - checkBreaker(createdBreaker, TEST_LIMIT, TEST_WINDOW_MILLIS, TEST_RESET_MILLIS); - } - - @Test - public void testCreateCircuitBreakerEmptyConfig() { - CircuitBreakerConfig emptyConfig = - new CircuitBreakerConfig(-1, new DefaultFailureInterpreter()); - CircuitBreaker breaker = factory.createCircuitBreaker("testCreateEmpty", emptyConfig); - - checkBreaker(breaker, 0, 0, 15000L); // These are the CircuitBreaker Defaults. - } - - @Test - public void testCreateCircuitBreakerNullFailureInterpreter() { - CircuitBreakerConfig emptyConfig = - new CircuitBreakerConfig(-1, null); - CircuitBreaker breaker = factory.createCircuitBreaker("testCreateEmpty", emptyConfig); - - checkBreakerNoFailureInterpreter(breaker, 15000L); // These are the CircuitBreaker Defaults. - } - - @Test - public void testFindANamedCircuitBreaker() { - String monitorName = "testFind"; - CircuitBreaker createdBreaker = factory.createCircuitBreaker(monitorName, config); - CircuitBreaker foundBreaker = factory.findCircuitBreaker(monitorName); - assertEquals(createdBreaker, foundBreaker); - } - - @Test - public void testFindNonExistentCircuitBreaker() { - CircuitBreaker foundBreaker = factory.findCircuitBreaker("testNonExistent"); - assertNull(foundBreaker); - } - - @Test - public void testGetCircuitBreakerNames() { - Set testSet = new HashSet(); - testSet.add("one"); - testSet.add("two"); - testSet.add("three"); - testSet.add("four"); - - factory.createCircuitBreaker("one", config); - factory.createCircuitBreaker("two", config); - factory.createCircuitBreaker("three", config); - factory.createCircuitBreaker("four", config); - Set monitorNames = factory.getCircuitBreakerNames(); - assertEquals(testSet, monitorNames); - } - - @Test - public void testEmptyPropertyOverrides() { - Properties overrideProperties = new Properties(); - factory.setProperties(overrideProperties); - CircuitBreaker breaker = factory.createCircuitBreaker("emptyOverrides", config); - checkBreaker(breaker, TEST_LIMIT, TEST_WINDOW_MILLIS, TEST_RESET_MILLIS); - } - - @Test - public void testPropertyOverrides() { - Properties overrideProperties = new Properties(); - Integer overrideLimit = 10; - Long overrideResetMillis = 50000L; - Long overrideWindowMillis = 500000L; - String name = "testOverrides"; - - overrideProperties.put("circuit." + name + ".limit", overrideLimit.toString()); - overrideProperties.put("circuit." + name + ".resetMillis", overrideResetMillis.toString()); - overrideProperties.put("circuit." + name + ".windowMillis", overrideWindowMillis.toString()); - factory.setProperties(overrideProperties); - - CircuitBreaker breaker = factory.createCircuitBreaker(name, config); - checkBreaker(breaker, overrideLimit, overrideWindowMillis, overrideResetMillis); - } - - @Test - public void testInvalidPropertyOverrides() { - Properties overrideProperties = new Properties(); - String name = "testOverrides"; - - overrideProperties.put("circuit." + name + ".limit", "badLimit"); - overrideProperties.put("circuit." + name + ".resetMillis", "badResetMillis"); - overrideProperties.put("circuit." + name + ".windowMillis", "badWindowMillis"); - factory.setProperties(overrideProperties); - - CircuitBreakerConfig emptyConfig = - new CircuitBreakerConfig(-1, new DefaultFailureInterpreter()); - CircuitBreaker breaker = factory.createCircuitBreaker(name, emptyConfig); - checkBreaker(breaker, 0, 0L, 15000L); // These are the CircuitBreaker defaults. - assertNotNull(breaker); - } - - private void checkBreaker(CircuitBreaker breaker, - int expectedLimit, - long expectedWindowMillis, - long expectedResetMillis) { - - assertNotNull(breaker); - - DefaultFailureInterpreter failureInterpreter = (DefaultFailureInterpreter)breaker.getFailureInterpreter(); - - if (failureInterpreter != null) { - assertEquals(failureInterpreter.getLimit(), expectedLimit); - assertEquals(failureInterpreter.getWindowMillis(), expectedWindowMillis); - } - - assertEquals(breaker.getResetMillis(), expectedResetMillis); - } - - private void checkBreakerNoFailureInterpreter(CircuitBreaker breaker, - long expectedResetMillis) { - - assertNotNull(breaker); - assertEquals(breaker.getResetMillis(), expectedResetMillis); - } + private CircuitBreakerFactory factory; + private CircuitBreakerConfig config; + + private static final int TEST_LIMIT = 5; + private static final long TEST_WINDOW_MILLIS = 30000L; + private static final long TEST_RESET_MILLIS = 10000L; + + @Before + public void setUp() { + factory = new CircuitBreakerFactory(); + config = new CircuitBreakerConfig(TEST_RESET_MILLIS, + new DefaultFailureInterpreter(TEST_LIMIT, TEST_WINDOW_MILLIS)); + } + + @Test + public void testCreateCircuitBreaker() { + CircuitBreaker breaker = factory.createCircuitBreaker("testCreate", config); + checkBreaker(breaker, TEST_LIMIT, TEST_WINDOW_MILLIS, TEST_RESET_MILLIS); + } + + @Test + public void testCreateDuplicateCircuitBreaker() { + String name = "testCreate"; + CircuitBreaker createdBreaker = factory.createCircuitBreaker(name, config); + CircuitBreaker secondBreaker = factory.createCircuitBreaker(name, config); + + assertSame(createdBreaker, secondBreaker); + checkBreaker(createdBreaker, TEST_LIMIT, TEST_WINDOW_MILLIS, TEST_RESET_MILLIS); + } + + @Test + public void testCreateCircuitBreakerEmptyConfig() { + CircuitBreakerConfig emptyConfig = new CircuitBreakerConfig(-1, new DefaultFailureInterpreter()); + CircuitBreaker breaker = factory.createCircuitBreaker("testCreateEmpty", emptyConfig); + + checkBreaker(breaker, 0, 0, 15000L); // These are the CircuitBreaker Defaults. + } + + @Test + public void testCreateCircuitBreakerNullFailureInterpreter() { + CircuitBreakerConfig emptyConfig = new CircuitBreakerConfig(-1, null); + CircuitBreaker breaker = factory.createCircuitBreaker("testCreateEmpty", emptyConfig); + + checkBreakerNoFailureInterpreter(breaker, 15000L); // These are the CircuitBreaker Defaults. + } + + @Test + public void testFindANamedCircuitBreaker() { + String monitorName = "testFind"; + CircuitBreaker createdBreaker = factory.createCircuitBreaker(monitorName, config); + CircuitBreaker foundBreaker = factory.findCircuitBreaker(monitorName); + assertEquals(createdBreaker, foundBreaker); + } + + @Test + public void testFindNonExistentCircuitBreaker() { + CircuitBreaker foundBreaker = factory.findCircuitBreaker("testNonExistent"); + assertNull(foundBreaker); + } + + @Test + public void testGetCircuitBreakerNames() { + Set testSet = new HashSet(); + testSet.add("one"); + testSet.add("two"); + testSet.add("three"); + testSet.add("four"); + + factory.createCircuitBreaker("one", config); + factory.createCircuitBreaker("two", config); + factory.createCircuitBreaker("three", config); + factory.createCircuitBreaker("four", config); + Set monitorNames = factory.getCircuitBreakerNames(); + assertEquals(testSet, monitorNames); + } + + @Test + public void testEmptyPropertyOverrides() { + Properties overrideProperties = new Properties(); + factory.setProperties(overrideProperties); + CircuitBreaker breaker = factory.createCircuitBreaker("emptyOverrides", config); + checkBreaker(breaker, TEST_LIMIT, TEST_WINDOW_MILLIS, TEST_RESET_MILLIS); + } + + @Test + public void testPropertyOverrides() { + Properties overrideProperties = new Properties(); + Integer overrideLimit = 10; + Long overrideResetMillis = 50000L; + Long overrideWindowMillis = 500000L; + String name = "testOverrides"; + + overrideProperties.put("circuit." + name + ".limit", overrideLimit.toString()); + overrideProperties.put("circuit." + name + ".resetMillis", overrideResetMillis.toString()); + overrideProperties.put("circuit." + name + ".windowMillis", overrideWindowMillis.toString()); + factory.setProperties(overrideProperties); + + CircuitBreaker breaker = factory.createCircuitBreaker(name, config); + checkBreaker(breaker, overrideLimit, overrideWindowMillis, overrideResetMillis); + } + + @Test + public void testInvalidPropertyOverrides() { + Properties overrideProperties = new Properties(); + String name = "testOverrides"; + + overrideProperties.put("circuit." + name + ".limit", "badLimit"); + overrideProperties.put("circuit." + name + ".resetMillis", "badResetMillis"); + overrideProperties.put("circuit." + name + ".windowMillis", "badWindowMillis"); + factory.setProperties(overrideProperties); + + CircuitBreakerConfig emptyConfig = new CircuitBreakerConfig(-1, new DefaultFailureInterpreter()); + CircuitBreaker breaker = factory.createCircuitBreaker(name, emptyConfig); + checkBreaker(breaker, 0, 0L, 15000L); // These are the CircuitBreaker defaults. + assertNotNull(breaker); + } + + private void checkBreaker(CircuitBreaker breaker, int expectedLimit, long expectedWindowMillis, + long expectedResetMillis) { + + assertNotNull(breaker); + + DefaultFailureInterpreter failureInterpreter = (DefaultFailureInterpreter) breaker.getFailureInterpreter(); + + if (failureInterpreter != null) { + assertEquals(failureInterpreter.getLimit(), expectedLimit); + assertEquals(failureInterpreter.getWindowMillis(), expectedWindowMillis); + } + + assertEquals(breaker.getResetMillis(), expectedResetMillis); + } + + private void checkBreakerNoFailureInterpreter(CircuitBreaker breaker, long expectedResetMillis) { + + assertNotNull(breaker); + assertEquals(breaker.getResetMillis(), expectedResetMillis); + } } diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/TestConstantFlowRegulator.java b/jrugged-core/src/test/java/org/fishwife/jrugged/TestConstantFlowRegulator.java index 5081f961..67badc6d 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/TestConstantFlowRegulator.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/TestConstantFlowRegulator.java @@ -30,99 +30,99 @@ import static org.junit.Assert.assertTrue; public class TestConstantFlowRegulator { - private ConstantFlowRegulator impl; - private Callable mockCallable; - private Runnable mockRunnable; - - @SuppressWarnings("unchecked") - @Before - public void setUp() { - impl = new ConstantFlowRegulator(); - mockCallable = createMock(Callable.class); - mockRunnable = createMock(Runnable.class); - } - - @Test - public void testConfiguration() { - impl.setRequestPerSecondThreshold(50); - - assertEquals(50, impl.getRequestPerSecondThreshold()); - } - - @Test - public void testRPSThresholdDefault() { - assertEquals(-1, impl.getRequestPerSecondThreshold()); - } - - @Test - public void testFlowThrottlerCanProceedWhenThresholdIsDefault() { - assertTrue(impl.canProceed()); - } - - @Test - public void testCanProceedWhenThresholdLessThanConfigured() { - impl.setRequestPerSecondThreshold(50); - - assertTrue(impl.canProceed()); - } - - @Test (expected=Exception.class) - public void testThrowsExceptionWhenCallableCanNotProceed() throws Exception { - final Object obj = new Object(); - - expect(mockCallable.call()).andReturn(obj); - expectLastCall().anyTimes(); - replay(mockCallable); - - impl.setRequestPerSecondThreshold(1); - - for (int i = 0; i < 5; i++) { - try { - impl.invoke(mockCallable); - } - catch (FlowRateExceededException e) { - System.out.println(e.getMessage()); - //Ignore these - } - } - - impl.invoke(mockCallable); - - verify(mockCallable); - } - - public class DomainException extends Exception {} - - public class TestConstantFlowRegulatorExceptionMapper - implements ConstantFlowRegulatorExceptionMapper { - public DomainException map(ConstantFlowRegulator flowRegulator, FlowRateExceededException e) { - return new DomainException(); - } - } - - @Test (expected=DomainException.class) - public void testThrowsMappedExceptionWhenCanNotProceed() throws Exception { - impl.setRequestPerSecondThreshold(1); - impl.setExceptionMapper(new TestConstantFlowRegulatorExceptionMapper()); - for (int i = 0; i < 5; i++) { - impl.invoke(mockCallable); - } - } - - @Test (expected=Exception.class) - public void testThrowsExceptionWhenRunnableCanNotProceed() throws Exception { - impl.setRequestPerSecondThreshold(1); - for (int i = 0; i < 5; i++) { - impl.invoke(mockRunnable); - } - } - - @Test (expected=DomainException.class) - public void testThrowsMappedExceptionWhenCanNotProceedViaConstructor() throws Exception { - ConstantFlowRegulator impl = new ConstantFlowRegulator(1, new TestConstantFlowRegulatorExceptionMapper()); - for (int i = 0; i < 5; i++) { - impl.invoke(mockCallable); - } - } + private ConstantFlowRegulator impl; + private Callable mockCallable; + private Runnable mockRunnable; + + @SuppressWarnings("unchecked") + @Before + public void setUp() { + impl = new ConstantFlowRegulator(); + mockCallable = createMock(Callable.class); + mockRunnable = createMock(Runnable.class); + } + + @Test + public void testConfiguration() { + impl.setRequestPerSecondThreshold(50); + + assertEquals(50, impl.getRequestPerSecondThreshold()); + } + + @Test + public void testRPSThresholdDefault() { + assertEquals(-1, impl.getRequestPerSecondThreshold()); + } + + @Test + public void testFlowThrottlerCanProceedWhenThresholdIsDefault() { + assertTrue(impl.canProceed()); + } + + @Test + public void testCanProceedWhenThresholdLessThanConfigured() { + impl.setRequestPerSecondThreshold(50); + + assertTrue(impl.canProceed()); + } + + @Test(expected = Exception.class) + public void testThrowsExceptionWhenCallableCanNotProceed() throws Exception { + final Object obj = new Object(); + + expect(mockCallable.call()).andReturn(obj); + expectLastCall().anyTimes(); + replay(mockCallable); + + impl.setRequestPerSecondThreshold(1); + + for (int i = 0; i < 5; i++) { + try { + impl.invoke(mockCallable); + } catch (FlowRateExceededException e) { + System.out.println(e.getMessage()); + // Ignore these + } + } + + impl.invoke(mockCallable); + + verify(mockCallable); + } + + public class DomainException extends Exception { + } + + public class TestConstantFlowRegulatorExceptionMapper + implements ConstantFlowRegulatorExceptionMapper { + public DomainException map(ConstantFlowRegulator flowRegulator, FlowRateExceededException e) { + return new DomainException(); + } + } + + @Test(expected = DomainException.class) + public void testThrowsMappedExceptionWhenCanNotProceed() throws Exception { + impl.setRequestPerSecondThreshold(1); + impl.setExceptionMapper(new TestConstantFlowRegulatorExceptionMapper()); + for (int i = 0; i < 5; i++) { + impl.invoke(mockCallable); + } + } + + @Test(expected = Exception.class) + public void testThrowsExceptionWhenRunnableCanNotProceed() throws Exception { + impl.setRequestPerSecondThreshold(1); + for (int i = 0; i < 5; i++) { + impl.invoke(mockRunnable); + } + } + + @Test(expected = DomainException.class) + public void testThrowsMappedExceptionWhenCanNotProceedViaConstructor() throws Exception { + ConstantFlowRegulator impl = new ConstantFlowRegulator(1, new TestConstantFlowRegulatorExceptionMapper()); + for (int i = 0; i < 5; i++) { + impl.invoke(mockCallable); + } + } } diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/TestDefaultFailureInterpreter.java b/jrugged-core/src/test/java/org/fishwife/jrugged/TestDefaultFailureInterpreter.java index 60fce7ee..43e7e352 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/TestDefaultFailureInterpreter.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/TestDefaultFailureInterpreter.java @@ -24,95 +24,95 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; -public final class TestDefaultFailureInterpreter{ - - private DefaultFailureInterpreter impl; - - @Before - public void setUp() { - impl = new DefaultFailureInterpreter(); - } - - // constructor tests - @Test - public void testDefaultConstructor() { - - assertEquals(0, impl.getLimit()); - assertEquals(0, impl.getWindowMillis()); - - assertEquals(0, impl.getIgnore().size()); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testConstructorWithIgnore() { - final Class exnClass = RuntimeException.class; - final Class[] myIgnore = { exnClass }; - - impl = new DefaultFailureInterpreter(myIgnore); - - assertEquals(0, impl.getLimit()); - assertEquals(0, impl.getWindowMillis()); - - assertEquals(1, impl.getIgnore().size()); - for(Class clazz : impl.getIgnore()) { - assertSame(clazz, exnClass); - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testConstructorWithIgnoreAndTolerance() { - final Class exnClass = RuntimeException.class; - final Class[] myIgnore = { exnClass }; - final int frequency = 7777; - final long time = 1234L; - - impl = new DefaultFailureInterpreter(myIgnore, frequency, time); - - assertEquals(frequency, impl.getLimit()); - assertEquals(time, impl.getWindowMillis()); - - assertEquals(1, impl.getIgnore().size()); - for(Class clazz : impl.getIgnore()) { - assertSame(clazz, exnClass); - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Test - public void testIgnoredExceptionDoesNotTrip() { - final Class ignoreClass = IOException.class; - final Class[] myIgnore = { ignoreClass }; - - impl.setIgnore(myIgnore); - assertFalse(impl.shouldTrip(new IOException())); - } - - @Test - public void testAnyExceptionTripsByDefault() { - assertTrue(impl.shouldTrip(new IOException())); - } - - @Test - public void testDoesntTripIfFailuresAreWithinTolerance() { - impl.setLimit(2); - impl.setWindowMillis(1000); - Exception exn1 = new Exception(); - Exception exn2 = new Exception(); - boolean result = impl.shouldTrip(exn1); - assertFalse("this should be false 1",result); - result = impl.shouldTrip(exn2); - assertFalse("this should be false 2",result); - } - - @Test - public void testTripsIfFailuresExceedTolerance() { - impl.setLimit(2); - impl.setWindowMillis(1000); - assertFalse("this should be false 1",impl.shouldTrip(new Exception())); - assertFalse("this should be false 2",impl.shouldTrip(new Exception())); - assertTrue("this should be true 3",impl.shouldTrip(new Exception())); - } +public final class TestDefaultFailureInterpreter { + + private DefaultFailureInterpreter impl; + + @Before + public void setUp() { + impl = new DefaultFailureInterpreter(); + } + + // constructor tests + @Test + public void testDefaultConstructor() { + + assertEquals(0, impl.getLimit()); + assertEquals(0, impl.getWindowMillis()); + + assertEquals(0, impl.getIgnore().size()); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test + public void testConstructorWithIgnore() { + final Class exnClass = RuntimeException.class; + final Class[] myIgnore = { exnClass }; + + impl = new DefaultFailureInterpreter(myIgnore); + + assertEquals(0, impl.getLimit()); + assertEquals(0, impl.getWindowMillis()); + + assertEquals(1, impl.getIgnore().size()); + for (Class clazz : impl.getIgnore()) { + assertSame(clazz, exnClass); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test + public void testConstructorWithIgnoreAndTolerance() { + final Class exnClass = RuntimeException.class; + final Class[] myIgnore = { exnClass }; + final int frequency = 7777; + final long time = 1234L; + + impl = new DefaultFailureInterpreter(myIgnore, frequency, time); + + assertEquals(frequency, impl.getLimit()); + assertEquals(time, impl.getWindowMillis()); + + assertEquals(1, impl.getIgnore().size()); + for (Class clazz : impl.getIgnore()) { + assertSame(clazz, exnClass); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test + public void testIgnoredExceptionDoesNotTrip() { + final Class ignoreClass = IOException.class; + final Class[] myIgnore = { ignoreClass }; + + impl.setIgnore(myIgnore); + assertFalse(impl.shouldTrip(new IOException())); + } + + @Test + public void testAnyExceptionTripsByDefault() { + assertTrue(impl.shouldTrip(new IOException())); + } + + @Test + public void testDoesntTripIfFailuresAreWithinTolerance() { + impl.setLimit(2); + impl.setWindowMillis(1000); + Exception exn1 = new Exception(); + Exception exn2 = new Exception(); + boolean result = impl.shouldTrip(exn1); + assertFalse("this should be false 1", result); + result = impl.shouldTrip(exn2); + assertFalse("this should be false 2", result); + } + + @Test + public void testTripsIfFailuresExceedTolerance() { + impl.setLimit(2); + impl.setWindowMillis(1000); + assertFalse("this should be false 1", impl.shouldTrip(new Exception())); + assertFalse("this should be false 2", impl.shouldTrip(new Exception())); + assertTrue("this should be true 3", impl.shouldTrip(new Exception())); + } } diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/TestFailureInterpreter.java b/jrugged-core/src/test/java/org/fishwife/jrugged/TestFailureInterpreter.java index f2012e8c..e2af75a8 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/TestFailureInterpreter.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/TestFailureInterpreter.java @@ -27,54 +27,55 @@ public final class TestFailureInterpreter { - @SuppressWarnings("unchecked") - @Test - public void testAcceptableException() throws Exception { - final RuntimeException theExn = new RuntimeException(); - final Callable callable = createMock(Callable.class); - final FailureInterpreter interpreter = createMock(FailureInterpreter.class); - final CircuitBreaker cb = new CircuitBreaker(interpreter); - - expect(callable.call()).andThrow(theExn); - expect(interpreter.shouldTrip(theExn)).andReturn(false); - - replay(callable); - replay(interpreter); - - try { - cb.invoke(callable); - fail("exception expected."); - } catch (Exception e) {} - - assertEquals("Status should be UP", Status.UP, cb.getStatus()); - verify(callable); - verify(interpreter); - } - - @SuppressWarnings("unchecked") - @Test - public void testUnacceptableException() throws Exception { - final RuntimeException theExn = new RuntimeException(); - final Callable callable = createMock(Callable.class); - final FailureInterpreter interpreter = createMock(FailureInterpreter.class); - final CircuitBreaker cb = new CircuitBreaker(interpreter); - - expect(callable.call()).andThrow(theExn); - expect(interpreter.shouldTrip(theExn)) - .andReturn(true); - - replay(callable); - replay(interpreter); - - try { - cb.invoke(callable); - fail("exception expected."); - } catch (Exception e) {} - - verify(callable); - verify(interpreter); - - assertEquals("Status should be DOWN", Status.DOWN, cb.getStatus()); - } + @SuppressWarnings("unchecked") + @Test + public void testAcceptableException() throws Exception { + final RuntimeException theExn = new RuntimeException(); + final Callable callable = createMock(Callable.class); + final FailureInterpreter interpreter = createMock(FailureInterpreter.class); + final CircuitBreaker cb = new CircuitBreaker(interpreter); + + expect(callable.call()).andThrow(theExn); + expect(interpreter.shouldTrip(theExn)).andReturn(false); + + replay(callable); + replay(interpreter); + + try { + cb.invoke(callable); + fail("exception expected."); + } catch (Exception e) { + } + + assertEquals("Status should be UP", Status.UP, cb.getStatus()); + verify(callable); + verify(interpreter); + } + + @SuppressWarnings("unchecked") + @Test + public void testUnacceptableException() throws Exception { + final RuntimeException theExn = new RuntimeException(); + final Callable callable = createMock(Callable.class); + final FailureInterpreter interpreter = createMock(FailureInterpreter.class); + final CircuitBreaker cb = new CircuitBreaker(interpreter); + + expect(callable.call()).andThrow(theExn); + expect(interpreter.shouldTrip(theExn)).andReturn(true); + + replay(callable); + replay(interpreter); + + try { + cb.invoke(callable); + fail("exception expected."); + } catch (Exception e) { + } + + verify(callable); + verify(interpreter); + + assertEquals("Status should be DOWN", Status.DOWN, cb.getStatus()); + } } diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/TestInitializer.java b/jrugged-core/src/test/java/org/fishwife/jrugged/TestInitializer.java index f3497431..c8c51822 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/TestInitializer.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/TestInitializer.java @@ -28,97 +28,97 @@ import static org.junit.Assert.assertTrue; public class TestInitializer { - private Initializable mockClient; - private Initializer impl; - - @Before - public void setUp() { - mockClient = createMock(Initializable.class); - impl = new Initializer(mockClient); - } - - @Test - public void testFirstTimeTrial() throws Exception { - mockClient.tryInit(); - mockClient.afterInit(); - replay(mockClient); - - impl.initialize(); - Thread.sleep(5); - - verify(mockClient); - assertTrue(impl.isInitialized()); - assertFalse(impl.isCancelled()); - assertEquals(1, impl.getNumAttempts()); - } - - public void testThirdTimesACharm() throws Exception { - mockClient.tryInit(); - expectLastCall().andThrow(new RuntimeException()).times(2); - mockClient.tryInit(); - mockClient.afterInit(); - replay(mockClient); - - impl.setRetryMillis(1); - impl.initialize(); - Thread.sleep(5); - - assertTrue(impl.isInitialized()); - assertFalse(impl.isCancelled()); - assertEquals(3, impl.getNumAttempts()); - verify(mockClient); - } - - @Test - public void testExceededRetries() throws Exception { - mockClient.tryInit(); - expectLastCall().andThrow(new RuntimeException()).times(2); - replay(mockClient); - - impl.setMaxRetries(2); - impl.setRetryMillis(30); - impl.initialize(); - Thread.sleep(100); - - assertFalse(impl.isInitialized()); - assertFalse(impl.isCancelled()); - assertEquals(2, impl.getNumAttempts()); - verify(mockClient); - } - - @Test - public void testExceededRetriesAndCallbackTriggeredSuccessfully() throws Exception { - mockClient.tryInit(); - expectLastCall().andThrow(new RuntimeException()).times(2); - - mockClient.configuredRetriesMetOrExceededWithoutSuccess(); - replay(mockClient); - - impl.setMaxRetries(2); - impl.setRetryMillis(3); - impl.initialize(); - Thread.sleep(10); - - assertFalse(impl.isInitialized()); - assertFalse(impl.isCancelled()); - assertEquals(2, impl.getNumAttempts()); - verify(mockClient); - } - - @Test - public void testCancellation() throws Exception { - mockClient.tryInit(); - expectLastCall().andThrow(new RuntimeException()); - replay(mockClient); - - impl.initialize(); - Thread.sleep(1); - impl.destroy(); - - assertFalse(impl.isInitialized()); - assertTrue(impl.isCancelled()); - assertEquals(1, impl.getNumAttempts()); - verify(mockClient); - } + private Initializable mockClient; + private Initializer impl; + + @Before + public void setUp() { + mockClient = createMock(Initializable.class); + impl = new Initializer(mockClient); + } + + @Test + public void testFirstTimeTrial() throws Exception { + mockClient.tryInit(); + mockClient.afterInit(); + replay(mockClient); + + impl.initialize(); + Thread.sleep(5); + + verify(mockClient); + assertTrue(impl.isInitialized()); + assertFalse(impl.isCancelled()); + assertEquals(1, impl.getNumAttempts()); + } + + public void testThirdTimesACharm() throws Exception { + mockClient.tryInit(); + expectLastCall().andThrow(new RuntimeException()).times(2); + mockClient.tryInit(); + mockClient.afterInit(); + replay(mockClient); + + impl.setRetryMillis(1); + impl.initialize(); + Thread.sleep(5); + + assertTrue(impl.isInitialized()); + assertFalse(impl.isCancelled()); + assertEquals(3, impl.getNumAttempts()); + verify(mockClient); + } + + @Test + public void testExceededRetries() throws Exception { + mockClient.tryInit(); + expectLastCall().andThrow(new RuntimeException()).times(2); + replay(mockClient); + + impl.setMaxRetries(2); + impl.setRetryMillis(30); + impl.initialize(); + Thread.sleep(100); + + assertFalse(impl.isInitialized()); + assertFalse(impl.isCancelled()); + assertEquals(2, impl.getNumAttempts()); + verify(mockClient); + } + + @Test + public void testExceededRetriesAndCallbackTriggeredSuccessfully() throws Exception { + mockClient.tryInit(); + expectLastCall().andThrow(new RuntimeException()).times(2); + + mockClient.configuredRetriesMetOrExceededWithoutSuccess(); + replay(mockClient); + + impl.setMaxRetries(2); + impl.setRetryMillis(3); + impl.initialize(); + Thread.sleep(10); + + assertFalse(impl.isInitialized()); + assertFalse(impl.isCancelled()); + assertEquals(2, impl.getNumAttempts()); + verify(mockClient); + } + + @Test + public void testCancellation() throws Exception { + mockClient.tryInit(); + expectLastCall().andThrow(new RuntimeException()); + replay(mockClient); + + impl.initialize(); + Thread.sleep(1); + impl.destroy(); + + assertFalse(impl.isInitialized()); + assertTrue(impl.isCancelled()); + assertEquals(1, impl.getNumAttempts()); + verify(mockClient); + } } diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/TestLatencyTracker.java b/jrugged-core/src/test/java/org/fishwife/jrugged/TestLatencyTracker.java index 3bdf7e24..4e515f0f 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/TestLatencyTracker.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/TestLatencyTracker.java @@ -27,45 +27,44 @@ import static org.junit.Assert.fail; public class TestLatencyTracker { - private LatencyTracker impl; + private LatencyTracker impl; - @Before - public void setUp() { - impl = new LatencyTracker(); - } + @Before + public void setUp() { + impl = new LatencyTracker(); + } - @Test - public void testCallableSuccess() throws Exception { - final Object o = new Object(); + @Test + public void testCallableSuccess() throws Exception { + final Object o = new Object(); - Object result = impl.invoke(new Callable() { - public Object call() throws Exception { - Thread.sleep(1); - return o; - } - }); + Object result = impl.invoke(new Callable() { + public Object call() throws Exception { + Thread.sleep(1); + return o; + } + }); - assertSame(result, o); - assertTrue(impl.getLastSuccessMillis() > 0); - assertEquals(0, impl.getLastFailureMillis()); - } + assertSame(result, o); + assertTrue(impl.getLastSuccessMillis() > 0); + assertEquals(0, impl.getLastFailureMillis()); + } - @Test - public void testCallableFailure() throws Exception { + @Test + public void testCallableFailure() throws Exception { - try { - impl.invoke(new Callable() { - public Object call() throws Exception { - Thread.sleep(1); - throw new Exception(); - } - }); - fail("should have thrown exception"); - } - catch (Exception expected) { - } + try { + impl.invoke(new Callable() { + public Object call() throws Exception { + Thread.sleep(1); + throw new Exception(); + } + }); + fail("should have thrown exception"); + } catch (Exception expected) { + } - assertTrue(impl.getLastFailureMillis() > 0); - assertEquals(0, impl.getLastSuccessMillis()); - } + assertTrue(impl.getLastFailureMillis() > 0); + assertEquals(0, impl.getLastSuccessMillis()); + } } diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/TestPercentErrPerTimeFailureInterpreter.java b/jrugged-core/src/test/java/org/fishwife/jrugged/TestPercentErrPerTimeFailureInterpreter.java index bb72cc8c..27a8fc5a 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/TestPercentErrPerTimeFailureInterpreter.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/TestPercentErrPerTimeFailureInterpreter.java @@ -23,283 +23,264 @@ public class TestPercentErrPerTimeFailureInterpreter { - private class DummyRunnable implements Runnable { - - public void run() { - - } - } - - private class DummyRunnableException implements Runnable { - - public void run() { - throw new RuntimeException(); - } - } - - private class DummyMyRunnableException implements Runnable { - - public void run() { - throw new MyRuntimeException(); - } - } - - private class MyRuntimeException extends RuntimeException { - - } - - @Test - public void testTripsWhenNoWindowConditionsExist() { - PercentErrPerTimeFailureInterpreter pept = new PercentErrPerTimeFailureInterpreter(); - - assertTrue(pept.shouldTrip(new Exception())); - } - - @Test - @SuppressWarnings("unchecked") - public void testDoesNotTripWhenExceptionIsIgnored() { - PercentErrPerTimeFailureInterpreter pept = new PercentErrPerTimeFailureInterpreter(); - pept.setIgnore(new Class[] {MyRuntimeException.class}); - - assertFalse(pept.shouldTrip(new MyRuntimeException())); - assertTrue(pept.shouldTrip(new Exception())); - } - - @Test - public void testTripsWhenPercentErrorInWindowIsGreaterThanConfigured() throws Exception { - PercentErrPerTimeFailureInterpreter pept = new PercentErrPerTimeFailureInterpreter(); - RequestCounter rc = new RequestCounter(); - pept.setRequestCounter(rc); - pept.setPercent(51); - pept.setWindowMillis(5000); - - try { - rc.invoke(new DummyRunnable()); - rc.invoke(new DummyRunnable()); - rc.invoke(new DummyRunnable()); - } - catch (Exception e) { - pept.shouldTrip(e); - } - - try { - rc.invoke(new DummyRunnableException()); - } - catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - - try { - rc.invoke(new DummyRunnableException()); - } - catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - - try { - rc.invoke(new DummyRunnableException()); - } - catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - - try { - rc.invoke(new DummyRunnableException()); - } - catch (Exception e) { - assertTrue(pept.shouldTrip(e)); - } - } - - @Test - public void testTripsWhenPercentErrorInWindowIsEqualConfigured() throws Exception { - PercentErrPerTimeFailureInterpreter pept = new PercentErrPerTimeFailureInterpreter(); - RequestCounter rc = new RequestCounter(); - pept.setRequestCounter(rc); - pept.setPercent(50); - pept.setWindowMillis(5000); - - try { - rc.invoke(new DummyRunnable()); - rc.invoke(new DummyRunnable()); - rc.invoke(new DummyRunnable()); - } - catch (Exception e) { - pept.shouldTrip(e); - } - - try { - rc.invoke(new DummyRunnableException()); - } - catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - - try { - rc.invoke(new DummyRunnableException()); - } - catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - - try { - rc.invoke(new DummyRunnableException()); - } - catch (Exception e) { - assertTrue(pept.shouldTrip(e)); - } - } - - @Test - public void testDoesNotTripWhenPercentErrorInWindowIsLessThanConfigured() throws Exception { - PercentErrPerTimeFailureInterpreter pept = new PercentErrPerTimeFailureInterpreter(); - RequestCounter rc = new RequestCounter(); - pept.setRequestCounter(rc); - pept.setPercent(51); - pept.setWindowMillis(2500); - - try { - rc.invoke(new DummyRunnable()); - rc.invoke(new DummyRunnable()); - rc.invoke(new DummyRunnable()); - } - catch (Exception e) { - pept.shouldTrip(e); - } - - try { - rc.invoke(new DummyRunnableException()); - } - catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - - try { - rc.invoke(new DummyRunnableException()); - } - catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - - try { - rc.invoke(new DummyRunnableException()); - } - catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - } - - @Test - public void testDoesNotTripWhenRequestCountInWindowIsLessThanThreshold() throws Exception { - PercentErrPerTimeFailureInterpreter pept = new PercentErrPerTimeFailureInterpreter(); - RequestCounter rc = new RequestCounter(); - pept.setRequestCounter(rc); - pept.setPercent(51); - pept.setWindowMillis(250000); - pept.setRequestThreshold(8); - - try { - rc.invoke(new DummyRunnable()); - rc.invoke(new DummyRunnable()); - rc.invoke(new DummyRunnable()); - } catch (Exception e) { - pept.shouldTrip(e); - } - - try { - rc.invoke(new DummyRunnableException()); - } catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - - try { - rc.invoke(new DummyRunnableException()); - } catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - - try { - rc.invoke(new DummyRunnableException()); - } catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - - //seventh total request, fourth failure, pushes percentage to 57%, but should not trip because total number - //of requests is below threshold of 8 - try { - rc.invoke(new DummyRunnableException()); - } catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - } - - @Test - public void testTripsOnSecondWindowWithRequestThresholdConfigured() throws Exception { - PercentErrPerTimeFailureInterpreter pept = new PercentErrPerTimeFailureInterpreter(); - RequestCounter rc = new RequestCounter(); - pept.setRequestCounter(rc); - pept.setPercent(50); - pept.setWindowMillis(1000); - pept.setRequestThreshold(5); - - // - // First window - 1 valid request, 3 failures valid requests, less than request window, not tripped - // - rc.invoke(new DummyRunnable()); - - try { - rc.invoke(new DummyRunnableException()); - } - catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - - try { - rc.invoke(new DummyRunnableException()); - } - catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - - try { - rc.invoke(new DummyRunnableException()); - } - catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - - // Sleep to reset the window - Thread.sleep(1100); - - // - // Second Window - 2 failures, 2 success, 1 failure - should trip at the end. - // - try { - rc.invoke(new DummyRunnableException()); - } - catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - - try { - rc.invoke(new DummyRunnableException()); - } - catch (Exception e) { - assertFalse(pept.shouldTrip(e)); - } - - rc.invoke(new DummyRunnable()); - rc.invoke(new DummyRunnable()); - - try { - rc.invoke(new DummyRunnableException()); - } - catch (Exception e) { - assertTrue(pept.shouldTrip(e)); - } - - - } + private class DummyRunnable implements Runnable { + + public void run() { + + } + } + + private class DummyRunnableException implements Runnable { + + public void run() { + throw new RuntimeException(); + } + } + + private class DummyMyRunnableException implements Runnable { + + public void run() { + throw new MyRuntimeException(); + } + } + + private class MyRuntimeException extends RuntimeException { + + } + + @Test + public void testTripsWhenNoWindowConditionsExist() { + PercentErrPerTimeFailureInterpreter pept = new PercentErrPerTimeFailureInterpreter(); + + assertTrue(pept.shouldTrip(new Exception())); + } + + @Test + @SuppressWarnings("unchecked") + public void testDoesNotTripWhenExceptionIsIgnored() { + PercentErrPerTimeFailureInterpreter pept = new PercentErrPerTimeFailureInterpreter(); + pept.setIgnore(new Class[] { MyRuntimeException.class }); + + assertFalse(pept.shouldTrip(new MyRuntimeException())); + assertTrue(pept.shouldTrip(new Exception())); + } + + @Test + public void testTripsWhenPercentErrorInWindowIsGreaterThanConfigured() throws Exception { + PercentErrPerTimeFailureInterpreter pept = new PercentErrPerTimeFailureInterpreter(); + RequestCounter rc = new RequestCounter(); + pept.setRequestCounter(rc); + pept.setPercent(51); + pept.setWindowMillis(5000); + + try { + rc.invoke(new DummyRunnable()); + rc.invoke(new DummyRunnable()); + rc.invoke(new DummyRunnable()); + } catch (Exception e) { + pept.shouldTrip(e); + } + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertTrue(pept.shouldTrip(e)); + } + } + + @Test + public void testTripsWhenPercentErrorInWindowIsEqualConfigured() throws Exception { + PercentErrPerTimeFailureInterpreter pept = new PercentErrPerTimeFailureInterpreter(); + RequestCounter rc = new RequestCounter(); + pept.setRequestCounter(rc); + pept.setPercent(50); + pept.setWindowMillis(5000); + + try { + rc.invoke(new DummyRunnable()); + rc.invoke(new DummyRunnable()); + rc.invoke(new DummyRunnable()); + } catch (Exception e) { + pept.shouldTrip(e); + } + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertTrue(pept.shouldTrip(e)); + } + } + + @Test + public void testDoesNotTripWhenPercentErrorInWindowIsLessThanConfigured() throws Exception { + PercentErrPerTimeFailureInterpreter pept = new PercentErrPerTimeFailureInterpreter(); + RequestCounter rc = new RequestCounter(); + pept.setRequestCounter(rc); + pept.setPercent(51); + pept.setWindowMillis(2500); + + try { + rc.invoke(new DummyRunnable()); + rc.invoke(new DummyRunnable()); + rc.invoke(new DummyRunnable()); + } catch (Exception e) { + pept.shouldTrip(e); + } + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + } + + @Test + public void testDoesNotTripWhenRequestCountInWindowIsLessThanThreshold() throws Exception { + PercentErrPerTimeFailureInterpreter pept = new PercentErrPerTimeFailureInterpreter(); + RequestCounter rc = new RequestCounter(); + pept.setRequestCounter(rc); + pept.setPercent(51); + pept.setWindowMillis(250000); + pept.setRequestThreshold(8); + + try { + rc.invoke(new DummyRunnable()); + rc.invoke(new DummyRunnable()); + rc.invoke(new DummyRunnable()); + } catch (Exception e) { + pept.shouldTrip(e); + } + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + + // seventh total request, fourth failure, pushes percentage to 57%, but should + // not trip because total number + // of requests is below threshold of 8 + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + } + + @Test + public void testTripsOnSecondWindowWithRequestThresholdConfigured() throws Exception { + PercentErrPerTimeFailureInterpreter pept = new PercentErrPerTimeFailureInterpreter(); + RequestCounter rc = new RequestCounter(); + pept.setRequestCounter(rc); + pept.setPercent(50); + pept.setWindowMillis(1000); + pept.setRequestThreshold(5); + + // + // First window - 1 valid request, 3 failures valid requests, less than request + // window, not tripped + // + rc.invoke(new DummyRunnable()); + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + + // Sleep to reset the window + Thread.sleep(1100); + + // + // Second Window - 2 failures, 2 success, 1 failure - should trip at the end. + // + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertFalse(pept.shouldTrip(e)); + } + + rc.invoke(new DummyRunnable()); + rc.invoke(new DummyRunnable()); + + try { + rc.invoke(new DummyRunnableException()); + } catch (Exception e) { + assertTrue(pept.shouldTrip(e)); + } + + } } - diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/TestPerformanceMonitor.java b/jrugged-core/src/test/java/org/fishwife/jrugged/TestPerformanceMonitor.java index eabaebaa..9a31b724 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/TestPerformanceMonitor.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/TestPerformanceMonitor.java @@ -22,89 +22,86 @@ public class TestPerformanceMonitor { - @Test - public void testSuccessAndFailureCounts() { - int numberOfTimesToTryAMethodCall = 500; - int numberOfAttemptsBeforeThrowingException = 5; - int expectedNumberOfFailures = - numberOfTimesToTryAMethodCall - / numberOfAttemptsBeforeThrowingException; - - int expectedNumberOfSuccess = numberOfTimesToTryAMethodCall - - expectedNumberOfFailures; - - PerformanceMonitor perfMon = new PerformanceMonitor(); - - final OccasionalExceptionPerformer performer = - new OccasionalExceptionPerformer(numberOfAttemptsBeforeThrowingException); - - for(int i=0; i testSet = new HashSet(); - testSet.add("one"); - testSet.add("two"); - testSet.add("three"); - testSet.add("four"); + @Test + public void testGetPerformanceMonitorNames() { + Set testSet = new HashSet(); + testSet.add("one"); + testSet.add("two"); + testSet.add("three"); + testSet.add("four"); - factory.createPerformanceMonitor("one"); - factory.createPerformanceMonitor("two"); - factory.createPerformanceMonitor("three"); - factory.createPerformanceMonitor("four"); - Set monitorNames = factory.getPerformanceMonitorNames(); - assertEquals(testSet, monitorNames); - } + factory.createPerformanceMonitor("one"); + factory.createPerformanceMonitor("two"); + factory.createPerformanceMonitor("three"); + factory.createPerformanceMonitor("four"); + Set monitorNames = factory.getPerformanceMonitorNames(); + assertEquals(testSet, monitorNames); + } } diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/TestRequestCounter.java b/jrugged-core/src/test/java/org/fishwife/jrugged/TestRequestCounter.java index dd26e200..ea56efb2 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/TestRequestCounter.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/TestRequestCounter.java @@ -10,110 +10,110 @@ import org.junit.Test; public class TestRequestCounter { - - private RequestCounter impl; - - @Before - public void setUp() { - impl = new RequestCounter(); - } - - @Test - public void testCountersInitiallyZero() { - assertArrayEquals(new long[] {0L, 0L, 0L}, impl.sample()); - } - - @Test - public void testSucceedingCallable() throws Exception { - final Object o = new Object(); - - Object result = impl.invoke(new Callable() { - public Object call() throws Exception { - return o; - } - }); - - assertSame(o, result); - assertArrayEquals(new long[] {1L, 1L, 0L}, impl.sample()); - } - - @Test - public void testFailingCallable() throws Exception { - final Exception ex = new Exception(); - - try { - impl.invoke(new Callable() { - public Object call() throws Exception { - throw ex; - } - }); - fail("should have thrown exception"); - } catch (Exception e) { - assertSame(ex, e); - } - - assertArrayEquals(new long[] {1L, 0L, 1L}, impl.sample()); - } - - @Test - public void testSucceedingRunnable() throws Exception { - impl.invoke(new Runnable() { - public void run() { - // no-op - } - }); - - assertArrayEquals(new long[] {1L, 1L, 0L}, impl.sample()); - } - - @Test - public void testFailingRunnable() { - final RuntimeException rtex = new RuntimeException(); - - try { - impl.invoke(new Runnable() { - public void run() { - throw rtex; - } - }); - fail("should have thrown exception"); - } catch (Exception e) { - assertSame(rtex, e); - } - - assertArrayEquals(new long[] {1L, 0L, 1L}, impl.sample()); - } - - @Test - public void testSucceedingRunnableWithResult() throws Exception { - final Object o = new Object(); - - Object result = impl.invoke(new Runnable() { - public void run() { - // no-op - } - }, o); - - assertSame(o, result); - assertArrayEquals(new long[] {1L, 1L, 0L}, impl.sample()); - } - - @Test - public void testFailingRunnableWithResult() { - final Object o = new Object(); - final RuntimeException rtex = new RuntimeException(); - - try { - impl.invoke(new Runnable() { - public void run() { - throw rtex; - } - }, o); - fail("should have thrown exception"); - } catch (Exception e) { - assertSame(rtex, e); - } - - assertArrayEquals(new long[] {1L, 0L, 1L}, impl.sample()); - } + + private RequestCounter impl; + + @Before + public void setUp() { + impl = new RequestCounter(); + } + + @Test + public void testCountersInitiallyZero() { + assertArrayEquals(new long[] { 0L, 0L, 0L }, impl.sample()); + } + + @Test + public void testSucceedingCallable() throws Exception { + final Object o = new Object(); + + Object result = impl.invoke(new Callable() { + public Object call() throws Exception { + return o; + } + }); + + assertSame(o, result); + assertArrayEquals(new long[] { 1L, 1L, 0L }, impl.sample()); + } + + @Test + public void testFailingCallable() throws Exception { + final Exception ex = new Exception(); + + try { + impl.invoke(new Callable() { + public Object call() throws Exception { + throw ex; + } + }); + fail("should have thrown exception"); + } catch (Exception e) { + assertSame(ex, e); + } + + assertArrayEquals(new long[] { 1L, 0L, 1L }, impl.sample()); + } + + @Test + public void testSucceedingRunnable() throws Exception { + impl.invoke(new Runnable() { + public void run() { + // no-op + } + }); + + assertArrayEquals(new long[] { 1L, 1L, 0L }, impl.sample()); + } + + @Test + public void testFailingRunnable() { + final RuntimeException rtex = new RuntimeException(); + + try { + impl.invoke(new Runnable() { + public void run() { + throw rtex; + } + }); + fail("should have thrown exception"); + } catch (Exception e) { + assertSame(rtex, e); + } + + assertArrayEquals(new long[] { 1L, 0L, 1L }, impl.sample()); + } + + @Test + public void testSucceedingRunnableWithResult() throws Exception { + final Object o = new Object(); + + Object result = impl.invoke(new Runnable() { + public void run() { + // no-op + } + }, o); + + assertSame(o, result); + assertArrayEquals(new long[] { 1L, 1L, 0L }, impl.sample()); + } + + @Test + public void testFailingRunnableWithResult() { + final Object o = new Object(); + final RuntimeException rtex = new RuntimeException(); + + try { + impl.invoke(new Runnable() { + public void run() { + throw rtex; + } + }, o); + fail("should have thrown exception"); + } catch (Exception e) { + assertSame(rtex, e); + } + + assertArrayEquals(new long[] { 1L, 0L, 1L }, impl.sample()); + } } diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/TestRolledUpMonitoredService.java b/jrugged-core/src/test/java/org/fishwife/jrugged/TestRolledUpMonitoredService.java index 29d75791..6928b05b 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/TestRolledUpMonitoredService.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/TestRolledUpMonitoredService.java @@ -29,142 +29,142 @@ public class TestRolledUpMonitoredService { - private RolledUpMonitoredService rolledUpService; - - MonitoredService mockService1; - - MonitoredService mockService2; - - private static final String ROLLEDUP_SERVICE_NAME = "Rolledup Service"; - private static final String SERVICE_1_NAME="Service 1"; - private static final String SERVICE_2_NAME="Service 2"; - - private static final String SERVICE_1_REASON = "Service 1 Reason"; - private static final String SERVICE_2_REASON = "Service 2 Reason"; - - @Before - public void setUp() { - mockService1 = createMock(MonitoredService.class); - mockService2 = createMock(MonitoredService.class); - List criticals = new ArrayList(); - criticals.add(mockService1); - List nonCriticals = new ArrayList(); - nonCriticals.add(mockService2); - - rolledUpService = new RolledUpMonitoredService(ROLLEDUP_SERVICE_NAME, criticals, nonCriticals); - } - - @Test - public void testAllUp() { - expect(mockService1.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_1_NAME, Status.UP)); - expect(mockService2.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_2_NAME, Status.UP)); - replay(mockService1); - replay(mockService2); - - ServiceStatus serviceStatus = rolledUpService.getServiceStatus(); - - assertEquals(ROLLEDUP_SERVICE_NAME, serviceStatus.getName()); - assertEquals(serviceStatus.getStatus(), Status.UP); - } - - @Test - public void testCriticalDegraded() { - expect(mockService1.getServiceStatus()).andReturn( - new ServiceStatus(SERVICE_1_NAME, Status.DEGRADED, SERVICE_1_REASON)); - expect(mockService2.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_2_NAME, Status.UP)); - replay(mockService1); - replay(mockService2); - - ServiceStatus serviceStatus = rolledUpService.getServiceStatus(); - - assertEquals(ROLLEDUP_SERVICE_NAME, serviceStatus.getName()); - assertEquals(serviceStatus.getStatus(), Status.DEGRADED); - assertTrue(serviceStatus.getReasons().contains(SERVICE_1_NAME + ":" + SERVICE_1_REASON)); - } - - @Test - public void testNonCriticalDegraded() { - expect(mockService1.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_1_NAME, Status.UP)); - expect(mockService2.getServiceStatus()).andReturn( - new ServiceStatus(SERVICE_2_NAME, Status.DEGRADED, SERVICE_2_REASON)); - replay(mockService1); - replay(mockService2); - - ServiceStatus serviceStatus = rolledUpService.getServiceStatus(); - - assertEquals(ROLLEDUP_SERVICE_NAME, serviceStatus.getName()); - assertEquals(serviceStatus.getStatus(), Status.DEGRADED); - assertTrue(serviceStatus.getReasons().contains(SERVICE_2_NAME + ":" + SERVICE_2_REASON)); - } - - @Test - public void testCritialAndNonCriticalDegraded() { - expect(mockService1.getServiceStatus()).andReturn( - new ServiceStatus(SERVICE_1_NAME, Status.DEGRADED, SERVICE_1_REASON)); - expect(mockService2.getServiceStatus()).andReturn( - new ServiceStatus(SERVICE_2_NAME, Status.DEGRADED, SERVICE_2_REASON)); - replay(mockService1); - replay(mockService2); - - ServiceStatus serviceStatus = rolledUpService.getServiceStatus(); - - assertEquals(ROLLEDUP_SERVICE_NAME, serviceStatus.getName()); - assertEquals(serviceStatus.getStatus(), Status.DEGRADED); - assertTrue(serviceStatus.getReasons().contains(SERVICE_1_NAME + ":" + SERVICE_1_REASON)); - assertTrue(serviceStatus.getReasons().contains(SERVICE_2_NAME + ":" + SERVICE_2_REASON)); - } - - @Test - public void testNonCritialDown() { - expect(mockService1.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_1_NAME, Status.UP)); - expect(mockService2.getServiceStatus()).andReturn( - new ServiceStatus(SERVICE_2_NAME, Status.DOWN, SERVICE_2_REASON)); - replay(mockService1); - replay(mockService2); - - ServiceStatus serviceStatus = rolledUpService.getServiceStatus(); - - assertEquals(ROLLEDUP_SERVICE_NAME, serviceStatus.getName()); - assertEquals(serviceStatus.getStatus(), Status.DEGRADED); - assertTrue(serviceStatus.getReasons().contains(SERVICE_2_NAME + ":" + SERVICE_2_REASON)); - } - - @Test - public void testGetServiceStatusList() { - expect(mockService1.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_1_NAME, Status.UP)).times(2); - expect(mockService2.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_2_NAME, Status.UP)).times(2); - replay(mockService1); - replay(mockService2); - - List statusList = rolledUpService.getAllStatuses(); - - assertTrue(statusList.contains(mockService1.getServiceStatus())); - assertTrue(statusList.contains(mockService2.getServiceStatus())); - } - - @Test - public void testGetCriticalServiceStatusList() { - expect(mockService1.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_1_NAME, Status.UP)).times(2); - expect(mockService2.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_2_NAME, Status.UP)).times(2); - replay(mockService1); - replay(mockService2); - - List statusList = rolledUpService.getCriticalStatuses(); - - assertTrue(statusList.contains(mockService1.getServiceStatus())); - assertFalse(statusList.contains(mockService2.getServiceStatus())); - } - - @Test - public void testGetNonCriticalServiceStatusList() { - expect(mockService1.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_1_NAME, Status.UP)).times(2); - expect(mockService2.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_2_NAME, Status.UP)).times(2); - replay(mockService1); - replay(mockService2); - - List statusList = rolledUpService.getNonCriticalStatuses(); - - assertFalse(statusList.contains(mockService1.getServiceStatus())); - assertTrue(statusList.contains(mockService2.getServiceStatus())); - } + private RolledUpMonitoredService rolledUpService; + + MonitoredService mockService1; + + MonitoredService mockService2; + + private static final String ROLLEDUP_SERVICE_NAME = "Rolledup Service"; + private static final String SERVICE_1_NAME = "Service 1"; + private static final String SERVICE_2_NAME = "Service 2"; + + private static final String SERVICE_1_REASON = "Service 1 Reason"; + private static final String SERVICE_2_REASON = "Service 2 Reason"; + + @Before + public void setUp() { + mockService1 = createMock(MonitoredService.class); + mockService2 = createMock(MonitoredService.class); + List criticals = new ArrayList(); + criticals.add(mockService1); + List nonCriticals = new ArrayList(); + nonCriticals.add(mockService2); + + rolledUpService = new RolledUpMonitoredService(ROLLEDUP_SERVICE_NAME, criticals, nonCriticals); + } + + @Test + public void testAllUp() { + expect(mockService1.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_1_NAME, Status.UP)); + expect(mockService2.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_2_NAME, Status.UP)); + replay(mockService1); + replay(mockService2); + + ServiceStatus serviceStatus = rolledUpService.getServiceStatus(); + + assertEquals(ROLLEDUP_SERVICE_NAME, serviceStatus.getName()); + assertEquals(serviceStatus.getStatus(), Status.UP); + } + + @Test + public void testCriticalDegraded() { + expect(mockService1.getServiceStatus()) + .andReturn(new ServiceStatus(SERVICE_1_NAME, Status.DEGRADED, SERVICE_1_REASON)); + expect(mockService2.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_2_NAME, Status.UP)); + replay(mockService1); + replay(mockService2); + + ServiceStatus serviceStatus = rolledUpService.getServiceStatus(); + + assertEquals(ROLLEDUP_SERVICE_NAME, serviceStatus.getName()); + assertEquals(serviceStatus.getStatus(), Status.DEGRADED); + assertTrue(serviceStatus.getReasons().contains(SERVICE_1_NAME + ":" + SERVICE_1_REASON)); + } + + @Test + public void testNonCriticalDegraded() { + expect(mockService1.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_1_NAME, Status.UP)); + expect(mockService2.getServiceStatus()) + .andReturn(new ServiceStatus(SERVICE_2_NAME, Status.DEGRADED, SERVICE_2_REASON)); + replay(mockService1); + replay(mockService2); + + ServiceStatus serviceStatus = rolledUpService.getServiceStatus(); + + assertEquals(ROLLEDUP_SERVICE_NAME, serviceStatus.getName()); + assertEquals(serviceStatus.getStatus(), Status.DEGRADED); + assertTrue(serviceStatus.getReasons().contains(SERVICE_2_NAME + ":" + SERVICE_2_REASON)); + } + + @Test + public void testCritialAndNonCriticalDegraded() { + expect(mockService1.getServiceStatus()) + .andReturn(new ServiceStatus(SERVICE_1_NAME, Status.DEGRADED, SERVICE_1_REASON)); + expect(mockService2.getServiceStatus()) + .andReturn(new ServiceStatus(SERVICE_2_NAME, Status.DEGRADED, SERVICE_2_REASON)); + replay(mockService1); + replay(mockService2); + + ServiceStatus serviceStatus = rolledUpService.getServiceStatus(); + + assertEquals(ROLLEDUP_SERVICE_NAME, serviceStatus.getName()); + assertEquals(serviceStatus.getStatus(), Status.DEGRADED); + assertTrue(serviceStatus.getReasons().contains(SERVICE_1_NAME + ":" + SERVICE_1_REASON)); + assertTrue(serviceStatus.getReasons().contains(SERVICE_2_NAME + ":" + SERVICE_2_REASON)); + } + + @Test + public void testNonCritialDown() { + expect(mockService1.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_1_NAME, Status.UP)); + expect(mockService2.getServiceStatus()) + .andReturn(new ServiceStatus(SERVICE_2_NAME, Status.DOWN, SERVICE_2_REASON)); + replay(mockService1); + replay(mockService2); + + ServiceStatus serviceStatus = rolledUpService.getServiceStatus(); + + assertEquals(ROLLEDUP_SERVICE_NAME, serviceStatus.getName()); + assertEquals(serviceStatus.getStatus(), Status.DEGRADED); + assertTrue(serviceStatus.getReasons().contains(SERVICE_2_NAME + ":" + SERVICE_2_REASON)); + } + + @Test + public void testGetServiceStatusList() { + expect(mockService1.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_1_NAME, Status.UP)).times(2); + expect(mockService2.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_2_NAME, Status.UP)).times(2); + replay(mockService1); + replay(mockService2); + + List statusList = rolledUpService.getAllStatuses(); + + assertTrue(statusList.contains(mockService1.getServiceStatus())); + assertTrue(statusList.contains(mockService2.getServiceStatus())); + } + + @Test + public void testGetCriticalServiceStatusList() { + expect(mockService1.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_1_NAME, Status.UP)).times(2); + expect(mockService2.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_2_NAME, Status.UP)).times(2); + replay(mockService1); + replay(mockService2); + + List statusList = rolledUpService.getCriticalStatuses(); + + assertTrue(statusList.contains(mockService1.getServiceStatus())); + assertFalse(statusList.contains(mockService2.getServiceStatus())); + } + + @Test + public void testGetNonCriticalServiceStatusList() { + expect(mockService1.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_1_NAME, Status.UP)).times(2); + expect(mockService2.getServiceStatus()).andReturn(new ServiceStatus(SERVICE_2_NAME, Status.UP)).times(2); + replay(mockService1); + replay(mockService2); + + List statusList = rolledUpService.getNonCriticalStatuses(); + + assertFalse(statusList.contains(mockService1.getServiceStatus())); + assertTrue(statusList.contains(mockService2.getServiceStatus())); + } } diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/TestSampledQuantile.java b/jrugged-core/src/test/java/org/fishwife/jrugged/TestSampledQuantile.java index 97303c51..704c98f4 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/TestSampledQuantile.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/TestSampledQuantile.java @@ -25,185 +25,186 @@ public class TestSampledQuantile { - private SampledQuantile impl; - - @Before - public void setUp() { - impl = new SampledQuantile(); - } - - @Test - public void quantileWithNoSamplesShouldReturnZero() { - assertEquals(0, impl.getPercentile(50)); - } - - @Test - public void quantileWithOneSampleShouldReturnThatSample() { - impl.addSample(42); - assertEquals(42, impl.getPercentile(50)); - } - - @Test - public void medianOfThreeSamplesIsMiddleSample() { - impl.addSample(42); - impl.addSample(41); - impl.addSample(43); - assertEquals(42, impl.getPercentile(50)); - } - - @Test - public void medianOfFiveSamplesWithRepeatsStillWorks() { - impl.addSample(41); - impl.addSample(43); - impl.addSample(42); - impl.addSample(41); - impl.addSample(43); - assertEquals(42, impl.getPercentile(50)); - } - - @Test - public void medianOfTwoSamplesIsTheirAverage() { - impl.addSample(41); - impl.addSample(43); - assertEquals(42, impl.getPercentile(50)); - } - - @Test - public void canGetMedianAsExpressedInQuantiles() { - impl.addSample(42); - impl.addSample(41); - impl.addSample(43); - assertEquals(42, impl.getQuantile(1,2)); - } - - @Test - public void canGetMedianDirectly() { - impl.addSample(42); - impl.addSample(41); - impl.addSample(43); - assertEquals(42, impl.getMedian()); - } - - @Test - public void zerothQuantileShouldThrowException() { - impl.addSample(41); - try { - impl.getQuantile(0,7); - fail("should have thrown exception"); - } catch (SampledQuantile.QuantileOutOfBoundsException expected) { - } - } - - @Test - public void qthQuantileShouldThrowException() { - impl.addSample(41); - try { - impl.getQuantile(7,7); - fail("should have thrown exception"); - } catch (SampledQuantile.QuantileOutOfBoundsException expected) { - } - } - - @Test - public void canSpecifyMaxSamples() { - impl = new SampledQuantile(10); - for(int i=0; i<20; i++) impl.addSample(0); - assertEquals(10, impl.getNumSamples()); - } - - @Test - public void canSpecifyCurrentTimeWhenAddingSample() { - impl.addSample(41, System.currentTimeMillis()); - } - - @Test - public void ignoresSamplesOutsideOfSpecifiedSecondWindow() { - impl = new SampledQuantile(60, TimeUnit.SECONDS); - long now = System.currentTimeMillis(); - impl.addSample(7, now - 90 * 1000L); - impl.addSample(42, now); - assertEquals(42, impl.getPercentile(50, now+1)); - } - - @Test - public void ignoresSamplesOutsideOfSpecifiedNanosecondWindow() { - impl = new SampledQuantile(60 * 1000000000L, TimeUnit.NANOSECONDS); - long now = System.currentTimeMillis(); - impl.addSample(7, now - 90 * 1000L); - impl.addSample(42, now); - assertEquals(42, impl.getPercentile(50, now+1)); - } - - @Test - public void ignoresSamplesOutsideOfSpecifiedMicrosecondWindow() { - impl = new SampledQuantile(60 * 1000000L, TimeUnit.MICROSECONDS); - long now = System.currentTimeMillis(); - impl.addSample(7, now - 90 * 1000L); - impl.addSample(42, now); - assertEquals(42, impl.getPercentile(50, now+1)); - } - - @Test - public void ignoresSamplesOutsideOfSpecifiedMillisecondWindow() { - impl = new SampledQuantile(60 * 1000L, TimeUnit.MILLISECONDS); - long now = System.currentTimeMillis(); - impl.addSample(7, now - 90 * 1000L); - impl.addSample(42, now); - assertEquals(42, impl.getPercentile(50, now+1)); - } - - @Test - public void ignoresSamplesOutsideOfSpecifiedMinuteWindow() { - impl = new SampledQuantile(60L, TimeUnit.SECONDS); - long now = System.currentTimeMillis(); - impl.addSample(7, now - 90 * 1000L); - impl.addSample(42, now); - assertEquals(42, impl.getPercentile(50, now+1)); - } - - @Test - public void ignoresSamplesOutsideOfSpecifiedHourWindow() { - impl = new SampledQuantile(3600L, TimeUnit.SECONDS); - long now = System.currentTimeMillis(); - impl.addSample(7, now - 5400 * 1000L); - impl.addSample(42, now); - assertEquals(42, impl.getPercentile(50, now+1)); - } - - @Test - public void ignoresSamplesOutsideOfSpecifiedDayWindow() { - impl = new SampledQuantile(86400L, TimeUnit.SECONDS); - long now = System.currentTimeMillis(); - impl.addSample(7, now - 2 * 24 * 3600 * 1000L); - impl.addSample(42, now); - assertEquals(42, impl.getPercentile(50, now+1)); - } - - @Test - public void windowedSamplingWorks() { - long t0 = System.currentTimeMillis(); - impl = new SampledQuantile(10, 60L, TimeUnit.SECONDS, t0); - for(int t=0; t<30 * 1000; t++) { - impl.addSample(1L, t0 + t); - } - long t1 = t0 + 30 * 1000L; - assertEquals(1L, impl.getPercentile(50, t1)); - - for(int t=0; t<60*1000; t++) { - impl.addSample(2L, t1 + t); - } - long t2 = t1 + 60 * 1000L; - assertEquals(2L, impl.getPercentile(50, t2)); - impl.addSample(3L, t2+1); - } - - @Test - public void windowedSamplingHandlesLongTimesBetweenSamples() { - long t0 = System.currentTimeMillis(); - impl = new SampledQuantile(10, 60L, TimeUnit.SECONDS, t0); - impl.addSample(1L, t0 + 1); - long t1 = t0 + 90 * 1000L; - impl.addSample(2L, t1); - assertEquals(2L, impl.getPercentile(50, t1)); - } + private SampledQuantile impl; + + @Before + public void setUp() { + impl = new SampledQuantile(); + } + + @Test + public void quantileWithNoSamplesShouldReturnZero() { + assertEquals(0, impl.getPercentile(50)); + } + + @Test + public void quantileWithOneSampleShouldReturnThatSample() { + impl.addSample(42); + assertEquals(42, impl.getPercentile(50)); + } + + @Test + public void medianOfThreeSamplesIsMiddleSample() { + impl.addSample(42); + impl.addSample(41); + impl.addSample(43); + assertEquals(42, impl.getPercentile(50)); + } + + @Test + public void medianOfFiveSamplesWithRepeatsStillWorks() { + impl.addSample(41); + impl.addSample(43); + impl.addSample(42); + impl.addSample(41); + impl.addSample(43); + assertEquals(42, impl.getPercentile(50)); + } + + @Test + public void medianOfTwoSamplesIsTheirAverage() { + impl.addSample(41); + impl.addSample(43); + assertEquals(42, impl.getPercentile(50)); + } + + @Test + public void canGetMedianAsExpressedInQuantiles() { + impl.addSample(42); + impl.addSample(41); + impl.addSample(43); + assertEquals(42, impl.getQuantile(1, 2)); + } + + @Test + public void canGetMedianDirectly() { + impl.addSample(42); + impl.addSample(41); + impl.addSample(43); + assertEquals(42, impl.getMedian()); + } + + @Test + public void zerothQuantileShouldThrowException() { + impl.addSample(41); + try { + impl.getQuantile(0, 7); + fail("should have thrown exception"); + } catch (SampledQuantile.QuantileOutOfBoundsException expected) { + } + } + + @Test + public void qthQuantileShouldThrowException() { + impl.addSample(41); + try { + impl.getQuantile(7, 7); + fail("should have thrown exception"); + } catch (SampledQuantile.QuantileOutOfBoundsException expected) { + } + } + + @Test + public void canSpecifyMaxSamples() { + impl = new SampledQuantile(10); + for (int i = 0; i < 20; i++) + impl.addSample(0); + assertEquals(10, impl.getNumSamples()); + } + + @Test + public void canSpecifyCurrentTimeWhenAddingSample() { + impl.addSample(41, System.currentTimeMillis()); + } + + @Test + public void ignoresSamplesOutsideOfSpecifiedSecondWindow() { + impl = new SampledQuantile(60, TimeUnit.SECONDS); + long now = System.currentTimeMillis(); + impl.addSample(7, now - 90 * 1000L); + impl.addSample(42, now); + assertEquals(42, impl.getPercentile(50, now + 1)); + } + + @Test + public void ignoresSamplesOutsideOfSpecifiedNanosecondWindow() { + impl = new SampledQuantile(60 * 1000000000L, TimeUnit.NANOSECONDS); + long now = System.currentTimeMillis(); + impl.addSample(7, now - 90 * 1000L); + impl.addSample(42, now); + assertEquals(42, impl.getPercentile(50, now + 1)); + } + + @Test + public void ignoresSamplesOutsideOfSpecifiedMicrosecondWindow() { + impl = new SampledQuantile(60 * 1000000L, TimeUnit.MICROSECONDS); + long now = System.currentTimeMillis(); + impl.addSample(7, now - 90 * 1000L); + impl.addSample(42, now); + assertEquals(42, impl.getPercentile(50, now + 1)); + } + + @Test + public void ignoresSamplesOutsideOfSpecifiedMillisecondWindow() { + impl = new SampledQuantile(60 * 1000L, TimeUnit.MILLISECONDS); + long now = System.currentTimeMillis(); + impl.addSample(7, now - 90 * 1000L); + impl.addSample(42, now); + assertEquals(42, impl.getPercentile(50, now + 1)); + } + + @Test + public void ignoresSamplesOutsideOfSpecifiedMinuteWindow() { + impl = new SampledQuantile(60L, TimeUnit.SECONDS); + long now = System.currentTimeMillis(); + impl.addSample(7, now - 90 * 1000L); + impl.addSample(42, now); + assertEquals(42, impl.getPercentile(50, now + 1)); + } + + @Test + public void ignoresSamplesOutsideOfSpecifiedHourWindow() { + impl = new SampledQuantile(3600L, TimeUnit.SECONDS); + long now = System.currentTimeMillis(); + impl.addSample(7, now - 5400 * 1000L); + impl.addSample(42, now); + assertEquals(42, impl.getPercentile(50, now + 1)); + } + + @Test + public void ignoresSamplesOutsideOfSpecifiedDayWindow() { + impl = new SampledQuantile(86400L, TimeUnit.SECONDS); + long now = System.currentTimeMillis(); + impl.addSample(7, now - 2 * 24 * 3600 * 1000L); + impl.addSample(42, now); + assertEquals(42, impl.getPercentile(50, now + 1)); + } + + @Test + public void windowedSamplingWorks() { + long t0 = System.currentTimeMillis(); + impl = new SampledQuantile(10, 60L, TimeUnit.SECONDS, t0); + for (int t = 0; t < 30 * 1000; t++) { + impl.addSample(1L, t0 + t); + } + long t1 = t0 + 30 * 1000L; + assertEquals(1L, impl.getPercentile(50, t1)); + + for (int t = 0; t < 60 * 1000; t++) { + impl.addSample(2L, t1 + t); + } + long t2 = t1 + 60 * 1000L; + assertEquals(2L, impl.getPercentile(50, t2)); + impl.addSample(3L, t2 + 1); + } + + @Test + public void windowedSamplingHandlesLongTimesBetweenSamples() { + long t0 = System.currentTimeMillis(); + impl = new SampledQuantile(10, 60L, TimeUnit.SECONDS, t0); + impl.addSample(1L, t0 + 1); + long t1 = t0 + 90 * 1000L; + impl.addSample(2L, t1); + assertEquals(2L, impl.getPercentile(50, t1)); + } } diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/TestServiceRetrier.java b/jrugged-core/src/test/java/org/fishwife/jrugged/TestServiceRetrier.java index 2d99cc37..e5f33549 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/TestServiceRetrier.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/TestServiceRetrier.java @@ -32,264 +32,263 @@ public class TestServiceRetrier { - private class DummyCallable implements Callable { + private class DummyCallable implements Callable { - private String _message; - private int _count = 0; - private int _failCount = 5; - private Exception _exception; + private String _message; + private int _count = 0; + private int _failCount = 5; + private Exception _exception; - public DummyCallable (String message, int failCount) { - this(message, failCount, new Exception("FAIL!")); - } + public DummyCallable(String message, int failCount) { + this(message, failCount, new Exception("FAIL!")); + } - public DummyCallable (String message, int failCount, Exception exception) { - _message = message; - _failCount = failCount; - _exception = exception; - } + public DummyCallable(String message, int failCount, Exception exception) { + _message = message; + _failCount = failCount; + _exception = exception; + } - public String call() throws Exception { + public String call() throws Exception { - _count++; - if (_count < _failCount) - throw _exception; + _count++; + if (_count < _failCount) + throw _exception; - return _message; - } + return _message; + } - public int getCount() { - return _count; - } - } + public int getCount() { + return _count; + } + } - private class DummyRunnable implements Runnable { + private class DummyRunnable implements Runnable { - private int _count = 0; - private int _failCount = 5; + private int _count = 0; + private int _failCount = 5; - public DummyRunnable (int failCount) { - _failCount = failCount; - } + public DummyRunnable(int failCount) { + _failCount = failCount; + } - public void run(){ + public void run() { - _count++; - if (_count < _failCount) - throw new RuntimeException("FAIL! " + _count); - } + _count++; + if (_count < _failCount) + throw new RuntimeException("FAIL! " + _count); + } - public int getCount() { - return _count; - } - } + public int getCount() { + return _count; + } + } - class CaptureSleepServiceRetrier extends ServiceRetrier { + class CaptureSleepServiceRetrier extends ServiceRetrier { - private int _sleepCallCount = 0; - private List _capturedSleepValues = new ArrayList(); + private int _sleepCallCount = 0; + private List _capturedSleepValues = new ArrayList(); - @Override - protected void sleep(long millis) { - _sleepCallCount++; - _capturedSleepValues.add(millis); - } + @Override + protected void sleep(long millis) { + _sleepCallCount++; + _capturedSleepValues.add(millis); + } - public int getSleepCallCount() { - return _sleepCallCount; - } + public int getSleepCallCount() { + return _sleepCallCount; + } - public List getCapturedSleepValues() { - return _capturedSleepValues; - } - } + public List getCapturedSleepValues() { + return _capturedSleepValues; + } + } - @Test - public void testInvokeSucceedsWithNoRetries() throws Exception { + @Test + public void testInvokeSucceedsWithNoRetries() throws Exception { - DummyCallable foo = new DummyCallable("Foo!", 5); + DummyCallable foo = new DummyCallable("Foo!", 5); - ServiceRetrier retrier = new ServiceRetrier(); - retrier.setDelay(100); - retrier.setMaxTries(5); - retrier.setDoubleDelay(false); + ServiceRetrier retrier = new ServiceRetrier(); + retrier.setDelay(100); + retrier.setMaxTries(5); + retrier.setDoubleDelay(false); - String result = retrier.invoke(foo); - assertEquals(5, foo.getCount()); - assertEquals("Foo!", result); - } + String result = retrier.invoke(foo); + assertEquals(5, foo.getCount()); + assertEquals("Foo!", result); + } - @Test - public void testInvokeSucceedsWithRetries() throws Exception { + @Test + public void testInvokeSucceedsWithRetries() throws Exception { - DummyCallable foo = new DummyCallable("Foo!", 5); + DummyCallable foo = new DummyCallable("Foo!", 5); - ServiceRetrier retrier = new ServiceRetrier(100, 5); - retrier.setDoubleDelay(false); + ServiceRetrier retrier = new ServiceRetrier(100, 5); + retrier.setDoubleDelay(false); - String result = retrier.invoke(foo); - assertEquals(5, foo.getCount()); - assertEquals("Foo!", result); - } + String result = retrier.invoke(foo); + assertEquals(5, foo.getCount()); + assertEquals("Foo!", result); + } - @Test - public void testInvokeSucceedsWithRetriesAndEmptyRetryOn() throws Exception { + @Test + public void testInvokeSucceedsWithRetriesAndEmptyRetryOn() throws Exception { - DummyCallable foo = new DummyCallable("Foo!", 5); + DummyCallable foo = new DummyCallable("Foo!", 5); - ServiceRetrier retrier = new ServiceRetrier(100, 5); + ServiceRetrier retrier = new ServiceRetrier(100, 5); - @SuppressWarnings("unchecked") - Class[] retryOn = new Class[0]; - retrier.setRetryOn(retryOn); + @SuppressWarnings("unchecked") + Class[] retryOn = new Class[0]; + retrier.setRetryOn(retryOn); - String result = retrier.invoke(foo); - assertEquals(5, foo.getCount()); - assertEquals("Foo!", result); - } + String result = retrier.invoke(foo); + assertEquals(5, foo.getCount()); + assertEquals("Foo!", result); + } - @Test - public void testInvokeSucceedsWithRetriesAndSpecificExceptionInRetryOn() throws Exception { + @Test + public void testInvokeSucceedsWithRetriesAndSpecificExceptionInRetryOn() throws Exception { - DummyCallable foo = new DummyCallable("Foo!", 5, new IOException()); + DummyCallable foo = new DummyCallable("Foo!", 5, new IOException()); - ServiceRetrier retrier = new ServiceRetrier(100, 5); + ServiceRetrier retrier = new ServiceRetrier(100, 5); - @SuppressWarnings("unchecked") - Class[] retryOn = new Class[] { IllegalArgumentException.class, IOException.class }; - retrier.setRetryOn(retryOn); + @SuppressWarnings("unchecked") + Class[] retryOn = new Class[] { IllegalArgumentException.class, IOException.class }; + retrier.setRetryOn(retryOn); - String result = retrier.invoke(foo); - assertEquals(5, foo.getCount()); - assertEquals("Foo!", result); - } + String result = retrier.invoke(foo); + assertEquals(5, foo.getCount()); + assertEquals("Foo!", result); + } + @Test + public void testConstructorWithNoArgs() { + ServiceRetrier serviceRetrier = new ServiceRetrier(); + Assert.assertEquals(1000, serviceRetrier.getDelay()); + Assert.assertEquals(10, serviceRetrier.getMaxTries()); + assertFalse(serviceRetrier.isDoubleDelay()); + assertFalse(serviceRetrier.isThrowCauseException()); + Assert.assertEquals(null, serviceRetrier.getRetryOn()); + } - @Test - public void testConstructorWithNoArgs() { - ServiceRetrier serviceRetrier = new ServiceRetrier(); - Assert.assertEquals(1000, serviceRetrier.getDelay()); - Assert.assertEquals(10, serviceRetrier.getMaxTries()); - assertFalse(serviceRetrier.isDoubleDelay()); - assertFalse(serviceRetrier.isThrowCauseException()); - Assert.assertEquals(null, serviceRetrier.getRetryOn()); - } - - @Test - public void testConstructorWithDelayAndMaxTries() throws Exception { - ServiceRetrier retrier = new ServiceRetrier(100, 5); - assertEquals(5, retrier.getMaxTries()); - assertEquals(100, retrier.getDelay()); - } - - @Test - public void testConfigurationWithAll() throws Exception { - @SuppressWarnings("unchecked") - Class[] retryOn = new Class[0]; - - ServiceRetrier retrier = new ServiceRetrier(100, 5, true, true, retryOn); - assertEquals(5, retrier.getMaxTries()); - assertEquals(100, retrier.getDelay()); - assertTrue(retrier.isDoubleDelay()); - assertTrue(retrier.isThrowCauseException()); - assertSame(retryOn, retrier.getRetryOn()); - } - - @Test - public void testInvokeSucceedRunnable() throws Exception { - - DummyRunnable foo = new DummyRunnable(5); - - ServiceRetrier retrier = new ServiceRetrier(); - retrier.setDelay(100); - retrier.setMaxTries(5); - retrier.setDoubleDelay(false); - - retrier.invoke(foo); - assertEquals(5, foo.getCount()); - } - - @Test - public void testInvokeSucceedRunnableResult() throws Exception { - - DummyRunnable foo = new DummyRunnable(5); - - ServiceRetrier retrier = new ServiceRetrier(); - retrier.setDelay(100); - retrier.setMaxTries(5); - retrier.setDoubleDelay(false); - - String result = "Foo!"; - retrier.invoke(foo, result); - assertEquals(5, foo.getCount()); - assertEquals("Foo!", result); - } - - @Test - public void testDoubleDelay() throws Exception { - - DummyCallable foo = new DummyCallable("Foo!", 5); - - CaptureSleepServiceRetrier retrier = new CaptureSleepServiceRetrier(); - retrier.setDelay(100); - retrier.setMaxTries(5); - retrier.setDoubleDelay(true); - - retrier.invoke(foo); - - assertTrue(retrier.isDoubleDelay()); - assertEquals(4, retrier.getSleepCallCount()); - assertEquals(100L, (long)retrier.getCapturedSleepValues().get(0)); - assertEquals(200L, (long)retrier.getCapturedSleepValues().get(1)); - assertEquals(400L, (long)retrier.getCapturedSleepValues().get(2)); - assertEquals(800L, (long)retrier.getCapturedSleepValues().get(3)); - } - - @Test - public void testInvokeFail() { - - DummyCallable foo = new DummyCallable("Foo!", 4); - - ServiceRetrier retrier = new ServiceRetrier(); - retrier.setDelay(100); - retrier.setMaxTries(3); - retrier.setDoubleDelay(false); - - try { - retrier.invoke(foo); - fail("Should have thrown exception"); - } catch (Exception ex) { - // pass - } - } - - @Test - public void testInvokeFailsWithUnexpectedException() throws Exception { - - DummyCallable foo = new DummyCallable("Foo!", 2); - ServiceRetrier retrier = new ServiceRetrier(100, 5); - - @SuppressWarnings("unchecked") - Class[] retryOn = new Class[] { IOException.class }; - retrier.setRetryOn(retryOn); - - try { - retrier.invoke(foo); - fail("Should have thrown exception"); - } catch (Exception ex) { - // pass - } - } - - @Test(expected=IllegalArgumentException.class) - public void testMaxTriesLessThanOneThrowsException() throws Exception { - ServiceRetrier retrier = new ServiceRetrier(); - retrier.setMaxTries(-1); - } - - @Test(expected=IllegalArgumentException.class) - public void testDelayLessThanZeroThrowsException() throws Exception { - ServiceRetrier retrier = new ServiceRetrier(); - retrier.setDelay(-1); - } + @Test + public void testConstructorWithDelayAndMaxTries() throws Exception { + ServiceRetrier retrier = new ServiceRetrier(100, 5); + assertEquals(5, retrier.getMaxTries()); + assertEquals(100, retrier.getDelay()); + } + + @Test + public void testConfigurationWithAll() throws Exception { + @SuppressWarnings("unchecked") + Class[] retryOn = new Class[0]; + + ServiceRetrier retrier = new ServiceRetrier(100, 5, true, true, retryOn); + assertEquals(5, retrier.getMaxTries()); + assertEquals(100, retrier.getDelay()); + assertTrue(retrier.isDoubleDelay()); + assertTrue(retrier.isThrowCauseException()); + assertSame(retryOn, retrier.getRetryOn()); + } + + @Test + public void testInvokeSucceedRunnable() throws Exception { + + DummyRunnable foo = new DummyRunnable(5); + + ServiceRetrier retrier = new ServiceRetrier(); + retrier.setDelay(100); + retrier.setMaxTries(5); + retrier.setDoubleDelay(false); + + retrier.invoke(foo); + assertEquals(5, foo.getCount()); + } + + @Test + public void testInvokeSucceedRunnableResult() throws Exception { + + DummyRunnable foo = new DummyRunnable(5); + + ServiceRetrier retrier = new ServiceRetrier(); + retrier.setDelay(100); + retrier.setMaxTries(5); + retrier.setDoubleDelay(false); + + String result = "Foo!"; + retrier.invoke(foo, result); + assertEquals(5, foo.getCount()); + assertEquals("Foo!", result); + } + + @Test + public void testDoubleDelay() throws Exception { + + DummyCallable foo = new DummyCallable("Foo!", 5); + + CaptureSleepServiceRetrier retrier = new CaptureSleepServiceRetrier(); + retrier.setDelay(100); + retrier.setMaxTries(5); + retrier.setDoubleDelay(true); + + retrier.invoke(foo); + + assertTrue(retrier.isDoubleDelay()); + assertEquals(4, retrier.getSleepCallCount()); + assertEquals(100L, (long) retrier.getCapturedSleepValues().get(0)); + assertEquals(200L, (long) retrier.getCapturedSleepValues().get(1)); + assertEquals(400L, (long) retrier.getCapturedSleepValues().get(2)); + assertEquals(800L, (long) retrier.getCapturedSleepValues().get(3)); + } + + @Test + public void testInvokeFail() { + + DummyCallable foo = new DummyCallable("Foo!", 4); + + ServiceRetrier retrier = new ServiceRetrier(); + retrier.setDelay(100); + retrier.setMaxTries(3); + retrier.setDoubleDelay(false); + + try { + retrier.invoke(foo); + fail("Should have thrown exception"); + } catch (Exception ex) { + // pass + } + } + + @Test + public void testInvokeFailsWithUnexpectedException() throws Exception { + + DummyCallable foo = new DummyCallable("Foo!", 2); + ServiceRetrier retrier = new ServiceRetrier(100, 5); + + @SuppressWarnings("unchecked") + Class[] retryOn = new Class[] { IOException.class }; + retrier.setRetryOn(retryOn); + + try { + retrier.invoke(foo); + fail("Should have thrown exception"); + } catch (Exception ex) { + // pass + } + } + + @Test(expected = IllegalArgumentException.class) + public void testMaxTriesLessThanOneThrowsException() throws Exception { + ServiceRetrier retrier = new ServiceRetrier(); + retrier.setMaxTries(-1); + } + + @Test(expected = IllegalArgumentException.class) + public void testDelayLessThanZeroThrowsException() throws Exception { + ServiceRetrier retrier = new ServiceRetrier(); + retrier.setDelay(-1); + } } diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/TestServiceStatus.java b/jrugged-core/src/test/java/org/fishwife/jrugged/TestServiceStatus.java index f1c63399..98aed565 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/TestServiceStatus.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/TestServiceStatus.java @@ -28,81 +28,81 @@ public class TestServiceStatus { - private ServiceStatus serviceStatus; - - @Test - public void testServiceStatus() { - - String name = "name"; - Status status = Status.UP; - - serviceStatus = new ServiceStatus(name, status); - - assertEquals(name, serviceStatus.getName()); - assertEquals(status, serviceStatus.getStatus()); - assertNotNull(serviceStatus.getReasons()); - assertTrue(serviceStatus.getReasons().isEmpty()); - } - - @Test - public void testServiceStatusWithNullReason() { - - String name = "name"; - Status status = Status.UP; - - serviceStatus = new ServiceStatus(name, status, (String)null); - - assertEquals(name, serviceStatus.getName()); - assertEquals(status, serviceStatus.getStatus()); - assertNotNull(serviceStatus.getReasons()); - assertTrue(serviceStatus.getReasons().isEmpty()); - } - - @Test - public void testServiceStatusWithNullReasons() { - - String name = "name"; - Status status = Status.UP; - - serviceStatus = new ServiceStatus(name, status, (List)null); - - assertEquals(name, serviceStatus.getName()); - assertEquals(status, serviceStatus.getStatus()); - assertNotNull(serviceStatus.getReasons()); - assertTrue(serviceStatus.getReasons().isEmpty()); - } - - @Test - public void testServiceStatusWithReason() { - - String name = "name"; - Status status = Status.UP; - String reason = "reason"; - - serviceStatus = new ServiceStatus(name, status, reason); - - assertEquals(name, serviceStatus.getName()); - assertEquals(status, serviceStatus.getStatus()); - assertNotNull(serviceStatus.getReasons()); - assertTrue(serviceStatus.getReasons().contains(reason)); - } - - @Test - public void testServiceStatusWithReasons() { - String name = "name"; - Status status = Status.UP; - String reason1 = "reason1"; - String reason2 = "reason2"; - List reasonList = new ArrayList(); - reasonList.add(reason1); - reasonList.add(reason2); - - serviceStatus = new ServiceStatus(name, status, reasonList); - assertEquals(name, serviceStatus.getName()); - assertEquals(status, serviceStatus.getStatus()); - assertNotNull(serviceStatus.getReasons()); - assertTrue(serviceStatus.getReasons().contains(reason1)); - assertTrue(serviceStatus.getReasons().contains(reason2)); - assertNotSame(serviceStatus.getReasons(), reasonList); - } + private ServiceStatus serviceStatus; + + @Test + public void testServiceStatus() { + + String name = "name"; + Status status = Status.UP; + + serviceStatus = new ServiceStatus(name, status); + + assertEquals(name, serviceStatus.getName()); + assertEquals(status, serviceStatus.getStatus()); + assertNotNull(serviceStatus.getReasons()); + assertTrue(serviceStatus.getReasons().isEmpty()); + } + + @Test + public void testServiceStatusWithNullReason() { + + String name = "name"; + Status status = Status.UP; + + serviceStatus = new ServiceStatus(name, status, (String) null); + + assertEquals(name, serviceStatus.getName()); + assertEquals(status, serviceStatus.getStatus()); + assertNotNull(serviceStatus.getReasons()); + assertTrue(serviceStatus.getReasons().isEmpty()); + } + + @Test + public void testServiceStatusWithNullReasons() { + + String name = "name"; + Status status = Status.UP; + + serviceStatus = new ServiceStatus(name, status, (List) null); + + assertEquals(name, serviceStatus.getName()); + assertEquals(status, serviceStatus.getStatus()); + assertNotNull(serviceStatus.getReasons()); + assertTrue(serviceStatus.getReasons().isEmpty()); + } + + @Test + public void testServiceStatusWithReason() { + + String name = "name"; + Status status = Status.UP; + String reason = "reason"; + + serviceStatus = new ServiceStatus(name, status, reason); + + assertEquals(name, serviceStatus.getName()); + assertEquals(status, serviceStatus.getStatus()); + assertNotNull(serviceStatus.getReasons()); + assertTrue(serviceStatus.getReasons().contains(reason)); + } + + @Test + public void testServiceStatusWithReasons() { + String name = "name"; + Status status = Status.UP; + String reason1 = "reason1"; + String reason2 = "reason2"; + List reasonList = new ArrayList(); + reasonList.add(reason1); + reasonList.add(reason2); + + serviceStatus = new ServiceStatus(name, status, reasonList); + assertEquals(name, serviceStatus.getName()); + assertEquals(status, serviceStatus.getStatus()); + assertNotNull(serviceStatus.getReasons()); + assertTrue(serviceStatus.getReasons().contains(reason1)); + assertTrue(serviceStatus.getReasons().contains(reason2)); + assertNotSame(serviceStatus.getReasons(), reasonList); + } } diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/TestServiceWrapperChain.java b/jrugged-core/src/test/java/org/fishwife/jrugged/TestServiceWrapperChain.java index 31e775a1..d7b4eb46 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/TestServiceWrapperChain.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/TestServiceWrapperChain.java @@ -9,247 +9,247 @@ import org.junit.Test; - public class TestServiceWrapperChain { - private ServiceWrapperChain impl; - - @Test - public void callableJustGetsInvokedWithNoWrappers() throws Exception { - impl = new ServiceWrapperChain(new ArrayList()); - final Object out = new Object(); - Callable c = new Callable() { - public Object call() throws Exception { - return out; - } - }; - assertSame(out, impl.invoke(c)); - } - - @Test - public void runnableJustGetsRunWithNoWrappers() throws Exception { - Runnable r = createMock(Runnable.class); - impl = new ServiceWrapperChain(new ArrayList()); - r.run(); - replay(r); - impl.invoke(r); - verify(r); - } - - @Test - public void runnableWithResultJustGetsRunWithNoWrappers() throws Exception { - Runnable r = createMock(Runnable.class); - impl = new ServiceWrapperChain(new ArrayList()); - final Object out = new Object(); - r.run(); - replay(r); - Object result = impl.invoke(r, out); - verify(r); - assertSame(out, result); - } - - @Test - public void callableWithOneWrapperRunsThroughWrapper() throws Exception { - final Object out = new Object(); - Callable c = new Callable() { - public Object call() throws Exception { - return null; - } - }; - ServiceWrapper wrapper = createMock(ServiceWrapper.class); - impl = new ServiceWrapperChain(Arrays.asList(wrapper)); - - expect(wrapper.invoke(c)).andReturn(out); - replay(wrapper); - Object result = impl.invoke(c); - verify(wrapper); - assertSame(out, result); - } - - @Test - public void runnableWithOneWrapperRunsThroughWrapper() throws Exception { - Runnable r = createMock(Runnable.class); - ServiceWrapper wrapper = createMock(ServiceWrapper.class); - impl = new ServiceWrapperChain(Arrays.asList(wrapper)); - - wrapper.invoke(r); - replay(wrapper); - replay(r); - impl.invoke(r); - verify(wrapper); - verify(r); - } - - @Test - public void runnableWithReturnWithOneWrapperRunsThroughWrapper() throws Exception { - final Object out = new Object(); - Runnable r = createMock(Runnable.class); - ServiceWrapper wrapper = createMock(ServiceWrapper.class); - impl = new ServiceWrapperChain(Arrays.asList(wrapper)); - - wrapper.invoke(r); - replay(wrapper); - replay(r); - Object result = impl.invoke(r, out); - verify(wrapper); - verify(r); - assertSame(out, result); - } - - @Test - public void callableWithTwoWrappersRunsThroughFirstWrapperFirst() throws Exception { - final Object out1 = new Object(); - final Object out2 = new Object(); - Callable c = new NullCallable(); - ServiceWrapper wrapper1 = new NullWrapper() { - @SuppressWarnings("unchecked") - @Override - public T invoke(Callable c) throws Exception { - return (T)out1; - } - }; - ServiceWrapper wrapper2 = new NullWrapper() { - @SuppressWarnings("unchecked") - @Override - public T invoke(Callable c) throws Exception { - return (T)out2; - } - }; - impl = new ServiceWrapperChain(Arrays.asList(wrapper1, wrapper2)); - assertSame(out1, impl.invoke(c)); - } - - @Test - public void runnableWithTwoWrappersRunsThroughFirstWrapperFirst() throws Exception { - Runnable r = new NullRunnable(); - final Flag f1 = new Flag(); - final Flag f2 = new Flag(); - ServiceWrapper wrapper1 = new NullWrapper() { - @Override - public void invoke(Runnable r) throws Exception { - f1.set = true; - } - }; - ServiceWrapper wrapper2 = new NullWrapper() { - @Override - public void invoke(Runnable r) throws Exception { - f2.set = false; - } - }; - impl = new ServiceWrapperChain(Arrays.asList(wrapper1, wrapper2)); - impl.invoke(r); - assertTrue(f1.set); - assertFalse(f2.set); - } - - @Test - public void runnableWithReturnWithTwoWrappersRunsThroughFirstWrapperFirst() throws Exception { - final Object out = new Object(); - Runnable r = new NullRunnable(); - final Flag f1 = new Flag(); - final Flag f2 = new Flag(); - ServiceWrapper wrapper1 = new NullWrapper() { - @Override - public void invoke(Runnable r) throws Exception { - f1.set = true; - } - }; - ServiceWrapper wrapper2 = new NullWrapper() { - @Override - public void invoke(Runnable r) throws Exception { - f2.set = false; - } - }; - impl = new ServiceWrapperChain(Arrays.asList(wrapper1, wrapper2)); - Object result = impl.invoke(r, out); - assertTrue(f1.set); - assertFalse(f2.set); - assertSame(out, result); - } - - @Test - public void callableWithTwoNullWrappersThreadsProperly() throws Exception { - final Object out = new Object(); - Callable c = new Callable() { - public Object call() throws Exception { - return out; - } - }; - NullWrapper wrapper1 = new NullWrapper(); - NullWrapper wrapper2 = new NullWrapper(); - impl = new ServiceWrapperChain(Arrays.asList((ServiceWrapper)wrapper1, (ServiceWrapper)wrapper2)); - Object result = impl.invoke(c); - assertTrue(wrapper1.invokedCallable); - assertTrue(wrapper2.invokedCallable); - assertSame(out, result); - } - - @Test - public void runnableWithTwoNullWrappersThreadsProperly() throws Exception { - final Flag f = new Flag(); - Runnable r = new Runnable() { - public void run() { - f.set = true; - } - }; - NullWrapper wrapper1 = new NullWrapper(); - NullWrapper wrapper2 = new NullWrapper(); - impl = new ServiceWrapperChain(Arrays.asList((ServiceWrapper)wrapper1, (ServiceWrapper)wrapper2)); - impl.invoke(r); - assertTrue(wrapper1.invokedRunnable); - assertTrue(wrapper2.invokedRunnable); - assertTrue(f.set); - } - - @Test - public void runnableWithReturnWithTwoNullWrappersThreadsProperly() throws Exception { - final Object out = new Object(); - final Flag f = new Flag(); - Runnable r = new Runnable() { - public void run() { - f.set = true; - } - }; - NullWrapper wrapper1 = new NullWrapper(); - NullWrapper wrapper2 = new NullWrapper(); - impl = new ServiceWrapperChain(Arrays.asList((ServiceWrapper)wrapper1, (ServiceWrapper)wrapper2)); - Object result = impl.invoke(r, out); - assertTrue(wrapper1.invokedRunnable); - assertTrue(wrapper2.invokedRunnable); - assertTrue(f.set); - assertSame(out, result); - } - - private static class Flag { - public boolean set = false; - } - - private static class NullRunnable implements Runnable { - public void run() { } - } - - private static class NullCallable implements Callable { - public Object call() throws Exception { - return null; - } - } - - private static class NullWrapper implements ServiceWrapper { - public boolean invokedCallable; - public boolean invokedRunnable; - - public T invoke(Callable c) throws Exception { - invokedCallable = true; - return c.call(); - } - - public void invoke(Runnable r) throws Exception { - invokedRunnable = true; - r.run(); - } - - public T invoke(Runnable r, T result) throws Exception { - return null; - } - } + private ServiceWrapperChain impl; + + @Test + public void callableJustGetsInvokedWithNoWrappers() throws Exception { + impl = new ServiceWrapperChain(new ArrayList()); + final Object out = new Object(); + Callable c = new Callable() { + public Object call() throws Exception { + return out; + } + }; + assertSame(out, impl.invoke(c)); + } + + @Test + public void runnableJustGetsRunWithNoWrappers() throws Exception { + Runnable r = createMock(Runnable.class); + impl = new ServiceWrapperChain(new ArrayList()); + r.run(); + replay(r); + impl.invoke(r); + verify(r); + } + + @Test + public void runnableWithResultJustGetsRunWithNoWrappers() throws Exception { + Runnable r = createMock(Runnable.class); + impl = new ServiceWrapperChain(new ArrayList()); + final Object out = new Object(); + r.run(); + replay(r); + Object result = impl.invoke(r, out); + verify(r); + assertSame(out, result); + } + + @Test + public void callableWithOneWrapperRunsThroughWrapper() throws Exception { + final Object out = new Object(); + Callable c = new Callable() { + public Object call() throws Exception { + return null; + } + }; + ServiceWrapper wrapper = createMock(ServiceWrapper.class); + impl = new ServiceWrapperChain(Arrays.asList(wrapper)); + + expect(wrapper.invoke(c)).andReturn(out); + replay(wrapper); + Object result = impl.invoke(c); + verify(wrapper); + assertSame(out, result); + } + + @Test + public void runnableWithOneWrapperRunsThroughWrapper() throws Exception { + Runnable r = createMock(Runnable.class); + ServiceWrapper wrapper = createMock(ServiceWrapper.class); + impl = new ServiceWrapperChain(Arrays.asList(wrapper)); + + wrapper.invoke(r); + replay(wrapper); + replay(r); + impl.invoke(r); + verify(wrapper); + verify(r); + } + + @Test + public void runnableWithReturnWithOneWrapperRunsThroughWrapper() throws Exception { + final Object out = new Object(); + Runnable r = createMock(Runnable.class); + ServiceWrapper wrapper = createMock(ServiceWrapper.class); + impl = new ServiceWrapperChain(Arrays.asList(wrapper)); + + wrapper.invoke(r); + replay(wrapper); + replay(r); + Object result = impl.invoke(r, out); + verify(wrapper); + verify(r); + assertSame(out, result); + } + + @Test + public void callableWithTwoWrappersRunsThroughFirstWrapperFirst() throws Exception { + final Object out1 = new Object(); + final Object out2 = new Object(); + Callable c = new NullCallable(); + ServiceWrapper wrapper1 = new NullWrapper() { + @SuppressWarnings("unchecked") + @Override + public T invoke(Callable c) throws Exception { + return (T) out1; + } + }; + ServiceWrapper wrapper2 = new NullWrapper() { + @SuppressWarnings("unchecked") + @Override + public T invoke(Callable c) throws Exception { + return (T) out2; + } + }; + impl = new ServiceWrapperChain(Arrays.asList(wrapper1, wrapper2)); + assertSame(out1, impl.invoke(c)); + } + + @Test + public void runnableWithTwoWrappersRunsThroughFirstWrapperFirst() throws Exception { + Runnable r = new NullRunnable(); + final Flag f1 = new Flag(); + final Flag f2 = new Flag(); + ServiceWrapper wrapper1 = new NullWrapper() { + @Override + public void invoke(Runnable r) throws Exception { + f1.set = true; + } + }; + ServiceWrapper wrapper2 = new NullWrapper() { + @Override + public void invoke(Runnable r) throws Exception { + f2.set = false; + } + }; + impl = new ServiceWrapperChain(Arrays.asList(wrapper1, wrapper2)); + impl.invoke(r); + assertTrue(f1.set); + assertFalse(f2.set); + } + + @Test + public void runnableWithReturnWithTwoWrappersRunsThroughFirstWrapperFirst() throws Exception { + final Object out = new Object(); + Runnable r = new NullRunnable(); + final Flag f1 = new Flag(); + final Flag f2 = new Flag(); + ServiceWrapper wrapper1 = new NullWrapper() { + @Override + public void invoke(Runnable r) throws Exception { + f1.set = true; + } + }; + ServiceWrapper wrapper2 = new NullWrapper() { + @Override + public void invoke(Runnable r) throws Exception { + f2.set = false; + } + }; + impl = new ServiceWrapperChain(Arrays.asList(wrapper1, wrapper2)); + Object result = impl.invoke(r, out); + assertTrue(f1.set); + assertFalse(f2.set); + assertSame(out, result); + } + + @Test + public void callableWithTwoNullWrappersThreadsProperly() throws Exception { + final Object out = new Object(); + Callable c = new Callable() { + public Object call() throws Exception { + return out; + } + }; + NullWrapper wrapper1 = new NullWrapper(); + NullWrapper wrapper2 = new NullWrapper(); + impl = new ServiceWrapperChain(Arrays.asList((ServiceWrapper) wrapper1, (ServiceWrapper) wrapper2)); + Object result = impl.invoke(c); + assertTrue(wrapper1.invokedCallable); + assertTrue(wrapper2.invokedCallable); + assertSame(out, result); + } + + @Test + public void runnableWithTwoNullWrappersThreadsProperly() throws Exception { + final Flag f = new Flag(); + Runnable r = new Runnable() { + public void run() { + f.set = true; + } + }; + NullWrapper wrapper1 = new NullWrapper(); + NullWrapper wrapper2 = new NullWrapper(); + impl = new ServiceWrapperChain(Arrays.asList((ServiceWrapper) wrapper1, (ServiceWrapper) wrapper2)); + impl.invoke(r); + assertTrue(wrapper1.invokedRunnable); + assertTrue(wrapper2.invokedRunnable); + assertTrue(f.set); + } + + @Test + public void runnableWithReturnWithTwoNullWrappersThreadsProperly() throws Exception { + final Object out = new Object(); + final Flag f = new Flag(); + Runnable r = new Runnable() { + public void run() { + f.set = true; + } + }; + NullWrapper wrapper1 = new NullWrapper(); + NullWrapper wrapper2 = new NullWrapper(); + impl = new ServiceWrapperChain(Arrays.asList((ServiceWrapper) wrapper1, (ServiceWrapper) wrapper2)); + Object result = impl.invoke(r, out); + assertTrue(wrapper1.invokedRunnable); + assertTrue(wrapper2.invokedRunnable); + assertTrue(f.set); + assertSame(out, result); + } + + private static class Flag { + public boolean set = false; + } + + private static class NullRunnable implements Runnable { + public void run() { + } + } + + private static class NullCallable implements Callable { + public Object call() throws Exception { + return null; + } + } + + private static class NullWrapper implements ServiceWrapper { + public boolean invokedCallable; + public boolean invokedRunnable; + + public T invoke(Callable c) throws Exception { + invokedCallable = true; + return c.call(); + } + + public void invoke(Runnable r) throws Exception { + invokedRunnable = true; + r.run(); + } + + public T invoke(Runnable r, T result) throws Exception { + return null; + } + } } diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/TestWindowedEventCounter.java b/jrugged-core/src/test/java/org/fishwife/jrugged/TestWindowedEventCounter.java index 3daacd3b..c9e3c8a8 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/TestWindowedEventCounter.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/TestWindowedEventCounter.java @@ -25,181 +25,180 @@ public final class TestWindowedEventCounter { - private WindowedEventCounter impl; - - private StoppedClock clock = new StoppedClock(); - - private static int CAPACITY = 3; - - private static long WINDOW_MILLIS = 5L; - - @Before - public void setUp() { - impl = new WindowedEventCounter(CAPACITY, WINDOW_MILLIS); - clock.currentTimeMillis = System.currentTimeMillis(); - impl.setClock(clock); - - // several tests depend on this assumption - assertTrue(WINDOW_MILLIS > CAPACITY); - } - - // constructor tests - @Test - public void testConstructor() { - assertEquals(CAPACITY, impl.getCapacity()); - assertEquals(WINDOW_MILLIS, impl.getWindowMillis()); - } - - @Test - public void testConstructorThrowsExceptionOnBadCapacity() { - try { - new WindowedEventCounter(0, WINDOW_MILLIS); - fail("constructor should have thrown IllegalArgumentException"); - } catch (IllegalArgumentException iae) { - // this is expected. ignore and let test pass. - } - } - - @Test - public void testConstructorThrowsExceptionOnBadWindowMillis() { - try { - new WindowedEventCounter(CAPACITY, 0); - fail("constructor should have thrown IllegalArgumentException"); - } catch (IllegalArgumentException iae) { - // this is expected. ignore and let test pass. - } - } - - @Test - public void testStartsEmpty() { - assertEquals(0, impl.tally()); - } - - @Test - public void testCountsToCapacity() { - for (int i = 1; i <= CAPACITY; i++) { - impl.mark(); - clock.currentTimeMillis = clock.currentTimeMillis + 1; - assertEquals(i, impl.tally()); - } - } - - @Test - public void testCountsToCapacityOnOverflow() { - for (int i = 0; i < (CAPACITY * 2); i++) { - clock.currentTimeMillis = clock.currentTimeMillis + 1; - impl.mark(); - } - assertEquals(CAPACITY, impl.tally()); - } - - @Test - public void testRollingExpiration() { - // empty at t0 - long t0 = clock.currentTimeMillis; - - /* - * fill 'er up, marking once per milli (first event is at t1, second - * event at t2...) - */ - for (int i = 1; i <= CAPACITY; i++) { - clock.currentTimeMillis = t0 + i; - impl.mark(); - } - - /* - * represents that last time that all the events should still be - * in-window. - */ - clock.currentTimeMillis = t0 + 1 + WINDOW_MILLIS; - int expectedCount = CAPACITY; - assertEquals(CAPACITY, impl.tally()); - - // the tally count should drain at a rate one per milli now - for (int j = 1; j <= CAPACITY; j++) { - clock.currentTimeMillis++; - expectedCount--; - assertEquals(expectedCount, impl.tally()); - } - - clock.currentTimeMillis++; - // we should be empty now - assertEquals(0, impl.tally()); - } - - @Test - public void testReducingWindowDecreasesTally() throws Exception { - // empty at t0 - long t0 = clock.currentTimeMillis; - - /* - * fill 'er up, marking once per milli (first event is at t1, second - * event at t2...) - */ - for (int i = 1; i <= CAPACITY; i++) { - clock.currentTimeMillis = t0 + i; - impl.mark(); - } - - // Move time to 1 MS past the last entry. - clock.currentTimeMillis = clock.currentTimeMillis + 1; - - impl.setWindowMillis(1); - assertEquals(1, impl.tally()); - } - - @Test - public void testReducingCapacityDecreasesTally() { - // fill up to capacity - for (int i = 1; i <= CAPACITY; i++) { - impl.mark(); - } - assertEquals(CAPACITY, impl.tally()); - - // reduce capacity - impl.setCapacity(CAPACITY - 1); - assertEquals(CAPACITY - 1, impl.tally()); - } - - @Test - public void testIncreasingCapacity() { - // fill up to capacity - for (int i = 1; i <= CAPACITY; i++) { - impl.mark(); - } - assertEquals(CAPACITY, impl.tally()); - - impl.setCapacity(CAPACITY + 1); - - assertEquals(CAPACITY, impl.tally()); - } - - @Test - public void testSettingBadCapacityThrowsException() { - try { - impl.setCapacity(0); - fail("should have thrown IllegalArgumentException"); - } catch (IllegalArgumentException iae) { - // this is expected. ignore and let test pass. - } - } - - @Test - public void testSettingBadWindowMillisThrowsException() { - try { - impl.setWindowMillis(0); - fail("should have thrown IllegalArgumentException"); - } catch (IllegalArgumentException iae) { - // this is expected. ignore and let test pass. - } - } - - public class StoppedClock implements Clock { - public long currentTimeMillis; - - public long currentTimeMillis() { - return currentTimeMillis; - } - } + private WindowedEventCounter impl; + + private StoppedClock clock = new StoppedClock(); + + private static int CAPACITY = 3; + + private static long WINDOW_MILLIS = 5L; + + @Before + public void setUp() { + impl = new WindowedEventCounter(CAPACITY, WINDOW_MILLIS); + clock.currentTimeMillis = System.currentTimeMillis(); + impl.setClock(clock); + + // several tests depend on this assumption + assertTrue(WINDOW_MILLIS > CAPACITY); + } + + // constructor tests + @Test + public void testConstructor() { + assertEquals(CAPACITY, impl.getCapacity()); + assertEquals(WINDOW_MILLIS, impl.getWindowMillis()); + } + + @Test + public void testConstructorThrowsExceptionOnBadCapacity() { + try { + new WindowedEventCounter(0, WINDOW_MILLIS); + fail("constructor should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // this is expected. ignore and let test pass. + } + } + + @Test + public void testConstructorThrowsExceptionOnBadWindowMillis() { + try { + new WindowedEventCounter(CAPACITY, 0); + fail("constructor should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // this is expected. ignore and let test pass. + } + } + + @Test + public void testStartsEmpty() { + assertEquals(0, impl.tally()); + } + + @Test + public void testCountsToCapacity() { + for (int i = 1; i <= CAPACITY; i++) { + impl.mark(); + clock.currentTimeMillis = clock.currentTimeMillis + 1; + assertEquals(i, impl.tally()); + } + } + + @Test + public void testCountsToCapacityOnOverflow() { + for (int i = 0; i < (CAPACITY * 2); i++) { + clock.currentTimeMillis = clock.currentTimeMillis + 1; + impl.mark(); + } + assertEquals(CAPACITY, impl.tally()); + } + + @Test + public void testRollingExpiration() { + // empty at t0 + long t0 = clock.currentTimeMillis; + + /* + * fill 'er up, marking once per milli (first event is at t1, second event at + * t2...) + */ + for (int i = 1; i <= CAPACITY; i++) { + clock.currentTimeMillis = t0 + i; + impl.mark(); + } + + /* + * represents that last time that all the events should still be in-window. + */ + clock.currentTimeMillis = t0 + 1 + WINDOW_MILLIS; + int expectedCount = CAPACITY; + assertEquals(CAPACITY, impl.tally()); + + // the tally count should drain at a rate one per milli now + for (int j = 1; j <= CAPACITY; j++) { + clock.currentTimeMillis++; + expectedCount--; + assertEquals(expectedCount, impl.tally()); + } + + clock.currentTimeMillis++; + // we should be empty now + assertEquals(0, impl.tally()); + } + + @Test + public void testReducingWindowDecreasesTally() throws Exception { + // empty at t0 + long t0 = clock.currentTimeMillis; + + /* + * fill 'er up, marking once per milli (first event is at t1, second event at + * t2...) + */ + for (int i = 1; i <= CAPACITY; i++) { + clock.currentTimeMillis = t0 + i; + impl.mark(); + } + + // Move time to 1 MS past the last entry. + clock.currentTimeMillis = clock.currentTimeMillis + 1; + + impl.setWindowMillis(1); + assertEquals(1, impl.tally()); + } + + @Test + public void testReducingCapacityDecreasesTally() { + // fill up to capacity + for (int i = 1; i <= CAPACITY; i++) { + impl.mark(); + } + assertEquals(CAPACITY, impl.tally()); + + // reduce capacity + impl.setCapacity(CAPACITY - 1); + assertEquals(CAPACITY - 1, impl.tally()); + } + + @Test + public void testIncreasingCapacity() { + // fill up to capacity + for (int i = 1; i <= CAPACITY; i++) { + impl.mark(); + } + assertEquals(CAPACITY, impl.tally()); + + impl.setCapacity(CAPACITY + 1); + + assertEquals(CAPACITY, impl.tally()); + } + + @Test + public void testSettingBadCapacityThrowsException() { + try { + impl.setCapacity(0); + fail("should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // this is expected. ignore and let test pass. + } + } + + @Test + public void testSettingBadWindowMillisThrowsException() { + try { + impl.setWindowMillis(0); + fail("should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // this is expected. ignore and let test pass. + } + } + + public class StoppedClock implements Clock { + public long currentTimeMillis; + + public long currentTimeMillis() { + return currentTimeMillis; + } + } } \ No newline at end of file diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/clocks/TestDefaultHardwareClock.java b/jrugged-core/src/test/java/org/fishwife/jrugged/clocks/TestDefaultHardwareClock.java index 7b8939f3..2d1281f2 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/clocks/TestDefaultHardwareClock.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/clocks/TestDefaultHardwareClock.java @@ -25,100 +25,103 @@ import org.junit.Test; import org.fishwife.jrugged.interval.DiscreteInterval; - public class TestDefaultHardwareClock { - private DefaultHardwareClock.Env mockEnv; - private DefaultHardwareClock impl; - private Random random; - - @Before - public void setUp() { - random = new Random(); - mockEnv = createMock(DefaultHardwareClock.Env.class); - impl = new DefaultHardwareClock(mockEnv); - } - - @Test - public void normalizesToPositiveReadings() { - expect(mockEnv.nanoTime()).andReturn(-100L); - expect(mockEnv.nanoTime()).andReturn(-90L).anyTimes(); - expect(mockEnv.currentTimeMillis()).andReturn(0L).anyTimes(); - replay(mockEnv); - DiscreteInterval out = impl.getNanoTime(); - verify(mockEnv); - assertTrue(out.getMin() >= 0); - assertTrue(out.getMax() >= 0); - } - - @Test - public void elapsedTimeWorksIfNoElapsedTime() { - assertEquals(0L, impl.elapsedTime(1L, 1L)); - } - - @Test - public void elapsedTimeWorksWhenNoOverflow() { - assertEquals(3L, impl.elapsedTime(4L,7L)); - } - - @Test - public void elapsedTimeWorksWhenOverflowed() { - long start = Long.MAX_VALUE - 3L; - long end = start + 10L; - assertEquals(10L, impl.elapsedTime(start, end)); - } - - @Test - public void canSampleImmediatelyIncrementingClock() { - expect(mockEnv.nanoTime()).andReturn(4L); - expect(mockEnv.nanoTime()).andReturn(5L); - replay(mockEnv); - assertEquals(1L, impl.sampleGranularity()); - verify(mockEnv); - } - - @Test - public void samplesClockUntilItTicks() { - int i = random.nextInt(10) + 2; - expect(mockEnv.nanoTime()).andReturn(4L).times(i); - expect(mockEnv.nanoTime()).andReturn(10L); - replay(mockEnv); - assertEquals(6L, impl.sampleGranularity()); - verify(mockEnv); - } - - @Test - public void canRetrieveClockReadingInterval() { - impl = new DefaultHardwareClock(); - assertTrue(impl.getNanoTime() instanceof DiscreteInterval); - } - - @Test - public void errorIsHalfOfGranularityForEvenGranularity() { - impl = new DefaultHardwareClock(mockEnv) { - public long getGranularity() { return 6L; } - }; - expect(mockEnv.nanoTime()).andReturn(10L).anyTimes(); - replay(mockEnv); - assertEquals(7L, impl.getNanoTime().size()); - verify(mockEnv); - } - - @Test - public void errorRoundsProperlyForOddGranularity() { - impl = new DefaultHardwareClock(mockEnv) { - public long getGranularity() { return 5L; } - }; - expect(mockEnv.nanoTime()).andReturn(10L).anyTimes(); - replay(mockEnv); - assertEquals(7L, impl.getNanoTime().size()); - verify(mockEnv); - } - - @Test - public void canTakeMultipleReadings() { - impl = new DefaultHardwareClock(); - assertNotNull(impl.getNanoTime()); - assertNotNull(impl.getNanoTime()); - } + private DefaultHardwareClock.Env mockEnv; + private DefaultHardwareClock impl; + private Random random; + + @Before + public void setUp() { + random = new Random(); + mockEnv = createMock(DefaultHardwareClock.Env.class); + impl = new DefaultHardwareClock(mockEnv); + } + + @Test + public void normalizesToPositiveReadings() { + expect(mockEnv.nanoTime()).andReturn(-100L); + expect(mockEnv.nanoTime()).andReturn(-90L).anyTimes(); + expect(mockEnv.currentTimeMillis()).andReturn(0L).anyTimes(); + replay(mockEnv); + DiscreteInterval out = impl.getNanoTime(); + verify(mockEnv); + assertTrue(out.getMin() >= 0); + assertTrue(out.getMax() >= 0); + } + + @Test + public void elapsedTimeWorksIfNoElapsedTime() { + assertEquals(0L, impl.elapsedTime(1L, 1L)); + } + + @Test + public void elapsedTimeWorksWhenNoOverflow() { + assertEquals(3L, impl.elapsedTime(4L, 7L)); + } + + @Test + public void elapsedTimeWorksWhenOverflowed() { + long start = Long.MAX_VALUE - 3L; + long end = start + 10L; + assertEquals(10L, impl.elapsedTime(start, end)); + } + + @Test + public void canSampleImmediatelyIncrementingClock() { + expect(mockEnv.nanoTime()).andReturn(4L); + expect(mockEnv.nanoTime()).andReturn(5L); + replay(mockEnv); + assertEquals(1L, impl.sampleGranularity()); + verify(mockEnv); + } + + @Test + public void samplesClockUntilItTicks() { + int i = random.nextInt(10) + 2; + expect(mockEnv.nanoTime()).andReturn(4L).times(i); + expect(mockEnv.nanoTime()).andReturn(10L); + replay(mockEnv); + assertEquals(6L, impl.sampleGranularity()); + verify(mockEnv); + } + + @Test + public void canRetrieveClockReadingInterval() { + impl = new DefaultHardwareClock(); + assertTrue(impl.getNanoTime() instanceof DiscreteInterval); + } + + @Test + public void errorIsHalfOfGranularityForEvenGranularity() { + impl = new DefaultHardwareClock(mockEnv) { + public long getGranularity() { + return 6L; + } + }; + expect(mockEnv.nanoTime()).andReturn(10L).anyTimes(); + replay(mockEnv); + assertEquals(7L, impl.getNanoTime().size()); + verify(mockEnv); + } + + @Test + public void errorRoundsProperlyForOddGranularity() { + impl = new DefaultHardwareClock(mockEnv) { + public long getGranularity() { + return 5L; + } + }; + expect(mockEnv.nanoTime()).andReturn(10L).anyTimes(); + replay(mockEnv); + assertEquals(7L, impl.getNanoTime().size()); + verify(mockEnv); + } + + @Test + public void canTakeMultipleReadings() { + impl = new DefaultHardwareClock(); + assertNotNull(impl.getNanoTime()); + assertNotNull(impl.getNanoTime()); + } } diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/clocks/TestTimer.java b/jrugged-core/src/test/java/org/fishwife/jrugged/clocks/TestTimer.java index 824ff728..6959ad5a 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/clocks/TestTimer.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/clocks/TestTimer.java @@ -7,262 +7,260 @@ import org.junit.Before; import org.junit.Test; - public class TestTimer { - private HardwareClock mockClock; - private Timer impl; - - @Before - public void setUp() { - mockClock = createMock(HardwareClock.class); - impl = new Timer(mockClock); - } - - @Test - public void createTimer() { - new Timer(); - } - - @Test - public void provideAlternativeHardwareClock() { - new Timer(mockClock); - } - - @Test - public void canSetTimer() { - impl.set(40000L,3000L); - } - - @Test(expected=IllegalStateException.class) - public void cannotStartTimerThanHasNotBeenSet() { - expect(mockClock.getGranularity()).andReturn(1000L).anyTimes(); - replay(mockClock); - impl.start(); - verify(mockClock); - } - - @Test - public void canStartATimerThatHasBeenSet() { - expect(mockClock.getGranularity()).andReturn(1000L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(3L,4L)).anyTimes(); - replay(mockClock); - impl.set(40000L,3000L); - impl.start(); - verify(mockClock); - } - - @Test - public void cannotSetATimerWithASmallerErrorThanClockGranularity() { - expect(mockClock.getGranularity()).andReturn(1000L).anyTimes(); - replay(mockClock); - assertFalse(impl.set(1000L, 500L)); - verify(mockClock); - } + private HardwareClock mockClock; + private Timer impl; + + @Before + public void setUp() { + mockClock = createMock(HardwareClock.class); + impl = new Timer(mockClock); + } + + @Test + public void createTimer() { + new Timer(); + } + + @Test + public void provideAlternativeHardwareClock() { + new Timer(mockClock); + } + + @Test + public void canSetTimer() { + impl.set(40000L, 3000L); + } + + @Test(expected = IllegalStateException.class) + public void cannotStartTimerThanHasNotBeenSet() { + expect(mockClock.getGranularity()).andReturn(1000L).anyTimes(); + replay(mockClock); + impl.start(); + verify(mockClock); + } + + @Test + public void canStartATimerThatHasBeenSet() { + expect(mockClock.getGranularity()).andReturn(1000L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(3L, 4L)).anyTimes(); + replay(mockClock); + impl.set(40000L, 3000L); + impl.start(); + verify(mockClock); + } + + @Test + public void cannotSetATimerWithASmallerErrorThanClockGranularity() { + expect(mockClock.getGranularity()).andReturn(1000L).anyTimes(); + replay(mockClock); + assertFalse(impl.set(1000L, 500L)); + verify(mockClock); + } + + @Test + public void hasNotElapsedIfNotStarted() { + expect(mockClock.getGranularity()).andReturn(1000L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(3L, 4L)).anyTimes(); + replay(mockClock); + impl.set(1000L, 1000L); + assertFalse(impl.hasElapsed()); + } + + @Test + public void hasNotElapsedIfClockHasNotTicked() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 6L)).anyTimes(); + replay(mockClock); + impl.set(1000L, 100L); + impl.start(); + assertFalse(impl.hasElapsed()); + verify(mockClock); + } + + @Test + public void hasElapsedIfCurrentClockReadingIsWithinTargetRange() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 5L)); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(1010L, 1015L)).anyTimes(); + replay(mockClock); + impl.set(1000L, 100L); + impl.start(); + assertTrue(impl.hasElapsed()); + verify(mockClock); + } + + @Test + public void hasNotElapsedIfCurrentClockReadingIsNotEntirelyWithinTargetRange() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 5L)); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(900L, 1015L)).anyTimes(); + replay(mockClock); + impl.set(1000L, 100L); + impl.start(); + assertFalse(impl.hasElapsed()); + verify(mockClock); + } + + @Test + public void hasElapsedIfCurrentClockReadingPartiallyExceedsTargetRange() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 5L)); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(1015L, 2000L)).anyTimes(); + replay(mockClock); + impl.set(1000L, 100L); + impl.start(); + assertTrue(impl.hasElapsed()); + verify(mockClock); + } + + @Test + public void hasElapsedIfCurrentClockReadingCompletelyExceedsTargetRange() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 5L)); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(2000L, 2015L)).anyTimes(); + replay(mockClock); + impl.set(1000L, 100L); + impl.start(); + assertTrue(impl.hasElapsed()); + verify(mockClock); + } + + @Test + public void isNotLateIfCurrentClockReadingIsBeforeTargetRange() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 5L)); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(200L, 205L)).anyTimes(); + replay(mockClock); + impl.set(1000L, 100L); + impl.start(); + assertFalse(impl.isLate()); + verify(mockClock); + } - @Test - public void hasNotElapsedIfNotStarted() { - expect(mockClock.getGranularity()).andReturn(1000L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(3L,4L)).anyTimes(); - replay(mockClock); - impl.set(1000L,1000L); - assertFalse(impl.hasElapsed()); - } - - @Test - public void hasNotElapsedIfClockHasNotTicked() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,6L)).anyTimes(); - replay(mockClock); - impl.set(1000L,100L); - impl.start(); - assertFalse(impl.hasElapsed()); - verify(mockClock); - } - - @Test - public void hasElapsedIfCurrentClockReadingIsWithinTargetRange() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,5L)); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(1010L,1015L)).anyTimes(); - replay(mockClock); - impl.set(1000L,100L); - impl.start(); - assertTrue(impl.hasElapsed()); - verify(mockClock); - } - - @Test - public void hasNotElapsedIfCurrentClockReadingIsNotEntirelyWithinTargetRange() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,5L)); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(900L,1015L)).anyTimes(); - replay(mockClock); - impl.set(1000L,100L); - impl.start(); - assertFalse(impl.hasElapsed()); - verify(mockClock); - } + @Test + public void isNotLateIfCurrentClockReadingIsPartiallyBeforeTargetRange() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 5L)); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(200L, 1006L)).anyTimes(); + replay(mockClock); + impl.set(1000L, 100L); + impl.start(); + assertFalse(impl.isLate()); + verify(mockClock); + } - @Test - public void hasElapsedIfCurrentClockReadingPartiallyExceedsTargetRange() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,5L)); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(1015L,2000L)).anyTimes(); - replay(mockClock); - impl.set(1000L,100L); - impl.start(); - assertTrue(impl.hasElapsed()); - verify(mockClock); - } + @Test + public void isNotLateIfCurrentClockReadingIsWithinTargetRange() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 5L)); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(1005L, 1006L)).anyTimes(); + replay(mockClock); + impl.set(1000L, 100L); + impl.start(); + assertFalse(impl.isLate()); + verify(mockClock); + } - @Test - public void hasElapsedIfCurrentClockReadingCompletelyExceedsTargetRange() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,5L)); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(2000L,2015L)).anyTimes(); - replay(mockClock); - impl.set(1000L,100L); - impl.start(); - assertTrue(impl.hasElapsed()); - verify(mockClock); - } - - @Test - public void isNotLateIfCurrentClockReadingIsBeforeTargetRange() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,5L)); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(200L,205L)).anyTimes(); - replay(mockClock); - impl.set(1000L,100L); - impl.start(); - assertFalse(impl.isLate()); - verify(mockClock); - } + @Test + public void isLateIfCurrentClockReadingIsPartiallyBeyondTargetRange() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 5L)); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(1005L, 2006L)).anyTimes(); + replay(mockClock); + impl.set(1000L, 100L); + impl.start(); + assertTrue(impl.isLate()); + verify(mockClock); + } - @Test - public void isNotLateIfCurrentClockReadingIsPartiallyBeforeTargetRange() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,5L)); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(200L,1006L)).anyTimes(); - replay(mockClock); - impl.set(1000L,100L); - impl.start(); - assertFalse(impl.isLate()); - verify(mockClock); - } + @Test + public void isLateIfCurrentClockReadingIsCompletelyBeyondTargetRange() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 5L)); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(2005L, 2006L)).anyTimes(); + replay(mockClock); + impl.set(1000L, 100L); + impl.start(); + assertTrue(impl.isLate()); + verify(mockClock); + } - @Test - public void isNotLateIfCurrentClockReadingIsWithinTargetRange() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,5L)); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(1005L,1006L)).anyTimes(); - replay(mockClock); - impl.set(1000L,100L); - impl.start(); - assertFalse(impl.isLate()); - verify(mockClock); - } + @Test + public void isNotLateIfNotStarted() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 5L)).anyTimes(); + replay(mockClock); + impl.set(1000L, 100L); + assertFalse(impl.isLate()); + verify(mockClock); + } - @Test - public void isLateIfCurrentClockReadingIsPartiallyBeyondTargetRange() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,5L)); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(1005L,2006L)).anyTimes(); - replay(mockClock); - impl.set(1000L,100L); - impl.start(); - assertTrue(impl.isLate()); - verify(mockClock); - } + @Test + public void timeRemainingTillElapsedIsDurationIfNotStarted() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 5L)).anyTimes(); + replay(mockClock); + impl.set(1000L, 100L); + assertEquals(new DiscreteInterval(900L, 1100L), impl.getTimeRemaining()); + verify(mockClock); + } - @Test - public void isLateIfCurrentClockReadingIsCompletelyBeyondTargetRange() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,5L)); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(2005L,2006L)).anyTimes(); - replay(mockClock); - impl.set(1000L,100L); - impl.start(); - assertTrue(impl.isLate()); - verify(mockClock); - } + @Test(expected = IllegalStateException.class) + public void cannotAskForRemainingTimeIfNotSet() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 5L)).anyTimes(); + replay(mockClock); + impl.getTimeRemaining(); + } - @Test - public void isNotLateIfNotStarted() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,5L)).anyTimes(); - replay(mockClock); - impl.set(1000L,100L); - assertFalse(impl.isLate()); - verify(mockClock); - } + @Test + public void hasFullTimeRemainingIfClockHasNotTicked() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 5L)).anyTimes(); + replay(mockClock); + impl.set(1000L, 100L); + impl.start(); + assertEquals(new DiscreteInterval(899L, 1101L), impl.getTimeRemaining()); + verify(mockClock); + } - @Test - public void timeRemainingTillElapsedIsDurationIfNotStarted() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,5L)).anyTimes(); - replay(mockClock); - impl.set(1000L,100L); - assertEquals(new DiscreteInterval(900L,1100L), impl.getTimeRemaining()); - verify(mockClock); - } - - @Test(expected=IllegalStateException.class) - public void cannotAskForRemainingTimeIfNotSet() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,5L)).anyTimes(); - replay(mockClock); - impl.getTimeRemaining(); - } - - @Test - public void hasFullTimeRemainingIfClockHasNotTicked() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,5L)).anyTimes(); - replay(mockClock); - impl.set(1000L,100L); - impl.start(); - assertEquals(new DiscreteInterval(899L,1101L), impl.getTimeRemaining()); - verify(mockClock); - } + @Test + public void canComputePartialTimeRemaining() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 5L)); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(504L, 505L)).anyTimes(); + replay(mockClock); + impl.set(1000L, 100L); + impl.start(); + assertEquals(new DiscreteInterval(399L, 601L), impl.getTimeRemaining()); + verify(mockClock); + } - - @Test - public void canComputePartialTimeRemaining() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,5L)); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(504L,505L)).anyTimes(); - replay(mockClock); - impl.set(1000L,100L); - impl.start(); - assertEquals(new DiscreteInterval(399L,601L), impl.getTimeRemaining()); - verify(mockClock); - } + @Test + public void hasZeroTimeRemainingIfElapsed() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 5L)); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(1005L, 1006L)).anyTimes(); + replay(mockClock); + impl.set(1000L, 100L); + impl.start(); + assertEquals(new DiscreteInterval(0L, 0L), impl.getTimeRemaining()); + verify(mockClock); + } - @Test - public void hasZeroTimeRemainingIfElapsed() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,5L)); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(1005L,1006L)).anyTimes(); - replay(mockClock); - impl.set(1000L,100L); - impl.start(); - assertEquals(new DiscreteInterval(0L,0L), impl.getTimeRemaining()); - verify(mockClock); - } - - @Test - public void canSleepUntilClockElapses() { - expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L,5L)); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(504L,505L)); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(704L,705L)); - expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(1005L,1006L)); - replay(mockClock); - impl.set(1000L,100L); - impl.start(); - impl.waitUntilElapsed(); - verify(mockClock); - } + @Test + public void canSleepUntilClockElapses() { + expect(mockClock.getGranularity()).andReturn(1L).anyTimes(); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(4L, 5L)); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(504L, 505L)); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(704L, 705L)); + expect(mockClock.getNanoTime()).andReturn(new DiscreteInterval(1005L, 1006L)); + replay(mockClock); + impl.set(1000L, 100L); + impl.start(); + impl.waitUntilElapsed(); + verify(mockClock); + } } diff --git a/jrugged-core/src/test/java/org/fishwife/jrugged/interval/TestDiscreteInterval.java b/jrugged-core/src/test/java/org/fishwife/jrugged/interval/TestDiscreteInterval.java index 24deca27..7e0035f4 100644 --- a/jrugged-core/src/test/java/org/fishwife/jrugged/interval/TestDiscreteInterval.java +++ b/jrugged-core/src/test/java/org/fishwife/jrugged/interval/TestDiscreteInterval.java @@ -8,217 +8,217 @@ public class TestDiscreteInterval { - private Random random = new Random(); - - @Test - public void canCreate() { - new DiscreteInterval(1, 3); - } - - @Test - public void allowsMinGreaterThanMax() { - new DiscreteInterval(3, 1); - } - - @Test - public void canGetMinimum() { - assertEquals(1, (new DiscreteInterval(1, 3)).getMin()); - } - - @Test - public void canGetMaximum() { - assertEquals(3, (new DiscreteInterval(1, 3)).getMax()); - } - - @Test - public void canAdd() { - DiscreteInterval i1 = new DiscreteInterval(1, 3); - DiscreteInterval i2 = new DiscreteInterval(2, 4); - DiscreteInterval out = i1.plus(i2); - assertEquals(1 + 2, out.getMin()); - assertEquals(3 + 4, out.getMax()); - } - - @Test - public void addCanHandleWraparoundAddition() { - long x = Long.MAX_VALUE - 5; - DiscreteInterval i1 = new DiscreteInterval(x-1,x); - DiscreteInterval out = i1.plus(new DiscreteInterval(10,10)); - assertEquals(Long.MIN_VALUE + 3, out.getMin()); - assertEquals(Long.MIN_VALUE + 4, out.getMax()); - } - - @Test - public void addCanHandleAdditionOfStraddlingInterval() { - DiscreteInterval i1 = new DiscreteInterval(Long.MAX_VALUE,Long.MIN_VALUE); - DiscreteInterval out = i1.plus(new DiscreteInterval(10,10)); - assertEquals(Long.MIN_VALUE + 9, out.getMin()); - assertEquals(Long.MIN_VALUE + 10, out.getMax()); - } - - @Test - public void canNegate() { - DiscreteInterval i = new DiscreteInterval(1, 3); - DiscreteInterval out = i.negate(); - assertEquals(-3, out.getMin()); - assertEquals(-1, out.getMax()); - } - - @Test - public void sizeOfConstantIntervalIsOne() { - DiscreteInterval i = new DiscreteInterval(1,1); - assertEquals(1, i.size()); - } - - @Test - public void sizeOfWiderIntervalReturnsDifferencePlusOne() { - DiscreteInterval i = new DiscreteInterval(1,3); - assertEquals(3, i.size()); - } - - @Test - public void negationOfPositiveIntervalMaintainsSize() { - DiscreteInterval i = new DiscreteInterval(1, 3); - assertEquals(i.size(), i.negate().size()); - } - - @Test - public void negationOfStraddlingIntervalMaintainsSize() { - DiscreteInterval i = new DiscreteInterval(Long.MAX_VALUE, Long.MIN_VALUE); - assertEquals(i.size(), i.negate().size()); - } - - @Test - public void negationOfNegativeIntervalMaintainsSize() { - DiscreteInterval i = new DiscreteInterval(-3, -1); - assertEquals(i.size(), i.negate().size()); - } - - @Test - public void canSubtract() { - DiscreteInterval i1 = new DiscreteInterval(1, 3); - DiscreteInterval i2 = new DiscreteInterval(2, 4); - DiscreteInterval out = i2.minus(i1); - assertEquals(-1, out.getMin()); - assertEquals(3, out.getMax()); - } - - @Test - public void subtractionIsAddingNegative() { - DiscreteInterval i1 = new DiscreteInterval(random.nextInt(1000), random.nextInt(1000)); - DiscreteInterval i2 = new DiscreteInterval(random.nextInt(1000), random.nextInt(1000)); - DiscreteInterval sub = i1.minus(i2); - DiscreteInterval sub2 = i1.plus(i2.negate()); - assertEquals(sub.getMin(), sub2.getMin()); - assertEquals(sub.getMax(), sub2.getMax()); - } - - @Test - public void doesNotContainNonOverlappingInterval() { - DiscreteInterval i1 = new DiscreteInterval(1,3); - assertFalse(i1.contains(new DiscreteInterval(5,6))); - } - - @Test - public void doesNotContainPartiallyHigherInterval() { - DiscreteInterval i1 = new DiscreteInterval(1,3); - assertFalse(i1.contains(new DiscreteInterval(2,4))); - } - - @Test - public void doesNotContainPartiallyLowerInterval() { - DiscreteInterval i1 = new DiscreteInterval(1,3); - assertFalse(i1.contains(new DiscreteInterval(0,2))); - } - - @Test - public void containsFullySurroundedInterval() { - DiscreteInterval i1 = new DiscreteInterval(1,6); - assertTrue(i1.contains(new DiscreteInterval(3,4))); - } - - @Test - public void containsIntervalWithMatchingMinimum() { - DiscreteInterval i1 = new DiscreteInterval(1,6); - assertTrue(i1.contains(new DiscreteInterval(1,4))); - } - - @Test - public void containsIntervalWithMatchingMaximum() { - DiscreteInterval i1 = new DiscreteInterval(1,6); - assertTrue(i1.contains(new DiscreteInterval(3,6))); - } - - @Test - public void containsItself() { - DiscreteInterval i1 = new DiscreteInterval(1,6); - assertTrue(i1.contains(i1)); - } - - @Test - public void canGenerateStringRep() { - DiscreteInterval i1 = new DiscreteInterval(1,6); - assertEquals("[1,6]", i1.toString()); - } - - @Test - public void intervalsWithSameBoundsAreEqual() { - DiscreteInterval i1 = new DiscreteInterval(1,6); - DiscreteInterval i2 = new DiscreteInterval(1,6); - assertEquals(i1, i2); - } - - @Test - public void intervalsWithDifferentBoundsAreNotEqual() { - DiscreteInterval i1 = new DiscreteInterval(1,6); - DiscreteInterval i2 = new DiscreteInterval(2,3); - assertFalse(i1.equals(i2)); - } - - @Test - public void intervalsWithDifferentMinimumsAreNotEqual() { - DiscreteInterval i1 = new DiscreteInterval(1,6); - DiscreteInterval i2 = new DiscreteInterval(2,6); - assertFalse(i1.equals(i2)); - } - - @Test - public void intervalsWithDifferentMaximumsAreNotEqual() { - DiscreteInterval i1 = new DiscreteInterval(1,6); - DiscreteInterval i2 = new DiscreteInterval(1,5); - assertFalse(i1.equals(i2)); - } - - @Test - public void intervalIsEqualToItself() { - DiscreteInterval i1 = new DiscreteInterval(1,6); - assertTrue(i1.equals(i1)); - } - - @Test - public void intervalIsNotEqualToNull() { - DiscreteInterval i1 = new DiscreteInterval(1,6); - assertFalse(i1.equals(null)); - } - - @Test - public void intervalIsNotEqualToNonIntervals() { - DiscreteInterval i1 = new DiscreteInterval(1,6); - assertFalse(i1.equals(new Object())); - } - - @Test - public void hashCodesOfEqualIntervalsAreEqual() { - DiscreteInterval i1 = new DiscreteInterval(1,6); - DiscreteInterval i2 = new DiscreteInterval(1,6); - assertEquals(i1.hashCode(), i2.hashCode()); - } - - @Test - public void hashCodesOfUnequalIntervalsAreEqual() { - DiscreteInterval i1 = new DiscreteInterval(1,6); - DiscreteInterval i2 = new DiscreteInterval(2,3); - assertFalse(i1.hashCode() == i2.hashCode()); - } + private Random random = new Random(); + + @Test + public void canCreate() { + new DiscreteInterval(1, 3); + } + + @Test + public void allowsMinGreaterThanMax() { + new DiscreteInterval(3, 1); + } + + @Test + public void canGetMinimum() { + assertEquals(1, (new DiscreteInterval(1, 3)).getMin()); + } + + @Test + public void canGetMaximum() { + assertEquals(3, (new DiscreteInterval(1, 3)).getMax()); + } + + @Test + public void canAdd() { + DiscreteInterval i1 = new DiscreteInterval(1, 3); + DiscreteInterval i2 = new DiscreteInterval(2, 4); + DiscreteInterval out = i1.plus(i2); + assertEquals(1 + 2, out.getMin()); + assertEquals(3 + 4, out.getMax()); + } + + @Test + public void addCanHandleWraparoundAddition() { + long x = Long.MAX_VALUE - 5; + DiscreteInterval i1 = new DiscreteInterval(x - 1, x); + DiscreteInterval out = i1.plus(new DiscreteInterval(10, 10)); + assertEquals(Long.MIN_VALUE + 3, out.getMin()); + assertEquals(Long.MIN_VALUE + 4, out.getMax()); + } + + @Test + public void addCanHandleAdditionOfStraddlingInterval() { + DiscreteInterval i1 = new DiscreteInterval(Long.MAX_VALUE, Long.MIN_VALUE); + DiscreteInterval out = i1.plus(new DiscreteInterval(10, 10)); + assertEquals(Long.MIN_VALUE + 9, out.getMin()); + assertEquals(Long.MIN_VALUE + 10, out.getMax()); + } + + @Test + public void canNegate() { + DiscreteInterval i = new DiscreteInterval(1, 3); + DiscreteInterval out = i.negate(); + assertEquals(-3, out.getMin()); + assertEquals(-1, out.getMax()); + } + + @Test + public void sizeOfConstantIntervalIsOne() { + DiscreteInterval i = new DiscreteInterval(1, 1); + assertEquals(1, i.size()); + } + + @Test + public void sizeOfWiderIntervalReturnsDifferencePlusOne() { + DiscreteInterval i = new DiscreteInterval(1, 3); + assertEquals(3, i.size()); + } + + @Test + public void negationOfPositiveIntervalMaintainsSize() { + DiscreteInterval i = new DiscreteInterval(1, 3); + assertEquals(i.size(), i.negate().size()); + } + + @Test + public void negationOfStraddlingIntervalMaintainsSize() { + DiscreteInterval i = new DiscreteInterval(Long.MAX_VALUE, Long.MIN_VALUE); + assertEquals(i.size(), i.negate().size()); + } + + @Test + public void negationOfNegativeIntervalMaintainsSize() { + DiscreteInterval i = new DiscreteInterval(-3, -1); + assertEquals(i.size(), i.negate().size()); + } + + @Test + public void canSubtract() { + DiscreteInterval i1 = new DiscreteInterval(1, 3); + DiscreteInterval i2 = new DiscreteInterval(2, 4); + DiscreteInterval out = i2.minus(i1); + assertEquals(-1, out.getMin()); + assertEquals(3, out.getMax()); + } + + @Test + public void subtractionIsAddingNegative() { + DiscreteInterval i1 = new DiscreteInterval(random.nextInt(1000), random.nextInt(1000)); + DiscreteInterval i2 = new DiscreteInterval(random.nextInt(1000), random.nextInt(1000)); + DiscreteInterval sub = i1.minus(i2); + DiscreteInterval sub2 = i1.plus(i2.negate()); + assertEquals(sub.getMin(), sub2.getMin()); + assertEquals(sub.getMax(), sub2.getMax()); + } + + @Test + public void doesNotContainNonOverlappingInterval() { + DiscreteInterval i1 = new DiscreteInterval(1, 3); + assertFalse(i1.contains(new DiscreteInterval(5, 6))); + } + + @Test + public void doesNotContainPartiallyHigherInterval() { + DiscreteInterval i1 = new DiscreteInterval(1, 3); + assertFalse(i1.contains(new DiscreteInterval(2, 4))); + } + + @Test + public void doesNotContainPartiallyLowerInterval() { + DiscreteInterval i1 = new DiscreteInterval(1, 3); + assertFalse(i1.contains(new DiscreteInterval(0, 2))); + } + + @Test + public void containsFullySurroundedInterval() { + DiscreteInterval i1 = new DiscreteInterval(1, 6); + assertTrue(i1.contains(new DiscreteInterval(3, 4))); + } + + @Test + public void containsIntervalWithMatchingMinimum() { + DiscreteInterval i1 = new DiscreteInterval(1, 6); + assertTrue(i1.contains(new DiscreteInterval(1, 4))); + } + + @Test + public void containsIntervalWithMatchingMaximum() { + DiscreteInterval i1 = new DiscreteInterval(1, 6); + assertTrue(i1.contains(new DiscreteInterval(3, 6))); + } + + @Test + public void containsItself() { + DiscreteInterval i1 = new DiscreteInterval(1, 6); + assertTrue(i1.contains(i1)); + } + + @Test + public void canGenerateStringRep() { + DiscreteInterval i1 = new DiscreteInterval(1, 6); + assertEquals("[1,6]", i1.toString()); + } + + @Test + public void intervalsWithSameBoundsAreEqual() { + DiscreteInterval i1 = new DiscreteInterval(1, 6); + DiscreteInterval i2 = new DiscreteInterval(1, 6); + assertEquals(i1, i2); + } + + @Test + public void intervalsWithDifferentBoundsAreNotEqual() { + DiscreteInterval i1 = new DiscreteInterval(1, 6); + DiscreteInterval i2 = new DiscreteInterval(2, 3); + assertFalse(i1.equals(i2)); + } + + @Test + public void intervalsWithDifferentMinimumsAreNotEqual() { + DiscreteInterval i1 = new DiscreteInterval(1, 6); + DiscreteInterval i2 = new DiscreteInterval(2, 6); + assertFalse(i1.equals(i2)); + } + + @Test + public void intervalsWithDifferentMaximumsAreNotEqual() { + DiscreteInterval i1 = new DiscreteInterval(1, 6); + DiscreteInterval i2 = new DiscreteInterval(1, 5); + assertFalse(i1.equals(i2)); + } + + @Test + public void intervalIsEqualToItself() { + DiscreteInterval i1 = new DiscreteInterval(1, 6); + assertTrue(i1.equals(i1)); + } + + @Test + public void intervalIsNotEqualToNull() { + DiscreteInterval i1 = new DiscreteInterval(1, 6); + assertFalse(i1.equals(null)); + } + + @Test + public void intervalIsNotEqualToNonIntervals() { + DiscreteInterval i1 = new DiscreteInterval(1, 6); + assertFalse(i1.equals(new Object())); + } + + @Test + public void hashCodesOfEqualIntervalsAreEqual() { + DiscreteInterval i1 = new DiscreteInterval(1, 6); + DiscreteInterval i2 = new DiscreteInterval(1, 6); + assertEquals(i1.hashCode(), i2.hashCode()); + } + + @Test + public void hashCodesOfUnequalIntervalsAreEqual() { + DiscreteInterval i1 = new DiscreteInterval(1, 6); + DiscreteInterval i2 = new DiscreteInterval(2, 3); + assertFalse(i1.hashCode() == i2.hashCode()); + } } diff --git a/jrugged-examples/pom.xml b/jrugged-examples/pom.xml index 8dc0f50a..4173f623 100644 --- a/jrugged-examples/pom.xml +++ b/jrugged-examples/pom.xml @@ -280,10 +280,12 @@ - 4.3.20.RELEASE - 2.0 + + 5.1.5.RELEASE + 2.2.1-b03 + 4.4 - 3.0.1 + 4.0.1 1.2 diff --git a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/AspectResponseTweaker.java b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/AspectResponseTweaker.java index 962a702a..541cf0c3 100644 --- a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/AspectResponseTweaker.java +++ b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/AspectResponseTweaker.java @@ -20,20 +20,21 @@ public class AspectResponseTweaker { - @PerformanceMonitor("AspectResponseTweaker") - public int delay() throws Exception { - Random r = new Random(); - int count = r.nextInt(2001); - - if (count > 1650) { - throw new Exception("Count was over the limit."); - } - - try { - Thread.sleep(count); - } catch (InterruptedException e) { } - - return count; - } + @PerformanceMonitor("AspectResponseTweaker") + public int delay() throws Exception { + Random r = new Random(); + int count = r.nextInt(2001); + + if (count > 1650) { + throw new Exception("Count was over the limit."); + } + + try { + Thread.sleep(count); + } catch (InterruptedException e) { + } + + return count; + } } diff --git a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/BreakerResponseTweaker.java b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/BreakerResponseTweaker.java index 7b1d20db..37984744 100644 --- a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/BreakerResponseTweaker.java +++ b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/BreakerResponseTweaker.java @@ -18,18 +18,19 @@ public class BreakerResponseTweaker { - public int delay() throws Exception { - Random r = new Random(); - int count = r.nextInt(2001); + public int delay() throws Exception { + Random r = new Random(); + int count = r.nextInt(2001); - if (count > 1000) { - throw new Exception("Count was over the limit."); - } + if (count > 1000) { + throw new Exception("Count was over the limit."); + } - try { - Thread.sleep(count); - } catch (InterruptedException e) { } - return count; - } + try { + Thread.sleep(count); + } catch (InterruptedException e) { + } + return count; + } } diff --git a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/InterceptResponseTweaker.java b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/InterceptResponseTweaker.java index 79b0f583..2e5f6907 100644 --- a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/InterceptResponseTweaker.java +++ b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/InterceptResponseTweaker.java @@ -18,18 +18,19 @@ public class InterceptResponseTweaker { - public int delay() throws Exception { - Random r = new Random(); - int count = r.nextInt(2001); + public int delay() throws Exception { + Random r = new Random(); + int count = r.nextInt(2001); - if (count > 1650) { - throw new Exception("Count was over the limit."); - } + if (count > 1650) { + throw new Exception("Count was over the limit."); + } - try { - Thread.sleep(count); - } catch (InterruptedException e) { } - return count; - } + try { + Thread.sleep(count); + } catch (InterruptedException e) { + } + return count; + } } diff --git a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/PercentileTracker.java b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/PercentileTracker.java index 5c56e1e2..0da1deb4 100644 --- a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/PercentileTracker.java +++ b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/PercentileTracker.java @@ -22,86 +22,89 @@ /** * This class implements a way to get percentiles from a list of samples within - * a given time range using the algorithm described at - * http://cnx.org/content/m10805/latest/. - * The percentile does not sample itself; it merely computes the asked for percentile - * when called. + * a given time range using the algorithm described at http://cnx.org/content/m10805/latest/. + * The percentile does not sample itself; it merely computes the asked for + * percentile when called. */ public class PercentileTracker { - private long windowMillis; - - protected ConcurrentSkipListMap cslm = new ConcurrentSkipListMap(); - - public PercentileTracker(long windowMillis) { - this.windowMillis = windowMillis; - } - - /** Updates the average with the latest measurement. - * @param sample the latest measurement in the rolling average */ - public synchronized void update(double sample) { - long now = System.currentTimeMillis(); - - removeOutOfTimeWindowEntries(); - cslm.put(now, sample); - } - - /** - * Returns a computed percentile value. - * - * @param requestedPercentile Which whole number percentile value needs to be calculated and returned - * @return double the percentile - */ - public double getPercentile(int requestedPercentile) { - return getPercentile(requestedPercentile, new ArrayList(cslm.values())); - } - - protected double getPercentile(int requestedPercentile, ArrayList values) { - if (values == null || values.size() == 0) { - return 0d; - } - - Collections.sort(values); - Double[] mySampleSet = values.toArray(new Double[values.size()]); - - //This is the Excel Percentile Rank - double rank = (((double) requestedPercentile / 100d) * (values.size() - 1)) + 1; - - //This is the Weighted Percentile Rank - //double rank = ((double) requestedPercentile / 100d) * (values.size() + 1); - - double returnPercentile; - - int integerRank = (int) rank; - double remainder = rank - integerRank; - - if (remainder > 0) { - //Interpolate the percentile - double valueAtRankIr = mySampleSet[integerRank - 1]; - double valueAtRankIrPlusOne = mySampleSet[integerRank]; - returnPercentile = remainder * (valueAtRankIrPlusOne - valueAtRankIr) + valueAtRankIr; - } - else { - // Use the rank to find the 'exact' percentile. - returnPercentile = mySampleSet[integerRank - 1]; - } - return returnPercentile; - - } - - private void removeOutOfTimeWindowEntries() { - if (cslm.isEmpty()) { - return; - } - - // Optimization - if the last entry is also outside - // the time window, all items in the list can be cleared. - if (System.currentTimeMillis() - cslm.lastKey() > windowMillis) { - cslm.clear(); - return; - } - - while ((cslm.lastKey() - cslm.firstKey()) > windowMillis) { - cslm.pollFirstEntry(); //The first entry is now too old, remove it - } - } + private long windowMillis; + + protected ConcurrentSkipListMap cslm = new ConcurrentSkipListMap(); + + public PercentileTracker(long windowMillis) { + this.windowMillis = windowMillis; + } + + /** + * Updates the average with the latest measurement. + * + * @param sample the latest measurement in the rolling average + */ + public synchronized void update(double sample) { + long now = System.currentTimeMillis(); + + removeOutOfTimeWindowEntries(); + cslm.put(now, sample); + } + + /** + * Returns a computed percentile value. + * + * @param requestedPercentile Which whole number percentile value needs to be + * calculated and returned + * @return double the percentile + */ + public double getPercentile(int requestedPercentile) { + return getPercentile(requestedPercentile, new ArrayList(cslm.values())); + } + + protected double getPercentile(int requestedPercentile, ArrayList values) { + if (values == null || values.size() == 0) { + return 0d; + } + + Collections.sort(values); + Double[] mySampleSet = values.toArray(new Double[values.size()]); + + // This is the Excel Percentile Rank + double rank = (((double) requestedPercentile / 100d) * (values.size() - 1)) + 1; + + // This is the Weighted Percentile Rank + // double rank = ((double) requestedPercentile / 100d) * (values.size() + 1); + + double returnPercentile; + + int integerRank = (int) rank; + double remainder = rank - integerRank; + + if (remainder > 0) { + // Interpolate the percentile + double valueAtRankIr = mySampleSet[integerRank - 1]; + double valueAtRankIrPlusOne = mySampleSet[integerRank]; + returnPercentile = remainder * (valueAtRankIrPlusOne - valueAtRankIr) + valueAtRankIr; + } else { + // Use the rank to find the 'exact' percentile. + returnPercentile = mySampleSet[integerRank - 1]; + } + return returnPercentile; + + } + + private void removeOutOfTimeWindowEntries() { + if (cslm.isEmpty()) { + return; + } + + // Optimization - if the last entry is also outside + // the time window, all items in the list can be cleared. + if (System.currentTimeMillis() - cslm.lastKey() > windowMillis) { + cslm.clear(); + return; + } + + while ((cslm.lastKey() - cslm.firstKey()) > windowMillis) { + cslm.pollFirstEntry(); // The first entry is now too old, remove it + } + } } diff --git a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/performance/FixedDelayPerformer.java b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/performance/FixedDelayPerformer.java index d04ec8b5..e35b8dc2 100644 --- a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/performance/FixedDelayPerformer.java +++ b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/performance/FixedDelayPerformer.java @@ -14,21 +14,22 @@ */ package org.fishwife.jrugged.examples.performance; -/** A test class that can be used to model a wrapped call that takes a - * specific amount of time to execute. +/** + * A test class that can be used to model a wrapped call that takes a specific + * amount of time to execute. */ public class FixedDelayPerformer implements Runnable { - private final long _delayMillis; + private final long _delayMillis; - public FixedDelayPerformer(Long delayMillis) { - _delayMillis = delayMillis; - } + public FixedDelayPerformer(Long delayMillis) { + _delayMillis = delayMillis; + } - public void run() { - try { - Thread.sleep(_delayMillis); - } catch (Exception e) { - } - } + public void run() { + try { + Thread.sleep(_delayMillis); + } catch (Exception e) { + } + } } diff --git a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/performance/InstrumentationImpact.java b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/performance/InstrumentationImpact.java index a3cfbb15..a69a3f32 100644 --- a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/performance/InstrumentationImpact.java +++ b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/performance/InstrumentationImpact.java @@ -18,105 +18,94 @@ import org.fishwife.jrugged.PerformanceMonitor; -/** This is an example driver program that measures the amount of - * overhead introduced by wrapping a method call with a - * {@link PerformanceMonitor}. */ +/** + * This is an example driver program that measures the amount of overhead + * introduced by wrapping a method call with a {@link PerformanceMonitor}. + */ public class InstrumentationImpact { - public static void main (String[] args) throws Exception { - InstrumentationImpact ii = new InstrumentationImpact(); - ii.examplePerformanceImpact(); - ii.exampleExceptionCountsImpact(); - ii.exampleRunningSuccessRPS(); - } - - public void examplePerformanceImpact() throws Exception { - PerformanceMonitor perfMon = new PerformanceMonitor(); - final FixedDelayPerformer performer = new FixedDelayPerformer(50L); - - long startDelay = System.currentTimeMillis(); - for(int i=0; i<500; i++) { - perfMon.invoke(performer); - } - long endDelay = System.currentTimeMillis(); - - long start = System.currentTimeMillis(); - for(int i=0; i<500; i++) { - performer.run(); - } - long end = System.currentTimeMillis(); - - long deltaDelay = endDelay - startDelay; - long delta = end - start; - long difference = deltaDelay - delta; - long percentEffect = (difference/delta) * 100; - - System.out.println("PerfMon Runtime Total: " + deltaDelay); - System.out.println("Plain Runtime Total: " + delta); - System.out.println(String.format("Instrumentation Effect: %s, Difference Percent: %s", - difference, percentEffect)); - } - - public void exampleExceptionCountsImpact() { - int numberOfTimesToTryAMethodCall = 500; - int numberOfAttemptsBeforeThrowingException = 5; - int expectedNumberOfFailures = - numberOfTimesToTryAMethodCall / - numberOfAttemptsBeforeThrowingException; - int expectedNumberOfSuccess = numberOfTimesToTryAMethodCall - - expectedNumberOfFailures; - - PerformanceMonitor perfMon = new PerformanceMonitor(); - final OccasionalExceptionPerformer performer = - new OccasionalExceptionPerformer(numberOfAttemptsBeforeThrowingException); - - for(int i=0; inth - * call. */ +/** + * A test class that will throw an exeception every nth call. + */ public class OccasionalExceptionPerformer implements Runnable { - private int _callsPerException; - private int _loopCounter; - - public OccasionalExceptionPerformer(int callsPerException) { - _callsPerException = callsPerException; - } + private int _callsPerException; + private int _loopCounter; - public void run() { - _loopCounter++; - if (_loopCounter % _callsPerException == 0) - { - throw new IllegalStateException("Duh"); - } - } + public OccasionalExceptionPerformer(int callsPerException) { + _callsPerException = callsPerException; + } + public void run() { + _loopCounter++; + if (_loopCounter % _callsPerException == 0) { + throw new IllegalStateException("Duh"); + } + } } diff --git a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/webapp/AspectPerformanceMonitorExample.java b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/webapp/AspectPerformanceMonitorExample.java index 9e72449a..bf8b245e 100644 --- a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/webapp/AspectPerformanceMonitorExample.java +++ b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/webapp/AspectPerformanceMonitorExample.java @@ -31,57 +31,57 @@ @Controller() public class AspectPerformanceMonitorExample { - @Autowired - private PerformanceMonitorAspect performanceAspect; - public PerformanceMonitorAspect getPerformanceAspect() { - return performanceAspect; - } - public void setPerformanceAspect(PerformanceMonitorAspect performanceAspect) { - this.performanceAspect = performanceAspect; - } + @Autowired + private PerformanceMonitorAspect performanceAspect; - @Autowired - private AspectResponseTweaker aspectResponseTweaker; - public AspectResponseTweaker getResponseTweaker() { - return aspectResponseTweaker; - } - public void setAspectResponseTweaker(AspectResponseTweaker aspectResponseTweaker) { - this.aspectResponseTweaker = aspectResponseTweaker; - } + public PerformanceMonitorAspect getPerformanceAspect() { + return performanceAspect; + } - @RequestMapping("/aspectPerformanceMonitor") - public ModelAndView viewMain(HttpServletRequest request, HttpServletResponse response) throws Exception { - int delayedFor = aspectResponseTweaker.delay(); - ModelAndView view = new ModelAndView("aspectPerf-monitor"); - view.addObject("delay", new Integer(delayedFor)); - return view; - } + public void setPerformanceAspect(PerformanceMonitorAspect performanceAspect) { + this.performanceAspect = performanceAspect; + } - @RequestMapping("/aspectPerformanceMonitor/stats") - public ModelAndView viewPerformanceMonitor(HttpServletRequest request, - HttpServletResponse response) throws Exception { - final StringBuilder sb = new StringBuilder(); - PerformanceMonitorFactory factory = performanceAspect.getPerformanceMonitorFactory(); - for (String monitorName : factory.getPerformanceMonitorNames()) { - PerformanceMonitor m = factory.findPerformanceMonitor(monitorName); - sb.append(String.format("[%s]", monitorName)).append("\n"); - // Go through all methods and invoke those with ManagedAttribute - // marker annotations - Method[] methods = m.getClass().getMethods(); - for (Method monitorMethod : methods) { - if (monitorMethod.getName().startsWith("get")) { - sb.append( - String.format("\t%s: %s\n", - monitorMethod.getName().substring(3), - monitorMethod.invoke(m, new Object[] {}) - ) - ); - } - } - sb.append("\n"); - } - response.setContentType("text/plain"); - response.getWriter().println(sb.toString()); - return null; - } + @Autowired + private AspectResponseTweaker aspectResponseTweaker; + + public AspectResponseTweaker getResponseTweaker() { + return aspectResponseTweaker; + } + + public void setAspectResponseTweaker(AspectResponseTweaker aspectResponseTweaker) { + this.aspectResponseTweaker = aspectResponseTweaker; + } + + @RequestMapping("/aspectPerformanceMonitor") + public ModelAndView viewMain(HttpServletRequest request, HttpServletResponse response) throws Exception { + int delayedFor = aspectResponseTweaker.delay(); + ModelAndView view = new ModelAndView("aspectPerf-monitor"); + view.addObject("delay", new Integer(delayedFor)); + return view; + } + + @RequestMapping("/aspectPerformanceMonitor/stats") + public ModelAndView viewPerformanceMonitor(HttpServletRequest request, HttpServletResponse response) + throws Exception { + final StringBuilder sb = new StringBuilder(); + PerformanceMonitorFactory factory = performanceAspect.getPerformanceMonitorFactory(); + for (String monitorName : factory.getPerformanceMonitorNames()) { + PerformanceMonitor m = factory.findPerformanceMonitor(monitorName); + sb.append(String.format("[%s]", monitorName)).append("\n"); + // Go through all methods and invoke those with ManagedAttribute + // marker annotations + Method[] methods = m.getClass().getMethods(); + for (Method monitorMethod : methods) { + if (monitorMethod.getName().startsWith("get")) { + sb.append(String.format("\t%s: %s\n", monitorMethod.getName().substring(3), + monitorMethod.invoke(m, new Object[] {}))); + } + } + sb.append("\n"); + } + response.setContentType("text/plain"); + response.getWriter().println(sb.toString()); + return null; + } } diff --git a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/webapp/InterceptCircuitBreakerExample.java b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/webapp/InterceptCircuitBreakerExample.java index e5bfc474..66a4899a 100644 --- a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/webapp/InterceptCircuitBreakerExample.java +++ b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/webapp/InterceptCircuitBreakerExample.java @@ -29,54 +29,54 @@ @Controller() public class InterceptCircuitBreakerExample { - @Autowired - private CircuitBreakerBean circuitBreakerBean; - public CircuitBreakerBean getCircuitBreakerBean() { - return circuitBreakerBean; - } - public void setCircuitBreakerBean(CircuitBreakerBean circuitBreakerBean) { - this.circuitBreakerBean = circuitBreakerBean; - } + @Autowired + private CircuitBreakerBean circuitBreakerBean; - @Autowired - private BreakerResponseTweaker breakerResponseTweaker; - public BreakerResponseTweaker getResponseTweaker() { - return breakerResponseTweaker; - } - public void setResponseTweaker(BreakerResponseTweaker breakerResponseTweaker) { - this.breakerResponseTweaker = breakerResponseTweaker; - } + public CircuitBreakerBean getCircuitBreakerBean() { + return circuitBreakerBean; + } - @RequestMapping("/interceptCircuitBreaker") - public ModelAndView viewMain(HttpServletRequest request, HttpServletResponse response) throws Exception { - int delayedFor = breakerResponseTweaker.delay(); - ModelAndView view = new ModelAndView("interceptBreaker"); - view.addObject("delay", new Integer(delayedFor)); - return view; - } + public void setCircuitBreakerBean(CircuitBreakerBean circuitBreakerBean) { + this.circuitBreakerBean = circuitBreakerBean; + } - @RequestMapping("/interceptCircuitBreaker/stats") - public ModelAndView viewPerformanceMonitor(HttpServletRequest request, - HttpServletResponse response) throws Exception { - final StringBuilder sb = new StringBuilder(); + @Autowired + private BreakerResponseTweaker breakerResponseTweaker; - // Go through all methods and invoke those with ManagedAttribute - // marker annotations - Method[] methods = circuitBreakerBean.getClass().getMethods(); - for (Method monitorMethod : methods) { - if (monitorMethod.getName().startsWith("get")) { - sb.append( - String.format("\t%s: %s\n", - monitorMethod.getName().substring(3), - monitorMethod.invoke(circuitBreakerBean, new Object[] {}) - ) - ); - } - } - sb.append("\n"); + public BreakerResponseTweaker getResponseTweaker() { + return breakerResponseTweaker; + } - response.setContentType("text/plain"); - response.getWriter().println(sb.toString()); - return null; - } + public void setResponseTweaker(BreakerResponseTweaker breakerResponseTweaker) { + this.breakerResponseTweaker = breakerResponseTweaker; + } + + @RequestMapping("/interceptCircuitBreaker") + public ModelAndView viewMain(HttpServletRequest request, HttpServletResponse response) throws Exception { + int delayedFor = breakerResponseTweaker.delay(); + ModelAndView view = new ModelAndView("interceptBreaker"); + view.addObject("delay", new Integer(delayedFor)); + return view; + } + + @RequestMapping("/interceptCircuitBreaker/stats") + public ModelAndView viewPerformanceMonitor(HttpServletRequest request, HttpServletResponse response) + throws Exception { + final StringBuilder sb = new StringBuilder(); + + // Go through all methods and invoke those with ManagedAttribute + // marker annotations + Method[] methods = circuitBreakerBean.getClass().getMethods(); + for (Method monitorMethod : methods) { + if (monitorMethod.getName().startsWith("get")) { + sb.append(String.format("\t%s: %s\n", monitorMethod.getName().substring(3), + monitorMethod.invoke(circuitBreakerBean, new Object[] {}))); + } + } + sb.append("\n"); + + response.setContentType("text/plain"); + response.getWriter().println(sb.toString()); + return null; + } } diff --git a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/webapp/InterceptPerformanceMonitorExample.java b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/webapp/InterceptPerformanceMonitorExample.java index cb52f870..b0e5cfc4 100644 --- a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/webapp/InterceptPerformanceMonitorExample.java +++ b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/webapp/InterceptPerformanceMonitorExample.java @@ -29,54 +29,54 @@ @Controller() public class InterceptPerformanceMonitorExample { - @Autowired - private PerformanceMonitorBean performanceBean; - public PerformanceMonitorBean getPerformanceBean() { - return performanceBean; - } - public void setPerformanceBean(PerformanceMonitorBean performanceBean) { - this.performanceBean = performanceBean; - } + @Autowired + private PerformanceMonitorBean performanceBean; - @Autowired - private InterceptResponseTweaker interceptResponseTweaker; - public InterceptResponseTweaker getResponseTweaker() { - return interceptResponseTweaker; - } - public void setResponseTweaker(InterceptResponseTweaker interceptResponseTweaker) { - this.interceptResponseTweaker = interceptResponseTweaker; - } + public PerformanceMonitorBean getPerformanceBean() { + return performanceBean; + } - @RequestMapping("/interceptPerformanceMonitor") - public ModelAndView viewMain(HttpServletRequest request, HttpServletResponse response) throws Exception { - int delayedFor = interceptResponseTweaker.delay(); - ModelAndView view = new ModelAndView("interceptPerf-monitor"); - view.addObject("delay", new Integer(delayedFor)); - return view; - } + public void setPerformanceBean(PerformanceMonitorBean performanceBean) { + this.performanceBean = performanceBean; + } - @RequestMapping("/interceptPerformanceMonitor/stats") - public ModelAndView viewPerformanceMonitor(HttpServletRequest request, - HttpServletResponse response) throws Exception { - final StringBuilder sb = new StringBuilder(); + @Autowired + private InterceptResponseTweaker interceptResponseTweaker; - // Go through all methods and invoke those with ManagedAttribute - // marker annotations - Method[] methods = performanceBean.getClass().getMethods(); - for (Method monitorMethod : methods) { - if (monitorMethod.getName().startsWith("get")) { - sb.append( - String.format("\t%s: %s\n", - monitorMethod.getName().substring(3), - monitorMethod.invoke(performanceBean, new Object[] {}) - ) - ); - } - } - sb.append("\n"); + public InterceptResponseTweaker getResponseTweaker() { + return interceptResponseTweaker; + } - response.setContentType("text/plain"); - response.getWriter().println(sb.toString()); - return null; - } + public void setResponseTweaker(InterceptResponseTweaker interceptResponseTweaker) { + this.interceptResponseTweaker = interceptResponseTweaker; + } + + @RequestMapping("/interceptPerformanceMonitor") + public ModelAndView viewMain(HttpServletRequest request, HttpServletResponse response) throws Exception { + int delayedFor = interceptResponseTweaker.delay(); + ModelAndView view = new ModelAndView("interceptPerf-monitor"); + view.addObject("delay", new Integer(delayedFor)); + return view; + } + + @RequestMapping("/interceptPerformanceMonitor/stats") + public ModelAndView viewPerformanceMonitor(HttpServletRequest request, HttpServletResponse response) + throws Exception { + final StringBuilder sb = new StringBuilder(); + + // Go through all methods and invoke those with ManagedAttribute + // marker annotations + Method[] methods = performanceBean.getClass().getMethods(); + for (Method monitorMethod : methods) { + if (monitorMethod.getName().startsWith("get")) { + sb.append(String.format("\t%s: %s\n", monitorMethod.getName().substring(3), + monitorMethod.invoke(performanceBean, new Object[] {}))); + } + } + sb.append("\n"); + + response.setContentType("text/plain"); + response.getWriter().println(sb.toString()); + return null; + } } diff --git a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/webapp/RootController.java b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/webapp/RootController.java index 84b0593e..de452c1c 100644 --- a/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/webapp/RootController.java +++ b/jrugged-examples/src/main/java/org/fishwife/jrugged/examples/webapp/RootController.java @@ -24,10 +24,10 @@ @Controller public class RootController { - @RequestMapping("/") - public ModelAndView viewIndex(HttpServletRequest request, HttpServletResponse response) throws Exception { - ModelAndView view = new ModelAndView("index"); - return view; - } + @RequestMapping("/") + public ModelAndView viewIndex(HttpServletRequest request, HttpServletResponse response) throws Exception { + ModelAndView view = new ModelAndView("index"); + return view; + } } diff --git a/jrugged-examples/src/test/java/org/fishwife/jrugged/examples/TestPercentileTracker.java b/jrugged-examples/src/test/java/org/fishwife/jrugged/examples/TestPercentileTracker.java index 0a8a1e58..064dc04d 100644 --- a/jrugged-examples/src/test/java/org/fishwife/jrugged/examples/TestPercentileTracker.java +++ b/jrugged-examples/src/test/java/org/fishwife/jrugged/examples/TestPercentileTracker.java @@ -27,87 +27,87 @@ public class TestPercentileTracker { - ArrayList normalizedTestArray; + ArrayList normalizedTestArray; - @Before - public void setup() { - normalizedTestArray = new ArrayList() { - {add(0d); - add(1d); - add(2d); - add(3d); - add(4d); - add(5d); - add(6d); - add(7d); - add(8d); - add(9d); - add(10d); - } - }; - } + @Before + public void setup() { + normalizedTestArray = new ArrayList() { + { + add(0d); + add(1d); + add(2d); + add(3d); + add(4d); + add(5d); + add(6d); + add(7d); + add(8d); + add(9d); + add(10d); + } + }; + } - @Test - public void test90ithPercentile () { - PercentileTracker pt = new PercentileTracker(5000); - double value = pt.getPercentile(90, normalizedTestArray); - Assert.assertEquals(9d, value, 0); - } + @Test + public void test90ithPercentile() { + PercentileTracker pt = new PercentileTracker(5000); + double value = pt.getPercentile(90, normalizedTestArray); + Assert.assertEquals(9d, value, 0); + } - @Test - public void test95ithPercentile () { - PercentileTracker pt = new PercentileTracker(5000); - double value = pt.getPercentile(95, normalizedTestArray); - Assert.assertEquals(9.5d, value, 0); - } + @Test + public void test95ithPercentile() { + PercentileTracker pt = new PercentileTracker(5000); + double value = pt.getPercentile(95, normalizedTestArray); + Assert.assertEquals(9.5d, value, 0); + } - @Test - public void testUpdate() { - PercentileTracker pt = new PercentileTracker(5000); - for (int i = 0; i < 11; i++) { - pt.update((double)i); - try { - sleep(10); - } catch (InterruptedException e) { - fail(); - } - } - double value = pt.getPercentile(95); - Assert.assertEquals(9.5d, value, 0); + @Test + public void testUpdate() { + PercentileTracker pt = new PercentileTracker(5000); + for (int i = 0; i < 11; i++) { + pt.update((double) i); + try { + sleep(10); + } catch (InterruptedException e) { + fail(); + } + } + double value = pt.getPercentile(95); + Assert.assertEquals(9.5d, value, 0); - } + } - @Test - public void testEmptyPercentileTrackerDoesNotNPE() { - PercentileTracker pt = new PercentileTracker(5000); + @Test + public void testEmptyPercentileTrackerDoesNotNPE() { + PercentileTracker pt = new PercentileTracker(5000); - try { - pt.getPercentile(99); - } - catch (Exception e) { - fail(); - } - } + try { + pt.getPercentile(99); + } catch (Exception e) { + fail(); + } + } - @Test - public void testPTWholeListOutsideWindow() { - PercentileTracker pt = new PercentileTracker(200); - for (int i = 0; i < 11; i++) { - pt.update((double)i); - try { - sleep(10); - } catch (InterruptedException e) { - fail(); - } - } - Assert.assertEquals(pt.cslm.entrySet().size(), 11); + @Test + public void testPTWholeListOutsideWindow() { + PercentileTracker pt = new PercentileTracker(200); + for (int i = 0; i < 11; i++) { + pt.update((double) i); + try { + sleep(10); + } catch (InterruptedException e) { + fail(); + } + } + Assert.assertEquals(pt.cslm.entrySet().size(), 11); - try { - sleep(300); - } catch (InterruptedException e) { - fail(); - } + try { + sleep(300); + } catch (InterruptedException e) { + fail(); + } - pt.update(1d); - } + pt.update(1d); + } } diff --git a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/AbstractHttpClientDecorator.java b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/AbstractHttpClientDecorator.java index ec842822..242c19e1 100644 --- a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/AbstractHttpClientDecorator.java +++ b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/AbstractHttpClientDecorator.java @@ -36,69 +36,62 @@ */ public abstract class AbstractHttpClientDecorator implements HttpClient { - protected HttpClient backend; - - public AbstractHttpClientDecorator(HttpClient backend) { - this.backend = backend; - } - - public abstract HttpResponse execute(HttpHost host, HttpRequest req, - HttpContext ctx) throws IOException, ClientProtocolException; - - protected HttpHost getHttpHost(HttpUriRequest req) { - URI uri = req.getURI(); - String scheme = uri.getScheme(); - if ("HTTPS".equalsIgnoreCase(scheme)) { - return new HttpHost(uri.getScheme() + "://" + uri.getAuthority()); - } else { - return new HttpHost(uri.getAuthority()); - } - } - - public HttpResponse execute(HttpUriRequest req) throws IOException, - ClientProtocolException { - return execute(req, (HttpContext)null); - } - - public HttpResponse execute(HttpUriRequest req, HttpContext ctx) - throws IOException, ClientProtocolException { - return execute(getHttpHost(req), req, ctx); - } - - public HttpResponse execute(HttpHost host, HttpRequest req) - throws IOException, ClientProtocolException { - return execute(host, req, (HttpContext)null); - } - - public T execute(HttpUriRequest req, ResponseHandler rh) - throws IOException, ClientProtocolException { - return rh.handleResponse(execute(req)); - } - - public T execute(HttpUriRequest req, - ResponseHandler rh, HttpContext ctx) - throws IOException, ClientProtocolException { - return rh.handleResponse(execute(req, ctx)); - } - - public T execute(HttpHost host, HttpRequest req, - ResponseHandler rh) throws IOException, - ClientProtocolException { - return rh.handleResponse(execute(host, req)); - } - - public T execute(HttpHost host, HttpRequest req, - ResponseHandler rh, HttpContext ctx) - throws IOException, ClientProtocolException { - return rh.handleResponse(execute(host, req, ctx)); - } - - public ClientConnectionManager getConnectionManager() { - return backend.getConnectionManager(); - } - - public HttpParams getParams() { - return backend.getParams(); - } + protected HttpClient backend; + + public AbstractHttpClientDecorator(HttpClient backend) { + this.backend = backend; + } + + public abstract HttpResponse execute(HttpHost host, HttpRequest req, HttpContext ctx) + throws IOException, ClientProtocolException; + + protected HttpHost getHttpHost(HttpUriRequest req) { + URI uri = req.getURI(); + String scheme = uri.getScheme(); + if ("HTTPS".equalsIgnoreCase(scheme)) { + return new HttpHost(uri.getScheme() + "://" + uri.getAuthority()); + } else { + return new HttpHost(uri.getAuthority()); + } + } + + public HttpResponse execute(HttpUriRequest req) throws IOException, ClientProtocolException { + return execute(req, (HttpContext) null); + } + + public HttpResponse execute(HttpUriRequest req, HttpContext ctx) throws IOException, ClientProtocolException { + return execute(getHttpHost(req), req, ctx); + } + + public HttpResponse execute(HttpHost host, HttpRequest req) throws IOException, ClientProtocolException { + return execute(host, req, (HttpContext) null); + } + + public T execute(HttpUriRequest req, ResponseHandler rh) + throws IOException, ClientProtocolException { + return rh.handleResponse(execute(req)); + } + + public T execute(HttpUriRequest req, ResponseHandler rh, HttpContext ctx) + throws IOException, ClientProtocolException { + return rh.handleResponse(execute(req, ctx)); + } + + public T execute(HttpHost host, HttpRequest req, ResponseHandler rh) + throws IOException, ClientProtocolException { + return rh.handleResponse(execute(host, req)); + } + + public T execute(HttpHost host, HttpRequest req, ResponseHandler rh, HttpContext ctx) + throws IOException, ClientProtocolException { + return rh.handleResponse(execute(host, req, ctx)); + } + + public ClientConnectionManager getConnectionManager() { + return backend.getConnectionManager(); + } + + public HttpParams getParams() { + return backend.getParams(); + } } - diff --git a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/DefaultResponseFailureAssessor.java b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/DefaultResponseFailureAssessor.java index 368111d0..3416e717 100644 --- a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/DefaultResponseFailureAssessor.java +++ b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/DefaultResponseFailureAssessor.java @@ -2,15 +2,15 @@ import org.apache.http.HttpResponse; -/** A {@link ResponseFailureAssessor} that treats all - * 4XX and 5XX status codes as failures. +/** + * A {@link ResponseFailureAssessor} that treats all 4XX and 5XX status codes as + * failures. */ public class DefaultResponseFailureAssessor implements ResponseFailureAssessor { - public boolean isFailure(HttpResponse resp) { - int status = resp.getStatusLine().getStatusCode(); - return (status >= 400 && status <= 499) || - (status >= 500 && status <= 599); - } + public boolean isFailure(HttpResponse resp) { + int status = resp.getStatusLine().getStatusCode(); + return (status >= 400 && status <= 499) || (status >= 500 && status <= 599); + } } diff --git a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/FailureExposingHttpClient.java b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/FailureExposingHttpClient.java index a2150ae7..8f386821 100644 --- a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/FailureExposingHttpClient.java +++ b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/FailureExposingHttpClient.java @@ -26,33 +26,33 @@ import org.apache.http.protocol.HttpContext; /** - * This is a decorator for an {@link org.apache.http.client.HttpClient} - * that will raise runtime exceptions for 4XX or 5XX responses, so that - * they can be used to signal failures to JRugged + * This is a decorator for an {@link org.apache.http.client.HttpClient} that + * will raise runtime exceptions for 4XX or 5XX responses, so that they can be + * used to signal failures to JRugged * {@link org.fishwife.jrugged.ServiceWrapper} instances. * */ public class FailureExposingHttpClient extends AbstractHttpClientDecorator { - private ResponseFailureAssessor assessor; - - public FailureExposingHttpClient(HttpClient backend) { - super(backend); - assessor = new DefaultResponseFailureAssessor(); - } - - public FailureExposingHttpClient(HttpClient backend, ResponseFailureAssessor assessor) { - super(backend); - this.assessor = assessor; - } - - public HttpResponse execute(HttpHost host, HttpRequest req, HttpContext ctx) - throws IOException, ClientProtocolException { - HttpResponse resp = backend.execute(host, req, ctx); - if (assessor.isFailure(resp)) { - throw new UnsuccessfulResponseException(resp); - } - return resp; - } + private ResponseFailureAssessor assessor; + + public FailureExposingHttpClient(HttpClient backend) { + super(backend); + assessor = new DefaultResponseFailureAssessor(); + } + + public FailureExposingHttpClient(HttpClient backend, ResponseFailureAssessor assessor) { + super(backend); + this.assessor = assessor; + } + + public HttpResponse execute(HttpHost host, HttpRequest req, HttpContext ctx) + throws IOException, ClientProtocolException { + HttpResponse resp = backend.execute(host, req, ctx); + if (assessor.isFailure(resp)) { + throw new UnsuccessfulResponseException(resp); + } + return resp; + } } diff --git a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/FailureHandlingHttpClient.java b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/FailureHandlingHttpClient.java index 19123169..a2a18d9f 100644 --- a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/FailureHandlingHttpClient.java +++ b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/FailureHandlingHttpClient.java @@ -27,19 +27,19 @@ public class FailureHandlingHttpClient extends AbstractHttpClientDecorator { - public FailureHandlingHttpClient(HttpClient backend) { - super(backend); - } + public FailureHandlingHttpClient(HttpClient backend) { + super(backend); + } - public HttpResponse execute(HttpHost host, HttpRequest req, HttpContext ctx) - throws IOException, ClientProtocolException { - try { - return backend.execute(host, req, ctx); - } catch (UnsuccessfulResponseException ure) { - return ure.getResponse(); - } catch (CircuitBreakerException cbe) { - throw new IOException(cbe); - } - } + public HttpResponse execute(HttpHost host, HttpRequest req, HttpContext ctx) + throws IOException, ClientProtocolException { + try { + return backend.execute(host, req, ctx); + } catch (UnsuccessfulResponseException ure) { + return ure.getResponse(); + } catch (CircuitBreakerException cbe) { + throw new IOException(cbe); + } + } } diff --git a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/PerHostServiceWrappedHttpClient.java b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/PerHostServiceWrappedHttpClient.java index d660ab20..102734d8 100644 --- a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/PerHostServiceWrappedHttpClient.java +++ b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/PerHostServiceWrappedHttpClient.java @@ -33,42 +33,42 @@ public class PerHostServiceWrappedHttpClient extends AbstractHttpClientDecorator { - private ServiceWrapperFactory factory; - private Map clients = new HashMap(); + private ServiceWrapperFactory factory; + private Map clients = new HashMap(); - public PerHostServiceWrappedHttpClient(HttpClient backend, ServiceWrapperFactory factory) { - super(backend); - this.factory = factory; - } + public PerHostServiceWrappedHttpClient(HttpClient backend, ServiceWrapperFactory factory) { + super(backend); + this.factory = factory; + } - public HttpResponse execute(HttpHost host, HttpRequest req, HttpContext ctx) - throws IOException, ClientProtocolException { - host = getCanonicalHost(host); - HttpClient client = clients.get(host); - if (client == null) { - ServiceWrapper wrapper = factory.getWrapperWithName(host.toHostString()); - client = new ServiceWrappedHttpClient(backend, wrapper); - clients.put(host, client); - } - return client.execute(host, req, ctx); - } + public HttpResponse execute(HttpHost host, HttpRequest req, HttpContext ctx) + throws IOException, ClientProtocolException { + host = getCanonicalHost(host); + HttpClient client = clients.get(host); + if (client == null) { + ServiceWrapper wrapper = factory.getWrapperWithName(host.toHostString()); + client = new ServiceWrappedHttpClient(backend, wrapper); + clients.put(host, client); + } + return client.execute(host, req, ctx); + } - private HttpHost getCanonicalHost(HttpHost host) { - URI uri; - try { - uri = new URI(host.toURI()); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - String hostname = uri.getHost(); - int port = uri.getPort(); - String scheme = uri.getScheme(); - boolean isHttps = "HTTPS".equalsIgnoreCase(scheme); - String schemePart = isHttps ? (scheme + "://") : ""; - if (port == -1) { - port = isHttps ? 443 : 80; - } - return new HttpHost(schemePart + hostname + ":" + port); - } + private HttpHost getCanonicalHost(HttpHost host) { + URI uri; + try { + uri = new URI(host.toURI()); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + String hostname = uri.getHost(); + int port = uri.getPort(); + String scheme = uri.getScheme(); + boolean isHttps = "HTTPS".equalsIgnoreCase(scheme); + String schemePart = isHttps ? (scheme + "://") : ""; + if (port == -1) { + port = isHttps ? 443 : 80; + } + return new HttpHost(schemePart + hostname + ":" + port); + } } diff --git a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/ResponseFailureAssessor.java b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/ResponseFailureAssessor.java index bd3dafba..8f2e20b2 100644 --- a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/ResponseFailureAssessor.java +++ b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/ResponseFailureAssessor.java @@ -2,19 +2,19 @@ import org.apache.http.HttpResponse; -/** A ResponseFailureAssessor is used by the {@link FailureExposingHttpClient} - * to determine whether a given response should be considered a - * "failure" or not. +/** + * A ResponseFailureAssessor is used by the {@link FailureExposingHttpClient} to + * determine whether a given response should be considered a "failure" or not. */ public interface ResponseFailureAssessor { - /** - * Returns true if the given response should be - * treated as a failure. N.B.: implementors should - * not consume the response body unless they check - * that the underlying {@link org.apache.http.HttpEntity} is - * repeatable first. - * @param response Is this HttpResponse a failure or not - * @return boolean indication of failure - */ - boolean isFailure(HttpResponse response); + /** + * Returns true if the given response should be treated as a + * failure. N.B.: implementors should not consume the response + * body unless they check that the underlying {@link org.apache.http.HttpEntity} + * is repeatable first. + * + * @param response Is this HttpResponse a failure or not + * @return boolean indication of failure + */ + boolean isFailure(HttpResponse response); } diff --git a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/ServiceWrappedHttpClient.java b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/ServiceWrappedHttpClient.java index a84e8ec3..a830ffc0 100644 --- a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/ServiceWrappedHttpClient.java +++ b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/ServiceWrappedHttpClient.java @@ -27,33 +27,32 @@ import org.fishwife.jrugged.ServiceWrapper; /** - * Facade class for assembling an HttpClient that will feed request - * executions through a ServiceWrapper, while exposing 4XX and 5XX - * responses as exceptions so that the ServiceWrapper can experience them - * as failures, but unwrapping those exceptions before getting up to - * the original caller. + * Facade class for assembling an HttpClient that will feed request executions + * through a ServiceWrapper, while exposing 4XX and 5XX responses as exceptions + * so that the ServiceWrapper can experience them as failures, but unwrapping + * those exceptions before getting up to the original caller. */ public class ServiceWrappedHttpClient extends AbstractHttpClientDecorator { - private HttpClient client; - - public ServiceWrappedHttpClient(HttpClient backend, ServiceWrapper wrapper) { - super(backend); - HttpClient client1 = new FailureExposingHttpClient(backend); - HttpClient client2 = new ServiceWrappedHttpClientDecorator(client1, wrapper); - this.client = new FailureHandlingHttpClient(client2); - } - - public ServiceWrappedHttpClient(HttpClient backend, ServiceWrapper wrapper, ResponseFailureAssessor assessor) { - super(backend); - HttpClient client1 = new FailureExposingHttpClient(backend, assessor); - HttpClient client2 = new ServiceWrappedHttpClientDecorator(client1, wrapper); - this.client = new FailureHandlingHttpClient(client2); - } - - public HttpResponse execute(HttpHost host, HttpRequest req, HttpContext ctx) - throws IOException, ClientProtocolException { - return client.execute(host, req, ctx); - } + private HttpClient client; + + public ServiceWrappedHttpClient(HttpClient backend, ServiceWrapper wrapper) { + super(backend); + HttpClient client1 = new FailureExposingHttpClient(backend); + HttpClient client2 = new ServiceWrappedHttpClientDecorator(client1, wrapper); + this.client = new FailureHandlingHttpClient(client2); + } + + public ServiceWrappedHttpClient(HttpClient backend, ServiceWrapper wrapper, ResponseFailureAssessor assessor) { + super(backend); + HttpClient client1 = new FailureExposingHttpClient(backend, assessor); + HttpClient client2 = new ServiceWrappedHttpClientDecorator(client1, wrapper); + this.client = new FailureHandlingHttpClient(client2); + } + + public HttpResponse execute(HttpHost host, HttpRequest req, HttpContext ctx) + throws IOException, ClientProtocolException { + return client.execute(host, req, ctx); + } } diff --git a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/ServiceWrappedHttpClientDecorator.java b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/ServiceWrappedHttpClientDecorator.java index 6bda4a04..3393218b 100644 --- a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/ServiceWrappedHttpClientDecorator.java +++ b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/ServiceWrappedHttpClientDecorator.java @@ -33,28 +33,28 @@ */ public class ServiceWrappedHttpClientDecorator extends AbstractHttpClientDecorator { - private ServiceWrapper wrapper; + private ServiceWrapper wrapper; - public ServiceWrappedHttpClientDecorator(HttpClient backend, ServiceWrapper wrapper) { - super(backend); - this.wrapper = wrapper; - } + public ServiceWrappedHttpClientDecorator(HttpClient backend, ServiceWrapper wrapper) { + super(backend); + this.wrapper = wrapper; + } - public HttpResponse execute(final HttpHost host, final HttpRequest req, final HttpContext ctx) - throws IOException, ClientProtocolException { - try { - return wrapper.invoke(new Callable() { - public HttpResponse call() throws Exception { - return backend.execute(host, req, ctx); - } - }); - } catch (IOException ioe) { - throw(ioe); - } catch (RuntimeException re) { - throw(re); - } catch (Exception e) { - throw new RuntimeException(e); - } - } + public HttpResponse execute(final HttpHost host, final HttpRequest req, final HttpContext ctx) + throws IOException, ClientProtocolException { + try { + return wrapper.invoke(new Callable() { + public HttpResponse call() throws Exception { + return backend.execute(host, req, ctx); + } + }); + } catch (IOException ioe) { + throw (ioe); + } catch (RuntimeException re) { + throw (re); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/UnsuccessfulResponseException.java b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/UnsuccessfulResponseException.java index 70280c7b..449c6d98 100644 --- a/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/UnsuccessfulResponseException.java +++ b/jrugged-httpclient/src/main/java/org/fishwife/jrugged/httpclient/UnsuccessfulResponseException.java @@ -19,21 +19,21 @@ import org.apache.http.HttpResponse; /** - * Used to wrap 4XX or 5XX responses from an HTTP server in exceptions - * so that {@link org.fishwife.jrugged.ServiceWrapper} instances can - * experience them as failures. + * Used to wrap 4XX or 5XX responses from an HTTP server in exceptions so that + * {@link org.fishwife.jrugged.ServiceWrapper} instances can experience them as + * failures. */ public class UnsuccessfulResponseException extends RuntimeException { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private HttpResponse response; + private HttpResponse response; - public UnsuccessfulResponseException(HttpResponse resp) { - this.response = resp; - } + public UnsuccessfulResponseException(HttpResponse resp) { + this.response = resp; + } - public HttpResponse getResponse() { - return response; - } + public HttpResponse getResponse() { + return response; + } } diff --git a/jrugged-httpclient/src/test/java/org/fishwife/jrugged/httpclient/TestAbstractHttpClientDecorator.java b/jrugged-httpclient/src/test/java/org/fishwife/jrugged/httpclient/TestAbstractHttpClientDecorator.java index e4d53d6e..bcae3419 100644 --- a/jrugged-httpclient/src/test/java/org/fishwife/jrugged/httpclient/TestAbstractHttpClientDecorator.java +++ b/jrugged-httpclient/src/test/java/org/fishwife/jrugged/httpclient/TestAbstractHttpClientDecorator.java @@ -31,34 +31,32 @@ public class TestAbstractHttpClientDecorator { - private AbstractHttpClientDecorator impl; - - @Before - public void setUp() { - impl = new AbstractHttpClientDecorator(null) { - public HttpResponse execute(HttpHost host, HttpRequest req, - HttpContext ctx) throws IOException, - ClientProtocolException { - throw new IllegalStateException("not implemented"); - } - }; - } - - @Test - public void canExtractSimpleHostProperly() { - assertEquals(new HttpHost("foo.example.com"), - impl.getHttpHost(new HttpGet("http://foo.example.com/bar"))); - } - - @Test - public void canExtractHostWithPort() { - assertEquals(new HttpHost("foo.example.com:8080"), - impl.getHttpHost(new HttpGet("http://foo.example.com:8080/bar"))); - } - - @Test - public void canExtractHttpsHostProperly() { - assertEquals(new HttpHost("https://foo.example.com:443"), - impl.getHttpHost(new HttpGet("https://foo.example.com:443/bar"))); - } + private AbstractHttpClientDecorator impl; + + @Before + public void setUp() { + impl = new AbstractHttpClientDecorator(null) { + public HttpResponse execute(HttpHost host, HttpRequest req, HttpContext ctx) + throws IOException, ClientProtocolException { + throw new IllegalStateException("not implemented"); + } + }; + } + + @Test + public void canExtractSimpleHostProperly() { + assertEquals(new HttpHost("foo.example.com"), impl.getHttpHost(new HttpGet("http://foo.example.com/bar"))); + } + + @Test + public void canExtractHostWithPort() { + assertEquals(new HttpHost("foo.example.com:8080"), + impl.getHttpHost(new HttpGet("http://foo.example.com:8080/bar"))); + } + + @Test + public void canExtractHttpsHostProperly() { + assertEquals(new HttpHost("https://foo.example.com:443"), + impl.getHttpHost(new HttpGet("https://foo.example.com:443/bar"))); + } } diff --git a/jrugged-httpclient/src/test/java/org/fishwife/jrugged/httpclient/TestFailureExposingHttpClient.java b/jrugged-httpclient/src/test/java/org/fishwife/jrugged/httpclient/TestFailureExposingHttpClient.java index bed17a6b..b68c6ea7 100644 --- a/jrugged-httpclient/src/test/java/org/fishwife/jrugged/httpclient/TestFailureExposingHttpClient.java +++ b/jrugged-httpclient/src/test/java/org/fishwife/jrugged/httpclient/TestFailureExposingHttpClient.java @@ -33,115 +33,116 @@ import org.junit.Before; import org.junit.Test; - public class TestFailureExposingHttpClient { - private FailureExposingHttpClient impl; - private StubHttpClient backend; - private HttpResponse resp; - private HttpHost host; - private HttpRequest req; - private HttpContext ctx; - - @Before - public void setUp() { - backend = new StubHttpClient(); - impl = new FailureExposingHttpClient(backend); - host = new HttpHost("foo.example.com"); - req = new HttpGet("http://foo.example.com/"); - ctx = new BasicHttpContext(); - } - - @Test - public void returns1XXResponseAsIs() throws Exception { - for(int i = 100; i <= 199; i++) { - resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, i, "1XX Thingy"); - backend.setResponse(resp); - assertSame(resp, impl.execute(host, req, ctx)); - } - } - - @Test - public void returns2XXResponseAsIs() throws Exception { - for(int i = 200; i <= 299; i++) { - resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, i, "Success"); - backend.setResponse(resp); - assertSame(resp, impl.execute(host, req, ctx)); - } - } - - @Test - public void returns3XXResponseAsIs() throws Exception { - for(int i = 300; i <= 399; i++) { - resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, i, "3XX Thingy"); - backend.setResponse(resp); - assertSame(resp, impl.execute(host, req, ctx)); - } - } - - @Test - public void generatesFailureOn4XXResponse() throws Exception { - for(int i = 400; i <= 499; i++) { - resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, i, "Client Error"); - backend.setResponse(resp); - try { - impl.execute(host, req, ctx); - fail("should have thrown exception"); - } catch (UnsuccessfulResponseException expected) { - } - } - } - - @Test - public void generatesFailureOn5XXResponse() throws Exception { - for(int i = 500; i <= 599; i++) { - resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, i, "Server Error"); - backend.setResponse(resp); - try { - impl.execute(host, req, ctx); - fail("should have thrown exception"); - } catch (UnsuccessfulResponseException expected) { - } - } - } - - @Test(expected=UnsuccessfulResponseException.class) - public void exposesFailureIfAssessorSaysTo() throws Exception { - resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); - backend.setResponse(resp); - impl = new FailureExposingHttpClient(backend, new ResponseFailureAssessor() { - public boolean isFailure(HttpResponse response) { - return true; - } - }); - impl.execute(host, req, ctx); - } - - @Test - public void doesNotExposeFailureIfAssessorSaysNotTo() throws Exception { - resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_INTERNAL_SERVER_ERROR, "Bork"); - backend.setResponse(resp); - impl = new FailureExposingHttpClient(backend, new ResponseFailureAssessor() { - public boolean isFailure(HttpResponse response) { - return false; - } - }); - HttpResponse result = impl.execute(host, req, ctx); - assertSame(result, resp); - } - - private static class StubHttpClient extends AbstractHttpClientDecorator { - private HttpResponse response; - - public StubHttpClient() { super(null); } - - public HttpResponse execute(HttpHost host, HttpRequest req, - HttpContext ctx) throws IOException, ClientProtocolException { - return response; - } - - public void setResponse(HttpResponse resp) { - this.response = resp; - } - } + private FailureExposingHttpClient impl; + private StubHttpClient backend; + private HttpResponse resp; + private HttpHost host; + private HttpRequest req; + private HttpContext ctx; + + @Before + public void setUp() { + backend = new StubHttpClient(); + impl = new FailureExposingHttpClient(backend); + host = new HttpHost("foo.example.com"); + req = new HttpGet("http://foo.example.com/"); + ctx = new BasicHttpContext(); + } + + @Test + public void returns1XXResponseAsIs() throws Exception { + for (int i = 100; i <= 199; i++) { + resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, i, "1XX Thingy"); + backend.setResponse(resp); + assertSame(resp, impl.execute(host, req, ctx)); + } + } + + @Test + public void returns2XXResponseAsIs() throws Exception { + for (int i = 200; i <= 299; i++) { + resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, i, "Success"); + backend.setResponse(resp); + assertSame(resp, impl.execute(host, req, ctx)); + } + } + + @Test + public void returns3XXResponseAsIs() throws Exception { + for (int i = 300; i <= 399; i++) { + resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, i, "3XX Thingy"); + backend.setResponse(resp); + assertSame(resp, impl.execute(host, req, ctx)); + } + } + + @Test + public void generatesFailureOn4XXResponse() throws Exception { + for (int i = 400; i <= 499; i++) { + resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, i, "Client Error"); + backend.setResponse(resp); + try { + impl.execute(host, req, ctx); + fail("should have thrown exception"); + } catch (UnsuccessfulResponseException expected) { + } + } + } + + @Test + public void generatesFailureOn5XXResponse() throws Exception { + for (int i = 500; i <= 599; i++) { + resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, i, "Server Error"); + backend.setResponse(resp); + try { + impl.execute(host, req, ctx); + fail("should have thrown exception"); + } catch (UnsuccessfulResponseException expected) { + } + } + } + + @Test(expected = UnsuccessfulResponseException.class) + public void exposesFailureIfAssessorSaysTo() throws Exception { + resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); + backend.setResponse(resp); + impl = new FailureExposingHttpClient(backend, new ResponseFailureAssessor() { + public boolean isFailure(HttpResponse response) { + return true; + } + }); + impl.execute(host, req, ctx); + } + + @Test + public void doesNotExposeFailureIfAssessorSaysNotTo() throws Exception { + resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_INTERNAL_SERVER_ERROR, "Bork"); + backend.setResponse(resp); + impl = new FailureExposingHttpClient(backend, new ResponseFailureAssessor() { + public boolean isFailure(HttpResponse response) { + return false; + } + }); + HttpResponse result = impl.execute(host, req, ctx); + assertSame(result, resp); + } + + private static class StubHttpClient extends AbstractHttpClientDecorator { + private HttpResponse response; + + public StubHttpClient() { + super(null); + } + + public HttpResponse execute(HttpHost host, HttpRequest req, HttpContext ctx) + throws IOException, ClientProtocolException { + return response; + } + + public void setResponse(HttpResponse resp) { + this.response = resp; + } + } } diff --git a/jrugged-httpclient/src/test/java/org/fishwife/jrugged/httpclient/TestFailureHandlingHttpClient.java b/jrugged-httpclient/src/test/java/org/fishwife/jrugged/httpclient/TestFailureHandlingHttpClient.java index 31c42d81..50b9402d 100644 --- a/jrugged-httpclient/src/test/java/org/fishwife/jrugged/httpclient/TestFailureHandlingHttpClient.java +++ b/jrugged-httpclient/src/test/java/org/fishwife/jrugged/httpclient/TestFailureHandlingHttpClient.java @@ -35,57 +35,53 @@ import org.junit.Before; import org.junit.Test; - public class TestFailureHandlingHttpClient { - private FailureHandlingHttpClient impl; - private HttpClient mockBackend; - private HttpResponse resp; - private HttpHost host; - private HttpRequest req; - private HttpContext ctx; + private FailureHandlingHttpClient impl; + private HttpClient mockBackend; + private HttpResponse resp; + private HttpHost host; + private HttpRequest req; + private HttpContext ctx; - @Before - public void setUp() { - mockBackend = createMock(HttpClient.class); - resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); - host = new HttpHost("foo.example.com"); - req = new HttpGet("http://foo.example.com/"); - ctx = new BasicHttpContext(); - impl = new FailureHandlingHttpClient(mockBackend); - } + @Before + public void setUp() { + mockBackend = createMock(HttpClient.class); + resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); + host = new HttpHost("foo.example.com"); + req = new HttpGet("http://foo.example.com/"); + ctx = new BasicHttpContext(); + impl = new FailureHandlingHttpClient(mockBackend); + } - @Test - public void returnsBackendResponseOnSuccess() throws Exception { - expect(mockBackend.execute(host, req, ctx)) - .andReturn(resp); - replay(mockBackend); - HttpResponse result = impl.execute(host, req, ctx); - verify(mockBackend); - assertSame(resp, result); - } + @Test + public void returnsBackendResponseOnSuccess() throws Exception { + expect(mockBackend.execute(host, req, ctx)).andReturn(resp); + replay(mockBackend); + HttpResponse result = impl.execute(host, req, ctx); + verify(mockBackend); + assertSame(resp, result); + } - @Test - public void returnsEnclosedResponseOnUnsuccessfulException() throws Exception { - Exception e = new UnsuccessfulResponseException(resp); - expect(mockBackend.execute(host, req, ctx)) - .andThrow(e); - replay(mockBackend); - HttpResponse result = impl.execute(host, req, ctx); - verify(mockBackend); - assertSame(resp, result); - } + @Test + public void returnsEnclosedResponseOnUnsuccessfulException() throws Exception { + Exception e = new UnsuccessfulResponseException(resp); + expect(mockBackend.execute(host, req, ctx)).andThrow(e); + replay(mockBackend); + HttpResponse result = impl.execute(host, req, ctx); + verify(mockBackend); + assertSame(resp, result); + } - @Test - public void throwsIOExceptionForCircuitBreakerException() throws Exception { - expect(mockBackend.execute(host, req, ctx)) - .andThrow(new CircuitBreakerException()); - replay(mockBackend); - try { - impl.execute(host, req, ctx); - fail("should have thrown exception"); - } catch (IOException expected) { - } - verify(mockBackend); - } + @Test + public void throwsIOExceptionForCircuitBreakerException() throws Exception { + expect(mockBackend.execute(host, req, ctx)).andThrow(new CircuitBreakerException()); + replay(mockBackend); + try { + impl.execute(host, req, ctx); + fail("should have thrown exception"); + } catch (IOException expected) { + } + verify(mockBackend); + } } diff --git a/jrugged-httpclient/src/test/java/org/fishwife/jrugged/httpclient/TestPerHostServiceWrappedHttpClient.java b/jrugged-httpclient/src/test/java/org/fishwife/jrugged/httpclient/TestPerHostServiceWrappedHttpClient.java index 1580c94b..2ee125ba 100644 --- a/jrugged-httpclient/src/test/java/org/fishwife/jrugged/httpclient/TestPerHostServiceWrappedHttpClient.java +++ b/jrugged-httpclient/src/test/java/org/fishwife/jrugged/httpclient/TestPerHostServiceWrappedHttpClient.java @@ -39,191 +39,196 @@ public class TestPerHostServiceWrappedHttpClient { - private PerHostServiceWrappedHttpClient impl; - private HttpClient mockBackend; - private ServiceWrapperFactory mockFactory; - private final static String HOST_STRING = "foo.example.com:8080"; - private HttpHost host; - private HttpRequest req; - private HttpContext ctx; - private HttpResponse resp; - - @Before - public void setUp() { - host = new HttpHost(HOST_STRING); - req = new HttpGet("http://foo.example.com:8080/"); - ctx = new BasicHttpContext(); - resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); - - mockBackend = createMock(HttpClient.class); - mockFactory = createMock(ServiceWrapperFactory.class); - impl = new PerHostServiceWrappedHttpClient(mockBackend, mockFactory); - } - - private void replayMocks() { - replay(mockBackend); - replay(mockFactory); - } - - private void verifyMocks() { - verify(mockBackend); - verify(mockFactory); - } - - @Test - public void isAnHttpClient() { - assertTrue(impl instanceof HttpClient); - } - - @Test - public void usesRequestHostToCreateWrapper() throws Exception { - final ServiceWrapper wrapper = new NullWrapper(); - expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) - .andReturn(resp); - expect(mockFactory.getWrapperWithName(HOST_STRING)).andReturn(wrapper); - replayMocks(); - impl.execute(host, req, ctx); - verifyMocks(); - } - - @Test - public void wiresWrapperUpToRequest() throws Exception { - final Flag f = new Flag(); - ServiceWrapper wrapper = new NullWrapper(f); - expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) - .andReturn(resp); - expect(mockFactory.getWrapperWithName(isA(String.class))).andReturn(wrapper); - replayMocks(); - impl.execute(host, req, ctx); - verifyMocks(); - assertTrue(f.set); - } - - @Test - public void reusesWrapperForRequestsFromSameHost() throws Exception { - ServiceWrapper wrapper = new NullWrapper(); - HttpUriRequest req1 = new HttpGet("http://foo.example.com/bar"); - HttpUriRequest req2 = new HttpGet("http://foo.example.com/baz"); - HttpResponse resp1 = resp; - HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); - expect(mockFactory.getWrapperWithName(isA(String.class))).andReturn(wrapper); - expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) - .andReturn(resp1); - expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) - .andReturn(resp2); - - replayMocks(); - impl.execute(req1, ctx); - impl.execute(req2, ctx); - verifyMocks(); - } - - @Test - public void reusesWrapperForRequestsFromEquivalentHostsDefaultHttpPort() throws Exception { - ServiceWrapper wrapper = new NullWrapper(); - HttpUriRequest req1 = new HttpGet("http://foo.example.com/bar"); - HttpUriRequest req2 = new HttpGet("http://foo.example.com:80/baz"); - HttpResponse resp1 = resp; - HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); - expect(mockFactory.getWrapperWithName(isA(String.class))).andReturn(wrapper); - expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) - .andReturn(resp1); - expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) - .andReturn(resp2); - - replayMocks(); - impl.execute(req1, ctx); - impl.execute(req2, ctx); - verifyMocks(); - } - - @Test - public void reusesWrapperForRequestsFromEquivalentHostsDefaultHttpsPort() throws Exception { - ServiceWrapper wrapper = new NullWrapper(); - HttpUriRequest req1 = new HttpGet("https://foo.example.com/bar"); - HttpUriRequest req2 = new HttpGet("https://foo.example.com:443/baz"); - HttpResponse resp1 = resp; - HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); - expect(mockFactory.getWrapperWithName(isA(String.class))).andReturn(wrapper); - expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) - .andReturn(resp1); - expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) - .andReturn(resp2); - - replayMocks(); - impl.execute(req1, ctx); - impl.execute(req2, ctx); - verifyMocks(); - } - - @Test - public void reusesWrapperForRequestsFromEquivalentHostsCaseInsensitiveHostName() throws Exception { - ServiceWrapper wrapper = new NullWrapper(); - HttpUriRequest req1 = new HttpGet("http://foo.example.com/bar"); - HttpUriRequest req2 = new HttpGet("http://FOO.Example.cOM/baz"); - HttpResponse resp1 = resp; - HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); - expect(mockFactory.getWrapperWithName(isA(String.class))).andReturn(wrapper); - expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) - .andReturn(resp1); - expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) - .andReturn(resp2); - - replayMocks(); - impl.execute(req1, ctx); - impl.execute(req2, ctx); - verifyMocks(); - } - - @Test - public void usesDifferentWrappersForDifferentHosts() throws Exception { - Flag f1 = new Flag(); - ServiceWrapper wrapper1 = new NullWrapper(f1); - Flag f2 = new Flag(); - ServiceWrapper wrapper2 = new NullWrapper(f2); - HttpUriRequest req1 = new HttpGet("http://foo.example.com/"); - HttpUriRequest req2 = new HttpGet("http://bar.example.com/"); - HttpResponse resp1 = resp; - HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); - expect(mockFactory.getWrapperWithName("foo.example.com:80")).andReturn(wrapper1); - expect(mockFactory.getWrapperWithName("bar.example.com:80")).andReturn(wrapper2); - expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) - .andReturn(resp1); - expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) - .andReturn(resp2); - - replayMocks(); - impl.execute(req1, ctx); - assertTrue(f1.set && !f2.set); - impl.execute(req2, ctx); - assertTrue(f1.set && f2.set); - verifyMocks(); - } - - private static class Flag { - public boolean set; - } - - private static class NullWrapper implements ServiceWrapper { - - private Flag f = new Flag(); - public NullWrapper() { } - public NullWrapper(Flag f) { this.f = f; } - - public T invoke(Callable c) throws Exception { - f.set = true; - return c.call(); - } - - public void invoke(Runnable r) throws Exception { - f.set = true; - r.run(); - } - - public T invoke(Runnable r, T result) throws Exception { - f.set = true; - r.run(); - return result; - } - } + private PerHostServiceWrappedHttpClient impl; + private HttpClient mockBackend; + private ServiceWrapperFactory mockFactory; + private final static String HOST_STRING = "foo.example.com:8080"; + private HttpHost host; + private HttpRequest req; + private HttpContext ctx; + private HttpResponse resp; + + @Before + public void setUp() { + host = new HttpHost(HOST_STRING); + req = new HttpGet("http://foo.example.com:8080/"); + ctx = new BasicHttpContext(); + resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); + + mockBackend = createMock(HttpClient.class); + mockFactory = createMock(ServiceWrapperFactory.class); + impl = new PerHostServiceWrappedHttpClient(mockBackend, mockFactory); + } + + private void replayMocks() { + replay(mockBackend); + replay(mockFactory); + } + + private void verifyMocks() { + verify(mockBackend); + verify(mockFactory); + } + + @Test + public void isAnHttpClient() { + assertTrue(impl instanceof HttpClient); + } + + @Test + public void usesRequestHostToCreateWrapper() throws Exception { + final ServiceWrapper wrapper = new NullWrapper(); + expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) + .andReturn(resp); + expect(mockFactory.getWrapperWithName(HOST_STRING)).andReturn(wrapper); + replayMocks(); + impl.execute(host, req, ctx); + verifyMocks(); + } + + @Test + public void wiresWrapperUpToRequest() throws Exception { + final Flag f = new Flag(); + ServiceWrapper wrapper = new NullWrapper(f); + expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) + .andReturn(resp); + expect(mockFactory.getWrapperWithName(isA(String.class))).andReturn(wrapper); + replayMocks(); + impl.execute(host, req, ctx); + verifyMocks(); + assertTrue(f.set); + } + + @Test + public void reusesWrapperForRequestsFromSameHost() throws Exception { + ServiceWrapper wrapper = new NullWrapper(); + HttpUriRequest req1 = new HttpGet("http://foo.example.com/bar"); + HttpUriRequest req2 = new HttpGet("http://foo.example.com/baz"); + HttpResponse resp1 = resp; + HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); + expect(mockFactory.getWrapperWithName(isA(String.class))).andReturn(wrapper); + expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) + .andReturn(resp1); + expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) + .andReturn(resp2); + + replayMocks(); + impl.execute(req1, ctx); + impl.execute(req2, ctx); + verifyMocks(); + } + + @Test + public void reusesWrapperForRequestsFromEquivalentHostsDefaultHttpPort() throws Exception { + ServiceWrapper wrapper = new NullWrapper(); + HttpUriRequest req1 = new HttpGet("http://foo.example.com/bar"); + HttpUriRequest req2 = new HttpGet("http://foo.example.com:80/baz"); + HttpResponse resp1 = resp; + HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); + expect(mockFactory.getWrapperWithName(isA(String.class))).andReturn(wrapper); + expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) + .andReturn(resp1); + expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) + .andReturn(resp2); + + replayMocks(); + impl.execute(req1, ctx); + impl.execute(req2, ctx); + verifyMocks(); + } + + @Test + public void reusesWrapperForRequestsFromEquivalentHostsDefaultHttpsPort() throws Exception { + ServiceWrapper wrapper = new NullWrapper(); + HttpUriRequest req1 = new HttpGet("https://foo.example.com/bar"); + HttpUriRequest req2 = new HttpGet("https://foo.example.com:443/baz"); + HttpResponse resp1 = resp; + HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); + expect(mockFactory.getWrapperWithName(isA(String.class))).andReturn(wrapper); + expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) + .andReturn(resp1); + expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) + .andReturn(resp2); + + replayMocks(); + impl.execute(req1, ctx); + impl.execute(req2, ctx); + verifyMocks(); + } + + @Test + public void reusesWrapperForRequestsFromEquivalentHostsCaseInsensitiveHostName() throws Exception { + ServiceWrapper wrapper = new NullWrapper(); + HttpUriRequest req1 = new HttpGet("http://foo.example.com/bar"); + HttpUriRequest req2 = new HttpGet("http://FOO.Example.cOM/baz"); + HttpResponse resp1 = resp; + HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); + expect(mockFactory.getWrapperWithName(isA(String.class))).andReturn(wrapper); + expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) + .andReturn(resp1); + expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) + .andReturn(resp2); + + replayMocks(); + impl.execute(req1, ctx); + impl.execute(req2, ctx); + verifyMocks(); + } + + @Test + public void usesDifferentWrappersForDifferentHosts() throws Exception { + Flag f1 = new Flag(); + ServiceWrapper wrapper1 = new NullWrapper(f1); + Flag f2 = new Flag(); + ServiceWrapper wrapper2 = new NullWrapper(f2); + HttpUriRequest req1 = new HttpGet("http://foo.example.com/"); + HttpUriRequest req2 = new HttpGet("http://bar.example.com/"); + HttpResponse resp1 = resp; + HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); + expect(mockFactory.getWrapperWithName("foo.example.com:80")).andReturn(wrapper1); + expect(mockFactory.getWrapperWithName("bar.example.com:80")).andReturn(wrapper2); + expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) + .andReturn(resp1); + expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpContext.class))) + .andReturn(resp2); + + replayMocks(); + impl.execute(req1, ctx); + assertTrue(f1.set && !f2.set); + impl.execute(req2, ctx); + assertTrue(f1.set && f2.set); + verifyMocks(); + } + + private static class Flag { + public boolean set; + } + + private static class NullWrapper implements ServiceWrapper { + + private Flag f = new Flag(); + + public NullWrapper() { + } + + public NullWrapper(Flag f) { + this.f = f; + } + + public T invoke(Callable c) throws Exception { + f.set = true; + return c.call(); + } + + public void invoke(Runnable r) throws Exception { + f.set = true; + r.run(); + } + + public T invoke(Runnable r, T result) throws Exception { + f.set = true; + r.run(); + return result; + } + } } diff --git a/jrugged-spring/pom.xml b/jrugged-spring/pom.xml index ef219d6e..ceca5202 100644 --- a/jrugged-spring/pom.xml +++ b/jrugged-spring/pom.xml @@ -28,10 +28,10 @@ https://github.com/Comcast/jrugged - 4.3.20.RELEASE - 18.0 + 5.1.5.RELEASE + 19.0 1.10.19 - 1.2.1 + 2.1 diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/AnnotatedMethodFilter.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/AnnotatedMethodFilter.java index b4ea9ab0..399f9b46 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/AnnotatedMethodFilter.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/AnnotatedMethodFilter.java @@ -30,27 +30,27 @@ */ public class AnnotatedMethodFilter implements TypeFilter { - private final Class annotatedClass; - - /** - * Create filter for classes with {@link java.lang.reflect.Method}s - * annotated with specified annotation. - * - * @param annotatedClass The annotated Class - */ - public AnnotatedMethodFilter(Class annotatedClass) { - this.annotatedClass = annotatedClass; - } - - /** - * {@inheritDoc} - */ - public boolean match(MetadataReader metadataReader, - MetadataReaderFactory metadataReaderFactory) throws IOException { - AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); - Set annotatedMethods = annotationMetadata - .getAnnotatedMethods(annotatedClass.getCanonicalName()); - return !annotatedMethods.isEmpty(); - } + private final Class annotatedClass; + + /** + * Create filter for classes with {@link java.lang.reflect.Method}s annotated + * with specified annotation. + * + * @param annotatedClass The annotated Class + */ + public AnnotatedMethodFilter(Class annotatedClass) { + this.annotatedClass = annotatedClass; + } + + /** + * {@inheritDoc} + */ + public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) + throws IOException { + AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); + Set annotatedMethods = annotationMetadata + .getAnnotatedMethods(annotatedClass.getCanonicalName()); + return !annotatedMethods.isEmpty(); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/AnnotatedMethodScanner.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/AnnotatedMethodScanner.java index 6cbd6fe5..44213fdf 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/AnnotatedMethodScanner.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/AnnotatedMethodScanner.java @@ -24,59 +24,59 @@ public class AnnotatedMethodScanner { - private final ClassLoader classLoader; - private final ClassPathScanningCandidateComponentProvider provider; + private final ClassLoader classLoader; + private final ClassPathScanningCandidateComponentProvider provider; - public AnnotatedMethodScanner() { - classLoader = AnnotatedMethodScanner.class.getClassLoader(); - provider = new ClassPathScanningCandidateComponentProvider(false); - } + public AnnotatedMethodScanner() { + classLoader = AnnotatedMethodScanner.class.getClassLoader(); + provider = new ClassPathScanningCandidateComponentProvider(false); + } - // package private for testing only - AnnotatedMethodScanner(ClassLoader classLoader, ClassPathScanningCandidateComponentProvider provider) { - this.classLoader = classLoader; - this.provider = provider; - } + // package private for testing only + AnnotatedMethodScanner(ClassLoader classLoader, ClassPathScanningCandidateComponentProvider provider) { + this.classLoader = classLoader; + this.provider = provider; + } - /** - * Find all methods on classes under scanBase that are annotated with annotationClass. - * - * @param scanBase Package to scan recursively, in dot notation (ie: org.jrugged...) - * @param annotationClass Class of the annotation to search for - * @return Set<Method> The set of all @{java.lang.reflect.Method}s having the annotation - */ - public Set findAnnotatedMethods(String scanBase, Class annotationClass) { - Set filteredComponents = findCandidateBeans(scanBase, annotationClass); - return extractAnnotatedMethods(filteredComponents, annotationClass); - } + /** + * Find all methods on classes under scanBase that are annotated with + * annotationClass. + * + * @param scanBase Package to scan recursively, in dot notation (ie: + * org.jrugged...) + * @param annotationClass Class of the annotation to search for + * @return Set<Method> The set of all @{java.lang.reflect.Method}s having + * the annotation + */ + public Set findAnnotatedMethods(String scanBase, Class annotationClass) { + Set filteredComponents = findCandidateBeans(scanBase, annotationClass); + return extractAnnotatedMethods(filteredComponents, annotationClass); + } - Set extractAnnotatedMethods( - Set filteredComponents, - Class annoClass) { - Set annotatedMethods = new HashSet(); - for (BeanDefinition bd : filteredComponents) { - try { - String className = bd.getBeanClassName(); - Class beanClass = classLoader.loadClass(className); - for (Method m : beanClass.getMethods()) { - if (m.getAnnotation(annoClass) != null) { - annotatedMethods.add(m); - } - } - } catch (ClassNotFoundException cnfe) { - // no-op - } - } + Set extractAnnotatedMethods(Set filteredComponents, Class annoClass) { + Set annotatedMethods = new HashSet(); + for (BeanDefinition bd : filteredComponents) { + try { + String className = bd.getBeanClassName(); + Class beanClass = classLoader.loadClass(className); + for (Method m : beanClass.getMethods()) { + if (m.getAnnotation(annoClass) != null) { + annotatedMethods.add(m); + } + } + } catch (ClassNotFoundException cnfe) { + // no-op + } + } - return annotatedMethods; - } + return annotatedMethods; + } - synchronized Set findCandidateBeans(String scanBase, - Class annotatedClass) { - provider.resetFilters(false); - provider.addIncludeFilter(new AnnotatedMethodFilter(annotatedClass)); + synchronized Set findCandidateBeans(String scanBase, Class annotatedClass) { + provider.resetFilters(false); + provider.addIncludeFilter(new AnnotatedMethodFilter(annotatedClass)); - String basePackage = scanBase.replace('.', '/'); - return provider.findCandidateComponents(basePackage); - } + String basePackage = scanBase.replace('.', '/'); + return provider.findCandidateComponents(basePackage); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/AsyncCircuitBreaker.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/AsyncCircuitBreaker.java index 96abb458..776166ce 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/AsyncCircuitBreaker.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/AsyncCircuitBreaker.java @@ -12,122 +12,136 @@ public class AsyncCircuitBreaker extends org.fishwife.jrugged.CircuitBreaker { - /** Creates a {@link AsyncCircuitBreaker} with a {@link - * DefaultFailureInterpreter} and the default "tripped" exception - * behavior (throwing a {@link CircuitBreakerException}). */ - public AsyncCircuitBreaker() { - } + /** + * Creates a {@link AsyncCircuitBreaker} with a + * {@link DefaultFailureInterpreter} and the default "tripped" exception + * behavior (throwing a {@link CircuitBreakerException}). + */ + public AsyncCircuitBreaker() { + } - /** Creates a {@link AsyncCircuitBreaker} with a {@link - * DefaultFailureInterpreter} and the default "tripped" exception - * behavior (throwing a {@link CircuitBreakerException}). - * @param name the name for the {@link AsyncCircuitBreaker}. - */ - public AsyncCircuitBreaker(String name) { - this.name = name; - } + /** + * Creates a {@link AsyncCircuitBreaker} with a + * {@link DefaultFailureInterpreter} and the default "tripped" exception + * behavior (throwing a {@link CircuitBreakerException}). + * + * @param name the name for the {@link AsyncCircuitBreaker}. + */ + public AsyncCircuitBreaker(String name) { + this.name = name; + } - /** Creates a {@link AsyncCircuitBreaker} with the specified {@link - * FailureInterpreter} and the default "tripped" exception - * behavior (throwing a {@link CircuitBreakerException}). - * @param fi the FailureInterpreter to use when - * determining whether a specific failure ought to cause the - * breaker to trip - */ - public AsyncCircuitBreaker(FailureInterpreter fi) { - failureInterpreter = fi; - } + /** + * Creates a {@link AsyncCircuitBreaker} with the specified + * {@link FailureInterpreter} and the default "tripped" exception behavior + * (throwing a {@link CircuitBreakerException}). + * + * @param fi the FailureInterpreter to use when determining whether + * a specific failure ought to cause the breaker to trip + */ + public AsyncCircuitBreaker(FailureInterpreter fi) { + failureInterpreter = fi; + } - /** Creates a {@link AsyncCircuitBreaker} with the specified {@link - * FailureInterpreter} and the default "tripped" exception - * behavior (throwing a {@link CircuitBreakerException}). - * @param name the name for the {@link AsyncCircuitBreaker}. - * @param fi the FailureInterpreter to use when - * determining whether a specific failure ought to cause the - * breaker to trip - */ - public AsyncCircuitBreaker(String name, FailureInterpreter fi) { - this.name = name; - failureInterpreter = fi; - } + /** + * Creates a {@link AsyncCircuitBreaker} with the specified + * {@link FailureInterpreter} and the default "tripped" exception behavior + * (throwing a {@link CircuitBreakerException}). + * + * @param name the name for the {@link AsyncCircuitBreaker}. + * @param fi the FailureInterpreter to use when determining + * whether a specific failure ought to cause the breaker to trip + */ + public AsyncCircuitBreaker(String name, FailureInterpreter fi) { + this.name = name; + failureInterpreter = fi; + } - /** Creates a {@link AsyncCircuitBreaker} with a {@link - * DefaultFailureInterpreter} and using the supplied {@link - * CircuitBreakerExceptionMapper} when client calls are made - * while the breaker is tripped. - * @param name the name for the {@link AsyncCircuitBreaker}. - * @param mapper helper used to translate a {@link - * CircuitBreakerException} into an application-specific one */ - public AsyncCircuitBreaker(String name, CircuitBreakerExceptionMapper mapper) { - this.name = name; - exceptionMapper = mapper; - } + /** + * Creates a {@link AsyncCircuitBreaker} with a + * {@link DefaultFailureInterpreter} and using the supplied + * {@link CircuitBreakerExceptionMapper} when client calls are made while the + * breaker is tripped. + * + * @param name the name for the {@link AsyncCircuitBreaker}. + * @param mapper helper used to translate a {@link CircuitBreakerException} into + * an application-specific one + */ + public AsyncCircuitBreaker(String name, CircuitBreakerExceptionMapper mapper) { + this.name = name; + exceptionMapper = mapper; + } - /** Creates a {@link AsyncCircuitBreaker} with the provided {@link - * FailureInterpreter} and using the provided {@link - * CircuitBreakerExceptionMapper} when client calls are made - * while the breaker is tripped. - * @param name the name for the {@link AsyncCircuitBreaker}. - * @param fi the FailureInterpreter to use when - * determining whether a specific failure ought to cause the - * breaker to trip - * @param mapper helper used to translate a {@link - * CircuitBreakerException} into an application-specific one */ - public AsyncCircuitBreaker(String name, - FailureInterpreter fi, - CircuitBreakerExceptionMapper mapper) { - this.name = name; - failureInterpreter = fi; - exceptionMapper = mapper; - } + /** + * Creates a {@link AsyncCircuitBreaker} with the provided + * {@link FailureInterpreter} and using the provided + * {@link CircuitBreakerExceptionMapper} when client calls are made while the + * breaker is tripped. + * + * @param name the name for the {@link AsyncCircuitBreaker}. + * @param fi the FailureInterpreter to use when determining + * whether a specific failure ought to cause the breaker to trip + * @param mapper helper used to translate a {@link CircuitBreakerException} into + * an application-specific one + */ + public AsyncCircuitBreaker(String name, FailureInterpreter fi, + CircuitBreakerExceptionMapper mapper) { + this.name = name; + failureInterpreter = fi; + exceptionMapper = mapper; + } - /** Wrap the given service call with the {@link AsyncCircuitBreaker} protection logic. - * @param callable the {@link java.util.concurrent.Callable} to attempt - * @param The result of a future call - * @return {@link ListenableFuture} of whatever callable would return - * @throws org.fishwife.jrugged.CircuitBreakerException if the - * breaker was OPEN or HALF_CLOSED and this attempt wasn't the - * reset attempt - * @throws Exception if the {@link org.fishwife.jrugged.CircuitBreaker} is in OPEN state - */ - public ListenableFuture invokeAsync(Callable> callable) throws Exception { + /** + * Wrap the given service call with the {@link AsyncCircuitBreaker} protection + * logic. + * + * @param callable the {@link java.util.concurrent.Callable} to attempt + * @param The result of a future call + * @return {@link ListenableFuture} of whatever callable would return + * @throws org.fishwife.jrugged.CircuitBreakerException if the breaker + * was OPEN or HALF_CLOSED and this attempt wasn't the reset + * attempt + * @throws Exception if the {@link org.fishwife.jrugged.CircuitBreaker} is in + * OPEN state + */ + public ListenableFuture invokeAsync(Callable> callable) throws Exception { - final SettableListenableFuture response = new SettableListenableFuture(); - ListenableFutureCallback callback = new ListenableFutureCallback() { - @Override - public void onSuccess(T result) { - close(); - response.set(result); - } + final SettableListenableFuture response = new SettableListenableFuture(); + ListenableFutureCallback callback = new ListenableFutureCallback() { + @Override + public void onSuccess(T result) { + close(); + response.set(result); + } - @Override - public void onFailure(Throwable ex) { - try { - handleFailure(ex); - } catch (Exception e) { - response.setException(e); - } - } - }; + @Override + public void onFailure(Throwable ex) { + try { + handleFailure(ex); + } catch (Exception e) { + response.setException(e); + } + } + }; - if (!byPass) { - if (!allowRequest()) { - throw mapException(new CircuitBreakerException()); - } + if (!byPass) { + if (!allowRequest()) { + throw mapException(new CircuitBreakerException()); + } - try { - isAttemptLive = true; - callable.call().addCallback(callback); - return response; - } catch (Throwable cause) { - // This shouldn't happen because Throwables are handled in the async onFailure callback - handleFailure(cause); - } - throw new IllegalStateException("not possible"); - } - else { - callable.call().addCallback(callback); - return response; - } - } + try { + isAttemptLive = true; + callable.call().addCallback(callback); + return response; + } catch (Throwable cause) { + // This shouldn't happen because Throwables are handled in the async onFailure + // callback + handleFailure(cause); + } + throw new IllegalStateException("not possible"); + } else { + callable.call().addCallback(callback); + return response; + } + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/CircuitBreakerBean.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/CircuitBreakerBean.java index fe6f6ce0..f85eeeda 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/CircuitBreakerBean.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/CircuitBreakerBean.java @@ -24,282 +24,290 @@ import org.springframework.jmx.export.annotation.ManagedResource; /** - * This is basically a {@link CircuitBreaker} that adds JMX - * annotations to some of the methods so that the core library - * doesn't have to depend on spring-context. + * This is basically a {@link CircuitBreaker} that adds JMX annotations to some + * of the methods so that the core library doesn't have to depend on + * spring-context. */ @ManagedResource public class CircuitBreakerBean extends CircuitBreaker implements InitializingBean { - private boolean disabledAtStart = false; + private boolean disabledAtStart = false; - /** - * {@inheritDoc} - */ - public void afterPropertiesSet() throws Exception { - if (disabledAtStart) tripHard(); - } + /** + * {@inheritDoc} + */ + public void afterPropertiesSet() throws Exception { + if (disabledAtStart) + tripHard(); + } - /** - * Creates a {@link CircuitBreakerBean} with a - * {@link DefaultFailureInterpreter} and the default "tripped" exception - * behavior (throwing a {@link org.fishwife.jrugged.CircuitBreakerException}). - */ - public CircuitBreakerBean() { super(); } + /** + * Creates a {@link CircuitBreakerBean} with a {@link DefaultFailureInterpreter} + * and the default "tripped" exception behavior (throwing a + * {@link org.fishwife.jrugged.CircuitBreakerException}). + */ + public CircuitBreakerBean() { + super(); + } - /** - * Creates a {@link CircuitBreakerBean} with a - * {@link DefaultFailureInterpreter} and the default "tripped" exception - * behavior (throwing a {@link org.fishwife.jrugged.CircuitBreakerException}). - * @param name the name for the {@link CircuitBreakerBean} - */ - public CircuitBreakerBean(String name) { super(name); } + /** + * Creates a {@link CircuitBreakerBean} with a {@link DefaultFailureInterpreter} + * and the default "tripped" exception behavior (throwing a + * {@link org.fishwife.jrugged.CircuitBreakerException}). + * + * @param name the name for the {@link CircuitBreakerBean} + */ + public CircuitBreakerBean(String name) { + super(name); + } - /** - * Creates a {@link CircuitBreakerBean} with the specified - * {@link FailureInterpreter} and the default "tripped" exception - * behavior (throwing a {@link org.fishwife.jrugged.CircuitBreakerException}). - * @param fi the FailureInterpreter to use when - * determining whether a specific failure ought to cause the - * breaker to trip - */ - public CircuitBreakerBean(FailureInterpreter fi) { - super(fi); - } + /** + * Creates a {@link CircuitBreakerBean} with the specified + * {@link FailureInterpreter} and the default "tripped" exception behavior + * (throwing a {@link org.fishwife.jrugged.CircuitBreakerException}). + * + * @param fi the FailureInterpreter to use when determining whether + * a specific failure ought to cause the breaker to trip + */ + public CircuitBreakerBean(FailureInterpreter fi) { + super(fi); + } - /** - * Creates a {@link CircuitBreakerBean} with the specified - * {@link FailureInterpreter} and the default "tripped" exception - * behavior (throwing a {@link org.fishwife.jrugged.CircuitBreakerException}). - * @param name the name for the {@link CircuitBreakerBean} - * @param fi the FailureInterpreter to use when - * determining whether a specific failure ought to cause the - * breaker to trip - */ - public CircuitBreakerBean(String name, FailureInterpreter fi) { - super(name, fi); - } + /** + * Creates a {@link CircuitBreakerBean} with the specified + * {@link FailureInterpreter} and the default "tripped" exception behavior + * (throwing a {@link org.fishwife.jrugged.CircuitBreakerException}). + * + * @param name the name for the {@link CircuitBreakerBean} + * @param fi the FailureInterpreter to use when determining + * whether a specific failure ought to cause the breaker to trip + */ + public CircuitBreakerBean(String name, FailureInterpreter fi) { + super(name, fi); + } - /** - * Creates a {@link CircuitBreaker} with a {@link - * DefaultFailureInterpreter} and using the supplied {@link - * CircuitBreakerExceptionMapper} when client calls are made - * while the breaker is tripped. - * @param name the name for the {@link CircuitBreakerBean} - * @param mapper helper used to translate a {@link - * org.fishwife.jrugged.CircuitBreakerException} into an application-specific one - */ - public CircuitBreakerBean(String name, CircuitBreakerExceptionMapper mapper) { - super(name, mapper); - } + /** + * Creates a {@link CircuitBreaker} with a {@link DefaultFailureInterpreter} and + * using the supplied {@link CircuitBreakerExceptionMapper} when client calls + * are made while the breaker is tripped. + * + * @param name the name for the {@link CircuitBreakerBean} + * @param mapper helper used to translate a + * {@link org.fishwife.jrugged.CircuitBreakerException} into an + * application-specific one + */ + public CircuitBreakerBean(String name, CircuitBreakerExceptionMapper mapper) { + super(name, mapper); + } - /** - * Creates a {@link CircuitBreaker} with the provided {@link - * FailureInterpreter} and using the provided {@link - * CircuitBreakerExceptionMapper} when client calls are made - * while the breaker is tripped. - * - * @param name the name for the {@link CircuitBreakerBean} - * @param fi the FailureInterpreter to use when - * determining whether a specific failure ought to cause the - * breaker to trip - * @param mapper helper used to translate a {@link - * org.fishwife.jrugged.CircuitBreakerException} into an application-specific one - */ - public CircuitBreakerBean(String name, FailureInterpreter fi, - CircuitBreakerExceptionMapper mapper) { - super(name, fi, mapper); - } + /** + * Creates a {@link CircuitBreaker} with the provided {@link FailureInterpreter} + * and using the provided {@link CircuitBreakerExceptionMapper} when client + * calls are made while the breaker is tripped. + * + * @param name the name for the {@link CircuitBreakerBean} + * @param fi the FailureInterpreter to use when determining + * whether a specific failure ought to cause the breaker to trip + * @param mapper helper used to translate a + * {@link org.fishwife.jrugged.CircuitBreakerException} into an + * application-specific one + */ + public CircuitBreakerBean(String name, FailureInterpreter fi, + CircuitBreakerExceptionMapper mapper) { + super(name, fi, mapper); + } - /** - * Manually trips the CircuitBreaker until {@link #reset()} is invoked. - */ - @ManagedOperation - @Override - public void tripHard() { - super.tripHard(); - } + /** + * Manually trips the CircuitBreaker until {@link #reset()} is invoked. + */ + @ManagedOperation + @Override + public void tripHard() { + super.tripHard(); + } - /** - * Manually trips the CircuitBreaker until {@link #reset()} is invoked. - */ - @ManagedOperation - @Override - public void trip() { - super.trip(); - } + /** + * Manually trips the CircuitBreaker until {@link #reset()} is invoked. + */ + @ManagedOperation + @Override + public void trip() { + super.trip(); + } - /** - * Returns the last time the breaker tripped OPEN, measured in - * milliseconds since the Epoch. - * @return long the last failure time - */ - @ManagedAttribute - @Override - public long getLastTripTime() { - return super.getLastTripTime(); - } + /** + * Returns the last time the breaker tripped OPEN, measured in milliseconds + * since the Epoch. + * + * @return long the last failure time + */ + @ManagedAttribute + @Override + public long getLastTripTime() { + return super.getLastTripTime(); + } - /** - * Returns the number of times the breaker has tripped OPEN during - * its lifetime. - * @return long the number of times the circuit breaker tripped - */ - @ManagedAttribute - @Override - public long getTripCount() { - return super.getTripCount(); - } + /** + * Returns the number of times the breaker has tripped OPEN during its lifetime. + * + * @return long the number of times the circuit breaker tripped + */ + @ManagedAttribute + @Override + public long getTripCount() { + return super.getTripCount(); + } - /** - * When called with true - causes the {@link CircuitBreaker} to byPass - * its functionality allowing requests to be executed unmolested - * until the CircuitBreaker is reset or the byPass - * is manually set to false. - */ - @ManagedAttribute - @Override - public void setByPassState(boolean b) { - super.setByPassState(b); - } + /** + * When called with true - causes the {@link CircuitBreaker} to byPass its + * functionality allowing requests to be executed unmolested until the + * CircuitBreaker is reset or the byPass is manually set to false. + */ + @ManagedAttribute + @Override + public void setByPassState(boolean b) { + super.setByPassState(b); + } - /** - * Get the current state of the {@link CircuitBreaker} byPass - * - * @return boolean the byPass flag's current value - */ - @ManagedAttribute - @Override - public boolean getByPassState() { - return super.getByPassState(); - } + /** + * Get the current state of the {@link CircuitBreaker} byPass + * + * @return boolean the byPass flag's current value + */ + @ManagedAttribute + @Override + public boolean getByPassState() { + return super.getByPassState(); + } - /** - * Manually set the breaker to be reset and ready for use. This - * is only useful after a manual trip otherwise the breaker will - * trip automatically again if the service is still unavailable. - * Just like a real breaker. WOOT!!! - */ - @ManagedOperation - @Override - public void reset() { - super.reset(); - } + /** + * Manually set the breaker to be reset and ready for use. This is only useful + * after a manual trip otherwise the breaker will trip automatically again if + * the service is still unavailable. Just like a real breaker. WOOT!!! + */ + @ManagedOperation + @Override + public void reset() { + super.reset(); + } - /** - * Returns the cooldown period in milliseconds. - * - * @return long - */ - @ManagedAttribute - @Override - public long getResetMillis() { - return super.getResetMillis(); - } + /** + * Returns the cooldown period in milliseconds. + * + * @return long + */ + @ManagedAttribute + @Override + public long getResetMillis() { + return super.getResetMillis(); + } - /** Sets the reset period to the given number of milliseconds. The - * default is 15,000 (make one retry attempt every 15 seconds). - * - * @param l number of milliseconds to "cool down" after tripping - * before allowing a "test request" through again - */ - @ManagedAttribute - @Override - public void setResetMillis(long l) { - super.setResetMillis(l); - } + /** + * Sets the reset period to the given number of milliseconds. The default is + * 15,000 (make one retry attempt every 15 seconds). + * + * @param l number of milliseconds to "cool down" after tripping before allowing + * a "test request" through again + */ + @ManagedAttribute + @Override + public void setResetMillis(long l) { + super.setResetMillis(l); + } - /** - * Returns a {@link String} representation of the breaker's - * status; potentially useful for exposing to monitoring software. - * - * @return String which is "GREEN" if - * the breaker is CLOSED; "YELLOW" if the breaker - * is HALF_CLOSED; and "RED" if the breaker is - * OPEN (tripped). - */ - @ManagedAttribute - @Override - public String getHealthCheck() { return super.getHealthCheck(); } + /** + * Returns a {@link String} representation of the breaker's status; potentially + * useful for exposing to monitoring software. + * + * @return String which is "GREEN" if the breaker is + * CLOSED; "YELLOW" if the breaker is HALF_CLOSED; and + * "RED" if the breaker is OPEN (tripped). + */ + @ManagedAttribute + @Override + public String getHealthCheck() { + return super.getHealthCheck(); + } - /** - * Gets the failure tolerance limit for the {@link DefaultFailureInterpreter} that - * comes with a {@link CircuitBreaker} by default. - * - * @see DefaultFailureInterpreter - * - * @return the number of tolerated failures in a window - */ - @ManagedAttribute - public int getLimit() { - return ((DefaultFailureInterpreter) super.getFailureInterpreter()).getLimit(); - } + /** + * Gets the failure tolerance limit for the {@link DefaultFailureInterpreter} + * that comes with a {@link CircuitBreaker} by default. + * + * @see DefaultFailureInterpreter + * + * @return the number of tolerated failures in a window + */ + @ManagedAttribute + public int getLimit() { + return ((DefaultFailureInterpreter) super.getFailureInterpreter()).getLimit(); + } - /** - * Specifies the failure tolerance limit for the {@link - * DefaultFailureInterpreter} that comes with a {@link - * CircuitBreaker} by default. - * - * @see DefaultFailureInterpreter - * - * @param limit the number of tolerated failures in a window - */ - @ManagedAttribute - @Override - public void setLimit(int limit) { - ((DefaultFailureInterpreter) super.getFailureInterpreter()).setLimit(limit); - } + /** + * Specifies the failure tolerance limit for the + * {@link DefaultFailureInterpreter} that comes with a {@link CircuitBreaker} by + * default. + * + * @see DefaultFailureInterpreter + * + * @param limit the number of tolerated failures in a window + */ + @ManagedAttribute + @Override + public void setLimit(int limit) { + ((DefaultFailureInterpreter) super.getFailureInterpreter()).setLimit(limit); + } - /** - * Gets the tolerance window in milliseconds for the {@link DefaultFailureInterpreter} - * that comes with a {@link CircuitBreaker} by default. - * - * @see DefaultFailureInterpreter - * - * @return length of the window in milliseconds - */ - @ManagedAttribute - public long getWindowMillis() { - return ((DefaultFailureInterpreter) super.getFailureInterpreter()).getWindowMillis(); - } + /** + * Gets the tolerance window in milliseconds for the + * {@link DefaultFailureInterpreter} that comes with a {@link CircuitBreaker} by + * default. + * + * @see DefaultFailureInterpreter + * + * @return length of the window in milliseconds + */ + @ManagedAttribute + public long getWindowMillis() { + return ((DefaultFailureInterpreter) super.getFailureInterpreter()).getWindowMillis(); + } - /** - * Specifies the tolerance window in milliseconds for the {@link - * DefaultFailureInterpreter} that comes with a {@link - * CircuitBreaker} by default. - * - * @see DefaultFailureInterpreter - * - * @param windowMillis length of the window in milliseconds - */ - @ManagedAttribute - @Override - public void setWindowMillis(long windowMillis) { - super.setWindowMillis(windowMillis); - } + /** + * Specifies the tolerance window in milliseconds for the + * {@link DefaultFailureInterpreter} that comes with a {@link CircuitBreaker} by + * default. + * + * @see DefaultFailureInterpreter + * + * @param windowMillis length of the window in milliseconds + */ + @ManagedAttribute + @Override + public void setWindowMillis(long windowMillis) { + super.setWindowMillis(windowMillis); + } - /** - * returns a {@link String} representation of the breaker's - * last known exception that caused it to OPEN (i.e. when the breaker - * opens, it will record the specific exception that caused it to open) - * - * @return String which is the full stack trace. - */ - @ManagedAttribute - @Override - public String getTripExceptionAsString() { - return super.getTripExceptionAsString(); - } + /** + * returns a {@link String} representation of the breaker's last known exception + * that caused it to OPEN (i.e. when the breaker opens, it will record the + * specific exception that caused it to open) + * + * @return String which is the full stack trace. + */ + @ManagedAttribute + @Override + public String getTripExceptionAsString() { + return super.getTripExceptionAsString(); + } - /** - * Specifies whether the associated CircuitBreaker should be tripped - * at startup time. - * - * @param b true if the CircuitBreaker should start - * open (tripped); false if the CircuitBreaker should start - * closed (not tripped). - */ - public void setDisabled(boolean b) { - disabledAtStart = b; - } + /** + * Specifies whether the associated CircuitBreaker should be tripped at startup + * time. + * + * @param b true if the CircuitBreaker should start open (tripped); + * false if the CircuitBreaker should start closed (not + * tripped). + */ + public void setDisabled(boolean b) { + disabledAtStart = b; + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/CircuitBreakerBeanFactory.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/CircuitBreakerBeanFactory.java index b1df6c46..0a5f4baa 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/CircuitBreakerBeanFactory.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/CircuitBreakerBeanFactory.java @@ -30,119 +30,127 @@ /** * Factory to create new {@link CircuitBreakerBean} instances and keep track of - * them. If a {@link MBeanExportOperations} is set, then the CircuitBreakerBean will be - * automatically exported as a JMX MBean. + * them. If a {@link MBeanExportOperations} is set, then the CircuitBreakerBean + * will be automatically exported as a JMX MBean. */ public class CircuitBreakerBeanFactory extends CircuitBreakerFactory implements InitializingBean { - @Autowired(required = false) - private MBeanExportOperations mBeanExportOperations; - - private String packageScanBase; - - /** - * {@inheritDoc} - */ - public void afterPropertiesSet() { - buildAnnotatedCircuitBreakers(); - } - - /** - * Set the {@link MBeanExporter} to use to export {@link CircuitBreakerBean} - * instances as JMX MBeans. - * @param mBeanExporter the {@link MBeanExporter} to set. - */ - @Deprecated - public void setMBeanExporter(MBeanExporter mBeanExporter) { - setMBeanExportOperations(mBeanExporter); - } - - /** - * Set the {@link MBeanExportOperations} to use to export {@link CircuitBreakerBean} - * instances as JMX MBeans. - * @param mBeanExportOperations the {@link MBeanExportOperations} to set. - */ - public void setMBeanExportOperations(MBeanExportOperations mBeanExportOperations) { - this.mBeanExportOperations = mBeanExportOperations; - } - - /** - * If specified, CircuitBreakerBeanFactory will scan all classes - * under packageScanBase for methods with the - * {@link org.fishwife.jrugged.aspects.CircuitBreaker} annotation - * and initialize circuitbreakers for them. - * - * @param packageScanBase Where should the scan for annotations begin - */ - public void setPackageScanBase(String packageScanBase) { - this.packageScanBase = packageScanBase; - } - - /** - * If packageScanBase is defined will search packages for {@link org.fishwife.jrugged.aspects.CircuitBreaker} - * annotations and create circuitbreakers for them. - */ - public void buildAnnotatedCircuitBreakers() { - if (packageScanBase != null) { - AnnotatedMethodScanner methodScanner = new AnnotatedMethodScanner(); - for (Method m : methodScanner.findAnnotatedMethods(packageScanBase, org.fishwife.jrugged.aspects.CircuitBreaker.class)) { - org.fishwife.jrugged.aspects.CircuitBreaker circuitBreakerAnnotation = m.getAnnotation(org.fishwife.jrugged.aspects.CircuitBreaker.class); - DefaultFailureInterpreter dfi = new DefaultFailureInterpreter(circuitBreakerAnnotation.ignore(), circuitBreakerAnnotation.limit(), circuitBreakerAnnotation.windowMillis()); - CircuitBreakerConfig config = new CircuitBreakerConfig(circuitBreakerAnnotation.resetMillis(), dfi); - createCircuitBreaker(circuitBreakerAnnotation.name(), config); - } - } - - } - - /** - * Create a new {@link CircuitBreakerBean} and map it to the provided value. - * If the {@link MBeanExportOperations} is set, then the CircuitBreakerBean will be - * exported as a JMX MBean. - * If the CircuitBreaker already exists, then the existing instance is - * returned. - * @param name the value for the {@link org.fishwife.jrugged.CircuitBreaker} - * @param config the {@link org.fishwife.jrugged.CircuitBreakerConfig} - */ - public synchronized CircuitBreaker createCircuitBreaker(String name, CircuitBreakerConfig config) { - - CircuitBreaker circuitBreaker = findCircuitBreaker(name); - - if (circuitBreaker == null) { - circuitBreaker = new CircuitBreakerBean(name); - - configureCircuitBreaker(name, circuitBreaker, config); - - if (mBeanExportOperations != null) { - ObjectName objectName; - - try { - objectName = new ObjectName("org.fishwife.jrugged.spring:type=CircuitBreakerBean," + "name=" + name); - } catch (MalformedObjectNameException e) { - throw new IllegalArgumentException("Invalid MBean Name " + name, e); - - } - - mBeanExportOperations.registerManagedResource(circuitBreaker, objectName); - } - - addCircuitBreakerToMap(name, circuitBreaker); - } - - return circuitBreaker; - } - - /** - * Find an existing {@link CircuitBreakerBean} - * @param name the value for the {@link CircuitBreakerBean} - * @return the found {@link CircuitBreakerBean}, or null if it is not found. - */ - public CircuitBreakerBean findCircuitBreakerBean(String name) { - CircuitBreaker circuitBreaker = findCircuitBreaker(name); - - if (circuitBreaker instanceof CircuitBreakerBean) { - return (CircuitBreakerBean) circuitBreaker; - } - return null; - } + @Autowired(required = false) + private MBeanExportOperations mBeanExportOperations; + + private String packageScanBase; + + /** + * {@inheritDoc} + */ + public void afterPropertiesSet() { + buildAnnotatedCircuitBreakers(); + } + + /** + * Set the {@link MBeanExporter} to use to export {@link CircuitBreakerBean} + * instances as JMX MBeans. + * + * @param mBeanExporter the {@link MBeanExporter} to set. + */ + @Deprecated + public void setMBeanExporter(MBeanExporter mBeanExporter) { + setMBeanExportOperations(mBeanExporter); + } + + /** + * Set the {@link MBeanExportOperations} to use to export + * {@link CircuitBreakerBean} instances as JMX MBeans. + * + * @param mBeanExportOperations the {@link MBeanExportOperations} to set. + */ + public void setMBeanExportOperations(MBeanExportOperations mBeanExportOperations) { + this.mBeanExportOperations = mBeanExportOperations; + } + + /** + * If specified, CircuitBreakerBeanFactory will scan all classes under + * packageScanBase for methods with the + * {@link org.fishwife.jrugged.aspects.CircuitBreaker} annotation and initialize + * circuitbreakers for them. + * + * @param packageScanBase Where should the scan for annotations begin + */ + public void setPackageScanBase(String packageScanBase) { + this.packageScanBase = packageScanBase; + } + + /** + * If packageScanBase is defined will search packages for + * {@link org.fishwife.jrugged.aspects.CircuitBreaker} annotations and create + * circuitbreakers for them. + */ + public void buildAnnotatedCircuitBreakers() { + if (packageScanBase != null) { + AnnotatedMethodScanner methodScanner = new AnnotatedMethodScanner(); + for (Method m : methodScanner.findAnnotatedMethods(packageScanBase, + org.fishwife.jrugged.aspects.CircuitBreaker.class)) { + org.fishwife.jrugged.aspects.CircuitBreaker circuitBreakerAnnotation = m + .getAnnotation(org.fishwife.jrugged.aspects.CircuitBreaker.class); + DefaultFailureInterpreter dfi = new DefaultFailureInterpreter(circuitBreakerAnnotation.ignore(), + circuitBreakerAnnotation.limit(), circuitBreakerAnnotation.windowMillis()); + CircuitBreakerConfig config = new CircuitBreakerConfig(circuitBreakerAnnotation.resetMillis(), dfi); + createCircuitBreaker(circuitBreakerAnnotation.name(), config); + } + } + + } + + /** + * Create a new {@link CircuitBreakerBean} and map it to the provided value. If + * the {@link MBeanExportOperations} is set, then the CircuitBreakerBean will be + * exported as a JMX MBean. If the CircuitBreaker already exists, then the + * existing instance is returned. + * + * @param name the value for the {@link org.fishwife.jrugged.CircuitBreaker} + * @param config the {@link org.fishwife.jrugged.CircuitBreakerConfig} + */ + public synchronized CircuitBreaker createCircuitBreaker(String name, CircuitBreakerConfig config) { + + CircuitBreaker circuitBreaker = findCircuitBreaker(name); + + if (circuitBreaker == null) { + circuitBreaker = new CircuitBreakerBean(name); + + configureCircuitBreaker(name, circuitBreaker, config); + + if (mBeanExportOperations != null) { + ObjectName objectName; + + try { + objectName = new ObjectName( + "org.fishwife.jrugged.spring:type=CircuitBreakerBean," + "name=" + name); + } catch (MalformedObjectNameException e) { + throw new IllegalArgumentException("Invalid MBean Name " + name, e); + + } + + mBeanExportOperations.registerManagedResource(circuitBreaker, objectName); + } + + addCircuitBreakerToMap(name, circuitBreaker); + } + + return circuitBreaker; + } + + /** + * Find an existing {@link CircuitBreakerBean} + * + * @param name the value for the {@link CircuitBreakerBean} + * @return the found {@link CircuitBreakerBean}, or null if it is not found. + */ + public CircuitBreakerBean findCircuitBreakerBean(String name) { + CircuitBreaker circuitBreaker = findCircuitBreaker(name); + + if (circuitBreaker instanceof CircuitBreakerBean) { + return (CircuitBreakerBean) circuitBreaker; + } + return null; + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/FlowRegulatorBean.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/FlowRegulatorBean.java index 8c28e9ac..e2b3f43a 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/FlowRegulatorBean.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/FlowRegulatorBean.java @@ -20,25 +20,25 @@ import org.springframework.jmx.export.annotation.ManagedAttribute; public class FlowRegulatorBean extends ConstantFlowRegulator { - public FlowRegulatorBean() { - super(); - } + public FlowRegulatorBean() { + super(); + } - @ManagedAttribute - @Override - /** - * {@inheritDoc} - */ - public void setRequestPerSecondThreshold(int i) { - super.setRequestPerSecondThreshold(i); - } + @ManagedAttribute + @Override + /** + * {@inheritDoc} + */ + public void setRequestPerSecondThreshold(int i) { + super.setRequestPerSecondThreshold(i); + } - @ManagedAttribute - @Override - /** - * {@inheritDoc} - */ - public int getRequestPerSecondThreshold() { - return super.getRequestPerSecondThreshold(); - } + @ManagedAttribute + @Override + /** + * {@inheritDoc} + */ + public int getRequestPerSecondThreshold() { + return super.getRequestPerSecondThreshold(); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/PerformanceMonitorBean.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/PerformanceMonitorBean.java index 355d46df..165a0cbd 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/PerformanceMonitorBean.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/PerformanceMonitorBean.java @@ -20,297 +20,298 @@ import org.springframework.jmx.export.annotation.ManagedAttribute; import org.springframework.jmx.export.annotation.ManagedResource; -/** The {@link PerformanceMonitorBean} is a straightforward wrapper - * around a {@link PerformanceMonitor} that allows for leveraging - * automated exposure of the information via Spring's JMX annotations. +/** + * The {@link PerformanceMonitorBean} is a straightforward wrapper around a + * {@link PerformanceMonitor} that allows for leveraging automated exposure of + * the information via Spring's JMX annotations. */ @ManagedResource public class PerformanceMonitorBean extends PerformanceMonitor { - /** Constructs a PerformanceMonitorBean. */ - public PerformanceMonitorBean() { - super(); - } - - @ManagedAttribute - @Override - public double getAverageSuccessLatencyLastMinute() { - return super.getAverageSuccessLatencyLastMinute(); - } - - @ManagedAttribute - @Override - public double getAverageSuccessLatencyLastHour() { - return super.getAverageSuccessLatencyLastHour(); - } - - @ManagedAttribute - @Override - public double getAverageSuccessLatencyLastDay() { - return super.getAverageSuccessLatencyLastDay(); - } - - @ManagedAttribute - @Override - public double getAverageFailureLatencyLastMinute() { - return super.getAverageFailureLatencyLastMinute(); - } - - @ManagedAttribute - @Override - public double getAverageFailureLatencyLastHour() { - return super.getAverageFailureLatencyLastHour(); - } - - @ManagedAttribute - @Override - public double getAverageFailureLatencyLastDay() { - return super.getAverageFailureLatencyLastDay(); - } - - @ManagedAttribute - @Override - public double getTotalRequestsPerSecondLastMinute() { - return super.getTotalRequestsPerSecondLastMinute(); - } - - @ManagedAttribute - @Override - public double getSuccessRequestsPerSecondLastMinute() { - return super.getSuccessRequestsPerSecondLastMinute(); - } - - @ManagedAttribute - @Override - public double getFailureRequestsPerSecondLastMinute() { - return super.getFailureRequestsPerSecondLastMinute(); - } - - @ManagedAttribute - @Override - public double getTotalRequestsPerSecondLastHour() { - return super.getTotalRequestsPerSecondLastHour(); - } - - @ManagedAttribute - @Override - public double getSuccessRequestsPerSecondLastHour() { - return super.getSuccessRequestsPerSecondLastHour(); - } - - @ManagedAttribute - @Override - public double getFailureRequestsPerSecondLastHour() { - return super.getFailureRequestsPerSecondLastHour(); - } - - @ManagedAttribute - @Override - public double getTotalRequestsPerSecondLastDay() { - return super.getTotalRequestsPerSecondLastDay(); - } - - @ManagedAttribute - @Override - public double getSuccessRequestsPerSecondLastDay() { - return super.getSuccessRequestsPerSecondLastDay(); - } - - @ManagedAttribute - @Override - public double getFailureRequestsPerSecondLastDay() { - return super.getFailureRequestsPerSecondLastDay(); - } - - @ManagedAttribute - @Override - public double getTotalRequestsPerSecondLifetime() { - return super.getTotalRequestsPerSecondLifetime(); - } - - @ManagedAttribute - @Override - public double getSuccessRequestsPerSecondLifetime() { - return super.getSuccessRequestsPerSecondLifetime(); - } - - @ManagedAttribute - @Override - public double getFailureRequestsPerSecondLifetime() { - return super.getFailureRequestsPerSecondLifetime(); - } - - @ManagedAttribute - @Override - public long getRequestCount() { - return super.getRequestCount(); - } - - @ManagedAttribute - @Override - public long getSuccessCount() { - return super.getSuccessCount(); - } - - @ManagedAttribute - @Override - public long getFailureCount() { - return super.getFailureCount(); - } - - @ManagedAttribute - @Override - public long getMedianPercentileSuccessLatencyLifetime() { - return super.getMedianPercentileSuccessLatencyLifetime(); - } - - @ManagedAttribute - @Override - public long get95thPercentileSuccessLatencyLifetime() { - return super.get95thPercentileSuccessLatencyLifetime(); - } - - @ManagedAttribute - @Override - public long get99thPercentileSuccessLatencyLifetime() { - return super.get99thPercentileSuccessLatencyLifetime(); - } - - @ManagedAttribute - @Override - public long getMaxSuccessLatencyLifetime() { - return super.getMaxSuccessLatencyLifetime(); - } - - @ManagedAttribute - @Override - public long getMedianPercentileSuccessLatencyLastMinute() { - return super.getMedianPercentileSuccessLatencyLastMinute(); - } - - @ManagedAttribute - @Override - public long get95thPercentileSuccessLatencyLastMinute() { - return super.get95thPercentileSuccessLatencyLastMinute(); - } - - @ManagedAttribute - @Override - public long get99thPercentileSuccessLatencyLastMinute() { - return super.get99thPercentileSuccessLatencyLastMinute(); - } - - @ManagedAttribute - @Override - public long getMedianPercentileSuccessfulLatencyLastHour() { - return super.getMedianPercentileSuccessfulLatencyLastHour(); - } - - @ManagedAttribute - @Override - public long get95thPercentileSuccessLatencyLastHour() { - return super.get95thPercentileSuccessLatencyLastHour(); - } - - @ManagedAttribute - @Override - public long get99thPercentileSuccessLatencyLastHour() { - return super.get99thPercentileSuccessLatencyLastHour(); - } - - @ManagedAttribute - @Override - public long getMedianPercentileSuccessLatencyLastDay() { - return super.getMedianPercentileSuccessLatencyLastDay(); - } - - @ManagedAttribute - @Override - public long get95thPercentileSuccessLatencyLastDay() { - return super.get95thPercentileSuccessLatencyLastDay(); - } - - @ManagedAttribute - @Override - public long get99thPercentileSuccessLatencyLastDay() { - return super.get99thPercentileSuccessLatencyLastDay(); - } - - @ManagedAttribute - @Override - public long getMedianPercentileFailureLatencyLifetime() { - return super.getMedianPercentileFailureLatencyLifetime(); - } - - @ManagedAttribute - @Override - public long get95thPercentileFailureLatencyLifetime() { - return super.get95thPercentileFailureLatencyLifetime(); - } - - @ManagedAttribute - @Override - public long get99thPercentileFailureLatencyLifetime() { - return super.get99thPercentileFailureLatencyLifetime(); - } - - @ManagedAttribute - @Override - public long getMaxFailureLatencyLifetime() { - return super.getMaxFailureLatencyLifetime(); - } - - @ManagedAttribute - @Override - public long getMedianPercentileFailureLatencyLastMinute() { - return super.getMedianPercentileFailureLatencyLastMinute(); - } - - @ManagedAttribute - @Override - public long get95thPercentileFailureLatencyLastMinute() { - return super.get95thPercentileFailureLatencyLastMinute(); - } - - @ManagedAttribute - @Override - public long get99thPercentileFailureLatencyLastMinute() { - return super.get99thPercentileFailureLatencyLastMinute(); - } - - @ManagedAttribute - @Override - public long getMedianPercentileFailureLatencyLastHour() { - return super.getMedianPercentileFailureLatencyLastHour(); - } - - @ManagedAttribute - @Override - public long get95thPercentileFailureLatencyLastHour() { - return super.get95thPercentileFailureLatencyLastHour(); - } - - @ManagedAttribute - @Override - public long get99thPercentileFailureLatencyLastHour() { - return super.get99thPercentileFailureLatencyLastHour(); - } - - @ManagedAttribute - @Override - public long getMedianPercentileFailureLatencyLastDay() { - return super.getMedianPercentileFailureLatencyLastDay(); - } - - @ManagedAttribute - @Override - public long get95thPercentileFailureLatencyLastDay() { - return super.get95thPercentileFailureLatencyLastDay(); - } - - @ManagedAttribute - @Override - public long get99thPercentileFailureLatencyLastDay() { - return super.get99thPercentileFailureLatencyLastDay(); - } + /** Constructs a PerformanceMonitorBean. */ + public PerformanceMonitorBean() { + super(); + } + + @ManagedAttribute + @Override + public double getAverageSuccessLatencyLastMinute() { + return super.getAverageSuccessLatencyLastMinute(); + } + + @ManagedAttribute + @Override + public double getAverageSuccessLatencyLastHour() { + return super.getAverageSuccessLatencyLastHour(); + } + + @ManagedAttribute + @Override + public double getAverageSuccessLatencyLastDay() { + return super.getAverageSuccessLatencyLastDay(); + } + + @ManagedAttribute + @Override + public double getAverageFailureLatencyLastMinute() { + return super.getAverageFailureLatencyLastMinute(); + } + + @ManagedAttribute + @Override + public double getAverageFailureLatencyLastHour() { + return super.getAverageFailureLatencyLastHour(); + } + + @ManagedAttribute + @Override + public double getAverageFailureLatencyLastDay() { + return super.getAverageFailureLatencyLastDay(); + } + + @ManagedAttribute + @Override + public double getTotalRequestsPerSecondLastMinute() { + return super.getTotalRequestsPerSecondLastMinute(); + } + + @ManagedAttribute + @Override + public double getSuccessRequestsPerSecondLastMinute() { + return super.getSuccessRequestsPerSecondLastMinute(); + } + + @ManagedAttribute + @Override + public double getFailureRequestsPerSecondLastMinute() { + return super.getFailureRequestsPerSecondLastMinute(); + } + + @ManagedAttribute + @Override + public double getTotalRequestsPerSecondLastHour() { + return super.getTotalRequestsPerSecondLastHour(); + } + + @ManagedAttribute + @Override + public double getSuccessRequestsPerSecondLastHour() { + return super.getSuccessRequestsPerSecondLastHour(); + } + + @ManagedAttribute + @Override + public double getFailureRequestsPerSecondLastHour() { + return super.getFailureRequestsPerSecondLastHour(); + } + + @ManagedAttribute + @Override + public double getTotalRequestsPerSecondLastDay() { + return super.getTotalRequestsPerSecondLastDay(); + } + + @ManagedAttribute + @Override + public double getSuccessRequestsPerSecondLastDay() { + return super.getSuccessRequestsPerSecondLastDay(); + } + + @ManagedAttribute + @Override + public double getFailureRequestsPerSecondLastDay() { + return super.getFailureRequestsPerSecondLastDay(); + } + + @ManagedAttribute + @Override + public double getTotalRequestsPerSecondLifetime() { + return super.getTotalRequestsPerSecondLifetime(); + } + + @ManagedAttribute + @Override + public double getSuccessRequestsPerSecondLifetime() { + return super.getSuccessRequestsPerSecondLifetime(); + } + + @ManagedAttribute + @Override + public double getFailureRequestsPerSecondLifetime() { + return super.getFailureRequestsPerSecondLifetime(); + } + + @ManagedAttribute + @Override + public long getRequestCount() { + return super.getRequestCount(); + } + + @ManagedAttribute + @Override + public long getSuccessCount() { + return super.getSuccessCount(); + } + + @ManagedAttribute + @Override + public long getFailureCount() { + return super.getFailureCount(); + } + + @ManagedAttribute + @Override + public long getMedianPercentileSuccessLatencyLifetime() { + return super.getMedianPercentileSuccessLatencyLifetime(); + } + + @ManagedAttribute + @Override + public long get95thPercentileSuccessLatencyLifetime() { + return super.get95thPercentileSuccessLatencyLifetime(); + } + + @ManagedAttribute + @Override + public long get99thPercentileSuccessLatencyLifetime() { + return super.get99thPercentileSuccessLatencyLifetime(); + } + + @ManagedAttribute + @Override + public long getMaxSuccessLatencyLifetime() { + return super.getMaxSuccessLatencyLifetime(); + } + + @ManagedAttribute + @Override + public long getMedianPercentileSuccessLatencyLastMinute() { + return super.getMedianPercentileSuccessLatencyLastMinute(); + } + + @ManagedAttribute + @Override + public long get95thPercentileSuccessLatencyLastMinute() { + return super.get95thPercentileSuccessLatencyLastMinute(); + } + + @ManagedAttribute + @Override + public long get99thPercentileSuccessLatencyLastMinute() { + return super.get99thPercentileSuccessLatencyLastMinute(); + } + + @ManagedAttribute + @Override + public long getMedianPercentileSuccessfulLatencyLastHour() { + return super.getMedianPercentileSuccessfulLatencyLastHour(); + } + + @ManagedAttribute + @Override + public long get95thPercentileSuccessLatencyLastHour() { + return super.get95thPercentileSuccessLatencyLastHour(); + } + + @ManagedAttribute + @Override + public long get99thPercentileSuccessLatencyLastHour() { + return super.get99thPercentileSuccessLatencyLastHour(); + } + + @ManagedAttribute + @Override + public long getMedianPercentileSuccessLatencyLastDay() { + return super.getMedianPercentileSuccessLatencyLastDay(); + } + + @ManagedAttribute + @Override + public long get95thPercentileSuccessLatencyLastDay() { + return super.get95thPercentileSuccessLatencyLastDay(); + } + + @ManagedAttribute + @Override + public long get99thPercentileSuccessLatencyLastDay() { + return super.get99thPercentileSuccessLatencyLastDay(); + } + + @ManagedAttribute + @Override + public long getMedianPercentileFailureLatencyLifetime() { + return super.getMedianPercentileFailureLatencyLifetime(); + } + + @ManagedAttribute + @Override + public long get95thPercentileFailureLatencyLifetime() { + return super.get95thPercentileFailureLatencyLifetime(); + } + + @ManagedAttribute + @Override + public long get99thPercentileFailureLatencyLifetime() { + return super.get99thPercentileFailureLatencyLifetime(); + } + + @ManagedAttribute + @Override + public long getMaxFailureLatencyLifetime() { + return super.getMaxFailureLatencyLifetime(); + } + + @ManagedAttribute + @Override + public long getMedianPercentileFailureLatencyLastMinute() { + return super.getMedianPercentileFailureLatencyLastMinute(); + } + + @ManagedAttribute + @Override + public long get95thPercentileFailureLatencyLastMinute() { + return super.get95thPercentileFailureLatencyLastMinute(); + } + + @ManagedAttribute + @Override + public long get99thPercentileFailureLatencyLastMinute() { + return super.get99thPercentileFailureLatencyLastMinute(); + } + + @ManagedAttribute + @Override + public long getMedianPercentileFailureLatencyLastHour() { + return super.getMedianPercentileFailureLatencyLastHour(); + } + + @ManagedAttribute + @Override + public long get95thPercentileFailureLatencyLastHour() { + return super.get95thPercentileFailureLatencyLastHour(); + } + + @ManagedAttribute + @Override + public long get99thPercentileFailureLatencyLastHour() { + return super.get99thPercentileFailureLatencyLastHour(); + } + + @ManagedAttribute + @Override + public long getMedianPercentileFailureLatencyLastDay() { + return super.getMedianPercentileFailureLatencyLastDay(); + } + + @ManagedAttribute + @Override + public long get95thPercentileFailureLatencyLastDay() { + return super.get95thPercentileFailureLatencyLastDay(); + } + + @ManagedAttribute + @Override + public long get99thPercentileFailureLatencyLastDay() { + return super.get99thPercentileFailureLatencyLastDay(); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/PerformanceMonitorBeanFactory.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/PerformanceMonitorBeanFactory.java index 073a7f2c..17b73372 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/PerformanceMonitorBeanFactory.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/PerformanceMonitorBeanFactory.java @@ -30,143 +30,143 @@ /** * Factory to create new {@link PerformanceMonitorBean} instances and keep track - * of the created instances. If the {@link MBeanExportOperations} is set, then the - * PerformanceMonitorBean will be automatically exported as a JMX MBean. + * of the created instances. If the {@link MBeanExportOperations} is set, then + * the PerformanceMonitorBean will be automatically exported as a JMX MBean. */ public class PerformanceMonitorBeanFactory extends PerformanceMonitorFactory implements InitializingBean { - @Autowired(required=false) - private MBeanExportOperations mBeanExportOperations; - - private List initialPerformanceMonitorList; - - private String packageScanBase; - - /** - * Constructor. - */ - public PerformanceMonitorBeanFactory() { - initialPerformanceMonitorList = new ArrayList(); - packageScanBase = null; - } - - /** - * {@inheritDoc} - */ - public void afterPropertiesSet() { - createInitialPerformanceMonitors(); - } - - /** - * Set the list of initial {@link PerformanceMonitorBean} instances to create. - * @param initialPerformanceMonitors the list of {@link PerformanceMonitorBean} names - */ - public void setInitialPerformanceMonitors(List initialPerformanceMonitors) { - if (initialPerformanceMonitors != null) { - initialPerformanceMonitorList.addAll(initialPerformanceMonitors); - } - } - - /** - * If specified, PerformanceMonitorBeanFactory will scan all classes - * under packageScanBase for methods with the - * {@link org.fishwife.jrugged.aspects.PerformanceMonitor} annotation - * and initialize performance monitors for them. - * - * @param packageScanBase Where should the scan for annotations begin - */ - public void setPackageScanBase(String packageScanBase) { - this.packageScanBase = packageScanBase; - } - - /** - * Create the initial {@link PerformanceMonitorBean} instances. - */ - public void createInitialPerformanceMonitors() { - if (packageScanBase != null) { - AnnotatedMethodScanner methodScanner = new AnnotatedMethodScanner(); - for (Method m : methodScanner.findAnnotatedMethods(packageScanBase, org.fishwife.jrugged.aspects.PerformanceMonitor.class)) { - org.fishwife.jrugged.aspects.PerformanceMonitor performanceMonitorAnnotation = m.getAnnotation(org.fishwife.jrugged.aspects.PerformanceMonitor.class); - initialPerformanceMonitorList.add(performanceMonitorAnnotation.value()); - } - } - for (String name: initialPerformanceMonitorList) { - createPerformanceMonitor(name); - } - } - - /** - * Set the {@link MBeanExporter} to use to export - * {@link PerformanceMonitorBean} instances as JMX MBeans. - * @param mBeanExporter the {@link MBeanExporter} to set. - */ - @Deprecated - public void setMBeanExporter(MBeanExporter mBeanExporter) { - setMBeanExportOperations(mBeanExporter); - } - - /** - * Set the {@link MBeanExportOperations} to use to export - * {@link PerformanceMonitorBean} instances as JMX MBeans. - * @param mBeanExportOperations the {@link MBeanExportOperations} to set. - */ - public void setMBeanExportOperations(MBeanExportOperations mBeanExportOperations) { - this.mBeanExportOperations = mBeanExportOperations; - } - - /** - * Create a new {@link PerformanceMonitorBean} and map it to the provided - * name. If the {@link MBeanExportOperations} is set, then the - * PerformanceMonitorBean will be exported as a JMX MBean. - * If the PerformanceMonitor already exists, then the existing instance is - * returned. - * @param name the value for the {@link PerformanceMonitorBean} - * @return the created {@link PerformanceMonitorBean} - * @throws IllegalArgumentException if the MBean value is invalid. - */ - public synchronized PerformanceMonitor createPerformanceMonitor( - String name) { - PerformanceMonitorBean performanceMonitor = - findPerformanceMonitorBean(name); - - if (performanceMonitor == null) { - performanceMonitor = new PerformanceMonitorBean(); - - if (mBeanExportOperations != null) { - ObjectName objectName; - - try { - objectName = new ObjectName( - "org.fishwife.jrugged.spring:type=" + - "PerformanceMonitorBean,name=" + name); - } - catch (MalformedObjectNameException e) { - throw new IllegalArgumentException( - "Invalid MBean Name " + name, e); - - } - - mBeanExportOperations.registerManagedResource( - performanceMonitor, objectName); - } - addPerformanceMonitorToMap(name, performanceMonitor); - } - - return performanceMonitor; - } - - /** - * Find an existing {@link PerformanceMonitorBean} - * @param name the value for the {@link PerformanceMonitorBean} - * @return the found {@link PerformanceMonitorBean}, or null if it is not - * found. - */ - public PerformanceMonitorBean findPerformanceMonitorBean(String name) { - PerformanceMonitor performanceMonitor = findPerformanceMonitor(name); - - if (performanceMonitor instanceof PerformanceMonitorBean) { - return (PerformanceMonitorBean)performanceMonitor; - } - return null; - } + @Autowired(required = false) + private MBeanExportOperations mBeanExportOperations; + + private List initialPerformanceMonitorList; + + private String packageScanBase; + + /** + * Constructor. + */ + public PerformanceMonitorBeanFactory() { + initialPerformanceMonitorList = new ArrayList(); + packageScanBase = null; + } + + /** + * {@inheritDoc} + */ + public void afterPropertiesSet() { + createInitialPerformanceMonitors(); + } + + /** + * Set the list of initial {@link PerformanceMonitorBean} instances to create. + * + * @param initialPerformanceMonitors the list of {@link PerformanceMonitorBean} + * names + */ + public void setInitialPerformanceMonitors(List initialPerformanceMonitors) { + if (initialPerformanceMonitors != null) { + initialPerformanceMonitorList.addAll(initialPerformanceMonitors); + } + } + + /** + * If specified, PerformanceMonitorBeanFactory will scan all classes under + * packageScanBase for methods with the + * {@link org.fishwife.jrugged.aspects.PerformanceMonitor} annotation and + * initialize performance monitors for them. + * + * @param packageScanBase Where should the scan for annotations begin + */ + public void setPackageScanBase(String packageScanBase) { + this.packageScanBase = packageScanBase; + } + + /** + * Create the initial {@link PerformanceMonitorBean} instances. + */ + public void createInitialPerformanceMonitors() { + if (packageScanBase != null) { + AnnotatedMethodScanner methodScanner = new AnnotatedMethodScanner(); + for (Method m : methodScanner.findAnnotatedMethods(packageScanBase, + org.fishwife.jrugged.aspects.PerformanceMonitor.class)) { + org.fishwife.jrugged.aspects.PerformanceMonitor performanceMonitorAnnotation = m + .getAnnotation(org.fishwife.jrugged.aspects.PerformanceMonitor.class); + initialPerformanceMonitorList.add(performanceMonitorAnnotation.value()); + } + } + for (String name : initialPerformanceMonitorList) { + createPerformanceMonitor(name); + } + } + + /** + * Set the {@link MBeanExporter} to use to export {@link PerformanceMonitorBean} + * instances as JMX MBeans. + * + * @param mBeanExporter the {@link MBeanExporter} to set. + */ + @Deprecated + public void setMBeanExporter(MBeanExporter mBeanExporter) { + setMBeanExportOperations(mBeanExporter); + } + + /** + * Set the {@link MBeanExportOperations} to use to export + * {@link PerformanceMonitorBean} instances as JMX MBeans. + * + * @param mBeanExportOperations the {@link MBeanExportOperations} to set. + */ + public void setMBeanExportOperations(MBeanExportOperations mBeanExportOperations) { + this.mBeanExportOperations = mBeanExportOperations; + } + + /** + * Create a new {@link PerformanceMonitorBean} and map it to the provided name. + * If the {@link MBeanExportOperations} is set, then the PerformanceMonitorBean + * will be exported as a JMX MBean. If the PerformanceMonitor already exists, + * then the existing instance is returned. + * + * @param name the value for the {@link PerformanceMonitorBean} + * @return the created {@link PerformanceMonitorBean} + * @throws IllegalArgumentException if the MBean value is invalid. + */ + public synchronized PerformanceMonitor createPerformanceMonitor(String name) { + PerformanceMonitorBean performanceMonitor = findPerformanceMonitorBean(name); + + if (performanceMonitor == null) { + performanceMonitor = new PerformanceMonitorBean(); + + if (mBeanExportOperations != null) { + ObjectName objectName; + + try { + objectName = new ObjectName( + "org.fishwife.jrugged.spring:type=" + "PerformanceMonitorBean,name=" + name); + } catch (MalformedObjectNameException e) { + throw new IllegalArgumentException("Invalid MBean Name " + name, e); + + } + + mBeanExportOperations.registerManagedResource(performanceMonitor, objectName); + } + addPerformanceMonitorToMap(name, performanceMonitor); + } + + return performanceMonitor; + } + + /** + * Find an existing {@link PerformanceMonitorBean} + * + * @param name the value for the {@link PerformanceMonitorBean} + * @return the found {@link PerformanceMonitorBean}, or null if it is not found. + */ + public PerformanceMonitorBean findPerformanceMonitorBean(String name) { + PerformanceMonitor performanceMonitor = findPerformanceMonitor(name); + + if (performanceMonitor instanceof PerformanceMonitorBean) { + return (PerformanceMonitorBean) performanceMonitor; + } + return null; + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/PerformanceMonitorFilter.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/PerformanceMonitorFilter.java index 50bb22ba..6c77ff8a 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/PerformanceMonitorFilter.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/PerformanceMonitorFilter.java @@ -29,47 +29,51 @@ import org.springframework.jmx.export.annotation.ManagedResource; /** - * This class is a standard Servlet filter that can be configured in web.xml to wrap all - * request handling in a {@link org.fishwife.jrugged.PerformanceMonitor}. In order to get - * useful access to the statistics, however, it is most convenient to make use of Spring's - * {@link org.springframework.web.filter.DelegatingFilterProxy} in web.xml and - * instantiate this filter within a Spring application context. This will allow the JMX - * annotations inherited from {@link PerformanceMonitorBean} to take effect, with the result - * that you can get a high-level performance monitor wrapped around all of your application's + * This class is a standard Servlet filter that can be configured in web.xml to + * wrap all request handling in a + * {@link org.fishwife.jrugged.PerformanceMonitor}. In order to get useful + * access to the statistics, however, it is most convenient to make use of + * Spring's {@link org.springframework.web.filter.DelegatingFilterProxy} in + * web.xml and instantiate this filter within a Spring application + * context. This will allow the JMX annotations inherited from + * {@link PerformanceMonitorBean} to take effect, with the result that you can + * get a high-level performance monitor wrapped around all of your application's * request handling. */ @ManagedResource public class PerformanceMonitorFilter extends PerformanceMonitorBean implements Filter { - public void doFilter(final ServletRequest req, final ServletResponse resp, - final FilterChain chain) throws IOException, ServletException { - try { - invoke(new Runnable() { - public void run() { - try { - chain.doFilter(req, resp); - } catch (IOException e) { - throw new WrappedException(e); - } catch (ServletException e) { - throw new WrappedException(e); - } - } - }); - } catch (WrappedException e) { - Throwable wrapped = e.getCause(); - if (wrapped instanceof IOException) { - throw (IOException)wrapped; - } else if (wrapped instanceof ServletException) { - throw (ServletException)wrapped; - } else { - throw new IllegalStateException("unknown wrapped exception", wrapped); - } - } catch (Exception e) { - throw new IllegalStateException("unknown checked exception", e); - } - } + public void doFilter(final ServletRequest req, final ServletResponse resp, final FilterChain chain) + throws IOException, ServletException { + try { + invoke(new Runnable() { + public void run() { + try { + chain.doFilter(req, resp); + } catch (IOException e) { + throw new WrappedException(e); + } catch (ServletException e) { + throw new WrappedException(e); + } + } + }); + } catch (WrappedException e) { + Throwable wrapped = e.getCause(); + if (wrapped instanceof IOException) { + throw (IOException) wrapped; + } else if (wrapped instanceof ServletException) { + throw (ServletException) wrapped; + } else { + throw new IllegalStateException("unknown wrapped exception", wrapped); + } + } catch (Exception e) { + throw new IllegalStateException("unknown checked exception", e); + } + } - public void init(FilterConfig config) throws ServletException { } + public void init(FilterConfig config) throws ServletException { + } - public void destroy() { } + public void destroy() { + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/RequestCounter.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/RequestCounter.java index 5be4f1f2..56d535e4 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/RequestCounter.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/RequestCounter.java @@ -8,25 +8,25 @@ public class RequestCounter extends org.fishwife.jrugged.RequestCounter implements ServiceWrapper { - @Override - public ListenableFuture invokeAsync(Callable> callable) throws Exception { + @Override + public ListenableFuture invokeAsync(Callable> callable) throws Exception { - final SettableListenableFuture response = new SettableListenableFuture(); - ListenableFutureCallback callback = new ListenableFutureCallback() { - @Override - public void onSuccess(T result) { - succeed(); - response.set(result); - } + final SettableListenableFuture response = new SettableListenableFuture(); + ListenableFutureCallback callback = new ListenableFutureCallback() { + @Override + public void onSuccess(T result) { + succeed(); + response.set(result); + } - @Override - public void onFailure(Throwable ex) { - fail(); - response.setException(ex); - } - }; + @Override + public void onFailure(Throwable ex) { + fail(); + response.setException(ex); + } + }; - callable.call().addCallback(callback); - return response; - } + callable.call().addCallback(callback); + return response; + } } \ No newline at end of file diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/RequestCounterBean.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/RequestCounterBean.java index 33f70940..1f3f15f4 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/RequestCounterBean.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/RequestCounterBean.java @@ -20,17 +20,17 @@ import org.springframework.jmx.export.annotation.ManagedOperation; public class RequestCounterBean extends RequestCounter { - public RequestCounterBean() { - super(); - } + public RequestCounterBean() { + super(); + } - @ManagedOperation - @Override - /** - * {@inheritDoc} - */ - public synchronized long[] sample() { - return super.sample(); - } + @ManagedOperation + @Override + /** + * {@inheritDoc} + */ + public synchronized long[] sample() { + return super.sample(); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/ServiceWrapper.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/ServiceWrapper.java index 0fdd37cb..11b4e169 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/ServiceWrapper.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/ServiceWrapper.java @@ -6,11 +6,16 @@ public interface ServiceWrapper extends org.fishwife.jrugged.ServiceWrapper { - /** Wraps a {@link java.util.concurrent.Callable} in some fashion. - * @param callable the service call to wrap - * @param The return value for a future call - * @return {@link ListenableFuture} of whatever callable would normally return - * @throws Exception because it's part of the {@link Callable#call()} method signature. Exceptions must be handled in the callback methods. - */ - ListenableFuture invokeAsync(Callable> callable) throws Exception; + /** + * Wraps a {@link java.util.concurrent.Callable} in some fashion. + * + * @param callable the service call to wrap + * @param The return value for a future call + * @return {@link ListenableFuture} of whatever callable would + * normally return + * @throws Exception because it's part of the {@link Callable#call()} method + * signature. Exceptions must be handled in the callback + * methods. + */ + ListenableFuture invokeAsync(Callable> callable) throws Exception; } \ No newline at end of file diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/ServiceWrapperInterceptor.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/ServiceWrapperInterceptor.java index 6c5fa0df..a4c63e79 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/ServiceWrapperInterceptor.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/ServiceWrapperInterceptor.java @@ -27,65 +27,67 @@ import org.fishwife.jrugged.ServiceWrapper; /** - * A Spring interceptor that allows wrapping a method invocation with - * a {@link ServiceWrapper} (for example, a {@link CircuitBreaker} or + * A Spring interceptor that allows wrapping a method invocation with a + * {@link ServiceWrapper} (for example, a {@link CircuitBreaker} or * {@link PerformanceMonitor}). */ public class ServiceWrapperInterceptor implements MethodInterceptor { - private Map methodMap; + private Map methodMap; - /** See if the given method invocation is one that needs to be - * called through a {@link ServiceWrapper}, and if so, do so. - * @param invocation the {@link MethodInvocation} in question - * @return whatever the underlying method call would normally - * return - * @throws Throwable that the method call would generate, or - * that the {@link ServiceWrapper} would generate when tripped. - */ - public Object invoke(final MethodInvocation invocation) throws Throwable { - String methodName = invocation.getMethod().getName(); + /** + * See if the given method invocation is one that needs to be called through a + * {@link ServiceWrapper}, and if so, do so. + * + * @param invocation the {@link MethodInvocation} in question + * @return whatever the underlying method call would normally return + * @throws Throwable that the method call would generate, or that the + * {@link ServiceWrapper} would generate when tripped. + */ + public Object invoke(final MethodInvocation invocation) throws Throwable { + String methodName = invocation.getMethod().getName(); - if (!shouldWrapMethodCall(methodName)) { - return invocation.proceed(); - } - else { - ServiceWrapper wrapper = methodMap.get(methodName); + if (!shouldWrapMethodCall(methodName)) { + return invocation.proceed(); + } else { + ServiceWrapper wrapper = methodMap.get(methodName); - return wrapper.invoke(new Callable() { - public Object call() throws Exception { - try { - return invocation.proceed(); - } catch (Throwable e) { - if (e instanceof Exception) - throw (Exception) e; - else if (e instanceof Error) - throw (Error) e; - else - throw new RuntimeException(e); - } - } - }); - } - } + return wrapper.invoke(new Callable() { + public Object call() throws Exception { + try { + return invocation.proceed(); + } catch (Throwable e) { + if (e instanceof Exception) + throw (Exception) e; + else if (e instanceof Error) + throw (Error) e; + else + throw new RuntimeException(e); + } + } + }); + } + } - private boolean shouldWrapMethodCall(String methodName) { - if (methodMap == null) { - return true; // Wrap all by default - } + private boolean shouldWrapMethodCall(String methodName) { + if (methodMap == null) { + return true; // Wrap all by default + } - if (methodMap.get(methodName) != null) { - return true; //Wrap a specific method - } + if (methodMap.get(methodName) != null) { + return true; // Wrap a specific method + } - // If I get to this point, I should not wrap the call. - return false; - } + // If I get to this point, I should not wrap the call. + return false; + } - /** Specifies which methods will be wrapped with which ServiceWrappers. - * @param methodMap the mapping! - */ - public void setMethods(Map methodMap) { - this.methodMap = methodMap; - } + /** + * Specifies which methods will be wrapped with which ServiceWrappers. + * + * @param methodMap the mapping! + */ + public void setMethods(Map methodMap) { + this.methodMap = methodMap; + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/StatusController.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/StatusController.java index c9aaca4c..4293a0bd 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/StatusController.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/StatusController.java @@ -31,77 +31,74 @@ /** * This is a convenient {@link Controller} that can be used to implement a - * "heartbeat" URL in a web application. The StatusController - * has a particular {@link MonitoredService} associated with it (a common use - * case would be to inject a {@link org.fishwife.jrugged.RolledUpMonitoredService} for - * overall system health. The StatusController writes the - * current status out in the response body and sets an appropriate HTTP - * response code. This is useful in a load balancer setting where the load - * balancer periodically pings a pool of application servers to see if they - * are "OK" and removes them from the pool if they are not. If the - * Monitorable is capable of serving requests (i.e. is GREEN - * or YELLOW) then we return a 2XX response code; otherwise we return a 5XX - * response code. + * "heartbeat" URL in a web application. The StatusController has a + * particular {@link MonitoredService} associated with it (a common use case + * would be to inject a {@link org.fishwife.jrugged.RolledUpMonitoredService} + * for overall system health. The StatusController writes the + * current status out in the response body and sets an appropriate HTTP response + * code. This is useful in a load balancer setting where the load balancer + * periodically pings a pool of application servers to see if they are "OK" and + * removes them from the pool if they are not. If the Monitorable + * is capable of serving requests (i.e. is GREEN or YELLOW) then we return a 2XX + * response code; otherwise we return a 5XX response code. */ public class StatusController implements Controller { - private static Map responseCodeMap; - static { - responseCodeMap = new HashMap(); - responseCodeMap.put(Status.FAILED, HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - responseCodeMap.put(Status.INIT, HttpServletResponse.SC_SERVICE_UNAVAILABLE); - responseCodeMap.put(Status.DOWN, HttpServletResponse.SC_SERVICE_UNAVAILABLE); - responseCodeMap.put(Status.DEGRADED, HttpServletResponse.SC_OK); - responseCodeMap.put(Status.BYPASS, HttpServletResponse.SC_OK); - responseCodeMap.put(Status.UP, HttpServletResponse.SC_OK); - } + private static Map responseCodeMap; + static { + responseCodeMap = new HashMap(); + responseCodeMap.put(Status.FAILED, HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + responseCodeMap.put(Status.INIT, HttpServletResponse.SC_SERVICE_UNAVAILABLE); + responseCodeMap.put(Status.DOWN, HttpServletResponse.SC_SERVICE_UNAVAILABLE); + responseCodeMap.put(Status.DEGRADED, HttpServletResponse.SC_OK); + responseCodeMap.put(Status.BYPASS, HttpServletResponse.SC_OK); + responseCodeMap.put(Status.UP, HttpServletResponse.SC_OK); + } - private MonitoredService monitoredService; + private MonitoredService monitoredService; - public StatusController(MonitoredService monitoredService) { - this.monitoredService = monitoredService; - } + public StatusController(MonitoredService monitoredService) { + this.monitoredService = monitoredService; + } - public ModelAndView handleRequest(HttpServletRequest req, - HttpServletResponse resp) throws Exception { - Status currentStatus = monitoredService.getServiceStatus().getStatus(); - setResponseCode(currentStatus, resp); - setAppropriateWarningHeaders(resp, currentStatus); - setCachingHeaders(resp); - writeOutCurrentStatusInResponseBody(resp, currentStatus); - return null; - } + public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception { + Status currentStatus = monitoredService.getServiceStatus().getStatus(); + setResponseCode(currentStatus, resp); + setAppropriateWarningHeaders(resp, currentStatus); + setCachingHeaders(resp); + writeOutCurrentStatusInResponseBody(resp, currentStatus); + return null; + } - private void setCachingHeaders(HttpServletResponse resp) { - long now = System.currentTimeMillis(); - resp.setDateHeader("Date", now); - resp.setDateHeader("Expires", now); - resp.setHeader("Cache-Control","no-cache"); - } + private void setCachingHeaders(HttpServletResponse resp) { + long now = System.currentTimeMillis(); + resp.setDateHeader("Date", now); + resp.setDateHeader("Expires", now); + resp.setHeader("Cache-Control", "no-cache"); + } - private void setAppropriateWarningHeaders(HttpServletResponse resp, - Status currentStatus) { - if (Status.DEGRADED.equals(currentStatus)) { - resp.addHeader("Warning", "199 jrugged \"Status degraded\""); - } - } + private void setAppropriateWarningHeaders(HttpServletResponse resp, Status currentStatus) { + if (Status.DEGRADED.equals(currentStatus)) { + resp.addHeader("Warning", "199 jrugged \"Status degraded\""); + } + } - private void writeOutCurrentStatusInResponseBody(HttpServletResponse resp, - Status currentStatus) throws IOException { - resp.setHeader("Content-Type","text/plain;charset=utf-8"); - String body = currentStatus + "\n"; - byte[] bytes = body.getBytes(); - resp.setHeader("Content-Length", bytes.length + ""); - OutputStream out = resp.getOutputStream(); - out.write(bytes); - out.flush(); - out.close(); - } + private void writeOutCurrentStatusInResponseBody(HttpServletResponse resp, Status currentStatus) + throws IOException { + resp.setHeader("Content-Type", "text/plain;charset=utf-8"); + String body = currentStatus + "\n"; + byte[] bytes = body.getBytes(); + resp.setHeader("Content-Length", bytes.length + ""); + OutputStream out = resp.getOutputStream(); + out.write(bytes); + out.flush(); + out.close(); + } - private void setResponseCode(Status currentStatus, HttpServletResponse resp) { - if (responseCodeMap.containsKey(currentStatus)) { - resp.setStatus(responseCodeMap.get(currentStatus)); - } - } + private void setResponseCode(Status currentStatus, HttpServletResponse resp) { + if (responseCodeMap.containsKey(currentStatus)) { + resp.setStatus(responseCodeMap.get(currentStatus)); + } + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/aspects/RetryTemplate.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/aspects/RetryTemplate.java index d023ef43..076ce4ba 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/aspects/RetryTemplate.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/aspects/RetryTemplate.java @@ -13,15 +13,17 @@ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface RetryTemplate { - /*** - * The name of the spring bean where the appropriate retryTemplate can be found. - * @return String the name of the template - */ - String name() default "retryTemplate"; + /*** + * The name of the spring bean where the appropriate retryTemplate can be found. + * + * @return String the name of the template + */ + String name() default "retryTemplate"; - /*** - * The name of the spring bean where a recovery callback method can be found. - * @return String the callback name or "" - */ - String recoveryCallbackName() default ""; + /*** + * The name of the spring bean where a recovery callback method can be found. + * + * @return String the callback name or "" + */ + String recoveryCallbackName() default ""; } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/aspects/RetryTemplateAspect.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/aspects/RetryTemplateAspect.java index e95a0916..565e0994 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/aspects/RetryTemplateAspect.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/aspects/RetryTemplateAspect.java @@ -19,76 +19,68 @@ @Aspect public class RetryTemplateAspect implements BeanFactoryAware { - private static final Logger logger = - LoggerFactory.getLogger(RetryTemplateAspect.class); + private static final Logger logger = LoggerFactory.getLogger(RetryTemplateAspect.class); - private BeanFactory beanFactory; + private BeanFactory beanFactory; - /** Default constructor. */ - public RetryTemplateAspect() { - } + /** Default constructor. */ + public RetryTemplateAspect() { + } - @Autowired - @Required - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = beanFactory; - } + @Autowired + @Required + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } - /** Runs a method call through the spring managed - * {@link org.springframework.retry.support.RetryTemplate} instance indicated - * by the annotations "name" attribute. - * - * @param pjp a {@link org.aspectj.lang.ProceedingJoinPoint} representing an annotated - * method call. - * @param retryTemplateAnnotation the {@link org.fishwife.jrugged.spring.aspects.RetryTemplate} annotation - * that wrapped the method. - * @throws Throwable if the method invocation itself or the wrapping - * {@link org.springframework.retry.support.RetryTemplate} throws one during execution. - * @return The return value from the method call. - */ - @Around("@annotation(retryTemplateAnnotation)") - public Object retry(final ProceedingJoinPoint pjp, - final RetryTemplate retryTemplateAnnotation) throws Throwable { - final String name = retryTemplateAnnotation.name(); - final String recoveryCallbackName = retryTemplateAnnotation.recoveryCallbackName(); + /** + * Runs a method call through the spring managed + * {@link org.springframework.retry.support.RetryTemplate} instance indicated by + * the annotations "name" attribute. + * + * @param pjp a {@link org.aspectj.lang.ProceedingJoinPoint} + * representing an annotated method call. + * @param retryTemplateAnnotation the + * {@link org.fishwife.jrugged.spring.aspects.RetryTemplate} + * annotation that wrapped the method. + * @throws Throwable if the method invocation itself or the wrapping + * {@link org.springframework.retry.support.RetryTemplate} + * throws one during execution. + * @return The return value from the method call. + */ + @Around("@annotation(retryTemplateAnnotation)") + public Object retry(final ProceedingJoinPoint pjp, final RetryTemplate retryTemplateAnnotation) throws Throwable { + final String name = retryTemplateAnnotation.name(); + final String recoveryCallbackName = retryTemplateAnnotation.recoveryCallbackName(); - org.springframework.retry.support.RetryTemplate retryTemplate = - beanFactory.getBean(name, org.springframework.retry.support.RetryTemplate.class); + org.springframework.retry.support.RetryTemplate retryTemplate = beanFactory.getBean(name, + org.springframework.retry.support.RetryTemplate.class); - RecoveryCallback recoveryCallback = null; - if (! Strings.isNullOrEmpty(recoveryCallbackName)) { - recoveryCallback = - beanFactory.getBean(recoveryCallbackName, org.springframework.retry.RecoveryCallback.class); - } + RecoveryCallback recoveryCallback = null; + if (!Strings.isNullOrEmpty(recoveryCallbackName)) { + recoveryCallback = beanFactory.getBean(recoveryCallbackName, + org.springframework.retry.RecoveryCallback.class); + } + if (logger.isDebugEnabled()) { + logger.debug( + "Have @RetryTemplate method with retryTemplate name {} and callback name {}, " + + "wrapping call on method {} of target object {}", + new Object[] { name, recoveryCallbackName, pjp.getSignature().getName(), pjp.getTarget() }); + } - if (logger.isDebugEnabled()) { - logger.debug("Have @RetryTemplate method with retryTemplate name {} and callback name {}, " + - "wrapping call on method {} of target object {}", - new Object[]{ - name, - recoveryCallbackName, - pjp.getSignature().getName(), - pjp.getTarget()}); - } - - return retryTemplate.execute( - new RetryCallback() { - public Object doWithRetry(RetryContext context) throws Exception { - try { - return pjp.proceed(); - } - catch (Error e) { - throw e; - } - catch (Exception e) { - throw e; - } - catch (Throwable e) { - throw new RuntimeException(e); - } - } - }, - recoveryCallback); - } + return retryTemplate.execute(new RetryCallback() { + public Object doWithRetry(RetryContext context) throws Exception { + try { + return pjp.proceed(); + } catch (Error e) { + throw e; + } catch (Exception e) { + throw e; + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + }, recoveryCallback); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/JRuggedNamespaceHandler.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/JRuggedNamespaceHandler.java index 8aa0863d..3df46ac8 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/JRuggedNamespaceHandler.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/JRuggedNamespaceHandler.java @@ -19,27 +19,24 @@ import org.springframework.beans.factory.xml.NamespaceHandlerSupport; /** - * Handler class for the JRugged Spring namespace. This class registers - * custom parsers and decorators for the new perform element and the - * perfmon and methods attributes on bean elements. + * Handler class for the JRugged Spring namespace. This class registers custom + * parsers and decorators for the new perform element and the perfmon and + * methods attributes on bean elements. * * This class is associated with the http://www.fishwife.org/schema/jrugged * namespace via the META-INF/spring.handlers file. */ public class JRuggedNamespaceHandler extends NamespaceHandlerSupport { - /** - * Called by Spring to register any parsers and decorators. - */ - public void init() { - registerBeanDefinitionParser("perfmon", - new PerformanceMonitorBeanDefinitionParser()); + /** + * Called by Spring to register any parsers and decorators. + */ + public void init() { + registerBeanDefinitionParser("perfmon", new PerformanceMonitorBeanDefinitionParser()); - registerBeanDefinitionDecoratorForAttribute("perfmon", - new PerformanceMonitorBeanDefinitionDecorator()); + registerBeanDefinitionDecoratorForAttribute("perfmon", new PerformanceMonitorBeanDefinitionDecorator()); - registerBeanDefinitionDecoratorForAttribute("methods", - new MonitorMethodInterceptorDefinitionDecorator()); - } + registerBeanDefinitionDecoratorForAttribute("methods", new MonitorMethodInterceptorDefinitionDecorator()); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/MonitorMethodInterceptorDefinitionDecorator.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/MonitorMethodInterceptorDefinitionDecorator.java index f8b17bf3..daa0cde3 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/MonitorMethodInterceptorDefinitionDecorator.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/MonitorMethodInterceptorDefinitionDecorator.java @@ -30,106 +30,101 @@ import org.w3c.dom.Node; /** - * This class is invoked when Spring encounters the jrugged:methods attribute - * on a bean. It parses the attribute value with is a comma delimited list - * of method names to wrap with the PerformanceMonitor. It tells Spring to - * create a SingleServiceWrapperInterceptor named after the bean with - * "PerformanceMonitorInterceptor" appended to the name. It also defines - * a PerformanceMonitorBean named after the bean with "PerformanceMonitor" + * This class is invoked when Spring encounters the jrugged:methods attribute on + * a bean. It parses the attribute value with is a comma delimited list of + * method names to wrap with the PerformanceMonitor. It tells Spring to create a + * SingleServiceWrapperInterceptor named after the bean with + * "PerformanceMonitorInterceptor" appended to the name. It also defines a + * PerformanceMonitorBean named after the bean with "PerformanceMonitor" * appended to it. The interceptor has a reference to the PerformanceMonitor. */ -public class MonitorMethodInterceptorDefinitionDecorator implements - BeanDefinitionDecorator { - - /** - * Method called by Spring when it encounters the custom jrugged:methods - * attribute. Registers the performance monitor and interceptor. - */ - public BeanDefinitionHolder decorate(Node source, - BeanDefinitionHolder holder, - ParserContext context) { - - String beanName = holder.getBeanName(); - BeanDefinitionRegistry registry = context.getRegistry(); - registerPerformanceMonitor(beanName, registry); - registerInterceptor(source, beanName, registry); - - return holder; - } - - /** - * Register a new SingleServiceWrapperInterceptor for the bean being - * wrapped, associate it with the PerformanceMonitor and tell it which methods - * to intercept. - * - * @param source An Attribute node from the spring configuration - * @param beanName The name of the bean that this performance monitor is wrapped around - * @param registry The registry where all the spring beans are registered - */ - private void registerInterceptor(Node source, - String beanName, - BeanDefinitionRegistry registry) { - List methodList = buildMethodList(source); - - BeanDefinitionBuilder initializer = - BeanDefinitionBuilder.rootBeanDefinition(SingleServiceWrapperInterceptor.class); - initializer.addPropertyValue("methods", methodList); - - String perfMonitorName = beanName + "PerformanceMonitor"; - initializer.addPropertyReference("serviceWrapper", perfMonitorName); - - String interceptorName = beanName + "PerformanceMonitorInterceptor"; - registry.registerBeanDefinition(interceptorName, initializer.getBeanDefinition()); - } - - /** - * Parse the jrugged:methods attribute into a List of strings of method - * names - * - * @param source An Attribute node from the spring configuration - * - * @return List<String> - */ - private List buildMethodList(Node source) { - Attr attribute = (Attr)source; - String methods = attribute.getValue(); - return parseMethodList(methods); - } - - /** - * Parse a comma-delimited list of method names into a List of strings. - * Whitespace is ignored. - * - * @param methods the comma delimited list of methods from the spring configuration - * - * @return List<String> - */ - public List parseMethodList(String methods) { - - String[] methodArray = StringUtils.delimitedListToStringArray(methods, ","); - - List methodList = new ArrayList(); - - for (String methodName : methodArray) { - methodList.add(methodName.trim()); - } - return methodList; - } - - /** - * Register a new PerformanceMonitor with Spring if it does not already exist. - * - * @param beanName The name of the bean that this performance monitor is wrapped around - * @param registry The registry where all the spring beans are registered - */ - private void registerPerformanceMonitor(String beanName, - BeanDefinitionRegistry registry) { - - String perfMonitorName = beanName + "PerformanceMonitor"; - if (!registry.containsBeanDefinition(perfMonitorName)) { - BeanDefinitionBuilder initializer = - BeanDefinitionBuilder.rootBeanDefinition(PerformanceMonitorBean.class); - registry.registerBeanDefinition(perfMonitorName, initializer.getBeanDefinition()); - } - } +public class MonitorMethodInterceptorDefinitionDecorator implements BeanDefinitionDecorator { + + /** + * Method called by Spring when it encounters the custom jrugged:methods + * attribute. Registers the performance monitor and interceptor. + */ + public BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder holder, ParserContext context) { + + String beanName = holder.getBeanName(); + BeanDefinitionRegistry registry = context.getRegistry(); + registerPerformanceMonitor(beanName, registry); + registerInterceptor(source, beanName, registry); + + return holder; + } + + /** + * Register a new SingleServiceWrapperInterceptor for the bean being wrapped, + * associate it with the PerformanceMonitor and tell it which methods to + * intercept. + * + * @param source An Attribute node from the spring configuration + * @param beanName The name of the bean that this performance monitor is wrapped + * around + * @param registry The registry where all the spring beans are registered + */ + private void registerInterceptor(Node source, String beanName, BeanDefinitionRegistry registry) { + List methodList = buildMethodList(source); + + BeanDefinitionBuilder initializer = BeanDefinitionBuilder + .rootBeanDefinition(SingleServiceWrapperInterceptor.class); + initializer.addPropertyValue("methods", methodList); + + String perfMonitorName = beanName + "PerformanceMonitor"; + initializer.addPropertyReference("serviceWrapper", perfMonitorName); + + String interceptorName = beanName + "PerformanceMonitorInterceptor"; + registry.registerBeanDefinition(interceptorName, initializer.getBeanDefinition()); + } + + /** + * Parse the jrugged:methods attribute into a List of strings of method names + * + * @param source An Attribute node from the spring configuration + * + * @return List<String> + */ + private List buildMethodList(Node source) { + Attr attribute = (Attr) source; + String methods = attribute.getValue(); + return parseMethodList(methods); + } + + /** + * Parse a comma-delimited list of method names into a List of strings. + * Whitespace is ignored. + * + * @param methods the comma delimited list of methods from the spring + * configuration + * + * @return List<String> + */ + public List parseMethodList(String methods) { + + String[] methodArray = StringUtils.delimitedListToStringArray(methods, ","); + + List methodList = new ArrayList(); + + for (String methodName : methodArray) { + methodList.add(methodName.trim()); + } + return methodList; + } + + /** + * Register a new PerformanceMonitor with Spring if it does not already exist. + * + * @param beanName The name of the bean that this performance monitor is wrapped + * around + * @param registry The registry where all the spring beans are registered + */ + private void registerPerformanceMonitor(String beanName, BeanDefinitionRegistry registry) { + + String perfMonitorName = beanName + "PerformanceMonitor"; + if (!registry.containsBeanDefinition(perfMonitorName)) { + BeanDefinitionBuilder initializer = BeanDefinitionBuilder.rootBeanDefinition(PerformanceMonitorBean.class); + registry.registerBeanDefinition(perfMonitorName, initializer.getBeanDefinition()); + } + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/PerformanceMonitorBeanDefinitionDecorator.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/PerformanceMonitorBeanDefinitionDecorator.java index f9b80b7e..64e094f7 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/PerformanceMonitorBeanDefinitionDecorator.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/PerformanceMonitorBeanDefinitionDecorator.java @@ -28,68 +28,62 @@ /** * This class is invoked when Spring encounters the jrugged:perfmon attribute. * If the attribute is set to true, then it registers a BeanNameAutoProxyCreator - * that gets associated with the SingleServiceWrapperInterceptor created by - * the jrugged:methods attribute. + * that gets associated with the SingleServiceWrapperInterceptor created by the + * jrugged:methods attribute. */ -public class PerformanceMonitorBeanDefinitionDecorator implements - BeanDefinitionDecorator { +public class PerformanceMonitorBeanDefinitionDecorator implements BeanDefinitionDecorator { - /** - * Method called by Spring when it encounters the jrugged:perfmon attribute. - * Checks if the attribute is true, and if so, it registers a proxy for the - * bean. - */ - public BeanDefinitionHolder decorate(Node source, - BeanDefinitionHolder holder, - ParserContext context) { + /** + * Method called by Spring when it encounters the jrugged:perfmon attribute. + * Checks if the attribute is true, and if so, it registers a proxy for the + * bean. + */ + public BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder holder, ParserContext context) { - boolean enabled = getBooleanAttributeValue(source); - if (enabled) { - registerProxyCreator(source, holder, context); - } + boolean enabled = getBooleanAttributeValue(source); + if (enabled) { + registerProxyCreator(source, holder, context); + } - return holder; - } + return holder; + } - /** - * Gets the value of an attribute and returns true if it is set to "true" - * (case-insensitive), otherwise returns false. - * - * @param source An Attribute node from the spring configuration - * - * @return boolean - */ - private boolean getBooleanAttributeValue(Node source) { - Attr attribute = (Attr)source; - String value = attribute.getValue(); - return "true".equalsIgnoreCase(value); - } + /** + * Gets the value of an attribute and returns true if it is set to "true" + * (case-insensitive), otherwise returns false. + * + * @param source An Attribute node from the spring configuration + * + * @return boolean + */ + private boolean getBooleanAttributeValue(Node source) { + Attr attribute = (Attr) source; + String value = attribute.getValue(); + return "true".equalsIgnoreCase(value); + } - /** - * Registers a BeanNameAutoProxyCreator class that wraps the bean being - * monitored. The proxy is associated with the PerformanceMonitorInterceptor - * for the bean, which is created when parsing the methods attribute from - * the springconfiguration xml file. - * - * @param source An Attribute node from the spring configuration - * @param holder A container for the beans I will create - * @param context the context currently parsing my spring config - */ - private void registerProxyCreator(Node source, - BeanDefinitionHolder holder, - ParserContext context) { + /** + * Registers a BeanNameAutoProxyCreator class that wraps the bean being + * monitored. The proxy is associated with the PerformanceMonitorInterceptor for + * the bean, which is created when parsing the methods attribute from the + * springconfiguration xml file. + * + * @param source An Attribute node from the spring configuration + * @param holder A container for the beans I will create + * @param context the context currently parsing my spring config + */ + private void registerProxyCreator(Node source, BeanDefinitionHolder holder, ParserContext context) { - String beanName = holder.getBeanName(); - String proxyName = beanName + "Proxy"; - String interceptorName = beanName + "PerformanceMonitorInterceptor"; + String beanName = holder.getBeanName(); + String proxyName = beanName + "Proxy"; + String interceptorName = beanName + "PerformanceMonitorInterceptor"; - BeanDefinitionBuilder initializer = - BeanDefinitionBuilder.rootBeanDefinition(BeanNameAutoProxyCreator.class); + BeanDefinitionBuilder initializer = BeanDefinitionBuilder.rootBeanDefinition(BeanNameAutoProxyCreator.class); - initializer.addPropertyValue("beanNames", beanName); - initializer.addPropertyValue("interceptorNames", interceptorName); + initializer.addPropertyValue("beanNames", beanName); + initializer.addPropertyValue("interceptorNames", interceptorName); - BeanDefinitionRegistry registry = context.getRegistry(); - registry.registerBeanDefinition(proxyName, initializer.getBeanDefinition()); - } + BeanDefinitionRegistry registry = context.getRegistry(); + registry.registerBeanDefinition(proxyName, initializer.getBeanDefinition()); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/PerformanceMonitorBeanDefinitionParser.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/PerformanceMonitorBeanDefinitionParser.java index 9030149d..49b0023a 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/PerformanceMonitorBeanDefinitionParser.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/PerformanceMonitorBeanDefinitionParser.java @@ -27,20 +27,19 @@ * PerformanceMonitorBean class. Spring already provides an * AbstractSingleBeanDefinitionParser that handles most of the work to do this. */ -public class PerformanceMonitorBeanDefinitionParser extends - AbstractSingleBeanDefinitionParser { +public class PerformanceMonitorBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { - /** - * Return the class to instantiate. In this case it is PerformanceMonitorBean. - */ - protected Class getBeanClass(Element element) { - return PerformanceMonitorBean.class; - } + /** + * Return the class to instantiate. In this case it is PerformanceMonitorBean. + */ + protected Class getBeanClass(Element element) { + return PerformanceMonitorBean.class; + } - /** - * Disables lazy loading of the bean. - */ - protected void doParse(Element element, BeanDefinitionBuilder bean) { - bean.setLazyInit(false); - } + /** + * Disables lazy loading of the bean. + */ + protected void doParse(Element element, BeanDefinitionBuilder bean) { + bean.setLazyInit(false); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/SingleServiceWrapperInterceptor.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/SingleServiceWrapperInterceptor.java index 29df7de9..d0e91231 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/SingleServiceWrapperInterceptor.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/config/SingleServiceWrapperInterceptor.java @@ -29,90 +29,89 @@ */ public class SingleServiceWrapperInterceptor implements MethodInterceptor { - private List methodList; - private ServiceWrapper serviceWrapper; + private List methodList; + private ServiceWrapper serviceWrapper; - /** See if the given method invocation is one that needs to be - * called through a {@link ServiceWrapper}, and if so, do so. - * @param invocation the {@link MethodInvocation} in question - * @return whatever the underlying method call would normally - * return - * @throws Throwable that the method call would generate, or - * that the {@link ServiceWrapper} would generate when tripped. - */ - public Object invoke(final MethodInvocation invocation) throws Throwable { - String methodName = invocation.getMethod().getName(); + /** + * See if the given method invocation is one that needs to be called through a + * {@link ServiceWrapper}, and if so, do so. + * + * @param invocation the {@link MethodInvocation} in question + * @return whatever the underlying method call would normally return + * @throws Throwable that the method call would generate, or that the + * {@link ServiceWrapper} would generate when tripped. + */ + public Object invoke(final MethodInvocation invocation) throws Throwable { + String methodName = invocation.getMethod().getName(); - if (!shouldWrapMethodCall(methodName)) { - return invocation.proceed(); - } - else { - ServiceWrapper wrapper = serviceWrapper; + if (!shouldWrapMethodCall(methodName)) { + return invocation.proceed(); + } else { + ServiceWrapper wrapper = serviceWrapper; - return wrapper.invoke(new Callable() { - public Object call() throws Exception { - try { - return invocation.proceed(); - } catch (Throwable e) { - if (e instanceof Exception) - throw (Exception) e; - else if (e instanceof Error) - throw (Error) e; - else - throw new RuntimeException(e); - } - } - }); - } - } + return wrapper.invoke(new Callable() { + public Object call() throws Exception { + try { + return invocation.proceed(); + } catch (Throwable e) { + if (e instanceof Exception) + throw (Exception) e; + else if (e instanceof Error) + throw (Error) e; + else + throw new RuntimeException(e); + } + } + }); + } + } - /** - * Checks if the method being invoked should be wrapped by a service. - * It looks the method name up in the methodList. If its in the list, then - * the method should be wrapped. If the list is null, then all methods - * are wrapped. - * - * @param methodName The method being called - * - * @return boolean - */ - private boolean shouldWrapMethodCall(String methodName) { - if (methodList == null) { - return true; // Wrap all by default - } + /** + * Checks if the method being invoked should be wrapped by a service. It looks + * the method name up in the methodList. If its in the list, then the method + * should be wrapped. If the list is null, then all methods are wrapped. + * + * @param methodName The method being called + * + * @return boolean + */ + private boolean shouldWrapMethodCall(String methodName) { + if (methodList == null) { + return true; // Wrap all by default + } - if (methodList.contains(methodName)) { - return true; //Wrap a specific method - } + if (methodList.contains(methodName)) { + return true; // Wrap a specific method + } - // If I get to this point, I should not wrap the call. - return false; - } + // If I get to this point, I should not wrap the call. + return false; + } - /** - * Specifies which methods will be wrapped with the ServiceWrapper. - * - * @param methodList the methods I intend to wrap calls around - */ - public void setMethods(List methodList) { - this.methodList = methodList; - } + /** + * Specifies which methods will be wrapped with the ServiceWrapper. + * + * @param methodList the methods I intend to wrap calls around + */ + public void setMethods(List methodList) { + this.methodList = methodList; + } - /** - * Return the ServiceWrapper being used to wrap the methods. - * - * @return ServiceWrapper - */ - public ServiceWrapper getServiceWrapper() { - return serviceWrapper; - } + /** + * Return the ServiceWrapper being used to wrap the methods. + * + * @return ServiceWrapper + */ + public ServiceWrapper getServiceWrapper() { + return serviceWrapper; + } - /** - * Set the ServiceWrapper to wrap the methods with. - * - * @param serviceWrapper The wrapper instance from Spring config. - */ - public void setServiceWrapper(ServiceWrapper serviceWrapper) { - this.serviceWrapper = serviceWrapper; - } + /** + * Set the ServiceWrapper to wrap the methods with. + * + * @param serviceWrapper The wrapper instance from Spring config. + */ + public void setServiceWrapper(ServiceWrapper serviceWrapper) { + this.serviceWrapper = serviceWrapper; + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/MBeanOperationInvoker.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/MBeanOperationInvoker.java index bac03fe5..1f1a6856 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/MBeanOperationInvoker.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/MBeanOperationInvoker.java @@ -27,47 +27,50 @@ * The MBeanOperationInvoker is used to invoke an operation on an MBean. */ public class MBeanOperationInvoker { - MBeanServer mBeanServer; - ObjectName objectName; - MBeanOperationInfo operationInfo; + MBeanServer mBeanServer; + ObjectName objectName; + MBeanOperationInfo operationInfo; - /** - * Constructor. - * @param mBeanServer the {@link MBeanServer}. - * @param objectName the {@link ObjectName} for the MBean. - * @param operationInfo the {@link MBeanOperationInfo} for the Operation to invoke. - */ - public MBeanOperationInvoker(MBeanServer mBeanServer, ObjectName objectName, MBeanOperationInfo operationInfo) { - this.mBeanServer = mBeanServer; - this.objectName = objectName; - this.operationInfo = operationInfo; - } + /** + * Constructor. + * + * @param mBeanServer the {@link MBeanServer}. + * @param objectName the {@link ObjectName} for the MBean. + * @param operationInfo the {@link MBeanOperationInfo} for the Operation to + * invoke. + */ + public MBeanOperationInvoker(MBeanServer mBeanServer, ObjectName objectName, MBeanOperationInfo operationInfo) { + this.mBeanServer = mBeanServer; + this.objectName = objectName; + this.operationInfo = operationInfo; + } - /** - * Invoke the operation. - * @param parameterMap the {@link Map} of parameter names to value arrays. - * @return the {@link Object} return value from the operation. - * @throws JMException Java Management Exception - */ - public Object invokeOperation(Map parameterMap) throws JMException { - MBeanParameterInfo[] parameterInfoArray = operationInfo.getSignature(); + /** + * Invoke the operation. + * + * @param parameterMap the {@link Map} of parameter names to value arrays. + * @return the {@link Object} return value from the operation. + * @throws JMException Java Management Exception + */ + public Object invokeOperation(Map parameterMap) throws JMException { + MBeanParameterInfo[] parameterInfoArray = operationInfo.getSignature(); - Object[] values = new Object[parameterInfoArray.length]; - String[] types = new String[parameterInfoArray.length]; + Object[] values = new Object[parameterInfoArray.length]; + String[] types = new String[parameterInfoArray.length]; - MBeanValueConverter valueConverter = createMBeanValueConverter(parameterMap); + MBeanValueConverter valueConverter = createMBeanValueConverter(parameterMap); - for (int parameterNum = 0; parameterNum < parameterInfoArray.length; parameterNum++) { - MBeanParameterInfo parameterInfo = parameterInfoArray[parameterNum]; - String type = parameterInfo.getType(); - types[parameterNum] = type; - values[parameterNum] = valueConverter.convertParameterValue(parameterInfo.getName(), type); - } + for (int parameterNum = 0; parameterNum < parameterInfoArray.length; parameterNum++) { + MBeanParameterInfo parameterInfo = parameterInfoArray[parameterNum]; + String type = parameterInfo.getType(); + types[parameterNum] = type; + values[parameterNum] = valueConverter.convertParameterValue(parameterInfo.getName(), type); + } - return mBeanServer.invoke(objectName, operationInfo.getName(), values, types); - } + return mBeanServer.invoke(objectName, operationInfo.getName(), values, types); + } - MBeanValueConverter createMBeanValueConverter(Map parameterMap) { - return new MBeanValueConverter(parameterMap); - } + MBeanValueConverter createMBeanValueConverter(Map parameterMap) { + return new MBeanValueConverter(parameterMap); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/MBeanStringSanitizer.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/MBeanStringSanitizer.java index 76dbe2a4..52b6971f 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/MBeanStringSanitizer.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/MBeanStringSanitizer.java @@ -22,28 +22,30 @@ import java.net.URLDecoder; /** - * The MBeanStringSanitizer is used to turn MBean object, attribute, and operation names and values - * into web-friendly Strings. + * The MBeanStringSanitizer is used to turn MBean object, attribute, and + * operation names and values into web-friendly Strings. */ public class MBeanStringSanitizer { - /** - * Convert a URL Encoded name back to the original form. - * @param name the name to URL urlDecode. - * @param encoding the string encoding to be used (i.e. UTF-8) - * @return the name in original form. - * @throws UnsupportedEncodingException if the encoding is not supported. - */ - String urlDecode(String name, String encoding) throws UnsupportedEncodingException { - return URLDecoder.decode(name, encoding); - } + /** + * Convert a URL Encoded name back to the original form. + * + * @param name the name to URL urlDecode. + * @param encoding the string encoding to be used (i.e. UTF-8) + * @return the name in original form. + * @throws UnsupportedEncodingException if the encoding is not supported. + */ + String urlDecode(String name, String encoding) throws UnsupportedEncodingException { + return URLDecoder.decode(name, encoding); + } - /** - * Escape a value to be HTML friendly. - * @param value the Object value. - * @return the HTML-escaped String, or if the value is null. - */ - String escapeValue(Object value) { - return HtmlUtils.htmlEscape(value != null ? value.toString() : ""); - } + /** + * Escape a value to be HTML friendly. + * + * @param value the Object value. + * @return the HTML-escaped String, or if the value is null. + */ + String escapeValue(Object value) { + return HtmlUtils.htmlEscape(value != null ? value.toString() : ""); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/MBeanValueConverter.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/MBeanValueConverter.java index 5cca3187..0f6f25a2 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/MBeanValueConverter.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/MBeanValueConverter.java @@ -19,45 +19,57 @@ import java.util.Map; /** - * The MBeanValueConverter is used to convert {@link String} parameter values stored in a {@link Map} of - * parameter names to values into their native types. + * The MBeanValueConverter is used to convert {@link String} parameter values + * stored in a {@link Map} of parameter names to values into their native types. */ public class MBeanValueConverter { - private Map parameterMap; + private Map parameterMap; - /** - * Constructor. - * @param parameterMap the {@link Map} of parameter names to {@link String} values. - */ - public MBeanValueConverter(Map parameterMap) { - this.parameterMap = parameterMap; - } + /** + * Constructor. + * + * @param parameterMap the {@link Map} of parameter names to {@link String} + * values. + */ + public MBeanValueConverter(Map parameterMap) { + this.parameterMap = parameterMap; + } - /** - * Convert the {@link String} parameter value into its native type. - * The {@link String} '<null>' is converted into a null value. - * Only types String, Boolean, Int, Long, Float, and Double are supported. - * @param parameterName the parameter name to convert. - * @param type the native type for the parameter. - * @return the converted value. - * @throws NumberFormatException the parameter is not a number - * @throws UnhandledParameterTypeException unable to recognize the parameter type - */ - public Object convertParameterValue(String parameterName, String type) - throws NumberFormatException, UnhandledParameterTypeException { - String[] valueList = parameterMap.get(parameterName); - if (valueList == null || valueList.length == 0) return null; - String value = valueList[0]; - if (value.equals("")) return null; - if (type.equals("java.lang.String")) return value; - if (type.equals("boolean")) return Boolean.parseBoolean(value); - if (type.equals("int")) return Integer.parseInt(value); - if (type.equals("long")) return Long.parseLong(value); - if (type.equals("float")) return Float.parseFloat(value); - if (type.equals("double")) return Double.parseDouble(value); + /** + * Convert the {@link String} parameter value into its native type. The + * {@link String} '<null>' is converted into a null value. Only types + * String, Boolean, Int, Long, Float, and Double are supported. + * + * @param parameterName the parameter name to convert. + * @param type the native type for the parameter. + * @return the converted value. + * @throws NumberFormatException the parameter is not a number + * @throws UnhandledParameterTypeException unable to recognize the parameter + * type + */ + public Object convertParameterValue(String parameterName, String type) + throws NumberFormatException, UnhandledParameterTypeException { + String[] valueList = parameterMap.get(parameterName); + if (valueList == null || valueList.length == 0) + return null; + String value = valueList[0]; + if (value.equals("")) + return null; + if (type.equals("java.lang.String")) + return value; + if (type.equals("boolean")) + return Boolean.parseBoolean(value); + if (type.equals("int")) + return Integer.parseInt(value); + if (type.equals("long")) + return Long.parseLong(value); + if (type.equals("float")) + return Float.parseFloat(value); + if (type.equals("double")) + return Double.parseDouble(value); - throw new UnhandledParameterTypeException("Cannot convert " + value + " into type " + type + - " for parameter " + parameterName); - } + throw new UnhandledParameterTypeException( + "Cannot convert " + value + " into type " + type + " for parameter " + parameterName); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/OperationNotFoundException.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/OperationNotFoundException.java index a7fdd8fc..e068c9c4 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/OperationNotFoundException.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/OperationNotFoundException.java @@ -22,7 +22,7 @@ * Thrown when a requested Operation is not found on an MBean. */ class OperationNotFoundException extends JMException { - public OperationNotFoundException(String reason) { - super(reason); - } + public OperationNotFoundException(String reason) { + super(reason); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/UnhandledParameterTypeException.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/UnhandledParameterTypeException.java index 77563525..f6df7358 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/UnhandledParameterTypeException.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/UnhandledParameterTypeException.java @@ -22,5 +22,7 @@ * Thrown when an unhandled parameter type is found. */ public class UnhandledParameterTypeException extends JMException { - public UnhandledParameterTypeException(String reason) { super(reason); } + public UnhandledParameterTypeException(String reason) { + super(reason); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/WebMBeanAdapter.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/WebMBeanAdapter.java index 2ad9d23b..a34c91c0 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/WebMBeanAdapter.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/WebMBeanAdapter.java @@ -37,150 +37,161 @@ * The WebMBeanAdapter is used to query MBean Attributes and Operations using * web-friendly names. * - * It should be noted that by creating a web interface the JMX beans bypasses the JMX security - * mechanisms that are built into the JVM. If there is a need to limit access to the JMX - * beans then the web interface will need to be secured. + * It should be noted that by creating a web interface the JMX beans bypasses + * the JMX security mechanisms that are built into the JVM. If there is a need + * to limit access to the JMX beans then the web interface will need to be + * secured. */ public class WebMBeanAdapter { - private MBeanServer mBeanServer; - private ObjectName objectName; - private MBeanStringSanitizer sanitizer; - private String encoding; - private MBeanInfo mBeanInfo; - - /** - * Constructor. - * @param mBeanServer the {@link MBeanServer}. - * @param mBeanName the MBean name (can be URL-encoded). - * @param encoding the string encoding to be used (i.e. UTF-8) - * @throws JMException Java Management Exception - * @throws UnsupportedEncodingException if the encoding is not supported. - */ - public WebMBeanAdapter(MBeanServer mBeanServer, String mBeanName, String encoding) - throws JMException, UnsupportedEncodingException { - this.mBeanServer = mBeanServer; - this.encoding = encoding; - sanitizer = createMBeanStringSanitizer(); - objectName = createObjectName(sanitizer.urlDecode(mBeanName, encoding)); - mBeanInfo = mBeanServer.getMBeanInfo(objectName); - } - - /** - * Get the Attribute metadata for an MBean by name. - * @return the {@link Map} of {@link String} attribute names to {@link MBeanAttributeInfo} values. - */ - public Map getAttributeMetadata() { - - MBeanAttributeInfo[] attributeList = mBeanInfo.getAttributes(); - - Map attributeMap = new TreeMap(); - for (MBeanAttributeInfo attribute: attributeList) { - attributeMap.put(attribute.getName(), attribute); - } - return attributeMap; - } - - /** - * Get the Operation metadata for an MBean by name. - * @return the {@link Map} of {@link String} operation names to {@link MBeanOperationInfo} values. - */ - public Map getOperationMetadata() { - - MBeanOperationInfo[] operations = mBeanInfo.getOperations(); - - Map operationMap = new TreeMap(); - for (MBeanOperationInfo operation: operations) { - operationMap.put(operation.getName(), operation); - } - return operationMap; - } - - /** - * Get the Operation metadata for a single operation on an MBean by name. - * @param operationName the Operation name (can be URL-encoded). - * @return the {@link MBeanOperationInfo} for the operation. - * @throws OperationNotFoundException Method was not found - * @throws UnsupportedEncodingException if the encoding is not supported. - */ - public MBeanOperationInfo getOperationInfo(String operationName) - throws OperationNotFoundException, UnsupportedEncodingException { - - String decodedOperationName = sanitizer.urlDecode(operationName, encoding); - Map operationMap = getOperationMetadata(); - if (operationMap.containsKey(decodedOperationName)) { - return operationMap.get(decodedOperationName); - } - throw new OperationNotFoundException("Could not find operation " + operationName + " on MBean " + - objectName.getCanonicalName()); - } - - /** - * Get all the attribute values for an MBean by name. The values are HTML escaped. - * @return the {@link Map} of attribute names and values. - * @throws javax.management.AttributeNotFoundException Unable to find the 'attribute' - * @throws InstanceNotFoundException unable to find the specific bean - * @throws ReflectionException unable to interrogate the bean - */ - public Map getAttributeValues() - throws AttributeNotFoundException, InstanceNotFoundException, ReflectionException { - - HashSet attributeSet = new HashSet(); - - for (MBeanAttributeInfo attributeInfo : mBeanInfo.getAttributes()) { - attributeSet.add(attributeInfo.getName()); - } - - AttributeList attributeList = - mBeanServer.getAttributes(objectName, attributeSet.toArray(new String[attributeSet.size()])); - - Map attributeValueMap = new TreeMap(); - for (Attribute attribute : attributeList.asList()) { - attributeValueMap.put(attribute.getName(), sanitizer.escapeValue(attribute.getValue())); - } - - return attributeValueMap; - } - - /** - * Get the value for a single attribute on an MBean by name. - * @param attributeName the attribute name (can be URL-encoded). - * @return the value as a String. - * @throws JMException Java Management Exception - * @throws UnsupportedEncodingException if the encoding is not supported. - */ - public String getAttributeValue(String attributeName) - throws JMException, UnsupportedEncodingException { - String decodedAttributeName = sanitizer.urlDecode(attributeName, encoding); - return sanitizer.escapeValue(mBeanServer.getAttribute(objectName, decodedAttributeName)); - } - - /** - * Invoke an operation on an MBean by name. - * Note that only basic data types are supported for parameter values. - * @param operationName the operation name (can be URL-encoded). - * @param parameterMap the {@link Map} of parameter names and value arrays. - * @return the returned value from the operation. - * @throws JMException Java Management Exception - * @throws UnsupportedEncodingException if the encoding is not supported. - */ - public String invokeOperation(String operationName, Map parameterMap) - throws JMException, UnsupportedEncodingException { - MBeanOperationInfo operationInfo = getOperationInfo(operationName); - MBeanOperationInvoker invoker = createMBeanOperationInvoker(mBeanServer, objectName, operationInfo); - return sanitizer.escapeValue(invoker.invokeOperation(parameterMap)); - } - - MBeanStringSanitizer createMBeanStringSanitizer() { - return new MBeanStringSanitizer(); - } - - ObjectName createObjectName(String name) throws MalformedObjectNameException { - return new ObjectName(name); - } - - MBeanOperationInvoker createMBeanOperationInvoker( - MBeanServer mBeanServer, ObjectName objectName, MBeanOperationInfo operationInfo) { - return new MBeanOperationInvoker(mBeanServer, objectName, operationInfo); - } + private MBeanServer mBeanServer; + private ObjectName objectName; + private MBeanStringSanitizer sanitizer; + private String encoding; + private MBeanInfo mBeanInfo; + + /** + * Constructor. + * + * @param mBeanServer the {@link MBeanServer}. + * @param mBeanName the MBean name (can be URL-encoded). + * @param encoding the string encoding to be used (i.e. UTF-8) + * @throws JMException Java Management Exception + * @throws UnsupportedEncodingException if the encoding is not supported. + */ + public WebMBeanAdapter(MBeanServer mBeanServer, String mBeanName, String encoding) + throws JMException, UnsupportedEncodingException { + this.mBeanServer = mBeanServer; + this.encoding = encoding; + sanitizer = createMBeanStringSanitizer(); + objectName = createObjectName(sanitizer.urlDecode(mBeanName, encoding)); + mBeanInfo = mBeanServer.getMBeanInfo(objectName); + } + + /** + * Get the Attribute metadata for an MBean by name. + * + * @return the {@link Map} of {@link String} attribute names to + * {@link MBeanAttributeInfo} values. + */ + public Map getAttributeMetadata() { + + MBeanAttributeInfo[] attributeList = mBeanInfo.getAttributes(); + + Map attributeMap = new TreeMap(); + for (MBeanAttributeInfo attribute : attributeList) { + attributeMap.put(attribute.getName(), attribute); + } + return attributeMap; + } + + /** + * Get the Operation metadata for an MBean by name. + * + * @return the {@link Map} of {@link String} operation names to + * {@link MBeanOperationInfo} values. + */ + public Map getOperationMetadata() { + + MBeanOperationInfo[] operations = mBeanInfo.getOperations(); + + Map operationMap = new TreeMap(); + for (MBeanOperationInfo operation : operations) { + operationMap.put(operation.getName(), operation); + } + return operationMap; + } + + /** + * Get the Operation metadata for a single operation on an MBean by name. + * + * @param operationName the Operation name (can be URL-encoded). + * @return the {@link MBeanOperationInfo} for the operation. + * @throws OperationNotFoundException Method was not found + * @throws UnsupportedEncodingException if the encoding is not supported. + */ + public MBeanOperationInfo getOperationInfo(String operationName) + throws OperationNotFoundException, UnsupportedEncodingException { + + String decodedOperationName = sanitizer.urlDecode(operationName, encoding); + Map operationMap = getOperationMetadata(); + if (operationMap.containsKey(decodedOperationName)) { + return operationMap.get(decodedOperationName); + } + throw new OperationNotFoundException( + "Could not find operation " + operationName + " on MBean " + objectName.getCanonicalName()); + } + + /** + * Get all the attribute values for an MBean by name. The values are HTML + * escaped. + * + * @return the {@link Map} of attribute names and values. + * @throws javax.management.AttributeNotFoundException + * Unable to find the 'attribute' + * @throws InstanceNotFoundException unable to find the specific bean + * @throws ReflectionException unable to interrogate the bean + */ + public Map getAttributeValues() + throws AttributeNotFoundException, InstanceNotFoundException, ReflectionException { + + HashSet attributeSet = new HashSet(); + + for (MBeanAttributeInfo attributeInfo : mBeanInfo.getAttributes()) { + attributeSet.add(attributeInfo.getName()); + } + + AttributeList attributeList = mBeanServer.getAttributes(objectName, + attributeSet.toArray(new String[attributeSet.size()])); + + Map attributeValueMap = new TreeMap(); + for (Attribute attribute : attributeList.asList()) { + attributeValueMap.put(attribute.getName(), sanitizer.escapeValue(attribute.getValue())); + } + + return attributeValueMap; + } + + /** + * Get the value for a single attribute on an MBean by name. + * + * @param attributeName the attribute name (can be URL-encoded). + * @return the value as a String. + * @throws JMException Java Management Exception + * @throws UnsupportedEncodingException if the encoding is not supported. + */ + public String getAttributeValue(String attributeName) throws JMException, UnsupportedEncodingException { + String decodedAttributeName = sanitizer.urlDecode(attributeName, encoding); + return sanitizer.escapeValue(mBeanServer.getAttribute(objectName, decodedAttributeName)); + } + + /** + * Invoke an operation on an MBean by name. Note that only basic data types are + * supported for parameter values. + * + * @param operationName the operation name (can be URL-encoded). + * @param parameterMap the {@link Map} of parameter names and value arrays. + * @return the returned value from the operation. + * @throws JMException Java Management Exception + * @throws UnsupportedEncodingException if the encoding is not supported. + */ + public String invokeOperation(String operationName, Map parameterMap) + throws JMException, UnsupportedEncodingException { + MBeanOperationInfo operationInfo = getOperationInfo(operationName); + MBeanOperationInvoker invoker = createMBeanOperationInvoker(mBeanServer, objectName, operationInfo); + return sanitizer.escapeValue(invoker.invokeOperation(parameterMap)); + } + + MBeanStringSanitizer createMBeanStringSanitizer() { + return new MBeanStringSanitizer(); + } + + ObjectName createObjectName(String name) throws MalformedObjectNameException { + return new ObjectName(name); + } + + MBeanOperationInvoker createMBeanOperationInvoker(MBeanServer mBeanServer, ObjectName objectName, + MBeanOperationInfo operationInfo) { + return new MBeanOperationInvoker(mBeanServer, objectName, operationInfo); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/WebMBeanServerAdapter.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/WebMBeanServerAdapter.java index 902fbae9..b75aea32 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/WebMBeanServerAdapter.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/jmx/WebMBeanServerAdapter.java @@ -24,56 +24,61 @@ import java.util.TreeSet; /** - * The WebMBeanServerAdapter provides access to MBeans managed by an {@link MBeanServer} via - * simple string-based accessor methods. This is particularly useful for implementing a - * web interface to interact with the MBeans. Names of MBeans and returned values are sanitized - * using the {@link MBeanStringSanitizer} to make them HTML-friendly. + * The WebMBeanServerAdapter provides access to MBeans managed by an + * {@link MBeanServer} via simple string-based accessor methods. This is + * particularly useful for implementing a web interface to interact with the + * MBeans. Names of MBeans and returned values are sanitized using the + * {@link MBeanStringSanitizer} to make them HTML-friendly. * - * It should be noted that creating a web interface the JMX beans bypasses the JMX security - * mechanisms that are built into the JVM. If there is a need to limit access to the JMX - * beans then the web interface will need to be secured. + * It should be noted that creating a web interface the JMX beans bypasses the + * JMX security mechanisms that are built into the JVM. If there is a need to + * limit access to the JMX beans then the web interface will need to be secured. */ public class WebMBeanServerAdapter { - private MBeanServer mBeanServer; + private MBeanServer mBeanServer; - private MBeanStringSanitizer sanitizer; + private MBeanStringSanitizer sanitizer; - /** - * Constructor. - * @param mBeanServer the {@link MBeanServer}. - */ - public WebMBeanServerAdapter(MBeanServer mBeanServer) { - this.mBeanServer = mBeanServer; - sanitizer = createMBeanStringSanitizer(); - } + /** + * Constructor. + * + * @param mBeanServer the {@link MBeanServer}. + */ + public WebMBeanServerAdapter(MBeanServer mBeanServer) { + this.mBeanServer = mBeanServer; + sanitizer = createMBeanStringSanitizer(); + } - /** - * Get the {@link Set} of MBean names from the {@link MBeanServer}. The names are HTML sanitized. - * @return the {@link Set} of HTML sanitized MBean names. - */ - public Set getMBeanNames() { - Set nameSet = new TreeSet(); - for (ObjectInstance instance : mBeanServer.queryMBeans(null, null)) { - nameSet.add(sanitizer.escapeValue(instance.getObjectName().getCanonicalName())); - } - return nameSet; - } + /** + * Get the {@link Set} of MBean names from the {@link MBeanServer}. The names + * are HTML sanitized. + * + * @return the {@link Set} of HTML sanitized MBean names. + */ + public Set getMBeanNames() { + Set nameSet = new TreeSet(); + for (ObjectInstance instance : mBeanServer.queryMBeans(null, null)) { + nameSet.add(sanitizer.escapeValue(instance.getObjectName().getCanonicalName())); + } + return nameSet; + } - /** - * Create a WebMBeanAdaptor for a specified MBean name. - * @param mBeanName the MBean name (can be URL-encoded). - * @param encoding the string encoding to be used (i.e. UTF-8) - * @return the created WebMBeanAdaptor. - * @throws JMException Java Management Exception - * @throws UnsupportedEncodingException if the encoding is not supported. - */ - public WebMBeanAdapter createWebMBeanAdapter(String mBeanName, String encoding) - throws JMException, UnsupportedEncodingException { - return new WebMBeanAdapter(mBeanServer, mBeanName, encoding); - } + /** + * Create a WebMBeanAdaptor for a specified MBean name. + * + * @param mBeanName the MBean name (can be URL-encoded). + * @param encoding the string encoding to be used (i.e. UTF-8) + * @return the created WebMBeanAdaptor. + * @throws JMException Java Management Exception + * @throws UnsupportedEncodingException if the encoding is not supported. + */ + public WebMBeanAdapter createWebMBeanAdapter(String mBeanName, String encoding) + throws JMException, UnsupportedEncodingException { + return new WebMBeanAdapter(mBeanServer, mBeanName, encoding); + } - MBeanStringSanitizer createMBeanStringSanitizer() { - return new MBeanStringSanitizer(); - } + MBeanStringSanitizer createMBeanStringSanitizer() { + return new MBeanStringSanitizer(); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/retry/ClassifierSimpleRetryPolicy.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/retry/ClassifierSimpleRetryPolicy.java index 04cdd809..aea3c1ba 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/retry/ClassifierSimpleRetryPolicy.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/retry/ClassifierSimpleRetryPolicy.java @@ -24,84 +24,86 @@ import java.util.Collections; /*** - * An extension to the existing {@link SimpleRetryPolicy} to allow for using an arbitrary - * {@link Classifier} instance to determine if a given {@link Throwable} should trigger a - * retry. + * An extension to the existing {@link SimpleRetryPolicy} to allow for using an + * arbitrary {@link Classifier} instance to determine if a given + * {@link Throwable} should trigger a retry. */ -public class ClassifierSimpleRetryPolicy - extends SimpleRetryPolicy { - - private static final Predicate DEFAULT_PREDICATE = Predicates.alwaysFalse(); - private static final Classifier DEFAULT_CLASSIFIER = new PredicateBinaryExceptionClassifier(DEFAULT_PREDICATE); - - private volatile Classifier classifier; - - /*** - * Constructor. - * - * Uses the default values for the {@link #maxAttempts} - * Uses the default classifier, which returns false for all exceptions. - */ - public ClassifierSimpleRetryPolicy() { - this(SimpleRetryPolicy.DEFAULT_MAX_ATTEMPTS, DEFAULT_CLASSIFIER); - } - - /*** - * Constructor. - - * Uses the default classifier, which returns false for all exceptions. - * - * @param maxAttempts The maximum number of attempts allowed - */ - public ClassifierSimpleRetryPolicy(int maxAttempts) { - this(maxAttempts, DEFAULT_CLASSIFIER); - } - - /*** - * Constructor. - * - * Uses the default values for the {@link #maxAttempts} - * - * @param classifier The classifier used to determine if an exception should trigger a retry - */ - public ClassifierSimpleRetryPolicy(Classifier classifier) { - this(SimpleRetryPolicy.DEFAULT_MAX_ATTEMPTS, classifier); - } - - /*** - * Constructor. - * - * @param maxAttempts The maximum number of attempts allowed - * @param classifier The classifier used to determine if an exception should trigger a retry - */ - public ClassifierSimpleRetryPolicy(int maxAttempts, Classifier classifier) { - super(maxAttempts, Collections.EMPTY_MAP); - this.classifier = classifier; - } - - /*** - * Get the classifier instance. - * - * @return The classifier - */ - public Classifier getClassifier() { - return classifier; - } - - /*** - * Classify the exception as triggering a retry or not. - * - * @param throwable The exception which was thrown by the attempt. - * - * @return whether or not a retry should be attempted - */ - private boolean classify(Throwable throwable) { - return (classifier == null ? DEFAULT_CLASSIFIER : classifier).classify(throwable); - } - - @Override - public boolean canRetry(RetryContext context) { - Throwable t = context.getLastThrowable(); - return (t == null || classify(t)) && context.getRetryCount() < getMaxAttempts(); - } +public class ClassifierSimpleRetryPolicy extends SimpleRetryPolicy { + + private static final Predicate DEFAULT_PREDICATE = Predicates.alwaysFalse(); + private static final Classifier DEFAULT_CLASSIFIER = new PredicateBinaryExceptionClassifier( + DEFAULT_PREDICATE); + + private volatile Classifier classifier; + + /*** + * Constructor. + * + * Uses the default values for the {@link #maxAttempts} Uses the default + * classifier, which returns false for all exceptions. + */ + public ClassifierSimpleRetryPolicy() { + this(SimpleRetryPolicy.DEFAULT_MAX_ATTEMPTS, DEFAULT_CLASSIFIER); + } + + /*** + * Constructor. + * + * Uses the default classifier, which returns false for all exceptions. + * + * @param maxAttempts The maximum number of attempts allowed + */ + public ClassifierSimpleRetryPolicy(int maxAttempts) { + this(maxAttempts, DEFAULT_CLASSIFIER); + } + + /*** + * Constructor. + * + * Uses the default values for the {@link #maxAttempts} + * + * @param classifier The classifier used to determine if an exception should + * trigger a retry + */ + public ClassifierSimpleRetryPolicy(Classifier classifier) { + this(SimpleRetryPolicy.DEFAULT_MAX_ATTEMPTS, classifier); + } + + /*** + * Constructor. + * + * @param maxAttempts The maximum number of attempts allowed + * @param classifier The classifier used to determine if an exception should + * trigger a retry + */ + public ClassifierSimpleRetryPolicy(int maxAttempts, Classifier classifier) { + super(maxAttempts, Collections.EMPTY_MAP); + this.classifier = classifier; + } + + /*** + * Get the classifier instance. + * + * @return The classifier + */ + public Classifier getClassifier() { + return classifier; + } + + /*** + * Classify the exception as triggering a retry or not. + * + * @param throwable The exception which was thrown by the attempt. + * + * @return whether or not a retry should be attempted + */ + private boolean classify(Throwable throwable) { + return (classifier == null ? DEFAULT_CLASSIFIER : classifier).classify(throwable); + } + + @Override + public boolean canRetry(RetryContext context) { + Throwable t = context.getLastThrowable(); + return (t == null || classify(t)) && context.getRetryCount() < getMaxAttempts(); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/retry/ExtendedPredicates.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/retry/ExtendedPredicates.java index 546aa2e9..75523543 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/retry/ExtendedPredicates.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/retry/ExtendedPredicates.java @@ -23,48 +23,52 @@ * */ public final class ExtendedPredicates { - private ExtendedPredicates() { - super(); - } + private ExtendedPredicates() { + super(); + } - /*** - * Create a predicate to see if a given object is an instance of a specific class, given that - * all objects passed to this predicate will be a subclass of some parent of that class. - * - * @param superclazz The class which all objects passed to this predicate will be a subclass of - * @param clazz The class to see if the passed in object will be an instanceof - * @param The superclass - * @param The class - * @return The predicate - */ - public static Predicate isInstanceOf(final Class superclazz, final Class clazz) { - return new Predicate() { - public boolean apply(S input) { - return Predicates.instanceOf(clazz).apply(input); - } - }; - } + /*** + * Create a predicate to see if a given object is an instance of a specific + * class, given that all objects passed to this predicate will be a subclass of + * some parent of that class. + * + * @param superclazz The class which all objects passed to this predicate will + * be a subclass of + * @param clazz The class to see if the passed in object will be an + * instanceof + * @param The superclass + * @param The class + * @return The predicate + */ + public static Predicate isInstanceOf(final Class superclazz, final Class clazz) { + return new Predicate() { + public boolean apply(S input) { + return Predicates.instanceOf(clazz).apply(input); + } + }; + } - /*** - * Create a predicate to check if a throwable's error message contains a specific string. - * - * @param expected The expected string - * @param caseSensitive Is the comparison going to be case sensitive - * - * @return True if the throwable's message contains the expected string. - */ - public static Predicate throwableContainsMessage(final String expected, final boolean caseSensitive) { - return new Predicate() { - public boolean apply(Throwable input) { - String actual = input.getMessage(); - String exp = expected; - if (! caseSensitive) { - actual = actual.toLowerCase(); - exp = exp.toLowerCase(); - } - return actual.contains(exp); - } - }; - } + /*** + * Create a predicate to check if a throwable's error message contains a + * specific string. + * + * @param expected The expected string + * @param caseSensitive Is the comparison going to be case sensitive + * + * @return True if the throwable's message contains the expected string. + */ + public static Predicate throwableContainsMessage(final String expected, final boolean caseSensitive) { + return new Predicate() { + public boolean apply(Throwable input) { + String actual = input.getMessage(); + String exp = expected; + if (!caseSensitive) { + actual = actual.toLowerCase(); + exp = exp.toLowerCase(); + } + return actual.contains(exp); + } + }; + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/retry/ExtendedRetryTemplate.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/retry/ExtendedRetryTemplate.java index 17b9e704..f35ac216 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/retry/ExtendedRetryTemplate.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/retry/ExtendedRetryTemplate.java @@ -25,114 +25,111 @@ import org.springframework.retry.support.RetryTemplate; /*** - * Extended version of the {@link RetryTemplate} to allow easy use of the {@link Callable} - * interface, instead of the {@link RetryCallback} + * Extended version of the {@link RetryTemplate} to allow easy use of the + * {@link Callable} interface, instead of the {@link RetryCallback} */ public class ExtendedRetryTemplate extends RetryTemplate { - /*** - * Constructor. - */ - public ExtendedRetryTemplate() { - super(); - } + /*** + * Constructor. + */ + public ExtendedRetryTemplate() { + super(); + } - /*** - * Construct a {@link Callable} which wraps the given {@link RetryCallback}, - * and who's {@link java.util.concurrent.Callable#call()} method will execute - * the callback via this {@link ExtendedRetryTemplate} - * - * @param callback The callback to wrap - * @param The return type of the callback - * @return The callback as a Callable - */ - public Callable asCallable(final RetryCallback callback) { - return new Callable() { - public T call() throws Exception { - return ExtendedRetryTemplate.this.execute(callback); + /*** + * Construct a {@link Callable} which wraps the given {@link RetryCallback}, and + * who's {@link java.util.concurrent.Callable#call()} method will execute the + * callback via this {@link ExtendedRetryTemplate} + * + * @param callback The callback to wrap + * @param The return type of the callback + * @return The callback as a Callable + */ + public Callable asCallable(final RetryCallback callback) { + return new Callable() { + public T call() throws Exception { + return ExtendedRetryTemplate.this.execute(callback); - } - }; - } + } + }; + } - /*** - * Construct a {@link Callable} which wraps the given {@link Callable}, - * and who's {@link java.util.concurrent.Callable#call()} method will execute - * the callable via this {@link ExtendedRetryTemplate} - * - * @param callable The callable to wrap - * @param The return type of the callback - * @return The callback as a Callable - */ - public Callable asCallable(final Callable callable) { - return new Callable() { - public T call() throws Exception { - return ExtendedRetryTemplate.this.execute(new RetryCallback() { - public T doWithRetry(RetryContext retryContext) throws Exception { - return callable.call(); - } - }); - } - }; - } + /*** + * Construct a {@link Callable} which wraps the given {@link Callable}, and + * who's {@link java.util.concurrent.Callable#call()} method will execute the + * callable via this {@link ExtendedRetryTemplate} + * + * @param callable The callable to wrap + * @param The return type of the callback + * @return The callback as a Callable + */ + public Callable asCallable(final Callable callable) { + return new Callable() { + public T call() throws Exception { + return ExtendedRetryTemplate.this.execute(new RetryCallback() { + public T doWithRetry(RetryContext retryContext) throws Exception { + return callable.call(); + } + }); + } + }; + } - /*** - * Execute a given {@link Callable} with retry logic. - * - * @param callable The callable to execute - * @param The return type of the callable - * @return The result of the callable - * @throws Exception in the event that the callable throws - * @throws ExhaustedRetryException If all retry attempts have been exhausted - */ - public T execute(final Callable callable) throws Exception, ExhaustedRetryException { - return execute(new RetryCallback() { - public T doWithRetry(RetryContext retryContext) throws Exception { - return callable.call(); - } - }); - } + /*** + * Execute a given {@link Callable} with retry logic. + * + * @param callable The callable to execute + * @param The return type of the callable + * @return The result of the callable + * @throws Exception in the event that the callable throws + * @throws ExhaustedRetryException If all retry attempts have been exhausted + */ + public T execute(final Callable callable) throws Exception, ExhaustedRetryException { + return execute(new RetryCallback() { + public T doWithRetry(RetryContext retryContext) throws Exception { + return callable.call(); + } + }); + } - /*** - * Execute a given {@link Callable} with retry logic. - * - * @param callable The callable to execute - * @param retryState The current retryState - * @param The return type of the callable - * @return The result of the callable - * @throws Exception in the event that the callable throws - * @throws ExhaustedRetryException If all retry attempts have been exhausted - */ - public T execute(final Callable callable, RetryState retryState) throws Exception, ExhaustedRetryException { - return execute( - new RetryCallback() { - public T doWithRetry(RetryContext retryContext) throws Exception { - return callable.call(); - } - }, - retryState); - } + /*** + * Execute a given {@link Callable} with retry logic. + * + * @param callable The callable to execute + * @param retryState The current retryState + * @param The return type of the callable + * @return The result of the callable + * @throws Exception in the event that the callable throws + * @throws ExhaustedRetryException If all retry attempts have been exhausted + */ + public T execute(final Callable callable, RetryState retryState) throws Exception, ExhaustedRetryException { + return execute(new RetryCallback() { + public T doWithRetry(RetryContext retryContext) throws Exception { + return callable.call(); + } + }, retryState); + } - /*** - * Execute a given {@link Callable} with retry logic. - * - * @param callable The callable to execute - * @param recoveryCallback The recovery callback to execute when exceptions occur - * @param retryState The current retryState - * @param The return type of the callable - * @return The result of the callable - * @throws Exception in the event that the callable throws - * @throws ExhaustedRetryException If all retry attempts have been exhausted - */ - public T execute(final Callable callable, RecoveryCallback recoveryCallback, RetryState retryState) throws Exception, ExhaustedRetryException { - return execute( - new RetryCallback() { - public T doWithRetry(RetryContext retryContext) throws Exception { - return callable.call(); - } - }, - recoveryCallback, - retryState); - } + /*** + * Execute a given {@link Callable} with retry logic. + * + * @param callable The callable to execute + * @param recoveryCallback The recovery callback to execute when exceptions + * occur + * @param retryState The current retryState + * @param The return type of the callable + * @return The result of the callable + * @throws Exception in the event that the callable throws + * @throws ExhaustedRetryException If all retry attempts have been exhausted + */ + public T execute(final Callable callable, RecoveryCallback recoveryCallback, RetryState retryState) + throws Exception, ExhaustedRetryException { + return execute(new RetryCallback() { + public T doWithRetry(RetryContext retryContext) throws Exception { + return callable.call(); + } + }, recoveryCallback, retryState); + } } diff --git a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/retry/PredicateBinaryExceptionClassifier.java b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/retry/PredicateBinaryExceptionClassifier.java index fa3c0414..5a08ba8d 100644 --- a/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/retry/PredicateBinaryExceptionClassifier.java +++ b/jrugged-spring/src/main/java/org/fishwife/jrugged/spring/retry/PredicateBinaryExceptionClassifier.java @@ -19,44 +19,44 @@ import org.springframework.classify.ClassifierSupport; /*** - * A {@link Predicate} based classifier for {@link Throwable} objects which classifies them - * as boolean values. + * A {@link Predicate} based classifier for {@link Throwable} objects which + * classifies them as boolean values. */ public class PredicateBinaryExceptionClassifier extends ClassifierSupport { - private Predicate predicate; - - /*** - * Constructor. - * - * @param predicate The predicate to use to check the exception - */ - public PredicateBinaryExceptionClassifier(Predicate predicate) { - super(Boolean.TRUE); - this.predicate = predicate; - } - - /*** - * Get the predicate that is in use. - * - * @return the predicate - */ - public Predicate getPredicate() { - return predicate; - } - - /*** - * Set the predicate that is in use - * @param predicate the predicate - */ - public void setPredicate(Predicate predicate) { - this.predicate = predicate; - } - - @Override - public Boolean classify(Throwable classifiable) { - return predicate.apply(classifiable); - } - + private Predicate predicate; + + /*** + * Constructor. + * + * @param predicate The predicate to use to check the exception + */ + public PredicateBinaryExceptionClassifier(Predicate predicate) { + super(Boolean.TRUE); + this.predicate = predicate; + } + + /*** + * Get the predicate that is in use. + * + * @return the predicate + */ + public Predicate getPredicate() { + return predicate; + } + + /*** + * Set the predicate that is in use + * + * @param predicate the predicate + */ + public void setPredicate(Predicate predicate) { + this.predicate = predicate; + } + + @Override + public Boolean classify(Throwable classifiable) { + return predicate.apply(classifiable); + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/MonitoredServiceStub.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/MonitoredServiceStub.java index 99c12146..d6e80b4d 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/MonitoredServiceStub.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/MonitoredServiceStub.java @@ -22,15 +22,15 @@ public class MonitoredServiceStub implements MonitoredService { - private static final String NAME = "ServiceStub"; - private ServiceStatus status = new ServiceStatus(NAME, Status.UP); + private static final String NAME = "ServiceStub"; + private ServiceStatus status = new ServiceStatus(NAME, Status.UP); - public void setStatus(Status status) { - this.status = new ServiceStatus(NAME, status); - } + public void setStatus(Status status) { + this.status = new ServiceStatus(NAME, status); + } - public ServiceStatus getServiceStatus() { - return status; - } + public ServiceStatus getServiceStatus() { + return status; + } } \ No newline at end of file diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestAnnotatedMethodFilter.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestAnnotatedMethodFilter.java index a1584906..27133eb7 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestAnnotatedMethodFilter.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestAnnotatedMethodFilter.java @@ -40,66 +40,66 @@ public class TestAnnotatedMethodFilter { - private MetadataReader mockMetadataReader; - private MetadataReaderFactory mockMetadataReaderFactory; - private AnnotationMetadata mockAnnotationMetadata; - private MethodMetadata mockMethodMetadata; - - private AnnotatedMethodFilter impl; - - @Before - public void setUp() { - impl = new AnnotatedMethodFilter(SomeAnno.class); - mockMetadataReader = createMock(MetadataReader.class); - mockMetadataReaderFactory = createMock(MetadataReaderFactory.class); - mockAnnotationMetadata = createMock(AnnotationMetadata.class); - mockMethodMetadata = createMock(MethodMetadata.class); - - expect(mockMetadataReader.getAnnotationMetadata()).andReturn(mockAnnotationMetadata); - } - - @Test - public void testMatchReturnsFalseIfNoAnnotatedMethodsFound() throws IOException { - Set foundMethods = new HashSet(); - - expect(mockAnnotationMetadata.getAnnotatedMethods(SomeAnno.class.getCanonicalName())).andReturn(foundMethods); - - replayMocks(); - assertFalse(impl.match(mockMetadataReader, mockMetadataReaderFactory)); - verifyMocks(); - } - - @Test - public void testMatchReturnsFalseIfAnnotatedMethodsFound() throws IOException { - Set foundMethods = new HashSet(); - foundMethods.add(mockMethodMetadata); - - expect(mockAnnotationMetadata.getAnnotatedMethods(SomeAnno.class.getCanonicalName())).andReturn(foundMethods); - - replayMocks(); - assertTrue(impl.match(mockMetadataReader, mockMetadataReaderFactory)); - verifyMocks(); - } - - void replayMocks() { - replay(mockMetadataReader); - replay(mockMetadataReaderFactory); - replay(mockAnnotationMetadata); - replay(mockMethodMetadata); - } - - void verifyMocks() { - verify(mockMetadataReader); - verify(mockMetadataReaderFactory); - verify(mockAnnotationMetadata); - verify(mockMethodMetadata); - } - - // Dummy anno for test - @Target({ElementType.METHOD, ElementType.TYPE}) - @Retention(RetentionPolicy.RUNTIME) - private @interface SomeAnno { - - } + private MetadataReader mockMetadataReader; + private MetadataReaderFactory mockMetadataReaderFactory; + private AnnotationMetadata mockAnnotationMetadata; + private MethodMetadata mockMethodMetadata; + + private AnnotatedMethodFilter impl; + + @Before + public void setUp() { + impl = new AnnotatedMethodFilter(SomeAnno.class); + mockMetadataReader = createMock(MetadataReader.class); + mockMetadataReaderFactory = createMock(MetadataReaderFactory.class); + mockAnnotationMetadata = createMock(AnnotationMetadata.class); + mockMethodMetadata = createMock(MethodMetadata.class); + + expect(mockMetadataReader.getAnnotationMetadata()).andReturn(mockAnnotationMetadata); + } + + @Test + public void testMatchReturnsFalseIfNoAnnotatedMethodsFound() throws IOException { + Set foundMethods = new HashSet(); + + expect(mockAnnotationMetadata.getAnnotatedMethods(SomeAnno.class.getCanonicalName())).andReturn(foundMethods); + + replayMocks(); + assertFalse(impl.match(mockMetadataReader, mockMetadataReaderFactory)); + verifyMocks(); + } + + @Test + public void testMatchReturnsFalseIfAnnotatedMethodsFound() throws IOException { + Set foundMethods = new HashSet(); + foundMethods.add(mockMethodMetadata); + + expect(mockAnnotationMetadata.getAnnotatedMethods(SomeAnno.class.getCanonicalName())).andReturn(foundMethods); + + replayMocks(); + assertTrue(impl.match(mockMetadataReader, mockMetadataReaderFactory)); + verifyMocks(); + } + + void replayMocks() { + replay(mockMetadataReader); + replay(mockMetadataReaderFactory); + replay(mockAnnotationMetadata); + replay(mockMethodMetadata); + } + + void verifyMocks() { + verify(mockMetadataReader); + verify(mockMetadataReaderFactory); + verify(mockAnnotationMetadata); + verify(mockMethodMetadata); + } + + // Dummy anno for test + @Target({ ElementType.METHOD, ElementType.TYPE }) + @Retention(RetentionPolicy.RUNTIME) + private @interface SomeAnno { + + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestAnnotatedMethodScanner.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestAnnotatedMethodScanner.java index 3e081a8a..54bd11b3 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestAnnotatedMethodScanner.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestAnnotatedMethodScanner.java @@ -38,50 +38,49 @@ public class TestAnnotatedMethodScanner { - private ClassLoader mockClassLoader; - private ClassPathScanningCandidateComponentProvider mockProvider; + private ClassLoader mockClassLoader; + private ClassPathScanningCandidateComponentProvider mockProvider; - private AnnotatedMethodScanner impl; + private AnnotatedMethodScanner impl; - @Before - public void setUp() { - mockClassLoader = createMock(ClassLoader.class); - mockProvider = createMock(ClassPathScanningCandidateComponentProvider.class); - impl = new AnnotatedMethodScanner(mockClassLoader, mockProvider); - } + @Before + public void setUp() { + mockClassLoader = createMock(ClassLoader.class); + mockProvider = createMock(ClassPathScanningCandidateComponentProvider.class); + impl = new AnnotatedMethodScanner(mockClassLoader, mockProvider); + } - @Test - public void testFindCandidateBeansAppliesAnnotatedMethodFilter() { - String basePackage = "faux.package"; - Set filteredComponents = new HashSet(); + @Test + public void testFindCandidateBeansAppliesAnnotatedMethodFilter() { + String basePackage = "faux.package"; + Set filteredComponents = new HashSet(); - mockProvider.resetFilters(false); - expectLastCall(); - mockProvider.addIncludeFilter(EasyMock.isA(AnnotatedMethodFilter.class)); - expectLastCall(); + mockProvider.resetFilters(false); + expectLastCall(); + mockProvider.addIncludeFilter(EasyMock.isA(AnnotatedMethodFilter.class)); + expectLastCall(); - expect(mockProvider.findCandidateComponents(eq(basePackage.replace('.', '/')))). - andReturn(filteredComponents); + expect(mockProvider.findCandidateComponents(eq(basePackage.replace('.', '/')))).andReturn(filteredComponents); - replayMocks(); - impl.findCandidateBeans(basePackage, SomeAnno.class); - verifyMocks(); - } + replayMocks(); + impl.findCandidateBeans(basePackage, SomeAnno.class); + verifyMocks(); + } - void replayMocks() { - replay(mockClassLoader); - replay(mockProvider); - } + void replayMocks() { + replay(mockClassLoader); + replay(mockProvider); + } - void verifyMocks() { - verify(mockClassLoader); - verify(mockProvider); - } + void verifyMocks() { + verify(mockClassLoader); + verify(mockProvider); + } - // Dummy anno for test - @Target({ElementType.METHOD, ElementType.TYPE}) - @Retention(RetentionPolicy.RUNTIME) - private @interface SomeAnno { + // Dummy anno for test + @Target({ ElementType.METHOD, ElementType.TYPE }) + @Retention(RetentionPolicy.RUNTIME) + private @interface SomeAnno { - } + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestCircuitBreakerBean.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestCircuitBreakerBean.java index 8e2d1b7d..144a2f8b 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestCircuitBreakerBean.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestCircuitBreakerBean.java @@ -26,40 +26,40 @@ public class TestCircuitBreakerBean { - private CircuitBreakerBean impl; - private final Object out = new Object(); - private Callable call; + private CircuitBreakerBean impl; + private final Object out = new Object(); + private Callable call; - @Before - public void setUp() { - impl = new CircuitBreakerBean(); - call = new Callable() { - public Object call() throws Exception { - return out; - } - }; - } + @Before + public void setUp() { + impl = new CircuitBreakerBean(); + call = new Callable() { + public Object call() throws Exception { + return out; + } + }; + } - @Test - public void startsEnabled() throws Exception { - assertSame(out, impl.invoke(call)); - } + @Test + public void startsEnabled() throws Exception { + assertSame(out, impl.invoke(call)); + } - @Test - public void isenabledIfConfiguredAsNotDisabled() throws Exception { - impl.setDisabled(false); - impl.afterPropertiesSet(); - assertSame(out, impl.invoke(call)); - } + @Test + public void isenabledIfConfiguredAsNotDisabled() throws Exception { + impl.setDisabled(false); + impl.afterPropertiesSet(); + assertSame(out, impl.invoke(call)); + } - @Test - public void canBeDisabled() throws Exception { - impl.setDisabled(true); - impl.afterPropertiesSet(); - try { - impl.invoke(call); - fail("Should have thrown CircuitBreakerException"); - } catch (CircuitBreakerException cbe) { - } - } + @Test + public void canBeDisabled() throws Exception { + impl.setDisabled(true); + impl.afterPropertiesSet(); + try { + impl.invoke(call); + fail("Should have thrown CircuitBreakerException"); + } catch (CircuitBreakerException cbe) { + } + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestCircuitBreakerBeanFactory.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestCircuitBreakerBeanFactory.java index 3fb08524..439cc6e2 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestCircuitBreakerBeanFactory.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestCircuitBreakerBeanFactory.java @@ -36,109 +36,108 @@ public class TestCircuitBreakerBeanFactory { - private CircuitBreakerBeanFactory factory; - private CircuitBreakerConfig config; - - MBeanExporter mockMBeanExporter; - - @Before - public void setUp() { - factory = new CircuitBreakerBeanFactory(); - config = new CircuitBreakerConfig(10000L, new DefaultFailureInterpreter(5, 30000L)); - mockMBeanExporter = createMock(MBeanExporter.class); - mockMBeanExporter.registerManagedResource(EasyMock.anyObject(), EasyMock.anyObject()); - replay(mockMBeanExporter); - } - - @Test - public void testCreateCircuitBreaker() { - CircuitBreaker createdBreaker = factory.createCircuitBreaker("testCreate", config); - assertNotNull(createdBreaker); - } - - @Test - public void testCreateDuplicateCircuitBreaker() { - String name = "testCreate"; - CircuitBreaker createdBreaker = factory.createCircuitBreaker(name, config); - CircuitBreaker secondBreaker = factory.createCircuitBreaker(name, config); - - assertSame(createdBreaker, secondBreaker); - } - - @Test - public void testFindCircuitBreakerBean() { - String breakerName = "testFind"; - CircuitBreaker createdBreaker = factory.createCircuitBreaker(breakerName, config); - CircuitBreakerBean foundBreaker = factory.findCircuitBreakerBean(breakerName); - assertNotNull(foundBreaker); - assertEquals(createdBreaker, foundBreaker); - } - - @Test - public void testFindInvalidCircuitBreakerBean() { - String breakerName = "testFindInvalid"; - - // Create a map with an invalid CircuitBreaker (non-bean) in it, and jam it in. - ConcurrentHashMap invalidMap = new ConcurrentHashMap(); - invalidMap.put(breakerName, new CircuitBreaker()); - ReflectionTestUtils.setField(factory, "circuitBreakerMap", invalidMap); - - // Try to find it. - CircuitBreakerBean foundBreaker = factory.findCircuitBreakerBean(breakerName); - assertNull(foundBreaker); - } - - @Test - public void testCreatePerformanceMonitorObjectName() throws MalformedObjectNameException, NullPointerException { - mockMBeanExporter = createMock(MBeanExporter.class); - ObjectName objectName = new ObjectName( - "org.fishwife.jrugged.spring:type=" + - "CircuitBreakerBean,name=testCreate"); - mockMBeanExporter.registerManagedResource(EasyMock.anyObject(), EasyMock.eq(objectName)); - replay(mockMBeanExporter); - - factory.setMBeanExportOperations(mockMBeanExporter); - factory.createCircuitBreaker("testCreate", config); - EasyMock.verify(mockMBeanExporter); - } - - @Test - public void testBreakerWithoutMBeanExporter() { - factory.setMBeanExportOperations(null); - CircuitBreaker createdBreaker = factory.createCircuitBreaker("testCreateWithoutMBeanExporter", config); - assertNotNull(createdBreaker); - } - - @Test - public void testBreakerWithMBeanExporter() { - factory.setMBeanExportOperations(mockMBeanExporter); - CircuitBreaker createdBreaker = factory.createCircuitBreaker("testCreateWithoutMBeanExporter", config); - assertNotNull(createdBreaker); - } - - @Test(expected = IllegalArgumentException.class) - public void testBreakerWithInvalidName() { - factory.setMBeanExportOperations(mockMBeanExporter); - factory.createCircuitBreaker("=\"", config); - } - - //--------- - // The following tests depend on org.fishwife.jrugged.spring.testonly - - @Test - public void testFactoryFindsCircuitBreakers() { - factory.setPackageScanBase("org.fishwife.jrugged.spring.testonly"); - factory.buildAnnotatedCircuitBreakers(); - - assertNotNull(factory.findCircuitBreaker("breakerA")); - assertNotNull(factory.findCircuitBreaker("breakerB")); - } - - @Test - public void testWhenPackageScanNotProvidedAnnotationsNotLoaded() { - factory.buildAnnotatedCircuitBreakers(); - assertNull(factory.findCircuitBreaker("breakerA")); - assertNull(factory.findCircuitBreaker("breakerB")); - } + private CircuitBreakerBeanFactory factory; + private CircuitBreakerConfig config; + + MBeanExporter mockMBeanExporter; + + @Before + public void setUp() { + factory = new CircuitBreakerBeanFactory(); + config = new CircuitBreakerConfig(10000L, new DefaultFailureInterpreter(5, 30000L)); + mockMBeanExporter = createMock(MBeanExporter.class); + mockMBeanExporter.registerManagedResource(EasyMock.anyObject(), EasyMock.anyObject()); + replay(mockMBeanExporter); + } + + @Test + public void testCreateCircuitBreaker() { + CircuitBreaker createdBreaker = factory.createCircuitBreaker("testCreate", config); + assertNotNull(createdBreaker); + } + + @Test + public void testCreateDuplicateCircuitBreaker() { + String name = "testCreate"; + CircuitBreaker createdBreaker = factory.createCircuitBreaker(name, config); + CircuitBreaker secondBreaker = factory.createCircuitBreaker(name, config); + + assertSame(createdBreaker, secondBreaker); + } + + @Test + public void testFindCircuitBreakerBean() { + String breakerName = "testFind"; + CircuitBreaker createdBreaker = factory.createCircuitBreaker(breakerName, config); + CircuitBreakerBean foundBreaker = factory.findCircuitBreakerBean(breakerName); + assertNotNull(foundBreaker); + assertEquals(createdBreaker, foundBreaker); + } + + @Test + public void testFindInvalidCircuitBreakerBean() { + String breakerName = "testFindInvalid"; + + // Create a map with an invalid CircuitBreaker (non-bean) in it, and jam it in. + ConcurrentHashMap invalidMap = new ConcurrentHashMap(); + invalidMap.put(breakerName, new CircuitBreaker()); + ReflectionTestUtils.setField(factory, "circuitBreakerMap", invalidMap); + + // Try to find it. + CircuitBreakerBean foundBreaker = factory.findCircuitBreakerBean(breakerName); + assertNull(foundBreaker); + } + + @Test + public void testCreatePerformanceMonitorObjectName() throws MalformedObjectNameException, NullPointerException { + mockMBeanExporter = createMock(MBeanExporter.class); + ObjectName objectName = new ObjectName( + "org.fishwife.jrugged.spring:type=" + "CircuitBreakerBean,name=testCreate"); + mockMBeanExporter.registerManagedResource(EasyMock.anyObject(), EasyMock.eq(objectName)); + replay(mockMBeanExporter); + + factory.setMBeanExportOperations(mockMBeanExporter); + factory.createCircuitBreaker("testCreate", config); + EasyMock.verify(mockMBeanExporter); + } + + @Test + public void testBreakerWithoutMBeanExporter() { + factory.setMBeanExportOperations(null); + CircuitBreaker createdBreaker = factory.createCircuitBreaker("testCreateWithoutMBeanExporter", config); + assertNotNull(createdBreaker); + } + + @Test + public void testBreakerWithMBeanExporter() { + factory.setMBeanExportOperations(mockMBeanExporter); + CircuitBreaker createdBreaker = factory.createCircuitBreaker("testCreateWithoutMBeanExporter", config); + assertNotNull(createdBreaker); + } + + @Test(expected = IllegalArgumentException.class) + public void testBreakerWithInvalidName() { + factory.setMBeanExportOperations(mockMBeanExporter); + factory.createCircuitBreaker("=\"", config); + } + + // --------- + // The following tests depend on org.fishwife.jrugged.spring.testonly + + @Test + public void testFactoryFindsCircuitBreakers() { + factory.setPackageScanBase("org.fishwife.jrugged.spring.testonly"); + factory.buildAnnotatedCircuitBreakers(); + + assertNotNull(factory.findCircuitBreaker("breakerA")); + assertNotNull(factory.findCircuitBreaker("breakerB")); + } + + @Test + public void testWhenPackageScanNotProvidedAnnotationsNotLoaded() { + factory.buildAnnotatedCircuitBreakers(); + assertNull(factory.findCircuitBreaker("breakerA")); + assertNull(factory.findCircuitBreaker("breakerB")); + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestPerformanceMonitorBeanFactory.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestPerformanceMonitorBeanFactory.java index 857923b4..76815b69 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestPerformanceMonitorBeanFactory.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestPerformanceMonitorBeanFactory.java @@ -38,150 +38,135 @@ public class TestPerformanceMonitorBeanFactory { - private PerformanceMonitorBeanFactory factory; - - private MBeanExporter mockMBeanExporter; - - @Before - public void setUp() { - factory = new PerformanceMonitorBeanFactory(); - } - - @Test - public void testCreateWithInitialPerformanceMonitors() { - String name1 = "test1"; - String name2 = "test2"; - List nameList = new ArrayList(); - nameList.add(name1); - nameList.add(name2); - - factory.setInitialPerformanceMonitors(null); - factory.createInitialPerformanceMonitors(); - - factory.setInitialPerformanceMonitors(nameList); - factory.createInitialPerformanceMonitors(); - - assertNotNull(factory.findPerformanceMonitor(name1)); - assertNotNull(factory.findPerformanceMonitor(name2)); - } - - @Test - public void testCreatePerformanceMonitor() { - PerformanceMonitor createdMonitor = - factory.createPerformanceMonitor("testCreate"); - assertNotNull(createdMonitor); - } - - @Test - public void testCreatePerformanceMonitorObjectName() throws MalformedObjectNameException, NullPointerException { - mockMBeanExporter = createMock(MBeanExporter.class); - ObjectName objectName = new ObjectName( - "org.fishwife.jrugged.spring:type=" + - "PerformanceMonitorBean,name=testCreate"); - mockMBeanExporter.registerManagedResource(EasyMock.anyObject(), EasyMock.eq(objectName)); - replay(mockMBeanExporter); - - factory.setMBeanExportOperations(mockMBeanExporter); - factory.createPerformanceMonitor("testCreate"); - EasyMock.verify(mockMBeanExporter); - } - - @Test - public void testCreateDuplicatePerformanceMonitor() { - String name = "testCreate"; - PerformanceMonitor createdMonitor = - factory.createPerformanceMonitor(name); - PerformanceMonitor secondMonitor = - factory.createPerformanceMonitor(name); - assertSame(createdMonitor, secondMonitor); - } - - @Test - public void testFindPerformanceMonitorBean() { - String monitorName = "testFind"; - PerformanceMonitor createdMonitor = - factory.createPerformanceMonitor(monitorName); - PerformanceMonitorBean foundMonitor = - factory.findPerformanceMonitorBean(monitorName); - assertNotNull(foundMonitor); - assertEquals(createdMonitor, foundMonitor); - } - - @Test - public void testFindInvalidPerformanceMonitorBean() { - String monitorName = "testFindInvalid"; - - // Create a map with an invalid PerformanceMonitor (non-bean) in it, - // and jam it in. - Map invalidMap = - new HashMap(); - invalidMap.put(monitorName, new PerformanceMonitor()); - ReflectionTestUtils.setField( - factory, "performanceMonitorMap", invalidMap); - - // Try to find it. - PerformanceMonitorBean foundMonitor = - factory.findPerformanceMonitorBean(monitorName); - assertNull(foundMonitor); - } - @Test - public void testMonitorWithoutMBeanExporter() { - factory.setMBeanExportOperations(null); - PerformanceMonitor createdMonitor = - factory.createPerformanceMonitor( - "testCreateWithoutMBeanExporter"); - assertNotNull(createdMonitor); - } - - @Test - public void testMonitorWithMBeanExporter() { - mockMBeanExporter = createMock(MBeanExporter.class); - mockMBeanExporter.registerManagedResource( - EasyMock.anyObject(), EasyMock.anyObject()); - replay(mockMBeanExporter); - - factory.setMBeanExportOperations(mockMBeanExporter); - PerformanceMonitor createdMonitor = - factory.createPerformanceMonitor( - "testCreateWithoutMBeanExporter"); - assertNotNull(createdMonitor); - } - - @Test(expected=IllegalArgumentException.class) - public void testMonitorWithInvalidName() { - mockMBeanExporter = createMock(MBeanExporter.class); - mockMBeanExporter.registerManagedResource( - EasyMock.anyObject(), EasyMock.anyObject()); - replay(mockMBeanExporter); - - factory.setMBeanExportOperations(mockMBeanExporter); - factory.createPerformanceMonitor("=\""); - } - - - //--------- - // The following tests depend on org.fishwife.jrugged.spring.testonly - - @Test - public void testFactorySeededWithPackageScanBaseFindsMonitors() { - factory.setPackageScanBase("org.fishwife.jrugged.spring.testonly"); - factory.createInitialPerformanceMonitors(); - - assertNotNull(factory.findPerformanceMonitor("monitorA")); - assertNotNull(factory.findPerformanceMonitor("monitorB")); - } - - // the idea here is that the monitors are created and no exceptions occur - @Test - public void testPackageScanStyleAndInitialMonitorStylePlayNice() { - List initialPerformanceMonitors = new ArrayList(); - initialPerformanceMonitors.add("monitorA"); - - factory.setPackageScanBase("org.fishwife.jrugged.spring.testonly"); - factory.setInitialPerformanceMonitors(initialPerformanceMonitors); - factory.createInitialPerformanceMonitors(); - - assertNotNull(factory.findPerformanceMonitor("monitorA")); - assertNotNull(factory.findPerformanceMonitor("monitorB")); - } + private PerformanceMonitorBeanFactory factory; + + private MBeanExporter mockMBeanExporter; + + @Before + public void setUp() { + factory = new PerformanceMonitorBeanFactory(); + } + + @Test + public void testCreateWithInitialPerformanceMonitors() { + String name1 = "test1"; + String name2 = "test2"; + List nameList = new ArrayList(); + nameList.add(name1); + nameList.add(name2); + + factory.setInitialPerformanceMonitors(null); + factory.createInitialPerformanceMonitors(); + + factory.setInitialPerformanceMonitors(nameList); + factory.createInitialPerformanceMonitors(); + + assertNotNull(factory.findPerformanceMonitor(name1)); + assertNotNull(factory.findPerformanceMonitor(name2)); + } + + @Test + public void testCreatePerformanceMonitor() { + PerformanceMonitor createdMonitor = factory.createPerformanceMonitor("testCreate"); + assertNotNull(createdMonitor); + } + + @Test + public void testCreatePerformanceMonitorObjectName() throws MalformedObjectNameException, NullPointerException { + mockMBeanExporter = createMock(MBeanExporter.class); + ObjectName objectName = new ObjectName( + "org.fishwife.jrugged.spring:type=" + "PerformanceMonitorBean,name=testCreate"); + mockMBeanExporter.registerManagedResource(EasyMock.anyObject(), EasyMock.eq(objectName)); + replay(mockMBeanExporter); + + factory.setMBeanExportOperations(mockMBeanExporter); + factory.createPerformanceMonitor("testCreate"); + EasyMock.verify(mockMBeanExporter); + } + + @Test + public void testCreateDuplicatePerformanceMonitor() { + String name = "testCreate"; + PerformanceMonitor createdMonitor = factory.createPerformanceMonitor(name); + PerformanceMonitor secondMonitor = factory.createPerformanceMonitor(name); + assertSame(createdMonitor, secondMonitor); + } + + @Test + public void testFindPerformanceMonitorBean() { + String monitorName = "testFind"; + PerformanceMonitor createdMonitor = factory.createPerformanceMonitor(monitorName); + PerformanceMonitorBean foundMonitor = factory.findPerformanceMonitorBean(monitorName); + assertNotNull(foundMonitor); + assertEquals(createdMonitor, foundMonitor); + } + + @Test + public void testFindInvalidPerformanceMonitorBean() { + String monitorName = "testFindInvalid"; + + // Create a map with an invalid PerformanceMonitor (non-bean) in it, + // and jam it in. + Map invalidMap = new HashMap(); + invalidMap.put(monitorName, new PerformanceMonitor()); + ReflectionTestUtils.setField(factory, "performanceMonitorMap", invalidMap); + + // Try to find it. + PerformanceMonitorBean foundMonitor = factory.findPerformanceMonitorBean(monitorName); + assertNull(foundMonitor); + } + + @Test + public void testMonitorWithoutMBeanExporter() { + factory.setMBeanExportOperations(null); + PerformanceMonitor createdMonitor = factory.createPerformanceMonitor("testCreateWithoutMBeanExporter"); + assertNotNull(createdMonitor); + } + + @Test + public void testMonitorWithMBeanExporter() { + mockMBeanExporter = createMock(MBeanExporter.class); + mockMBeanExporter.registerManagedResource(EasyMock.anyObject(), EasyMock.anyObject()); + replay(mockMBeanExporter); + + factory.setMBeanExportOperations(mockMBeanExporter); + PerformanceMonitor createdMonitor = factory.createPerformanceMonitor("testCreateWithoutMBeanExporter"); + assertNotNull(createdMonitor); + } + + @Test(expected = IllegalArgumentException.class) + public void testMonitorWithInvalidName() { + mockMBeanExporter = createMock(MBeanExporter.class); + mockMBeanExporter.registerManagedResource(EasyMock.anyObject(), EasyMock.anyObject()); + replay(mockMBeanExporter); + + factory.setMBeanExportOperations(mockMBeanExporter); + factory.createPerformanceMonitor("=\""); + } + + // --------- + // The following tests depend on org.fishwife.jrugged.spring.testonly + + @Test + public void testFactorySeededWithPackageScanBaseFindsMonitors() { + factory.setPackageScanBase("org.fishwife.jrugged.spring.testonly"); + factory.createInitialPerformanceMonitors(); + + assertNotNull(factory.findPerformanceMonitor("monitorA")); + assertNotNull(factory.findPerformanceMonitor("monitorB")); + } + + // the idea here is that the monitors are created and no exceptions occur + @Test + public void testPackageScanStyleAndInitialMonitorStylePlayNice() { + List initialPerformanceMonitors = new ArrayList(); + initialPerformanceMonitors.add("monitorA"); + + factory.setPackageScanBase("org.fishwife.jrugged.spring.testonly"); + factory.setInitialPerformanceMonitors(initialPerformanceMonitors); + factory.createInitialPerformanceMonitors(); + + assertNotNull(factory.findPerformanceMonitor("monitorA")); + assertNotNull(factory.findPerformanceMonitor("monitorB")); + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestStatusController.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestStatusController.java index 860336f0..404e8ce9 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestStatusController.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/TestStatusController.java @@ -24,101 +24,99 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; - public class TestStatusController { - private MonitoredServiceStub monitoredService; - private StatusController impl; - private MockHttpServletRequest req; - private MockHttpServletResponse resp; - - @Before - public void setUp() { - monitoredService = new MonitoredServiceStub(); - impl = new StatusController(monitoredService); - req = new MockHttpServletRequest(); - resp = new MockHttpServletResponse(); - } - - private void assertResponseCodeIs(Status status, int code) throws Exception { - monitoredService.setStatus(status); - impl.handleRequest(req, resp); - assertEquals(code, resp.getStatus()); - } - - private void assertBodyForStatusIs(Status status, String bodyString) - throws Exception { - monitoredService.setStatus(status); - impl.handleRequest(req, resp); - assertEquals(bodyString, resp.getContentAsString()); - assertEquals("text/plain;charset=utf-8", resp.getHeader("Content-Type")); - assertEquals(bodyString.getBytes().length + "", resp.getHeader("Content-Length")); - } - - @Test - public void handlesRequestInternally() throws Exception { - assertNull(impl.handleRequest(req, resp)); - } - - @Test - public void returns200IfStatusIsUp() throws Exception { - assertResponseCodeIs(Status.UP, 200); - } - - @Test - public void returns503IfStatusIsDown() throws Exception { - assertResponseCodeIs(Status.DOWN, 503); - } - - @Test - public void returns200IfStatusIsDegraded() throws Exception { - assertResponseCodeIs(Status.DEGRADED, 200); - } - - @Test - public void setsWarningHeaderIfDegraded() throws Exception { - monitoredService.setStatus(Status.DEGRADED); - impl.handleRequest(req, resp); - boolean found = false; - for(Object val : resp.getHeaders("Warning")) { - if ("199 jrugged \"Status degraded\"".equals(val)) { - found = true; - } - } - assertTrue(found); - } - - @Test - public void returns200IfStatusIsBypass() throws Exception { - assertResponseCodeIs(Status.BYPASS, 200); - } - - @Test - public void returns500IfStatusIsFailed() throws Exception { - assertResponseCodeIs(Status.FAILED, 500); - } - - @Test - public void returns503IfStatusIsInit() throws Exception { - assertResponseCodeIs(Status.INIT, 503); - } - - @Test - public void writesStatusOutInResponseBodyWhenUp() throws Exception { - assertBodyForStatusIs(Status.UP, "UP\n"); - } - - @Test - public void writesStatusOutInResponseBodyWhenDown() throws Exception { - assertBodyForStatusIs(Status.DOWN, "DOWN\n"); - } - - @Test - public void setsNonCacheableHeaders() throws Exception { - impl.handleRequest(req,resp); - assertNotNull(resp.getHeader("Expires")); - assertEquals(resp.getHeader("Date"), resp.getHeader("Expires")); - assertEquals("no-cache", resp.getHeader("Cache-Control")); - } + private MonitoredServiceStub monitoredService; + private StatusController impl; + private MockHttpServletRequest req; + private MockHttpServletResponse resp; + + @Before + public void setUp() { + monitoredService = new MonitoredServiceStub(); + impl = new StatusController(monitoredService); + req = new MockHttpServletRequest(); + resp = new MockHttpServletResponse(); + } + + private void assertResponseCodeIs(Status status, int code) throws Exception { + monitoredService.setStatus(status); + impl.handleRequest(req, resp); + assertEquals(code, resp.getStatus()); + } + + private void assertBodyForStatusIs(Status status, String bodyString) throws Exception { + monitoredService.setStatus(status); + impl.handleRequest(req, resp); + assertEquals(bodyString, resp.getContentAsString()); + assertEquals("text/plain;charset=utf-8", resp.getHeader("Content-Type")); + assertEquals(bodyString.getBytes().length + "", resp.getHeader("Content-Length")); + } + + @Test + public void handlesRequestInternally() throws Exception { + assertNull(impl.handleRequest(req, resp)); + } + + @Test + public void returns200IfStatusIsUp() throws Exception { + assertResponseCodeIs(Status.UP, 200); + } + + @Test + public void returns503IfStatusIsDown() throws Exception { + assertResponseCodeIs(Status.DOWN, 503); + } + + @Test + public void returns200IfStatusIsDegraded() throws Exception { + assertResponseCodeIs(Status.DEGRADED, 200); + } + + @Test + public void setsWarningHeaderIfDegraded() throws Exception { + monitoredService.setStatus(Status.DEGRADED); + impl.handleRequest(req, resp); + boolean found = false; + for (Object val : resp.getHeaders("Warning")) { + if ("199 jrugged \"Status degraded\"".equals(val)) { + found = true; + } + } + assertTrue(found); + } + + @Test + public void returns200IfStatusIsBypass() throws Exception { + assertResponseCodeIs(Status.BYPASS, 200); + } + + @Test + public void returns500IfStatusIsFailed() throws Exception { + assertResponseCodeIs(Status.FAILED, 500); + } + + @Test + public void returns503IfStatusIsInit() throws Exception { + assertResponseCodeIs(Status.INIT, 503); + } + + @Test + public void writesStatusOutInResponseBodyWhenUp() throws Exception { + assertBodyForStatusIs(Status.UP, "UP\n"); + } + + @Test + public void writesStatusOutInResponseBodyWhenDown() throws Exception { + assertBodyForStatusIs(Status.DOWN, "DOWN\n"); + } + + @Test + public void setsNonCacheableHeaders() throws Exception { + impl.handleRequest(req, resp); + assertNotNull(resp.getHeader("Expires")); + assertEquals(resp.getHeader("Date"), resp.getHeader("Expires")); + assertEquals("no-cache", resp.getHeader("Cache-Control")); + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/aspects/RetryTemplateAspectTest.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/aspects/RetryTemplateAspectTest.java index 4a3feb69..b3a25851 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/aspects/RetryTemplateAspectTest.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/aspects/RetryTemplateAspectTest.java @@ -22,154 +22,140 @@ @RunWith(MockitoJUnitRunner.class) public class RetryTemplateAspectTest { - private static final String TEST_RETRY_TEMPLATE = "TestRetryTemplate"; - private static final String TEST_RETRY_TEMPLATE_RECOVERY = "TestRetryTemplateRecovery"; - - private RetryTemplateAspect aspect; - - @Mock - private RetryTemplate mockAnnotation; - - @Mock - private Signature mockSignature; - - @Mock - private BeanFactory beanFactory; - - @Mock - private ProceedingJoinPoint mockPjp; - - @Mock - private RecoveryCallback recoveryCallback; - - @Before - public void setUp() { - aspect = new RetryTemplateAspect(); - aspect.setBeanFactory(beanFactory); - Mockito.doReturn("Signature").when(mockSignature).getName(); - Mockito.doReturn(TEST_RETRY_TEMPLATE).when(mockAnnotation).name(); - Mockito.doReturn(TEST_RETRY_TEMPLATE_RECOVERY).when(mockAnnotation).recoveryCallbackName(); - Mockito.doReturn("Target").when(mockPjp).getTarget(); - Mockito.doReturn(mockSignature).when(mockPjp).getSignature(); - } - - @Test(expected = NoSuchBeanDefinitionException.class) - public void testRetryWithMissingBean() throws Throwable { - Mockito.doThrow(new NoSuchBeanDefinitionException("")).when(beanFactory) - .getBean(TEST_RETRY_TEMPLATE, org.springframework.retry.support.RetryTemplate.class); - try { - aspect.retry(mockPjp, mockAnnotation); - } - finally { - Mockito.verify(mockPjp, Mockito.never()).proceed(); - } - } - - @Test(expected = BeanNotOfRequiredTypeException.class) - public void testRetryWithWrongBeanType() throws Throwable { - Mockito.doThrow(new BeanNotOfRequiredTypeException("", String.class, String.class)).when(beanFactory) - .getBean(TEST_RETRY_TEMPLATE, org.springframework.retry.support.RetryTemplate.class); - try { - aspect.retry(mockPjp, mockAnnotation); - } - finally { - Mockito.verify(mockPjp, Mockito.never()).proceed(); - } - } - - - @Test - public void testRetry() throws Throwable { - org.springframework.retry.support.RetryTemplate template = - new org.springframework.retry.support.RetryTemplate(); - Mockito.doReturn(template).when(beanFactory) - .getBean(TEST_RETRY_TEMPLATE, org.springframework.retry.support.RetryTemplate.class); - Mockito.doReturn("a").when(mockPjp).proceed(); - Assert.assertEquals("a", aspect.retry(mockPjp, mockAnnotation)); - Mockito.verify(mockPjp, Mockito.times(1)).proceed(); - } - - @Test - public void testRetryExceptionWithRecovery() throws Throwable { - Mockito.doReturn(TEST_RETRY_TEMPLATE_RECOVERY).when(mockAnnotation).recoveryCallbackName(); - - org.springframework.retry.support.RetryTemplate template = - new org.springframework.retry.support.RetryTemplate(); - Map, Boolean> exceptionMap = new HashMap, Boolean>(); - exceptionMap.put(RuntimeException.class, Boolean.TRUE); - template.setRetryPolicy(new SimpleRetryPolicy(1, exceptionMap)); - - Mockito.doReturn(template).when(beanFactory) - .getBean(TEST_RETRY_TEMPLATE, org.springframework.retry.support.RetryTemplate.class); - Mockito.doReturn(recoveryCallback).when(beanFactory) - .getBean(TEST_RETRY_TEMPLATE_RECOVERY, RecoveryCallback.class); - Mockito.doThrow(new RuntimeException()).when(mockPjp).proceed(); - Mockito.doReturn("a").when(recoveryCallback).recover(Mockito.any(RetryContext.class)); - Assert.assertEquals("a", aspect.retry(mockPjp, mockAnnotation)); - Mockito.verify(mockPjp, Mockito.times(1)).proceed(); - } - - @Test(expected=RuntimeException.class) - public void testRetryExceptionWithoutRecovery() throws Throwable { - org.springframework.retry.support.RetryTemplate template = - new org.springframework.retry.support.RetryTemplate(); - Map, Boolean> exceptionMap = new HashMap, Boolean>(); - exceptionMap.put(RuntimeException.class, Boolean.TRUE); - template.setRetryPolicy(new SimpleRetryPolicy(1, exceptionMap)); - - Mockito.doReturn(template).when(beanFactory) - .getBean(TEST_RETRY_TEMPLATE, org.springframework.retry.support.RetryTemplate.class); - Mockito.doReturn(null).when(beanFactory) - .getBean(TEST_RETRY_TEMPLATE_RECOVERY, RecoveryCallback.class); - Mockito.doThrow(new RuntimeException()).when(mockPjp).proceed(); - try { - aspect.retry(mockPjp, mockAnnotation); - } - finally { - Mockito.verify(mockPjp, Mockito.times(1)).proceed(); - } - } - - @Test(expected=OutOfMemoryError.class) - public void testRetryErrorWithoutRecovery() throws Throwable { - org.springframework.retry.support.RetryTemplate template = - new org.springframework.retry.support.RetryTemplate(); - Map, Boolean> exceptionMap = new HashMap, Boolean>(); - exceptionMap.put(RuntimeException.class, Boolean.TRUE); - template.setRetryPolicy(new SimpleRetryPolicy(1, exceptionMap)); - - Mockito.doReturn(template).when(beanFactory) - .getBean(TEST_RETRY_TEMPLATE, org.springframework.retry.support.RetryTemplate.class); - Mockito.doReturn(null).when(beanFactory) - .getBean(TEST_RETRY_TEMPLATE_RECOVERY, RecoveryCallback.class); - Mockito.doThrow(new OutOfMemoryError()).when(mockPjp).proceed(); - try { - aspect.retry(mockPjp, mockAnnotation); - } - finally { - Mockito.verify(mockPjp, Mockito.times(1)).proceed(); - } - } - - @Test(expected=RuntimeException.class) - public void testRetryThrowableWithoutRecovery() throws Throwable { - org.springframework.retry.support.RetryTemplate template = - new org.springframework.retry.support.RetryTemplate(); - Map, Boolean> exceptionMap = new HashMap, Boolean>(); - exceptionMap.put(RuntimeException.class, Boolean.TRUE); - template.setRetryPolicy(new SimpleRetryPolicy(1, exceptionMap)); - - Mockito.doReturn(template).when(beanFactory) - .getBean(TEST_RETRY_TEMPLATE, org.springframework.retry.support.RetryTemplate.class); - Mockito.doReturn(null).when(beanFactory) - .getBean(TEST_RETRY_TEMPLATE_RECOVERY, RecoveryCallback.class); - Mockito.doThrow(new Throwable("")).when(mockPjp).proceed(); - try { - aspect.retry(mockPjp, mockAnnotation); - } - finally { - Mockito.verify(mockPjp, Mockito.times(1)).proceed(); - } - } + private static final String TEST_RETRY_TEMPLATE = "TestRetryTemplate"; + private static final String TEST_RETRY_TEMPLATE_RECOVERY = "TestRetryTemplateRecovery"; + + private RetryTemplateAspect aspect; + + @Mock + private RetryTemplate mockAnnotation; + + @Mock + private Signature mockSignature; + + @Mock + private BeanFactory beanFactory; + + @Mock + private ProceedingJoinPoint mockPjp; + + @Mock + private RecoveryCallback recoveryCallback; + + @Before + public void setUp() { + aspect = new RetryTemplateAspect(); + aspect.setBeanFactory(beanFactory); + Mockito.doReturn("Signature").when(mockSignature).getName(); + Mockito.doReturn(TEST_RETRY_TEMPLATE).when(mockAnnotation).name(); + Mockito.doReturn(TEST_RETRY_TEMPLATE_RECOVERY).when(mockAnnotation).recoveryCallbackName(); + Mockito.doReturn("Target").when(mockPjp).getTarget(); + Mockito.doReturn(mockSignature).when(mockPjp).getSignature(); + } + + @Test(expected = NoSuchBeanDefinitionException.class) + public void testRetryWithMissingBean() throws Throwable { + Mockito.doThrow(new NoSuchBeanDefinitionException("")).when(beanFactory).getBean(TEST_RETRY_TEMPLATE, + org.springframework.retry.support.RetryTemplate.class); + try { + aspect.retry(mockPjp, mockAnnotation); + } finally { + Mockito.verify(mockPjp, Mockito.never()).proceed(); + } + } + + @Test(expected = BeanNotOfRequiredTypeException.class) + public void testRetryWithWrongBeanType() throws Throwable { + Mockito.doThrow(new BeanNotOfRequiredTypeException("", String.class, String.class)).when(beanFactory) + .getBean(TEST_RETRY_TEMPLATE, org.springframework.retry.support.RetryTemplate.class); + try { + aspect.retry(mockPjp, mockAnnotation); + } finally { + Mockito.verify(mockPjp, Mockito.never()).proceed(); + } + } + + @Test + public void testRetry() throws Throwable { + org.springframework.retry.support.RetryTemplate template = new org.springframework.retry.support.RetryTemplate(); + Mockito.doReturn(template).when(beanFactory).getBean(TEST_RETRY_TEMPLATE, + org.springframework.retry.support.RetryTemplate.class); + Mockito.doReturn("a").when(mockPjp).proceed(); + Assert.assertEquals("a", aspect.retry(mockPjp, mockAnnotation)); + Mockito.verify(mockPjp, Mockito.times(1)).proceed(); + } + + @Test + public void testRetryExceptionWithRecovery() throws Throwable { + Mockito.doReturn(TEST_RETRY_TEMPLATE_RECOVERY).when(mockAnnotation).recoveryCallbackName(); + + org.springframework.retry.support.RetryTemplate template = new org.springframework.retry.support.RetryTemplate(); + Map, Boolean> exceptionMap = new HashMap, Boolean>(); + exceptionMap.put(RuntimeException.class, Boolean.TRUE); + template.setRetryPolicy(new SimpleRetryPolicy(1, exceptionMap)); + + Mockito.doReturn(template).when(beanFactory).getBean(TEST_RETRY_TEMPLATE, + org.springframework.retry.support.RetryTemplate.class); + Mockito.doReturn(recoveryCallback).when(beanFactory).getBean(TEST_RETRY_TEMPLATE_RECOVERY, + RecoveryCallback.class); + Mockito.doThrow(new RuntimeException()).when(mockPjp).proceed(); + Mockito.doReturn("a").when(recoveryCallback).recover(Mockito.any(RetryContext.class)); + Assert.assertEquals("a", aspect.retry(mockPjp, mockAnnotation)); + Mockito.verify(mockPjp, Mockito.times(1)).proceed(); + } + + @Test(expected = RuntimeException.class) + public void testRetryExceptionWithoutRecovery() throws Throwable { + org.springframework.retry.support.RetryTemplate template = new org.springframework.retry.support.RetryTemplate(); + Map, Boolean> exceptionMap = new HashMap, Boolean>(); + exceptionMap.put(RuntimeException.class, Boolean.TRUE); + template.setRetryPolicy(new SimpleRetryPolicy(1, exceptionMap)); + + Mockito.doReturn(template).when(beanFactory).getBean(TEST_RETRY_TEMPLATE, + org.springframework.retry.support.RetryTemplate.class); + Mockito.doReturn(null).when(beanFactory).getBean(TEST_RETRY_TEMPLATE_RECOVERY, RecoveryCallback.class); + Mockito.doThrow(new RuntimeException()).when(mockPjp).proceed(); + try { + aspect.retry(mockPjp, mockAnnotation); + } finally { + Mockito.verify(mockPjp, Mockito.times(1)).proceed(); + } + } + + @Test(expected = OutOfMemoryError.class) + public void testRetryErrorWithoutRecovery() throws Throwable { + org.springframework.retry.support.RetryTemplate template = new org.springframework.retry.support.RetryTemplate(); + Map, Boolean> exceptionMap = new HashMap, Boolean>(); + exceptionMap.put(RuntimeException.class, Boolean.TRUE); + template.setRetryPolicy(new SimpleRetryPolicy(1, exceptionMap)); + + Mockito.doReturn(template).when(beanFactory).getBean(TEST_RETRY_TEMPLATE, + org.springframework.retry.support.RetryTemplate.class); + Mockito.doReturn(null).when(beanFactory).getBean(TEST_RETRY_TEMPLATE_RECOVERY, RecoveryCallback.class); + Mockito.doThrow(new OutOfMemoryError()).when(mockPjp).proceed(); + try { + aspect.retry(mockPjp, mockAnnotation); + } finally { + Mockito.verify(mockPjp, Mockito.times(1)).proceed(); + } + } + + @Test(expected = RuntimeException.class) + public void testRetryThrowableWithoutRecovery() throws Throwable { + org.springframework.retry.support.RetryTemplate template = new org.springframework.retry.support.RetryTemplate(); + Map, Boolean> exceptionMap = new HashMap, Boolean>(); + exceptionMap.put(RuntimeException.class, Boolean.TRUE); + template.setRetryPolicy(new SimpleRetryPolicy(1, exceptionMap)); + + Mockito.doReturn(template).when(beanFactory).getBean(TEST_RETRY_TEMPLATE, + org.springframework.retry.support.RetryTemplate.class); + Mockito.doReturn(null).when(beanFactory).getBean(TEST_RETRY_TEMPLATE_RECOVERY, RecoveryCallback.class); + Mockito.doThrow(new Throwable("")).when(mockPjp).proceed(); + try { + aspect.retry(mockPjp, mockAnnotation); + } finally { + Mockito.verify(mockPjp, Mockito.times(1)).proceed(); + } + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/config/DummyService.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/config/DummyService.java index d8e1e8f9..b898504f 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/config/DummyService.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/config/DummyService.java @@ -16,18 +16,17 @@ */ package org.fishwife.jrugged.spring.config; - public class DummyService { - public String foo() { - return "Hello World!"; - } + public String foo() { + return "Hello World!"; + } - public String bar() { - return "Wazzzzup!"; - } + public String bar() { + return "Wazzzzup!"; + } - public String baz() { - return "Hey Everybody!"; - } + public String baz() { + return "Hey Everybody!"; + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/config/MonitorMethodInterceptorDefinitionDecoratorTest.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/config/MonitorMethodInterceptorDefinitionDecoratorTest.java index 7d41b99b..8958644a 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/config/MonitorMethodInterceptorDefinitionDecoratorTest.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/config/MonitorMethodInterceptorDefinitionDecoratorTest.java @@ -24,52 +24,52 @@ public class MonitorMethodInterceptorDefinitionDecoratorTest { - private MonitorMethodInterceptorDefinitionDecorator decorator; + private MonitorMethodInterceptorDefinitionDecorator decorator; - @Before - public void setUp() { - decorator = new MonitorMethodInterceptorDefinitionDecorator(); - } + @Before + public void setUp() { + decorator = new MonitorMethodInterceptorDefinitionDecorator(); + } - @Test - public void testParseMethodListNoName() { + @Test + public void testParseMethodListNoName() { - List methods = decorator.parseMethodList(""); + List methods = decorator.parseMethodList(""); - Assert.assertNotNull(methods); - Assert.assertTrue(methods.size() == 0); - } + Assert.assertNotNull(methods); + Assert.assertTrue(methods.size() == 0); + } - @Test - public void testParseMethodListOneName() { + @Test + public void testParseMethodListOneName() { - List methods = decorator.parseMethodList("foo"); + List methods = decorator.parseMethodList("foo"); - Assert.assertNotNull(methods); - Assert.assertTrue(methods.size() == 1); - Assert.assertTrue(methods.contains("foo")); - } + Assert.assertNotNull(methods); + Assert.assertTrue(methods.size() == 1); + Assert.assertTrue(methods.contains("foo")); + } - @Test - public void testParseMethodListTwoNames() { + @Test + public void testParseMethodListTwoNames() { - List methods = decorator.parseMethodList("foo, bar"); + List methods = decorator.parseMethodList("foo, bar"); - Assert.assertNotNull(methods); - Assert.assertTrue(methods.size() == 2); - Assert.assertTrue(methods.contains("foo")); - Assert.assertTrue(methods.contains("bar")); - } + Assert.assertNotNull(methods); + Assert.assertTrue(methods.size() == 2); + Assert.assertTrue(methods.contains("foo")); + Assert.assertTrue(methods.contains("bar")); + } - @Test - public void testParseMethodListThreeNames() { + @Test + public void testParseMethodListThreeNames() { - List methods = decorator.parseMethodList("foo, bar, baz"); + List methods = decorator.parseMethodList("foo, bar, baz"); - Assert.assertNotNull(methods); - Assert.assertTrue(methods.size() == 3); - Assert.assertTrue(methods.contains("foo")); - Assert.assertTrue(methods.contains("bar")); - Assert.assertTrue(methods.contains("baz")); - } + Assert.assertNotNull(methods); + Assert.assertTrue(methods.size() == 3); + Assert.assertTrue(methods.contains("foo")); + Assert.assertTrue(methods.contains("bar")); + Assert.assertTrue(methods.contains("baz")); + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/config/SpringHandlerTest.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/config/SpringHandlerTest.java index 48db8a9a..c88da1e2 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/config/SpringHandlerTest.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/config/SpringHandlerTest.java @@ -28,47 +28,44 @@ public class SpringHandlerTest { - ClassPathXmlApplicationContext context; + ClassPathXmlApplicationContext context; - @Before - public void setUp() { - String[] config = new String[] {"applicationContext.xml"}; - context = new ClassPathXmlApplicationContext(config); - } + @Before + public void setUp() { + String[] config = new String[] { "applicationContext.xml" }; + context = new ClassPathXmlApplicationContext(config); + } - @After - public void tearDown() { - context.close(); - } + @After + public void tearDown() { + context.close(); + } - @Test - public void testAttributesCreatePerfMon() { + @Test + public void testAttributesCreatePerfMon() { - DummyService service = (DummyService)context.getBean("dummyService"); - assertNotNull(service); + DummyService service = (DummyService) context.getBean("dummyService"); + assertNotNull(service); - PerformanceMonitor monitor = - (PerformanceMonitor)context.getBean("dummyServicePerformanceMonitor"); - assertNotNull(monitor); + PerformanceMonitor monitor = (PerformanceMonitor) context.getBean("dummyServicePerformanceMonitor"); + assertNotNull(monitor); - service.foo(); - assertEquals(1, monitor.getRequestCount()); + service.foo(); + assertEquals(1, monitor.getRequestCount()); - service.bar(); - assertEquals(2, monitor.getRequestCount()); + service.bar(); + assertEquals(2, monitor.getRequestCount()); - service.baz(); - assertEquals(2, monitor.getRequestCount()); + service.baz(); + assertEquals(2, monitor.getRequestCount()); - MethodInterceptor wrapper = - (MethodInterceptor)context.getBean("dummyServicePerformanceMonitorInterceptor"); - assertNotNull(wrapper); - } + MethodInterceptor wrapper = (MethodInterceptor) context.getBean("dummyServicePerformanceMonitorInterceptor"); + assertNotNull(wrapper); + } - @Test - public void testPerfMonElementCreatedPerfMon() { - PerformanceMonitor monitor = - (PerformanceMonitor)context.getBean("performanceMonitor"); - assertNotNull(monitor); - } + @Test + public void testPerfMonElementCreatedPerfMon() { + PerformanceMonitor monitor = (PerformanceMonitor) context.getBean("performanceMonitor"); + assertNotNull(monitor); + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestMBeanOperationInvoker.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestMBeanOperationInvoker.java index 0a77863d..db5772a7 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestMBeanOperationInvoker.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestMBeanOperationInvoker.java @@ -40,78 +40,79 @@ public class TestMBeanOperationInvoker { - private MBeanServer mockMBeanServer; - private ObjectName mockObjectName; - private MBeanOperationInfo mockOperationInfo; - private MBeanValueConverter mockValueConverter; - - private MBeanOperationInvoker invoker; - - class MockMBeanOperationInvoker extends MBeanOperationInvoker { - MockMBeanOperationInvoker(MBeanServer mBeanServer, ObjectName objectName, MBeanOperationInfo operationInfo) { - super(mBeanServer, objectName, operationInfo); - } - - MBeanValueConverter createMBeanValueConverter(Map parameterMap) { - return mockValueConverter; - } - } - - @Before - public void setUp() { - mockMBeanServer = createMock(MBeanServer.class); - mockObjectName = createMock(ObjectName.class); - mockOperationInfo = createMock(MBeanOperationInfo.class); - mockValueConverter = createMock(MBeanValueConverter.class); - - invoker = new MockMBeanOperationInvoker(mockMBeanServer, mockObjectName, mockOperationInfo); - } - - @Test - public void testConstructor() { - assertEquals(mockMBeanServer, ReflectionTestUtils.getField(invoker, "mBeanServer")); - assertEquals(mockObjectName, ReflectionTestUtils.getField(invoker, "objectName")); - assertEquals(mockOperationInfo, ReflectionTestUtils.getField(invoker, "operationInfo")); - } - - @Test - public void testInvokeOperation() throws JMException { - MBeanParameterInfo mockParameterInfo1 = createMock(MBeanParameterInfo.class); - MBeanParameterInfo mockParameterInfo2 = createMock(MBeanParameterInfo.class); - MBeanParameterInfo[] parameterInfoArray = new MBeanParameterInfo[] { mockParameterInfo1, mockParameterInfo2 }; - - expect(mockOperationInfo.getSignature()).andReturn(parameterInfoArray); - String name1 = "name 1"; - String type1 = "type 1"; - expect(mockParameterInfo1.getType()).andReturn(type1); - expect(mockParameterInfo1.getName()).andReturn(name1); - String value1 = "value 1"; - expect(mockValueConverter.convertParameterValue(name1, type1)).andReturn(value1); - - String name2 = "name 2"; - String type2 = "type 2"; - expect(mockParameterInfo2.getType()).andReturn(type2); - expect(mockParameterInfo2.getName()).andReturn(name2); - String value2 = "value 2"; - expect(mockValueConverter.convertParameterValue(name2, type2)).andReturn(value2); - - String operationName = "some_operation_name"; - expect(mockOperationInfo.getName()).andReturn(operationName); - - Object value = new Object(); - expect(mockMBeanServer.invoke(eq(mockObjectName), eq(operationName), anyObject(String[].class), anyObject(String[].class))).andReturn(value); - - replay(mockMBeanServer, mockObjectName, mockOperationInfo, mockValueConverter, - mockParameterInfo1, mockParameterInfo2); - - Map parameterMap = new HashMap(); - parameterMap.put(name1, new String[] { value1 }); - parameterMap.put(name2, new String[] { value2 }); - - Object invokeResult = invoker.invokeOperation(parameterMap); - - assertEquals(value, invokeResult); - verify(mockMBeanServer, mockObjectName, mockOperationInfo, mockValueConverter, - mockParameterInfo1, mockParameterInfo2); - } + private MBeanServer mockMBeanServer; + private ObjectName mockObjectName; + private MBeanOperationInfo mockOperationInfo; + private MBeanValueConverter mockValueConverter; + + private MBeanOperationInvoker invoker; + + class MockMBeanOperationInvoker extends MBeanOperationInvoker { + MockMBeanOperationInvoker(MBeanServer mBeanServer, ObjectName objectName, MBeanOperationInfo operationInfo) { + super(mBeanServer, objectName, operationInfo); + } + + MBeanValueConverter createMBeanValueConverter(Map parameterMap) { + return mockValueConverter; + } + } + + @Before + public void setUp() { + mockMBeanServer = createMock(MBeanServer.class); + mockObjectName = createMock(ObjectName.class); + mockOperationInfo = createMock(MBeanOperationInfo.class); + mockValueConverter = createMock(MBeanValueConverter.class); + + invoker = new MockMBeanOperationInvoker(mockMBeanServer, mockObjectName, mockOperationInfo); + } + + @Test + public void testConstructor() { + assertEquals(mockMBeanServer, ReflectionTestUtils.getField(invoker, "mBeanServer")); + assertEquals(mockObjectName, ReflectionTestUtils.getField(invoker, "objectName")); + assertEquals(mockOperationInfo, ReflectionTestUtils.getField(invoker, "operationInfo")); + } + + @Test + public void testInvokeOperation() throws JMException { + MBeanParameterInfo mockParameterInfo1 = createMock(MBeanParameterInfo.class); + MBeanParameterInfo mockParameterInfo2 = createMock(MBeanParameterInfo.class); + MBeanParameterInfo[] parameterInfoArray = new MBeanParameterInfo[] { mockParameterInfo1, mockParameterInfo2 }; + + expect(mockOperationInfo.getSignature()).andReturn(parameterInfoArray); + String name1 = "name 1"; + String type1 = "type 1"; + expect(mockParameterInfo1.getType()).andReturn(type1); + expect(mockParameterInfo1.getName()).andReturn(name1); + String value1 = "value 1"; + expect(mockValueConverter.convertParameterValue(name1, type1)).andReturn(value1); + + String name2 = "name 2"; + String type2 = "type 2"; + expect(mockParameterInfo2.getType()).andReturn(type2); + expect(mockParameterInfo2.getName()).andReturn(name2); + String value2 = "value 2"; + expect(mockValueConverter.convertParameterValue(name2, type2)).andReturn(value2); + + String operationName = "some_operation_name"; + expect(mockOperationInfo.getName()).andReturn(operationName); + + Object value = new Object(); + expect(mockMBeanServer.invoke(eq(mockObjectName), eq(operationName), anyObject(String[].class), + anyObject(String[].class))).andReturn(value); + + replay(mockMBeanServer, mockObjectName, mockOperationInfo, mockValueConverter, mockParameterInfo1, + mockParameterInfo2); + + Map parameterMap = new HashMap(); + parameterMap.put(name1, new String[] { value1 }); + parameterMap.put(name2, new String[] { value2 }); + + Object invokeResult = invoker.invokeOperation(parameterMap); + + assertEquals(value, invokeResult); + verify(mockMBeanServer, mockObjectName, mockOperationInfo, mockValueConverter, mockParameterInfo1, + mockParameterInfo2); + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestMBeanStringSanitizer.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestMBeanStringSanitizer.java index ca357fc1..02a12bd0 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestMBeanStringSanitizer.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestMBeanStringSanitizer.java @@ -25,34 +25,34 @@ public class TestMBeanStringSanitizer { - private MBeanStringSanitizer sanitizer; - - @Before - public void setUp() { - sanitizer = new MBeanStringSanitizer(); - } - - @Test - public void testUrlDecodeDecodesSlashes() throws Exception { - String testString = "this%2Fhas%2Fslashes"; - String sanitizedString = sanitizer.urlDecode(testString, "UTF-8"); - assertEquals("this/has/slashes", sanitizedString); - } - - @Test(expected= UnsupportedEncodingException.class) - public void testUrlDecodeThrowsUnsupportedEncodingException() throws Exception { - sanitizer.urlDecode("some_string_with_encoding_%2F", "unsupported_encoding"); - } - - @Test - public void testEscapeValueEscapesValues() { - String testString = "thisevil&characters"; - String escapedString = sanitizer.escapeValue(testString); - assertEquals("this<contains>evil&characters", escapedString); - } - - @Test - public void testEscapeValueEscapesNulls() { - assertEquals("<null>", sanitizer.escapeValue(null)); - } + private MBeanStringSanitizer sanitizer; + + @Before + public void setUp() { + sanitizer = new MBeanStringSanitizer(); + } + + @Test + public void testUrlDecodeDecodesSlashes() throws Exception { + String testString = "this%2Fhas%2Fslashes"; + String sanitizedString = sanitizer.urlDecode(testString, "UTF-8"); + assertEquals("this/has/slashes", sanitizedString); + } + + @Test(expected = UnsupportedEncodingException.class) + public void testUrlDecodeThrowsUnsupportedEncodingException() throws Exception { + sanitizer.urlDecode("some_string_with_encoding_%2F", "unsupported_encoding"); + } + + @Test + public void testEscapeValueEscapesValues() { + String testString = "thisevil&characters"; + String escapedString = sanitizer.escapeValue(testString); + assertEquals("this<contains>evil&characters", escapedString); + } + + @Test + public void testEscapeValueEscapesNulls() { + assertEquals("<null>", sanitizer.escapeValue(null)); + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestMBeanValueConverter.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestMBeanValueConverter.java index f0605747..73b0f799 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestMBeanValueConverter.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestMBeanValueConverter.java @@ -25,64 +25,64 @@ public class TestMBeanValueConverter { - private MBeanValueConverter converter; + private MBeanValueConverter converter; - @Before - public void setUp() { + @Before + public void setUp() { - Map parameterMap = new HashMap(); - parameterMap.put("nullString", new String[] { "" }); - parameterMap.put("stringValue", new String[] { "some_string" }); - parameterMap.put("booleanValue", new String[] { "true" }); - parameterMap.put("intValue", new String[] { "123" }); - parameterMap.put("longValue", new String[] { "456" }); - parameterMap.put("floatValue", new String[] { "123.45" }); - parameterMap.put("doubleValue", new String[] { "456.78" }); - converter = new MBeanValueConverter(parameterMap); - } + Map parameterMap = new HashMap(); + parameterMap.put("nullString", new String[] { "" }); + parameterMap.put("stringValue", new String[] { "some_string" }); + parameterMap.put("booleanValue", new String[] { "true" }); + parameterMap.put("intValue", new String[] { "123" }); + parameterMap.put("longValue", new String[] { "456" }); + parameterMap.put("floatValue", new String[] { "123.45" }); + parameterMap.put("doubleValue", new String[] { "456.78" }); + converter = new MBeanValueConverter(parameterMap); + } - @Test - public void testNonProvidedValue() throws Exception { - assertEquals(null, converter.convertParameterValue("not_in_the_map", "")); - } + @Test + public void testNonProvidedValue() throws Exception { + assertEquals(null, converter.convertParameterValue("not_in_the_map", "")); + } - @Test - public void testNullString() throws Exception { - assertEquals(null, converter.convertParameterValue("nullString", "any_type_will_do")); - } + @Test + public void testNullString() throws Exception { + assertEquals(null, converter.convertParameterValue("nullString", "any_type_will_do")); + } - @Test - public void testStringValue() throws Exception { - assertEquals("some_string", converter.convertParameterValue("stringValue", "java.lang.String")); - } + @Test + public void testStringValue() throws Exception { + assertEquals("some_string", converter.convertParameterValue("stringValue", "java.lang.String")); + } - @Test - public void testBooleanValue() throws Exception { - assertEquals(true, converter.convertParameterValue("booleanValue", "boolean")); - } + @Test + public void testBooleanValue() throws Exception { + assertEquals(true, converter.convertParameterValue("booleanValue", "boolean")); + } - @Test - public void testIntValue() throws Exception { - assertEquals(123, converter.convertParameterValue("intValue", "int")); - } + @Test + public void testIntValue() throws Exception { + assertEquals(123, converter.convertParameterValue("intValue", "int")); + } - @Test - public void testLongValue() throws Exception { - assertEquals((long)456, converter.convertParameterValue("longValue", "long")); - } + @Test + public void testLongValue() throws Exception { + assertEquals((long) 456, converter.convertParameterValue("longValue", "long")); + } - @Test - public void testFloatValue() throws Exception { - assertEquals((float)123.45, converter.convertParameterValue("floatValue", "float")); - } + @Test + public void testFloatValue() throws Exception { + assertEquals((float) 123.45, converter.convertParameterValue("floatValue", "float")); + } - @Test - public void testDoubleValue() throws Exception { - assertEquals(456.78, converter.convertParameterValue("doubleValue", "double")); - } + @Test + public void testDoubleValue() throws Exception { + assertEquals(456.78, converter.convertParameterValue("doubleValue", "double")); + } - @Test(expected=UnhandledParameterTypeException.class) - public void testUnhandledType() throws Exception { - converter.convertParameterValue("stringValue", "unknown_type"); - } + @Test(expected = UnhandledParameterTypeException.class) + public void testUnhandledType() throws Exception { + converter.convertParameterValue("stringValue", "unknown_type"); + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestWebMBeanAdapter.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestWebMBeanAdapter.java index 12b4a566..9efa7b6d 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestWebMBeanAdapter.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestWebMBeanAdapter.java @@ -45,253 +45,249 @@ public class TestWebMBeanAdapter { - private MBeanServer mockMBeanServer; - private MBeanStringSanitizer mockSanitizer; - private WebMBeanAdapter webMBeanAdapter; - private ObjectName mockObjectName; - private MBeanInfo mockMBeanInfo; - private MBeanOperationInvoker mockMBeanOperationInvoker; - private static final String ENCODING = "UTF-8"; - - class MockWebMBeanAdapter extends WebMBeanAdapter { - public MockWebMBeanAdapter(MBeanServer mBeanServer, String mBeanName) - throws JMException, UnsupportedEncodingException { - super(mBeanServer, mBeanName, ENCODING); - } - @Override - MBeanStringSanitizer createMBeanStringSanitizer() { - return mockSanitizer; - } - @Override - ObjectName createObjectName(String name) { - return mockObjectName; - } - @Override - MBeanOperationInvoker createMBeanOperationInvoker( - MBeanServer mBeanServer, ObjectName objectName, MBeanOperationInfo operationInfo) { - return mockMBeanOperationInvoker; - } - } - - @Before - public void setUp() throws Exception { - mockMBeanServer = createMock(MBeanServer.class); - mockSanitizer = createMock(MBeanStringSanitizer.class); - mockMBeanInfo = createMock(MBeanInfo.class); - mockObjectName = createMock(ObjectName.class); - mockMBeanOperationInvoker = createMock(MBeanOperationInvoker.class); - - String beanName = "some_bean_name"; - - expect(mockSanitizer.urlDecode(beanName, ENCODING)).andReturn(beanName); - expect(mockMBeanServer.getMBeanInfo(mockObjectName)).andReturn(mockMBeanInfo); - replay(mockSanitizer, mockMBeanServer); - - webMBeanAdapter = new MockWebMBeanAdapter(mockMBeanServer, beanName); - - verify(mockMBeanServer, mockSanitizer); - reset(mockMBeanServer, mockSanitizer); - } - - @Test - public void testConstructor() throws Exception { - assertEquals(mockMBeanServer, ReflectionTestUtils.getField(webMBeanAdapter, "mBeanServer")); - assertEquals(mockObjectName, ReflectionTestUtils.getField(webMBeanAdapter, "objectName")); - assertEquals(mockMBeanInfo, ReflectionTestUtils.getField(webMBeanAdapter, "mBeanInfo")); - } - - @Test - public void testGetAttributeMetadata() throws Exception { - - String attributeName1 = "attribute_name_1"; - MBeanAttributeInfo mockAttribute1 = createMock(MBeanAttributeInfo.class); - - String attributeName2 = "attribute_name_2"; - MBeanAttributeInfo mockAttribute2 = createMock(MBeanAttributeInfo.class); - - MBeanAttributeInfo[] attributeList = new MBeanAttributeInfo[2]; - attributeList[0] = mockAttribute1; - attributeList[1] = mockAttribute2; - expect(mockMBeanInfo.getAttributes()).andReturn(attributeList); - expect(mockAttribute1.getName()).andReturn(attributeName1); - expect(mockAttribute2.getName()).andReturn(attributeName2); - - replay(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, - mockAttribute1, mockAttribute2); - - Map attributeMap = webMBeanAdapter.getAttributeMetadata(); - - assertEquals(2, attributeMap.size()); - assertEquals(mockAttribute1, attributeMap.get(attributeName1)); - assertEquals(mockAttribute2, attributeMap.get(attributeName2)); - - verify(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, - mockAttribute1, mockAttribute2); - } - - @Test - public void testGetOperationMetadata() throws Exception { - String operationName1 = "operation_name_1"; - MBeanOperationInfo mockOperation1 = createMock(MBeanOperationInfo.class); - - String operationName2 = "operation_name_2"; - MBeanOperationInfo mockOperation2 = createMock(MBeanOperationInfo.class); - - MBeanOperationInfo[] operationList = new MBeanOperationInfo[2]; - operationList[0] = mockOperation1; - operationList[1] = mockOperation2; - expect(mockMBeanInfo.getOperations()).andReturn(operationList); - expect(mockOperation1.getName()).andReturn(operationName1); - expect(mockOperation2.getName()).andReturn(operationName2); - - replay(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, - mockOperation1, mockOperation2); - - Map operationMap = webMBeanAdapter.getOperationMetadata(); - - assertEquals(2, operationMap.size()); - assertEquals(mockOperation1, operationMap.get(operationName1)); - assertEquals(mockOperation2, operationMap.get(operationName2)); - - verify(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, - mockOperation1, mockOperation2); - } - - @Test - public void testGetOperationInfoWhenItExists() throws Exception { - String operationName1 = "operation_name_1"; - MBeanOperationInfo mockOperation1 = createMock(MBeanOperationInfo.class); - - String operationName2 = "operation_name_2"; - MBeanOperationInfo mockOperation2 = createMock(MBeanOperationInfo.class); - - expect(mockSanitizer.urlDecode(operationName2, ENCODING)).andReturn(operationName2); - MBeanOperationInfo[] operationList = new MBeanOperationInfo[2]; - operationList[0] = mockOperation1; - operationList[1] = mockOperation2; - expect(mockMBeanInfo.getOperations()).andReturn(operationList); - expect(mockOperation1.getName()).andReturn(operationName1); - expect(mockOperation2.getName()).andReturn(operationName2); + private MBeanServer mockMBeanServer; + private MBeanStringSanitizer mockSanitizer; + private WebMBeanAdapter webMBeanAdapter; + private ObjectName mockObjectName; + private MBeanInfo mockMBeanInfo; + private MBeanOperationInvoker mockMBeanOperationInvoker; + private static final String ENCODING = "UTF-8"; + + class MockWebMBeanAdapter extends WebMBeanAdapter { + public MockWebMBeanAdapter(MBeanServer mBeanServer, String mBeanName) + throws JMException, UnsupportedEncodingException { + super(mBeanServer, mBeanName, ENCODING); + } + + @Override + MBeanStringSanitizer createMBeanStringSanitizer() { + return mockSanitizer; + } + + @Override + ObjectName createObjectName(String name) { + return mockObjectName; + } + + @Override + MBeanOperationInvoker createMBeanOperationInvoker(MBeanServer mBeanServer, ObjectName objectName, + MBeanOperationInfo operationInfo) { + return mockMBeanOperationInvoker; + } + } + + @Before + public void setUp() throws Exception { + mockMBeanServer = createMock(MBeanServer.class); + mockSanitizer = createMock(MBeanStringSanitizer.class); + mockMBeanInfo = createMock(MBeanInfo.class); + mockObjectName = createMock(ObjectName.class); + mockMBeanOperationInvoker = createMock(MBeanOperationInvoker.class); + + String beanName = "some_bean_name"; + + expect(mockSanitizer.urlDecode(beanName, ENCODING)).andReturn(beanName); + expect(mockMBeanServer.getMBeanInfo(mockObjectName)).andReturn(mockMBeanInfo); + replay(mockSanitizer, mockMBeanServer); + + webMBeanAdapter = new MockWebMBeanAdapter(mockMBeanServer, beanName); - replay(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, - mockOperation1, mockOperation2); + verify(mockMBeanServer, mockSanitizer); + reset(mockMBeanServer, mockSanitizer); + } - MBeanOperationInfo operationInfo = webMBeanAdapter.getOperationInfo(operationName2); + @Test + public void testConstructor() throws Exception { + assertEquals(mockMBeanServer, ReflectionTestUtils.getField(webMBeanAdapter, "mBeanServer")); + assertEquals(mockObjectName, ReflectionTestUtils.getField(webMBeanAdapter, "objectName")); + assertEquals(mockMBeanInfo, ReflectionTestUtils.getField(webMBeanAdapter, "mBeanInfo")); + } + + @Test + public void testGetAttributeMetadata() throws Exception { + + String attributeName1 = "attribute_name_1"; + MBeanAttributeInfo mockAttribute1 = createMock(MBeanAttributeInfo.class); + + String attributeName2 = "attribute_name_2"; + MBeanAttributeInfo mockAttribute2 = createMock(MBeanAttributeInfo.class); + + MBeanAttributeInfo[] attributeList = new MBeanAttributeInfo[2]; + attributeList[0] = mockAttribute1; + attributeList[1] = mockAttribute2; + expect(mockMBeanInfo.getAttributes()).andReturn(attributeList); + expect(mockAttribute1.getName()).andReturn(attributeName1); + expect(mockAttribute2.getName()).andReturn(attributeName2); + + replay(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, mockAttribute1, mockAttribute2); + + Map attributeMap = webMBeanAdapter.getAttributeMetadata(); + + assertEquals(2, attributeMap.size()); + assertEquals(mockAttribute1, attributeMap.get(attributeName1)); + assertEquals(mockAttribute2, attributeMap.get(attributeName2)); + + verify(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, mockAttribute1, mockAttribute2); + } + + @Test + public void testGetOperationMetadata() throws Exception { + String operationName1 = "operation_name_1"; + MBeanOperationInfo mockOperation1 = createMock(MBeanOperationInfo.class); + + String operationName2 = "operation_name_2"; + MBeanOperationInfo mockOperation2 = createMock(MBeanOperationInfo.class); + + MBeanOperationInfo[] operationList = new MBeanOperationInfo[2]; + operationList[0] = mockOperation1; + operationList[1] = mockOperation2; + expect(mockMBeanInfo.getOperations()).andReturn(operationList); + expect(mockOperation1.getName()).andReturn(operationName1); + expect(mockOperation2.getName()).andReturn(operationName2); + + replay(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, mockOperation1, mockOperation2); + + Map operationMap = webMBeanAdapter.getOperationMetadata(); + + assertEquals(2, operationMap.size()); + assertEquals(mockOperation1, operationMap.get(operationName1)); + assertEquals(mockOperation2, operationMap.get(operationName2)); - assertEquals(mockOperation2, operationInfo); + verify(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, mockOperation1, mockOperation2); + } + + @Test + public void testGetOperationInfoWhenItExists() throws Exception { + String operationName1 = "operation_name_1"; + MBeanOperationInfo mockOperation1 = createMock(MBeanOperationInfo.class); - verify(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, - mockOperation1, mockOperation2); - } + String operationName2 = "operation_name_2"; + MBeanOperationInfo mockOperation2 = createMock(MBeanOperationInfo.class); - @Test(expected=OperationNotFoundException.class) - public void testGetOperationInfoThrowsOperationNotFoundException() throws Exception { - String operationName = "nonexistent"; - expect(mockObjectName.getCanonicalName()).andReturn("some_name"); + expect(mockSanitizer.urlDecode(operationName2, ENCODING)).andReturn(operationName2); + MBeanOperationInfo[] operationList = new MBeanOperationInfo[2]; + operationList[0] = mockOperation1; + operationList[1] = mockOperation2; + expect(mockMBeanInfo.getOperations()).andReturn(operationList); + expect(mockOperation1.getName()).andReturn(operationName1); + expect(mockOperation2.getName()).andReturn(operationName2); - MBeanOperationInfo[] operationList = new MBeanOperationInfo[0]; - expect(mockSanitizer.urlDecode(operationName, ENCODING)).andReturn(operationName); - expect(mockMBeanInfo.getOperations()).andReturn(operationList); + replay(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, mockOperation1, mockOperation2); - replay(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo); + MBeanOperationInfo operationInfo = webMBeanAdapter.getOperationInfo(operationName2); - webMBeanAdapter.getOperationInfo(operationName); - } + assertEquals(mockOperation2, operationInfo); - @Test - public void testGetAttributeValues() throws Exception { - MBeanAttributeInfo[] attributeInfoArray = new MBeanAttributeInfo[2]; - MBeanAttributeInfo mockAttributeInfo1 = createMock(MBeanAttributeInfo.class); - MBeanAttributeInfo mockAttributeInfo2 = createMock(MBeanAttributeInfo.class); - attributeInfoArray[0] = mockAttributeInfo1; - attributeInfoArray[1] = mockAttributeInfo2; - expect(mockMBeanInfo.getAttributes()).andReturn(attributeInfoArray); + verify(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, mockOperation1, mockOperation2); + } - String attributeName1 = "attribute_name_1"; - String attributeName2 = "attribute_name_2"; - expect(mockAttributeInfo1.getName()).andReturn(attributeName1); - expect(mockAttributeInfo2.getName()).andReturn(attributeName2); + @Test(expected = OperationNotFoundException.class) + public void testGetOperationInfoThrowsOperationNotFoundException() throws Exception { + String operationName = "nonexistent"; + expect(mockObjectName.getCanonicalName()).andReturn("some_name"); - AttributeList mockAttributeList = createMock(AttributeList.class); - expect(mockMBeanServer.getAttributes(eq(mockObjectName), anyObject(String[].class))).andReturn(mockAttributeList); + MBeanOperationInfo[] operationList = new MBeanOperationInfo[0]; + expect(mockSanitizer.urlDecode(operationName, ENCODING)).andReturn(operationName); + expect(mockMBeanInfo.getOperations()).andReturn(operationList); - List attributeList = new ArrayList(); - Attribute mockAttribute1 = createMock(Attribute.class); - Attribute mockAttribute2 = createMock(Attribute.class); + replay(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo); - attributeList.add(mockAttribute1); - attributeList.add(mockAttribute2); + webMBeanAdapter.getOperationInfo(operationName); + } - expect(mockAttributeList.asList()).andReturn(attributeList); + @Test + public void testGetAttributeValues() throws Exception { + MBeanAttributeInfo[] attributeInfoArray = new MBeanAttributeInfo[2]; + MBeanAttributeInfo mockAttributeInfo1 = createMock(MBeanAttributeInfo.class); + MBeanAttributeInfo mockAttributeInfo2 = createMock(MBeanAttributeInfo.class); + attributeInfoArray[0] = mockAttributeInfo1; + attributeInfoArray[1] = mockAttributeInfo2; + expect(mockMBeanInfo.getAttributes()).andReturn(attributeInfoArray); - String name1 = "name 1"; - String value1 = "value 1"; - expect(mockAttribute1.getName()).andReturn(name1); - expect(mockAttribute1.getValue()).andReturn(value1); - expect(mockSanitizer.escapeValue(value1)).andReturn(value1); + String attributeName1 = "attribute_name_1"; + String attributeName2 = "attribute_name_2"; + expect(mockAttributeInfo1.getName()).andReturn(attributeName1); + expect(mockAttributeInfo2.getName()).andReturn(attributeName2); - String name2 = "name 2"; - String value2 = "value 2"; - expect(mockAttribute2.getName()).andReturn(name2); - expect(mockAttribute2.getValue()).andReturn(value2); - expect(mockSanitizer.escapeValue(value2)).andReturn(value2); + AttributeList mockAttributeList = createMock(AttributeList.class); + expect(mockMBeanServer.getAttributes(eq(mockObjectName), anyObject(String[].class))) + .andReturn(mockAttributeList); - replay(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, - mockAttributeInfo1, mockAttributeInfo2, mockAttributeList, - mockAttribute1, mockAttribute2); + List attributeList = new ArrayList(); + Attribute mockAttribute1 = createMock(Attribute.class); + Attribute mockAttribute2 = createMock(Attribute.class); - Map attributeValueMap = webMBeanAdapter.getAttributeValues(); + attributeList.add(mockAttribute1); + attributeList.add(mockAttribute2); - assertEquals(2, attributeValueMap.size()); - assertEquals(value1, attributeValueMap.get(name1)); - assertEquals(value2, attributeValueMap.get(name2)); + expect(mockAttributeList.asList()).andReturn(attributeList); - verify(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, - mockAttributeInfo1, mockAttributeInfo2, mockAttributeList, - mockAttribute1, mockAttribute2); - } + String name1 = "name 1"; + String value1 = "value 1"; + expect(mockAttribute1.getName()).andReturn(name1); + expect(mockAttribute1.getValue()).andReturn(value1); + expect(mockSanitizer.escapeValue(value1)).andReturn(value1); - @Test - public void testGetAttributeValue() throws Exception { - String attributeName = "attribute_name"; - expect(mockSanitizer.urlDecode(attributeName, ENCODING)).andReturn(attributeName); + String name2 = "name 2"; + String value2 = "value 2"; + expect(mockAttribute2.getName()).andReturn(name2); + expect(mockAttribute2.getValue()).andReturn(value2); + expect(mockSanitizer.escapeValue(value2)).andReturn(value2); - Object value = new Object(); - String valueString = "some_string"; - expect(mockMBeanServer.getAttribute(mockObjectName, attributeName)).andReturn(value); - expect(mockSanitizer.escapeValue(value)).andReturn(valueString); + replay(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, mockAttributeInfo1, mockAttributeInfo2, + mockAttributeList, mockAttribute1, mockAttribute2); - replay(mockMBeanServer, mockSanitizer, mockObjectName); + Map attributeValueMap = webMBeanAdapter.getAttributeValues(); - String attributeValue = webMBeanAdapter.getAttributeValue(attributeName); + assertEquals(2, attributeValueMap.size()); + assertEquals(value1, attributeValueMap.get(name1)); + assertEquals(value2, attributeValueMap.get(name2)); - assertEquals(valueString, attributeValue); - verify(mockMBeanServer, mockSanitizer, mockObjectName); - } + verify(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, mockAttributeInfo1, mockAttributeInfo2, + mockAttributeList, mockAttribute1, mockAttribute2); + } - @Test - public void testInvokeOperation() throws Exception { - String operationName = "operation_name"; - expect(mockSanitizer.urlDecode(operationName, ENCODING)).andReturn(operationName); + @Test + public void testGetAttributeValue() throws Exception { + String attributeName = "attribute_name"; + expect(mockSanitizer.urlDecode(attributeName, ENCODING)).andReturn(attributeName); - MBeanOperationInfo mockOperation = createMock(MBeanOperationInfo.class); - MBeanOperationInfo[] operationList = new MBeanOperationInfo[1]; - operationList[0] = mockOperation; - expect(mockMBeanInfo.getOperations()).andReturn(operationList); - expect(mockOperation.getName()).andReturn(operationName); + Object value = new Object(); + String valueString = "some_string"; + expect(mockMBeanServer.getAttribute(mockObjectName, attributeName)).andReturn(value); + expect(mockSanitizer.escapeValue(value)).andReturn(valueString); - Map parameterMap = new HashMap(); - Object value = new Object(); - expect(mockMBeanOperationInvoker.invokeOperation(parameterMap)).andReturn(value); + replay(mockMBeanServer, mockSanitizer, mockObjectName); - String valueString = "some_value"; - expect(mockSanitizer.escapeValue(value)).andReturn(valueString); + String attributeValue = webMBeanAdapter.getAttributeValue(attributeName); - replay(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, mockOperation, mockMBeanOperationInvoker); + assertEquals(valueString, attributeValue); + verify(mockMBeanServer, mockSanitizer, mockObjectName); + } - String invokeResult = webMBeanAdapter.invokeOperation(operationName, parameterMap); + @Test + public void testInvokeOperation() throws Exception { + String operationName = "operation_name"; + expect(mockSanitizer.urlDecode(operationName, ENCODING)).andReturn(operationName); - assertEquals(valueString, invokeResult); - verify(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, mockOperation, mockMBeanOperationInvoker); - } + MBeanOperationInfo mockOperation = createMock(MBeanOperationInfo.class); + MBeanOperationInfo[] operationList = new MBeanOperationInfo[1]; + operationList[0] = mockOperation; + expect(mockMBeanInfo.getOperations()).andReturn(operationList); + expect(mockOperation.getName()).andReturn(operationName); + + Map parameterMap = new HashMap(); + Object value = new Object(); + expect(mockMBeanOperationInvoker.invokeOperation(parameterMap)).andReturn(value); + + String valueString = "some_value"; + expect(mockSanitizer.escapeValue(value)).andReturn(valueString); + + replay(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, mockOperation, mockMBeanOperationInvoker); + + String invokeResult = webMBeanAdapter.invokeOperation(operationName, parameterMap); + + assertEquals(valueString, invokeResult); + verify(mockMBeanServer, mockSanitizer, mockObjectName, mockMBeanInfo, mockOperation, mockMBeanOperationInvoker); + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestWebMBeanServerAdapter.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestWebMBeanServerAdapter.java index a01d7844..29ce5a6e 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestWebMBeanServerAdapter.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/jmx/TestWebMBeanServerAdapter.java @@ -36,53 +36,54 @@ public class TestWebMBeanServerAdapter { - private MBeanServer mockMbeanServer; - private WebMBeanServerAdapter webMBeanServerAdapter; - private MBeanStringSanitizer mockSanitizer; - - class MockWebMBeanServerAdapter extends WebMBeanServerAdapter { - public MockWebMBeanServerAdapter(MBeanServer mBeanServer) { - super(mBeanServer); - } - @Override - MBeanStringSanitizer createMBeanStringSanitizer() { - return mockSanitizer; - } - } - - @Before - public void setUp() { - mockMbeanServer = createMock(MBeanServer.class); - mockSanitizer = createMock(MBeanStringSanitizer.class); - webMBeanServerAdapter = new MockWebMBeanServerAdapter(mockMbeanServer); - } - - @Test - public void testGetMBeanNames() throws Exception { - String name1 = "com.test:type=objectName,value=1"; - String name2 = "com.test:type=objectName,value=2"; - ObjectName objectName1 = new ObjectName(name1); - ObjectInstance object1 = createMock(ObjectInstance.class); - expect(object1.getObjectName()).andReturn(objectName1); - - ObjectName objectName2 = new ObjectName(name2); - ObjectInstance object2 = createMock(ObjectInstance.class); - expect(object2.getObjectName()).andReturn(objectName2); - - Set objectInstanceList = new HashSet(); - objectInstanceList.add(object1); - objectInstanceList.add(object2); - expect(mockMbeanServer.queryMBeans(null, null)).andReturn(objectInstanceList); - expect(mockSanitizer.escapeValue(name1)).andReturn(name1); - expect(mockSanitizer.escapeValue(name2)).andReturn(name2); - - replay(mockMbeanServer, mockSanitizer, object1, object2); - - Set resultSet = webMBeanServerAdapter.getMBeanNames(); - - assertEquals(2, resultSet.size()); - assertTrue(resultSet.contains(name1)); - assertTrue(resultSet.contains(name2)); - verify(mockMbeanServer, mockSanitizer, object1, object2); - } + private MBeanServer mockMbeanServer; + private WebMBeanServerAdapter webMBeanServerAdapter; + private MBeanStringSanitizer mockSanitizer; + + class MockWebMBeanServerAdapter extends WebMBeanServerAdapter { + public MockWebMBeanServerAdapter(MBeanServer mBeanServer) { + super(mBeanServer); + } + + @Override + MBeanStringSanitizer createMBeanStringSanitizer() { + return mockSanitizer; + } + } + + @Before + public void setUp() { + mockMbeanServer = createMock(MBeanServer.class); + mockSanitizer = createMock(MBeanStringSanitizer.class); + webMBeanServerAdapter = new MockWebMBeanServerAdapter(mockMbeanServer); + } + + @Test + public void testGetMBeanNames() throws Exception { + String name1 = "com.test:type=objectName,value=1"; + String name2 = "com.test:type=objectName,value=2"; + ObjectName objectName1 = new ObjectName(name1); + ObjectInstance object1 = createMock(ObjectInstance.class); + expect(object1.getObjectName()).andReturn(objectName1); + + ObjectName objectName2 = new ObjectName(name2); + ObjectInstance object2 = createMock(ObjectInstance.class); + expect(object2.getObjectName()).andReturn(objectName2); + + Set objectInstanceList = new HashSet(); + objectInstanceList.add(object1); + objectInstanceList.add(object2); + expect(mockMbeanServer.queryMBeans(null, null)).andReturn(objectInstanceList); + expect(mockSanitizer.escapeValue(name1)).andReturn(name1); + expect(mockSanitizer.escapeValue(name2)).andReturn(name2); + + replay(mockMbeanServer, mockSanitizer, object1, object2); + + Set resultSet = webMBeanServerAdapter.getMBeanNames(); + + assertEquals(2, resultSet.size()); + assertTrue(resultSet.contains(name1)); + assertTrue(resultSet.contains(name2)); + verify(mockMbeanServer, mockSanitizer, object1, object2); + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/retry/ClassifierSimpleRetryPolicyTest.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/retry/ClassifierSimpleRetryPolicyTest.java index a2fa23bb..d863060e 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/retry/ClassifierSimpleRetryPolicyTest.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/retry/ClassifierSimpleRetryPolicyTest.java @@ -22,38 +22,38 @@ import org.springframework.retry.RetryContext; public class ClassifierSimpleRetryPolicyTest { - @Test - public void test_classify() { - RetryContext context = Mockito.mock(RetryContext.class); - Classifier classifier = Mockito.mock(Classifier.class); - ClassifierSimpleRetryPolicy policy = new ClassifierSimpleRetryPolicy(classifier); - Mockito.when(context.getLastThrowable()).thenReturn(new RuntimeException()); - Mockito.when(classifier.classify(Mockito.any(Throwable.class))).thenReturn(true); - Assert.assertTrue(policy.canRetry(context)); - Assert.assertSame(classifier, policy.getClassifier()); - } - - @Test - public void test_classify_nullClassifier() { - RetryContext context = Mockito.mock(RetryContext.class); - ClassifierSimpleRetryPolicy policy = new ClassifierSimpleRetryPolicy(null); - Mockito.when(context.getLastThrowable()).thenReturn(new RuntimeException()); - Assert.assertFalse(policy.canRetry(context)); - } - - @Test - public void test_classify_nullClassifier2() { - RetryContext context = Mockito.mock(RetryContext.class); - ClassifierSimpleRetryPolicy policy = new ClassifierSimpleRetryPolicy(); - Mockito.when(context.getLastThrowable()).thenReturn(new RuntimeException()); - Assert.assertFalse(policy.canRetry(context)); - } - - @Test - public void test_classify_nullClassifier3() { - RetryContext context = Mockito.mock(RetryContext.class); - ClassifierSimpleRetryPolicy policy = new ClassifierSimpleRetryPolicy(4); - Mockito.when(context.getLastThrowable()).thenReturn(new RuntimeException()); - Assert.assertFalse(policy.canRetry(context)); - } + @Test + public void test_classify() { + RetryContext context = Mockito.mock(RetryContext.class); + Classifier classifier = Mockito.mock(Classifier.class); + ClassifierSimpleRetryPolicy policy = new ClassifierSimpleRetryPolicy(classifier); + Mockito.when(context.getLastThrowable()).thenReturn(new RuntimeException()); + Mockito.when(classifier.classify(Mockito.any(Throwable.class))).thenReturn(true); + Assert.assertTrue(policy.canRetry(context)); + Assert.assertSame(classifier, policy.getClassifier()); + } + + @Test + public void test_classify_nullClassifier() { + RetryContext context = Mockito.mock(RetryContext.class); + ClassifierSimpleRetryPolicy policy = new ClassifierSimpleRetryPolicy(null); + Mockito.when(context.getLastThrowable()).thenReturn(new RuntimeException()); + Assert.assertFalse(policy.canRetry(context)); + } + + @Test + public void test_classify_nullClassifier2() { + RetryContext context = Mockito.mock(RetryContext.class); + ClassifierSimpleRetryPolicy policy = new ClassifierSimpleRetryPolicy(); + Mockito.when(context.getLastThrowable()).thenReturn(new RuntimeException()); + Assert.assertFalse(policy.canRetry(context)); + } + + @Test + public void test_classify_nullClassifier3() { + RetryContext context = Mockito.mock(RetryContext.class); + ClassifierSimpleRetryPolicy policy = new ClassifierSimpleRetryPolicy(4); + Mockito.when(context.getLastThrowable()).thenReturn(new RuntimeException()); + Assert.assertFalse(policy.canRetry(context)); + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/retry/ExtendedPredicatesTest.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/retry/ExtendedPredicatesTest.java index 9a73f29f..a8e38570 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/retry/ExtendedPredicatesTest.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/retry/ExtendedPredicatesTest.java @@ -23,29 +23,29 @@ public class ExtendedPredicatesTest { - @Test - public void test_isInstanceOf() { - Predicate t = ExtendedPredicates.isInstanceOf(Throwable.class, RuntimeException.class); - Assert.assertTrue(t.apply(new RuntimeException())); - Assert.assertFalse(t.apply(new IOException())); - } - - @Test - public void test_throwableContainsMessage_sensitive() { - Predicate t = ExtendedPredicates.throwableContainsMessage("foo", true); - Assert.assertTrue(t.apply(new RuntimeException("foo"))); - Assert.assertFalse(t.apply(new RuntimeException("Foo"))); - Assert.assertFalse(t.apply(new RuntimeException("bar"))); - Assert.assertFalse(t.apply(new RuntimeException("Bar"))); - } - - @Test - public void test_throwableContainsMessage_insensitive() { - Predicate t = ExtendedPredicates.throwableContainsMessage("foo", false); - Assert.assertTrue(t.apply(new RuntimeException("Foo"))); - Assert.assertTrue(t.apply(new RuntimeException("foo"))); - Assert.assertFalse(t.apply(new RuntimeException("bar"))); - Assert.assertFalse(t.apply(new RuntimeException("Bar"))); - } + @Test + public void test_isInstanceOf() { + Predicate t = ExtendedPredicates.isInstanceOf(Throwable.class, RuntimeException.class); + Assert.assertTrue(t.apply(new RuntimeException())); + Assert.assertFalse(t.apply(new IOException())); + } + + @Test + public void test_throwableContainsMessage_sensitive() { + Predicate t = ExtendedPredicates.throwableContainsMessage("foo", true); + Assert.assertTrue(t.apply(new RuntimeException("foo"))); + Assert.assertFalse(t.apply(new RuntimeException("Foo"))); + Assert.assertFalse(t.apply(new RuntimeException("bar"))); + Assert.assertFalse(t.apply(new RuntimeException("Bar"))); + } + + @Test + public void test_throwableContainsMessage_insensitive() { + Predicate t = ExtendedPredicates.throwableContainsMessage("foo", false); + Assert.assertTrue(t.apply(new RuntimeException("Foo"))); + Assert.assertTrue(t.apply(new RuntimeException("foo"))); + Assert.assertFalse(t.apply(new RuntimeException("bar"))); + Assert.assertFalse(t.apply(new RuntimeException("Bar"))); + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/retry/ExtendedRetryTemplateTest.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/retry/ExtendedRetryTemplateTest.java index 02d6aac4..889da903 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/retry/ExtendedRetryTemplateTest.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/retry/ExtendedRetryTemplateTest.java @@ -26,72 +26,71 @@ import org.springframework.retry.RetryState; public class ExtendedRetryTemplateTest { - @Test - public void test_asCallable_callable() throws Exception { - Callable callable = Mockito.mock(Callable.class); - ExtendedRetryTemplate template = new ExtendedRetryTemplate(); + @Test + public void test_asCallable_callable() throws Exception { + Callable callable = Mockito.mock(Callable.class); + ExtendedRetryTemplate template = new ExtendedRetryTemplate(); - Mockito.when(callable.call()).thenReturn(10L); + Mockito.when(callable.call()).thenReturn(10L); - Callable wrapped = template.asCallable(callable); - Assert.assertEquals(10L, wrapped.call().longValue()); + Callable wrapped = template.asCallable(callable); + Assert.assertEquals(10L, wrapped.call().longValue()); - Mockito.verify(callable, Mockito.times(1)).call(); - } + Mockito.verify(callable, Mockito.times(1)).call(); + } - @Test - public void test_asCallable_callback() throws Exception { - RetryCallback callback = Mockito.mock(RetryCallback.class); - ExtendedRetryTemplate template = new ExtendedRetryTemplate(); + @Test + public void test_asCallable_callback() throws Exception { + RetryCallback callback = Mockito.mock(RetryCallback.class); + ExtendedRetryTemplate template = new ExtendedRetryTemplate(); - Mockito.when(callback.doWithRetry(Mockito.any(RetryContext.class))).thenReturn(10L); + Mockito.when(callback.doWithRetry(Mockito.any(RetryContext.class))).thenReturn(10L); - Callable wrapped = template.asCallable(callback); - Assert.assertEquals(10L, wrapped.call().longValue()); + Callable wrapped = template.asCallable(callback); + Assert.assertEquals(10L, wrapped.call().longValue()); - Mockito.verify(callback, Mockito.times(1)).doWithRetry(Mockito.any(RetryContext.class)); - } + Mockito.verify(callback, Mockito.times(1)).doWithRetry(Mockito.any(RetryContext.class)); + } - @Test - public void test_execute_callable() throws Exception { - Callable callable = Mockito.mock(Callable.class); - ExtendedRetryTemplate template = new ExtendedRetryTemplate(); + @Test + public void test_execute_callable() throws Exception { + Callable callable = Mockito.mock(Callable.class); + ExtendedRetryTemplate template = new ExtendedRetryTemplate(); - Mockito.when(callable.call()).thenReturn(10L); + Mockito.when(callable.call()).thenReturn(10L); - Assert.assertEquals(10L, template.execute(callable).longValue()); + Assert.assertEquals(10L, template.execute(callable).longValue()); - Mockito.verify(callable, Mockito.times(1)).call(); + Mockito.verify(callable, Mockito.times(1)).call(); - } + } - @Test - public void test_execute_callableWithState() throws Exception { - Callable callable = Mockito.mock(Callable.class); - RetryState retryState = Mockito.mock(RetryState.class); - ExtendedRetryTemplate template = new ExtendedRetryTemplate(); + @Test + public void test_execute_callableWithState() throws Exception { + Callable callable = Mockito.mock(Callable.class); + RetryState retryState = Mockito.mock(RetryState.class); + ExtendedRetryTemplate template = new ExtendedRetryTemplate(); - Mockito.when(callable.call()).thenReturn(10L); + Mockito.when(callable.call()).thenReturn(10L); - Assert.assertEquals(10L, template.execute(callable, retryState).longValue()); + Assert.assertEquals(10L, template.execute(callable, retryState).longValue()); - Mockito.verify(callable, Mockito.times(1)).call(); + Mockito.verify(callable, Mockito.times(1)).call(); - } + } - @Test - public void test_execute_callableWithRecoveryAndState() throws Exception { - Callable callable = Mockito.mock(Callable.class); - RetryState retryState = Mockito.mock(RetryState.class); - RecoveryCallback recoveryCallback = Mockito.mock(RecoveryCallback.class); + @Test + public void test_execute_callableWithRecoveryAndState() throws Exception { + Callable callable = Mockito.mock(Callable.class); + RetryState retryState = Mockito.mock(RetryState.class); + RecoveryCallback recoveryCallback = Mockito.mock(RecoveryCallback.class); - ExtendedRetryTemplate template = new ExtendedRetryTemplate(); + ExtendedRetryTemplate template = new ExtendedRetryTemplate(); - Mockito.when(callable.call()).thenReturn(10L); + Mockito.when(callable.call()).thenReturn(10L); - Assert.assertEquals(10L, template.execute(callable, recoveryCallback, retryState).longValue()); + Assert.assertEquals(10L, template.execute(callable, recoveryCallback, retryState).longValue()); - Mockito.verify(callable, Mockito.times(1)).call(); - } + Mockito.verify(callable, Mockito.times(1)).call(); + } } - diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/retry/PredicateBinaryExceptionClassifierTest.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/retry/PredicateBinaryExceptionClassifierTest.java index ef62c544..e2ec42a7 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/retry/PredicateBinaryExceptionClassifierTest.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/retry/PredicateBinaryExceptionClassifierTest.java @@ -22,14 +22,14 @@ public class PredicateBinaryExceptionClassifierTest { - @Test - public void testClassify() { - Predicate predicate = Mockito.mock(Predicate.class); - PredicateBinaryExceptionClassifier classifier = new PredicateBinaryExceptionClassifier(predicate); - classifier.setPredicate(predicate); - Mockito.when(predicate.apply(Mockito.any(Throwable.class))).thenReturn(false); - Assert.assertSame(predicate, classifier.getPredicate()); - Assert.assertFalse(classifier.classify(new RuntimeException())); - Mockito.verify(predicate, Mockito.times(1)).apply(Mockito.any(Throwable.class)); - } + @Test + public void testClassify() { + Predicate predicate = Mockito.mock(Predicate.class); + PredicateBinaryExceptionClassifier classifier = new PredicateBinaryExceptionClassifier(predicate); + classifier.setPredicate(predicate); + Mockito.when(predicate.apply(Mockito.any(Throwable.class))).thenReturn(false); + Assert.assertSame(predicate, classifier.getPredicate()); + Assert.assertFalse(classifier.classify(new RuntimeException())); + Mockito.verify(predicate, Mockito.times(1)).apply(Mockito.any(Throwable.class)); + } } diff --git a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/testonly/TestClass.java b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/testonly/TestClass.java index 76818274..217c2910 100644 --- a/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/testonly/TestClass.java +++ b/jrugged-spring/src/test/java/org/fishwife/jrugged/spring/testonly/TestClass.java @@ -24,33 +24,33 @@ @Ignore public class TestClass { - @PerformanceMonitor("monitorA") - public void monitoredMethod1() { + @PerformanceMonitor("monitorA") + public void monitoredMethod1() { - } + } - @PerformanceMonitor("monitorB") - public void monitoredMethod2() { + @PerformanceMonitor("monitorB") + public void monitoredMethod2() { - } + } - @PerformanceMonitor("monitorA") - public void monitoredMethod3() { + @PerformanceMonitor("monitorA") + public void monitoredMethod3() { - } + } - @CircuitBreaker(name = "breakerA") - public void circuitBreakerMethod1() { + @CircuitBreaker(name = "breakerA") + public void circuitBreakerMethod1() { - } + } - @CircuitBreaker(name = "breakerB") - public void circuitBreakerMethod2() { + @CircuitBreaker(name = "breakerB") + public void circuitBreakerMethod2() { - } + } - public void unmonitoredMethod() { + public void unmonitoredMethod() { - } + } }