Skip to content

Commit

Permalink
Merge pull request quarkusio#2683 from mkouba/issue-2673
Browse files Browse the repository at this point in the history
Observers - add support for Reception.IF_EXISTS
  • Loading branch information
cescoffier authored Jun 4, 2019
2 parents 29bfe5a + 7a47b7d commit 8d8436c
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
import io.quarkus.arc.processor.BeanProcessor.PrivateMembersCollector;
import io.quarkus.arc.processor.ResourceOutput.Resource;
import io.quarkus.arc.processor.ResourceOutput.Resource.SpecialType;
import io.quarkus.gizmo.AssignableResultHandle;
import io.quarkus.gizmo.BranchResult;
import io.quarkus.gizmo.BytecodeCreator;
import io.quarkus.gizmo.ClassCreator;
import io.quarkus.gizmo.ClassOutput;
import io.quarkus.gizmo.FieldCreator;
Expand All @@ -48,6 +51,7 @@
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import javax.enterprise.event.Reception;
import javax.enterprise.inject.spi.EventContext;
import javax.enterprise.inject.spi.ObserverMethod;
import org.jboss.jandex.AnnotationInstance;
Expand Down Expand Up @@ -194,25 +198,48 @@ protected void implementNotify(ObserverInfo observer, ClassCreator observerCreat
MethodCreator notify = observerCreator.getMethodCreator("notify", void.class, EventContext.class)
.setModifiers(ACC_PUBLIC);

AssignableResultHandle declaringProviderInstanceHandle = notify.createVariable(Object.class);
AssignableResultHandle ctxHandle = notify.createVariable(CreationalContextImpl.class);
ResultHandle declaringProviderHandle = notify
.readInstanceField(
FieldDescriptor.of(observerCreator.getClassName(), "declaringProvider", InjectableBean.class.getName()),
FieldDescriptor.of(observerCreator.getClassName(), "declaringProvider",
InjectableBean.class.getName()),
notify.getThis());

// It is safe to skip CreationalContext.release() for normal scoped declaring provider and no injection points
boolean skipRelease = observer.getDeclaringBean().getScope().isNormal()
&& observer.getInjection().injectionPoints.isEmpty();
ResultHandle ctxHandle = skipRelease ? notify.loadNull()
: notify.newInstance(MethodDescriptor.ofConstructor(CreationalContextImpl.class));
ResultHandle declaringProviderInstanceHandle = notify.invokeInterfaceMethod(
MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, declaringProviderHandle,
ctxHandle);

if (observer.getDeclaringBean().getScope().isNormal()) {
// We need to unwrap the client proxy
declaringProviderInstanceHandle = notify.invokeInterfaceMethod(
MethodDescriptors.CLIENT_PROXY_GET_CONTEXTUAL_INSTANCE,
declaringProviderInstanceHandle);

// If Reception.IF_EXISTS is used we must check the context of the declaring bean first
if (Reception.IF_EXISTS == observer.getReception()) {
BeanInfo declaringBean = observer.getDeclaringBean();
if (declaringBean != null && !BuiltinScope.DEPENDENT.is(declaringBean.getScope())) {
ResultHandle container = notify.invokeStaticMethod(MethodDescriptors.ARC_CONTAINER);
ResultHandle scope = notify.loadClass(declaringBean.getScope().getDotName().toString());
ResultHandle context = notify.invokeInterfaceMethod(MethodDescriptors.ARC_CONTAINER_GET_ACTIVE_CONTEXT,
container,
scope);
notify.ifNull(context).trueBranch().returnValue(null);
notify.assign(declaringProviderInstanceHandle,
notify.invokeInterfaceMethod(MethodDescriptors.CONTEXT_GET_IF_PRESENT, context,
declaringProviderHandle));
BranchResult doesNotExist = notify.ifNull(declaringProviderInstanceHandle);
doesNotExist.trueBranch().returnValue(null);
BytecodeCreator isNotPresent = doesNotExist.falseBranch();
isNotPresent.assign(ctxHandle, skipRelease ? isNotPresent.loadNull()
: isNotPresent.newInstance(MethodDescriptor.ofConstructor(CreationalContextImpl.class)));
}
} else {
notify.assign(ctxHandle, skipRelease ? notify.loadNull()
: notify.newInstance(MethodDescriptor.ofConstructor(CreationalContextImpl.class)));
notify.assign(declaringProviderInstanceHandle, notify.invokeInterfaceMethod(
MethodDescriptors.INJECTABLE_REF_PROVIDER_GET, declaringProviderHandle,
ctxHandle));
if (observer.getDeclaringBean().getScope().isNormal()) {
// We need to unwrap the client proxy
notify.assign(declaringProviderInstanceHandle, notify.invokeInterfaceMethod(
MethodDescriptors.CLIENT_PROXY_GET_CONTEXTUAL_INSTANCE,
declaringProviderInstanceHandle));
}
}

ResultHandle[] referenceHandles = new ResultHandle[observer.getObserverMethod().parameters().size()];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import javax.enterprise.event.Reception;
import javax.enterprise.inject.spi.DefinitionException;
import javax.enterprise.inject.spi.ObserverMethod;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget.Kind;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.MethodInfo;
import org.jboss.jandex.MethodParameterInfo;
import org.jboss.jandex.Type;
Expand Down Expand Up @@ -99,6 +101,17 @@ public boolean isAsync() {
return isAsync;
}

public Reception getReception() {
AnnotationInstance observesAnnotation = isAsync
? declaringBean.getDeployment().getAnnotation(observerMethod, DotNames.OBSERVES_ASYNC)
: declaringBean.getDeployment().getAnnotation(observerMethod, DotNames.OBSERVES);
AnnotationValue receptionValue = observesAnnotation.value("notifyObserver");
if (receptionValue == null) {
return Reception.ALWAYS;
}
return Reception.valueOf(receptionValue.asEnum());
}

void init(List<Throwable> errors) {
for (InjectionPointInfo injectionPoint : injection.injectionPoints) {
Beans.resolveInjectionPoint(declaringBean.getDeployment(), this, injectionPoint, errors);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@ synchronized void shutdown() {
ArcCDI arcCdi = (ArcCDI) cdi;
arcCdi.destroy();
}
// Terminate request context if for any reason is still active
requestContext.terminate();
// Fire an event with qualifier @BeforeDestroyed(ApplicationScoped.class)
Set<Annotation> beforeDestroyQualifiers = new HashSet<>(4);
beforeDestroyQualifiers.add(BeforeDestroyed.Literal.APPLICATION);
Expand All @@ -282,7 +284,6 @@ synchronized void shutdown() {
destroyQualifiers.add(Any.Literal.INSTANCE);
EventImpl.createNotifier(Object.class, Object.class, destroyQualifiers, this).notify(toString());
singletonContext.destroy();
requestContext.terminate();
// Clear caches
contexts.clear();
beans.clear();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright 2018 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.quarkus.arc.test.observers.ifexists;

import static org.junit.Assert.assertEquals;

import io.quarkus.arc.Arc;
import io.quarkus.arc.ArcContainer;
import io.quarkus.arc.test.ArcTestContainer;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.Priority;
import javax.enterprise.context.Dependent;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;
import org.junit.Rule;
import org.junit.Test;

public class ReceptionIfExistsTest {

static final List<String> EVENTS = new CopyOnWriteArrayList<>();

@Rule
public ArcTestContainer container = new ArcTestContainer(DependentObserver.class, RequestScopedObserver.class);

@Test
public void testObserver() {
ArcContainer container = Arc.container();
container.beanManager().fireEvent("foo");
assertEquals(1, EVENTS.size());
assertEquals(DependentObserver.class.getName() + "foo", EVENTS.get(0));

// Activate the request context but the instance still does not exist
EVENTS.clear();
container.requestContext().activate();
container.beanManager().fireEvent("foo");
assertEquals(1, EVENTS.size());
assertEquals(DependentObserver.class.getName() + "foo", EVENTS.get(0));
container.requestContext().deactivate();

// Activate the request context and the instance exists
EVENTS.clear();
container.requestContext().activate();
// Force bean instance creation
container.instance(RequestScopedObserver.class).get().ping();
container.beanManager().fireEvent("foo");
assertEquals(2, EVENTS.size());
assertEquals(RequestScopedObserver.class.getName() + "foo", EVENTS.get(0));
assertEquals(DependentObserver.class.getName() + "foo", EVENTS.get(1));
container.requestContext().deactivate();
}

@RequestScoped
static class RequestScopedObserver {

void ping() {
}

void observeString(@Priority(1) @Observes(notifyObserver = Reception.IF_EXISTS) String value) {
EVENTS.add(RequestScopedObserver.class.getName() + value);
}

}

@Dependent
static class DependentObserver {

void observeString(@Priority(2) @Observes String value) {
EVENTS.add(DependentObserver.class.getName() + value);
}

}

}

0 comments on commit 8d8436c

Please sign in to comment.