All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.quarkus.funqy.deployment.FunctionScannerBuildStep Maven / Gradle / Ivy
package io.quarkus.funqy.deployment;
import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT;
import static io.quarkus.funqy.deployment.ReflectionRegistrationUtil.*;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
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.arc.deployment.AnnotationsTransformerBuildItem;
import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem;
import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
import io.quarkus.arc.processor.AnnotationsTransformer;
import io.quarkus.arc.processor.BuiltinScope;
import io.quarkus.arc.processor.DotNames;
import io.quarkus.arc.processor.Transformation;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem;
import io.quarkus.deployment.recording.RecorderContext;
import io.quarkus.funqy.Context;
import io.quarkus.funqy.Funq;
import io.quarkus.funqy.runtime.FunctionRecorder;
import io.quarkus.gizmo.Gizmo;
public class FunctionScannerBuildStep {
public static final DotName FUNQ = DotName.createSimple(Funq.class.getName());
public static final DotName CONTEXT = DotName.createSimple(Context.class.getName());
@BuildStep
public void scanFunctions(BeanArchiveIndexBuildItem beanArchiveIndexBuildItem,
BuildProducer transformers,
BuildProducer annotationsTransformer,
BuildProducer unremovableBeans,
BuildProducer reflectiveClass,
BuildProducer reflectiveHierarchy,
BuildProducer functions) {
IndexView index = beanArchiveIndexBuildItem.getIndex();
Collection funqs = index.getAnnotations(FUNQ);
Set classes = new HashSet<>();
Set classNames = new HashSet<>();
for (AnnotationInstance funqMethod : funqs) {
MethodInfo method = funqMethod.target().asMethod();
String className = method.declaringClass().name().toString();
classNames.add(className);
classes.add(method.declaringClass());
String methodName = method.name();
if (!Modifier.isPublic(method.flags())) {
throw new RuntimeException(
String.format("Method '%s' annotated with '@Funq' declared in the class '%s' is not public.",
methodName, className));
}
String functionName = null;
if (funqMethod.value() != null) {
functionName = funqMethod.value().asString();
}
if (functionName != null && functionName.isEmpty())
functionName = null;
functions.produce(new FunctionBuildItem(className, methodName, method.descriptor(), functionName));
String source = FunctionScannerBuildStep.class.getSimpleName() + " > " + method.declaringClass() + "[" + method
+ "]";
Type returnType = method.returnType();
if (returnType.kind() != Type.Kind.VOID) {
reflectiveHierarchy.produce(ReflectiveHierarchyBuildItem
.builder(returnType)
.index(index)
.ignoreTypePredicate(IGNORE_TYPE_FOR_REFLECTION_PREDICATE)
.ignoreFieldPredicate(IGNORE_FIELD_FOR_REFLECTION_PREDICATE)
.ignoreMethodPredicate(IGNORE_METHOD_FOR_REFLECTION_PREDICATE)
.source(source)
.build());
}
for (short i = 0; i < method.parametersCount(); i++) {
Type parameterType = method.parameterType(i);
if (!hasAnnotation(method, i, CONTEXT)) {
reflectiveHierarchy.produce(ReflectiveHierarchyBuildItem
.builder(parameterType)
.index(index)
.ignoreTypePredicate(IGNORE_TYPE_FOR_REFLECTION_PREDICATE)
.ignoreFieldPredicate(IGNORE_FIELD_FOR_REFLECTION_PREDICATE)
.ignoreMethodPredicate(IGNORE_METHOD_FOR_REFLECTION_PREDICATE)
.source(source)
.build());
}
}
}
Set withoutDefaultCtor = new HashSet<>();
for (ClassInfo clazz : classes) {
reflectiveClass.produce(ReflectiveClassBuildItem.builder(clazz.name().toString())
.reason(getClass().getName())
.methods().fields().build());
if (!clazz.hasNoArgsConstructor()) {
withoutDefaultCtor.add(clazz);
}
}
unremovableBeans.produce(new UnremovableBeanBuildItem(b -> classNames.contains(b.getBeanClass().toString())));
generateDefaultConstructors(transformers, withoutDefaultCtor);
// we need to use an annotation transformer here instead of an AdditionalBeanBuildItem because
// the use of the latter along with the BeanArchiveIndexBuildItem results in build cycles
annotationsTransformer.produce(new AnnotationsTransformerBuildItem(new AnnotationsTransformer() {
@Override
public boolean appliesTo(AnnotationTarget.Kind kind) {
return kind == AnnotationTarget.Kind.CLASS;
}
@Override
public void transform(TransformationContext transformationContext) {
ClassInfo clazz = transformationContext.getTarget().asClass();
if (!classes.contains(clazz))
return;
if (BuiltinScope.isDeclaredOn(clazz)) {
// nothing to do as the presence of a scope will automatically qualify the class as a bean
return;
}
Transformation transformation = transformationContext.transform();
transformation.add(BuiltinScope.DEPENDENT.getName());
if (clazz.declaredAnnotation(DotNames.TYPED) == null) {
// Add @Typed(MySubresource.class)
transformation.add(createTypedAnnotationInstance(clazz));
}
transformation.done();
}
}));
}
private static boolean hasAnnotation(MethodInfo method, short paramPosition, DotName annotation) {
for (AnnotationInstance annotationInstance : method.annotations()) {
AnnotationTarget target = annotationInstance.target();
if (target != null && target.kind() == AnnotationTarget.Kind.METHOD_PARAMETER
&& target.asMethodParameter().position() == paramPosition
&& annotationInstance.name().equals(annotation)) {
return true;
}
}
return false;
}
@BuildStep
@Record(STATIC_INIT)
public FunctionInitializedBuildItem staticInit(FunctionRecorder recorder, List functions,
RecorderContext context) {
if (functions == null || functions.isEmpty())
return null;
recorder.init();
for (FunctionBuildItem function : functions) {
if (function.getFunctionName() == null) {
recorder.register(context.classProxy(function.getClassName()), function.getMethodName(),
function.getDescriptor());
} else {
recorder.register(context.classProxy(function.getClassName()), function.getMethodName(),
function.getDescriptor(), function.getFunctionName());
}
}
return FunctionInitializedBuildItem.SINGLETON;
}
private static void generateDefaultConstructors(BuildProducer transformers,
Set withoutDefaultCtor) {
for (ClassInfo classInfo : withoutDefaultCtor) {
// don't generate constructor for normal scoped beans as the Quarkus Arc does that for us
final BuiltinScope scope = BuiltinScope.from(classInfo);
if (scope != null && scope.getInfo().isNormal()) {
continue;
}
// keep it super simple - only generate default constructor is the object is a direct descendant of Object
if (!(classInfo.superClassType() != null && classInfo.superClassType().name().equals(DotNames.OBJECT))) {
return;
}
final String name = classInfo.name().toString();
transformers
.produce(new BytecodeTransformerBuildItem(name, new BiFunction() {
@Override
public ClassVisitor apply(String className, ClassVisitor classVisitor) {
ClassVisitor cv = new ClassVisitor(Gizmo.ASM_API_VERSION, classVisitor) {
@Override
public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
MethodVisitor ctor = visitMethod(Modifier.PUBLIC | Opcodes.ACC_SYNTHETIC, "", "()V",
null,
null);
ctor.visitCode();
ctor.visitVarInsn(Opcodes.ALOAD, 0);
ctor.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V", false);
ctor.visitInsn(Opcodes.RETURN);
ctor.visitMaxs(1, 1);
ctor.visitEnd();
}
};
return cv;
}
}));
}
}
private AnnotationInstance createTypedAnnotationInstance(ClassInfo clazz) {
return AnnotationInstance.create(DotNames.TYPED, clazz,
new AnnotationValue[] { AnnotationValue.createArrayValue("value",
new AnnotationValue[] { AnnotationValue.createClassValue("value",
Type.create(clazz.name(), org.jboss.jandex.Type.Kind.CLASS)) }) });
}
}