/* * Copyright (C) 2015 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.injection.client.test; import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Set; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.Dependent; import javax.enterprise.inject.Any; import org.jboss.errai.cdi.injection.client.AbstractBean; import org.jboss.errai.cdi.injection.client.ApplicationScopedBean; import org.jboss.errai.cdi.injection.client.CommonInterface; import org.jboss.errai.cdi.injection.client.CommonInterfaceB; import org.jboss.errai.cdi.injection.client.Cow; import org.jboss.errai.cdi.injection.client.CreditCard; import org.jboss.errai.cdi.injection.client.DependentScopedBean; import org.jboss.errai.cdi.injection.client.DependentScopedBeanWithDependencies; import org.jboss.errai.cdi.injection.client.DisabledAlternativeBean; import org.jboss.errai.cdi.injection.client.FoobieScopedBean; import org.jboss.errai.cdi.injection.client.FoobieScopedOverriddenBean; import org.jboss.errai.cdi.injection.client.InheritedApplicationScopedBean; import org.jboss.errai.cdi.injection.client.InheritedFromAbstractBean; import org.jboss.errai.cdi.injection.client.InterfaceA; import org.jboss.errai.cdi.injection.client.InterfaceB; import org.jboss.errai.cdi.injection.client.InterfaceC; import org.jboss.errai.cdi.injection.client.InterfaceD; import org.jboss.errai.cdi.injection.client.InterfaceRoot; import org.jboss.errai.cdi.injection.client.InterfaceWithNamedImpls; import org.jboss.errai.cdi.injection.client.OuterBeanInterface; import org.jboss.errai.cdi.injection.client.Pig; import org.jboss.errai.cdi.injection.client.Visa; import org.jboss.errai.cdi.injection.client.qualifier.LincolnBar; import org.jboss.errai.cdi.injection.client.qualifier.QualA; import org.jboss.errai.cdi.injection.client.qualifier.QualAppScopeBeanA; import org.jboss.errai.cdi.injection.client.qualifier.QualAppScopeBeanB; import org.jboss.errai.cdi.injection.client.qualifier.QualB; import org.jboss.errai.cdi.injection.client.qualifier.QualEnum; import org.jboss.errai.cdi.injection.client.qualifier.QualParmAppScopeBeanApples; import org.jboss.errai.cdi.injection.client.qualifier.QualParmAppScopeBeanOranges; import org.jboss.errai.cdi.injection.client.qualifier.QualV; import org.jboss.errai.enterprise.client.cdi.AbstractErraiCDITest; import org.jboss.errai.ioc.client.container.DestructionCallback; import org.jboss.errai.ioc.client.container.IOC; import org.jboss.errai.ioc.client.container.IOCResolutionException; import org.jboss.errai.ioc.client.container.SyncBeanDef; import org.jboss.errai.ioc.client.container.SyncBeanManager; /** * @author Mike Brock * @author Christian Sadilek <csadilek@redhat.com> */ public class BeanManagerIntegrationTest extends AbstractErraiCDITest { { disableBus = true; } private final QualA QUAL_A = new QualA() { @Override public Class<? extends Annotation> annotationType() { return QualA.class; } }; private final Any anyAnno = new Any() { @Override public Class<? extends Annotation> annotationType() { return Any.class; } }; @Override public String getModuleName() { return "org.jboss.errai.cdi.injection.InjectionTestModule"; } public void testBeanManagerLookupSimple() { final SyncBeanDef<ApplicationScopedBean> bean = IOC.getBeanManager().lookupBean(ApplicationScopedBean.class); assertNotNull(bean); } public void testBeanManagerLookupInheritedScopeBean() { final SyncBeanDef<InheritedApplicationScopedBean> bean = IOC.getBeanManager().lookupBean(InheritedApplicationScopedBean.class, anyAnno); assertNotNull("inherited application scoped bean did not lookup", bean); final InheritedApplicationScopedBean beanInst = bean.getInstance(); 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); final InheritedApplicationScopedBean beanInst2 = bean.getInstance(); assertSame("bean is not observing application scope", beanInst, beanInst2); } public void testBeanManagerLookupBeanFromAbstractRootType() { final SyncBeanDef<AbstractBean> bean = IOC.getBeanManager().lookupBean(AbstractBean.class); assertNotNull("did not find any beans matching", bean); final AbstractBean beanInst = bean.getInstance(); assertNotNull("bean instance is null", beanInst); assertTrue("bean is incorrect instance: " + beanInst.getClass(), beanInst instanceof InheritedFromAbstractBean); } /** * This test effectively tests that the IOC container comprehends the full type hierarchy, considering both supertypes * and transverse interface types. */ public void testBeanManagerLookupForOuterInterfaceRootType() { final SyncBeanDef<OuterBeanInterface> bean = IOC.getBeanManager().lookupBean(OuterBeanInterface.class); assertNotNull("did not find any beans matching", bean); final OuterBeanInterface beanInst = bean.getInstance(); assertNotNull("bean instance is null", beanInst); assertTrue("bean is incorrect instance: " + beanInst.getClass(), beanInst instanceof InheritedFromAbstractBean); } public void testBeanManagerLookupForOuterInterfacesOfNonAbstractType() { final SyncBeanDef<InterfaceC> beanC = IOC.getBeanManager().lookupBean(InterfaceC.class); assertNotNull("did not find any beans matching", beanC); final SyncBeanDef<InterfaceD> beanD = IOC.getBeanManager().lookupBean(InterfaceD.class); assertNotNull("did not find any beans matching", beanD); } public void testBeanManagerLookupForExtendedInterfaceType() { // This should find ApplicationScopedBeanA, ApplicationScopedBeanB and ApplicationScopedBeanC final Collection<SyncBeanDef<InterfaceRoot>> beans = IOC.getBeanManager().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<SyncBeanDef<InterfaceA>> beansB = IOC.getBeanManager().lookupBeans(InterfaceA.class); assertEquals("did not find both managed implementations of " + InterfaceA.class.getName(), 2, beansB.size()); // This should find only ApplicationScopedBeanB final Collection<SyncBeanDef<InterfaceB>> beansC = IOC.getBeanManager().lookupBeans(InterfaceB.class); assertEquals("did not find exactly one managed implementation of " + InterfaceB.class.getName(), 1, beansC.size()); } public void testBeanManagerAPIs() { final SyncBeanManager mgr = IOC.getBeanManager(); final SyncBeanDef<QualAppScopeBeanA> bean = mgr.lookupBean(QualAppScopeBeanA.class, anyAnno); final Set<Annotation> a = bean.getQualifiers(); assertEquals("there should be two qualifiers", 2, a.size()); assertTrue("wrong qualifiers", annotationSetMatches(a, QualA.class, Any.class)); } public void testQualifiedLookup() { 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<SyncBeanDef<CommonInterface>> beans = IOC.getBeanManager().lookupBeans(CommonInterface.class); assertEquals("wrong number of beans", 2, beans.size()); final SyncBeanDef<CommonInterface> beanA = IOC.getBeanManager().lookupBean(CommonInterface.class, qualA); assertNotNull("no bean found", beanA); assertTrue("wrong bean looked up", beanA.getInstance() instanceof QualAppScopeBeanA); final SyncBeanDef<CommonInterface> beanB = IOC.getBeanManager().lookupBean(CommonInterface.class, qualB); assertNotNull("no bean found", beanB); assertTrue("wrong bean looked up", beanB.getInstance() instanceof QualAppScopeBeanB); } 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; } }; final Collection<SyncBeanDef<CommonInterfaceB>> beans = IOC.getBeanManager().lookupBeans(CommonInterfaceB.class); assertEquals("wrong number of beans", 2, beans.size()); final SyncBeanDef<CommonInterfaceB> beanA = IOC.getBeanManager().lookupBean(CommonInterfaceB.class, qualApples); assertNotNull("no bean found", beanA); assertTrue("wrong bean looked up", beanA.getInstance() instanceof QualParmAppScopeBeanApples); final SyncBeanDef<CommonInterfaceB> beanB = IOC.getBeanManager().lookupBean(CommonInterfaceB.class, qualOranges); assertNotNull("no bean found", beanB); assertTrue("wrong bean looked up", beanB.getInstance() instanceof QualParmAppScopeBeanOranges); } public void testQualifierLookupWithAnnoAttribFailure() { final QualV qualOrange = new QualV() { @Override public QualEnum value() { return QualEnum.ORANGES; } @Override public Class<? extends Annotation> annotationType() { return QualV.class; } @Override public int amount() { return 5; } }; final QualV qualApple = new QualV() { @Override public QualEnum value() { return QualEnum.APPLES; } @Override public Class<? extends Annotation> annotationType() { return QualV.class; } @Override public int amount() { return 6; } }; final Collection<SyncBeanDef<CommonInterfaceB>> beans = IOC.getBeanManager().lookupBeans(CommonInterfaceB.class); assertEquals("wrong number of beans", 2, beans.size()); final SyncBeanDef<CommonInterfaceB> beanA = IOC.getBeanManager().lookupBean(CommonInterfaceB.class, qualOrange); assertNotNull("no bean found", beanA); assertFalse("wrong bean looked up", beanA.getInstance() instanceof QualParmAppScopeBeanApples); final SyncBeanDef<CommonInterfaceB> beanB = IOC.getBeanManager().lookupBean(CommonInterfaceB.class, qualApple); assertNotNull("no bean found", beanB); assertFalse("wrong bean looked up", beanB.getInstance() instanceof QualParmAppScopeBeanOranges); } public void testQualifiedLookupFailure() { final LincolnBar wrongAnno = new LincolnBar() { @Override public Class<? extends Annotation> annotationType() { return LincolnBar.class; } }; try { final SyncBeanDef<CommonInterface> bean = IOC.getBeanManager().lookupBean(CommonInterface.class, anyAnno); fail("should have thrown an exception, but got: " + bean); } catch (final IOCResolutionException e) { assertTrue("wrong exception thrown: " + e.getMessage(), e.getMessage().contains("Multiple beans matched")); } try { final SyncBeanDef<CommonInterface> bean = IOC.getBeanManager().lookupBean(CommonInterface.class, wrongAnno); fail("should have thrown an exception, but got: " + bean); } catch (final IOCResolutionException e) { assertTrue("wrong exception thrown: " + e.getMessage(), e.getMessage().contains("No beans matched")); } } public void testLookupByName() { final Collection<SyncBeanDef> beans = IOC.getBeanManager().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)); for (final SyncBeanDef<?> bean : beans) { assertEquals("animal", bean.getName()); } } public void testNameAvailableThroughInterfaceLookup() { final Collection<SyncBeanDef<CreditCard>> beans = IOC.getBeanManager().lookupBeans(CreditCard.class); for (final SyncBeanDef<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); } } } public void testNameAvailableThroughConcreteTypeLookup() { final Collection<SyncBeanDef<Visa>> beans = IOC.getBeanManager().lookupBeans(Visa.class); for (final SyncBeanDef<Visa> bean : beans) { assertNotNull("Missing name on " + bean, bean.getName()); } } public void testLookupAllBeans() { final Collection<SyncBeanDef<Object>> beans = IOC.getBeanManager().lookupBeans(Object.class); assertTrue(!beans.isEmpty()); } public void testLookupAllBeansQualified() { final Collection<SyncBeanDef<Object>> beans = IOC.getBeanManager().lookupBeans(Object.class, QUAL_A); assertEquals("Expected one bean but found multiple: " + beans.toString(), 1, beans.size()); assertEquals(QualAppScopeBeanA.class, beans.iterator().next().getBeanClass()); } public void testReportedScopeCorrect() { final SyncBeanDef<ApplicationScopedBean> appScopeBean = IOC.getBeanManager().lookupBean(ApplicationScopedBean.class); final SyncBeanDef<DependentScopedBean> dependentIOCBean = IOC.getBeanManager().lookupBean(DependentScopedBean.class); assertEquals(ApplicationScoped.class, appScopeBean.getScope()); assertEquals(Dependent.class, dependentIOCBean.getScope()); } public void testAddingProgrammaticDestructionCallback() { final DependentScopedBean dependentScopedBean = IOC.getBeanManager().lookupBean(DependentScopedBean.class).newInstance(); class TestValueHolder { boolean destroyed = false; } final TestValueHolder testValueHolder = new TestValueHolder(); IOC.getBeanManager().addDestructionCallback(dependentScopedBean, new DestructionCallback<Object>() { @Override public void destroy(final Object bean) { testValueHolder.destroyed = true; } }); IOC.getBeanManager().destroyBean(dependentScopedBean); assertEquals(true, testValueHolder.destroyed); } /** * 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. */ public void testNormalScopeOverridesDependent() { final FoobieScopedBean foobieScopedBean1 = IOC.getBeanManager().lookupBean(FoobieScopedBean.class).getInstance(); final FoobieScopedBean foobieScopedBean2 = IOC.getBeanManager().lookupBean(FoobieScopedBean.class).getInstance(); assertNotNull(foobieScopedBean1); assertNotSame(foobieScopedBean1, foobieScopedBean2); final FoobieScopedOverriddenBean foobieScopedOverriddenBean1 = IOC.getBeanManager().lookupBean(FoobieScopedOverriddenBean.class).getInstance(); final FoobieScopedOverriddenBean foobieScopedOverriddenBean2 = IOC.getBeanManager().lookupBean(FoobieScopedOverriddenBean.class).getInstance(); assertNotNull(foobieScopedOverriddenBean1); assertSame(foobieScopedOverriddenBean1, foobieScopedOverriddenBean2); } public void testProgrammaticlyRegisteredBeansAreLookedUp() { final SyncBeanManager bm = IOC.getBeanManager(); assertEquals("The disabled alternative must not be in the bean manager before being programmatically added.", 0, bm.lookupBeans(DisabledAlternativeBean.class).size()); bm.registerBean(new SyncBeanDef<DisabledAlternativeBean>() { @Override public Class<DisabledAlternativeBean> getType() { return DisabledAlternativeBean.class; } @Override public Class<?> getBeanClass() { return DisabledAlternativeBean.class; } @Override public Class<? extends Annotation> getScope() { return Dependent.class; } @Override public DisabledAlternativeBean getInstance() { return new DisabledAlternativeBean(); } @Override public DisabledAlternativeBean newInstance() { return new DisabledAlternativeBean(); } @Override public Set<Annotation> getQualifiers() { return Collections.emptySet(); } @Override public boolean matches(final Set<Annotation> annotations) { return true; } @Override public String getName() { return "Name of DisabledAlternative"; } @Override public boolean isActivated() { return true; } @Override public boolean isAssignableTo(final Class<?> type) { return Arrays.asList(Object.class, DisabledAlternativeBean.class).contains(type); } }); assertEquals("Failed to lookup programmatically added bean by type.", 1, bm.lookupBeans(DisabledAlternativeBean.class).size()); assertEquals("Failed to lookup programmatically added bean by name.", 1, bm.lookupBeans("Name of DisabledAlternative").size()); } public void testLookupByNameDoesNotFindOtherBeansOfSameType() throws Exception { final SyncBeanManager beanManager = IOC.getBeanManager(); beanManager.registerBean(new SyncBeanDef<InterfaceWithNamedImpls>() { @Override public Class<InterfaceWithNamedImpls> getType() { return InterfaceWithNamedImpls.class; } @Override public Class<?> getBeanClass() { return InterfaceWithNamedImpls.class; } @Override public Class<? extends Annotation> getScope() { return Dependent.class; } @Override public Set<Annotation> getQualifiers() { return Collections.emptySet(); } @Override public boolean matches(final Set<Annotation> annotations) { return true; } @Override public String getName() { return "Programmatic"; } @Override public boolean isActivated() { return true; } @Override public InterfaceWithNamedImpls getInstance() { return null; } @Override public InterfaceWithNamedImpls newInstance() { return null; } @Override public boolean isAssignableTo(final Class<?> type) { return Arrays.asList(Object.class, InterfaceWithNamedImpls.class).contains(type); } }); assertEquals("Found multiple beans looking up @Named bean.", 1, beanManager.lookupBeans("Named").size()); assertEquals("Found multiple beans looking up programmatically registered bean by name.", 1, beanManager.lookupBeans("Programmatic").size()); } private static boolean containsInstanceOf(final Collection<SyncBeanDef> defs, final Class<?> clazz) { for (final SyncBeanDef def : defs) { if (def.getType().equals(clazz)) return true; } return false; } }