Search in sources :

Example 1 with ValidationErrorBuildItem

use of io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem in project quarkus by quarkusio.

the class ArcProcessor method registerSyntheticObservers.

// PHASE 3 - register synthetic observers
@BuildStep
public ObserverRegistrationPhaseBuildItem registerSyntheticObservers(BeanRegistrationPhaseBuildItem beanRegistrationPhase, List<BeanConfiguratorBuildItem> beanConfigurators, BuildProducer<ReflectiveMethodBuildItem> reflectiveMethods, BuildProducer<ReflectiveFieldBuildItem> reflectiveFields, BuildProducer<UnremovableBeanBuildItem> unremovableBeans, BuildProducer<ValidationPhaseBuildItem.ValidationErrorBuildItem> validationErrors) {
    for (BeanConfiguratorBuildItem configurator : beanConfigurators) {
        // Just make sure the configurator is processed
        configurator.getValues().forEach(BeanConfigurator::done);
    }
    // Initialize the type -> bean map
    beanRegistrationPhase.getBeanProcessor().getBeanDeployment().initBeanByTypeMap();
    // Register a synthetic bean for each List<?> with qualifier @All
    List<InjectionPointInfo> listAll = beanRegistrationPhase.getInjectionPoints().stream().filter(this::isListAllInjectionPoint).collect(Collectors.toList());
    for (InjectionPointInfo injectionPoint : listAll) {
        // Note that at this point we can be sure that the required type is List<>
        Type typeParam = injectionPoint.getType().asParameterizedType().arguments().get(0);
        if (typeParam.kind() == Type.Kind.WILDCARD_TYPE) {
            ClassInfo declaringClass;
            if (injectionPoint.isField()) {
                declaringClass = injectionPoint.getTarget().asField().declaringClass();
            } else {
                declaringClass = injectionPoint.getTarget().asMethod().declaringClass();
            }
            if (declaringClass.classAnnotation(DotNames.KOTLIN_METADATA_ANNOTATION) != null) {
                validationErrors.produce(new ValidationErrorBuildItem(new DefinitionException("kotlin.collections.List cannot be used together with the @All qualifier, please use MutableList or java.util.List instead: " + injectionPoint.getTargetInfo())));
            } else {
                validationErrors.produce(new ValidationErrorBuildItem(new DefinitionException("Wildcard is not a legal type argument for " + injectionPoint.getTargetInfo())));
            }
        } else if (typeParam.kind() == Type.Kind.TYPE_VARIABLE) {
            validationErrors.produce(new ValidationErrorBuildItem(new DefinitionException("Type variable is not a legal type argument for " + injectionPoint.getTargetInfo())));
        }
    }
    if (!listAll.isEmpty()) {
        registerListInjectionPointsBeans(beanRegistrationPhase, listAll, reflectiveMethods, reflectiveFields, unremovableBeans);
    }
    BeanProcessor beanProcessor = beanRegistrationPhase.getBeanProcessor();
    ObserverRegistrar.RegistrationContext registrationContext = beanProcessor.registerSyntheticObservers();
    return new ObserverRegistrationPhaseBuildItem(registrationContext, beanProcessor);
}
Also used : BeanConfigurator(io.quarkus.arc.processor.BeanConfigurator) ValidationErrorBuildItem(io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem) Type(org.jboss.jandex.Type) InjectionPointInfo(io.quarkus.arc.processor.InjectionPointInfo) BeanProcessor(io.quarkus.arc.processor.BeanProcessor) DefinitionException(javax.enterprise.inject.spi.DefinitionException) BeanConfiguratorBuildItem(io.quarkus.arc.deployment.BeanRegistrationPhaseBuildItem.BeanConfiguratorBuildItem) ObserverRegistrar(io.quarkus.arc.processor.ObserverRegistrar) ClassInfo(org.jboss.jandex.ClassInfo) BuildStep(io.quarkus.deployment.annotations.BuildStep)

Example 2 with ValidationErrorBuildItem

use of io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem in project quarkus by quarkusio.

the class WrongAnnotationUsageProcessor method detect.

