-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12158 from gytis/rest-data-mongo
Panache REST Data with MongoDB
- Loading branch information
Showing
25 changed files
with
1,194 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
extensions/panache/mongodb-rest-data-panache/deployment/pom.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<parent> | ||
<groupId>io.quarkus</groupId> | ||
<artifactId>quarkus-mongodb-rest-data-panache-parent</artifactId> | ||
<version>999-SNAPSHOT</version> | ||
<relativePath>../pom.xml</relativePath> | ||
</parent> | ||
|
||
<artifactId>quarkus-mongodb-rest-data-panache-deployment</artifactId> | ||
<name>Quarkus - MongoDB REST data with Panache - Deployment</name> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>io.quarkus</groupId> | ||
<artifactId>quarkus-mongodb-rest-data-panache</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>io.quarkus</groupId> | ||
<artifactId>quarkus-rest-data-panache-deployment</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>io.quarkus</groupId> | ||
<artifactId>quarkus-mongodb-panache-deployment</artifactId> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-compiler-plugin</artifactId> | ||
<configuration> | ||
<annotationProcessorPaths> | ||
<path> | ||
<groupId>io.quarkus</groupId> | ||
<artifactId>quarkus-extension-processor</artifactId> | ||
<version>${project.version}</version> | ||
</path> | ||
</annotationProcessorPaths> | ||
</configuration> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> |
65 changes: 65 additions & 0 deletions
65
...ain/java/io/quarkus/mongodb/rest/data/panache/deployment/EntityDataAccessImplementor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package io.quarkus.mongodb.rest.data.panache.deployment; | ||
|
||
import static io.quarkus.gizmo.MethodDescriptor.ofMethod; | ||
|
||
import java.util.List; | ||
|
||
import io.quarkus.gizmo.BytecodeCreator; | ||
import io.quarkus.gizmo.ResultHandle; | ||
import io.quarkus.mongodb.panache.PanacheMongoEntityBase; | ||
import io.quarkus.mongodb.panache.PanacheQuery; | ||
import io.quarkus.panache.common.Page; | ||
import io.quarkus.panache.common.Sort; | ||
import io.quarkus.rest.data.panache.deployment.DataAccessImplementor; | ||
|
||
final class EntityDataAccessImplementor implements DataAccessImplementor { | ||
|
||
private final String entityClassName; | ||
|
||
EntityDataAccessImplementor(String entityClassName) { | ||
this.entityClassName = entityClassName; | ||
} | ||
|
||
@Override | ||
public ResultHandle findById(BytecodeCreator creator, ResultHandle id) { | ||
return creator.invokeStaticMethod( | ||
ofMethod(entityClassName, "findById", PanacheMongoEntityBase.class, Object.class), id); | ||
} | ||
|
||
@Override | ||
public ResultHandle listAll(BytecodeCreator creator, ResultHandle sort) { | ||
return creator.invokeStaticMethod(ofMethod(entityClassName, "listAll", List.class, Sort.class), sort); | ||
} | ||
|
||
@Override | ||
public ResultHandle findAll(BytecodeCreator creator, ResultHandle page, ResultHandle sort) { | ||
ResultHandle query = creator.invokeStaticMethod( | ||
ofMethod(entityClassName, "findAll", PanacheQuery.class, Sort.class), sort); | ||
creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), query, page); | ||
return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "list", List.class), query); | ||
} | ||
|
||
@Override | ||
public ResultHandle persist(BytecodeCreator creator, ResultHandle entity) { | ||
creator.invokeVirtualMethod(ofMethod(entityClassName, "persist", void.class), entity); | ||
return entity; | ||
} | ||
|
||
@Override | ||
public ResultHandle update(BytecodeCreator creator, ResultHandle entity) { | ||
creator.invokeVirtualMethod(ofMethod(entityClassName, "persistOrUpdate", void.class), entity); | ||
return entity; | ||
} | ||
|
||
@Override | ||
public ResultHandle deleteById(BytecodeCreator creator, ResultHandle id) { | ||
return creator.invokeStaticMethod(ofMethod(entityClassName, "deleteById", boolean.class, Object.class), id); | ||
} | ||
|
||
@Override | ||
public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page) { | ||
ResultHandle query = creator.invokeStaticMethod(ofMethod(entityClassName, "findAll", PanacheQuery.class)); | ||
creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), query, page); | ||
return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", int.class), query); | ||
} | ||
} |
121 changes: 121 additions & 0 deletions
121
.../main/java/io/quarkus/mongodb/rest/data/panache/deployment/MongoPanacheRestProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package io.quarkus.mongodb.rest.data.panache.deployment; | ||
|
||
import static io.quarkus.deployment.Feature.MONGODB_REST_DATA_PANACHE; | ||
|
||
import java.lang.reflect.Modifier; | ||
import java.util.List; | ||
|
||
import org.jboss.jandex.ClassInfo; | ||
import org.jboss.jandex.DotName; | ||
import org.jboss.jandex.IndexView; | ||
import org.jboss.jandex.Type; | ||
import org.objectweb.asm.ClassVisitor; | ||
import org.objectweb.asm.FieldVisitor; | ||
|
||
import io.quarkus.arc.deployment.UnremovableBeanBuildItem; | ||
import io.quarkus.deployment.annotations.BuildProducer; | ||
import io.quarkus.deployment.annotations.BuildStep; | ||
import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; | ||
import io.quarkus.deployment.builditem.CombinedIndexBuildItem; | ||
import io.quarkus.deployment.builditem.FeatureBuildItem; | ||
import io.quarkus.gizmo.Gizmo; | ||
import io.quarkus.mongodb.rest.data.panache.PanacheMongoEntityResource; | ||
import io.quarkus.mongodb.rest.data.panache.PanacheMongoRepositoryResource; | ||
import io.quarkus.rest.data.panache.deployment.DataAccessImplementor; | ||
import io.quarkus.rest.data.panache.deployment.RestDataEntityInfo; | ||
import io.quarkus.rest.data.panache.deployment.RestDataResourceBuildItem; | ||
import io.quarkus.rest.data.panache.deployment.RestDataResourceInfo; | ||
|
||
class MongoPanacheRestProcessor { | ||
|
||
private static final DotName PANACHE_MONGO_ENTITY_RESOURCE_INTERFACE = DotName | ||
.createSimple(PanacheMongoEntityResource.class.getName()); | ||
|
||
private static final DotName PANACHE_MONGO_REPOSITORY_RESOURCE_INTERFACE = DotName | ||
.createSimple(PanacheMongoRepositoryResource.class.getName()); | ||
|
||
@BuildStep | ||
FeatureBuildItem feature() { | ||
return new FeatureBuildItem(MONGODB_REST_DATA_PANACHE); | ||
} | ||
|
||
@BuildStep | ||
void findEntityResources(CombinedIndexBuildItem index, BuildProducer<RestDataResourceBuildItem> resourcesProducer) { | ||
RestDataEntityInfoProvider entityInfoProvider = new RestDataEntityInfoProvider(index.getIndex()); | ||
for (ClassInfo classInfo : index.getIndex().getKnownDirectImplementors(PANACHE_MONGO_ENTITY_RESOURCE_INTERFACE)) { | ||
validateResource(index.getIndex(), classInfo); | ||
List<Type> generics = getGenericTypes(classInfo); | ||
String entityClassName = generics.get(0).toString(); | ||
String idClassName = generics.get(1).toString(); | ||
RestDataEntityInfo entityInfo = entityInfoProvider.get(entityClassName, idClassName); | ||
DataAccessImplementor dataAccessImplementor = new EntityDataAccessImplementor(entityClassName); | ||
RestDataResourceInfo resourceInfo = new RestDataResourceInfo(classInfo.toString(), entityInfo, | ||
dataAccessImplementor); | ||
resourcesProducer.produce(new RestDataResourceBuildItem(resourceInfo)); | ||
} | ||
} | ||
|
||
@BuildStep | ||
void findRepositoryResources(CombinedIndexBuildItem index, BuildProducer<RestDataResourceBuildItem> resourcesProducer, | ||
BuildProducer<UnremovableBeanBuildItem> unremovableBeansProducer, | ||
BuildProducer<BytecodeTransformerBuildItem> bytecodeTransformersProducer) { | ||
RestDataEntityInfoProvider entityInfoProvider = new RestDataEntityInfoProvider(index.getIndex()); | ||
for (ClassInfo classInfo : index.getIndex().getKnownDirectImplementors(PANACHE_MONGO_REPOSITORY_RESOURCE_INTERFACE)) { | ||
validateResource(index.getIndex(), classInfo); | ||
List<Type> generics = getGenericTypes(classInfo); | ||
String repositoryClassName = generics.get(0).toString(); | ||
String entityClassName = generics.get(1).toString(); | ||
String idClassName = generics.get(2).toString(); | ||
RestDataEntityInfo entityInfo = entityInfoProvider.get(entityClassName, idClassName); | ||
DataAccessImplementor dataAccessImplementor = new RepositoryDataAccessImplementor(repositoryClassName); | ||
RestDataResourceInfo resourceInfo = new RestDataResourceInfo(classInfo.toString(), entityInfo, | ||
dataAccessImplementor); | ||
resourcesProducer.produce(new RestDataResourceBuildItem(resourceInfo)); | ||
unremovableBeansProducer.produce( | ||
new UnremovableBeanBuildItem(new UnremovableBeanBuildItem.BeanClassNameExclusion(repositoryClassName))); | ||
bytecodeTransformersProducer | ||
.produce(getEntityIdAnnotationTransformer(entityClassName, entityInfo.getIdField().name())); | ||
} | ||
} | ||
|
||
private void validateResource(IndexView index, ClassInfo classInfo) { | ||
if (!Modifier.isInterface(classInfo.flags())) { | ||
throw new RuntimeException(classInfo.name() + " has to be an interface"); | ||
} | ||
|
||
if (classInfo.interfaceNames().size() > 1) { | ||
throw new RuntimeException(classInfo.name() + " should only extend REST Data Panache interface"); | ||
} | ||
|
||
if (!index.getKnownDirectImplementors(classInfo.name()).isEmpty()) { | ||
throw new RuntimeException(classInfo.name() + " should not be extended or implemented"); | ||
} | ||
} | ||
|
||
private List<Type> getGenericTypes(ClassInfo classInfo) { | ||
return classInfo.interfaceTypes() | ||
.stream() | ||
.findFirst() | ||
.orElseThrow(() -> new RuntimeException(classInfo.toString() + " does not have generic types")) | ||
.asParameterizedType() | ||
.arguments(); | ||
} | ||
|
||
/** | ||
* Annotate Mongo entity ID fields with a RESTEasy links annotation. | ||
* Otherwise RESTEasy will not be able to generate links that use ID as path parameter. | ||
*/ | ||
private BytecodeTransformerBuildItem getEntityIdAnnotationTransformer(String entityClassName, String idFieldName) { | ||
return new BytecodeTransformerBuildItem(entityClassName, | ||
(className, classVisitor) -> new ClassVisitor(Gizmo.ASM_API_VERSION, classVisitor) { | ||
@Override | ||
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { | ||
FieldVisitor fieldVisitor = super.visitField(access, name, descriptor, signature, value); | ||
if (name.equals(idFieldName)) { | ||
fieldVisitor.visitAnnotation("Lorg/jboss/resteasy/links/ResourceID;", true).visitEnd(); | ||
} | ||
return fieldVisitor; | ||
} | ||
}); | ||
} | ||
} |
90 changes: 90 additions & 0 deletions
90
...java/io/quarkus/mongodb/rest/data/panache/deployment/RepositoryDataAccessImplementor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package io.quarkus.mongodb.rest.data.panache.deployment; | ||
|
||
import static io.quarkus.gizmo.MethodDescriptor.ofMethod; | ||
|
||
import java.lang.annotation.Annotation; | ||
import java.util.List; | ||
|
||
import io.quarkus.arc.Arc; | ||
import io.quarkus.arc.ArcContainer; | ||
import io.quarkus.arc.InstanceHandle; | ||
import io.quarkus.gizmo.BytecodeCreator; | ||
import io.quarkus.gizmo.ResultHandle; | ||
import io.quarkus.mongodb.panache.PanacheMongoRepositoryBase; | ||
import io.quarkus.mongodb.panache.PanacheQuery; | ||
import io.quarkus.panache.common.Page; | ||
import io.quarkus.panache.common.Sort; | ||
import io.quarkus.rest.data.panache.deployment.DataAccessImplementor; | ||
|
||
final class RepositoryDataAccessImplementor implements DataAccessImplementor { | ||
|
||
private final String repositoryClassName; | ||
|
||
RepositoryDataAccessImplementor(String repositoryClassName) { | ||
this.repositoryClassName = repositoryClassName; | ||
} | ||
|
||
@Override | ||
public ResultHandle findById(BytecodeCreator creator, ResultHandle id) { | ||
return creator.invokeInterfaceMethod(ofMethod(PanacheMongoRepositoryBase.class, "findById", Object.class, Object.class), | ||
getRepositoryInstance(creator), id); | ||
} | ||
|
||
@Override | ||
public ResultHandle listAll(BytecodeCreator creator, ResultHandle sort) { | ||
return creator.invokeInterfaceMethod(ofMethod(PanacheMongoRepositoryBase.class, "listAll", List.class, Sort.class), | ||
getRepositoryInstance(creator), sort); | ||
} | ||
|
||
@Override | ||
public ResultHandle findAll(BytecodeCreator creator, ResultHandle page, ResultHandle sort) { | ||
ResultHandle query = creator.invokeInterfaceMethod( | ||
ofMethod(PanacheMongoRepositoryBase.class, "findAll", PanacheQuery.class, Sort.class), | ||
getRepositoryInstance(creator), sort); | ||
creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), query, page); | ||
return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "list", List.class), query); | ||
} | ||
|
||
@Override | ||
public ResultHandle persist(BytecodeCreator creator, ResultHandle entity) { | ||
creator.invokeInterfaceMethod(ofMethod(PanacheMongoRepositoryBase.class, "persist", void.class, Object.class), | ||
getRepositoryInstance(creator), entity); | ||
return entity; | ||
} | ||
|
||
@Override | ||
public ResultHandle update(BytecodeCreator creator, ResultHandle entity) { | ||
creator.invokeInterfaceMethod(ofMethod(PanacheMongoRepositoryBase.class, "persistOrUpdate", void.class, Object.class), | ||
getRepositoryInstance(creator), entity); | ||
return entity; | ||
} | ||
|
||
@Override | ||
public ResultHandle deleteById(BytecodeCreator creator, ResultHandle id) { | ||
return creator.invokeInterfaceMethod( | ||
ofMethod(PanacheMongoRepositoryBase.class, "deleteById", boolean.class, Object.class), | ||
getRepositoryInstance(creator), id); | ||
} | ||
|
||
@Override | ||
public ResultHandle pageCount(BytecodeCreator creator, ResultHandle page) { | ||
ResultHandle query = creator.invokeInterfaceMethod( | ||
ofMethod(PanacheMongoRepositoryBase.class, "findAll", PanacheQuery.class), getRepositoryInstance(creator)); | ||
creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "page", PanacheQuery.class, Page.class), query, page); | ||
return creator.invokeInterfaceMethod(ofMethod(PanacheQuery.class, "pageCount", int.class), query); | ||
} | ||
|
||
private ResultHandle getRepositoryInstance(BytecodeCreator creator) { | ||
ResultHandle arcContainer = creator.invokeStaticMethod(ofMethod(Arc.class, "container", ArcContainer.class)); | ||
ResultHandle instanceHandle = creator.invokeInterfaceMethod( | ||
ofMethod(ArcContainer.class, "instance", InstanceHandle.class, Class.class, Annotation[].class), | ||
arcContainer, creator.loadClass(repositoryClassName), creator.newArray(Annotation.class, 0)); | ||
ResultHandle instance = creator.invokeInterfaceMethod( | ||
ofMethod(InstanceHandle.class, "get", Object.class), instanceHandle); | ||
|
||
creator.ifNull(instance).trueBranch() | ||
.throwException(RuntimeException.class, repositoryClassName + " instance was not found"); | ||
|
||
return instance; | ||
} | ||
} |
Oops, something went wrong.