|
2 | 2 |
|
3 | 3 | import static org.apache.commons.lang3.BooleanUtils.isFalse;
|
4 | 4 |
|
| 5 | +import java.lang.reflect.Modifier; |
5 | 6 | import java.util.ArrayList;
|
6 | 7 | import java.util.Arrays;
|
7 | 8 | import java.util.List;
|
8 | 9 | import java.util.Locale;
|
9 | 10 | import java.util.Set;
|
| 11 | +import java.util.function.BiFunction; |
10 | 12 | import java.util.function.Function;
|
11 | 13 | import java.util.stream.Collectors;
|
12 | 14 |
|
|
24 | 26 | import org.hibernate.query.criteria.HibernateCriteriaBuilder;
|
25 | 27 | import org.hibernate.relational.SchemaManager;
|
26 | 28 | import org.jboss.jandex.AnnotationInstance;
|
| 29 | +import org.jboss.jandex.AnnotationTarget; |
27 | 30 | import org.jboss.jandex.AnnotationTarget.Kind;
|
28 | 31 | import org.jboss.jandex.AnnotationTransformation;
|
29 | 32 | import org.jboss.jandex.AnnotationValue;
|
| 33 | +import org.jboss.jandex.ClassInfo; |
30 | 34 | import org.jboss.jandex.ClassType;
|
| 35 | +import org.jboss.jandex.CompositeIndex; |
31 | 36 | import org.jboss.jandex.DotName;
|
32 | 37 | import org.jboss.jandex.FieldInfo;
|
| 38 | +import org.jboss.jandex.MethodInfo; |
33 | 39 | import org.jboss.jandex.ParameterizedType;
|
34 | 40 | import org.jboss.jandex.Type;
|
| 41 | +import org.objectweb.asm.ClassVisitor; |
35 | 42 |
|
36 | 43 | import io.agroal.api.AgroalDataSource;
|
37 | 44 | import io.quarkus.agroal.spi.JdbcDataSourceBuildItem;
|
38 | 45 | import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
|
39 | 46 | import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem;
|
40 | 47 | import io.quarkus.arc.deployment.BeanDefiningAnnotationBuildItem;
|
| 48 | +import io.quarkus.arc.deployment.BeanDiscoveryFinishedBuildItem; |
41 | 49 | import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
|
42 | 50 | import io.quarkus.arc.deployment.SyntheticBeanBuildItem.ExtendedBeanConfigurator;
|
43 | 51 | import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
|
44 | 52 | import io.quarkus.arc.deployment.ValidationPhaseBuildItem;
|
45 | 53 | import io.quarkus.arc.processor.AnnotationsTransformer;
|
| 54 | +import io.quarkus.arc.processor.BeanInfo; |
| 55 | +import io.quarkus.arc.processor.BuiltinScope; |
46 | 56 | import io.quarkus.arc.processor.DotNames;
|
| 57 | +import io.quarkus.arc.processor.ScopeInfo; |
47 | 58 | import io.quarkus.arc.processor.Transformation;
|
48 | 59 | import io.quarkus.deployment.Capabilities;
|
49 | 60 | import io.quarkus.deployment.Capability;
|
|
52 | 63 | import io.quarkus.deployment.annotations.BuildSteps;
|
53 | 64 | import io.quarkus.deployment.annotations.ExecutionTime;
|
54 | 65 | import io.quarkus.deployment.annotations.Record;
|
| 66 | +import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; |
55 | 67 | import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
|
| 68 | +import io.quarkus.gizmo.ClassTransformer; |
| 69 | +import io.quarkus.gizmo.MethodDescriptor; |
56 | 70 | import io.quarkus.hibernate.orm.PersistenceUnit;
|
57 | 71 | import io.quarkus.hibernate.orm.runtime.HibernateOrmRecorder;
|
58 | 72 | import io.quarkus.hibernate.orm.runtime.HibernateOrmRuntimeConfig;
|
@@ -276,6 +290,62 @@ void registerBeans(HibernateOrmConfig hibernateOrmConfig,
|
276 | 290 | unremovableBeans.produce(UnremovableBeanBuildItem.beanTypes(jpaModel.getPotentialCdiBeanClassNames()));
|
277 | 291 | }
|
278 | 292 |
|
| 293 | + @BuildStep |
| 294 | + void transformBeans(JpaModelBuildItem jpaModel, JpaModelIndexBuildItem indexBuildItem, |
| 295 | + BeanDiscoveryFinishedBuildItem beans, |
| 296 | + BuildProducer<BytecodeTransformerBuildItem> producer) { |
| 297 | + if (!HibernateOrmProcessor.hasEntities(jpaModel)) { |
| 298 | + return; |
| 299 | + } |
| 300 | + |
| 301 | + // the idea here is to remove the 'private' modifier from all methods that are annotated with JPA Listener methods |
| 302 | + // and don't belong to entities |
| 303 | + CompositeIndex index = indexBuildItem.getIndex(); |
| 304 | + for (DotName dotName : jpaModel.getPotentialCdiBeanClassNames()) { |
| 305 | + if (jpaModel.getManagedClassNames().contains(dotName.toString())) { |
| 306 | + continue; |
| 307 | + } |
| 308 | + ClassInfo classInfo = index.getClassByName(dotName); |
| 309 | + List<BeanInfo> matchingBeans = beans.getBeans().stream().filter(bi -> bi.getBeanClass().equals(dotName)).toList(); |
| 310 | + if (matchingBeans.size() == 1) { |
| 311 | + ScopeInfo beanScope = matchingBeans.get(0).getScope(); |
| 312 | + for (DotName jpaListenerDotName : ClassNames.JPA_LISTENER_ANNOTATIONS) { |
| 313 | + for (AnnotationInstance annotationInstance : classInfo.annotations(jpaListenerDotName)) { |
| 314 | + AnnotationTarget target = annotationInstance.target(); |
| 315 | + if (target.kind() != AnnotationTarget.Kind.METHOD) { |
| 316 | + continue; |
| 317 | + } |
| 318 | + MethodInfo method = target.asMethod(); |
| 319 | + if (Modifier.isPrivate(method.flags())) { |
| 320 | + if (beanScope.getDotName().equals(BuiltinScope.SINGLETON.getName())) { |
| 321 | + // we can safely transform in this case |
| 322 | + producer.produce(new BytecodeTransformerBuildItem(method.declaringClass().name().toString(), |
| 323 | + new BiFunction<>() { |
| 324 | + @Override |
| 325 | + public ClassVisitor apply(String cls, ClassVisitor clsVisitor) { |
| 326 | + var classTransformer = new ClassTransformer(cls); |
| 327 | + classTransformer.modifyMethod(MethodDescriptor.of(method)) |
| 328 | + .removeModifiers(Modifier.PRIVATE); |
| 329 | + return classTransformer.applyTo(clsVisitor); |
| 330 | + } |
| 331 | + })); |
| 332 | + } else { |
| 333 | + // we can't transform because the client proxy does not know about the transformation and |
| 334 | + // will therefore simply copy the private method which will then likely fail because it does |
| 335 | + // not contain the injected fields |
| 336 | + throw new IllegalArgumentException( |
| 337 | + "Methods that are annotated with JPA Listener annotations should not be private. Offending method is '" |
| 338 | + + method.declaringClass().name() + "#" + method.name() + "'"); |
| 339 | + } |
| 340 | + } |
| 341 | + } |
| 342 | + } |
| 343 | + } else { |
| 344 | + // we don't really know what to do here, just bail and CDI will figure it out |
| 345 | + } |
| 346 | + } |
| 347 | + } |
| 348 | + |
279 | 349 | @BuildStep
|
280 | 350 | void registerAnnotations(BuildProducer<AdditionalBeanBuildItem> additionalBeans,
|
281 | 351 | BuildProducer<BeanDefiningAnnotationBuildItem> beanDefiningAnnotations) {
|
|
0 commit comments