@BuildStep
void detect(ArcConfig config, ApplicationIndexBuildItem applicationIndex, CustomScopeAnnotationsBuildItem scopeAnnotations, TransformedAnnotationsBuildItem transformedAnnotations, BuildProducer<ValidationErrorBuildItem> validationErrors) {
    if (!config.detectWrongAnnotations) {
        return;
    }
    IndexView index = applicationIndex.getIndex();
    // Detect unsupported annotations
    List<UnsupportedAnnotation> unsupported = new ArrayList<>();
    Function<AnnotationInstance, String> singletonFun = new Function<AnnotationInstance, String>() {

        @Override
        public String apply(AnnotationInstance annotationInstance) {
            return String.format("%s declared on %s, use @javax.inject.Singleton instead", annotationInstance.toString(false), getTargetInfo(annotationInstance));
        }
    };
    unsupported.add(new UnsupportedAnnotation(DotName.createSimple("com.google.inject.Singleton"), singletonFun));
    unsupported.add(new UnsupportedAnnotation(DotName.createSimple("javax.ejb.Singleton"), singletonFun));
    unsupported.add(new UnsupportedAnnotation(DotName.createSimple("groovy.lang.Singleton"), singletonFun));
    unsupported.add(new UnsupportedAnnotation(DotName.createSimple("jakarta.ejb.Singleton"), singletonFun));
    Map<AnnotationInstance, String> wrongUsages = new HashMap<>();
    for (UnsupportedAnnotation annotation : unsupported) {
        for (AnnotationInstance annotationInstance : index.getAnnotations(annotation.name)) {
            wrongUsages.put(annotationInstance, annotation.messageFun.apply(annotationInstance));
        }
    }
    if (!wrongUsages.isEmpty()) {
        for (Entry<AnnotationInstance, String> entry : wrongUsages.entrySet()) {
            validationErrors.produce(new ValidationErrorBuildItem(new IllegalStateException(entry.getValue())));
        }
    }
    // Note that a scope annotation is typically defined as @Target({TYPE, METHOD, FIELD}) but it's not forbidden to use ElementType.TYPE_USE
    for (ClassInfo clazz : index.getKnownClasses()) {
        NestingType nestingType = clazz.nestingType();
        if (NestingType.ANONYMOUS == nestingType || NestingType.LOCAL == nestingType || (NestingType.INNER == nestingType && !Modifier.isStatic(clazz.flags()))) {
            // Annotations declared on the class, incl. the annotations added via transformers
            Collection<AnnotationInstance> classAnnotations = transformedAnnotations.getAnnotations(clazz);
            if (classAnnotations.isEmpty() && clazz.annotations().isEmpty()) {
                continue;
            }
            if (scopeAnnotations.isScopeIn(classAnnotations)) {
                validationErrors.produce(new ValidationErrorBuildItem(new IllegalStateException(String.format("The %s class %s has a scope annotation but it must be ignored per the CDI rules", clazz.nestingType().toString(), clazz.name().toString()))));
            } else if (clazz.annotations().containsKey(DotNames.OBSERVES)) {
                validationErrors.produce(new ValidationErrorBuildItem(new IllegalStateException(String.format("The %s class %s declares an observer method but it must be ignored per the CDI rules", clazz.nestingType().toString(), clazz.name().toString()))));
            } else if (clazz.annotations().containsKey(DotNames.PRODUCES)) {
                validationErrors.produce(new ValidationErrorBuildItem(new IllegalStateException(String.format("The %s class %s declares a producer but it must be ignored per the CDI rules", clazz.nestingType().toString(), clazz.name().toString()))));
            }
        }
    }
}
Also used : ValidationErrorBuildItem(io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem) NestingType(org.jboss.jandex.ClassInfo.NestingType) HashMap(java.util.HashMap) IndexView(org.jboss.jandex.IndexView) ArrayList(java.util.ArrayList) Function(java.util.function.Function) AnnotationInstance(org.jboss.jandex.AnnotationInstance) ClassInfo(org.jboss.jandex.ClassInfo) BuildStep(io.quarkus.deployment.annotations.BuildStep)

Example 3 with ValidationErrorBuildItem

use of io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem in project quarkus by quarkusio.

the class CacheProcessor method validateCacheAnnotationsAndProduceCacheNames.

