diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/Context.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/Context.java index 9ef6a41e3f01..e83d982cb639 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/Context.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/Context.java @@ -102,6 +102,8 @@ public final class Context { private boolean usesQuarkusOrm = false; private boolean usesQuarkusReactive = false; + private boolean usesQuarkusPanache2 = false; + private boolean usesQuarkusReactiveCommon = false; private String[] includes = {"*"}; private String[] excludes = {}; @@ -443,6 +445,22 @@ public boolean usesQuarkusReactive() { return usesQuarkusReactive; } + public void setUsesQuarkusPanache2(boolean b) { + usesQuarkusPanache2 = b; + } + + public boolean usesQuarkusPanache2() { + return usesQuarkusPanache2; + } + + public void setUsesQuarkusReactiveCommon(boolean b) { + usesQuarkusReactiveCommon = b; + } + + public boolean usesQuarkusReactiveCommon() { + return usesQuarkusReactiveCommon; + } + public void setInclude(String include) { includes = include.split(",\\s*"); } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/HibernateProcessor.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/HibernateProcessor.java index da6364466851..c624a5795d11 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/HibernateProcessor.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/HibernateProcessor.java @@ -253,9 +253,16 @@ private boolean handleSettings(ProcessingEnvironment environment) { PackageElement quarkusOrmPanachePackage = context.getProcessingEnvironment().getElementUtils() .getPackageElement( "io.quarkus.hibernate.orm.panache" ); + PackageElement quarkusPanache2Package = + context.getProcessingEnvironment().getElementUtils() + .getPackageElement( "io.quarkus.hibernate.panache" ); PackageElement quarkusReactivePanachePackage = context.getProcessingEnvironment().getElementUtils() .getPackageElement( "io.quarkus.hibernate.reactive.panache" ); + // This is imported automatically by Quarkus extensions when HR is also imported + PackageElement quarkusReactivePanacheCommonPackage = + context.getProcessingEnvironment().getElementUtils() + .getPackageElement( "io.quarkus.hibernate.reactive.panache.common" ); if ( packagePresent(quarkusReactivePanachePackage) && packagePresent(quarkusOrmPanachePackage) ) { @@ -275,6 +282,8 @@ && packagePresent(quarkusOrmPanachePackage) ) { context.setQuarkusInjection( packagePresent(quarkusOrmPackage) || packagePresent(quarkusReactivePackage) ); context.setUsesQuarkusOrm( packagePresent(quarkusOrmPanachePackage) ); context.setUsesQuarkusReactive( packagePresent(quarkusReactivePanachePackage) ); + context.setUsesQuarkusPanache2( packagePresent(quarkusPanache2Package) ); + context.setUsesQuarkusReactiveCommon( packagePresent(quarkusReactivePanacheCommonPackage) ); final Map options = environment.getOptions(); @@ -389,72 +398,7 @@ private void processClasses(RoundEnvironment roundEnvironment) { private void processElement(Element element, @Nullable Element parent) { try { - if ( !included( element ) - || hasAnnotation( element, Constants.EXCLUDE ) - || hasPackageAnnotation( element, Constants.EXCLUDE ) - || element.getModifiers().contains( Modifier.PRIVATE ) ) { - // skip it completely - return; - } - else if ( isEntityOrEmbeddable( element ) - && !element.getModifiers().contains( Modifier.PRIVATE ) ) { - context.logMessage( Diagnostic.Kind.OTHER, "Processing annotated entity class '" + element + "'" ); - handleRootElementAnnotationMirrors( element, parent ); - } - else if ( hasAuxiliaryAnnotations( element ) ) { - context.logMessage( Diagnostic.Kind.OTHER, "Processing annotated class '" + element + "'" ); - handleRootElementAuxiliaryAnnotationMirrors( element ); - } - else if ( element instanceof TypeElement typeElement ) { - final AnnotationMirror repository = getAnnotationMirror( element, JD_REPOSITORY ); - if ( repository != null ) { - final AnnotationValue provider = getAnnotationValue( repository, "provider" ); - if ( provider == null - || provider.getValue().toString().isEmpty() - || provider.getValue().toString().equalsIgnoreCase("hibernate") ) { - context.logMessage( Diagnostic.Kind.OTHER, "Processing repository class '" + element + "'" ); - final AnnotationMetaEntity metaEntity = - AnnotationMetaEntity.create( typeElement, context, - parentMetadata( parent, context::getMetaEntity ) ); - if ( metaEntity.isInitialized() ) { - context.addMetaAuxiliary( metaEntity.getQualifiedName(), metaEntity ); - } - // otherwise discard it (assume it has query by magical method name stuff) - } - } - else { - for ( Element member : typeElement.getEnclosedElements() ) { - if ( hasAnnotation( member, HQL, SQL, FIND ) ) { - context.logMessage( Diagnostic.Kind.OTHER, "Processing annotated class '" + element + "'" ); - final AnnotationMetaEntity metaEntity = - AnnotationMetaEntity.create( typeElement, context, - parentMetadata( parent, context::getMetaEntity ) ); - context.addMetaAuxiliary( metaEntity.getQualifiedName(), metaEntity ); - break; - } - } - if ( enclosesEntityOrEmbeddable( element ) ) { - final NonManagedMetamodel metaEntity = - NonManagedMetamodel.create( typeElement, context, false, - parentMetadata( parent, context::getMetamodel ) ); - context.addMetaEntity( metaEntity.getQualifiedName(), metaEntity ); - if ( context.generateJakartaDataStaticMetamodel() ) { - final NonManagedMetamodel dataMetaEntity = - NonManagedMetamodel.create( typeElement, context, true, - parentMetadata( parent, context::getDataMetaEntity ) ); - context.addDataMetaEntity( dataMetaEntity.getQualifiedName(), dataMetaEntity ); - } - - } - } - } - if ( isClassRecordOrInterfaceType( element ) ) { - for ( final Element child : element.getEnclosedElements() ) { - if ( isClassRecordOrInterfaceType( child ) ) { - processElement( child, element ); - } - } - } + inspectRootElement(element, parent, null); } catch ( ProcessLaterException processLaterException ) { if ( element instanceof TypeElement typeElement ) { @@ -484,6 +428,77 @@ private boolean hasPackageAnnotation(Element element, String annotation) { return pack != null && hasAnnotation( pack, annotation ); } + private void inspectRootElement(Element element, @Nullable Element parent, @Nullable TypeElement primaryEntity) { + if ( !included( element ) + || hasAnnotation( element, Constants.EXCLUDE ) + || hasPackageAnnotation( element, Constants.EXCLUDE ) + || element.getModifiers().contains( Modifier.PRIVATE ) ) { + // skip it completely + return; + } + else if ( isEntityOrEmbeddable( element ) + && !element.getModifiers().contains( Modifier.PRIVATE ) ) { + context.logMessage( Diagnostic.Kind.OTHER, "Processing annotated entity class '" + element + "'" ); + handleRootElementAnnotationMirrors( element, parent ); + } + else if ( hasAuxiliaryAnnotations( element ) ) { + context.logMessage( Diagnostic.Kind.OTHER, "Processing annotated class '" + element + "'" ); + handleRootElementAuxiliaryAnnotationMirrors( element ); + } + else if ( element instanceof TypeElement typeElement ) { + final AnnotationMirror repository = getAnnotationMirror( element, JD_REPOSITORY ); + if ( repository != null ) { + final AnnotationValue provider = getAnnotationValue( repository, "provider" ); + if ( provider == null + || provider.getValue().toString().isEmpty() + || provider.getValue().toString().equalsIgnoreCase("hibernate") ) { + context.logMessage( Diagnostic.Kind.OTHER, "Processing repository class '" + element + "'" ); + final AnnotationMetaEntity metaEntity = + AnnotationMetaEntity.create( typeElement, context, + parentMetadata( parent, context::getMetaEntity ), + primaryEntity ); + if ( metaEntity.isInitialized() ) { + context.addMetaAuxiliary( metaEntity.getQualifiedName(), metaEntity ); + } + // otherwise discard it (assume it has query by magical method name stuff) + } + } + else { + for ( Element member : typeElement.getEnclosedElements() ) { + if ( hasAnnotation( member, HQL, SQL, FIND ) ) { + context.logMessage( Diagnostic.Kind.OTHER, "Processing annotated class '" + element + "'" ); + final AnnotationMetaEntity metaEntity = + AnnotationMetaEntity.create( typeElement, context, + parentMetadata( parent, context::getMetaEntity ), + primaryEntity ); + context.addMetaAuxiliary( metaEntity.getQualifiedName(), metaEntity ); + break; + } + } + if ( enclosesEntityOrEmbeddable( element ) ) { + final NonManagedMetamodel metaEntity = + NonManagedMetamodel.create( typeElement, context, false, + parentMetadata( parent, context::getMetamodel ) ); + context.addMetaEntity( metaEntity.getQualifiedName(), metaEntity ); + if ( context.generateJakartaDataStaticMetamodel() ) { + final NonManagedMetamodel dataMetaEntity = + NonManagedMetamodel.create( typeElement, context, true, + parentMetadata( parent, context::getDataMetaEntity ) ); + context.addDataMetaEntity( dataMetaEntity.getQualifiedName(), dataMetaEntity ); + } + + } + } + } + if ( isClassRecordOrInterfaceType( element ) ) { + for ( final Element child : element.getEnclosedElements() ) { + if ( isClassRecordOrInterfaceType( child ) ) { + processElement( child, element ); + } + } + } + } + private void createMetaModelClasses() { for ( Metamodel aux : context.getMetaAuxiliaries() ) { @@ -635,6 +650,7 @@ private void handleRootElementAnnotationMirrors(final Element element, @Nullable final TypeElement typeElement = (TypeElement) element; indexEntityName( typeElement ); indexEnumFields( typeElement ); + indexQueryInterfaces( typeElement ); final String qualifiedName = typeElement.getQualifiedName().toString(); final Metamodel alreadyExistingMetaEntity = @@ -653,7 +669,7 @@ private void handleRootElementAnnotationMirrors(final Element element, @Nullable final AnnotationMetaEntity metaEntity = AnnotationMetaEntity.create( typeElement, context, requiresLazyMemberInitialization, - true, false, parentMetaEntity ); + true, false, parentMetaEntity, typeElement ); if ( alreadyExistingMetaEntity != null ) { metaEntity.mergeInMembers( alreadyExistingMetaEntity ); } @@ -672,7 +688,7 @@ && hasAnnotation( element, ENTITY, MAPPED_SUPERCLASS ) final AnnotationMetaEntity dataMetaEntity = AnnotationMetaEntity.create( typeElement, context, requiresLazyMemberInitialization, - true, true, parentDataEntity ); + true, true, parentDataEntity, typeElement ); // final Metamodel alreadyExistingDataMetaEntity = // tryGettingExistingDataEntityFromContext( mirror, '_' + qualifiedName ); // if ( alreadyExistingDataMetaEntity != null ) { @@ -691,6 +707,14 @@ private static boolean hasHandwrittenMetamodel(Element element) { .contentEquals('_' + element.getSimpleName().toString())); } + private void indexQueryInterfaces(TypeElement typeElement) { + for ( Element element : typeElement.getEnclosedElements() ) { + if( element.getKind() == ElementKind.INTERFACE ) { + inspectRootElement( element, typeElement, typeElement ); + } + } + } + private void indexEntityName(TypeElement typeElement) { final AnnotationMirror mirror = getAnnotationMirror( typeElement, ENTITY ); if ( mirror != null ) { diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java index 8773d5b6a2f7..2596ffca76a5 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/AnnotationMetaEntity.java @@ -95,6 +95,7 @@ import static org.hibernate.processor.util.TypeUtils.getAnnotationMirror; import static org.hibernate.processor.util.TypeUtils.getAnnotationValue; import static org.hibernate.processor.util.TypeUtils.getGeneratedClassFullyQualifiedName; +import static org.hibernate.processor.util.TypeUtils.getInheritedAnnotationMirror; import static org.hibernate.processor.util.TypeUtils.hasAnnotation; import static org.hibernate.processor.util.TypeUtils.implementsInterface; import static org.hibernate.processor.util.TypeUtils.primitiveClassMatchesKind; @@ -174,7 +175,8 @@ public class AnnotationMetaEntity extends AnnotationMeta { public AnnotationMetaEntity( TypeElement element, Context context, boolean managed, boolean jakartaDataStaticMetamodel, - @Nullable AnnotationMeta parent) { + @Nullable AnnotationMeta parent, + @Nullable TypeElement primaryEntity) { this.element = element; this.context = context; this.managed = managed; @@ -182,6 +184,7 @@ public AnnotationMetaEntity( this.quarkusInjection = context.isQuarkusInjection(); this.importContext = parent != null ? parent : new ImportContextImpl( getPackageName( context, element ) ); jakartaDataStaticModel = jakartaDataStaticMetamodel; + this.primaryEntity = primaryEntity; importContext.importType( getGeneratedClassFullyQualifiedName( element, getPackageName( context, element ), jakartaDataStaticModel ) ); @@ -190,17 +193,23 @@ public AnnotationMetaEntity( } } + public static AnnotationMetaEntity create(TypeElement element, Context context, @Nullable AnnotationMetaEntity parent, + @Nullable TypeElement primaryEntity) { + return create( element,context, false, false, false, parent, primaryEntity ); + } + public static AnnotationMetaEntity create(TypeElement element, Context context, @Nullable AnnotationMetaEntity parent) { - return create( element,context, false, false, false, parent ); + return create( element,context, false, false, false, parent, null ); } public static AnnotationMetaEntity create( TypeElement element, Context context, boolean lazilyInitialised, boolean managed, boolean jakartaData, - @Nullable AnnotationMetaEntity parent) { + @Nullable AnnotationMetaEntity parent, + @Nullable TypeElement primaryEntity) { final AnnotationMetaEntity annotationMetaEntity = - new AnnotationMetaEntity( element, context, managed, jakartaData, parent ); + new AnnotationMetaEntity( element, context, managed, jakartaData, parent, primaryEntity ); if ( parent != null ) { parent.addInnerClass( annotationMetaEntity ); } @@ -406,6 +415,7 @@ else if ( hasAnnotation( method, JD_DELETE ) ) { } else if ( method.getEnclosingElement().getKind().isInterface() && !method.isDefault() + && !method.getModifiers().contains(Modifier.PRIVATE) && !isSessionGetter(method) ) { final String companionClassName = element.getQualifiedName().toString() + '$'; if ( context.getElementUtils().getTypeElement(companionClassName) == null ) { @@ -417,8 +427,10 @@ else if ( method.getEnclosingElement().getKind().isInterface() } } - primaryEntity = primaryEntity( lifecycleMethods ); final boolean hibernateRepo = isExplicitlyHibernateRepository(); + if ( primaryEntity == null ) { + primaryEntity = primaryEntity( lifecycleMethods ); + } if ( !checkEntity( primaryEntity, hibernateRepo ) || !checkEntities( lifecycleMethods, hibernateRepo ) ) { // NOTE EARLY EXIT with initialized = false @@ -457,6 +469,10 @@ && containsAnnotation( method, HQL, SQL, FIND ) ) { addPersistentMembers( gettersAndSettersOfClass, AccessType.PROPERTY ); addIdClassIfNeeded( fieldsOfClass, gettersAndSettersOfClass ); + + if( hasAnnotation( element, ENTITY) && isPanache2Type(element) && !jakartaDataStaticModel ) { + addRepositoryMembers( element ); + } } addAuxiliaryMembers(); @@ -510,6 +526,41 @@ private void addIdClassIfNeeded(List fields, List getIdMemberNames(List fields, List methods) { final List components = new ArrayList<>(); for ( var field : fields ) { @@ -637,6 +688,67 @@ private boolean isEquivalentPrimitiveType(TypeMirror type, TypeMirror match) { && isSameType( context.getTypeUtils().boxedClass( ((PrimitiveType) type) ).asType(), match ); } + private void addAccessors(@Nullable Element repositoryType, @Nullable TypeMirror idType, + String repositoryAccessor, String repositorySuperType) { + TypeElement finalPrimaryEntity = primaryEntity; + if ( repositoryType != null ) { + members.put( repositoryAccessor, new CDIAccessorMetaAttribute( this, repositoryAccessor, repositoryType.getSimpleName().toString() ) ); + } + else if ( idType != null && finalPrimaryEntity != null ) { + String repositoryTypeName = "Panache"+repositoryAccessor.substring(0,1).toUpperCase()+repositoryAccessor.substring(1)+"Repository"; + members.put( repositoryAccessor, new CDIAccessorMetaAttribute( this, repositoryAccessor, repositoryTypeName ) ); + members.put( repositoryAccessor + "Repository", new CDITypeMetaAttribute( this, repositoryTypeName, repositorySuperType +"<"+ finalPrimaryEntity.getSimpleName()+", "+ idType.toString()+">" ) ); + } + } + + private @Nullable TypeMirror findIdType() { + TypeElement primaryEntityForTest = primaryEntity; + if ( primaryEntityForTest == null ) { + return null; + } + AnnotationMirror idClass = getInheritedAnnotationMirror( this.context.getElementUtils(), primaryEntityForTest, ID_CLASS ); + if ( idClass != null ) { + AnnotationValue value = getAnnotationValue(idClass, "value" ); + // I don't think this can have a null value + if ( value != null ) { + return (TypeMirror) value.getValue(); + } + } + Element idMember = findIdMember(); + if ( idMember != null ) { + TypeMirror typedIdMember = this.context.getTypeUtils().asMemberOf((DeclaredType) primaryEntityForTest.asType(), idMember); + return switch(typedIdMember.getKind()) { + case ARRAY, DECLARED, BOOLEAN, BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE -> typedIdMember; + case EXECUTABLE -> ((ExecutableType) typedIdMember).getReturnType(); + default -> { + message( element, + "Unhandled id member kind: "+typedIdMember+" for id "+idMember, + Diagnostic.Kind.ERROR ); + yield null; + } + }; + } + return null; + } + + private @Nullable Element findIdMember() { + if ( primaryEntity == null ) { + message( element, + "No primary entity defined to find id member", + Diagnostic.Kind.ERROR ); + return null; + } + for ( Element member : context.getAllMembers( primaryEntity ) ) { + if ( hasAnnotation( member, ID, EMBEDDED_ID ) ) { + return member; + } + } + message( element, + "Could not find any member annotated with @Id or @EmbeddedId", + Diagnostic.Kind.ERROR ); + return null; + } + private boolean checkEntities(List lifecycleMethods, boolean hibernateRepo) { boolean foundPersistenceEntity = false; VariableElement nonPersistenceParameter = null; @@ -764,21 +876,26 @@ private void setupSession() { final ExecutableElement getter = findSessionGetter( element ); if ( getter != null ) { // Never make a DAO for Panache subtypes - if ( !isPanacheType( element ) ) { + if ( !isPanacheType( element ) && !isPanache2Type( element ) ) { repository = true; sessionType = addDaoConstructor( getter ); } - else { - // For Panache subtypes, we look at the session type, but no DAO, we want static methods + else if ( ! isPanache2Repository( element ) && !isPanache2Type( element ) ) { + // For Panache 1 subtypes, we look at the session type, but no DAO, we want static methods sessionType = fullReturnType(getter); } + else { + // For Panache 2 repositories we want a repository + repository = true; + sessionType = setupQuarkusDaoConstructor( getter, element ); + } } else if ( element.getKind() == ElementKind.INTERFACE && !jakartaDataRepository - && ( context.usesQuarkusOrm() || context.usesQuarkusReactive() ) ) { + && ( context.usesQuarkusOrm() || context.usesQuarkusReactive() || context.usesQuarkusPanache2() ) ) { // if we don't have a getter, and not a JD repository, but we're in Quarkus, we know how to find the default sessions repository = true; - sessionType = setupQuarkusDaoConstructor(); + sessionType = setupQuarkusDaoConstructor( null, element ); } if ( !repository && jakartaDataRepository ) { repository = true; @@ -878,6 +995,19 @@ private boolean isReactivePanacheType(TypeElement type) { || extendsClass( type, PANACHE_REACTIVE_ENTITY_BASE ); } + private boolean isPanache2Type(TypeElement type) { + return implementsInterface( type, PANACHE2_ENTITY_MARKER ) + || isPanache2Repository( type ); + } + + private boolean isPanache2Repository(TypeElement type) { + return implementsInterface( type, PANACHE2_MANAGED_BLOCKING_REPOSITORY_BASE ) + || implementsInterface( type, PANACHE2_STATELESS_BLOCKING_REPOSITORY_BASE ) + || implementsInterface( type, PANACHE2_MANAGED_REACTIVE_REPOSITORY_BASE ) + || implementsInterface( type, PANACHE2_STATELESS_REACTIVE_REPOSITORY_BASE ) + ; + } + /** * If there is a session getter method, we generate an instance * variable backing it, together with a constructor that initializes @@ -915,10 +1045,51 @@ private String addDaoConstructor(@Nullable ExecutableElement method) { /** * For Quarkus, we generate a constructor with injection for EntityManager in ORM, * and in HR, we define the static session getter. + * For Panache 2, we can use the element to figure out what kind of session we want since this + * is for repositories */ - private String setupQuarkusDaoConstructor() { - if ( context.usesQuarkusOrm() ) { - String name = "getEntityManager"; + private String setupQuarkusDaoConstructor(@Nullable ExecutableElement getter, @Nullable TypeElement element) { + boolean favorBlocking = context.usesQuarkusOrm() + || (context.usesQuarkusPanache2() + && element != null + && (implementsInterface(element, PANACHE2_MANAGED_BLOCKING_REPOSITORY_BASE) + || implementsInterface(element, PANACHE2_STATELESS_BLOCKING_REPOSITORY_BASE))); + if ( context.usesQuarkusPanache2() + && element != null + && !implementsInterface(element, PANACHE2_MANAGED_BLOCKING_REPOSITORY_BASE) + && !implementsInterface(element, PANACHE2_STATELESS_BLOCKING_REPOSITORY_BASE) + && !implementsInterface(element, PANACHE2_MANAGED_REACTIVE_REPOSITORY_BASE) + && !implementsInterface(element, PANACHE2_STATELESS_REACTIVE_REPOSITORY_BASE) + // FIXME: add other default for JD repos? + ) { + // look for any annotated method, see if they return a Uni + final List methodsOfClass = + methodsIn( context.getAllMembers( element ) ); + for ( ExecutableElement method : methodsOfClass ) { + // trust the first method, no need to look for them all + if ( containsAnnotation( method, HQL, SQL, JD_QUERY, FIND, JD_FIND ) ) { + favorBlocking = !isUni( method.getReturnType() ); + break; + } + } + } + // FIXME: probably go in this branch if we have a getter too? + if ( favorBlocking ) { + String name; + String sessionType; + if ( getter != null ) { + name = getter.getSimpleName().toString(); + sessionType = fullReturnType(getter); + } + else if(element != null + && implementsInterface(element, PANACHE2_STATELESS_BLOCKING_REPOSITORY_BASE)) { + name = "getStatelessSession"; + sessionType = HIB_STATELESS_SESSION; + } + else { // good default + name = "getSession"; + sessionType = HIB_SESSION; + } putMember( name, new RepositoryConstructor( this, @@ -934,13 +1105,20 @@ private String setupQuarkusDaoConstructor() { true ) ); - return ENTITY_MANAGER; + return sessionType; } else { importType( Constants.QUARKUS_SESSION_OPERATIONS ); // use this getter to get the method, do not generate an injection point for its type - sessionGetter = "SessionOperations.getSession()"; - return Constants.UNI_MUTINY_SESSION; + if(element != null + && implementsInterface(element, PANACHE2_STATELESS_REACTIVE_REPOSITORY_BASE)) { + sessionGetter = "SessionOperations.getStatelessSession()"; + return UNI_MUTINY_STATELESS_SESSION; + } + else { + sessionGetter = "SessionOperations.getSession()"; + return Constants.UNI_MUTINY_SESSION; + } } } @@ -1452,6 +1630,15 @@ private static TypeMirror ununi(TypeMirror returnType) { return returnType; } + private static boolean isUni (TypeMirror returnType){ + if ( returnType.getKind() == TypeKind.DECLARED ) { + final DeclaredType declaredType = (DeclaredType) returnType; + final TypeElement typeElement = (TypeElement) declaredType.asElement(); + return typeElement.getQualifiedName().contentEquals( Constants.UNI ); + } + return false; + } + private static boolean isLegalRawResultType(String containerTypeName) { return LEGAL_RAW_RESULT_TYPES.contains( containerTypeName ); } diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/CDIAccessorMetaAttribute.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/CDIAccessorMetaAttribute.java new file mode 100644 index 000000000000..22babb13d35d --- /dev/null +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/CDIAccessorMetaAttribute.java @@ -0,0 +1,106 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.processor.annotation; + +import javax.lang.model.element.Element; + +import org.hibernate.processor.model.MetaAttribute; +import org.hibernate.processor.model.Metamodel; +import org.hibernate.processor.util.StringUtil; + +public class CDIAccessorMetaAttribute implements MetaAttribute { + + private AnnotationMetaEntity annotationMetaEntity; + private String propertyName; + private String typeName; + + public CDIAccessorMetaAttribute(AnnotationMetaEntity annotationMetaEntity, Element repositoryElement) { + this.annotationMetaEntity = annotationMetaEntity; + // turn the name into lowercase + String name = repositoryElement.getSimpleName().toString(); + // FIXME: this is wrong for types like STEFQueries + this.propertyName = StringUtil.decapitalize( name ); + this.typeName = name; + } + + public CDIAccessorMetaAttribute(AnnotationMetaEntity annotationMetaEntity, String propertyName, String className) { + this.annotationMetaEntity = annotationMetaEntity; + this.propertyName = propertyName; + this.typeName = className; + } + + @Override + public boolean hasTypedAttribute() { + return true; + } + + @Override + public boolean hasStringAttribute() { + return false; + } + + @Override + public String getAttributeDeclarationString() { + final StringBuilder declaration = new StringBuilder(); + modifiers( declaration ); + preamble( declaration ); + returnCDI( declaration ); + closingBrace( declaration ); + return declaration.toString(); + } + + private void returnCDI(StringBuilder declaration) { + annotationMetaEntity.importType("jakarta.enterprise.inject.spi.CDI"); + declaration + .append("\treturn CDI.current().select(") + .append(typeName) + .append(".class).get();\n"); + } + + void closingBrace(StringBuilder declaration) { + declaration.append("}"); + } + + void preamble(StringBuilder declaration) { + declaration + .append(typeName) + .append(" ") + .append( getPropertyName() ); + declaration + .append("() {\n"); + } + + @Override + public String getAttributeNameDeclarationString() { + return ""; + } + + @Override + public String getMetaType() { + throw new UnsupportedOperationException("operation not supported"); + } + + @Override + public String getPropertyName() { + return propertyName; + } + + @Override + public String getTypeDeclaration() { + return ""; + } + + void modifiers(StringBuilder declaration) { + declaration + .append("\npublic static "); + } + + + @Override + public Metamodel getHostingEntity() { + return annotationMetaEntity; + } + +} diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/CDITypeMetaAttribute.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/CDITypeMetaAttribute.java new file mode 100644 index 000000000000..868fdb6c9102 --- /dev/null +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/CDITypeMetaAttribute.java @@ -0,0 +1,92 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.processor.annotation; + +import org.hibernate.processor.HibernateProcessor; +import org.hibernate.processor.model.MetaAttribute; +import org.hibernate.processor.model.Metamodel; + +public class CDITypeMetaAttribute implements MetaAttribute { + + private AnnotationMetaEntity annotationMetaEntity; + private String typeName; + private Object superTypeName; + + public CDITypeMetaAttribute(AnnotationMetaEntity annotationMetaEntity, String className, String superTypeName) { + this.annotationMetaEntity = annotationMetaEntity; + this.superTypeName = superTypeName; + this.typeName = className; + } + + @Override + public boolean hasTypedAttribute() { + return true; + } + + @Override + public boolean hasStringAttribute() { + return false; + } + + @Override + public String getAttributeDeclarationString() { + final StringBuilder declaration = new StringBuilder(); + modifiers( declaration ); + preamble( declaration ); + closingBrace( declaration ); + return declaration.toString(); + } + + void closingBrace(StringBuilder declaration) { + declaration.append("}"); + } + + void preamble(StringBuilder declaration) { + declaration + .append("class ") + .append(typeName) + .append(" implements ") + .append( superTypeName ); + declaration + .append(" {\n"); + } + + @Override + public String getAttributeNameDeclarationString() { + return ""; + } + + @Override + public String getMetaType() { + throw new UnsupportedOperationException("operation not supported"); + } + + @Override + public String getPropertyName() { + return ""; + } + + @Override + public String getTypeDeclaration() { + return ""; + } + + void modifiers(StringBuilder declaration) { + annotationMetaEntity.importType("jakarta.annotation.Generated"); + annotationMetaEntity.importType("jakarta.enterprise.context.Dependent"); + declaration + .append("\n@Dependent\n") + .append("@Generated(\""+HibernateProcessor.class.getName()+"\")\n"); + declaration + .append("public static "); + } + + + @Override + public Metamodel getHostingEntity() { + return annotationMetaEntity; + } + +} diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/NonManagedMetamodel.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/NonManagedMetamodel.java index ce55cbcb4a9a..6127612d4022 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/NonManagedMetamodel.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/annotation/NonManagedMetamodel.java @@ -12,7 +12,7 @@ public class NonManagedMetamodel extends AnnotationMetaEntity { public NonManagedMetamodel(TypeElement element, Context context, boolean jakartaDataStaticMetamodel, @Nullable AnnotationMeta parent) { - super( element, context, false, jakartaDataStaticMetamodel, parent ); + super( element, context, false, jakartaDataStaticMetamodel, parent, null ); } public static NonManagedMetamodel create( diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/Constants.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/Constants.java index c521d28a9d62..7bb3793da03b 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/Constants.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/Constants.java @@ -158,6 +158,12 @@ public final class Constants { public static final String PANACHE_REACTIVE_REPOSITORY_BASE = "io.quarkus.hibernate.reactive.panache.PanacheRepositoryBase"; public static final String PANACHE_REACTIVE_ENTITY_BASE = "io.quarkus.hibernate.reactive.panache.PanacheEntityBase"; + public static final String PANACHE2_ENTITY_MARKER = "io.quarkus.hibernate.panache.PanacheEntityMarker"; + public static final String PANACHE2_MANAGED_BLOCKING_REPOSITORY_BASE = "io.quarkus.hibernate.panache.managed.blocking.PanacheManagedBlockingRepositoryBase"; + public static final String PANACHE2_STATELESS_BLOCKING_REPOSITORY_BASE = "io.quarkus.hibernate.panache.stateless.blocking.PanacheStatelessBlockingRepositoryBase"; + public static final String PANACHE2_MANAGED_REACTIVE_REPOSITORY_BASE = "io.quarkus.hibernate.panache.managed.reactive.PanacheManagedReactiveRepositoryBase"; + public static final String PANACHE2_STATELESS_REACTIVE_REPOSITORY_BASE = "io.quarkus.hibernate.panache.stateless.reactive.PanacheStatelessReactiveRepositoryBase"; + public static final Map COLLECTIONS = Map.of( COLLECTION, Constants.COLLECTION_ATTRIBUTE, SET, Constants.SET_ATTRIBUTE, diff --git a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/TypeUtils.java b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/TypeUtils.java index a8ef4f810d47..8be4f51d2f86 100644 --- a/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/TypeUtils.java +++ b/tooling/metamodel-generator/src/main/java/org/hibernate/processor/util/TypeUtils.java @@ -27,6 +27,7 @@ import javax.lang.model.type.TypeVariable; import javax.lang.model.type.WildcardType; import javax.lang.model.util.ElementFilter; +import javax.lang.model.util.Elements; import javax.lang.model.util.SimpleTypeVisitor8; import javax.tools.Diagnostic; import java.util.HashMap; @@ -247,6 +248,26 @@ public static boolean isAnnotationMirrorOfType(AnnotationMirror annotationMirror return null; } + /** + * Checks whether the {@code Element} hosts the annotation (directly or inherited) with the given fully qualified class name. + * + * @param element the element to check for the hosted annotation + * @param qualifiedName the fully qualified class name of the annotation to check for + * + * @return the annotation mirror for the specified annotation class from the {@code Element} or {@code null} in case + * the {@code TypeElement} does not host the specified annotation (directly or inherited). + */ + public static @Nullable AnnotationMirror getInheritedAnnotationMirror(Elements elements, Element element, String qualifiedName) { + assert element != null; + assert qualifiedName != null; + for ( AnnotationMirror mirror : elements.getAllAnnotationMirrors(element) ) { + if ( isAnnotationMirrorOfType( mirror, qualifiedName ) ) { + return mirror; + } + } + return null; + } + public static boolean hasAnnotation(Element element, String qualifiedName) { return getAnnotationMirror( element, qualifiedName ) != null; } @@ -657,6 +678,14 @@ public static boolean implementsInterface(TypeElement type, String interfaceName } } } + TypeMirror superclass = type.getSuperclass(); + if ( superclass != null && superclass.getKind() == TypeKind.DECLARED ) { + final DeclaredType declaredType = (DeclaredType) superclass; + final TypeElement typeElement = (TypeElement) declaredType.asElement(); + if ( implementsInterface( typeElement, interfaceName) ) { + return true; + } + } return false; } diff --git a/tooling/metamodel-generator/src/quarkusOrmPanache/java/org/hibernate/processor/test/ormPanache/QuarkusOrmPanacheTest.java b/tooling/metamodel-generator/src/quarkusOrmPanache/java/org/hibernate/processor/test/ormPanache/QuarkusOrmPanacheTest.java index ccf3fe5bae0c..1798b595fe77 100644 --- a/tooling/metamodel-generator/src/quarkusOrmPanache/java/org/hibernate/processor/test/ormPanache/QuarkusOrmPanacheTest.java +++ b/tooling/metamodel-generator/src/quarkusOrmPanache/java/org/hibernate/processor/test/ormPanache/QuarkusOrmPanacheTest.java @@ -4,6 +4,7 @@ */ package org.hibernate.processor.test.ormPanache; +import org.hibernate.Session; import org.hibernate.processor.test.util.CompilationTest; import org.hibernate.processor.test.util.TestUtil; import org.hibernate.processor.test.util.WithClasses; @@ -100,7 +101,7 @@ void testQuarkusRepositoryMetamodel() throws Exception { Assertions.assertFalse( Modifier.isStatic( method.getModifiers() ) ); // Make sure we have the proper constructor - Constructor constructor = repositoryClass.getDeclaredConstructor( EntityManager.class ); + Constructor constructor = repositoryClass.getDeclaredConstructor( Session.class ); Assertions.assertNotNull( constructor ); Assertions.assertTrue( constructor.isAnnotationPresent( Inject.class ) ); }