package org.infinispan.distribution.group;
import static org.infinispan.util.ReflectionUtil.invokeAccessibly;
import org.infinispan.util.concurrent.ConcurrentMapFactory;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import org.infinispan.util.ReflectionUtil;
import org.infinispan.util.Util;
public class GroupManagerImpl implements GroupManager {
private static interface GroupMetadata {
GroupMetadata NONE = new GroupMetadata() {
@Override
public String getGroup(Object instance) {
return null;
}
};
String getGroup(Object instance);
}
private static class GroupMetadataImpl implements GroupMetadata {
private final Method method;
public GroupMetadataImpl(Method method) {
if (!String.class.isAssignableFrom(method.getReturnType()))
throw new IllegalArgumentException(Util.formatString("@Group method %s must return java.lang.String", method));
if (method.getParameterTypes().length > 0)
throw new IllegalArgumentException(Util.formatString("@Group method %s must jave zero arguments", method));
this.method = method;
}
@Override
public String getGroup(Object instance) {
return String.class.cast(invokeAccessibly(instance, method, Util.EMPTY_OBJECT_ARRAY));
}
}
private static GroupMetadata createGroupMetadata(Class<?> clazz) {
Collection<Method> possibleMethods = ReflectionUtil.getAllMethods(clazz, Group.class);
if (possibleMethods.isEmpty())
return GroupMetadata.NONE;
else if (possibleMethods.size() == 1)
return new GroupMetadataImpl(possibleMethods.iterator().next());
else
throw new IllegalStateException(Util.formatString("Cannot define more that one @Group method for class hierarchy rooted at %s", clazz.getName()));
}
private final ConcurrentMap<Class<?>, GroupMetadata> groupMetadataCache;
private final List<Grouper<?>> groupers;
public GroupManagerImpl(List<Grouper<?>> groupers) {
this.groupMetadataCache = ConcurrentMapFactory.makeConcurrentMap();
if (groupers != null)
this.groupers = groupers;
else
this.groupers = Collections.emptyList();
}
@Override
public String getGroup(Object key) {
GroupMetadata metadata = getMetadata(key);
if (metadata != null) {
return applyGroupers(metadata.getGroup(key), key);
} else
return applyGroupers(null, key);
}
private String applyGroupers(String group, Object key) {
for (Grouper<?> grouper : groupers) {
if (grouper.getKeyType().isAssignableFrom(key.getClass()))
group = ((Grouper<Object>) grouper).computeGroup(key, group);
}
return group;
}
private GroupMetadata getMetadata(final Object key) {
final Class<?> keyClass = key.getClass();
GroupMetadata groupMetadata = groupMetadataCache.get(keyClass);
if (groupMetadata == null) {
//this is not ideal as it is possible for the group metadata to be redundantly calculated several times.
//however profiling showed that using the Map<Class,Future> cache-approach is significantly slower on
// the long run
groupMetadata = createGroupMetadata(keyClass);
GroupMetadata previous = groupMetadataCache.putIfAbsent(keyClass, groupMetadata);
if (previous != null) {
// in case another thread added a metadata already, discard what we created and reuse the existing.
return previous;
}
}
return groupMetadata;
}
}