diff --git a/bom/application/pom.xml b/bom/application/pom.xml
index 93166305a5bb9..381fc3ebdcb4b 100644
--- a/bom/application/pom.xml
+++ b/bom/application/pom.xml
@@ -639,6 +639,11 @@
quarkus-panache-common-deployment
${project.version}
+
+ io.quarkus
+ quarkus-mongodb-panache-common-deployment
+ ${project.version}
+
io.quarkus
quarkus-panache-mock
@@ -709,6 +714,16 @@
quarkus-mongodb-panache-deployment
${project.version}
+
+ io.quarkus
+ quarkus-mongodb-panache-kotlin
+ ${project.version}
+
+
+ io.quarkus
+ quarkus-mongodb-panache-common
+ ${project.version}
+
io.quarkus
quarkus-hibernate-search-elasticsearch
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/Capability.java b/core/deployment/src/main/java/io/quarkus/deployment/Capability.java
index f2672f2ec5ea6..dc1263da2918b 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/Capability.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/Capability.java
@@ -31,6 +31,7 @@ public enum Capability {
JWT,
TIKA,
MONGODB_PANACHE,
+ MONGODB_PANACHE_KOTLIN,
FLYWAY,
LIQUIBASE,
SECURITY,
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java b/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java
index 2f1c3145eb830..90461bc4d0ee1 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/ExtensionLoader.java
@@ -10,6 +10,7 @@
import static io.quarkus.deployment.util.ReflectUtil.rawTypeIs;
import static io.quarkus.deployment.util.ReflectUtil.rawTypeOf;
import static io.quarkus.deployment.util.ReflectUtil.rawTypeOfParameter;
+import static java.util.Arrays.asList;
import java.io.IOException;
import java.lang.reflect.AnnotatedElement;
@@ -253,6 +254,9 @@ public static Consumer loadStepsFrom(Class> clazz, BuildTim
final Constructor>[] constructors = clazz.getDeclaredConstructors();
// this is the chain configuration that will contain all steps on this class and be returned
Consumer chainConfig = Functions.discardingConsumer();
+ if (Modifier.isAbstract(clazz.getModifiers())) {
+ return chainConfig;
+ }
// this is the step configuration that applies to all steps on this class
Consumer stepConfig = Functions.discardingConsumer();
// this is the build step instance setup that applies to all steps on this class
@@ -482,7 +486,7 @@ public static Consumer loadStepsFrom(Class> clazz, BuildTim
}
// now iterate the methods
- final Method[] methods = clazz.getDeclaredMethods();
+ final List methods = getMethods(clazz);
for (Method method : methods) {
final int mods = method.getModifiers();
if (Modifier.isStatic(mods)) {
@@ -963,6 +967,15 @@ public String toString() {
return chainConfig;
}
+ protected static List getMethods(Class> clazz) {
+ List declaredMethods = new ArrayList<>();
+ if (!clazz.getName().equals(Object.class.getName())) {
+ declaredMethods.addAll(getMethods(clazz.getSuperclass()));
+ declaredMethods.addAll(asList(clazz.getDeclaredMethods()));
+ }
+ return declaredMethods;
+ }
+
private static BooleanSupplier and(BooleanSupplier a, BooleanSupplier b) {
return () -> a.getAsBoolean() && b.getAsBoolean();
}
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/Feature.java b/core/deployment/src/main/java/io/quarkus/deployment/Feature.java
index 56bab5e6a90d5..843cd31d9f4fd 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/Feature.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/Feature.java
@@ -59,6 +59,7 @@ public enum Feature {
MAILER,
MONGODB_CLIENT,
MONGODB_PANACHE,
+ MONGODB_PANACHE_KOTLIN,
MUTINY,
NARAYANA_JTA,
NARAYANA_STM,
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/util/AsmUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/util/AsmUtil.java
index b1d43433b0780..c72b9c9d88ba5 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/util/AsmUtil.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/util/AsmUtil.java
@@ -1,7 +1,20 @@
package io.quarkus.deployment.util;
+import static java.util.Arrays.asList;
+import static org.objectweb.asm.Type.BOOLEAN_TYPE;
+import static org.objectweb.asm.Type.BYTE_TYPE;
+import static org.objectweb.asm.Type.CHAR_TYPE;
+import static org.objectweb.asm.Type.FLOAT_TYPE;
+import static org.objectweb.asm.Type.INT_TYPE;
+import static org.objectweb.asm.Type.LONG_TYPE;
+import static org.objectweb.asm.Type.SHORT_TYPE;
+import static org.objectweb.asm.Type.VOID_TYPE;
+import static org.objectweb.asm.Type.getType;
+
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.function.Function;
import org.jboss.jandex.ArrayType;
@@ -20,6 +33,36 @@
*/
public class AsmUtil {
+ public static final List PRIMITIVES = asList(
+ VOID_TYPE,
+ BOOLEAN_TYPE,
+ CHAR_TYPE,
+ BYTE_TYPE,
+ SHORT_TYPE,
+ INT_TYPE,
+ FLOAT_TYPE,
+ LONG_TYPE);
+ public static final List WRAPPERS = asList(
+ getType(Void.class),
+ getType(Boolean.class),
+ getType(Character.class),
+ getType(Byte.class),
+ getType(Short.class),
+ getType(Integer.class),
+ getType(Float.class),
+ getType(Long.class));
+ public static final Map WRAPPER_TO_PRIMITIVE = new HashMap<>();
+
+ static {
+ for (int i = 0; i < AsmUtil.PRIMITIVES.size(); i++) {
+ AsmUtil.WRAPPER_TO_PRIMITIVE.put(AsmUtil.WRAPPERS.get(i), AsmUtil.PRIMITIVES.get(i));
+ }
+ }
+
+ public static org.objectweb.asm.Type autobox(org.objectweb.asm.Type primitive) {
+ return WRAPPERS.get(primitive.getSort());
+ }
+
/**
* Returns the Java bytecode signature of a given Jandex MethodInfo using the given type argument mappings.
* For example, given this method:
@@ -431,6 +474,43 @@ public static void unboxIfRequired(MethodVisitor mv, Type jandexType) {
}
}
+ /**
+ * Calls the right unboxing method for the given Jandex Type if it is a primitive.
+ *
+ * @param mv The MethodVisitor on which to visit the unboxing instructions
+ * @param type The Jandex Type to unbox if it is a primitive.
+ */
+ public static void unboxIfRequired(MethodVisitor mv, org.objectweb.asm.Type type) {
+ if (type.getSort() <= org.objectweb.asm.Type.DOUBLE) {
+ switch (type.getSort()) {
+ case org.objectweb.asm.Type.BOOLEAN:
+ unbox(mv, "java/lang/Boolean", "booleanValue", "Z");
+ break;
+ case org.objectweb.asm.Type.BYTE:
+ unbox(mv, "java/lang/Byte", "byteValue", "B");
+ break;
+ case org.objectweb.asm.Type.CHAR:
+ unbox(mv, "java/lang/Character", "charValue", "C");
+ break;
+ case org.objectweb.asm.Type.DOUBLE:
+ unbox(mv, "java/lang/Double", "doubleValue", "D");
+ break;
+ case org.objectweb.asm.Type.FLOAT:
+ unbox(mv, "java/lang/Float", "floatValue", "F");
+ break;
+ case org.objectweb.asm.Type.INT:
+ unbox(mv, "java/lang/Integer", "intValue", "I");
+ break;
+ case org.objectweb.asm.Type.LONG:
+ unbox(mv, "java/lang/Long", "longValue", "J");
+ break;
+ case org.objectweb.asm.Type.SHORT:
+ unbox(mv, "java/lang/Short", "shortValue", "S");
+ break;
+ }
+ }
+ }
+
private static void unbox(MethodVisitor mv, String owner, String methodName, String returnTypeSignature) {
mv.visitTypeInsn(Opcodes.CHECKCAST, owner);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, methodName, "()" + returnTypeSignature, false);
diff --git a/extensions/mongodb-client/runtime/src/test/java/io/quarkus/mongodb/reactive/MongoWithReplicasTestBase.java b/extensions/mongodb-client/runtime/src/test/java/io/quarkus/mongodb/reactive/MongoWithReplicasTestBase.java
index 23561d8a5c24c..f9aa3a09c5589 100644
--- a/extensions/mongodb-client/runtime/src/test/java/io/quarkus/mongodb/reactive/MongoWithReplicasTestBase.java
+++ b/extensions/mongodb-client/runtime/src/test/java/io/quarkus/mongodb/reactive/MongoWithReplicasTestBase.java
@@ -108,10 +108,10 @@ protected String getConnectionString() {
}
private static void initializeReplicaSet(final List mongodConfigList) throws UnknownHostException {
- final String arbitrerAddress = "mongodb://" + mongodConfigList.get(0).net().getServerAddress().getHostName() + ":"
+ final String arbiterAddress = "mongodb://" + mongodConfigList.get(0).net().getServerAddress().getHostName() + ":"
+ mongodConfigList.get(0).net().getPort();
final MongoClientSettings mo = MongoClientSettings.builder()
- .applyConnectionString(new ConnectionString(arbitrerAddress)).build();
+ .applyConnectionString(new ConnectionString(arbiterAddress)).build();
try (MongoClient mongo = MongoClients.create(mo)) {
final MongoDatabase mongoAdminDB = mongo.getDatabase("admin");
diff --git a/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheCompanionEnhancer.java b/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheCompanionEnhancer.java
index 620bb5cb771bb..a8e4585184f37 100644
--- a/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheCompanionEnhancer.java
+++ b/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheCompanionEnhancer.java
@@ -23,7 +23,7 @@ public class KotlinPanacheCompanionEnhancer extends PanacheEntityEnhancer methodCustomizers) {
- super(index, KotlinPanacheResourceProcessor.PANACHE_ENTITY_BASE_DOTNAME, methodCustomizers);
+ super(index, methodCustomizers);
modelInfo = new MetamodelInfo<>();
}
diff --git a/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheEntityClassVisitor.java b/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheEntityClassVisitor.java
index 49df9ed685726..74b478f1a039a 100644
--- a/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheEntityClassVisitor.java
+++ b/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheEntityClassVisitor.java
@@ -13,18 +13,17 @@
import io.quarkus.panache.common.deployment.EntityField;
import io.quarkus.panache.common.deployment.EntityModel;
import io.quarkus.panache.common.deployment.MetamodelInfo;
-import io.quarkus.panache.common.deployment.PanacheEntityEnhancer;
import io.quarkus.panache.common.deployment.PanacheMethodCustomizer;
+import io.quarkus.panache.common.deployment.visitors.PanacheEntityClassVisitor;
/**
* This visitor process kotlin entities and removes the final modifier from the compiler generated getters and setters.
* Unlike Java entities, we don't need to generate the getters and setters because kotlinc does that for us but kotlin,
* by default, is final so we need to open those up for hibernate to add its hooks.
*/
-class KotlinPanacheEntityClassVisitor extends PanacheEntityEnhancer.PanacheEntityClassVisitor {
+class KotlinPanacheEntityClassVisitor extends PanacheEntityClassVisitor {
private String entityBinaryType;
- private org.objectweb.asm.Type entityType;
public KotlinPanacheEntityClassVisitor(String className, ClassVisitor outputClassVisitor,
MetamodelInfo> modelInfo,
@@ -38,8 +37,7 @@ public KotlinPanacheEntityClassVisitor(String className, ClassVisitor outputClas
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
- entityBinaryType = name.replace('.', '/');
- entityType = org.objectweb.asm.Type.getType("L" + entityBinaryType + ";");
+ entityBinaryType = name;
}
@Override
@@ -52,20 +50,10 @@ public MethodVisitor visitMethod(int access, String name, String descriptor, Str
}
@Override
- protected String getModelDescriptor() {
- return "Ljava/lang/Class;";
- }
-
- @Override
- protected String getPanacheOperationsBinaryName() {
+ protected String getPanacheOperationsInternalName() {
return KotlinPanacheEntityEnhancer.JPA_OPERATIONS_BINARY_NAME;
}
- @Override
- protected void injectModel(MethodVisitor mv) {
- mv.visitLdcInsn(entityType);
- }
-
@Override
protected void generateAccessorSetField(MethodVisitor mv, EntityField field) {
mv.visitMethodInsn(
diff --git a/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheEntityEnhancer.java b/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheEntityEnhancer.java
index bd682866d26bd..f976635ad80ef 100644
--- a/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheEntityEnhancer.java
+++ b/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheEntityEnhancer.java
@@ -1,10 +1,11 @@
package io.quarkus.hibernate.orm.panache.kotlin.deployment;
+import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.PANACHE_ENTITY_BASE;
+import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.TRANSIENT;
+
import java.lang.reflect.Modifier;
import java.util.List;
-import javax.persistence.Transient;
-
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.FieldInfo;
@@ -24,16 +25,15 @@ public class KotlinPanacheEntityEnhancer extends PanacheEntityEnhancer methodCustomizers) {
- super(index, KotlinPanacheResourceProcessor.PANACHE_ENTITY_BASE_DOTNAME, methodCustomizers);
+ super(index, methodCustomizers);
modelInfo = new MetamodelInfo<>();
}
@Override
public ClassVisitor apply(String className, ClassVisitor outputClassVisitor) {
- return new KotlinPanacheEntityClassVisitor(className, outputClassVisitor, modelInfo, panacheEntityBaseClassInfo,
+ return new KotlinPanacheEntityClassVisitor(className, outputClassVisitor, modelInfo,
+ indexView.getClassByName(PANACHE_ENTITY_BASE),
indexView.getClassByName(DotName.createSimple(className)), methodCustomizers);
}
@@ -43,7 +43,7 @@ public void collectFields(ClassInfo classInfo) {
String name = fieldInfo.name();
if (Modifier.isPublic(fieldInfo.flags())
&& !Modifier.isStatic(fieldInfo.flags())
- && !fieldInfo.hasAnnotation(DOTNAME_TRANSIENT)) {
+ && !fieldInfo.hasAnnotation(TRANSIENT)) {
entityModel.addField(new EntityField(name, DescriptorUtils.typeToString(fieldInfo.type())));
}
}
diff --git a/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheRepositoryClassVisitor.java b/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheRepositoryClassVisitor.java
index 674bf0ba3a44f..6fd8e6b5b4a4b 100644
--- a/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheRepositoryClassVisitor.java
+++ b/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheRepositoryClassVisitor.java
@@ -1,11 +1,11 @@
package io.quarkus.hibernate.orm.panache.kotlin.deployment;
+import static io.quarkus.deployment.util.AsmUtil.autobox;
import static io.quarkus.gizmo.Gizmo.ASM_API_VERSION;
import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.JPA_OPERATIONS;
-import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.PANACHE_REPOSITORY_BASE_DOTNAME;
-import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.autobox;
+import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.PANACHE_REPOSITORY_BASE;
import static io.quarkus.hibernate.orm.panache.kotlin.deployment.KotlinPanacheResourceProcessor.sanitize;
-import static io.quarkus.panache.common.deployment.PanacheRepositoryEnhancer.PanacheRepositoryClassVisitor.findEntityTypeArgumentsForPanacheRepository;
+import static io.quarkus.panache.common.deployment.visitors.PanacheRepositoryClassVisitor.findEntityTypeArgumentsForPanacheRepository;
import static org.jboss.jandex.DotName.createSimple;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
@@ -57,7 +57,7 @@ public void visit(int version, int access, String name, String signature, String
final String repositoryClassName = name.replace('/', '.');
String[] foundTypeArguments = findEntityTypeArgumentsForPanacheRepository(indexView, repositoryClassName,
- PANACHE_REPOSITORY_BASE_DOTNAME);
+ PANACHE_REPOSITORY_BASE);
String entityBinaryType = foundTypeArguments[0];
entitySignature = "L" + entityBinaryType + ";";
diff --git a/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheRepositoryEnhancer.java b/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheRepositoryEnhancer.java
index 003d256378604..53e58e4f21d47 100644
--- a/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheRepositoryEnhancer.java
+++ b/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheRepositoryEnhancer.java
@@ -8,7 +8,7 @@
public class KotlinPanacheRepositoryEnhancer extends PanacheRepositoryEnhancer {
public KotlinPanacheRepositoryEnhancer(IndexView index) {
- super(index, KotlinPanacheResourceProcessor.PANACHE_REPOSITORY_BASE_DOTNAME);
+ super(index, KotlinPanacheResourceProcessor.PANACHE_REPOSITORY_BASE);
}
@Override
diff --git a/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheResourceProcessor.java b/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheResourceProcessor.java
index f463cca99af13..a3b4e29795ca3 100644
--- a/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheResourceProcessor.java
+++ b/extensions/panache/hibernate-orm-panache-kotlin/deployment/src/main/java/io/quarkus/hibernate/orm/panache/kotlin/deployment/KotlinPanacheResourceProcessor.java
@@ -11,6 +11,7 @@
import java.util.stream.Collectors;
import javax.persistence.EntityManager;
+import javax.persistence.Transient;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
@@ -40,7 +41,6 @@
import io.quarkus.panache.common.deployment.PanacheFieldAccessEnhancer;
import io.quarkus.panache.common.deployment.PanacheMethodCustomizer;
import io.quarkus.panache.common.deployment.PanacheMethodCustomizerBuildItem;
-import io.quarkus.panache.common.deployment.PanacheRepositoryEnhancer;
public final class KotlinPanacheResourceProcessor {
@@ -49,12 +49,12 @@ public final class KotlinPanacheResourceProcessor {
static final String JPA_OPERATIONS = createSimple(JpaOperations.class.getName())
.toString().replace(".", "/");
- static final DotName PANACHE_REPOSITORY_BASE_DOTNAME = createSimple(PanacheRepositoryBase.class.getName());
- static final DotName PANACHE_REPOSITORY_DOTNAME = createSimple(PanacheRepository.class.getName());
+ static final DotName PANACHE_REPOSITORY_BASE = createSimple(PanacheRepositoryBase.class.getName());
+ static final DotName PANACHE_REPOSITORY = createSimple(PanacheRepository.class.getName());
- static final DotName PANACHE_ENTITY_BASE_DOTNAME = createSimple(PanacheEntityBase.class.getName());
- static final DotName PANACHE_ENTITY_DOTNAME = createSimple(PanacheEntity.class.getName());
- static final DotName PANACHE_COMPANION_DOTNAME = createSimple(PanacheCompanion.class.getName());
+ static final DotName PANACHE_ENTITY_BASE = createSimple(PanacheEntityBase.class.getName());
+ static final DotName PANACHE_ENTITY = createSimple(PanacheEntity.class.getName());
+ static final DotName PANACHE_COMPANION = createSimple(PanacheCompanion.class.getName());
static final String PANACHE_ENTITY_BASE_SIGNATURE = toBinarySignature(PanacheEntityBase.class);
static final String PANACHE_ENTITY_SIGNATURE = toBinarySignature(PanacheEntity.class);
@@ -62,6 +62,7 @@ public final class KotlinPanacheResourceProcessor {
static final String OBJECT_SIGNATURE = toBinarySignature(Object.class);
static final String CLASS_SIGNATURE = toBinarySignature(Class.class);
+ static final DotName TRANSIENT = DotName.createSimple(Transient.class.getName());
static final org.objectweb.asm.Type CLASS_TYPE = org.objectweb.asm.Type.getType(Class.class);
static final org.objectweb.asm.Type OBJECT_TYPE = org.objectweb.asm.Type.getType(Object.class);
@@ -72,15 +73,6 @@ public final class KotlinPanacheResourceProcessor {
org.objectweb.asm.Type.getType(PanacheEntityBase.class),
org.objectweb.asm.Type.getType(PanacheEntity.class),
org.objectweb.asm.Type.getType(PanacheCompanion.class));
- public static final List PRIMITIVE_TYPES = asList(
- org.objectweb.asm.Type.getType(Void.class),
- org.objectweb.asm.Type.getType(Boolean.class),
- org.objectweb.asm.Type.getType(Character.class),
- org.objectweb.asm.Type.getType(Byte.class),
- org.objectweb.asm.Type.getType(Short.class),
- org.objectweb.asm.Type.getType(Integer.class),
- org.objectweb.asm.Type.getType(Float.class),
- org.objectweb.asm.Type.getType(Long.class));
static org.objectweb.asm.Type sanitize(org.objectweb.asm.Type[] argumentTypes) {
org.objectweb.asm.Type primitiveReplaced = null;
@@ -98,10 +90,6 @@ static org.objectweb.asm.Type sanitize(org.objectweb.asm.Type[] argumentTypes) {
return primitiveReplaced;
}
- static org.objectweb.asm.Type autobox(org.objectweb.asm.Type primitive) {
- return PRIMITIVE_TYPES.get(primitive.getSort());
- }
-
@NotNull
static String toBinarySignature(Class> type) {
return org.objectweb.asm.Type.getType(type).getDescriptor();
@@ -147,16 +135,16 @@ void build(CombinedIndexBuildItem index,
KotlinPanacheRepositoryEnhancer daoEnhancer = new KotlinPanacheRepositoryEnhancer(index.getIndex());
Set daoClasses = new HashSet<>();
- for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(PANACHE_REPOSITORY_BASE_DOTNAME)) {
+ for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(PANACHE_REPOSITORY_BASE)) {
// Skip PanacheRepository
- if (classInfo.name().equals(PANACHE_REPOSITORY_DOTNAME))
+ if (classInfo.name().equals(PANACHE_REPOSITORY))
continue;
- if (PanacheRepositoryEnhancer.skipRepository(classInfo))
+ if (daoEnhancer.skipRepository(classInfo))
continue;
daoClasses.add(classInfo.name().toString());
}
- for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(PANACHE_REPOSITORY_DOTNAME)) {
- if (PanacheRepositoryEnhancer.skipRepository(classInfo))
+ for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(PANACHE_REPOSITORY)) {
+ if (daoEnhancer.skipRepository(classInfo))
continue;
daoClasses.add(classInfo.name().toString());
}
@@ -171,15 +159,15 @@ void build(CombinedIndexBuildItem index,
// Note that we do this in two passes because for some reason Jandex does not give us subtypes
// of PanacheEntity if we ask for subtypes of PanacheEntityBase
// NOTE: we don't skip abstract/generic entities because they still need accessors
- for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(PANACHE_COMPANION_DOTNAME)) {
- if (classInfo.name().equals(PANACHE_ENTITY_DOTNAME))
+ for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(PANACHE_COMPANION)) {
+ if (classInfo.name().equals(PANACHE_ENTITY))
continue;
if (modelClasses.add(classInfo.name().toString())) {
transformers.produce(new BytecodeTransformerBuildItem(classInfo.name().toString(), companionEnhancer));
}
}
- for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(PANACHE_ENTITY_BASE_DOTNAME)) {
- if (classInfo.name().equals(PANACHE_ENTITY_DOTNAME))
+ for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(PANACHE_ENTITY_BASE)) {
+ if (classInfo.name().equals(PANACHE_ENTITY))
continue;
if (modelClasses.add(classInfo.name().toString())) {
entityEnhancer.collectFields(classInfo);
diff --git a/extensions/panache/hibernate-orm-panache-kotlin/runtime/pom.xml b/extensions/panache/hibernate-orm-panache-kotlin/runtime/pom.xml
index 7b468b79fa036..a9890fa2d479b 100644
--- a/extensions/panache/hibernate-orm-panache-kotlin/runtime/pom.xml
+++ b/extensions/panache/hibernate-orm-panache-kotlin/runtime/pom.xml
@@ -63,6 +63,7 @@
org.jetbrains.kotlin
kotlin-reflect
${kotlin.version}
+ test
org.ow2.asm
diff --git a/extensions/panache/hibernate-orm-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/orm/panache/kotlin/PanacheCompanion.kt b/extensions/panache/hibernate-orm-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/orm/panache/kotlin/PanacheCompanion.kt
index c230798d60e58..beaf6597c9e6c 100644
--- a/extensions/panache/hibernate-orm-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/orm/panache/kotlin/PanacheCompanion.kt
+++ b/extensions/panache/hibernate-orm-panache-kotlin/runtime/src/main/kotlin/io/quarkus/hibernate/orm/panache/kotlin/PanacheCompanion.kt
@@ -56,7 +56,7 @@ interface PanacheCompanion {
fun find(query: String, vararg params: Any): PanacheQuery = injectionMissing()
/**
- * Find entities using a query and the given sort options, with optional indexed parameters.
+ * Find entities using a query and the given sort options with optional indexed parameters.
*
* @param query a query string
* @param sort the sort strategy to use
diff --git a/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheHibernateResourceProcessor.java b/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheHibernateResourceProcessor.java
index e1c600b6add26..f8b88fad31145 100644
--- a/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheHibernateResourceProcessor.java
+++ b/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheHibernateResourceProcessor.java
@@ -40,7 +40,6 @@
import io.quarkus.panache.common.deployment.PanacheFieldAccessEnhancer;
import io.quarkus.panache.common.deployment.PanacheMethodCustomizer;
import io.quarkus.panache.common.deployment.PanacheMethodCustomizerBuildItem;
-import io.quarkus.panache.common.deployment.PanacheRepositoryEnhancer;
public final class PanacheHibernateResourceProcessor {
@@ -117,12 +116,12 @@ void build(CombinedIndexBuildItem index,
// Skip PanacheRepository
if (classInfo.name().equals(DOTNAME_PANACHE_REPOSITORY))
continue;
- if (PanacheRepositoryEnhancer.skipRepository(classInfo))
+ if (daoEnhancer.skipRepository(classInfo))
continue;
daoClasses.add(classInfo.name().toString());
}
for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(DOTNAME_PANACHE_REPOSITORY)) {
- if (PanacheRepositoryEnhancer.skipRepository(classInfo))
+ if (daoEnhancer.skipRepository(classInfo))
continue;
daoClasses.add(classInfo.name().toString());
}
diff --git a/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheJpaEntityEnhancer.java b/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheJpaEntityEnhancer.java
index b66738d056c75..df2ee5f216cdc 100644
--- a/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheJpaEntityEnhancer.java
+++ b/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheJpaEntityEnhancer.java
@@ -24,6 +24,7 @@
import io.quarkus.panache.common.deployment.MetamodelInfo;
import io.quarkus.panache.common.deployment.PanacheEntityEnhancer;
import io.quarkus.panache.common.deployment.PanacheMethodCustomizer;
+import io.quarkus.panache.common.deployment.visitors.PanacheEntityClassVisitor;
public class PanacheJpaEntityEnhancer extends PanacheEntityEnhancer>> {
@@ -41,13 +42,14 @@ public class PanacheJpaEntityEnhancer extends PanacheEntityEnhancer methodCustomizers) {
- super(index, PanacheHibernateResourceProcessor.DOTNAME_PANACHE_ENTITY_BASE, methodCustomizers);
+ super(index, methodCustomizers);
modelInfo = new MetamodelInfo<>();
}
@Override
public ClassVisitor apply(String className, ClassVisitor outputClassVisitor) {
- return new PanacheJpaEntityClassVisitor(className, outputClassVisitor, modelInfo, panacheEntityBaseClassInfo,
+ return new PanacheJpaEntityClassVisitor(className, outputClassVisitor, modelInfo,
+ indexView.getClassByName(PanacheHibernateResourceProcessor.DOTNAME_PANACHE_ENTITY_BASE),
indexView.getClassByName(DotName.createSimple(className)), methodCustomizers);
}
@@ -72,7 +74,7 @@ protected String getModelDescriptor() {
}
@Override
- protected String getPanacheOperationsBinaryName() {
+ protected String getPanacheOperationsInternalName() {
return JPA_OPERATIONS_BINARY_NAME;
}
diff --git a/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheJpaRepositoryEnhancer.java b/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheJpaRepositoryEnhancer.java
index 16dace472aeba..29ef3e9d485b5 100644
--- a/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheJpaRepositoryEnhancer.java
+++ b/extensions/panache/hibernate-orm-panache/deployment/src/main/java/io/quarkus/hibernate/orm/panache/deployment/PanacheJpaRepositoryEnhancer.java
@@ -1,14 +1,13 @@
package io.quarkus.hibernate.orm.panache.deployment;
-import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.MethodVisitor;
import io.quarkus.hibernate.orm.panache.PanacheRepository;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import io.quarkus.panache.common.deployment.PanacheRepositoryEnhancer;
+import io.quarkus.panache.common.deployment.visitors.PanacheRepositoryClassVisitor;
public class PanacheJpaRepositoryEnhancer extends PanacheRepositoryEnhancer {
@@ -22,15 +21,14 @@ public PanacheJpaRepositoryEnhancer(IndexView index) {
@Override
public ClassVisitor apply(String className, ClassVisitor outputClassVisitor) {
- return new PanacheJpaRepositoryClassVisitor(className, outputClassVisitor, panacheRepositoryBaseClassInfo,
+ return new PanacheJpaRepositoryClassVisitor(className, outputClassVisitor,
this.indexView);
}
static class PanacheJpaRepositoryClassVisitor extends PanacheRepositoryClassVisitor {
- public PanacheJpaRepositoryClassVisitor(String className, ClassVisitor outputClassVisitor,
- ClassInfo panacheRepositoryBaseClassInfo, IndexView indexView) {
- super(className, outputClassVisitor, panacheRepositoryBaseClassInfo, indexView);
+ public PanacheJpaRepositoryClassVisitor(String className, ClassVisitor outputClassVisitor, IndexView indexView) {
+ super(className, outputClassVisitor, indexView);
}
@Override
@@ -44,19 +42,8 @@ protected DotName getPanacheRepositoryBaseDotName() {
}
@Override
- protected String getPanacheOperationsBinaryName() {
+ protected String getPanacheOperationsInternalName() {
return PanacheJpaEntityEnhancer.JPA_OPERATIONS_BINARY_NAME;
}
-
- @Override
- protected void injectModel(MethodVisitor mv) {
- // inject Class
- mv.visitLdcInsn(entityType);
- }
-
- @Override
- protected String getModelDescriptor() {
- return "Ljava/lang/Class;";
- }
}
}
diff --git a/extensions/panache/mongodb-panache-common/deployment/pom.xml b/extensions/panache/mongodb-panache-common/deployment/pom.xml
new file mode 100644
index 0000000000000..c3c794379512d
--- /dev/null
+++ b/extensions/panache/mongodb-panache-common/deployment/pom.xml
@@ -0,0 +1,67 @@
+
+
+
+ quarkus-mongodb-panache-common-parent
+ io.quarkus
+ 999-SNAPSHOT
+ ../
+
+ 4.0.0
+
+ quarkus-mongodb-panache-common-deployment
+ Quarkus - MongoDB with Panache - Common Deployment
+
+
+
+ io.quarkus
+ quarkus-core-deployment
+
+
+ io.quarkus
+ quarkus-jsonb-spi
+
+
+ io.quarkus
+ quarkus-jackson-spi
+
+
+ io.quarkus
+ quarkus-panache-common-deployment
+
+
+ io.quarkus
+ quarkus-mongodb-client-deployment
+
+
+ io.quarkus
+ quarkus-mongodb-panache-common
+
+
+
+ io.quarkus
+ quarkus-junit5-internal
+ test
+
+
+
+
+
+
+ maven-compiler-plugin
+
+
+
+ io.quarkus
+ quarkus-extension-processor
+ ${project.version}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
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
new file mode 100644
index 0000000000000..ed1f5649ef18c
--- /dev/null
+++ b/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/BasePanacheMongoResourceProcessor.java
@@ -0,0 +1,384 @@
+package io.quarkus.mongodb.panache.deployment;
+
+import static io.quarkus.deployment.util.JandexUtil.resolveTypeParameters;
+import static org.jboss.jandex.DotName.createSimple;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+import org.bson.codecs.pojo.annotations.BsonId;
+import org.bson.codecs.pojo.annotations.BsonIgnore;
+import org.bson.codecs.pojo.annotations.BsonProperty;
+import org.bson.types.ObjectId;
+import org.jboss.jandex.AnnotationInstance;
+import org.jboss.jandex.AnnotationValue;
+import org.jboss.jandex.ClassInfo;
+import org.jboss.jandex.CompositeIndex;
+import org.jboss.jandex.DotName;
+import org.jboss.jandex.FieldInfo;
+import org.jboss.jandex.IndexView;
+import org.jboss.jandex.Indexer;
+import org.jboss.jandex.MethodInfo;
+import org.jboss.jandex.Type;
+
+import io.quarkus.arc.deployment.ValidationPhaseBuildItem;
+import io.quarkus.builder.BuildException;
+import io.quarkus.deployment.annotations.BuildProducer;
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.annotations.ExecutionTime;
+import io.quarkus.deployment.annotations.Record;
+import io.quarkus.deployment.bean.JavaBeanUtil;
+import io.quarkus.deployment.builditem.ApplicationArchivesBuildItem;
+import io.quarkus.deployment.builditem.ApplicationIndexBuildItem;
+import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem;
+import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
+import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
+import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem;
+import io.quarkus.deployment.index.IndexingUtil;
+import io.quarkus.deployment.util.JandexUtil;
+import io.quarkus.jackson.spi.JacksonModuleBuildItem;
+import io.quarkus.jsonb.spi.JsonbDeserializerBuildItem;
+import io.quarkus.jsonb.spi.JsonbSerializerBuildItem;
+import io.quarkus.mongodb.deployment.MongoClientNameBuildItem;
+import io.quarkus.mongodb.deployment.MongoUnremovableClientsBuildItem;
+import io.quarkus.mongodb.panache.MongoEntity;
+import io.quarkus.mongodb.panache.PanacheMongoRecorder;
+import io.quarkus.mongodb.panache.ProjectionFor;
+import io.quarkus.mongodb.panache.jackson.ObjectIdDeserializer;
+import io.quarkus.mongodb.panache.jackson.ObjectIdSerializer;
+import io.quarkus.panache.common.deployment.PanacheEntityClassesBuildItem;
+import io.quarkus.panache.common.deployment.PanacheEntityEnhancer;
+import io.quarkus.panache.common.deployment.PanacheMethodCustomizer;
+import io.quarkus.panache.common.deployment.PanacheMethodCustomizerBuildItem;
+import io.quarkus.panache.common.deployment.PanacheRepositoryEnhancer;
+
+public abstract class BasePanacheMongoResourceProcessor {
+ public static final DotName BSON_ID = createSimple(BsonId.class.getName());
+ public static final DotName BSON_IGNORE = createSimple(BsonIgnore.class.getName());
+ public static final DotName BSON_PROPERTY = createSimple(BsonProperty.class.getName());
+ public static final DotName MONGO_ENTITY = createSimple(MongoEntity.class.getName());
+ public static final DotName OBJECT_ID = createSimple(ObjectId.class.getName());
+ public static final String OBJECT_SIGNATURE = toBinarySignature(Object.class);
+ public static final DotName PROJECTION_FOR = createSimple(ProjectionFor.class.getName());
+
+ protected static String toBinarySignature(Class> type) {
+ return org.objectweb.asm.Type.getType(type).getDescriptor();
+ }
+
+ @BuildStep
+ public void buildImperative(CombinedIndexBuildItem index, ApplicationIndexBuildItem applicationIndex,
+ BuildProducer transformers,
+ BuildProducer reflectiveClass,
+ BuildProducer propertyMappingClass,
+ List methodCustomizersBuildItems) {
+
+ List methodCustomizers = methodCustomizersBuildItems.stream()
+ .map(bi -> bi.getMethodCustomizer()).collect(Collectors.toList());
+
+ processTypes(index, transformers, reflectiveClass, propertyMappingClass, getImperativeTypeBundle(),
+ createRepositoryEnhancer(index), createEntityEnhancer(index, methodCustomizers));
+ }
+
+ @BuildStep
+ public void buildReactive(CombinedIndexBuildItem index,
+ BuildProducer reflectiveClass,
+ BuildProducer propertyMappingClass,
+ BuildProducer transformers,
+ List methodCustomizersBuildItems) {
+ List methodCustomizers = methodCustomizersBuildItems.stream()
+ .map(bi -> bi.getMethodCustomizer()).collect(Collectors.toList());
+
+ processTypes(index, transformers, reflectiveClass, propertyMappingClass, getReactiveTypeBundle(),
+ createReactiveRepositoryEnhancer(index), createReactiveEntityEnhancer(index, methodCustomizers));
+ }
+
+ @BuildStep
+ @Record(ExecutionTime.STATIC_INIT)
+ protected void buildReplacementMap(List propertyMappingClasses, CombinedIndexBuildItem index,
+ PanacheMongoRecorder recorder) {
+ Map> replacementMap = new ConcurrentHashMap<>();
+ for (PropertyMappingClassBuildStep classToMap : propertyMappingClasses) {
+ DotName dotName = createSimple(classToMap.getClassName());
+ ClassInfo classInfo = index.getIndex().getClassByName(dotName);
+ if (classInfo != null) {
+ // only compute field replacement for types inside the index
+ Map classReplacementMap = replacementMap.computeIfAbsent(classToMap.getClassName(),
+ className -> computeReplacement(classInfo));
+ if (classToMap.getAliasClassName() != null) {
+ // also register the replacement map for the projection classes
+ replacementMap.put(classToMap.getAliasClassName(), classReplacementMap);
+ }
+ }
+ }
+
+ recorder.setReplacementCache(replacementMap);
+ }
+
+ private Map computeReplacement(ClassInfo classInfo) {
+ Map replacementMap = new HashMap<>();
+ for (FieldInfo field : classInfo.fields()) {
+ AnnotationInstance bsonProperty = field.annotation(BSON_PROPERTY);
+ if (bsonProperty != null) {
+ replacementMap.put(field.name(), bsonProperty.value().asString());
+ }
+ }
+ for (MethodInfo method : classInfo.methods()) {
+ if (method.name().startsWith("get")) {
+ // we try to replace also for getter
+ AnnotationInstance bsonProperty = method.annotation(BSON_PROPERTY);
+ if (bsonProperty != null) {
+ String fieldName = JavaBeanUtil.decapitalize(method.name().substring(3));
+ replacementMap.put(fieldName, bsonProperty.value().asString());
+ }
+ }
+ }
+ return replacementMap.isEmpty() ? Collections.emptyMap() : replacementMap;
+ }
+
+ protected abstract PanacheEntityEnhancer> createEntityEnhancer(CombinedIndexBuildItem index,
+ List methodCustomizers);
+
+ protected abstract PanacheEntityEnhancer> createReactiveEntityEnhancer(CombinedIndexBuildItem index,
+ List methodCustomizers);
+
+ protected abstract PanacheRepositoryEnhancer createReactiveRepositoryEnhancer(CombinedIndexBuildItem index);
+
+ protected abstract PanacheRepositoryEnhancer createRepositoryEnhancer(CombinedIndexBuildItem index);
+
+ private void extractMappings(Map classPropertyMapping, ClassInfo target, CombinedIndexBuildItem index) {
+ for (FieldInfo fieldInfo : target.fields()) {
+ if (fieldInfo.hasAnnotation(BSON_PROPERTY)) {
+ AnnotationInstance bsonProperty = fieldInfo.annotation(BSON_PROPERTY);
+ classPropertyMapping.put(fieldInfo.name(), bsonProperty.value().asString());
+ }
+ }
+ for (MethodInfo methodInfo : target.methods()) {
+ if (methodInfo.hasAnnotation(BSON_PROPERTY)) {
+ AnnotationInstance bsonProperty = methodInfo.annotation(BSON_PROPERTY);
+ classPropertyMapping.put(methodInfo.name(), bsonProperty.value().asString());
+ }
+ }
+
+ // climb up the hierarchy of types
+ if (!target.superClassType().name().equals(JandexUtil.DOTNAME_OBJECT)) {
+ Type superType = target.superClassType();
+ ClassInfo superClass = index.getIndex().getClassByName(superType.name());
+ extractMappings(classPropertyMapping, superClass, index);
+ }
+ }
+
+ @BuildStep
+ protected PanacheEntityClassesBuildItem findEntityClasses(List entityClasses) {
+ if (!entityClasses.isEmpty()) {
+ Set ret = new HashSet<>();
+ for (PanacheMongoEntityClassBuildItem entityClass : entityClasses) {
+ ret.add(entityClass.get().name().toString());
+ }
+ return new PanacheEntityClassesBuildItem(ret);
+ }
+ return null;
+ }
+
+ protected abstract TypeBundle getImperativeTypeBundle();
+
+ protected abstract TypeBundle getReactiveTypeBundle();
+
+ @BuildStep
+ protected void handleProjectionFor(CombinedIndexBuildItem index,
+ BuildProducer propertyMappingClass,
+ BuildProducer transformers) {
+ // manage @BsonProperty for the @ProjectionFor annotation
+ Map> propertyMapping = new HashMap<>();
+ for (AnnotationInstance annotationInstance : index.getIndex().getAnnotations(PROJECTION_FOR)) {
+ Type targetClass = annotationInstance.value().asClass();
+ ClassInfo target = index.getIndex().getClassByName(targetClass.name());
+ Map classPropertyMapping = new HashMap<>();
+ extractMappings(classPropertyMapping, target, index);
+ propertyMapping.put(targetClass.name(), classPropertyMapping);
+ }
+ for (AnnotationInstance annotationInstance : index.getIndex().getAnnotations(PROJECTION_FOR)) {
+ Type targetClass = annotationInstance.value().asClass();
+ Map targetPropertyMapping = propertyMapping.get(targetClass.name());
+ if (targetPropertyMapping != null && !targetPropertyMapping.isEmpty()) {
+ ClassInfo info = annotationInstance.target().asClass();
+ ProjectionForEnhancer fieldEnhancer = new ProjectionForEnhancer(targetPropertyMapping);
+ transformers.produce(new BytecodeTransformerBuildItem(info.name().toString(), fieldEnhancer));
+ }
+
+ // Register for building the property mapping cache
+ propertyMappingClass
+ .produce(new PropertyMappingClassBuildStep(targetClass.name().toString(),
+ annotationInstance.target().asClass().name().toString()));
+ }
+ }
+
+ @BuildStep
+ public void mongoClientNames(ApplicationArchivesBuildItem applicationArchivesBuildItem,
+ BuildProducer mongoClientName) {
+ Set values = new HashSet<>();
+ IndexView indexView = applicationArchivesBuildItem.getRootArchive().getIndex();
+ Collection instances = indexView.getAnnotations(MONGO_ENTITY);
+ for (AnnotationInstance annotation : instances) {
+ AnnotationValue clientName = annotation.value("clientName");
+ if ((clientName != null) && !clientName.asString().isEmpty()) {
+ values.add(clientName.asString());
+ }
+ }
+ for (String value : values) {
+ // we don't want the qualifier @MongoClientName qualifier added
+ // as these clients will only be looked up programmatically via name
+ // see MongoOperations#mongoClient
+ mongoClientName.produce(new MongoClientNameBuildItem(value, false));
+ }
+ }
+
+ protected void processEntities(CombinedIndexBuildItem index,
+ BuildProducer transformers, BuildProducer reflectiveClass,
+ BuildProducer propertyMappingClass,
+ PanacheEntityEnhancer> entityEnhancer, TypeBundle typeBundle) {
+
+ Set modelClasses = new HashSet<>();
+ // Note that we do this in two passes because for some reason Jandex does not give us subtypes
+ // of PanacheMongoEntity if we ask for subtypes of PanacheMongoEntityBase
+ for (ClassInfo classInfo : index.getIndex().getAllKnownSubclasses(typeBundle.entityBase().dotName())) {
+ if (classInfo.name().equals(typeBundle.entity().dotName())) {
+ continue;
+ }
+ if (modelClasses.add(classInfo.name().toString()))
+ entityEnhancer.collectFields(classInfo);
+ }
+ for (ClassInfo classInfo : index.getIndex().getAllKnownSubclasses(typeBundle.entity().dotName())) {
+ if (modelClasses.add(classInfo.name().toString()))
+ entityEnhancer.collectFields(classInfo);
+ }
+
+ // iterate over all the entity classes
+ for (String modelClass : modelClasses) {
+ transformers.produce(new BytecodeTransformerBuildItem(modelClass, entityEnhancer));
+
+ //register for reflection entity classes
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, modelClass));
+
+ // Register for building the property mapping cache
+ propertyMappingClass.produce(new PropertyMappingClassBuildStep(modelClass));
+ }
+ }
+
+ protected void processRepositories(CombinedIndexBuildItem index,
+ BuildProducer transformers,
+ BuildProducer reflectiveClass,
+ BuildProducer propertyMappingClass,
+ PanacheRepositoryEnhancer repositoryEnhancer, TypeBundle typeBundle) {
+
+ Set daoClasses = new HashSet<>();
+ Set daoTypeParameters = new HashSet<>();
+ for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(typeBundle.repositoryBase().dotName())) {
+ // Skip PanacheMongoRepository and abstract repositories
+ if (classInfo.name().equals(typeBundle.repository().dotName()) || repositoryEnhancer.skipRepository(classInfo)) {
+ continue;
+ }
+ daoClasses.add(classInfo.name().toString());
+ daoTypeParameters.addAll(
+ resolveTypeParameters(classInfo.name(), typeBundle.repositoryBase().dotName(), index.getIndex()));
+ }
+ for (ClassInfo classInfo : index.getIndex().getAllKnownImplementors(typeBundle.repository().dotName())) {
+ if (repositoryEnhancer.skipRepository(classInfo)) {
+ continue;
+ }
+ daoClasses.add(classInfo.name().toString());
+ daoTypeParameters.addAll(
+ resolveTypeParameters(classInfo.name(), typeBundle.repositoryBase().dotName(), index.getIndex()));
+ }
+ for (String daoClass : daoClasses) {
+ transformers.produce(new BytecodeTransformerBuildItem(daoClass, repositoryEnhancer));
+ }
+
+ for (Type parameterType : daoTypeParameters) {
+ // Register for reflection the type parameters of the repository: this should be the entity class and the ID class
+ reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, parameterType.name().toString()));
+
+ // Register for building the property mapping cache
+ propertyMappingClass.produce(new PropertyMappingClassBuildStep(parameterType.name().toString()));
+ }
+ }
+
+ protected void processTypes(CombinedIndexBuildItem index,
+ BuildProducer transformers,
+ BuildProducer reflectiveClass,
+ BuildProducer propertyMappingClass,
+ TypeBundle typeBundle, PanacheRepositoryEnhancer repositoryEnhancer,
+ PanacheEntityEnhancer> entityEnhancer) {
+
+ processRepositories(index, transformers, reflectiveClass, propertyMappingClass,
+ repositoryEnhancer, typeBundle);
+ processEntities(index, transformers, reflectiveClass, propertyMappingClass,
+ entityEnhancer, typeBundle);
+ }
+
+ @BuildStep
+ protected ReflectiveHierarchyBuildItem registerForReflection(CombinedIndexBuildItem index) {
+ Indexer indexer = new Indexer();
+ Set additionalIndex = new HashSet<>();
+ IndexingUtil.indexClass(ObjectId.class.getName(), indexer, index.getIndex(), additionalIndex,
+ BasePanacheMongoResourceProcessor.class.getClassLoader());
+ CompositeIndex compositeIndex = CompositeIndex.create(index.getIndex(), indexer.complete());
+ Type type = Type.create(OBJECT_ID, Type.Kind.CLASS);
+ return new ReflectiveHierarchyBuildItem(type, compositeIndex);
+ }
+
+ @BuildStep
+ protected void registerJacksonSerDeser(BuildProducer customSerDeser) {
+ customSerDeser.produce(
+ new JacksonModuleBuildItem.Builder("ObjectIdModule")
+ .add(ObjectIdSerializer.class.getName(),
+ ObjectIdDeserializer.class.getName(),
+ ObjectId.class.getName())
+ .build());
+ }
+
+ @BuildStep
+ protected void registerJsonbSerDeser(BuildProducer jsonbSerializers,
+ BuildProducer jsonbDeserializers) {
+ jsonbSerializers
+ .produce(new JsonbSerializerBuildItem(io.quarkus.mongodb.panache.jsonb.ObjectIdSerializer.class.getName()));
+ jsonbDeserializers
+ .produce(new JsonbDeserializerBuildItem(io.quarkus.mongodb.panache.jsonb.ObjectIdDeserializer.class.getName()));
+ }
+
+ @BuildStep
+ public void unremovableClients(BuildProducer unremovable) {
+ unremovable.produce(new MongoUnremovableClientsBuildItem());
+ }
+
+ @BuildStep
+ protected ValidationPhaseBuildItem.ValidationErrorBuildItem validate(ValidationPhaseBuildItem validationPhase,
+ CombinedIndexBuildItem index) throws BuildException {
+ // we verify that no ID fields are defined (via @BsonId) when extending PanacheMongoEntity or ReactivePanacheMongoEntity
+ for (AnnotationInstance annotationInstance : index.getIndex().getAnnotations(BSON_ID)) {
+ ClassInfo info = JandexUtil.getEnclosingClass(annotationInstance);
+ if (JandexUtil.isSubclassOf(index.getIndex(), info,
+ getImperativeTypeBundle().entity().dotName())) {
+ BuildException be = new BuildException("You provide a MongoDB identifier via @BsonId inside '" + info.name() +
+ "' but one is already provided by PanacheMongoEntity, " +
+ "your class should extend PanacheMongoEntityBase instead, or use the id provided by PanacheMongoEntity",
+ Collections.emptyList());
+ return new ValidationPhaseBuildItem.ValidationErrorBuildItem(be);
+ } else if (JandexUtil.isSubclassOf(index.getIndex(), info,
+ getReactiveTypeBundle().entity().dotName())) {
+ BuildException be = new BuildException("You provide a MongoDB identifier via @BsonId inside '" + info.name() +
+ "' but one is already provided by ReactivePanacheMongoEntity, " +
+ "your class should extend ReactivePanacheMongoEntityBase instead, or use the id provided by ReactivePanacheMongoEntity",
+ Collections.emptyList());
+ return new ValidationPhaseBuildItem.ValidationErrorBuildItem(be);
+ }
+ }
+ return null;
+ }
+}
diff --git a/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/ByteCodeType.java b/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/ByteCodeType.java
new file mode 100644
index 0000000000000..498bd126b2edd
--- /dev/null
+++ b/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/ByteCodeType.java
@@ -0,0 +1,57 @@
+package io.quarkus.mongodb.panache.deployment;
+
+import static org.jboss.jandex.DotName.createSimple;
+import static org.objectweb.asm.Type.getType;
+
+import java.util.StringJoiner;
+
+import org.jboss.jandex.DotName;
+import org.objectweb.asm.Type;
+
+import io.quarkus.deployment.util.AsmUtil;
+
+public class ByteCodeType {
+ private final Type type;
+
+ public ByteCodeType(Type type) {
+ this.type = type;
+ }
+
+ public ByteCodeType(Class> type) {
+ this.type = getType(type);
+ }
+
+ public ByteCodeType(org.jboss.jandex.Type type) {
+ String typeDescriptor = type.kind() == org.jboss.jandex.Type.Kind.PRIMITIVE
+ ? type.toString()
+ : "L" + type.name().toString().replace('.', '/') + ";";
+ this.type = getType(typeDescriptor);
+ }
+
+ public String descriptor() {
+ return type.getDescriptor();
+ }
+
+ public DotName dotName() {
+ return createSimple(type.getClassName());
+ }
+
+ public String internalName() {
+ return type.getInternalName();
+ }
+
+ @Override
+ public String toString() {
+ return new StringJoiner(", ", ByteCodeType.class.getSimpleName() + "[", "]")
+ .add(type.toString())
+ .toString();
+ }
+
+ public Type type() {
+ return this.type;
+ }
+
+ public ByteCodeType unbox() {
+ return new ByteCodeType(AsmUtil.WRAPPER_TO_PRIMITIVE.getOrDefault(type, type));
+ }
+}
diff --git a/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PanacheMongoEntityClassBuildItem.java b/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PanacheMongoEntityClassBuildItem.java
similarity index 100%
rename from extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PanacheMongoEntityClassBuildItem.java
rename to extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PanacheMongoEntityClassBuildItem.java
diff --git a/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/ProjectionForEnhancer.java b/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/ProjectionForEnhancer.java
similarity index 100%
rename from extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/ProjectionForEnhancer.java
rename to extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/ProjectionForEnhancer.java
diff --git a/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PropertyMappingClassBuildStep.java b/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PropertyMappingClassBuildStep.java
similarity index 88%
rename from extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PropertyMappingClassBuildStep.java
rename to extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PropertyMappingClassBuildStep.java
index dd75677c27f79..a3a178f786c80 100644
--- a/extensions/panache/mongodb-panache/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PropertyMappingClassBuildStep.java
+++ b/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/PropertyMappingClassBuildStep.java
@@ -2,7 +2,7 @@
import io.quarkus.builder.item.MultiBuildItem;
-final class PropertyMappingClassBuildStep extends MultiBuildItem {
+public final class PropertyMappingClassBuildStep extends MultiBuildItem {
private String className;
private String aliasClassName;
diff --git a/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/TypeBundle.java b/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/TypeBundle.java
new file mode 100644
index 0000000000000..a0d5f99671896
--- /dev/null
+++ b/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/TypeBundle.java
@@ -0,0 +1,23 @@
+package io.quarkus.mongodb.panache.deployment;
+
+public interface TypeBundle {
+ ByteCodeType entity();
+
+ ByteCodeType entityBase();
+
+ ByteCodeType entityBaseCompanion();
+
+ ByteCodeType entityCompanion();
+
+ ByteCodeType entityCompanionBase();
+
+ ByteCodeType operations();
+
+ ByteCodeType queryType();
+
+ ByteCodeType repository();
+
+ ByteCodeType repositoryBase();
+
+ ByteCodeType updateType();
+}
diff --git a/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/visitors/PanacheMongoEntityClassVisitor.java b/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/visitors/PanacheMongoEntityClassVisitor.java
new file mode 100644
index 0000000000000..a9448f118af45
--- /dev/null
+++ b/extensions/panache/mongodb-panache-common/deployment/src/main/java/io/quarkus/mongodb/panache/deployment/visitors/PanacheMongoEntityClassVisitor.java
@@ -0,0 +1,128 @@
+package io.quarkus.mongodb.panache.deployment.visitors;
+
+import static io.quarkus.deployment.util.AsmUtil.getDescriptor;
+import static io.quarkus.mongodb.panache.deployment.BasePanacheMongoResourceProcessor.OBJECT_SIGNATURE;
+import static java.util.Arrays.asList;
+import static org.objectweb.asm.Opcodes.CHECKCAST;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+
+import java.util.List;
+import java.util.StringJoiner;
+import java.util.function.Function;
+
+import org.jboss.jandex.AnnotationInstance;
+import org.jboss.jandex.AnnotationValue;
+import org.jboss.jandex.ClassInfo;
+import org.jboss.jandex.MethodInfo;
+import org.jboss.jandex.Type;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import io.quarkus.deployment.util.AsmUtil;
+import io.quarkus.mongodb.panache.deployment.ByteCodeType;
+import io.quarkus.mongodb.panache.deployment.TypeBundle;
+import io.quarkus.panache.common.deployment.EntityField;
+import io.quarkus.panache.common.deployment.EntityModel;
+import io.quarkus.panache.common.deployment.MetamodelInfo;
+import io.quarkus.panache.common.deployment.PanacheEntityEnhancer;
+import io.quarkus.panache.common.deployment.PanacheMethodCustomizer;
+import io.quarkus.panache.common.deployment.visitors.PanacheEntityClassVisitor;
+
+public class PanacheMongoEntityClassVisitor extends PanacheEntityClassVisitor {
+ private static final ByteCodeType CLASS = new ByteCodeType(Class.class);
+
+ private final TypeBundle typeBundle;
+
+ public PanacheMongoEntityClassVisitor(String className,
+ ClassVisitor outputClassVisitor,
+ MetamodelInfo> modelInfo,
+ ClassInfo panacheEntityBaseClassInfo,
+ ClassInfo entityInfo,
+ List methodCustomizers,
+ TypeBundle typeBundle) {
+ super(className, outputClassVisitor, modelInfo, panacheEntityBaseClassInfo, entityInfo, methodCustomizers);
+ this.typeBundle = typeBundle;
+ }
+
+ @Override
+ protected void generateMethod(MethodInfo method, AnnotationValue targetReturnTypeErased) {
+ String descriptor = AsmUtil.getDescriptor(method, name -> null);
+ String signature = AsmUtil.getSignature(method, name -> null);
+ List parameters = method.parameters();
+
+ MethodVisitor mv = super.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC,
+ method.name(),
+ descriptor,
+ signature,
+ null);
+ for (int i = 0; i < parameters.size(); i++) {
+ mv.visitParameter(method.parameterName(i), 0 /* modifiers */);
+ }
+ mv.visitCode();
+ for (PanacheMethodCustomizer customizer : methodCustomizers) {
+ customizer.customize(thisClass, method, mv);
+ }
+ mv.visitFieldInsn(Opcodes.GETSTATIC, thisClass.getInternalName(), "operations",
+ typeBundle.operations().descriptor());
+ injectModel(mv);
+ for (int i = 0; i < parameters.size(); i++) {
+ mv.visitIntInsn(Opcodes.ALOAD, i);
+ }
+ invokeOperation(mv, method, method.parameters());
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+
+ }
+
+ private void invokeOperation(MethodVisitor mv, MethodInfo method, List parameters) {
+ String operationDescriptor;
+ Function argMapper = type -> null;
+
+ AnnotationInstance bridge = method.annotation(PanacheEntityEnhancer.DOTNAME_GENERATE_BRIDGE);
+ AnnotationValue targetReturnTypeErased = bridge.value("targetReturnTypeErased");
+ boolean erased = targetReturnTypeErased != null && targetReturnTypeErased.asBoolean();
+
+ StringJoiner joiner = new StringJoiner("", "(", ")");
+ joiner.add(CLASS.descriptor());
+ for (Type parameter : parameters) {
+ joiner.add(getDescriptor(parameter, argMapper));
+ }
+
+ List names = asList(typeBundle.queryType().dotName().toString(),
+ typeBundle.updateType().dotName().toString());
+ operationDescriptor = joiner +
+ (erased || names.contains(method.returnType().name().toString())
+ ? OBJECT_SIGNATURE
+ : getDescriptor(method.returnType(), argMapper));
+
+ mv.visitMethodInsn(INVOKEVIRTUAL, typeBundle.operations().internalName(), method.name(),
+ operationDescriptor, false);
+ if (method.returnType().kind() != Type.Kind.PRIMITIVE) {
+ Type type = method.returnType();
+ String cast;
+ if (erased) {
+ cast = thisClass.getInternalName();
+ } else {
+ cast = type.name().toString().replace('.', '/');
+ }
+ mv.visitTypeInsn(CHECKCAST, cast);
+ }
+ mv.visitInsn(AsmUtil.getReturnInstruction(method.returnType()));
+ }
+
+ @Override
+ protected String getPanacheOperationsInternalName() {
+ return typeBundle.operations().internalName();
+ }
+
+ @Override
+ protected void generateAccessorSetField(MethodVisitor mv, EntityField field) {
+ mv.visitFieldInsn(Opcodes.PUTFIELD, thisClass.getInternalName(), field.name, field.descriptor);
+ }
+
+ @Override
+ protected void generateAccessorGetField(MethodVisitor mv, EntityField field) {
+ mv.visitFieldInsn(Opcodes.GETFIELD, thisClass.getInternalName(), field.name, field.descriptor);
+ }
+}
diff --git a/extensions/panache/mongodb-panache-common/pom.xml b/extensions/panache/mongodb-panache-common/pom.xml
new file mode 100644
index 0000000000000..9510146247760
--- /dev/null
+++ b/extensions/panache/mongodb-panache-common/pom.xml
@@ -0,0 +1,20 @@
+
+
+
+ quarkus-build-parent
+ io.quarkus
+ 999-SNAPSHOT
+ ../../../build-parent/pom.xml
+
+ 4.0.0
+
+ quarkus-mongodb-panache-common-parent
+ Quarkus - MongoDB with Panache - Common
+ pom
+
+ runtime
+ deployment
+
+
\ No newline at end of file
diff --git a/extensions/panache/mongodb-panache-common/runtime/pom.xml b/extensions/panache/mongodb-panache-common/runtime/pom.xml
new file mode 100644
index 0000000000000..b27a973cba4d0
--- /dev/null
+++ b/extensions/panache/mongodb-panache-common/runtime/pom.xml
@@ -0,0 +1,88 @@
+
+
+
+ quarkus-mongodb-panache-common-parent
+ io.quarkus
+ 999-SNAPSHOT
+ ../
+
+ 4.0.0
+
+ quarkus-mongodb-panache-common
+ Quarkus - MongoDB with Panache - Common Runtime
+
+
+
+ io.quarkus
+ quarkus-core
+
+
+ io.quarkus
+ quarkus-panache-common
+
+
+ io.quarkus
+ quarkus-mongodb-client
+
+
+ io.quarkus
+ quarkus-panacheql
+
+
+
+
+ io.quarkus
+ quarkus-jsonb
+ true
+
+
+
+
+ io.quarkus
+ quarkus-jackson
+ true
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+
+
+
+
+ maven-compiler-plugin
+
+ -proc:none
+
+
+
+ io.quarkus
+ quarkus-bootstrap-maven-plugin
+
+
+
+ org.jboss.jandex
+ jandex-maven-plugin
+
+
+ make-index
+
+ jandex
+
+
+
+
+
+
+
+
+
diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/MongoEntity.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/MongoEntity.java
similarity index 100%
rename from extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/MongoEntity.java
rename to extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/MongoEntity.java
diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/PanacheMongoRecorder.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/PanacheMongoRecorder.java
similarity index 100%
rename from extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/PanacheMongoRecorder.java
rename to extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/PanacheMongoRecorder.java
diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/PanacheUpdate.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/PanacheUpdate.java
similarity index 100%
rename from extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/PanacheUpdate.java
rename to extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/PanacheUpdate.java
diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/ProjectionFor.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/ProjectionFor.java
similarity index 92%
rename from extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/ProjectionFor.java
rename to extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/ProjectionFor.java
index 6f74d7aab4a48..260267b716cb5 100644
--- a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/ProjectionFor.java
+++ b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/ProjectionFor.java
@@ -10,5 +10,5 @@
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ProjectionFor {
- public Class> value();
+ Class> value();
}
diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/binder/CommonQueryBinder.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/binder/CommonQueryBinder.java
similarity index 100%
rename from extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/binder/CommonQueryBinder.java
rename to extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/binder/CommonQueryBinder.java
diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/binder/MongoParserVisitor.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/binder/MongoParserVisitor.java
similarity index 100%
rename from extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/binder/MongoParserVisitor.java
rename to extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/binder/MongoParserVisitor.java
diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/binder/NativeQueryBinder.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/binder/NativeQueryBinder.java
similarity index 100%
rename from extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/binder/NativeQueryBinder.java
rename to extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/binder/NativeQueryBinder.java
diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/binder/PanacheQlQueryBinder.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/binder/PanacheQlQueryBinder.java
similarity index 100%
rename from extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/binder/PanacheQlQueryBinder.java
rename to extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/binder/PanacheQlQueryBinder.java
diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/jackson/ObjectIdDeserializer.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/jackson/ObjectIdDeserializer.java
similarity index 100%
rename from extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/jackson/ObjectIdDeserializer.java
rename to extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/jackson/ObjectIdDeserializer.java
diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/jackson/ObjectIdSerializer.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/jackson/ObjectIdSerializer.java
similarity index 100%
rename from extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/jackson/ObjectIdSerializer.java
rename to extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/jackson/ObjectIdSerializer.java
diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/jsonb/ObjectIdDeserializer.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/jsonb/ObjectIdDeserializer.java
similarity index 100%
rename from extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/jsonb/ObjectIdDeserializer.java
rename to extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/jsonb/ObjectIdDeserializer.java
diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/jsonb/ObjectIdSerializer.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/jsonb/ObjectIdSerializer.java
similarity index 100%
rename from extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/jsonb/ObjectIdSerializer.java
rename to extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/jsonb/ObjectIdSerializer.java
diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/ReactivePanacheUpdate.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/ReactivePanacheUpdate.java
similarity index 100%
rename from extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/ReactivePanacheUpdate.java
rename to extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/ReactivePanacheUpdate.java
diff --git a/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/CommonReactivePanacheQueryImpl.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/CommonReactivePanacheQueryImpl.java
new file mode 100644
index 0000000000000..b767f36126dfa
--- /dev/null
+++ b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/CommonReactivePanacheQueryImpl.java
@@ -0,0 +1,238 @@
+package io.quarkus.mongodb.panache.reactive.runtime;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import org.bson.Document;
+import org.bson.conversions.Bson;
+
+import com.mongodb.client.model.Collation;
+
+import io.quarkus.mongodb.FindOptions;
+import io.quarkus.mongodb.panache.runtime.MongoPropertyUtil;
+import io.quarkus.mongodb.reactive.ReactiveMongoCollection;
+import io.quarkus.panache.common.Page;
+import io.quarkus.panache.common.Range;
+import io.quarkus.panache.common.exception.PanacheQueryException;
+import io.smallrye.mutiny.Multi;
+import io.smallrye.mutiny.Uni;
+
+public class CommonReactivePanacheQueryImpl {
+ private ReactiveMongoCollection collection;
+ private Bson mongoQuery;
+ private Bson sort;
+ private Bson projections;
+
+ private Page page;
+ private Uni count;
+
+ private Range range;
+
+ private Collation collation;
+
+ public CommonReactivePanacheQueryImpl(ReactiveMongoCollection extends Entity> collection, Bson mongoQuery, Bson sort) {
+ this.collection = collection;
+ this.mongoQuery = mongoQuery;
+ this.sort = sort;
+ }
+
+ private CommonReactivePanacheQueryImpl(CommonReactivePanacheQueryImpl previousQuery, Bson projections, Class> type) {
+ this.collection = previousQuery.collection.withDocumentClass(type);
+ this.mongoQuery = previousQuery.mongoQuery;
+ this.sort = previousQuery.sort;
+ this.projections = projections;
+ this.page = previousQuery.page;
+ this.count = previousQuery.count;
+ this.range = previousQuery.range;
+ this.collation = previousQuery.collation;
+ }
+
+ // Builder
+
+ public CommonReactivePanacheQueryImpl project(Class type) {
+ // collect field names from public fields and getters
+ Set fieldNames = MongoPropertyUtil.collectFields(type);
+
+ // create the projection document
+ Document projections = new Document();
+ for (String fieldName : fieldNames) {
+ projections.append(fieldName, 1);
+ }
+
+ return new CommonReactivePanacheQueryImpl(this, projections, type);
+ }
+
+ @SuppressWarnings("unchecked")
+ public CommonReactivePanacheQueryImpl page(Page page) {
+ this.page = page;
+ this.range = null; // reset the range to be able to switch from range to page
+ return (CommonReactivePanacheQueryImpl) this;
+ }
+
+ public CommonReactivePanacheQueryImpl page(int pageIndex, int pageSize) {
+ return page(Page.of(pageIndex, pageSize));
+ }
+
+ public CommonReactivePanacheQueryImpl nextPage() {
+ checkPagination();
+ return page(page.next());
+ }
+
+ public CommonReactivePanacheQueryImpl previousPage() {
+ checkPagination();
+ return page(page.previous());
+ }
+
+ public CommonReactivePanacheQueryImpl firstPage() {
+ checkPagination();
+ return page(page.first());
+ }
+
+ public Uni> lastPage() {
+ checkPagination();
+ Uni> map = pageCount().map(pageCount -> page(page.index(pageCount - 1)));
+ return map;
+ }
+
+ public Uni hasNextPage() {
+ checkPagination();
+ return pageCount().map(pageCount -> page.index < (pageCount - 1));
+ }
+
+ public boolean hasPreviousPage() {
+ checkPagination();
+ return page.index > 0;
+ }
+
+ public Uni pageCount() {
+ checkPagination();
+ return count().map(count -> {
+ if (count == 0)
+ return 1; // a single page of zero results
+ return (int) Math.ceil((double) count / (double) page.size);
+ });
+ }
+
+ public Page page() {
+ checkPagination();
+ return page;
+ }
+
+ private void checkPagination() {
+ if (page == null) {
+ throw new UnsupportedOperationException(
+ "Cannot call a page related method, "
+ + "call page(Page) or page(int, int) to initiate pagination first");
+ }
+ if (range != null) {
+ throw new UnsupportedOperationException("Cannot call a page related method in a ranged query, " +
+ "call page(Page) or page(int, int) to initiate pagination first");
+ }
+ }
+
+ public CommonReactivePanacheQueryImpl range(int startIndex, int lastIndex) {
+ this.range = Range.of(startIndex, lastIndex);
+ // reset the page to its default to be able to switch from page to range
+ this.page = null;
+ return (CommonReactivePanacheQueryImpl) this;
+ }
+
+ public CommonReactivePanacheQueryImpl withCollation(Collation collation) {
+ this.collation = collation;
+ return (CommonReactivePanacheQueryImpl) this;
+ }
+
+ // Results
+
+ @SuppressWarnings("unchecked")
+ public Uni count() {
+ if (count == null) {
+ count = collection.countDocuments(mongoQuery);
+ }
+ return count;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Uni> list() {
+ Multi results = stream();
+ return results.collectItems().asList();
+ }
+
+ @SuppressWarnings("unchecked")
+ public Multi stream() {
+ FindOptions options = buildOptions();
+ return mongoQuery == null ? collection.find(options) : collection.find(mongoQuery, options);
+ }
+
+ public Uni firstResult() {
+ Uni> optionalEntity = firstResultOptional();
+ return optionalEntity.map(optional -> optional.orElse(null));
+ }
+
+ public Uni> firstResultOptional() {
+ FindOptions options = buildOptions(1);
+ Multi results = mongoQuery == null ? collection.find(options) : collection.find(mongoQuery, options);
+ return results.collectItems().first().map(o -> Optional.ofNullable(o));
+ }
+
+ @SuppressWarnings("unchecked")
+ public Uni singleResult() {
+ FindOptions options = buildOptions(2);
+ Multi results = mongoQuery == null ? collection.find(options) : collection.find(mongoQuery, options);
+ return results.collectItems().asList().map(list -> {
+ if (list.size() != 1) {
+ throw new PanacheQueryException("There should be only one result");
+ } else {
+ return list.get(0);
+ }
+ });
+ }
+
+ public Uni> singleResultOptional() {
+ FindOptions options = buildOptions(2);
+ Multi results = mongoQuery == null ? collection.find(options) : collection.find(mongoQuery, options);
+ return results.collectItems().asList().map(list -> {
+ if (list.size() == 2) {
+ throw new PanacheQueryException("There should be no more than one result");
+ }
+ return list.isEmpty() ? Optional.empty() : Optional.of(list.get(0));
+ });
+ }
+
+ private FindOptions buildOptions() {
+ FindOptions options = new FindOptions();
+ options.sort(sort);
+ if (range != null) {
+ // range is 0 based, so we add 1 to the limit
+ options.skip(range.getStartIndex()).limit(range.getLastIndex() - range.getStartIndex() + 1);
+ } else if (page != null) {
+ options.skip(page.index * page.size).limit(page.size);
+ }
+ if (projections != null) {
+ options.projection(this.projections);
+ }
+ if (this.collation != null) {
+ options.collation(collation);
+ }
+ return options;
+ }
+
+ private FindOptions buildOptions(int maxResults) {
+ FindOptions options = new FindOptions();
+ options.sort(sort);
+ if (range != null) {
+ // range is 0 based, so we add 1 to the limit
+ options.skip(range.getStartIndex());
+ } else if (page != null) {
+ options.skip(page.index * page.size);
+ }
+ if (projections != null) {
+ options.projection(this.projections);
+ }
+ if (this.collation != null) {
+ options.collation(collation);
+ }
+ return options.limit(maxResults);
+ }
+}
diff --git a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/ReactiveMongoOperations.java b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/ReactiveMongoOperations.java
similarity index 67%
rename from extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/ReactiveMongoOperations.java
rename to extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/ReactiveMongoOperations.java
index b092143b53a49..38671bff4399f 100644
--- a/extensions/panache/mongodb-panache/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/ReactiveMongoOperations.java
+++ b/extensions/panache/mongodb-panache-common/runtime/src/main/java/io/quarkus/mongodb/panache/reactive/runtime/ReactiveMongoOperations.java
@@ -30,8 +30,6 @@
import io.quarkus.mongodb.panache.MongoEntity;
import io.quarkus.mongodb.panache.binder.NativeQueryBinder;
import io.quarkus.mongodb.panache.binder.PanacheQlQueryBinder;
-import io.quarkus.mongodb.panache.reactive.ReactivePanacheQuery;
-import io.quarkus.mongodb.panache.reactive.ReactivePanacheUpdate;
import io.quarkus.mongodb.reactive.ReactiveMongoClient;
import io.quarkus.mongodb.reactive.ReactiveMongoCollection;
import io.quarkus.mongodb.reactive.ReactiveMongoDatabase;
@@ -40,21 +38,27 @@
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
-public class ReactiveMongoOperations {
+@SuppressWarnings({ "rawtypes", "unchecked", "Convert2MethodRef" })
+public abstract class ReactiveMongoOperations {
+ public final String ID = "_id";
private static final Logger LOGGER = Logger.getLogger(ReactiveMongoOperations.class);
- public static final String ID = "_id";
-
private static final Map defaultDatabaseName = new ConcurrentHashMap<>();
- //
- // Instance methods
+ protected abstract QueryType createQuery(ReactiveMongoCollection collection, Document query, Document sortDoc);
+
+ protected abstract UpdateType createUpdate(ReactiveMongoCollection> collection, Class> entityClass,
+ Document docUpdate);
+
+ protected abstract Uni> list(QueryType query);
- public static Uni persist(Object entity) {
+ protected abstract Multi> stream(QueryType query);
+
+ public Uni persist(Object entity) {
ReactiveMongoCollection collection = mongoCollection(entity);
return persist(collection, entity);
}
- public static Uni persist(Iterable> entities) {
+ public Uni persist(Iterable> entities) {
return Uni.createFrom().deferred(() -> {
// not all iterables are re-traversal, so we traverse it once for copying inside a list
List