Reimplement class caching, fix performance issue

This commit is contained in:
Dan Mulloy 2016-11-27 13:17:58 -05:00
parent 35e37121f9
commit fc5caf143d
3 changed files with 103 additions and 59 deletions

View File

@ -1,5 +1,6 @@
package com.comphenix.protocol.reflect;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Member;
@ -72,6 +73,16 @@ public abstract class MethodInfo implements GenericDeclaration, Member {
public boolean isConstructor() {
return false;
}
public <T extends Annotation> T getAnnotation(Class<T> clazz) {
return method.getAnnotation(clazz);
}
public Annotation[] getAnnotations() {
return method.getAnnotations();
}
public Annotation[] getDeclaredAnnotations() {
return method.getDeclaredAnnotations();
}
};
}
@ -152,6 +163,16 @@ public abstract class MethodInfo implements GenericDeclaration, Member {
public boolean isConstructor() {
return true;
}
public <T extends Annotation> T getAnnotation(Class<T> clazz) {
return constructor.getAnnotation(clazz);
}
public Annotation[] getAnnotations() {
return constructor.getAnnotations();
}
public Annotation[] getDeclaredAnnotations() {
return constructor.getAnnotations();
}
};
}

View File

@ -1,4 +1,4 @@
/*
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2012 Kristian S. Stangeland
*
@ -14,11 +14,11 @@
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.utility;
import java.util.Map;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
@ -29,10 +29,10 @@ import com.google.common.collect.Maps;
* @author Kristian
*/
class CachedPackage {
private final Map<String, Class<?>> cache;
private final Map<String, Optional<Class<?>>> cache;
private final String packageName;
private final ClassSource source;
/**
* Construct a new cached package.
* @param packageName - the name of the current package.
@ -43,16 +43,20 @@ class CachedPackage {
this.cache = Maps.newConcurrentMap();
this.source = source;
}
/**
* Associate a given class with a class name.
* @param className - class name.
* @param clazz - type of class.
*/
public void setPackageClass(String className, Class<?> clazz) {
cache.put(className, clazz);
if (clazz != null) {
cache.put(className, Optional.<Class<?>> of(clazz));
} else {
cache.remove(className);
}
}
/**
* Retrieve the class object of a specific class in the current package.
* @param className - the specific class.
@ -60,26 +64,33 @@ class CachedPackage {
* @throws RuntimeException If we are unable to find the given class.
*/
public Class<?> getPackageClass(String className) {
Preconditions.checkNotNull(className, "className cannot be null!");
// See if we've already looked it up
if (cache.containsKey(className)) {
Optional<Class<?>> result = cache.get(className);
if (!result.isPresent()) {
throw new RuntimeException("Cannot find class " + className);
}
return result.get();
}
try {
Class<?> result = cache.get(Preconditions.checkNotNull(className, "className cannot be NULL"));
// Concurrency is not a problem - we don't care if we look up a class twice
if (result == null) {
// Look up the class dynamically
result = source.loadClass(combine(packageName, className));
if (result == null)
throw new IllegalArgumentException("Source " + source + " returned NULL for " + className);
cache.put(className, result);
}
return result;
} catch (ClassNotFoundException e) {
setPackageClass(className, null);
throw new RuntimeException("Cannot find class " + className, e);
// Try looking it up
Class<?> clazz = source.loadClass(combine(packageName, className));
if (clazz == null) {
throw new IllegalArgumentException("Source " + source + " returned null for " + className);
}
cache.put(className, Optional.<Class<?>> of(clazz));
return clazz;
} catch (ClassNotFoundException ex) {
cache.put(className, Optional.<Class<?>> absent());
throw new RuntimeException("Cannot find class " + className, ex);
}
}
/**
* Correctly combine a package name and the child class we're looking for.
* @param packageName - name of the package, or an empty string for the default package.

View File

@ -942,6 +942,9 @@ public class MinecraftReflection {
return getMinecraftClass("MinecraftServer");
} catch (RuntimeException e) {
useFallbackServer();
// Reset the cache and try again
setMinecraftClass("MinecraftServer", null);
return getMinecraftClass("MinecraftServer");
}
}
@ -990,8 +993,10 @@ public class MinecraftReflection {
try {
return getMinecraftClass("ServerConfigurationManager", "PlayerList");
} catch (RuntimeException e) {
// Try again
useFallbackServer();
// Reset cache and try again
setMinecraftClass("ServerConfigurationManager", null);
return getMinecraftClass("ServerConfigurationManager");
}
}
@ -1578,6 +1583,9 @@ public class MinecraftReflection {
} catch (RuntimeException e) {
// Initialize first
getAttributeSnapshotClass();
// Reset cache and try again
setMinecraftClass("AttributeModifier", null);
return getMinecraftClass("AttributeModifier");
}
}
@ -1893,9 +1901,11 @@ public class MinecraftReflection {
* @throws RuntimeException If we are unable to find the given class.
*/
public static Class<?> getCraftBukkitClass(String className) {
return getClass(getCraftBukkitPackage() + "." + className);
if (craftbukkitPackage == null)
craftbukkitPackage = new CachedPackage(getCraftBukkitPackage(), getClassSource());
return craftbukkitPackage.getPackageClass(className);
}
/**
* Retrieve the class object of a specific Minecraft class.
* @param className - the specific Minecraft class.
@ -1903,30 +1913,9 @@ public class MinecraftReflection {
* @throws RuntimeException If we are unable to find the given class.
*/
public static Class<?> getMinecraftClass(String className) {
return getClass(getMinecraftPackage() + "." + className);
}
/**
* Retrieve the class object of a specific Minecraft library class.
* @param className - the specific library Minecraft class.
* @return Class object.
* @throws RuntimeException If we are unable to find the given class.
*/
public static Class<?> getMinecraftLibraryClass(String className) {
return getClass(getMinecraftLibraryPackage() + "." + className);
}
/**
* Set the class object for the specific library class.
* @param className - name of the Minecraft library class.
* @param clazz - the new class object.
* @return The provided clazz object.
*/
private static Class<?> setMinecraftLibraryClass(String className, Class<?> clazz) {
if (libraryPackage == null)
libraryPackage = new CachedPackage(getMinecraftLibraryPackage(), getClassSource());
libraryPackage.setPackageClass(className, clazz);
return clazz;
if (minecraftPackage == null)
minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
return minecraftPackage.getPackageClass(className);
}
/**
@ -1964,6 +1953,7 @@ public class MinecraftReflection {
// Just use the default class loader
classSource = ClassSource.fromClassLoader();
}
return classSource;
}
@ -1978,7 +1968,7 @@ public class MinecraftReflection {
try {
// Try the main class first
return getMinecraftClass(className);
} catch (RuntimeException e1) {
} catch (RuntimeException e) {
Class<?> success = null;
// Try every alias too
@ -1986,8 +1976,8 @@ public class MinecraftReflection {
try {
success = getMinecraftClass(alias);
break;
} catch (RuntimeException e2) {
// Swallov
} catch (RuntimeException e1) {
// e1.printStackTrace();
}
}
@ -1997,15 +1987,37 @@ public class MinecraftReflection {
return success;
} else {
// Hack failed
throw new RuntimeException(
String.format("Unable to find %s (%s)",
className,
Joiner.on(", ").join(aliases))
);
throw new RuntimeException(String.format("Unable to find %s (%s)", className, Joiner.on(", ").join(aliases)));
}
}
}
/**
* Retrieve the class object of a specific Minecraft library class.
* @param className - the specific library Minecraft class.
* @return Class object.
* @throws RuntimeException If we are unable to find the given class.
*/
public static Class<?> getMinecraftLibraryClass(String className) {
if (libraryPackage == null)
libraryPackage = new CachedPackage(getMinecraftLibraryPackage(), getClassSource());
return libraryPackage.getPackageClass(className);
}
/**
* Set the class object for the specific library class.
* @param className - name of the Minecraft library class.
* @param clazz - the new class object.
* @return The provided clazz object.
*/
private static Class<?> setMinecraftLibraryClass(String className, Class<?> clazz) {
if (libraryPackage == null)
libraryPackage = new CachedPackage(getMinecraftLibraryPackage(), getClassSource());
libraryPackage.setPackageClass(className, clazz);
return clazz;
}
/**
* Dynamically retrieve the NetworkManager name.
* @return Name of the NetworkManager class.