Skip to content

UnnecessaryQualifier outputs warnings for CDI observers #5173

@bannmann

Description

@bannmann

With the reproducer source file below, UnnecessaryQualifier in error-prone 2.40.0 reports the following warnings:

  1. [28,4] [UnnecessaryQualifier] A qualifier annotation has no effect here.
  2. [56,42] [UnnecessaryQualifier] A qualifier annotation has no effect here.
    • This is a new issue. It's a false positive as Observes considers the qualifier:
      Each observer method must have exactly one event parameter, of the same type as the event type it observes. Event qualifiers may be declared by annotating the event parameter. When searching for observer methods for an event, the container considers the type and qualifiers of the event parameter.
    • The same false positive occurs with the popular @Observes @Initialized(ApplicationScoped.class) pattern, which emulates EJB's @Startup annotation. See https://www.adam-bien.com/roller/abien/entry/startup_initialization_logic_with_cdi.
package com.example;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.enterprise.event.Event;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.inject.Qualifier;

import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;

import com.google.errorprone.annotations.Keep;

@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Inject, access = AccessLevel.PROTECTED)
public final class FooDeleter
{
    @Deleted // ⚠️1
    private final Event<Foo> fooDeletedEvent;

    public void delete(int fooId)
    {
        log.info("Deleting Foo {}", fooId);

        fooDeletedEvent.fire(null);
    }
}

@Qualifier
@Target({ METHOD, FIELD, PARAMETER, TYPE })
@Retention(RUNTIME)
@interface Deleted
{
}

@Value
class Foo
{
    int id;
}

@Slf4j
final class DeletionObserver
{
    @Keep
    private void afterFooDelete(@Observes @Deleted Foo deletedFoo) // ⚠️2
    {
        log.info("Observed deletion of {}", deletedFoo);
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions