Skip to content

Commit

Permalink
Merge pull request #38454 from manovotn/issue38453
Browse files Browse the repository at this point in the history
Arc - Add @default qualifier to events fired through BM#getEvent() API
  • Loading branch information
manovotn authored Jan 29, 2024
2 parents 379b794 + d5249f6 commit ffec931
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import jakarta.enterprise.event.ObserverException;
import jakarta.enterprise.event.TransactionPhase;
import jakarta.enterprise.inject.Any;
import jakarta.enterprise.inject.Default;
import jakarta.enterprise.inject.spi.EventContext;
import jakarta.enterprise.inject.spi.EventMetadata;
import jakarta.enterprise.inject.spi.InjectionPoint;
Expand Down Expand Up @@ -68,10 +69,7 @@ class EventImpl<T> implements Event<T> {
EventImpl(Type eventType, Set<Annotation> qualifiers, InjectionPoint injectionPoint) {
this.eventType = initEventType(eventType);
this.injectionPointTypeHierarchy = new HierarchyDiscovery(this.eventType);
Set<Annotation> eventQualifiers = new HashSet<>();
eventQualifiers.addAll(qualifiers);
eventQualifiers.add(Any.Literal.INSTANCE);
this.qualifiers = Set.copyOf(eventQualifiers);
this.qualifiers = Set.copyOf(qualifiers);
this.notifiers = new ConcurrentHashMap<>(DEFAULT_CACHE_CAPACITY);
this.injectionPoint = injectionPoint;
}
Expand Down Expand Up @@ -172,9 +170,16 @@ static <T> Notifier<T> createNotifier(Class<?> runtimeType, Type eventType, Set<

static <T> Notifier<T> createNotifier(Class<?> runtimeType, Type eventType, Set<Annotation> qualifiers,
ArcContainerImpl container, boolean activateRequestContext, InjectionPoint injectionPoint) {
EventMetadata metadata = new EventMetadataImpl(qualifiers, eventType, injectionPoint);
// all events should have `@Any` qualifiers
// if there was no other explicit qualifier added, also add @Default
Set<Annotation> normalizedQualifiers = new HashSet<>(qualifiers);
if (normalizedQualifiers.isEmpty()) {
normalizedQualifiers.add(Default.Literal.INSTANCE);
}
normalizedQualifiers.add(Any.Literal.INSTANCE);
EventMetadata metadata = new EventMetadataImpl(normalizedQualifiers, eventType, injectionPoint);
List<ObserverMethod<? super T>> notifierObserverMethods = new ArrayList<>(
container.resolveObservers(eventType, qualifiers));
container.resolveObservers(eventType, normalizedQualifiers));
return new Notifier<>(runtimeType, notifierObserverMethods, metadata, activateRequestContext);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package io.quarkus.arc.test.event.qualifier;

import java.lang.annotation.Annotation;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.enterprise.event.ObservesAsync;
import jakarta.enterprise.inject.Any;
import jakarta.enterprise.inject.Default;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.enterprise.inject.spi.EventMetadata;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.Arc;
import io.quarkus.arc.test.ArcTestContainer;

public class EventDefaultQualifierTest {

@RegisterExtension
public ArcTestContainer container = ArcTestContainer.builder().beanClasses(ObservingBean.class).build();

@Test
public void testDefaultQualifierPresent()
throws ExecutionException, InterruptedException, TimeoutException {
BeanManager bm = Arc.container().beanManager();
ObservingBean bean = Arc.container().select(ObservingBean.class).get();
bean.reset();

Set<Annotation> expectedQualifiers = Set.of(Any.Literal.INSTANCE, Default.Literal.INSTANCE);

// just get event fire right away - @Default should be included
bm.getEvent().fire(new Payload());
Assertions.assertEquals(1, bean.getDefaultObjectNotified());
Assertions.assertEquals(1, bean.getDefaultPayloadNotified());
Assertions.assertEquals(expectedQualifiers, bean.getDefaultObjectQualifiers());
Assertions.assertEquals(expectedQualifiers, bean.getDefaultPayloadQualifiers());

// select Payload and fire - @Default should be included
bm.getEvent().select(Payload.class).fire(new Payload());
Assertions.assertEquals(2, bean.getDefaultObjectNotified());
Assertions.assertEquals(2, bean.getDefaultPayloadNotified());
Assertions.assertEquals(expectedQualifiers, bean.getDefaultObjectQualifiers());
Assertions.assertEquals(expectedQualifiers, bean.getDefaultPayloadQualifiers());

// select Payload and explicitly add @Any qualifier, then fire - @Default should *not* be included
// therefore no notifications should occur
bm.getEvent().select(Payload.class, Any.Literal.INSTANCE).fire(new Payload());
Assertions.assertEquals(2, bean.getDefaultObjectNotified());
Assertions.assertEquals(2, bean.getDefaultPayloadNotified());

// same in async variant
// just get event fire right away - @Default should be included
bm.getEvent().fireAsync(new Payload()).toCompletableFuture().get(2, TimeUnit.SECONDS);
Assertions.assertEquals(1, bean.getDefaultObjectAsyncNotified());
Assertions.assertEquals(1, bean.getDefaultPayloadAsyncNotified());
Assertions.assertEquals(expectedQualifiers, bean.getDefaultObjectAsyncQualifiers());
Assertions.assertEquals(expectedQualifiers, bean.getDefaultPayloadAsyncQualifiers());

// select Payload and fire - @Default should be included
bm.getEvent().select(Payload.class).fireAsync(new Payload()).toCompletableFuture().get(2, TimeUnit.SECONDS);
Assertions.assertEquals(2, bean.getDefaultObjectAsyncNotified());
Assertions.assertEquals(2, bean.getDefaultPayloadAsyncNotified());
Assertions.assertEquals(expectedQualifiers, bean.getDefaultObjectAsyncQualifiers());
Assertions.assertEquals(expectedQualifiers, bean.getDefaultPayloadAsyncQualifiers());

// select Payload and explicitly add @Any qualifier, then fire - @Default should *not* be included
// therefore no notifications should occur
bm.getEvent().select(Payload.class, Any.Literal.INSTANCE).fireAsync(new Payload()).toCompletableFuture().get(2,
TimeUnit.SECONDS);
Assertions.assertEquals(2, bean.getDefaultObjectAsyncNotified());
Assertions.assertEquals(2, bean.getDefaultPayloadAsyncNotified());
}

public static class Payload {
}

@ApplicationScoped
public static class ObservingBean {

private volatile int defaultObjectNotified = 0;
private volatile int defaultObjectAsyncNotified = 0;
private volatile int defaultPayloadNotified = 0;
private volatile int defaultPayloadAsyncNotified = 0;
private volatile Set<Annotation> defaultObjectQualifiers;
private volatile Set<Annotation> defaultObjectAsyncQualifiers;
private volatile Set<Annotation> defaultPayloadQualifiers;
private volatile Set<Annotation> defaultPayloadAsyncQualifiers;

public void observeDefaultObject(@Observes @Default Object payload, EventMetadata em) {
// object type is very broad, only look for Payload runtime type
if (em.getType().equals(Payload.class)) {
this.defaultObjectNotified++;
this.defaultObjectQualifiers = em.getQualifiers();
}
}

public void observeDefaultPayload(@Observes @Default Payload payload, EventMetadata em) {
this.defaultPayloadNotified++;
this.defaultPayloadQualifiers = em.getQualifiers();
}

public void observeDefaultObjectAsync(@ObservesAsync @Default Object payload, EventMetadata em) {
// object type is very broad, only look for Payload runtime type
if (em.getType().equals(Payload.class)) {
this.defaultObjectAsyncNotified++;
this.defaultObjectAsyncQualifiers = em.getQualifiers();
}
}

public void observeDefaultPayloadAsync(@ObservesAsync @Default Payload payload, EventMetadata em) {
this.defaultPayloadAsyncNotified++;
this.defaultPayloadAsyncQualifiers = em.getQualifiers();
}

public int getDefaultObjectNotified() {
return defaultObjectNotified;
}

public int getDefaultPayloadNotified() {
return defaultPayloadNotified;
}

public Set<Annotation> getDefaultObjectQualifiers() {
return defaultObjectQualifiers;
}

public Set<Annotation> getDefaultPayloadQualifiers() {
return defaultPayloadQualifiers;
}

public int getDefaultObjectAsyncNotified() {
return defaultObjectAsyncNotified;
}

public int getDefaultPayloadAsyncNotified() {
return defaultPayloadAsyncNotified;
}

public Set<Annotation> getDefaultObjectAsyncQualifiers() {
return defaultObjectAsyncQualifiers;
}

public Set<Annotation> getDefaultPayloadAsyncQualifiers() {
return defaultPayloadAsyncQualifiers;
}

public void reset() {
this.defaultPayloadNotified = 0;
this.defaultPayloadAsyncNotified = 0;
this.defaultObjectNotified = 0;
this.defaultObjectAsyncNotified = 0;
this.defaultObjectQualifiers = null;
this.defaultObjectAsyncQualifiers = null;
this.defaultPayloadQualifiers = null;
this.defaultPayloadAsyncQualifiers = null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ public void testMetadata() {
Arc.container().beanManager().getEvent().fire(BigDecimal.ONE);
EventMetadata metadata = BigObserver.METADATA.get();
assertNotNull(metadata);
assertEquals(1, metadata.getQualifiers().size());
assertEquals(Any.class, metadata.getQualifiers().iterator().next().annotationType());
assertEquals(2, metadata.getQualifiers().size());
for (Annotation qualifier : metadata.getQualifiers()) {
assertTrue(qualifier.annotationType().equals(Any.class) || qualifier.annotationType().equals(Default.class));
}
assertEquals(BigDecimal.class, metadata.getType());
assertNull(metadata.getInjectionPoint());

Expand Down

0 comments on commit ffec931

Please sign in to comment.