/*
* Copyright (C) 2012 Red Hat, Inc. and/or its affiliates.
*
* 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 org.jboss.errai.cdi.async.test.bm.client;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Set;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Any;
import org.jboss.errai.cdi.async.test.bm.client.res.AbstractBean;
import org.jboss.errai.cdi.async.test.bm.client.res.ActivatedBean;
import org.jboss.errai.cdi.async.test.bm.client.res.ActivatedBeanInterface;
import org.jboss.errai.cdi.async.test.bm.client.res.ApplicationScopedBean;
import org.jboss.errai.cdi.async.test.bm.client.res.CommonInterface;
import org.jboss.errai.cdi.async.test.bm.client.res.CommonInterfaceB;
import org.jboss.errai.cdi.async.test.bm.client.res.Cow;
import org.jboss.errai.cdi.async.test.bm.client.res.CreditCard;
import org.jboss.errai.cdi.async.test.bm.client.res.DependentScopedBean;
import org.jboss.errai.cdi.async.test.bm.client.res.DependentScopedBeanWithDependencies;
import org.jboss.errai.cdi.async.test.bm.client.res.FoobieScopedBean;
import org.jboss.errai.cdi.async.test.bm.client.res.FoobieScopedOverriddenBean;
import org.jboss.errai.cdi.async.test.bm.client.res.InheritedApplicationScopedBean;
import org.jboss.errai.cdi.async.test.bm.client.res.InheritedFromAbstractBean;
import org.jboss.errai.cdi.async.test.bm.client.res.InterfaceA;
import org.jboss.errai.cdi.async.test.bm.client.res.InterfaceB;
import org.jboss.errai.cdi.async.test.bm.client.res.InterfaceC;
import org.jboss.errai.cdi.async.test.bm.client.res.InterfaceD;
import org.jboss.errai.cdi.async.test.bm.client.res.InterfaceRoot;
import org.jboss.errai.cdi.async.test.bm.client.res.LincolnBar;
import org.jboss.errai.cdi.async.test.bm.client.res.OuterBeanInterface;
import org.jboss.errai.cdi.async.test.bm.client.res.Pig;
import org.jboss.errai.cdi.async.test.bm.client.res.QualA;
import org.jboss.errai.cdi.async.test.bm.client.res.QualAppScopeBeanA;
import org.jboss.errai.cdi.async.test.bm.client.res.QualAppScopeBeanB;
import org.jboss.errai.cdi.async.test.bm.client.res.QualB;
import org.jboss.errai.cdi.async.test.bm.client.res.QualEnum;
import org.jboss.errai.cdi.async.test.bm.client.res.QualParmAppScopeBeanApples;
import org.jboss.errai.cdi.async.test.bm.client.res.QualParmAppScopeBeanOranges;
import org.jboss.errai.cdi.async.test.bm.client.res.QualV;
import org.jboss.errai.cdi.async.test.bm.client.res.TestBeanActivator;
import org.jboss.errai.cdi.async.test.bm.client.res.ViaInstanceModule;
import org.jboss.errai.cdi.async.test.bm.client.res.Visa;
import org.jboss.errai.common.client.util.CreationalCallback;
import org.jboss.errai.enterprise.client.cdi.AbstractErraiCDITest;
import org.jboss.errai.ioc.client.QualifierUtil;
import org.jboss.errai.ioc.client.container.DestructionCallback;
import org.jboss.errai.ioc.client.container.IOC;
import org.jboss.errai.ioc.client.container.IOCEnvironment;
import org.jboss.errai.ioc.client.container.IOCResolutionException;
import org.jboss.errai.ioc.client.container.RefHolder;
import org.jboss.errai.ioc.client.container.async.AsyncBeanDef;
import org.jboss.errai.ioc.client.container.async.AsyncBeanFuture;
import org.jboss.errai.ioc.client.container.async.AsyncBeanManager;
import org.jboss.errai.ioc.client.container.async.AsyncBeanQuery;
import com.google.gwt.core.shared.GWT;
/**
* @author Mike Brock
*/
public class AsyncCDIBeanManagerTest extends AbstractErraiCDITest {
{
disableBus = true;
}
@Override
public String getModuleName() {
return "org.jboss.errai.cdi.async.test.bm.AsyncCDIBeanManagerTest";
}
public void testBeanManagerLookupInheritedScopeBean() {
asyncTest(new Runnable() {
@Override
public void run() {
final AsyncBeanDef<InheritedApplicationScopedBean> bean =
IOC.getAsyncBeanManager().lookupBean(InheritedApplicationScopedBean.class, new QualB() {
@Override
public Class<? extends Annotation> annotationType() {
return QualB.class;
}
});
assertNotNull("inherited application scoped bean did not lookup", bean);
bean.getInstance(new CreationalCallback<InheritedApplicationScopedBean>() {
@Override
public void callback(final InheritedApplicationScopedBean beanInst) {
assertNotNull("bean instance is null", beanInst);
final DependentScopedBean bean1 = beanInst.getBean1();
assertNotNull("bean1 is null", bean1);
final DependentScopedBeanWithDependencies beanWithDependencies = beanInst.getBeanWithDependencies();
assertNotNull("beanWithDependencies is null", beanWithDependencies);
final DependentScopedBean bean2 = beanWithDependencies.getBean();
assertNotSame("bean1 and bean2 should be different", bean1, bean2);
bean.getInstance(new CreationalCallback<InheritedApplicationScopedBean>() {
@Override
public void callback(InheritedApplicationScopedBean beanInst2) {
assertSame("bean is not observing application scope", beanInst, beanInst2);
finishTest();
}
});
}
});
}
});
}
public void testBeanManagerLookupBeanFromAbstractRootType() {
asyncTest(new Runnable() {
@Override
public void run() {
final AsyncBeanDef<AbstractBean> bean = IOC.getAsyncBeanManager().lookupBean(AbstractBean.class);
assertNotNull("did not find any beans matching", bean);
bean.getInstance(new CreationalCallback<AbstractBean>() {
@Override
public void callback(final AbstractBean beanInst) {
assertNotNull("bean instance is null", beanInst);
assertTrue("bean is incorrect instance: " + beanInst.getClass(), beanInst instanceof InheritedFromAbstractBean);
finishTest();
}
});
}
});
}
/**
* This test effectively tests that the IOC container comprehends the full type hierarchy, considering both supertypes
* and transverse interface types.
*/
public void testBeanManagerLookupForOuterInterfaceRootType() {
asyncTest(new Runnable() {
@Override
public void run() {
final AsyncBeanDef<OuterBeanInterface> bean = IOC.getAsyncBeanManager().lookupBean(OuterBeanInterface.class);
assertNotNull("did not find any beans matching", bean);
bean.getInstance(new CreationalCallback<OuterBeanInterface>() {
@Override
public void callback(final OuterBeanInterface beanInst) {
assertNotNull("bean instance is null", beanInst);
assertTrue("bean is incorrect instance: " + beanInst.getClass(), beanInst instanceof InheritedFromAbstractBean);
finishTest();
}
});
}
});
}
public void testBeanManagerLookupForOuterInterfacesOfNonAbstractType() {
asyncTest(new Runnable() {
@Override
public void run() {
final AsyncBeanDef<InterfaceC> beanC = IOC.getAsyncBeanManager().lookupBean(InterfaceC.class);
assertNotNull("did not find any beans matching", beanC);
final AsyncBeanDef<InterfaceD> beanD = IOC.getAsyncBeanManager().lookupBean(InterfaceD.class);
assertNotNull("did not find any beans matching", beanD);
finishTest();
}
});
}
public void testBeanManagerLookupForExtendedInterfaceType() {
asyncTest(new Runnable() {
@Override
public void run() {
// This should find ApplicationScopedBeanA, ApplicationScopedBeanB and ApplicationScopedBeanC
final Collection<AsyncBeanDef<InterfaceRoot>> beans = IOC.getAsyncBeanManager().lookupBeans(InterfaceRoot.class);
assertEquals("did not find all managed implementations of " + InterfaceRoot.class.getName(), 3, beans.size());
// This should find ApplicationScopedBeanA and ApplicationScopedBeanB (InterfaceB extends InterfaceA)
final Collection<AsyncBeanDef<InterfaceA>> beansB = IOC.getAsyncBeanManager().lookupBeans(InterfaceA.class);
assertEquals("did not find both managed implementations of " + InterfaceA.class.getName(), 2, beansB.size());
// This should find only ApplicationScopedBeanB
final Collection<AsyncBeanDef<InterfaceB>> beansC = IOC.getAsyncBeanManager().lookupBeans(InterfaceB.class);
assertEquals("did not find exactly one managed implementation of " + InterfaceB.class.getName(), 1, beansC.size());
finishTest();
}
});
}
public void testBeanManagerAPIs() {
asyncTest(new Runnable() {
@Override
public void run() {
final AsyncBeanManager mgr = IOC.getAsyncBeanManager();
final AsyncBeanDef<QualAppScopeBeanA> bean = mgr.lookupBean(QualAppScopeBeanA.class, new QualA() {
@Override
public Class<? extends Annotation> annotationType() {
return QualA.class;
}
});
final Set<Annotation> a = bean.getQualifiers();
assertEquals("there should be two qualifiers", 2, a.size());
assertTrue("wrong qualifiers", annotationSetMatches(a, QualA.class, Any.class));
finishTest();
}
});
}
public void testQualifiedLookup() {
asyncTest(new Runnable() {
@Override
public void run() {
final QualA qualA = new QualA() {
@Override
public Class<? extends Annotation> annotationType() {
return QualA.class;
}
};
final QualB qualB = new QualB() {
@Override
public Class<? extends Annotation> annotationType() {
return QualB.class;
}
};
final Collection<AsyncBeanDef<CommonInterface>> beans
= IOC.getAsyncBeanManager().lookupBeans(CommonInterface.class, QualifierUtil.ANY_ANNOTATION);
assertEquals("wrong number of beans", 2, beans.size());
final AsyncBeanDef<CommonInterface> beanA = IOC.getAsyncBeanManager().lookupBean(CommonInterface.class, qualA);
assertNotNull("no bean found", beanA);
beanA.getInstance(new CreationalCallback<CommonInterface>() {
@Override
public void callback(CommonInterface beanInstance) {
assertTrue("wrong bean looked up", beanInstance instanceof QualAppScopeBeanA);
final AsyncBeanDef<CommonInterface> beanB = IOC.getAsyncBeanManager().lookupBean(CommonInterface.class, qualB);
assertNotNull("no bean found", beanB);
beanB.getInstance(new CreationalCallback<CommonInterface>() {
@Override
public void callback(CommonInterface beanInstance) {
assertTrue("wrong bean looked up", beanInstance instanceof QualAppScopeBeanB);
finishTest();
}
});
}
});
}
});
}
public void testQualifierLookupWithAnnoAttrib() {
final QualV qualApples = new QualV() {
@Override
public QualEnum value() {
return QualEnum.APPLES;
}
@Override
public Class<? extends Annotation> annotationType() {
return QualV.class;
}
@Override
public int amount() {
return 5;
}
};
final QualV qualOranges = new QualV() {
@Override
public QualEnum value() {
return QualEnum.ORANGES;
}
@Override
public Class<? extends Annotation> annotationType() {
return QualV.class;
}
@Override
public int amount() {
return 6;
}
};
asyncTest(new Runnable() {
@Override
public void run() {
final Collection<AsyncBeanDef<CommonInterfaceB>> beans
= IOC.getAsyncBeanManager().lookupBeans(CommonInterfaceB.class, QualifierUtil.ANY_ANNOTATION);
assertEquals("wrong number of beans", 2, beans.size());
final AsyncBeanDef<CommonInterfaceB> beanA
= IOC.getAsyncBeanManager().lookupBean(CommonInterfaceB.class, qualApples);
assertNotNull("no bean found", beanA);
beanA.getInstance(new CreationalCallback<CommonInterfaceB>() {
@Override
public void callback(CommonInterfaceB beanInstance) {
assertTrue("wrong bean looked up", beanInstance instanceof QualParmAppScopeBeanApples);
final AsyncBeanDef<CommonInterfaceB> beanB
= IOC.getAsyncBeanManager().lookupBean(CommonInterfaceB.class, qualOranges);
assertNotNull("no bean found", beanB);
beanB.getInstance(new CreationalCallback<CommonInterfaceB>() {
@Override
public void callback(CommonInterfaceB beanInstance) {
assertTrue("wrong bean looked up", beanInstance instanceof QualParmAppScopeBeanOranges);
finishTest();
}
});
}
});
}
});
}
public void testQualifiedLookupFailure() {
asyncTest(new Runnable() {
@Override
public void run() {
final LincolnBar wrongAnno = new LincolnBar() {
@Override
public Class<? extends Annotation> annotationType() {
return LincolnBar.class;
}
};
try {
final AsyncBeanDef<CommonInterface> bean = IOC.getAsyncBeanManager().lookupBean(CommonInterface.class, QualifierUtil.ANY_ANNOTATION);
fail("should have thrown an exception, but got: " + bean);
}
catch (IOCResolutionException e) {
assertTrue("wrong exception thrown: " + e.getMessage(), e.getMessage().contains("Multiple beans matched"));
}
try {
final AsyncBeanDef<CommonInterface> bean = IOC.getAsyncBeanManager().lookupBean(CommonInterface.class, wrongAnno);
fail("should have thrown an exception, but got: " + bean);
}
catch (IOCResolutionException e) {
assertTrue("wrong exception thrown", e.getMessage().contains("No beans matched"));
}
finishTest();
}
});
}
public void testLookupByName() {
asyncTest(new Runnable() {
@Override
public void run() {
final Collection<AsyncBeanDef> beans = IOC.getAsyncBeanManager().lookupBeans("animal");
assertEquals("wrong number of beans", 2, beans.size());
assertTrue("should contain a pig", containsInstanceOf(beans, Pig.class));
assertTrue("should contain a cow", containsInstanceOf(beans, Cow.class));
finishTest();
}
});
}
public void testNameAvailableThroughInterfaceLookup() {
asyncTest(new Runnable() {
@Override
public void run() {
Collection<AsyncBeanDef<CreditCard>> beans = IOC.getAsyncBeanManager().lookupBeans(CreditCard.class);
for (AsyncBeanDef<CreditCard> bean : beans) {
if (bean.getBeanClass().getName().endsWith("Visa")) {
assertEquals("visa", bean.getName());
}
else if (bean.getBeanClass().getName().endsWith("Amex")) {
assertEquals("amex", bean.getName());
}
else {
fail("Unexpected bean was returned from lookup: " + bean);
}
}
finishTest();
}
});
}
public void testNameAvailableThroughConcreteTypeLookup() {
asyncTest(new Runnable() {
@Override
public void run() {
Collection<AsyncBeanDef<Visa>> beans = IOC.getAsyncBeanManager().lookupBeans(Visa.class);
for (AsyncBeanDef<Visa> bean : beans) {
assertNotNull("Missing name on " + bean, bean.getName());
}
finishTest();
}
});
}
public void testLookupAllBeans() {
asyncTest(new Runnable() {
@Override
public void run() {
final Collection<AsyncBeanDef<Object>> beans = IOC.getAsyncBeanManager().lookupBeans(Object.class);
assertTrue(!beans.isEmpty());
finishTest();
}
});
}
private final QualA QUAL_A = new QualA() {
@Override
public Class<? extends Annotation> annotationType() {
return QualA.class;
}
};
public void testLookupAllBeansQualified() {
asyncTest(new Runnable() {
@Override
public void run() {
final Collection<AsyncBeanDef<Object>> beans
= IOC.getAsyncBeanManager().lookupBeans(Object.class, QUAL_A);
assertEquals("Unexpected number of beans matched. Actual results: " + beans, 1, beans.size());
assertEquals(QualAppScopeBeanA.class, beans.iterator().next().getBeanClass());
finishTest();
}
});
}
public void testReportedScopeCorrect() {
asyncTest(new Runnable() {
@Override
public void run() {
final AsyncBeanDef<ApplicationScopedBean> appScopeBean
= IOC.getAsyncBeanManager().lookupBean(ApplicationScopedBean.class);
final AsyncBeanDef<DependentScopedBean> dependentIOCBean
= IOC.getAsyncBeanManager().lookupBean(DependentScopedBean.class);
assertEquals(ApplicationScoped.class, appScopeBean.getScope());
assertEquals(Dependent.class, dependentIOCBean.getScope());
finishTest();
}
});
}
public void testAddingProgrammaticDestructionCallback() {
asyncTest(new Runnable() {
@Override
public void run() {
IOC.getAsyncBeanManager().lookupBean(DependentScopedBean.class)
.newInstance(new CreationalCallback<DependentScopedBean>() {
@Override
public void callback(DependentScopedBean beanInstance) {
class TestValueHolder {
boolean destroyed = false;
}
final TestValueHolder testValueHolder = new TestValueHolder();
IOC.getAsyncBeanManager()
.addDestructionCallback(beanInstance, new DestructionCallback<Object>() {
@Override
public void destroy(Object bean) {
testValueHolder.destroyed = true;
}
});
IOC.getAsyncBeanManager().destroyBean(beanInstance);
assertEquals(true, testValueHolder.destroyed);
finishTest();
}
});
}
});
}
/**
* Tests that beans marked as Dependent scoped by an IOCExtension can still be forced into a different scope (in this
* case, ApplicationScoped) when they are annotated as such.
* <p/>
* Besides this being a good idea on its own, both Errai UI Templates and Errai Navigation rely on this behaviour.
* <p/>
* NOTE: This looks really crazy written as an asynchronous test.
*/
public void testNormalScopeOverridesDependent() {
asyncTest(new Runnable() {
@Override
public void run() {
final AsyncBeanQuery beanQuery = new AsyncBeanQuery();
final AsyncBeanFuture<FoobieScopedBean> foobieScopedFuture1 = beanQuery.load(FoobieScopedBean.class);
final AsyncBeanFuture<FoobieScopedBean> foobieScopedFuture2 = beanQuery.load(FoobieScopedBean.class);
final AsyncBeanFuture<FoobieScopedOverriddenBean> foobieScopedOverriddenFuture1
= beanQuery.load(FoobieScopedOverriddenBean.class);
final AsyncBeanFuture<FoobieScopedOverriddenBean> foobieScopedOverriddenFuture2
= beanQuery.load(FoobieScopedOverriddenBean.class);
beanQuery.query(new Runnable() {
@Override
public void run() {
final FoobieScopedBean foobieScopedBean1 = foobieScopedFuture1.get();
final FoobieScopedBean foobieScopedBean2 = foobieScopedFuture2.get();
final FoobieScopedOverriddenBean foobieScopedOverriddenBean1 = foobieScopedOverriddenFuture1.get();
final FoobieScopedOverriddenBean foobieScopedOverriddenBean2 = foobieScopedOverriddenFuture2.get();
assertNotNull(foobieScopedBean1);
assertNotSame(foobieScopedBean1, foobieScopedBean2);
assertNotNull(foobieScopedOverriddenBean1);
assertSame(foobieScopedOverriddenBean1, foobieScopedOverriddenBean2);
finishTest();
}
});
}
});
}
public void testBeanActivator() {
final RefHolder<TestBeanActivator> activatorRef = new RefHolder<TestBeanActivator>();
// This will happen synchronously as BeanActivators can not be annotated @LoadAsync
IOC.getAsyncBeanManager().lookupBean(TestBeanActivator.class)
.getInstance(new CreationalCallback<TestBeanActivator>() {
@Override
public void callback(TestBeanActivator beanInstance) {
activatorRef.set(beanInstance);
}
});
TestBeanActivator activator = activatorRef.get();
activator.setActived(true);
AsyncBeanDef<ActivatedBean> bean = IOC.getAsyncBeanManager().lookupBean(ActivatedBean.class);
assertTrue(bean.isActivated());
activator.setActived(false);
assertFalse(bean.isActivated());
AsyncBeanDef<ActivatedBeanInterface> qualifiedBean = IOC.getAsyncBeanManager().lookupBean(ActivatedBeanInterface.class);
assertFalse(qualifiedBean.isActivated());
activator.setActived(true);
assertTrue(qualifiedBean.isActivated());
}
public void testBeanActiveByDefault() {
AsyncBeanDef<DependentScopedBean> bean = IOC.getAsyncBeanManager().lookupBean(DependentScopedBean.class);
assertTrue(bean.isActivated());
}
public void testInjectInstanceForSyncTypeWithAsyncEnabled() throws Exception {
final ViaInstanceModule module = IOC.getBeanManager().lookupBean(ViaInstanceModule.class).getInstance();
try {
module.syncViaInstance.get();
} catch (Throwable t) {
throw new AssertionError("Should be able to get sync instance via Instance.get()", t);
}
}
public void testErrorWhenUsingInstanceWithAsyncType() throws Exception {
final ViaInstanceModule module = IOC.getBeanManager().lookupBean(ViaInstanceModule.class).getInstance();
assertTrue("Precondition failed: This test must run with the async bean manager.", GWT.<IOCEnvironment>create(IOCEnvironment.class).isAsync());
try {
module.asyncViaInstance.get();
fail("Should have failed from an unsatisfied dependency exception.");
} catch (AssertionError ae) {
throw ae;
} catch (Throwable t) {
assertTrue("The exception thrown did not have the @LoadAsync hint. Observed: " + t.getMessage(), t.getMessage().contains("@LoadAsync"));
}
}
private static boolean containsInstanceOf(final Collection<AsyncBeanDef> defs, final Class<?> clazz) {
for (final AsyncBeanDef def : defs) {
if (def.getType().equals(clazz)) return true;
}
return false;
}
}