From 8c10a3b27aee8cc5ab32f1f0ff2d9ff3947b5076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 24 Apr 2023 19:05:40 +0200 Subject: [PATCH] Take into account non-public fields when enhancing Hibernate ORM entity field access --- .../BasePanacheMongoResourceProcessor.java | 13 ++++---- .../common/deployment/EntityField.java | 31 ++++++++++++++++++- .../common/deployment/MetamodelInfo.java | 2 +- ...eEntityClassAccessorGenerationVisitor.java | 10 +++--- ...nacheHibernateCommonResourceProcessor.java | 26 ++++++++-------- 5 files changed, 56 insertions(+), 26 deletions(-) diff --git a/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/BasePanacheMongoResourceProcessor.java b/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/BasePanacheMongoResourceProcessor.java index 199f8fae507358..92062687a109d8 100644 --- a/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/BasePanacheMongoResourceProcessor.java +++ b/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/BasePanacheMongoResourceProcessor.java @@ -289,14 +289,14 @@ protected void processEntities(CombinedIndexBuildItem index, } private void replaceFieldAccesses(BuildProducer transformers, MetamodelInfo modelInfo) { - Set entitiesWithPublicFields = modelInfo.getEntitiesWithPublicFields(); - if (entitiesWithPublicFields.isEmpty()) { - // There are no public fields to be accessed in the first place. + Set entitiesWithExternallyAccessibleFields = modelInfo.getEntitiesWithExternallyAccessibleFields(); + if (entitiesWithExternallyAccessibleFields.isEmpty()) { + // There are no fields to be accessed in the first place. return; } Set entityClassNamesInternal = new HashSet<>(); - for (String entityClassName : entitiesWithPublicFields) { + for (String entityClassName : entitiesWithExternallyAccessibleFields) { entityClassNamesInternal.add(entityClassName.replace(".", "/")); } @@ -323,10 +323,11 @@ private EntityModel createEntityModel(ClassInfo classInfo) { EntityModel entityModel = new EntityModel(classInfo); for (FieldInfo fieldInfo : classInfo.fields()) { String name = fieldInfo.name(); - if (Modifier.isPublic(fieldInfo.flags()) + var visibility = EntityField.Visibility.get(fieldInfo.flags()); + if (EntityField.Visibility.PUBLIC.equals(visibility) && !Modifier.isStatic(fieldInfo.flags()) && !fieldInfo.hasAnnotation(BSON_IGNORE)) { - entityModel.addField(new EntityField(name, DescriptorUtils.typeToString(fieldInfo.type()))); + entityModel.addField(new EntityField(name, DescriptorUtils.typeToString(fieldInfo.type()), visibility)); } } return entityModel; diff --git a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/EntityField.java b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/EntityField.java index a9a4da67fc828b..ddf95233ce0d28 100644 --- a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/EntityField.java +++ b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/EntityField.java @@ -1,5 +1,6 @@ package io.quarkus.panache.common.deployment; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -10,6 +11,7 @@ import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; import io.quarkus.deployment.bean.JavaBeanUtil; @@ -17,12 +19,14 @@ public class EntityField { public final String name; public final String descriptor; + public final Visibility visibility; public String signature; public final Set annotations = new HashSet<>(2); - public EntityField(String name, String descriptor) { + public EntityField(String name, String descriptor, Visibility visibility) { this.name = name; this.descriptor = descriptor; + this.visibility = visibility; } public String getGetterName() { @@ -42,6 +46,31 @@ public boolean hasAnnotation(String descriptor) { return false; } + public enum Visibility { + PUBLIC(Opcodes.ACC_PUBLIC), + PROTECTED(Opcodes.ACC_PROTECTED), + PACKAGE_PRIVATE(0), + PRIVATE(Opcodes.ACC_PRIVATE); + + public final int accessOpCode; + + Visibility(int accessOpCode) { + this.accessOpCode = accessOpCode; + } + + public static Visibility get(int flags) { + if (Modifier.isPublic(flags)) { + return PUBLIC; + } else if (Modifier.isPrivate(flags)) { + return PRIVATE; + } else if (Modifier.isProtected(flags)) { + return PROTECTED; + } else { + return PACKAGE_PRIVATE; + } + } + } + public static class EntityFieldAnnotation { public final String descriptor; public final Map attributes = new HashMap<>(2); diff --git a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/MetamodelInfo.java b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/MetamodelInfo.java index c6d3c3fd5afe8f..d71e99f2f9129a 100644 --- a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/MetamodelInfo.java +++ b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/MetamodelInfo.java @@ -16,7 +16,7 @@ public void addEntityModel(EntityModel entityModel) { entities.put(entityModel.name, entityModel); } - public Set getEntitiesWithPublicFields() { + public Set getEntitiesWithExternallyAccessibleFields() { return entities.entrySet().stream() .filter(e -> { EntityModel value = e.getValue(); diff --git a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/visitors/PanacheEntityClassAccessorGenerationVisitor.java b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/visitors/PanacheEntityClassAccessorGenerationVisitor.java index e1977eb0b82ef9..69f7cc2bdc42b4 100644 --- a/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/visitors/PanacheEntityClassAccessorGenerationVisitor.java +++ b/extensions/panache/panache-common/deployment/src/main/java/io/quarkus/panache/common/deployment/visitors/PanacheEntityClassAccessorGenerationVisitor.java @@ -68,11 +68,11 @@ public FieldVisitor visitField(int access, String name, String descriptor, Strin //so any errors are visible immediately, rather than data just being lost FieldVisitor superVisitor; - if (name.equals("id")) { - superVisitor = super.visitField(access, name, descriptor, signature, value); - } else { + if (!name.equals("id") && Modifier.isPublic(access)) { superVisitor = super.visitField((access | Modifier.PROTECTED) & ~(Modifier.PRIVATE | Modifier.PUBLIC), name, descriptor, signature, value); + } else { + superVisitor = super.visitField(access, name, descriptor, signature, value); } entityField.signature = signature; // if we have a mapped field, let's add some annotations @@ -126,7 +126,7 @@ private void generateAccessors() { String getterName = field.getGetterName(); String getterDescriptor = "()" + field.descriptor; if (!userMethods.contains(getterName + "/" + getterDescriptor)) { - MethodVisitor mv = super.visitMethod(Opcodes.ACC_PUBLIC, + MethodVisitor mv = super.visitMethod(field.visibility.accessOpCode, getterName, getterDescriptor, field.signature == null ? null : "()" + field.signature, null); mv.visitCode(); mv.visitIntInsn(Opcodes.ALOAD, 0); @@ -159,7 +159,7 @@ private void generateAccessors() { String setterName = field.getSetterName(); String setterDescriptor = "(" + field.descriptor + ")V"; if (!userMethods.contains(setterName + "/" + setterDescriptor)) { - MethodVisitor mv = super.visitMethod(Opcodes.ACC_PUBLIC, + MethodVisitor mv = super.visitMethod(field.visibility.accessOpCode, setterName, setterDescriptor, field.signature == null ? null : "(" + field.signature + ")V", null); mv.visitCode(); mv.visitIntInsn(Opcodes.ALOAD, 0); diff --git a/extensions/panache/panache-hibernate-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheHibernateCommonResourceProcessor.java b/extensions/panache/panache-hibernate-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheHibernateCommonResourceProcessor.java index 82cf77985d58ca..1bb7bfc10dcf6a 100644 --- a/extensions/panache/panache-hibernate-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheHibernateCommonResourceProcessor.java +++ b/extensions/panache/panache-hibernate-common/deployment/src/main/java/io/quarkus/panache/common/deployment/PanacheHibernateCommonResourceProcessor.java @@ -68,15 +68,15 @@ void findEntityClasses(CombinedIndexBuildItem index, // Share the metamodel for use in replaceFieldAccesses modelInfoBuildItem.produce(new HibernateMetamodelForFieldAccessBuildItem(modelInfo)); - Set entitiesWithPublicFields = modelInfo.getEntitiesWithPublicFields(); - if (entitiesWithPublicFields.isEmpty()) { - // There are no public fields to be accessed in the first place. + Set entitiesWithExternallyAccessibleFields = modelInfo.getEntitiesWithExternallyAccessibleFields(); + if (entitiesWithExternallyAccessibleFields.isEmpty()) { + // There are no fields to be accessed in the first place. return; } // Share with other extensions that we will generate accessors for some classes fieldAccessEnhancedEntityClasses - .produce(new PanacheEntityClassesBuildItem(entitiesWithPublicFields)); + .produce(new PanacheEntityClassesBuildItem(entitiesWithExternallyAccessibleFields)); } @BuildStep @@ -91,23 +91,23 @@ void replaceFieldAccesses(CombinedIndexBuildItem index, } MetamodelInfo modelInfo = modelInfoBuildItem.get().getMetamodelInfo(); - Set entitiesWithPublicFields = modelInfo.getEntitiesWithPublicFields(); - if (entitiesWithPublicFields.isEmpty()) { - // There are no public fields to be accessed in the first place. + Set entitiesWithExternallyAccessibleFields = modelInfo.getEntitiesWithExternallyAccessibleFields(); + if (entitiesWithExternallyAccessibleFields.isEmpty()) { + // There are no fields to be accessed in the first place. return; } - // Generate accessors for public fields in entities, mapped superclasses + // Generate accessors for externally accessible fields in entities, mapped superclasses // (and embeddables, see where we build modelInfo above). PanacheJpaEntityAccessorsEnhancer entityAccessorsEnhancer = new PanacheJpaEntityAccessorsEnhancer(index.getIndex(), modelInfo); - for (String entityClassName : entitiesWithPublicFields) { + for (String entityClassName : entitiesWithExternallyAccessibleFields) { transformers.produce(new BytecodeTransformerBuildItem(true, entityClassName, entityAccessorsEnhancer)); } // Replace field access in application code with calls to accessors Set entityClassNamesInternal = new HashSet<>(); - for (String entityClassName : entitiesWithPublicFields) { + for (String entityClassName : entitiesWithExternallyAccessibleFields) { entityClassNamesInternal.add(entityClassName.replace(".", "/")); } @@ -146,10 +146,10 @@ private EntityModel createEntityModel(ClassInfo classInfo) { EntityModel entityModel = new EntityModel(classInfo); for (FieldInfo fieldInfo : classInfo.fields()) { String name = fieldInfo.name(); - if (Modifier.isPublic(fieldInfo.flags()) - && !Modifier.isStatic(fieldInfo.flags()) + if (!Modifier.isStatic(fieldInfo.flags()) && !fieldInfo.hasAnnotation(DOTNAME_TRANSIENT)) { - entityModel.addField(new EntityField(name, DescriptorUtils.typeToString(fieldInfo.type()))); + entityModel.addField(new EntityField(name, DescriptorUtils.typeToString(fieldInfo.type()), + EntityField.Visibility.get(fieldInfo.flags()))); } } return entityModel;