Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refresh @Nested objects #31

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions src/main/java/org/weakref/jmx/AnnotationUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<Method, Method> findManagedMethods(Class<?> clazz)
{
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -293,30 +293,41 @@ public static boolean isManagedMethod(Method method)

public static boolean isFlatten(Method method)
{
return method != null && isAnnotationPresent(Flatten.class, new HashSet<Class<? extends Annotation>>(), method.getAnnotations());
return findAnnotation(Flatten.class, method) != null;
}

public static boolean isNested(Method method)
{
return method != null && isAnnotationPresent(Nested.class, new HashSet<Class<? extends Annotation>>(), method.getAnnotations());
return findAnnotation(Nested.class, method) != null;
}

public static <T extends Annotation> T findAnnotation(Class<T> annotationClass, Method method)
{
if (method != null) {
return findAnnotation(annotationClass, new HashSet<Class<? extends Annotation>>(), method.getAnnotations());
}
return null;
}

private static boolean isAnnotationPresent(Class<? extends Annotation> annotationClass, Set<Class<? extends Annotation>> processedTypes, Annotation... annotations)
private static <T extends Annotation> T findAnnotation(Class<T> annotationClass, Set<Class<? extends Annotation>> 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;
}
}
3 changes: 2 additions & 1 deletion src/main/java/org/weakref/jmx/Flatten.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
105 changes: 78 additions & 27 deletions src/main/java/org/weakref/jmx/MBeanAttributeBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,26 @@
import static org.weakref.jmx.ReflectionUtils.isValidGetter;
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;

public class MBeanAttributeBuilder
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope this class is not package-private only by mistake, and no-one uses it directly. Maybe we should make it so?

{
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;
Expand All @@ -40,16 +46,20 @@ 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;
}

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;
}
Expand Down Expand Up @@ -116,7 +126,7 @@ public MBeanAttributeBuilder nested()

public Collection<? extends MBeanFeature> build()
{
if (target == null) {
if (targetSupplier == null) {
throw new IllegalArgumentException("JmxAttribute must have a target object");
}

Expand All @@ -132,18 +142,14 @@ public Collection<? extends MBeanFeature> 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 nestedObjectType = getNestedObjectType(concreteGetter);
if (nestedObjectType == null) {
return ImmutableList.of();
}
long cacheDurationMillis = getNestedObjectCacheDuration(annotatedGetter);
Supplier nestedObjectSupplier = createNestedObjectSupplier(nestedObjectType, concreteGetter, cacheDurationMillis);

MBean mbean = new MBeanBuilder(value).build();
MBean mbean = MBeanBuilder.from(nestedObjectType, nestedObjectSupplier).build();
ArrayList<MBeanFeature> features = new ArrayList<MBeanFeature>();
features.addAll(mbean.getAttributes());
features.addAll(mbean.getOperations());
Expand All @@ -155,18 +161,14 @@ 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 nestedObjectType = getNestedObjectType(concreteGetter);
if (nestedObjectType == null) {
return ImmutableList.of();
}
long cacheDurationMillis = getNestedObjectCacheDuration(annotatedGetter);
Supplier nestedObjectSupplier = createNestedObjectSupplier(nestedObjectType, concreteGetter, cacheDurationMillis);

MBean mbean = new MBeanBuilder(value).build();
MBean mbean = MBeanBuilder.from(nestedObjectType, nestedObjectSupplier).build();
ArrayList<MBeanFeature> features = new ArrayList<MBeanFeature>();
for (MBeanAttribute attribute : mbean.getAttributes()) {
features.add(new NestedMBeanAttribute(attributeName, attribute));
Expand Down Expand Up @@ -219,7 +221,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));
}
}

Expand All @@ -242,4 +244,53 @@ private static String getAttributeName(Method... methods)
}
return null;
}

private Class getNestedObjectType(Method concreteGetter)
{
try {
Object value = concreteGetter.invoke(targetSupplier.get());
return value.getClass();
}
catch (Exception e) {
// todo log me
return null;
}
}

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 {
return requiredType.cast(concreteGetter.invoke(targetSupplier.get()));
}
catch (Exception e) {
// todo log me
return null;
}
}
};
if (cacheDurationMills >= 0) {
supplier = Suppliers.memoizeWithExpiration(supplier, cacheDurationMills, TimeUnit.MILLISECONDS);
} else {
supplier = Suppliers.memoize(supplier);
}
return supplier;
}
}
45 changes: 23 additions & 22 deletions src/main/java/org/weakref/jmx/MBeanBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,43 +19,42 @@
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;
private final List<MBeanAttributeBuilder> attributeBuilders = new ArrayList<MBeanAttributeBuilder>();
private final List<MBeanOperationBuilder> operationBuilders = new ArrayList<MBeanOperationBuilder>();
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<String, MBeanAttributeBuilder> attributeBuilders = new TreeMap<String, MBeanAttributeBuilder>();

for (Map.Entry<Method, Method> entry : AnnotationUtils.findManagedMethods(target.getClass()).entrySet()) {
for (Map.Entry<Method, Method> entry : AnnotationUtils.findManagedMethods(targetType).entrySet()) {
Method concreteMethod = entry.getKey();
Method annotatedMethod = entry.getValue();

Expand All @@ -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)
Expand All @@ -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();
Expand All @@ -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)
Expand All @@ -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;
Expand All @@ -134,7 +135,7 @@ public MBean build()
for (MBeanOperationBuilder operationBuilder : operationBuilders) {
operations.add(operationBuilder.build());
}

return new MBean(className, description, attributes, operations);
}
}
2 changes: 1 addition & 1 deletion src/main/java/org/weakref/jmx/MBeanExporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading