Skip to content

Package: ManageableClassGenerator$1

ManageableClassGenerator$1

nameinstructionbranchcomplexitylinemethod
name(TypeDescription)
M: 0 C: 4
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
{...}
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%

Coverage

1: package cz.cvut.kbss.jopa.model.metamodel.gen;
2:
3: import cz.cvut.kbss.jopa.model.JOPAPersistenceProperties;
4: import cz.cvut.kbss.jopa.model.Manageable;
5: import cz.cvut.kbss.jopa.model.metamodel.AnnotatedAccessor;
6: import cz.cvut.kbss.jopa.model.metamodel.EntityType;
7: import cz.cvut.kbss.jopa.model.metamodel.FieldSpecification;
8: import cz.cvut.kbss.jopa.sessions.UnitOfWork;
9: import cz.cvut.kbss.jopa.sessions.validator.AttributeModificationValidator;
10: import cz.cvut.kbss.jopa.utils.Configuration;
11: import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
12: import cz.cvut.kbss.jopa.utils.MetamodelUtils;
13: import net.bytebuddy.ByteBuddy;
14: import net.bytebuddy.NamingStrategy;
15: import net.bytebuddy.description.modifier.FieldPersistence;
16: import net.bytebuddy.description.modifier.Visibility;
17: import net.bytebuddy.description.type.TypeDescription;
18: import net.bytebuddy.dynamic.DynamicType;
19: import net.bytebuddy.implementation.FieldAccessor;
20: import net.bytebuddy.implementation.MethodDelegation;
21: import net.bytebuddy.implementation.SuperMethodCall;
22: import net.bytebuddy.implementation.bind.annotation.Origin;
23: import net.bytebuddy.implementation.bind.annotation.This;
24: import org.slf4j.Logger;
25: import org.slf4j.LoggerFactory;
26:
27: import java.io.File;
28: import java.io.IOException;
29: import java.lang.reflect.Method;
30: import java.util.Objects;
31:
32: import static net.bytebuddy.matcher.ElementMatchers.isSetter;
33:
34: /**
35: * Generates persistence context-aware classes that implement the {@link cz.cvut.kbss.jopa.model.Manageable} interface.
36: * <p>
37: * Such classes have an additional attribute not inherited from the base entity class. This attribute's value is a
38: * reference to the persistence context to which an instance is attached. {@link cz.cvut.kbss.jopa.model.Manageable}
39: * allows establishing and accessing this connection.
40: */
41: public class ManageableClassGenerator implements PersistenceContextAwareClassGenerator {
42:
43: private static final Logger LOG = LoggerFactory.getLogger(ManageableClassGenerator.class);
44:
45: private final ByteBuddy byteBuddy = new ByteBuddy().with(new NamingStrategy.AbstractBase() {
46:
47: @Override
48: protected String name(TypeDescription typeDescription) {
49: return "JOPA_" + typeDescription.getSimpleName();
50: }
51: });
52:
53: private final Configuration config;
54:
55: public ManageableClassGenerator(Configuration config) {this.config = config;}
56:
57: @Override
58: public <T> Class<? extends T> generate(Class<T> entityClass) {
59: Objects.requireNonNull(entityClass);
60: LOG.trace("Generating dynamic type for entity class {}.", entityClass);
61: DynamicType.Unloaded<? extends T> typeDef = byteBuddy.subclass(entityClass)
62: .annotateType(entityClass.getAnnotations())
63: .annotateType(new GeneratedEntityClassImpl())
64: .defineField("persistenceContext", UnitOfWork.class, Visibility.PRIVATE, FieldPersistence.TRANSIENT)
65: .implement(Manageable.class)
66: .intercept(FieldAccessor.ofBeanProperty())
67: .method(isSetter().and(new PersistentPropertySetterMatcher<>(entityClass)))
68: .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.to(SetterInterceptor.class)))
69: .make();
70: LOG.debug("Generated dynamic type {} for entity class {}.", typeDef, entityClass);
71: outputGeneratedClass(typeDef);
72: return typeDef.load(getClass().getClassLoader()).getLoaded();
73: }
74:
75: private <T> void outputGeneratedClass(DynamicType.Unloaded<? extends T> typeDef) {
76: final String outputDir = config.get(JOPAPersistenceProperties.CLASS_GENERATOR_OUTPUT_DIR, "");
77: if (!outputDir.isBlank()) {
78: final File output = new File(outputDir);
79: try {
80: LOG.trace("Saving generated class '{}' to '{}'.", typeDef, output);
81: typeDef.saveIn(output);
82: } catch (IOException e) {
83: LOG.error("Unable to output generated class {} to file {}.", typeDef, output, e);
84: }
85: }
86: }
87:
88: public static class SetterInterceptor {
89:
90: private SetterInterceptor() {
91: throw new AssertionError();
92: }
93:
94: public static void set(@This Manageable instance, @Origin Method setter) {
95: final UnitOfWork pc = instance.getPersistenceContext();
96: if (pc == null || !pc.isInTransaction()) {
97: return;
98: }
99: final String fieldName = AnnotatedAccessor.from(setter).getPropertyName();
100: final EntityType<?> et = pc.getMetamodel().entity(MetamodelUtils.getEntityClass(instance.getClass()));
101: final FieldSpecification<?, ?> fieldSpec = et.getFieldSpecification(fieldName);
102: if (EntityPropertiesUtils.isFieldTransient(fieldSpec.getJavaField())) {
103: return;
104: }
105: AttributeModificationValidator.verifyCanModify(fieldSpec);
106: pc.attributeChanged(instance, fieldSpec);
107: }
108: }
109: }