@BuildStep
void validateCacheAnnotationsAndProduceCacheNames(CombinedIndexBuildItem combinedIndex, List<AdditionalCacheNameBuildItem> additionalCacheNames, BuildProducer<ValidationErrorBuildItem> validationErrors, BuildProducer<CacheNamesBuildItem> cacheNames) {
    // Validation errors produced by this build step.
    List<Throwable> throwables = new ArrayList<>();
    // Cache names produced by this build step.
    Set<String> names = new HashSet<>();
    /*
         * First, for each non-repeated cache interceptor binding:
         * - its target is validated
         * - the corresponding cache name is collected
         */
    for (DotName bindingName : INTERCEPTOR_BINDINGS) {
        for (AnnotationInstance binding : combinedIndex.getIndex().getAnnotations(bindingName)) {
            throwables.addAll(validateInterceptorBindingTarget(binding, binding.target()));
            if (binding.target().kind() == METHOD) {
                /*
                     * Cache names from the interceptor bindings placed on cache interceptors must not be collected to prevent
                     * the instantiation of a cache with an empty name.
                     */
                names.add(binding.value(CACHE_NAME_PARAM).asString());
            }
        }
    }
    // The exact same things need to be done for repeated cache interceptor bindings.
    for (DotName containerName : INTERCEPTOR_BINDING_CONTAINERS) {
        for (AnnotationInstance container : combinedIndex.getIndex().getAnnotations(containerName)) {
            for (AnnotationInstance binding : container.value("value").asNestedArray()) {
                throwables.addAll(validateInterceptorBindingTarget(binding, container.target()));
                names.add(binding.value(CACHE_NAME_PARAM).asString());
            }
            /*
                 * Interception from repeated interceptor bindings won't work with the CDI implementation from MicroProfile REST
                 * Client. Using repeated interceptor bindings on a method from a class annotated with @RegisterRestClient must
                 * therefore be forbidden.
                 */
            if (container.target().kind() == METHOD) {
                MethodInfo methodInfo = container.target().asMethod();
                if (methodInfo.declaringClass().classAnnotation(REGISTER_REST_CLIENT) != null) {
                    throwables.add(new UnsupportedRepeatedAnnotationException(methodInfo));
                }
            }
        }
    }
    // Let's also collect the cache names from the @CacheName annotations.
    for (AnnotationInstance qualifier : combinedIndex.getIndex().getAnnotations(CACHE_NAME)) {
        // The @CacheName annotation from CacheProducer must be ignored.
        if (qualifier.target().kind() == METHOD) {
        /*
                 * This should only happen in CacheProducer. It'd be nice if we could forbid using @CacheName on a method in
                 * any other class, but Arc throws an AmbiguousResolutionException before we get a chance to validate things
                 * here.
                 */
        } else {
            names.add(qualifier.value().asString());
        }
    }
    // Finally, additional cache names provided by other extensions must be added to the cache names collection.
    for (AdditionalCacheNameBuildItem additionalCacheName : additionalCacheNames) {
        names.add(additionalCacheName.getName());
    }
    validationErrors.produce(new ValidationErrorBuildItem(throwables.toArray(new Throwable[0])));
    cacheNames.produce(new CacheNamesBuildItem(names));
}
Also used : ValidationErrorBuildItem(io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem) ArrayList(java.util.ArrayList) DotName(org.jboss.jandex.DotName) UnsupportedRepeatedAnnotationException(io.quarkus.cache.deployment.exception.UnsupportedRepeatedAnnotationException) MethodInfo(org.jboss.jandex.MethodInfo) AnnotationInstance(org.jboss.jandex.AnnotationInstance) HashSet(java.util.HashSet) BuildStep(io.quarkus.deployment.annotations.BuildStep)

Example 4 with ValidationErrorBuildItem

use of io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem in project quarkus by quarkusio.

the class MailerProcessor method validateMailTemplates.

@BuildStep
void validateMailTemplates(List<TemplatePathBuildItem> templatePaths, ValidationPhaseBuildItem validationPhase, BuildProducer<ValidationErrorBuildItem> validationErrors) {
    Set<String> filePaths = new HashSet<>();
    for (TemplatePathBuildItem templatePath : templatePaths) {
        String filePath = templatePath.getPath();
        if (File.separatorChar != '/') {
            filePath = filePath.replace(File.separatorChar, '/');
        }
        if (filePath.endsWith("html") || filePath.endsWith("htm") || filePath.endsWith("txt")) {
            // For e-mails we only consider html and txt templates
            filePaths.add(filePath);
            int idx = filePath.lastIndexOf('.');
            if (idx != -1) {
                // Also add version without suffix from the path
                // For example for "items.html" also add "items"
                filePaths.add(filePath.substring(0, idx));
            }
        }
    }
    for (InjectionPointInfo injectionPoint : validationPhase.getContext().get(BuildExtension.Key.INJECTION_POINTS)) {
        if (injectionPoint.getRequiredType().name().equals(MAIL_TEMPLATE)) {
            AnnotationInstance resourcePath = injectionPoint.getRequiredQualifier(QuteProcessor.LOCATION);
            String name;
            if (resourcePath != null) {
                name = resourcePath.value().asString();
            } else if (injectionPoint.hasDefaultedQualifier()) {
                name = QuteProcessor.getName(injectionPoint);
            } else {
                name = null;
            }
            if (name != null) {
                // For "@ResourcePath("github/pulls") MailTemplate pulls" we try to match "github/pulls"
                if (filePaths.stream().noneMatch(path -> path.endsWith(name))) {
                    validationErrors.produce(new ValidationErrorBuildItem(new IllegalStateException("No mail template found for " + injectionPoint.getTargetInfo())));
                }
            }
        }
    }
}
Also used : ValidationErrorBuildItem(io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem) InjectionPointInfo(io.quarkus.arc.processor.InjectionPointInfo) TemplatePathBuildItem(io.quarkus.qute.deployment.TemplatePathBuildItem) AnnotationInstance(org.jboss.jandex.AnnotationInstance) HashSet(java.util.HashSet) BuildStep(io.quarkus.deployment.annotations.BuildStep)

