Search in sources :

Example 1 with SimpleBuildItem

use of io.quarkus.builder.item.SimpleBuildItem in project quarkus by quarkusio.

the class ExtensionLoader method loadStepsFromClass.

/**
 * Load all the build steps from the given class.
 *
 * @param clazz the class to load from (must not be {@code null})
 * @param readResult the build time configuration read result (must not be {@code null})
 * @param runTimeProxies the map of run time proxy objects to populate for recorders (must not be {@code null})
 * @return a consumer which adds the steps to the given chain builder
 */
private static Consumer<BuildChainBuilder> loadStepsFromClass(Class<?> clazz, BuildTimeConfigurationReader.ReadResult readResult, Map<Class<?>, Object> runTimeProxies, BooleanSupplierFactoryBuildItem supplierFactory) {
    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
    BiConsumer<BuildContext, Object> stepInstanceSetup = Functions.discardingBiConsumer();
    if (constructors.length != 1) {
        throw reportError(clazz, "Build step classes must have exactly one constructor");
    }
    EnumSet<ConfigPhase> consumingConfigPhases = EnumSet.noneOf(ConfigPhase.class);
    final Constructor<?> constructor = constructors[0];
    if (!(Modifier.isPublic(constructor.getModifiers())))
        constructor.setAccessible(true);
    final Parameter[] ctorParameters = constructor.getParameters();
    final List<Function<BuildContext, Object>> ctorParamFns;
    if (ctorParameters.length == 0) {
        ctorParamFns = Collections.emptyList();
    } else {
        ctorParamFns = new ArrayList<>(ctorParameters.length);
        for (Parameter parameter : ctorParameters) {
            Type parameterType = parameter.getParameterizedType();
            final Class<?> parameterClass = parameter.getType();
            final boolean weak = parameter.isAnnotationPresent(Weak.class);
            final boolean overridable = parameter.isAnnotationPresent(Overridable.class);
            if (rawTypeExtends(parameterType, SimpleBuildItem.class)) {
                final Class<? extends SimpleBuildItem> buildItemClass = rawTypeOf(parameterType).asSubclass(SimpleBuildItem.class);
                stepConfig = stepConfig.andThen(bsb -> bsb.consumes(buildItemClass));
                ctorParamFns.add(bc -> bc.consume(buildItemClass));
            } else if (isListOf(parameterType, MultiBuildItem.class)) {
                final Class<? extends MultiBuildItem> buildItemClass = rawTypeOfParameter(parameterType, 0).asSubclass(MultiBuildItem.class);
                stepConfig = stepConfig.andThen(bsb -> bsb.consumes(buildItemClass));
                ctorParamFns.add(bc -> bc.consumeMulti(buildItemClass));
            } else if (isConsumerOf(parameterType, BuildItem.class)) {
                deprecatedProducer(parameter);
                final Class<? extends BuildItem> buildItemClass = rawTypeOfParameter(parameterType, 0).asSubclass(BuildItem.class);
                if (overridable) {
                    if (weak) {
                        stepConfig = stepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.OVERRIDABLE, ProduceFlag.WEAK));
                    } else {
                        stepConfig = stepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.OVERRIDABLE));
                    }
                } else {
                    if (weak) {
                        stepConfig = stepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.WEAK));
                    } else {
                        stepConfig = stepConfig.andThen(bsb -> bsb.produces(buildItemClass));
                    }
                }
                ctorParamFns.add(bc -> (Consumer<? extends BuildItem>) bc::produce);
            } else if (isBuildProducerOf(parameterType, BuildItem.class)) {
                deprecatedProducer(parameter);
                final Class<? extends BuildItem> buildItemClass = rawTypeOfParameter(parameterType, 0).asSubclass(BuildItem.class);
                if (overridable) {
                    if (weak) {
                        stepConfig = stepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.OVERRIDABLE, ProduceFlag.WEAK));
                    } else {
                        stepConfig = stepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.OVERRIDABLE));
                    }
                } else {
                    if (weak) {
                        stepConfig = stepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.WEAK));
                    } else {
                        stepConfig = stepConfig.andThen(bsb -> bsb.produces(buildItemClass));
                    }
                }
                ctorParamFns.add(bc -> (BuildProducer<? extends BuildItem>) bc::produce);
            } else if (isOptionalOf(parameterType, SimpleBuildItem.class)) {
                final Class<? extends SimpleBuildItem> buildItemClass = rawTypeOfParameter(parameterType, 0).asSubclass(SimpleBuildItem.class);
                stepConfig = stepConfig.andThen(bsb -> bsb.consumes(buildItemClass, ConsumeFlags.of(ConsumeFlag.OPTIONAL)));
                ctorParamFns.add(bc -> Optional.ofNullable(bc.consume(buildItemClass)));
            } else if (isSupplierOf(parameterType, SimpleBuildItem.class)) {
                final Class<? extends SimpleBuildItem> buildItemClass = rawTypeOfParameter(parameterType, 0).asSubclass(SimpleBuildItem.class);
                stepConfig = stepConfig.andThen(bsb -> bsb.consumes(buildItemClass));
                ctorParamFns.add(bc -> (Supplier<? extends SimpleBuildItem>) () -> bc.consume(buildItemClass));
            } else if (isSupplierOfOptionalOf(parameterType, SimpleBuildItem.class)) {
                final Class<? extends SimpleBuildItem> buildItemClass = rawTypeOfParameter(rawTypeOfParameter(parameterType, 0), 0).asSubclass(SimpleBuildItem.class);
                stepConfig = stepConfig.andThen(bsb -> bsb.consumes(buildItemClass, ConsumeFlags.of(ConsumeFlag.OPTIONAL)));
                ctorParamFns.add(bc -> (Supplier<Optional<? extends SimpleBuildItem>>) () -> Optional.ofNullable(bc.consume(buildItemClass)));
            } else if (rawTypeOf(parameterType) == Executor.class) {
                ctorParamFns.add(BuildContext::getExecutor);
            } else if (parameterClass.isAnnotationPresent(ConfigRoot.class)) {
                final ConfigRoot annotation = parameterClass.getAnnotation(ConfigRoot.class);
                final ConfigPhase phase = annotation.phase();
                consumingConfigPhases.add(phase);
                if (phase.isAvailableAtBuild()) {
                    ctorParamFns.add(bc -> bc.consume(ConfigurationBuildItem.class).getReadResult().requireObjectForClass(parameterClass));
                    if (phase == ConfigPhase.BUILD_AND_RUN_TIME_FIXED) {
                        runTimeProxies.computeIfAbsent(parameterClass, readResult::requireObjectForClass);
                    }
                } else if (phase.isReadAtMain()) {
                    throw reportError(parameter, phase + " configuration cannot be consumed here");
                } else {
                    throw reportError(parameterClass, "Unknown value for ConfigPhase");
                }
            } else if (isRecorder(parameterClass)) {
                throw reportError(parameter, "Bytecode recorders disallowed on constructor parameters");
            } else {
                throw reportError(parameter, "Unsupported constructor parameter type " + parameterType);
            }
        }
    }
    // index fields
    final Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        final int mods = field.getModifiers();
        if (Modifier.isStatic(mods)) {
            // ignore static fields
            continue;
        }
        if (Modifier.isFinal(mods)) {
            // ignore final fields
            continue;
        }
        if (!Modifier.isPublic(mods) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) {
            field.setAccessible(true);
        }
        // next, determine the type
        final Type fieldType = field.getGenericType();
        final Class<?> fieldClass = field.getType();
        final boolean weak = field.isAnnotationPresent(Weak.class);
        final boolean overridable = field.isAnnotationPresent(Overridable.class);
        if (rawTypeExtends(fieldType, SimpleBuildItem.class)) {
            final Class<? extends SimpleBuildItem> buildItemClass = rawTypeOf(fieldType).asSubclass(SimpleBuildItem.class);
            stepConfig = stepConfig.andThen(bsb -> bsb.consumes(buildItemClass));
            stepInstanceSetup = stepInstanceSetup.andThen((bc, o) -> ReflectUtil.setFieldVal(field, o, bc.consume(buildItemClass)));
        } else if (isListOf(fieldType, MultiBuildItem.class)) {
            final Class<? extends MultiBuildItem> buildItemClass = rawTypeOfParameter(fieldType, 0).asSubclass(MultiBuildItem.class);
            stepConfig = stepConfig.andThen(bsb -> bsb.consumes(buildItemClass));
            stepInstanceSetup = stepInstanceSetup.andThen((bc, o) -> ReflectUtil.setFieldVal(field, o, bc.consumeMulti(buildItemClass)));
        } else if (isConsumerOf(fieldType, BuildItem.class)) {
            deprecatedProducer(field);
            final Class<? extends BuildItem> buildItemClass = rawTypeOfParameter(fieldType, 0).asSubclass(BuildItem.class);
            if (overridable) {
                if (weak) {
                    stepConfig = stepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.OVERRIDABLE, ProduceFlag.WEAK));
                } else {
                    stepConfig = stepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.OVERRIDABLE));
                }
            } else {
                if (weak) {
                    stepConfig = stepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.WEAK));
                } else {
                    stepConfig = stepConfig.andThen(bsb -> bsb.produces(buildItemClass));
                }
            }
            stepInstanceSetup = stepInstanceSetup.andThen((bc, o) -> ReflectUtil.setFieldVal(field, o, (Consumer<? extends BuildItem>) bc::produce));
        } else if (isBuildProducerOf(fieldType, BuildItem.class)) {
            deprecatedProducer(field);
            final Class<? extends BuildItem> buildItemClass = rawTypeOfParameter(fieldType, 0).asSubclass(BuildItem.class);
            if (overridable) {
                if (weak) {
                    stepConfig = stepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.OVERRIDABLE, ProduceFlag.WEAK));
                } else {
                    stepConfig = stepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.OVERRIDABLE));
                }
            } else {
                if (weak) {
                    stepConfig = stepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.WEAK));
                } else {
                    stepConfig = stepConfig.andThen(bsb -> bsb.produces(buildItemClass));
                }
            }
            stepInstanceSetup = stepInstanceSetup.andThen((bc, o) -> ReflectUtil.setFieldVal(field, o, (BuildProducer<? extends BuildItem>) bc::produce));
        } else if (isOptionalOf(fieldType, SimpleBuildItem.class)) {
            final Class<? extends SimpleBuildItem> buildItemClass = rawTypeOfParameter(fieldType, 0).asSubclass(SimpleBuildItem.class);
            stepConfig = stepConfig.andThen(bsb -> bsb.consumes(buildItemClass, ConsumeFlags.of(ConsumeFlag.OPTIONAL)));
            stepInstanceSetup = stepInstanceSetup.andThen((bc, o) -> ReflectUtil.setFieldVal(field, o, Optional.ofNullable(bc.consume(buildItemClass))));
        } else if (isSupplierOf(fieldType, SimpleBuildItem.class)) {
            final Class<? extends SimpleBuildItem> buildItemClass = rawTypeOfParameter(fieldType, 0).asSubclass(SimpleBuildItem.class);
            stepConfig = stepConfig.andThen(bsb -> bsb.consumes(buildItemClass));
            stepInstanceSetup = stepInstanceSetup.andThen((bc, o) -> ReflectUtil.setFieldVal(field, o, (Supplier<? extends SimpleBuildItem>) () -> bc.consume(buildItemClass)));
        } else if (isSupplierOfOptionalOf(fieldType, SimpleBuildItem.class)) {
            final Class<? extends SimpleBuildItem> buildItemClass = rawTypeOfParameter(rawTypeOfParameter(fieldType, 0), 0).asSubclass(SimpleBuildItem.class);
            stepConfig = stepConfig.andThen(bsb -> bsb.consumes(buildItemClass, ConsumeFlags.of(ConsumeFlag.OPTIONAL)));
            stepInstanceSetup = stepInstanceSetup.andThen((bc, o) -> ReflectUtil.setFieldVal(field, o, (Supplier<Optional<? extends SimpleBuildItem>>) () -> Optional.ofNullable(bc.consume(buildItemClass))));
        } else if (fieldClass == Executor.class) {
            stepInstanceSetup = stepInstanceSetup.andThen((bc, o) -> ReflectUtil.setFieldVal(field, o, bc.getExecutor()));
        } else if (fieldClass.isAnnotationPresent(ConfigRoot.class)) {
            final ConfigRoot annotation = fieldClass.getAnnotation(ConfigRoot.class);
            final ConfigPhase phase = annotation.phase();
            consumingConfigPhases.add(phase);
            if (phase.isAvailableAtBuild()) {
                stepInstanceSetup = stepInstanceSetup.andThen((bc, o) -> {
                    final ConfigurationBuildItem configurationBuildItem = bc.consume(ConfigurationBuildItem.class);
                    ReflectUtil.setFieldVal(field, o, configurationBuildItem.getReadResult().requireObjectForClass(fieldClass));
                });
                if (phase == ConfigPhase.BUILD_AND_RUN_TIME_FIXED) {
                    runTimeProxies.computeIfAbsent(fieldClass, readResult::requireObjectForClass);
                }
            } else if (phase.isReadAtMain()) {
                throw reportError(field, phase + " configuration cannot be consumed here");
            } else {
                throw reportError(fieldClass, "Unknown value for ConfigPhase");
            }
        } else if (isRecorder(fieldClass)) {
            throw reportError(field, "Bytecode recorders disallowed on fields");
        } else {
            throw reportError(field, "Unsupported field type " + fieldType);
        }
    }
    // now iterate the methods
    final List<Method> methods = getMethods(clazz);
    for (Method method : methods) {
        final BuildStep buildStep = method.getAnnotation(BuildStep.class);
        if (buildStep == null) {
            continue;
        }
        if (Modifier.isStatic(method.getModifiers())) {
            throw new RuntimeException("A build step must be a non-static method: " + method);
        }
        if (!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
            method.setAccessible(true);
        }
        final Class<? extends BooleanSupplier>[] onlyIf = buildStep.onlyIf();
        final Class<? extends BooleanSupplier>[] onlyIfNot = buildStep.onlyIfNot();
        final Parameter[] methodParameters = method.getParameters();
        final Record recordAnnotation = method.getAnnotation(Record.class);
        final boolean isRecorder = recordAnnotation != null;
        final boolean identityComparison = isRecorder ? recordAnnotation.useIdentityComparisonForParameters() : true;
        if (isRecorder) {
            boolean recorderFound = false;
            for (Class<?> p : method.getParameterTypes()) {
                if (isRecorder(p)) {
                    recorderFound = true;
                    break;
                }
            }
            if (!recorderFound) {
                throw new RuntimeException(method + " is marked @Record but does not inject an @Recorder object");
            }
        }
        final List<BiFunction<BuildContext, BytecodeRecorderImpl, Object>> methodParamFns;
        Consumer<BuildStepBuilder> methodStepConfig = Functions.discardingConsumer();
        BooleanSupplier addStep = () -> true;
        for (boolean inv : new boolean[] { false, true }) {
            Class<? extends BooleanSupplier>[] testClasses = inv ? onlyIfNot : onlyIf;
            for (Class<? extends BooleanSupplier> testClass : testClasses) {
                BooleanSupplier bs = supplierFactory.get((Class<? extends BooleanSupplier>) testClass);
                if (inv) {
                    addStep = and(addStep, not(bs));
                } else {
                    addStep = and(addStep, bs);
                }
            }
        }
        final BooleanSupplier finalAddStep = addStep;
        if (isRecorder) {
            assert recordAnnotation != null;
            final ExecutionTime executionTime = recordAnnotation.value();
            final boolean optional = recordAnnotation.optional();
            methodStepConfig = methodStepConfig.andThen(bsb -> {
                bsb.produces(executionTime == ExecutionTime.STATIC_INIT ? StaticBytecodeRecorderBuildItem.class : MainBytecodeRecorderBuildItem.class, optional ? ProduceFlags.of(ProduceFlag.WEAK) : ProduceFlags.NONE);
            });
        }
        EnumSet<ConfigPhase> methodConsumingConfigPhases = consumingConfigPhases.clone();
        if (methodParameters.length == 0) {
            methodParamFns = Collections.emptyList();
        } else {
            methodParamFns = new ArrayList<>(methodParameters.length);
            for (Parameter parameter : methodParameters) {
                final boolean weak = parameter.isAnnotationPresent(Weak.class);
                final boolean overridable = parameter.isAnnotationPresent(Overridable.class);
                final Type parameterType = parameter.getParameterizedType();
                final Class<?> parameterClass = parameter.getType();
                if (rawTypeExtends(parameterType, SimpleBuildItem.class)) {
                    final Class<? extends SimpleBuildItem> buildItemClass = parameterClass.asSubclass(SimpleBuildItem.class);
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.consumes(buildItemClass));
                    methodParamFns.add((bc, bri) -> bc.consume(buildItemClass));
                } else if (isListOf(parameterType, MultiBuildItem.class)) {
                    final Class<? extends MultiBuildItem> buildItemClass = rawTypeOfParameter(parameterType, 0).asSubclass(MultiBuildItem.class);
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.consumes(buildItemClass));
                    methodParamFns.add((bc, bri) -> bc.consumeMulti(buildItemClass));
                } else if (isConsumerOf(parameterType, BuildItem.class)) {
                    final Class<? extends BuildItem> buildItemClass = rawTypeOfParameter(parameterType, 0).asSubclass(BuildItem.class);
                    if (overridable) {
                        if (weak) {
                            methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.OVERRIDABLE, ProduceFlag.WEAK));
                        } else {
                            methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.OVERRIDABLE));
                        }
                    } else {
                        if (weak) {
                            methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.WEAK));
                        } else {
                            methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(buildItemClass));
                        }
                    }
                    methodParamFns.add((bc, bri) -> (Consumer<? extends BuildItem>) bc::produce);
                } else if (isBuildProducerOf(parameterType, BuildItem.class)) {
                    final Class<? extends BuildItem> buildItemClass = rawTypeOfParameter(parameterType, 0).asSubclass(BuildItem.class);
                    if (overridable) {
                        if (weak) {
                            methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.OVERRIDABLE, ProduceFlag.WEAK));
                        } else {
                            methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.OVERRIDABLE));
                        }
                    } else {
                        if (weak) {
                            methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(buildItemClass, ProduceFlag.WEAK));
                        } else {
                            methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(buildItemClass));
                        }
                    }
                    methodParamFns.add((bc, bri) -> (BuildProducer<? extends BuildItem>) bc::produce);
                } else if (isOptionalOf(parameterType, SimpleBuildItem.class)) {
                    final Class<? extends SimpleBuildItem> buildItemClass = rawTypeOfParameter(parameterType, 0).asSubclass(SimpleBuildItem.class);
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.consumes(buildItemClass, ConsumeFlags.of(ConsumeFlag.OPTIONAL)));
                    methodParamFns.add((bc, bri) -> Optional.ofNullable(bc.consume(buildItemClass)));
                } else if (isSupplierOf(parameterType, SimpleBuildItem.class)) {
                    final Class<? extends SimpleBuildItem> buildItemClass = rawTypeOfParameter(parameterType, 0).asSubclass(SimpleBuildItem.class);
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.consumes(buildItemClass));
                    methodParamFns.add((bc, bri) -> (Supplier<? extends SimpleBuildItem>) () -> bc.consume(buildItemClass));
                } else if (isSupplierOfOptionalOf(parameterType, SimpleBuildItem.class)) {
                    final Class<? extends SimpleBuildItem> buildItemClass = rawTypeOfParameter(rawTypeOfParameter(parameterType, 0), 0).asSubclass(SimpleBuildItem.class);
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.consumes(buildItemClass, ConsumeFlags.of(ConsumeFlag.OPTIONAL)));
                    methodParamFns.add((bc, bri) -> (Supplier<Optional<? extends SimpleBuildItem>>) () -> Optional.ofNullable(bc.consume(buildItemClass)));
                } else if (rawTypeOf(parameterType) == Executor.class) {
                    methodParamFns.add((bc, bri) -> bc.getExecutor());
                } else if (parameterClass.isAnnotationPresent(ConfigRoot.class)) {
                    final ConfigRoot annotation = parameterClass.getAnnotation(ConfigRoot.class);
                    final ConfigPhase phase = annotation.phase();
                    methodConsumingConfigPhases.add(phase);
                    if (phase.isAvailableAtBuild()) {
                        methodParamFns.add((bc, bri) -> {
                            final ConfigurationBuildItem configurationBuildItem = bc.consume(ConfigurationBuildItem.class);
                            return configurationBuildItem.getReadResult().requireObjectForClass(parameterClass);
                        });
                        if (isRecorder && phase == ConfigPhase.BUILD_AND_RUN_TIME_FIXED) {
                            runTimeProxies.computeIfAbsent(parameterClass, readResult::requireObjectForClass);
                        }
                    } else if (phase.isReadAtMain()) {
                        if (isRecorder) {
                            methodParamFns.add((bc, bri) -> {
                                final RunTimeConfigurationProxyBuildItem proxies = bc.consume(RunTimeConfigurationProxyBuildItem.class);
                                return proxies.getProxyObjectFor(parameterClass);
                            });
                            runTimeProxies.computeIfAbsent(parameterClass, ConfigMappingUtils::newInstance);
                        } else {
                            throw reportError(parameter, phase + " configuration cannot be consumed here unless the method is a @Recorder");
                        }
                    } else {
                        throw reportError(parameterClass, "Unknown value for ConfigPhase");
                    }
                } else if (isRecorder(parameter.getType())) {
                    if (!isRecorder) {
                        throw reportError(parameter, "Cannot pass recorders to method which is not annotated with " + Record.class);
                    }
                    methodParamFns.add((bc, bri) -> {
                        assert bri != null;
                        return bri.getRecordingProxy(parameterClass);
                    });
                    // now look for recorder parameter injection
                    // as we now inject config directly into recorders we need to look at the constructor params
                    Constructor<?>[] ctors = parameter.getType().getDeclaredConstructors();
                    for (var ctor : ctors) {
                        if (ctors.length == 1 || ctor.isAnnotationPresent(Inject.class)) {
                            for (var type : ctor.getGenericParameterTypes()) {
                                Class<?> theType = null;
                                if (type instanceof ParameterizedType) {
                                    ParameterizedType pt = (ParameterizedType) type;
                                    if (pt.getRawType().equals(RuntimeValue.class)) {
                                        theType = (Class<?>) pt.getActualTypeArguments()[0];
                                    } else {
                                        throw new RuntimeException("Unknown recorder constructor parameter: " + type + " in recorder " + parameter.getType());
                                    }
                                } else {
                                    theType = (Class<?>) type;
                                }
                                ConfigRoot annotation = theType.getAnnotation(ConfigRoot.class);
                                if (annotation != null) {
                                    if (recordAnnotation.value() == ExecutionTime.STATIC_INIT) {
                                        methodConsumingConfigPhases.add(ConfigPhase.BUILD_AND_RUN_TIME_FIXED);
                                    } else {
                                        methodConsumingConfigPhases.add(annotation.phase());
                                    }
                                    if (annotation.phase().isReadAtMain()) {
                                        runTimeProxies.computeIfAbsent(theType, ConfigMappingUtils::newInstance);
                                    } else {
                                        runTimeProxies.computeIfAbsent(theType, readResult::requireObjectForClass);
                                    }
                                }
                            }
                        }
                    }
                } else if (parameter.getType() == RecorderContext.class || parameter.getType() == BytecodeRecorderImpl.class) {
                    if (!isRecorder) {
                        throw reportError(parameter, "Cannot pass recorder context to method which is not annotated with " + Record.class);
                    }
                    methodParamFns.add((bc, bri) -> bri);
                } else {
                    throw reportError(parameter, "Unsupported method parameter " + parameterType);
                }
            }
        }
        final BiConsumer<BuildContext, Object> resultConsumer;
        final Type returnType = method.getGenericReturnType();
        final boolean weak = method.isAnnotationPresent(Weak.class);
        final boolean overridable = method.isAnnotationPresent(Overridable.class);
        if (rawTypeIs(returnType, void.class)) {
            resultConsumer = Functions.discardingBiConsumer();
        } else if (rawTypeExtends(returnType, BuildItem.class)) {
            final Class<? extends BuildItem> type = method.getReturnType().asSubclass(BuildItem.class);
            if (overridable) {
                if (weak) {
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(type, ProduceFlag.OVERRIDABLE, ProduceFlag.WEAK));
                } else {
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(type, ProduceFlag.OVERRIDABLE));
                }
            } else {
                if (weak) {
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(type, ProduceFlag.WEAK));
                } else {
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(type));
                }
            }
            resultConsumer = (bc, o) -> {
                if (o != null)
                    bc.produce((BuildItem) o);
            };
        } else if (isOptionalOf(returnType, BuildItem.class)) {
            final Class<? extends BuildItem> type = rawTypeOfParameter(returnType, 0).asSubclass(BuildItem.class);
            if (overridable) {
                if (weak) {
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(type, ProduceFlag.OVERRIDABLE, ProduceFlag.WEAK));
                } else {
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(type, ProduceFlag.OVERRIDABLE));
                }
            } else {
                if (weak) {
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(type, ProduceFlag.WEAK));
                } else {
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(type));
                }
            }
            resultConsumer = (bc, o) -> ((Optional<? extends BuildItem>) o).ifPresent(bc::produce);
        } else if (isListOf(returnType, MultiBuildItem.class)) {
            final Class<? extends MultiBuildItem> type = rawTypeOfParameter(returnType, 0).asSubclass(MultiBuildItem.class);
            if (overridable) {
                if (weak) {
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(type, ProduceFlag.OVERRIDABLE, ProduceFlag.WEAK));
                } else {
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(type, ProduceFlag.OVERRIDABLE));
                }
            } else {
                if (weak) {
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(type, ProduceFlag.WEAK));
                } else {
                    methodStepConfig = methodStepConfig.andThen(bsb -> bsb.produces(type));
                }
            }
            resultConsumer = (bc, o) -> {
                if (o != null)
                    bc.produce((List<? extends MultiBuildItem>) o);
            };
        } else {
            throw reportError(method, "Unsupported method return type " + returnType);
        }
        if (methodConsumingConfigPhases.contains(ConfigPhase.BOOTSTRAP) || methodConsumingConfigPhases.contains(ConfigPhase.RUN_TIME)) {
            if (isRecorder && recordAnnotation.value() == ExecutionTime.STATIC_INIT) {
                throw reportError(method, "Bytecode recorder is static but an injected config object is declared as run time");
            }
            methodStepConfig = methodStepConfig.andThen(bsb -> bsb.consumes(RunTimeConfigurationProxyBuildItem.class));
            if (methodConsumingConfigPhases.contains(ConfigPhase.BOOTSTRAP)) {
                methodStepConfig = methodStepConfig.andThen(bsb -> bsb.afterProduce(BootstrapConfigSetupCompleteBuildItem.class));
            }
            if (methodConsumingConfigPhases.contains(ConfigPhase.RUN_TIME)) {
                methodStepConfig = methodStepConfig.andThen(bsb -> bsb.afterProduce(RuntimeConfigSetupCompleteBuildItem.class));
            }
        }
        if (methodConsumingConfigPhases.contains(ConfigPhase.BUILD_AND_RUN_TIME_FIXED) || methodConsumingConfigPhases.contains(ConfigPhase.BUILD_TIME)) {
            methodStepConfig = methodStepConfig.andThen(bsb -> bsb.consumes(ConfigurationBuildItem.class));
        }
        final Consume[] consumes = method.getAnnotationsByType(Consume.class);
        if (consumes.length > 0) {
            methodStepConfig = methodStepConfig.andThen(bsb -> {
                for (Consume consume : consumes) {
                    bsb.afterProduce(consume.value());
                }
            });
        }
        final Produce[] produces = method.getAnnotationsByType(Produce.class);
        if (produces.length > 0) {
            methodStepConfig = methodStepConfig.andThen(bsb -> {
                for (Produce produce : produces) {
                    bsb.beforeConsume(produce.value());
                }
            });
        }
        final ProduceWeak[] produceWeaks = method.getAnnotationsByType(ProduceWeak.class);
        if (produceWeaks.length > 0) {
            methodStepConfig = methodStepConfig.andThen(bsb -> {
                for (ProduceWeak produceWeak : produceWeaks) {
                    bsb.beforeConsume(produceWeak.value(), ProduceFlag.WEAK);
                }
            });
        }
        final Consumer<BuildStepBuilder> finalStepConfig = stepConfig.andThen(methodStepConfig).andThen(buildStepBuilder -> buildStepBuilder.buildIf(finalAddStep));
        final BiConsumer<BuildContext, Object> finalStepInstanceSetup = stepInstanceSetup;
        final String name = clazz.getName() + "#" + method.getName();
        chainConfig = chainConfig.andThen(bcb -> {
            BuildStepBuilder bsb = bcb.addBuildStep(new io.quarkus.builder.BuildStep() {

                public void execute(final BuildContext bc) {
                    Object[] ctorArgs = new Object[ctorParamFns.size()];
                    for (int i = 0; i < ctorArgs.length; i++) {
                        ctorArgs[i] = ctorParamFns.get(i).apply(bc);
                    }
                    Object instance;
                    try {
                        instance = constructor.newInstance(ctorArgs);
                    } catch (InstantiationException e) {
                        throw ReflectUtil.toError(e);
                    } catch (IllegalAccessException e) {
                        throw ReflectUtil.toError(e);
                    } catch (InvocationTargetException e) {
                        try {
                            throw e.getCause();
                        } catch (RuntimeException | Error e2) {
                            throw e2;
                        } catch (Throwable t) {
                            throw new IllegalStateException(t);
                        }
                    }
                    finalStepInstanceSetup.accept(bc, instance);
                    Object[] methodArgs = new Object[methodParamFns.size()];
                    BytecodeRecorderImpl bri = isRecorder ? new BytecodeRecorderImpl(recordAnnotation.value() == ExecutionTime.STATIC_INIT, clazz.getSimpleName(), method.getName(), Integer.toString(Math.abs(method.toString().hashCode())), identityComparison, s -> {
                        if (s instanceof Class) {
                            var cfg = ((Class<?>) s).getAnnotation(ConfigRoot.class);
                            if (cfg == null || (cfg.phase() != ConfigPhase.BUILD_AND_RUN_TIME_FIXED && recordAnnotation.value() == ExecutionTime.STATIC_INIT)) {
                                throw new RuntimeException("Can only inject BUILD_AND_RUN_TIME_FIXED objects into a constructor, use RuntimeValue to inject runtime config: " + s);
                            }
                            return runTimeProxies.get(s);
                        }
                        if (s instanceof ParameterizedType) {
                            ParameterizedType p = (ParameterizedType) s;
                            if (p.getRawType() == RuntimeValue.class) {
                                Object object = runTimeProxies.get(p.getActualTypeArguments()[0]);
                                if (object == null) {
                                    return new RuntimeValue<>();
                                }
                                return new RuntimeValue<>(object);
                            }
                        }
                        return null;
                    }) : null;
                    for (int i = 0; i < methodArgs.length; i++) {
                        methodArgs[i] = methodParamFns.get(i).apply(bc, bri);
                    }
                    Object result;
                    try {
                        result = method.invoke(instance, methodArgs);
                    } catch (IllegalAccessException e) {
                        throw ReflectUtil.toError(e);
                    } catch (InvocationTargetException e) {
                        try {
                            throw e.getCause();
                        } catch (RuntimeException | Error e2) {
                            throw e2;
                        } catch (Throwable t) {
                            throw new IllegalStateException(t);
                        }
                    }
                    resultConsumer.accept(bc, result);
                    if (isRecorder) {
                        // commit recorded data
                        if (recordAnnotation.value() == ExecutionTime.STATIC_INIT) {
                            bc.produce(new StaticBytecodeRecorderBuildItem(bri));
                        } else {
                            bc.produce(new MainBytecodeRecorderBuildItem(bri));
                        }
                    }
                }

                public String toString() {
                    return name;
                }
            });
            finalStepConfig.accept(bsb);
        });
    }
    return chainConfig;
}
Also used : RootDefinition(io.quarkus.deployment.configuration.definition.RootDefinition) ReflectUtil.isSupplierOfOptionalOf(io.quarkus.deployment.util.ReflectUtil.isSupplierOfOptionalOf) RecorderContext(io.quarkus.deployment.recording.RecorderContext) BooleanSupplier(java.util.function.BooleanSupplier) Functions(org.wildfly.common.function.Functions) ReflectUtil.isSupplierOf(io.quarkus.deployment.util.ReflectUtil.isSupplierOf) Arrays.asList(java.util.Arrays.asList) Map(java.util.Map) BytecodeCreator(io.quarkus.gizmo.BytecodeCreator) Recorder(io.quarkus.runtime.annotations.Recorder) ConsumeFlags(io.quarkus.builder.ConsumeFlags) RunTimeConfigurationProxyBuildItem(io.quarkus.deployment.builditem.RunTimeConfigurationProxyBuildItem) ReflectUtil.rawTypeIs(io.quarkus.deployment.util.ReflectUtil.rawTypeIs) KeyMap(io.smallrye.config.KeyMap) EnumSet(java.util.EnumSet) Consume(io.quarkus.deployment.annotations.Consume) Member(java.lang.reflect.Member) MainBytecodeRecorderBuildItem(io.quarkus.deployment.builditem.MainBytecodeRecorderBuildItem) StaticBytecodeRecorderBuildItem(io.quarkus.deployment.builditem.StaticBytecodeRecorderBuildItem) ReflectUtil.isBuildProducerOf(io.quarkus.deployment.util.ReflectUtil.isBuildProducerOf) InvocationTargetException(java.lang.reflect.InvocationTargetException) Weak(io.quarkus.deployment.annotations.Weak) ApplicationModel(io.quarkus.bootstrap.model.ApplicationModel) ConfigProvider(org.eclipse.microprofile.config.ConfigProvider) ConsumeFlag(io.quarkus.builder.ConsumeFlag) PropertiesConfigSource(io.smallrye.config.PropertiesConfigSource) ConfigClassWithPrefix(io.smallrye.config.ConfigMappings.ConfigClassWithPrefix) ResultHandle(io.quarkus.gizmo.ResultHandle) AnnotatedElement(java.lang.reflect.AnnotatedElement) Record(io.quarkus.deployment.annotations.Record) ProduceWeak(io.quarkus.deployment.annotations.ProduceWeak) DevModeType(io.quarkus.dev.spi.DevModeType) ReflectUtil.rawTypeOf(io.quarkus.deployment.util.ReflectUtil.rawTypeOf) BuildStepBuilder(io.quarkus.builder.BuildStepBuilder) BuildItem(io.quarkus.builder.item.BuildItem) SimpleBuildItem(io.quarkus.builder.item.SimpleBuildItem) Constructor(java.lang.reflect.Constructor) Supplier(java.util.function.Supplier) ArrayList(java.util.ArrayList) ConfigRoot(io.quarkus.runtime.annotations.ConfigRoot) BuildChainBuilder(io.quarkus.builder.BuildChainBuilder) Parameter(java.lang.reflect.Parameter) BiConsumer(java.util.function.BiConsumer) KeyMapBackedConfigSource(io.smallrye.config.KeyMapBackedConfigSource) Properties(java.util.Properties) Executor(java.util.concurrent.Executor) IOException(java.io.IOException) Field(java.lang.reflect.Field) FieldDescriptor(io.quarkus.gizmo.FieldDescriptor) ParameterizedType(java.lang.reflect.ParameterizedType) SmallRyeConfigBuilder(io.smallrye.config.SmallRyeConfigBuilder) ConfigPhase(io.quarkus.runtime.annotations.ConfigPhase) BiFunction(java.util.function.BiFunction) ProduceFlags(io.quarkus.builder.ProduceFlags) Overridable(io.quarkus.deployment.annotations.Overridable) BuildProducer(io.quarkus.deployment.annotations.BuildProducer) ConfigProviderResolver(org.eclipse.microprofile.config.spi.ConfigProviderResolver) ProduceFlag(io.quarkus.builder.ProduceFlag) ReflectUtil(io.quarkus.deployment.util.ReflectUtil) Method(java.lang.reflect.Method) BootstrapConfigSetupCompleteBuildItem(io.quarkus.deployment.builditem.BootstrapConfigSetupCompleteBuildItem) IdentityHashMap(java.util.IdentityHashMap) ReflectUtil.isOptionalOf(io.quarkus.deployment.util.ReflectUtil.isOptionalOf) ReflectUtil.rawTypeExtends(io.quarkus.deployment.util.ReflectUtil.rawTypeExtends) ExecutionTime(io.quarkus.deployment.annotations.ExecutionTime) Config(org.eclipse.microprofile.config.Config) DefaultValuesConfigurationSource(io.quarkus.deployment.configuration.DefaultValuesConfigurationSource) List(java.util.List) BuildContext(io.quarkus.builder.BuildContext) Type(java.lang.reflect.Type) Modifier(java.lang.reflect.Modifier) Optional(java.util.Optional) ConfigMappingUtils(io.quarkus.deployment.configuration.ConfigMappingUtils) ConfigUtils(io.quarkus.runtime.configuration.ConfigUtils) Logger(org.jboss.logging.Logger) LaunchMode(io.quarkus.runtime.LaunchMode) HashMap(java.util.HashMap) Function(java.util.function.Function) ReflectUtil.isConsumerOf(io.quarkus.deployment.util.ReflectUtil.isConsumerOf) Inject(javax.inject.Inject) ConfigurationBuildItem(io.quarkus.deployment.builditem.ConfigurationBuildItem) BuildStep(io.quarkus.deployment.annotations.BuildStep) ReflectUtil.isListOf(io.quarkus.deployment.util.ReflectUtil.isListOf) RuntimeValue(io.quarkus.runtime.RuntimeValue) RuntimeConfigSetupCompleteBuildItem(io.quarkus.deployment.builditem.RuntimeConfigSetupCompleteBuildItem) ObjectLoader(io.quarkus.deployment.recording.ObjectLoader) QuarkusConfigFactory(io.quarkus.runtime.configuration.QuarkusConfigFactory) BytecodeRecorderObjectLoaderBuildItem(io.quarkus.deployment.builditem.BytecodeRecorderObjectLoaderBuildItem) MethodDescriptor(io.quarkus.gizmo.MethodDescriptor) SmallRyeConfig(io.smallrye.config.SmallRyeConfig) BuildTimeConfigurationReader(io.quarkus.deployment.configuration.BuildTimeConfigurationReader) Produce(io.quarkus.deployment.annotations.Produce) Consumer(java.util.function.Consumer) MultiBuildItem(io.quarkus.builder.item.MultiBuildItem) NameIterator(io.smallrye.config.NameIterator) ReflectUtil.rawTypeOfParameter(io.quarkus.deployment.util.ReflectUtil.rawTypeOfParameter) ServiceUtil(io.quarkus.deployment.util.ServiceUtil) BytecodeRecorderImpl(io.quarkus.deployment.recording.BytecodeRecorderImpl) Collections(java.util.Collections) BuildProducer(io.quarkus.deployment.annotations.BuildProducer) ConfigRoot(io.quarkus.runtime.annotations.ConfigRoot) Produce(io.quarkus.deployment.annotations.Produce) SimpleBuildItem(io.quarkus.builder.item.SimpleBuildItem) ConfigPhase(io.quarkus.runtime.annotations.ConfigPhase) Optional(java.util.Optional) Method(java.lang.reflect.Method) InvocationTargetException(java.lang.reflect.InvocationTargetException) Consume(io.quarkus.deployment.annotations.Consume) BuildContext(io.quarkus.builder.BuildContext) BuildChainBuilder(io.quarkus.builder.BuildChainBuilder) RuntimeValue(io.quarkus.runtime.RuntimeValue) MultiBuildItem(io.quarkus.builder.item.MultiBuildItem) RecorderContext(io.quarkus.deployment.recording.RecorderContext) BuildStep(io.quarkus.deployment.annotations.BuildStep) StaticBytecodeRecorderBuildItem(io.quarkus.deployment.builditem.StaticBytecodeRecorderBuildItem) ParameterizedType(java.lang.reflect.ParameterizedType) BiFunction(java.util.function.BiFunction) Function(java.util.function.Function) Field(java.lang.reflect.Field) Executor(java.util.concurrent.Executor) BooleanSupplier(java.util.function.BooleanSupplier) Supplier(java.util.function.Supplier) Record(io.quarkus.deployment.annotations.Record) ConfigurationBuildItem(io.quarkus.deployment.builditem.ConfigurationBuildItem) BooleanSupplier(java.util.function.BooleanSupplier) BytecodeRecorderImpl(io.quarkus.deployment.recording.BytecodeRecorderImpl) Constructor(java.lang.reflect.Constructor) RunTimeConfigurationProxyBuildItem(io.quarkus.deployment.builditem.RunTimeConfigurationProxyBuildItem) MainBytecodeRecorderBuildItem(io.quarkus.deployment.builditem.MainBytecodeRecorderBuildItem) StaticBytecodeRecorderBuildItem(io.quarkus.deployment.builditem.StaticBytecodeRecorderBuildItem) BuildItem(io.quarkus.builder.item.BuildItem) SimpleBuildItem(io.quarkus.builder.item.SimpleBuildItem) BootstrapConfigSetupCompleteBuildItem(io.quarkus.deployment.builditem.BootstrapConfigSetupCompleteBuildItem) ConfigurationBuildItem(io.quarkus.deployment.builditem.ConfigurationBuildItem) RuntimeConfigSetupCompleteBuildItem(io.quarkus.deployment.builditem.RuntimeConfigSetupCompleteBuildItem) BytecodeRecorderObjectLoaderBuildItem(io.quarkus.deployment.builditem.BytecodeRecorderObjectLoaderBuildItem) MultiBuildItem(io.quarkus.builder.item.MultiBuildItem) ExecutionTime(io.quarkus.deployment.annotations.ExecutionTime) DevModeType(io.quarkus.dev.spi.DevModeType) ParameterizedType(java.lang.reflect.ParameterizedType) Type(java.lang.reflect.Type) BuildStepBuilder(io.quarkus.builder.BuildStepBuilder) BiFunction(java.util.function.BiFunction) RunTimeConfigurationProxyBuildItem(io.quarkus.deployment.builditem.RunTimeConfigurationProxyBuildItem) ProduceWeak(io.quarkus.deployment.annotations.ProduceWeak) Parameter(java.lang.reflect.Parameter) ReflectUtil.rawTypeOfParameter(io.quarkus.deployment.util.ReflectUtil.rawTypeOfParameter) MainBytecodeRecorderBuildItem(io.quarkus.deployment.builditem.MainBytecodeRecorderBuildItem)

