Skip to content

Commit

Permalink
Merge pull request #11043 from evanchooly/mongo-panache-kotlin
Browse files Browse the repository at this point in the history
implement kotlin support for mongodb panache
  • Loading branch information
evanchooly authored Aug 17, 2020
2 parents 72ecaa8 + 0adc1d3 commit bdcb548
Show file tree
Hide file tree
Showing 150 changed files with 11,669 additions and 2,261 deletions.
15 changes: 15 additions & 0 deletions bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,11 @@
<artifactId>quarkus-panache-common-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-panache-common-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-panache-mock</artifactId>
Expand Down Expand Up @@ -709,6 +714,16 @@
<artifactId>quarkus-mongodb-panache-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-panache-kotlin</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-panache-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-search-elasticsearch</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public enum Capability {
JWT,
TIKA,
MONGODB_PANACHE,
MONGODB_PANACHE_KOTLIN,
FLYWAY,
LIQUIBASE,
SECURITY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -253,6 +254,9 @@ public static Consumer<BuildChainBuilder> 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<BuildChainBuilder> chainConfig = Functions.discardingConsumer();
if (Modifier.isAbstract(clazz.getModifiers())) {
return chainConfig;
}
// this is the step configuration that applies to all steps on this class
Consumer<BuildStepBuilder> stepConfig = Functions.discardingConsumer();
// this is the build step instance setup that applies to all steps on this class
Expand Down Expand Up @@ -482,7 +486,7 @@ public static Consumer<BuildChainBuilder> loadStepsFrom(Class<?> clazz, BuildTim
}

// now iterate the methods
final Method[] methods = clazz.getDeclaredMethods();
final List<Method> methods = getMethods(clazz);
for (Method method : methods) {
final int mods = method.getModifiers();
if (Modifier.isStatic(mods)) {
Expand Down Expand Up @@ -963,6 +967,15 @@ public String toString() {
return chainConfig;
}

protected static List<Method> getMethods(Class<?> clazz) {
List<Method> 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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public enum Feature {
MAILER,
MONGODB_CLIENT,
MONGODB_PANACHE,
MONGODB_PANACHE_KOTLIN,
MUTINY,
NARAYANA_JTA,
NARAYANA_STM,
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -20,6 +33,36 @@
*/
public class AsmUtil {

public static final List<org.objectweb.asm.Type> PRIMITIVES = asList(
VOID_TYPE,
BOOLEAN_TYPE,
CHAR_TYPE,
BYTE_TYPE,
SHORT_TYPE,
INT_TYPE,
FLOAT_TYPE,
LONG_TYPE);
public static final List<org.objectweb.asm.Type> 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<org.objectweb.asm.Type, org.objectweb.asm.Type> 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:
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ protected String getConnectionString() {
}

private static void initializeReplicaSet(final List<IMongodConfig> 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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class KotlinPanacheCompanionEnhancer extends PanacheEntityEnhancer<Metamo
private static final DotName DOTNAME_TRANSIENT = DotName.createSimple(Transient.class.getName());

public KotlinPanacheCompanionEnhancer(IndexView index, List<PanacheMethodCustomizer> methodCustomizers) {
super(index, KotlinPanacheResourceProcessor.PANACHE_ENTITY_BASE_DOTNAME, methodCustomizers);
super(index, methodCustomizers);
modelInfo = new MetamodelInfo<>();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<EntityField> {
class KotlinPanacheEntityClassVisitor extends PanacheEntityClassVisitor<EntityField> {

private String entityBinaryType;
private org.objectweb.asm.Type entityType;

public KotlinPanacheEntityClassVisitor(String className, ClassVisitor outputClassVisitor,
MetamodelInfo<EntityModel<EntityField>> modelInfo,
Expand All @@ -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
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -24,16 +25,15 @@ public class KotlinPanacheEntityEnhancer extends PanacheEntityEnhancer<Metamodel
private final static String JPA_OPERATIONS_NAME = JpaOperations.class.getName();
public final static String JPA_OPERATIONS_BINARY_NAME = JPA_OPERATIONS_NAME.replace('.', '/');

private static final DotName DOTNAME_TRANSIENT = DotName.createSimple(Transient.class.getName());

public KotlinPanacheEntityEnhancer(IndexView index, List<PanacheMethodCustomizer> 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);
}

Expand All @@ -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())));
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 + ";";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit bdcb548

Please sign in to comment.