Example 5 with ValidationErrorBuildItem

use of io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem in project quarkus by quarkusio.

the class SchedulerProcessor method validateScheduledBusinessMethods.

@BuildStep
void validateScheduledBusinessMethods(SchedulerConfig config, List<ScheduledBusinessMethodItem> scheduledMethods, ValidationPhaseBuildItem validationPhase, BuildProducer<ValidationErrorBuildItem> validationErrors) {
    List<Throwable> errors = new ArrayList<>();
    Map<String, AnnotationInstance> encounteredIdentities = new HashMap<>();
    for (ScheduledBusinessMethodItem scheduledMethod : scheduledMethods) {
        MethodInfo method = scheduledMethod.getMethod();
        if (Modifier.isAbstract(method.flags())) {
            errors.add(new IllegalStateException("@Scheduled method must not be abstract: " + method.declaringClass().name() + "#" + method.name() + "()"));
            continue;
        }
        if (Modifier.isPrivate(method.flags())) {
            errors.add(new IllegalStateException("@Scheduled method must not be private: " + method.declaringClass().name() + "#" + method.name() + "()"));
            continue;
        }
        boolean isSuspendMethod = KotlinUtil.isSuspendMethod(method);
        // Validate method params and return type
        List<Type> params = method.parameters();
        int maxParamSize = isSuspendMethod ? 2 : 1;
        if (params.size() > maxParamSize || (params.size() == maxParamSize && !params.get(0).equals(SCHEDULED_EXECUTION_TYPE))) {
            errors.add(new IllegalStateException(String.format("Invalid scheduled business method parameters %s [method: %s, bean: %s]", params, method, scheduledMethod.getBean())));
        }
        if (!isValidReturnType(method)) {
            if (isSuspendMethod) {
                errors.add(new IllegalStateException(String.format("Suspending scheduled business method must return Unit [method: %s, bean: %s]", method, scheduledMethod.getBean())));
            } else {
                errors.add(new IllegalStateException(String.format("Scheduled business method must return void, CompletionStage<Void> or Uni<Void> [method: %s, bean: %s]", method, scheduledMethod.getBean())));
            }
        }
        // Validate cron() and every() expressions
        CronParser parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(config.cronType));
        for (AnnotationInstance scheduled : scheduledMethod.getSchedules()) {
            Throwable error = validateScheduled(parser, scheduled, encounteredIdentities, validationPhase.getContext());
            if (error != null) {
                errors.add(error);
            }
        }
    }
    if (!errors.isEmpty()) {
        validationErrors.produce(new ValidationErrorBuildItem(errors));
    }
}
Also used : ValidationErrorBuildItem(io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) Type(org.jboss.jandex.Type) MethodInfo(org.jboss.jandex.MethodInfo) AnnotationInstance(org.jboss.jandex.AnnotationInstance) CronParser(com.cronutils.parser.CronParser) BuildStep(io.quarkus.deployment.annotations.BuildStep)

Aggregations

ValidationErrorBuildItem (io.quarkus.arc.deployment.ValidationPhaseBuildItem.ValidationErrorBuildItem)5 BuildStep (io.quarkus.deployment.annotations.BuildStep)5 AnnotationInstance (org.jboss.jandex.AnnotationInstance)4 ArrayList (java.util.ArrayList)3 InjectionPointInfo (io.quarkus.arc.processor.InjectionPointInfo)2 HashMap (java.util.HashMap)2 HashSet (java.util.HashSet)2 ClassInfo (org.jboss.jandex.ClassInfo)2 MethodInfo (org.jboss.jandex.MethodInfo)2 Type (org.jboss.jandex.Type)2 CronParser (com.cronutils.parser.CronParser)1 BeanConfiguratorBuildItem (io.quarkus.arc.deployment.BeanRegistrationPhaseBuildItem.BeanConfiguratorBuildItem)1 BeanConfigurator (io.quarkus.arc.processor.BeanConfigurator)1 BeanProcessor (io.quarkus.arc.processor.BeanProcessor)1 ObserverRegistrar (io.quarkus.arc.processor.ObserverRegistrar)1 UnsupportedRepeatedAnnotationException (io.quarkus.cache.deployment.exception.UnsupportedRepeatedAnnotationException)1 TemplatePathBuildItem (io.quarkus.qute.deployment.TemplatePathBuildItem)1 Function (java.util.function.Function)1 DefinitionException (javax.enterprise.inject.spi.DefinitionException)1 NestingType (org.jboss.jandex.ClassInfo.NestingType)1