DATACMNS-1066 - ClassGeneratingPropertyAccessorFactory now opts out on failed access to ClassLoader.

We now eagerly check the accessibility of the defineClass(…) method on the class loader to be used for a PersistentEntity. This will then cause the clients to use a different PropertyAccessorFactory (as things stand today: the one using reflection) and not fail to create the class later on.
This commit is contained in:
Oliver Gierke
2017-05-16 19:11:12 +02:00
parent 71977c9309
commit 5f9d90ee3e

View File

@@ -36,7 +36,6 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.asm.ClassWriter;
@@ -44,7 +43,6 @@ import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.asm.Type;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
@@ -94,8 +92,8 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
* Checks whether an accessor class can be generated.
*
* @param entity
* @return {@literal true} if the runtime is equal or greater to Java 1.7, property name hash codes are unique and the
* type has a class loader we can use to re-inject types.
* @return {@literal true} if the runtime is equal or greater to Java 1.7, we can access the ClassLoader, the property
* name hash codes are unique and the type has a class loader we can use to re-inject types.
* @see PersistentPropertyAccessorFactory#isSupported(PersistentEntity)
*/
@Override
@@ -103,6 +101,12 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
Assert.notNull(entity, "PersistentEntity must not be null!");
try {
Evil.getClassLoaderMethod(entity);
} catch (Exception o_O) {
return false;
}
if (entity.getType().getClassLoader() == null || entity.getType().getPackage().getName().startsWith("java")) {
return false;
}
@@ -1432,6 +1436,7 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
* accessed from a class in the same class loader.
*
* @author Mark Paluch
* @author Oliver Gierke
*/
@UtilityClass
private static class Evil {
@@ -1446,25 +1451,30 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert
* @param persistentEntity
* @return
*/
@SuppressWarnings("rawtypes")
Class<?> defineClass(String name, byte[] bytes, int offset, int len, PersistentEntity<?, ?> persistentEntity) {
ClassLoader classLoader = persistentEntity.getType().getClassLoader();
Class<?> classLoaderClass = classLoader.getClass();
try {
Class<? extends PersistentEntity> persistentEntityClass = persistentEntity.getClass();
Method defineClass = ReflectionUtils.findMethod(classLoaderClass, "defineClass", String.class, byte[].class,
Integer.TYPE, Integer.TYPE, ProtectionDomain.class);
Method defineClass = getClassLoaderMethod(persistentEntity);
defineClass.setAccessible(true);
return (Class<?>) defineClass.invoke(classLoader, name, bytes, offset, len,
persistentEntityClass.getProtectionDomain());
persistentEntity.getClass().getProtectionDomain());
} catch (ReflectiveOperationException e) {
throw new IllegalStateException(e);
}
}
static Method getClassLoaderMethod(PersistentEntity<?, ?> entity) {
ClassLoader classLoader = entity.getType().getClassLoader();
Class<?> classLoaderClass = classLoader.getClass();
return ReflectionUtils.findMethod(classLoaderClass, "defineClass", String.class, byte[].class, Integer.TYPE,
Integer.TYPE, ProtectionDomain.class);
}
}
}