/* * 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; } }