Aggregations

ApplicationModel (io.quarkus.bootstrap.model.ApplicationModel)1 BuildChainBuilder (io.quarkus.builder.BuildChainBuilder)1 BuildContext (io.quarkus.builder.BuildContext)1 BuildStepBuilder (io.quarkus.builder.BuildStepBuilder)1 ConsumeFlag (io.quarkus.builder.ConsumeFlag)1 ConsumeFlags (io.quarkus.builder.ConsumeFlags)1 ProduceFlag (io.quarkus.builder.ProduceFlag)1 ProduceFlags (io.quarkus.builder.ProduceFlags)1 BuildItem (io.quarkus.builder.item.BuildItem)1 MultiBuildItem (io.quarkus.builder.item.MultiBuildItem)1 SimpleBuildItem (io.quarkus.builder.item.SimpleBuildItem)1 BuildProducer (io.quarkus.deployment.annotations.BuildProducer)1 BuildStep (io.quarkus.deployment.annotations.BuildStep)1 Consume (io.quarkus.deployment.annotations.Consume)1 ExecutionTime (io.quarkus.deployment.annotations.ExecutionTime)1 Overridable (io.quarkus.deployment.annotations.Overridable)1 Produce (io.quarkus.deployment.annotations.Produce)1 ProduceWeak (io.quarkus.deployment.annotations.ProduceWeak)1 Record (io.quarkus.deployment.annotations.Record)1 Weak (io.quarkus.deployment.annotations.Weak)1