Skip to content

Package: EntityReferenceProxyGenerator$SetterInterceptor

EntityReferenceProxyGenerator$SetterInterceptor

nameinstructionbranchcomplexitylinemethod
EntityReferenceProxyGenerator.SetterInterceptor()
M: 6 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
set(EntityReferenceProxy, Method, Object[])
M: 7 C: 19
73%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 2 C: 6
75%
M: 0 C: 1
100%

Coverage

1: package cz.cvut.kbss.jopa.proxy.reference;
2:
3: import cz.cvut.kbss.jopa.exception.LazyLoadingException;
4: import cz.cvut.kbss.jopa.exceptions.AttributeModificationForbiddenException;
5: import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
6: import cz.cvut.kbss.jopa.model.metamodel.AnnotatedAccessor;
7: import cz.cvut.kbss.jopa.model.metamodel.gen.PersistenceContextAwareClassGenerator;
8: import cz.cvut.kbss.jopa.model.metamodel.gen.PersistentPropertySetterMatcher;
9: import cz.cvut.kbss.jopa.proxy.lazy.gen.PersistentPropertyGetterMatcher;
10: import cz.cvut.kbss.jopa.sessions.UnitOfWork;
11: import net.bytebuddy.ByteBuddy;
12: import net.bytebuddy.NamingStrategy;
13: import net.bytebuddy.description.modifier.FieldPersistence;
14: import net.bytebuddy.description.modifier.Visibility;
15: import net.bytebuddy.description.type.TypeDescription;
16: import net.bytebuddy.dynamic.DynamicType;
17: import net.bytebuddy.implementation.FieldAccessor;
18: import net.bytebuddy.implementation.MethodDelegation;
19: import net.bytebuddy.implementation.bind.annotation.AllArguments;
20: import net.bytebuddy.implementation.bind.annotation.Origin;
21: import net.bytebuddy.implementation.bind.annotation.RuntimeType;
22: import net.bytebuddy.implementation.bind.annotation.This;
23: import org.slf4j.Logger;
24: import org.slf4j.LoggerFactory;
25:
26: import java.lang.reflect.InvocationTargetException;
27: import java.lang.reflect.Method;
28: import java.net.URI;
29: import java.util.Objects;
30:
31: import static net.bytebuddy.matcher.ElementMatchers.isGetter;
32: import static net.bytebuddy.matcher.ElementMatchers.isSetter;
33:
34: public class EntityReferenceProxyGenerator implements PersistenceContextAwareClassGenerator {
35:
36: private static final Logger LOG = LoggerFactory.getLogger(EntityReferenceProxyGenerator.class);
37:
38: private final ByteBuddy byteBuddy = new ByteBuddy().with(new NamingStrategy.AbstractBase() {
39: @Override
40: protected String name(TypeDescription typeDescription) {
41: return typeDescription.getSimpleName() + "_ReferenceProxy";
42: }
43: });
44:
45: @Override
46: public <T> Class<? extends T> generate(Class<T> entityClass) {
47: Objects.requireNonNull(entityClass);
48: LOG.trace("Generating reference proxy for entity class {}.", entityClass);
49: DynamicType.Unloaded<? extends T> typeDef = byteBuddy.subclass(entityClass)
50: .annotateType(new GeneratedEntityReferenceProxyImpl())
51: .defineField("identifier", URI.class, Visibility.PRIVATE, FieldPersistence.TRANSIENT)
52: .defineField("type", Class.class, Visibility.PRIVATE, FieldPersistence.TRANSIENT)
53: .defineField("descriptor", Descriptor.class, Visibility.PRIVATE, FieldPersistence.TRANSIENT)
54: .defineField("persistenceContext", UnitOfWork.class, Visibility.PRIVATE, FieldPersistence.TRANSIENT)
55: // Have to use Object, because otherwise it won't generate a setter for us
56: .defineField("value", entityClass, Visibility.PRIVATE, FieldPersistence.TRANSIENT)
57: .implement(TypeDescription.Generic.Builder.parameterizedType(EntityReferenceProxyPropertyAccessor.class, entityClass)
58: .build())
59: .intercept(FieldAccessor.ofBeanProperty())
60: .implement(EntityReferenceProxy.class)
61: .method(isSetter().and(new PersistentPropertySetterMatcher<>(entityClass)))
62: .intercept(MethodDelegation.to(SetterInterceptor.class))
63: .method(isGetter().and(new PersistentPropertyGetterMatcher<>(entityClass)))
64: .intercept(MethodDelegation.to(GetterInterceptor.class))
65: .make();
66: LOG.debug("Generated dynamic type {} for entity class {}.", typeDef, entityClass);
67: return typeDef.load(getClass().getClassLoader()).getLoaded();
68: }
69:
70: public static class GetterInterceptor {
71:
72: private GetterInterceptor() {
73: throw new AssertionError();
74: }
75:
76: @RuntimeType
77: public static <T> Object get(@This EntityReferenceProxy<T> proxy, @Origin Method getter) {
78: if (isIdentifierField(proxy, getter)) {
79: return proxy.getIdentifier();
80: }
81: final Object loaded = proxy.triggerLoading();
82: try {
83: return getter.invoke(loaded);
84: } catch (InvocationTargetException | IllegalAccessException e) {
85: throw new LazyLoadingException("Unable to invoke getter after loading referenced object.", e);
86: }
87: }
88: }
89:
90: private static <T> boolean isIdentifierField(EntityReferenceProxy<T> proxy, Method accessor) {
91: final String fieldName = AnnotatedAccessor.from(accessor).getPropertyName();
92: return proxy.getPersistenceContext().getMetamodel().entity(proxy.getType()).getIdentifier().getName()
93: .equals(fieldName);
94: }
95:
96: public static class SetterInterceptor {
97:
98: private SetterInterceptor() {
99: throw new AssertionError();
100: }
101:
102: public static <T> void set(@This EntityReferenceProxy<T> proxy, @Origin Method setter,
103: @AllArguments Object[] args) {
104:• if (isIdentifierField(proxy, setter)) {
105: throw new AttributeModificationForbiddenException("Cannot change entity reference identifier.");
106: }
107: final Object loaded = proxy.triggerLoading();
108: try {
109: setter.invoke(loaded, args);
110: } catch (InvocationTargetException | IllegalAccessException e) {
111: throw new LazyLoadingException("Unable to invoke setter after loading referenced object.", e);
112: }
113: }
114: }
115: }