From 963a28c8d37ed22f5314915e750ef58a4784acc5 Mon Sep 17 00:00:00 2001 From: arhimondr Date: Sun, 30 Aug 2015 15:55:30 +0200 Subject: [PATCH 1/2] replace target with target supplier --- .../weakref/jmx/MBeanAttributeBuilder.java | 72 ++++++++++++------- .../java/org/weakref/jmx/MBeanBuilder.java | 45 ++++++------ .../java/org/weakref/jmx/MBeanExporter.java | 2 +- .../weakref/jmx/MBeanOperationBuilder.java | 14 ++-- .../weakref/jmx/ReflectionMBeanAttribute.java | 27 +++---- .../weakref/jmx/ReflectionMBeanOperation.java | 26 ++++--- 6 files changed, 104 insertions(+), 82 deletions(-) diff --git a/src/main/java/org/weakref/jmx/MBeanAttributeBuilder.java b/src/main/java/org/weakref/jmx/MBeanAttributeBuilder.java index a64e346..d452677 100644 --- a/src/main/java/org/weakref/jmx/MBeanAttributeBuilder.java +++ b/src/main/java/org/weakref/jmx/MBeanAttributeBuilder.java @@ -18,6 +18,9 @@ import static org.weakref.jmx.ReflectionUtils.isValidGetter; import static org.weakref.jmx.ReflectionUtils.isValidSetter; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableList; + import javax.management.Descriptor; import javax.management.ImmutableDescriptor; import javax.management.MBeanAttributeInfo; @@ -31,7 +34,7 @@ public class MBeanAttributeBuilder { private final static Pattern getterOrSetterPattern = Pattern.compile("(get|set|is)(.+)"); - private Object target; + private Supplier targetSupplier; private String name; private Method concreteGetter; private Method annotatedGetter; @@ -40,10 +43,10 @@ public class MBeanAttributeBuilder private boolean flatten; private boolean nested; - public MBeanAttributeBuilder onInstance(Object target) + public MBeanAttributeBuilder withTargetSupplier(Supplier targetSupplier) { - if (target == null) throw new NullPointerException("target is null"); - this.target = target; + if (targetSupplier == null) throw new NullPointerException("targetSupplier is null"); + this.targetSupplier = targetSupplier; return this; } @@ -116,7 +119,7 @@ public MBeanAttributeBuilder nested() public Collection build() { - if (target == null) { + if (targetSupplier == null) { throw new IllegalArgumentException("JmxAttribute must have a target object"); } @@ -132,18 +135,13 @@ public Collection build() throw new IllegalArgumentException("Flattened JmxAttribute must have a concrete getter"); } - Object value = null; - try { - value = concreteGetter.invoke(target); - } - catch (Exception e) { - // todo log me - } - if (value == null) { - return Collections.emptySet(); + Class targetType = getNestedTargetType(concreteGetter); + if(targetType == null){ + return ImmutableList.of(); } + Supplier nestedObjectSupplier = createNestedObjectSupplier(targetType, concreteGetter); - MBean mbean = new MBeanBuilder(value).build(); + MBean mbean = MBeanBuilder.from(targetType, nestedObjectSupplier).build(); ArrayList features = new ArrayList(); features.addAll(mbean.getAttributes()); features.addAll(mbean.getOperations()); @@ -155,18 +153,13 @@ else if (nested || AnnotationUtils.isNested(annotatedGetter)) { throw new IllegalArgumentException("Nested JmxAttribute must have a concrete getter"); } - Object value = null; - try { - value = concreteGetter.invoke(target); - } - catch (Exception e) { - // todo log me - } - if (value == null) { - return Collections.emptySet(); + Class targetType = getNestedTargetType(concreteGetter); + if(targetType == null){ + return ImmutableList.of(); } + Supplier nestedObjectSupplier = createNestedObjectSupplier(targetType, concreteGetter); - MBean mbean = new MBeanBuilder(value).build(); + MBean mbean = MBeanBuilder.from(targetType, nestedObjectSupplier).build(); ArrayList features = new ArrayList(); for (MBeanAttribute attribute : mbean.getAttributes()) { features.add(new NestedMBeanAttribute(attributeName, attribute)); @@ -219,7 +212,7 @@ else if (nested || AnnotationUtils.isNested(annotatedGetter)) { descriptor); - return Collections.singleton(new ReflectionMBeanAttribute(mbeanAttributeInfo, target, concreteGetter, concreteSetter)); + return Collections.singleton(new ReflectionMBeanAttribute(mbeanAttributeInfo, targetSupplier, concreteGetter, concreteSetter)); } } @@ -242,4 +235,31 @@ private static String getAttributeName(Method... methods) } return null; } + + private Class getNestedTargetType(Method concreteGetter) + { + try { + Object value = concreteGetter.invoke(targetSupplier.get()); + return value.getClass(); + } + catch (Exception e) { + // todo log me + return null; + } + } + + private Supplier createNestedObjectSupplier(final Class requiredType, final Method concreteGetter){ + return new Supplier() { + public Object get() + { + try { + return requiredType.cast(concreteGetter.invoke(targetSupplier.get())); + } + catch (Exception e) { + // todo log me + return null; + } + } + }; + } } diff --git a/src/main/java/org/weakref/jmx/MBeanBuilder.java b/src/main/java/org/weakref/jmx/MBeanBuilder.java index 9c66634..96a0bb3 100644 --- a/src/main/java/org/weakref/jmx/MBeanBuilder.java +++ b/src/main/java/org/weakref/jmx/MBeanBuilder.java @@ -19,12 +19,17 @@ import static org.weakref.jmx.ReflectionUtils.isGetter; import static org.weakref.jmx.ReflectionUtils.isSetter; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; + import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; +import static com.google.common.base.Preconditions.checkNotNull; + class MBeanBuilder { private String className; @@ -32,30 +37,24 @@ class MBeanBuilder private final List operationBuilders = new ArrayList(); private String description; - public MBeanBuilder(String className) - { - this.className = className; - } - - public static MBeanBuilder from(String className) + public static MBeanBuilder from(Object object) { - return new MBeanBuilder(className); + return from(object.getClass(), Suppliers.ofInstance(object)); } - public static MBeanBuilder from(Object object) + static MBeanBuilder from(Class targetType, Supplier targetSupplier) { - return new MBeanBuilder(object); + return new MBeanBuilder(targetType, targetSupplier); } - public MBeanBuilder(Object target) + private MBeanBuilder(Class targetType, Supplier targetSupplier) { - if (target == null) { - throw new NullPointerException("target is null"); - } + checkNotNull(targetType, "targetType is null"); + checkNotNull(targetSupplier, "targetSupplier is null"); Map attributeBuilders = new TreeMap(); - for (Map.Entry entry : AnnotationUtils.findManagedMethods(target.getClass()).entrySet()) { + for (Map.Entry entry : AnnotationUtils.findManagedMethods(targetType).entrySet()) { Method concreteMethod = entry.getKey(); Method annotatedMethod = entry.getValue(); @@ -64,9 +63,9 @@ public MBeanBuilder(Object target) MBeanAttributeBuilder attributeBuilder = attributeBuilders.get(attributeName); if (attributeBuilder == null) { - attributeBuilder = new MBeanAttributeBuilder().named(attributeName).onInstance(target); + attributeBuilder = new MBeanAttributeBuilder().named(attributeName).withTargetSupplier(targetSupplier); } - + if (isGetter(concreteMethod)) { attributeBuilder = attributeBuilder .withConcreteGetter(concreteMethod) @@ -84,7 +83,7 @@ else if (isSetter(concreteMethod)) { // TODO: change this so that we are not making assumptions about mutability or side effects // in the builder addOperation() - .onInstance(target) + .withTargetSupplier(targetSupplier) .withConcreteMethod(concreteMethod) .withAnnotatedMethod(annotatedMethod) .build(); @@ -95,8 +94,8 @@ else if (isSetter(concreteMethod)) { this.attributeBuilders.add(attributeBuilder); } - className = target.getClass().getName(); - description = AnnotationUtils.getDescription(target.getClass().getAnnotations()); + className = targetType.getName(); + description = AnnotationUtils.getDescription(targetType.getAnnotations()); } public MBeanBuilder withDescription(String description) @@ -105,13 +104,15 @@ public MBeanBuilder withDescription(String description) return this; } - public MBeanAttributeBuilder addAttribute() { + public MBeanAttributeBuilder addAttribute() + { MBeanAttributeBuilder builder = new MBeanAttributeBuilder(); attributeBuilders.add(builder); return builder; } - public MBeanOperationBuilder addOperation() { + public MBeanOperationBuilder addOperation() + { MBeanOperationBuilder builder = new MBeanOperationBuilder(); operationBuilders.add(builder); return builder; @@ -134,7 +135,7 @@ public MBean build() for (MBeanOperationBuilder operationBuilder : operationBuilders) { operations.add(operationBuilder.build()); } - + return new MBean(className, description, attributes, operations); } } diff --git a/src/main/java/org/weakref/jmx/MBeanExporter.java b/src/main/java/org/weakref/jmx/MBeanExporter.java index fecf609..32e029d 100644 --- a/src/main/java/org/weakref/jmx/MBeanExporter.java +++ b/src/main/java/org/weakref/jmx/MBeanExporter.java @@ -68,7 +68,7 @@ public void export(String name, Object object) public void export(ObjectName objectName, Object object) { try { - MBeanBuilder builder = new MBeanBuilder(object); + MBeanBuilder builder = MBeanBuilder.from(object); MBean mbean = builder.build(); synchronized(exportedObjects) { diff --git a/src/main/java/org/weakref/jmx/MBeanOperationBuilder.java b/src/main/java/org/weakref/jmx/MBeanOperationBuilder.java index b00a100..4cedc6e 100644 --- a/src/main/java/org/weakref/jmx/MBeanOperationBuilder.java +++ b/src/main/java/org/weakref/jmx/MBeanOperationBuilder.java @@ -15,26 +15,28 @@ */ package org.weakref.jmx; +import com.google.common.base.Supplier; import com.thoughtworks.paranamer.BytecodeReadingParanamer; import com.thoughtworks.paranamer.Paranamer; import javax.management.Descriptor; import javax.management.MBeanOperationInfo; import javax.management.MBeanParameterInfo; + import java.lang.annotation.Annotation; import java.lang.reflect.Method; public class MBeanOperationBuilder { - private Object target; + private Supplier targetSupplier; private String name; private Method concreteMethod; private Method annotatedMethod; - public MBeanOperationBuilder onInstance(Object target) + public MBeanOperationBuilder withTargetSupplier(Supplier targetSupplier) { - if (target == null) throw new NullPointerException("target is null"); - this.target = target; + if (targetSupplier == null) throw new NullPointerException("targetSupplier is null"); + this.targetSupplier = targetSupplier; return this; } @@ -65,7 +67,7 @@ public MBeanOperationBuilder withAnnotatedMethod(Method annotatedMethod) public MBeanOperation build() { - if (target == null) { + if (targetSupplier == null) { throw new IllegalArgumentException("JmxOperation must have a target object"); } @@ -126,6 +128,6 @@ public MBeanOperation build() MBeanOperationInfo.UNKNOWN, descriptor); - return new ReflectionMBeanOperation(mbeanOperationInfo, target, concreteMethod); + return new ReflectionMBeanOperation(mbeanOperationInfo, targetSupplier, concreteMethod); } } \ No newline at end of file diff --git a/src/main/java/org/weakref/jmx/ReflectionMBeanAttribute.java b/src/main/java/org/weakref/jmx/ReflectionMBeanAttribute.java index 4d881c4..a97a34e 100644 --- a/src/main/java/org/weakref/jmx/ReflectionMBeanAttribute.java +++ b/src/main/java/org/weakref/jmx/ReflectionMBeanAttribute.java @@ -15,7 +15,9 @@ */ package org.weakref.jmx; +import static com.google.common.base.Preconditions.checkNotNull; import static org.weakref.jmx.ReflectionUtils.invoke; +import com.google.common.base.Supplier; import javax.management.AttributeNotFoundException; import javax.management.InvalidAttributeValueException; @@ -27,21 +29,21 @@ class ReflectionMBeanAttribute implements MBeanAttribute { private final MBeanAttributeInfo info; - private final Object target; + private final Supplier targetSupplier; private final String name; private final Method getter; private final Method setter; - public ReflectionMBeanAttribute(MBeanAttributeInfo info, Object target, Method getter, Method setter) + public ReflectionMBeanAttribute(MBeanAttributeInfo info, Supplier targetSupplier, Method getter, Method setter) { if (info == null) { throw new NullPointerException("info is null"); } - if (target == null) { - throw new NullPointerException("target is null"); + if (targetSupplier == null) { + throw new NullPointerException("targetSupplier is null"); } this.info = info; - this.target = target; + this.targetSupplier = targetSupplier; this.name = info.getName(); this.getter = getter; this.setter = setter; @@ -52,11 +54,6 @@ public MBeanAttributeInfo getInfo() return info; } - public Object getTarget() - { - return target; - } - public String getName() { return name; @@ -68,8 +65,7 @@ public Object getValue() if (getter == null) { throw new AttributeNotFoundException(name + " is write-only"); } - Object result = invoke(target, getter); - return result; + return invoke(getTarget(), getter); } public void setValue(Object value) @@ -81,6 +77,11 @@ public void setValue(Object value) if (!ReflectionUtils.isAssignable(value, setter.getParameterTypes()[0])) { throw new InvalidAttributeValueException("Can not assign " + value.getClass() + " to attribute " + name); } - invoke(target, setter, value); + invoke(getTarget(), setter, value); + } + + private Object getTarget() + { + return checkNotNull(targetSupplier.get(), "target is null"); } } \ No newline at end of file diff --git a/src/main/java/org/weakref/jmx/ReflectionMBeanOperation.java b/src/main/java/org/weakref/jmx/ReflectionMBeanOperation.java index 6181224..8548d67 100644 --- a/src/main/java/org/weakref/jmx/ReflectionMBeanOperation.java +++ b/src/main/java/org/weakref/jmx/ReflectionMBeanOperation.java @@ -15,22 +15,26 @@ */ package org.weakref.jmx; +import com.google.common.base.Supplier; + import javax.management.MBeanException; import javax.management.MBeanOperationInfo; import javax.management.ReflectionException; import java.lang.reflect.Method; +import static com.google.common.base.Preconditions.checkNotNull; + class ReflectionMBeanOperation implements MBeanOperation { private final MBeanOperationInfo info; - private final Object target; + private final Supplier targetSupplier; private final Method method; private final Signature signature; - public ReflectionMBeanOperation(MBeanOperationInfo info, Object target, Method method) + public ReflectionMBeanOperation(MBeanOperationInfo info, Supplier targetSupplier, Method method) { this.info = info; - this.target = target; + this.targetSupplier = targetSupplier; this.method = method; this.signature = new Signature(method); @@ -41,25 +45,19 @@ public MBeanOperationInfo getInfo() return info; } - public Object getTarget() - { - return target; - } - public Signature getSignature() { return signature; } - public Method getMethod() + public Object invoke(Object[] params) + throws MBeanException, ReflectionException { - return method; + return ReflectionUtils.invoke(getTarget(), method, params); } - public Object invoke(Object[] params) - throws MBeanException, ReflectionException + private Object getTarget() { - Object result = ReflectionUtils.invoke(target, method, params); - return result; + return checkNotNull(targetSupplier.get(), "target is null"); } } From 8206093933daf71eb4c238af82512e17e9208ed3 Mon Sep 17 00:00:00 2001 From: arhimondr Date: Sun, 30 Aug 2015 23:25:25 +0200 Subject: [PATCH 2/2] add cacheDurationMillis attribute to Flatten and Nested annotations --- .../java/org/weakref/jmx/AnnotationUtils.java | 29 +++++-- src/main/java/org/weakref/jmx/Flatten.java | 3 +- .../weakref/jmx/MBeanAttributeBuilder.java | 57 ++++++++++--- src/main/java/org/weakref/jmx/Nested.java | 3 +- .../java/org/weakref/jmx/TestExpiration.java | 84 +++++++++++++++++++ 5 files changed, 152 insertions(+), 24 deletions(-) create mode 100644 src/test/java/org/weakref/jmx/TestExpiration.java diff --git a/src/main/java/org/weakref/jmx/AnnotationUtils.java b/src/main/java/org/weakref/jmx/AnnotationUtils.java index 6225cf9..44ddaff 100644 --- a/src/main/java/org/weakref/jmx/AnnotationUtils.java +++ b/src/main/java/org/weakref/jmx/AnnotationUtils.java @@ -225,7 +225,7 @@ public static String getDescription(Annotation... annotations) * * @param clazz the class to analyze * @return a map that associates a concrete method to the actual method tagged as managed - * (which may belong to a different class in clazz's hierarchy) + * (which may belong to a different class in clazz's hierarchy) */ public static Map findManagedMethods(Class clazz) { @@ -259,7 +259,7 @@ public static Method findManagedMethod(Class clazz, String methodName, Class< try { Method method = clazz.getDeclaredMethod(methodName, paramTypes); if (isManagedMethod(method)) return method; - } + } catch (NoSuchMethodException e) { // ignore } @@ -293,30 +293,41 @@ public static boolean isManagedMethod(Method method) public static boolean isFlatten(Method method) { - return method != null && isAnnotationPresent(Flatten.class, new HashSet>(), method.getAnnotations()); + return findAnnotation(Flatten.class, method) != null; } public static boolean isNested(Method method) { - return method != null && isAnnotationPresent(Nested.class, new HashSet>(), method.getAnnotations()); + return findAnnotation(Nested.class, method) != null; + } + + public static T findAnnotation(Class annotationClass, Method method) + { + if (method != null) { + return findAnnotation(annotationClass, new HashSet>(), method.getAnnotations()); + } + return null; } - private static boolean isAnnotationPresent(Class annotationClass, Set> processedTypes, Annotation... annotations) + private static T findAnnotation(Class annotationClass, Set> processedTypes, Annotation... annotations) { // are any of the annotations the specified annotation for (Annotation annotation : annotations) { if (annotationClass.isInstance(annotation)) { - return true; + return annotationClass.cast(annotation); } } // are any of the annotations annotated with the specified annotation for (Annotation annotation : annotations) { - if (processedTypes.add(annotation.annotationType()) && isAnnotationPresent(annotationClass, processedTypes, annotation.annotationType().getAnnotations())) { - return true; + if (processedTypes.add(annotation.annotationType())) { + T foundAnnotation = findAnnotation(annotationClass, processedTypes, annotation.annotationType().getAnnotations()); + if (foundAnnotation != null) { + return foundAnnotation; + } } } - return false; + return null; } } diff --git a/src/main/java/org/weakref/jmx/Flatten.java b/src/main/java/org/weakref/jmx/Flatten.java index 9cb4a51..3ff3163 100644 --- a/src/main/java/org/weakref/jmx/Flatten.java +++ b/src/main/java/org/weakref/jmx/Flatten.java @@ -21,8 +21,9 @@ import java.lang.annotation.ElementType; @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @ManagedAnnotation public @interface Flatten { + long cacheDurationMillis() default -1; } diff --git a/src/main/java/org/weakref/jmx/MBeanAttributeBuilder.java b/src/main/java/org/weakref/jmx/MBeanAttributeBuilder.java index d452677..e639608 100644 --- a/src/main/java/org/weakref/jmx/MBeanAttributeBuilder.java +++ b/src/main/java/org/weakref/jmx/MBeanAttributeBuilder.java @@ -19,15 +19,18 @@ import static org.weakref.jmx.ReflectionUtils.isValidSetter; import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import javax.management.Descriptor; import javax.management.ImmutableDescriptor; import javax.management.MBeanAttributeInfo; + import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -45,14 +48,18 @@ public class MBeanAttributeBuilder public MBeanAttributeBuilder withTargetSupplier(Supplier targetSupplier) { - if (targetSupplier == null) throw new NullPointerException("targetSupplier is null"); + if (targetSupplier == null) { + throw new NullPointerException("targetSupplier is null"); + } this.targetSupplier = targetSupplier; return this; } public MBeanAttributeBuilder named(String name) { - if (name == null) throw new NullPointerException("name is null"); + if (name == null) { + throw new NullPointerException("name is null"); + } this.name = name; return this; } @@ -135,13 +142,14 @@ public Collection build() throw new IllegalArgumentException("Flattened JmxAttribute must have a concrete getter"); } - Class targetType = getNestedTargetType(concreteGetter); - if(targetType == null){ + Class nestedObjectType = getNestedObjectType(concreteGetter); + if (nestedObjectType == null) { return ImmutableList.of(); } - Supplier nestedObjectSupplier = createNestedObjectSupplier(targetType, concreteGetter); + long cacheDurationMillis = getNestedObjectCacheDuration(annotatedGetter); + Supplier nestedObjectSupplier = createNestedObjectSupplier(nestedObjectType, concreteGetter, cacheDurationMillis); - MBean mbean = MBeanBuilder.from(targetType, nestedObjectSupplier).build(); + MBean mbean = MBeanBuilder.from(nestedObjectType, nestedObjectSupplier).build(); ArrayList features = new ArrayList(); features.addAll(mbean.getAttributes()); features.addAll(mbean.getOperations()); @@ -153,13 +161,14 @@ else if (nested || AnnotationUtils.isNested(annotatedGetter)) { throw new IllegalArgumentException("Nested JmxAttribute must have a concrete getter"); } - Class targetType = getNestedTargetType(concreteGetter); - if(targetType == null){ + Class nestedObjectType = getNestedObjectType(concreteGetter); + if (nestedObjectType == null) { return ImmutableList.of(); } - Supplier nestedObjectSupplier = createNestedObjectSupplier(targetType, concreteGetter); + long cacheDurationMillis = getNestedObjectCacheDuration(annotatedGetter); + Supplier nestedObjectSupplier = createNestedObjectSupplier(nestedObjectType, concreteGetter, cacheDurationMillis); - MBean mbean = MBeanBuilder.from(targetType, nestedObjectSupplier).build(); + MBean mbean = MBeanBuilder.from(nestedObjectType, nestedObjectSupplier).build(); ArrayList features = new ArrayList(); for (MBeanAttribute attribute : mbean.getAttributes()) { features.add(new NestedMBeanAttribute(attributeName, attribute)); @@ -236,7 +245,7 @@ private static String getAttributeName(Method... methods) return null; } - private Class getNestedTargetType(Method concreteGetter) + private Class getNestedObjectType(Method concreteGetter) { try { Object value = concreteGetter.invoke(targetSupplier.get()); @@ -248,8 +257,24 @@ private Class getNestedTargetType(Method concreteGetter) } } - private Supplier createNestedObjectSupplier(final Class requiredType, final Method concreteGetter){ - return new Supplier() { + private long getNestedObjectCacheDuration(Method annotatedGetter) + { + long cacheDurationMillis = -1; + Nested nestedAnnotation = AnnotationUtils.findAnnotation(Nested.class, annotatedGetter); + if (nestedAnnotation != null) { + cacheDurationMillis = nestedAnnotation.cacheDurationMillis(); + } + Flatten flattenAnnotation = AnnotationUtils.findAnnotation(Flatten.class, annotatedGetter); + if (flattenAnnotation != null) { + cacheDurationMillis = flattenAnnotation.cacheDurationMillis(); + } + return cacheDurationMillis; + } + + private Supplier createNestedObjectSupplier(final Class requiredType, final Method concreteGetter, long cacheDurationMills) + { + Supplier supplier = new Supplier() + { public Object get() { try { @@ -261,5 +286,11 @@ public Object get() } } }; + if (cacheDurationMills >= 0) { + supplier = Suppliers.memoizeWithExpiration(supplier, cacheDurationMills, TimeUnit.MILLISECONDS); + } else { + supplier = Suppliers.memoize(supplier); + } + return supplier; } } diff --git a/src/main/java/org/weakref/jmx/Nested.java b/src/main/java/org/weakref/jmx/Nested.java index f61565b..bdbc0c1 100644 --- a/src/main/java/org/weakref/jmx/Nested.java +++ b/src/main/java/org/weakref/jmx/Nested.java @@ -21,8 +21,9 @@ import java.lang.annotation.ElementType; @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @ManagedAnnotation public @interface Nested { + long cacheDurationMillis() default -1; } \ No newline at end of file diff --git a/src/test/java/org/weakref/jmx/TestExpiration.java b/src/test/java/org/weakref/jmx/TestExpiration.java new file mode 100644 index 0000000..3b26858 --- /dev/null +++ b/src/test/java/org/weakref/jmx/TestExpiration.java @@ -0,0 +1,84 @@ +package org.weakref.jmx; + +import com.google.common.collect.Iterables; +import org.testng.annotations.Test; + +import javax.management.Attribute; + +import java.util.Collection; + +import static org.testng.Assert.assertEquals; + +public class TestExpiration +{ + private static final long CACHE_DURATION_MILLIS = 100; + private static final String DEFAULT_VALUE = "DEFAULT_VALUE"; + private static final String UPDATED_VALUE = "UPDATED_VALUE"; + + public static class SingleValueObject + { + private String value = DEFAULT_VALUE; + + @Managed + public String getValue() + { + return value; + } + + @Managed + public void setValue(String value) + { + this.value = value; + } + } + + public static class NestedWithExpiration + { + @Nested(cacheDurationMillis = CACHE_DURATION_MILLIS) + public SingleValueObject getSingleValueObject() + { + return new SingleValueObject(); + } + } + + public static class FlattenWithExpiration + { + @Flatten(cacheDurationMillis = CACHE_DURATION_MILLIS) + public SingleValueObject getSingleValueObject() + { + return new SingleValueObject(); + } + } + + @Test + public void testFlatterExpiration() + throws Exception + { + testExpiration(new FlattenWithExpiration()); + } + + @Test + public void testNestedExpiration() + throws Exception + { + testExpiration(new NestedWithExpiration()); + } + + private void testExpiration(Object object) + throws Exception + { + + MBean mBean = MBeanBuilder.from(object).build(); + Collection attributes = mBean.getAttributes(); + String attributeName = Iterables.getOnlyElement(attributes).getName(); + + assertEquals(mBean.getAttribute(attributeName), DEFAULT_VALUE); + + mBean.setAttribute(new Attribute(attributeName, UPDATED_VALUE)); + assertEquals(mBean.getAttribute(attributeName), UPDATED_VALUE); + + Thread.sleep(CACHE_DURATION_MILLIS + 1); + + assertEquals(mBean.getAttribute(attributeName), DEFAULT_VALUE); + } +}