| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.webbeans.container; |
| |
| import org.apache.webbeans.annotation.DefaultLiteral; |
| import org.apache.webbeans.component.AbstractOwbBean; |
| import org.apache.webbeans.component.AbstractProducerBean; |
| import org.apache.webbeans.component.ManagedBean; |
| import org.apache.webbeans.component.OwbBean; |
| import org.apache.webbeans.config.WebBeansContext; |
| import org.apache.webbeans.exception.WebBeansConfigurationException; |
| import org.apache.webbeans.exception.WebBeansDeploymentException; |
| import org.apache.webbeans.inject.AlternativesManager; |
| import org.apache.webbeans.logger.WebBeansLoggerFacade; |
| import org.apache.webbeans.spi.BDABeansXmlScanner; |
| import org.apache.webbeans.spi.ScannerService; |
| import org.apache.webbeans.util.AnnotationUtil; |
| import org.apache.webbeans.util.Asserts; |
| import org.apache.webbeans.util.ClassUtil; |
| import org.apache.webbeans.util.GenericsUtil; |
| import org.apache.webbeans.util.InjectionExceptionUtil; |
| import org.apache.webbeans.util.SingleItemSet; |
| import org.apache.webbeans.util.WebBeansUtil; |
| |
| import jakarta.enterprise.event.Event; |
| import jakarta.enterprise.inject.Instance; |
| import jakarta.enterprise.inject.UnproxyableResolutionException; |
| import jakarta.enterprise.inject.spi.AnnotatedType; |
| import jakarta.enterprise.inject.spi.Bean; |
| import jakarta.enterprise.inject.spi.InjectionPoint; |
| import java.lang.annotation.Annotation; |
| import java.lang.reflect.GenericArrayType; |
| import java.lang.reflect.ParameterizedType; |
| import java.lang.reflect.Type; |
| import java.lang.reflect.TypeVariable; |
| import java.lang.reflect.WildcardType; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| import static org.apache.webbeans.util.InjectionExceptionUtil.throwAmbiguousResolutionException; |
| |
| /** |
| * Injection point resolver class. |
| * <p> |
| * It is a singleton class per BeanManager. It is |
| * responsible for resolving the bean instances at the injection points for |
| * its bean manager. |
| * </p> |
| * |
| * @version $Rev$ $Date$ |
| */ |
| public class InjectionResolver |
| { |
| private static final Logger logger = WebBeansLoggerFacade.getLogger(InjectionResolver.class); |
| |
| /** |
| * Bean Manager |
| */ |
| private WebBeansContext webBeansContext; |
| |
| private AlternativesManager alternativesManager; |
| |
| /** |
| * Resolved beans by injection point type and qualifiers ({@link BeanCacheKey}). |
| * Filled after startup when the bean set is final. Misses are stored as |
| * {@link Collections#emptySet()} so repeated lookups avoid rescanning the deployment. |
| */ |
| private Map<BeanCacheKey, Set<Bean<?>>> resolvedBeansByType = new ConcurrentHashMap<>(); |
| |
| /** |
| * This Map contains all resolved beans via it's ExpressionLanguage name. |
| */ |
| private Map<String, Set<Bean<?>>> resolvedBeansByName = new ConcurrentHashMap<>(); |
| |
| /** |
| * Index of all beans by the raw {@link Class} of each of their (api) types. |
| * Built lazily once the bean set is final (post startup) so type-based resolution can look up |
| * candidate beans instead of linearly scanning the whole deployment. Reset in {@link #clearCaches()}. |
| */ |
| private volatile Map<Class<?>, Set<Bean<?>>> beansByRawType; |
| |
| /** |
| * Whether the container is in startup mode. |
| * Set to {@code false} immediately before the BeforeDeploymentValidation event gets fired. |
| */ |
| private boolean startup; |
| |
| private boolean fastMatching; |
| |
| private Bean<Instance<Object>> instanceBean; |
| private Bean<Event<Object>> eventBean; |
| |
| /** |
| * Creates a new injection resolve for given bean manager. |
| * |
| * @param webBeansContext WebBeansContext |
| */ |
| public InjectionResolver(WebBeansContext webBeansContext) |
| { |
| this.webBeansContext = webBeansContext; |
| alternativesManager = webBeansContext.getAlternativesManager(); |
| startup = true; |
| fastMatching = false; |
| instanceBean = webBeansContext.getWebBeansUtil().getInstanceBean(); |
| eventBean = webBeansContext.getWebBeansUtil().getEventBean(); |
| } |
| |
| public void setFastMatching(boolean fastMatching) |
| { |
| this.fastMatching = fastMatching; |
| } |
| |
| public void setStartup(boolean startup) |
| { |
| this.startup = startup; |
| } |
| |
| /** |
| * Clear caches. |
| */ |
| public void clearCaches() |
| { |
| resolvedBeansByName.clear(); |
| resolvedBeansByType.clear(); |
| beansByRawType = null; |
| } |
| |
| /** |
| * Lazily built index mapping the raw {@link Class} of every bean api type to the beans carrying it. |
| * Only used once the bean set is final (post startup); it mirrors exactly which beans the linear |
| * fast-matching scan would find via {@link ClassUtil#isRawClassEquals(Type, Type)}. |
| */ |
| private Map<Class<?>, Set<Bean<?>>> getBeansByRawType() |
| { |
| Map<Class<?>, Set<Bean<?>>> index = beansByRawType; |
| if (index == null) |
| { |
| synchronized (this) |
| { |
| index = beansByRawType; |
| if (index == null) |
| { |
| index = new HashMap<>(); |
| for (Bean<?> bean : webBeansContext.getBeanManagerImpl().getBeans()) |
| { |
| for (Type apiType : bean.getTypes()) |
| { |
| Class<?> rawType = ClassUtil.getRawPrimitiveType(apiType); |
| if (rawType != null) |
| { |
| index.computeIfAbsent(rawType, k -> new HashSet<>()).add(bean); |
| } |
| } |
| } |
| beansByRawType = index; |
| } |
| } |
| } |
| return index; |
| } |
| |
| /** |
| * Check the type of the injection point. |
| * <p> |
| * Injection point type can not be {@link java.lang.reflect.TypeVariable}. |
| * </p> |
| * |
| * @param injectionPoint injection point |
| * @throws WebBeansConfigurationException if not obey the rule |
| */ |
| public void checkInjectionPointType(InjectionPoint injectionPoint) |
| { |
| Type type = injectionPoint.getType(); |
| |
| //Check for injection point type variable |
| if (ClassUtil.isTypeVariable(type)) |
| { |
| throw new WebBeansConfigurationException("Injection point type : " + injectionPoint + " can not define Type Variable generic type"); |
| } |
| |
| //Check for raw event type (10.3.2) |
| if (type == Event.class) |
| { |
| throw new WebBeansConfigurationException("Injection point type : " + injectionPoint + " needs to define type argument for " + Event.class.getName()); |
| } |
| if (type == Instance.class) |
| { |
| throw new WebBeansConfigurationException("Injection point type : " + injectionPoint + " needs to define type argument for " + Instance.class.getName()); |
| } |
| |
| // not that happy about this check here and at runtime but few TCKs test Weld behavior only... |
| Bean<?> bean = resolve(implResolveByType(false, type, injectionPoint.getQualifiers().toArray(new Annotation[0])), |
| injectionPoint); |
| if (bean != null && ManagedBean.class.isInstance(bean)) |
| { |
| try |
| { |
| ManagedBean.class.cast(bean).valid(); |
| } |
| catch (UnproxyableResolutionException ure) |
| { |
| throw new WebBeansDeploymentException(ure); |
| } |
| } |
| } |
| |
| /** |
| * Check that a valid enabled bean exists in the deployment for the given |
| * injection point definition. |
| * |
| * @param injectionPoint injection point |
| * @throws WebBeansConfigurationException If bean is not available in the current deployment for given injection |
| */ |
| public void checkInjectionPoint(InjectionPoint injectionPoint) |
| { |
| WebBeansUtil.checkInjectionPointNamedQualifier(injectionPoint); |
| |
| Type type = injectionPoint.getType(); |
| if (ClassUtil.isTypeVariable(type)) |
| { |
| throw new WebBeansConfigurationException("Injection point type : " + injectionPoint + " type can not be defined as Typevariable or Wildcard type!"); |
| } |
| |
| if (webBeansContext.getBeanManagerImpl().isAfterBeanDiscoveryDone()) |
| { |
| Annotation[] qualifiers = new Annotation[injectionPoint.getQualifiers().size()]; |
| qualifiers = injectionPoint.getQualifiers().toArray(qualifiers); |
| |
| // OWB-890 some 3rd party InjectionPoints return null in getBean(); |
| Class<?> injectionPointClass = Object.class; // the fallback |
| Bean injectionPointBean = injectionPoint.getBean(); |
| if (injectionPointBean != null) |
| { |
| injectionPointClass = injectionPointBean.getBeanClass(); |
| } |
| if (injectionPointClass == null && type instanceof Class) |
| { |
| injectionPointClass = (Class) type; |
| } |
| |
| Set<Bean<?>> beanSet = implResolveByType(injectionPoint.isDelegate(), type, injectionPointClass, qualifiers); |
| |
| Bean<?> bean = resolve(beanSet, injectionPoint); |
| |
| if (bean == null) |
| { |
| Class<?> clazz; |
| if (type instanceof ParameterizedType) |
| { |
| ParameterizedType pt = (ParameterizedType) type; |
| |
| clazz = (Class<?>) pt.getRawType(); |
| } |
| else |
| { |
| clazz = (Class<?>) type; |
| } |
| InjectionExceptionUtil.throwUnsatisfiedResolutionException(clazz, injectionPoint, qualifiers); |
| } |
| } |
| } |
| |
| |
| /** |
| * Returns bean for injection point. |
| * |
| * @param injectionPoint injection point declaration |
| * @return bean for injection point |
| */ |
| public Bean<?> getInjectionPointBean(InjectionPoint injectionPoint) |
| { |
| |
| Type type = injectionPoint.getType(); |
| Class<?> clazz; |
| |
| if (type instanceof ParameterizedType) |
| { |
| ParameterizedType pt = (ParameterizedType) type; |
| clazz = (Class<?>) pt.getRawType(); |
| } |
| else |
| { |
| clazz = (Class<?>) type; |
| } |
| |
| Set<Annotation> qualSet = injectionPoint.getQualifiers(); |
| Annotation[] qualifiers = qualSet.toArray(new Annotation[0]); |
| |
| Set<Bean<?>> beanSet = implResolveByType(injectionPoint.isDelegate(), type, clazz, qualifiers); |
| |
| if (beanSet.isEmpty()) |
| { |
| InjectionExceptionUtil.throwUnsatisfiedResolutionException(clazz, injectionPoint, qualifiers); |
| } |
| |
| return resolve(beanSet, injectionPoint); |
| } |
| |
| private Bean<?> getInstanceOrEventInjectionBean(Type type) |
| { |
| Class<?> clazz; |
| if (type instanceof ParameterizedType) |
| { |
| ParameterizedType pt = (ParameterizedType) type; |
| clazz = (Class<?>) pt.getRawType(); |
| |
| if (clazz.isAssignableFrom(Instance.class)) |
| { |
| return instanceBean; |
| } |
| if (clazz.isAssignableFrom(Event.class)) |
| { |
| return eventBean; |
| } |
| } |
| |
| return null; |
| } |
| |
| |
| /** |
| * Returns set of beans for given bean name. |
| * |
| * @param name bean name |
| * @return set of beans for given bean name |
| */ |
| @SuppressWarnings("unchecked") |
| public Set<Bean<?>> implResolveByName(String name) |
| { |
| Asserts.assertNotNull(name, "name parameter"); |
| |
| String cacheKey = name; |
| Set<Bean<?>> resolvedComponents = resolvedBeansByName.get(cacheKey); |
| if (resolvedComponents != null) |
| { |
| return resolvedComponents; |
| } |
| |
| resolvedComponents = new HashSet<>(); |
| Set<Bean<?>> deployedComponents = webBeansContext.getBeanManagerImpl().getBeans(); |
| |
| //Finding all beans with given name |
| for (Bean<?> component : deployedComponents) |
| { |
| if (component.getName() != null) |
| { |
| if (component.getName().equals(name)) |
| { |
| resolvedComponents.add(component); |
| } |
| } |
| } |
| |
| if (resolvedComponents.isEmpty()) |
| { |
| // maintain negative cache but use standard empty set so we can garbage collect |
| resolvedBeansByName.put(cacheKey, Collections.EMPTY_SET); |
| } |
| else |
| { |
| resolvedBeansByName.put(cacheKey, resolvedComponents); |
| } |
| if (logger.isLoggable(Level.FINE)) |
| { |
| logger.log(Level.FINE, "DEBUG_ADD_BYNAME_CACHE_BEANS", cacheKey); |
| } |
| |
| return resolvedComponents; |
| } |
| |
| |
| /** |
| * Resolution by type. |
| * |
| * @param isDelegate whether the InjectionPoint is for a {@link jakarta.decorator.Delegate} |
| * @param injectionPointType injection point api type |
| * @param qualifiers qualifiers of the injection point |
| * @return set of resolved beans |
| */ |
| public Set<Bean<?>> implResolveByType(boolean isDelegate, Type injectionPointType, Annotation... qualifiers) |
| { |
| return implResolveByType(isDelegate, injectionPointType, null, qualifiers); |
| } |
| |
| private String getBDABeansXMLPath(Class<?> injectionPointBeanClass) |
| { |
| if (injectionPointBeanClass == null) |
| { |
| return null; |
| } |
| |
| ScannerService scannerService = webBeansContext.getScannerService(); |
| BDABeansXmlScanner beansXMLScanner = scannerService.getBDABeansXmlScanner(); |
| return beansXMLScanner.getBeansXml(injectionPointBeanClass); |
| } |
| |
| /** |
| * Resolution by type. |
| * |
| * @param isDelegate whether the InjectionPoint is for a {@link jakarta.decorator.Delegate} |
| * @param injectionPointType injection point api type |
| * @param qualifiers qualifiers of the injection point |
| * @return set of resolved beans |
| */ |
| public Set<Bean<?>> implResolveByType(boolean isDelegate, Type injectionPointType, |
| Class<?> injectionPointClass, Annotation... qualifiers) |
| { |
| ScannerService scannerService = webBeansContext.getScannerService(); |
| String bdaBeansXMLFilePath = null; |
| if (scannerService.isBDABeansXmlScanningEnabled()) |
| { |
| bdaBeansXMLFilePath = getBDABeansXMLPath(injectionPointClass); |
| } |
| |
| boolean currentQualifier = false; |
| |
| if (qualifiers.length == 0) |
| { |
| qualifiers = DefaultLiteral.ARRAY; |
| currentQualifier = true; |
| } |
| |
| Set<Bean<?>> resolvedComponents; |
| BeanCacheKey cacheKey = null; |
| |
| if (!startup) |
| { |
| // we only cache and validate once the set of Beans is final, otherwise we would cache crap |
| validateInjectionPointType(injectionPointType); |
| |
| cacheKey = new BeanCacheKey(isDelegate, injectionPointType, bdaBeansXMLFilePath, this::findQualifierModel, qualifiers); |
| |
| resolvedComponents = resolvedBeansByType.get(cacheKey); |
| if (resolvedComponents != null) |
| { |
| return resolvedComponents; |
| } |
| } |
| |
| resolvedComponents = new HashSet<>(); |
| HashMap<Type, Integer> visitedForGenerics = new HashMap<>(); |
| |
| boolean returnAll = injectionPointType.equals(Object.class) && currentQualifier; |
| |
| // Once the bean set is final (post startup) the fast-matching scan is a pure raw-type filter, |
| // so we can look up the candidate beans in the index instead of scanning the whole deployment. |
| if (fastMatching && !startup && !returnAll) |
| { |
| Class<?> ipRawType = ClassUtil.getRawPrimitiveType(injectionPointType); |
| if (ipRawType != null) |
| { |
| Set<Bean<?>> candidates = getBeansByRawType().get(ipRawType); |
| if (candidates != null) |
| { |
| for (Bean<?> component : candidates) |
| { |
| if (((OwbBean) component).isEnabled()) |
| { |
| resolvedComponents.add(component); |
| } |
| } |
| } |
| } |
| } |
| else |
| { |
| for (Bean<?> component : webBeansContext.getBeanManagerImpl().getBeans()) |
| { |
| // no need to check instanceof OwbBean as we always wrap in a |
| // ThirdpartyBeanImpl at least |
| if (!((OwbBean) component).isEnabled()) |
| { |
| continue; |
| } |
| |
| if (returnAll) |
| { |
| resolvedComponents.add(component); |
| } |
| else |
| { |
| if (fastMatching) |
| { |
| for (Type componentApiType : component.getTypes()) |
| { |
| |
| if (ClassUtil.isRawClassEquals(injectionPointType, componentApiType)) |
| { |
| resolvedComponents.add(component); |
| break; |
| } |
| } |
| } |
| else |
| { |
| for (Type componentApiType : component.getTypes()) |
| { |
| visitedForGenerics.clear(); |
| if (GenericsUtil.satisfiesDependency( |
| isDelegate, AbstractProducerBean.class.isInstance(component), |
| injectionPointType, componentApiType, visitedForGenerics)) |
| { |
| resolvedComponents.add(component); |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (!returnAll) |
| { |
| // Main scan may have left state in visitedForGenerics; refinement uses the same map. |
| visitedForGenerics.clear(); |
| |
| // Look for qualifiers |
| resolvedComponents = findByQualifier(resolvedComponents, injectionPointType, qualifiers); |
| |
| // have an additional round of checks for assignability of parameterized types. |
| Set<Bean<?>> byParameterizedType = findByParameterizedType(resolvedComponents, injectionPointType, isDelegate, visitedForGenerics); |
| if (byParameterizedType.isEmpty()) |
| { |
| resolvedComponents = findByBeanType(resolvedComponents, injectionPointType, isDelegate, visitedForGenerics); |
| } |
| else |
| { |
| resolvedComponents = byParameterizedType; |
| } |
| } |
| |
| if (resolvedComponents.isEmpty()) |
| { |
| // for Instance or Event creation we provided special Beans |
| // because they actually needs to fit every Qualifier |
| Bean<?> specialBean = getInstanceOrEventInjectionBean(injectionPointType); |
| if (specialBean != null) |
| { |
| resolvedComponents.add(specialBean); |
| } |
| } |
| |
| if (!startup) |
| { |
| if (resolvedComponents.isEmpty()) |
| { |
| resolvedBeansByType.put(cacheKey, Collections.emptySet()); |
| return Collections.emptySet(); |
| } |
| |
| resolvedBeansByType.put(cacheKey, resolvedComponents); |
| |
| if (logger.isLoggable(Level.FINE)) |
| { |
| logger.log(Level.FINE, "DEBUG_ADD_BYTYPE_CACHE_BEANS", cacheKey); |
| } |
| } |
| |
| return resolvedComponents; |
| } |
| |
| /** |
| * @param visited scratch map for {@link GenericsUtil#satisfiesDependency}; cleared before each call. |
| */ |
| private Set<Bean<?>> findByBeanType(Set<Bean<?>> allComponents, Type injectionPointType, boolean isDelegate, |
| Map<Type, Integer> visited) |
| { |
| if (allComponents == null || allComponents.isEmpty()) |
| { |
| // fast path |
| return allComponents; |
| } |
| |
| visited.clear(); |
| |
| Set<Bean<?>> resolved = new HashSet<>(); |
| for (Bean<?> bean : allComponents) |
| { |
| boolean isProducer = AbstractProducerBean.class.isInstance(bean); |
| for (Type type : bean.getTypes()) |
| { |
| visited.clear(); |
| if (GenericsUtil.satisfiesDependency(isDelegate, isProducer, injectionPointType, type, visited)) |
| { |
| resolved.add(bean); |
| } |
| |
| if (!ClassUtil.isParametrizedType(injectionPointType) |
| && ClassUtil.isRawClassEquals(injectionPointType, type)) |
| { |
| resolved.add(bean); |
| } |
| } |
| } |
| return resolved; |
| } |
| |
| /** |
| * @param visited scratch map for {@link GenericsUtil#satisfiesDependency}; cleared before each call. |
| */ |
| private Set<Bean<?>> findByParameterizedType(Set<Bean<?>> allComponents, Type injectionPointType, boolean isDelegate, |
| Map<Type, Integer> visited) |
| { |
| Bean<?> rawProducerBean = null; |
| |
| visited.clear(); |
| |
| Set<Bean<?>> resolvedComponents = new HashSet<>(); |
| for (Bean<?> component : allComponents) |
| { |
| boolean isProducer = AbstractProducerBean.class.isInstance(component); |
| for (Type componentApiType : component.getTypes()) |
| { |
| visited.clear(); |
| if (GenericsUtil.satisfiesDependency(isDelegate, isProducer, injectionPointType, componentApiType, visited)) |
| { |
| resolvedComponents.add(component); |
| break; |
| } |
| else if (isProducer && componentApiType instanceof Class && ClassUtil.isRawClassEquals(injectionPointType, componentApiType)) |
| { |
| rawProducerBean = component; |
| } |
| } |
| } |
| |
| if (resolvedComponents.isEmpty() && rawProducerBean != null) |
| { |
| resolvedComponents.add(rawProducerBean); |
| } |
| |
| return resolvedComponents; |
| } |
| |
| |
| /** |
| * Verify that we have a legal Type at the injection point. |
| * CDI can basically only handle Class and ParameterizedType injection points atm. |
| * @throws WebBeansConfigurationException on TypeVariable, WildcardType and GenericArrayType |
| * @throws IllegalArgumentException if the type is not yet supported by the spec. |
| */ |
| private void validateInjectionPointType(Type injectionPointType) |
| { |
| if (injectionPointType instanceof TypeVariable || injectionPointType instanceof WildcardType || injectionPointType instanceof GenericArrayType) |
| { |
| throw new WebBeansConfigurationException("Injection point cannot define Type Variable " + injectionPointType); |
| } |
| |
| if (!(injectionPointType instanceof Class) && |
| !(injectionPointType instanceof ParameterizedType)) |
| { |
| throw new IllegalArgumentException("Unsupported type " + injectionPointType.getClass()); |
| } |
| } |
| |
| /** |
| * Gets alternatives from set. |
| * |
| * @param beans resolved set |
| * @return contains alternatives |
| */ |
| public <X> Set<Bean<? extends X>> findByAlternatives(Set<Bean<? extends X>> beans) |
| { |
| // first check whether we have Alternatives with a Priority annotation |
| List<Class<?>> prioritizedAlternatives = alternativesManager.getPrioritizedAlternatives(); |
| |
| for (Class<?> alternativeClazz : prioritizedAlternatives) |
| { |
| for (Bean<? extends X> bean: beans) |
| { |
| if (alternativeClazz.equals(bean.getBeanClass())) |
| { |
| return new SingleItemSet<>(bean); |
| } |
| } |
| |
| } |
| |
| |
| // if none such Alternative got found let's check the 'old' alternatives from beans.xml |
| Set<Bean<? extends X>> alternativeSet = new HashSet<>(); |
| Set<Bean<? extends X>> enableSet = new HashSet<>(); |
| |
| for (Bean<? extends X> bean : beans) |
| { |
| if (bean.isAlternative() || |
| (bean instanceof AbstractProducerBean && |
| ((AbstractProducerBean) bean).getOwnerComponent().isAlternative())) |
| { |
| alternativeSet.add(bean); |
| } |
| else |
| { |
| if (alternativeSet.isEmpty()) |
| { |
| AbstractOwbBean<?> temp = (AbstractOwbBean<?>) bean; |
| if (temp.isEnabled()) |
| { |
| enableSet.add(bean); |
| } |
| } |
| } |
| } |
| |
| if (!alternativeSet.isEmpty()) |
| { |
| return alternativeSet; |
| } |
| |
| // |
| |
| return enableSet; |
| } |
| |
| /** |
| * resolve any ambiguity by checking for Alternatives. |
| * If any @Alternative exists, then we pick the one with the |
| * highest priority. |
| * |
| * @param beans |
| * @param injectionPoint only used for logging. Can be null. |
| * @param <X> |
| * @return the single resolved bean, null if none is activated |
| * @throws jakarta.enterprise.inject.AmbiguousResolutionException if more than 1 bean is active |
| */ |
| public <X> Bean<? extends X> resolve(Set<Bean<? extends X>> beans, InjectionPoint injectionPoint) |
| { |
| if (beans == null || beans.isEmpty()) |
| { |
| return null; |
| } |
| |
| if (beans.size() == 1) |
| { |
| // if there is only one Bean left, then there is for sure no ambiguity. |
| return beans.iterator().next(); |
| } |
| |
| Set set = resolveAll(beans); |
| |
| if (set.isEmpty()) |
| { |
| return null; |
| } |
| |
| if(set.size() > 1) |
| { |
| throwAmbiguousResolutionException(set, null, injectionPoint); |
| } |
| |
| return (Bean<? extends X>)set.iterator().next(); |
| } |
| |
| public <X> Set<Bean<? extends X>> resolveAll(Set<Bean<? extends X>> beans) |
| { |
| if (beans == null || beans.isEmpty()) |
| { |
| return Collections.emptySet(); |
| } |
| |
| Set set = findByAlternatives(beans); |
| |
| if (set == null || set.isEmpty()) |
| { |
| return Collections.emptySet(); |
| } |
| |
| return set; |
| } |
| |
| /** |
| * Returns filtered bean set according to the qualifiers. |
| * |
| * @param remainingSet bean set for filtering by qualifier |
| * @param annotations qualifiers on injection point |
| * @return filtered bean set according to the qualifiers |
| */ |
| private Set<Bean<?>> findByQualifier(Set<Bean<?>> remainingSet, Type type, Annotation... annotations) |
| { |
| if (remainingSet == null || remainingSet.isEmpty()) |
| { |
| // fast path |
| return remainingSet; |
| } |
| |
| Iterator<Bean<?>> it = remainingSet.iterator(); |
| Set<Bean<?>> result = new HashSet<>(); |
| |
| while (it.hasNext()) |
| { |
| Bean<?> component = it.next(); |
| Set<Annotation> qTypes = component.getQualifiers(); |
| |
| int i = 0; |
| for (Annotation annot : annotations) |
| { |
| for (Annotation qualifier : qTypes) |
| { |
| if (annot.annotationType().equals(qualifier.annotationType())) |
| { |
| AnnotatedType<?> at = findQualifierModel(qualifier.annotationType()); |
| if (at == null) |
| { |
| if (AnnotationUtil.isCdiAnnotationEqual(qualifier, annot)) |
| { |
| i++; |
| } |
| } |
| else |
| { |
| if (AnnotationUtil.isCdiAnnotationEqual(at, qualifier, annot)) |
| { |
| i++; |
| } |
| } |
| } |
| |
| } |
| } |
| |
| if (i == annotations.length) |
| { |
| result.add(component); |
| } |
| } |
| |
| return result; |
| } |
| |
| private AnnotatedType<? extends Annotation> findQualifierModel(final Class<?> qualifier) |
| { |
| return webBeansContext.getBeanManagerImpl().getAdditionalAnnotatedTypeQualifiers().get(qualifier); |
| } |
| } |