/* * JBoss, Home of Professional Open Source * Copyright 2008, Red Hat, Inc., and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * 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.weld.resolution; import java.io.Serializable; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.function.Function; import javax.enterprise.event.Event; import javax.enterprise.inject.Instance; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.InterceptionFactory; import javax.inject.Provider; import org.jboss.weld.bean.AbstractProducerBean; import org.jboss.weld.config.WeldConfiguration; import org.jboss.weld.inject.WeldInstance; import org.jboss.weld.manager.BeanManagerImpl; import org.jboss.weld.metadata.cache.MetaAnnotationStore; import org.jboss.weld.util.Beans; import org.jboss.weld.util.LazyValueHolder; import org.jboss.weld.util.Primitives; import org.jboss.weld.util.cache.ComputingCache; import org.jboss.weld.util.cache.ComputingCacheBuilder; import org.jboss.weld.util.collections.ImmutableSet; import org.jboss.weld.util.collections.WeldCollections; import org.jboss.weld.util.reflection.Reflections; /** * @author pmuir * @author alesj */ public abstract class AbstractTypeSafeBeanResolver<T extends Bean<?>, C extends Collection<T>> extends TypeSafeResolver<Resolvable, T, C, C> { private final BeanManagerImpl beanManager; private final ComputingCache<Set<Bean<?>>, Set<Bean<?>>> disambiguatedBeans; private final MetaAnnotationStore store; private final LazyValueHolder<Map<Type, ArrayList<T>>> beansByType; public class BeanDisambiguation implements Function<Set<Bean<?>>, Set<Bean<?>>> { private BeanDisambiguation() { } @Override public Set<Bean<?>> apply(Set<Bean<?>> from) { if (from.size() > 1) { ImmutableSet.Builder<Bean<?>> allBeans = ImmutableSet.builder(); // beans that are themselves alternatives or their defining bean is an alternative Set<Bean<?>> priorityBeans = new HashSet<Bean<?>>(); for (Bean<?> bean : from) { if (bean.isAlternative()) { priorityBeans.add(bean); } else if (bean instanceof AbstractProducerBean<?, ?, ?>) { AbstractProducerBean<?, ?, ?> producer = (AbstractProducerBean<?, ?, ?>) bean; if (producer.getDeclaringBean().isAlternative()) { priorityBeans.add(bean); } } allBeans.add(bean); } if (priorityBeans.isEmpty()) { return allBeans.build(); } else { if (priorityBeans.size() == 1) { return Collections.<Bean<?>>singleton(priorityBeans.iterator().next()); } else { return resolveAlternatives(priorityBeans); } } } else { return ImmutableSet.copyOf(from); } } /** * If all the beans left are alternatives with a priority, then the container will select the * alternative with the highest priority, and the ambiguous dependency is called resolvable. * */ public Set<Bean<?>> resolveAlternatives(Set<Bean<?>> alternatives) { int highestPriority = Integer.MIN_VALUE; Set<Bean<?>> selectedAlternativesWithHighestPriority = new HashSet<Bean<?>>(); for (Bean<?> bean : alternatives) { Integer priority = beanManager.getEnabled().getAlternativePriority(bean.getBeanClass()); if (priority == null) { // not all the beans left are alternatives with a priority - we are not able to resolve return ImmutableSet.copyOf(alternatives); } if (priority > highestPriority) { highestPriority = priority; selectedAlternativesWithHighestPriority.clear(); } if (priority == highestPriority) { selectedAlternativesWithHighestPriority.add(bean); } } return ImmutableSet.copyOf(selectedAlternativesWithHighestPriority); } } public AbstractTypeSafeBeanResolver(BeanManagerImpl beanManager, final Iterable<T> beans) { super(beans, beanManager.getServices().get(WeldConfiguration.class)); this.beanManager = beanManager; this.disambiguatedBeans = ComputingCacheBuilder.newBuilder().build(new BeanDisambiguation()); this.store = beanManager.getServices().get(MetaAnnotationStore.class); // beansByType stores a map of a type to all beans that are assignable to // that type. This means that it most cases we do not need to loop through // every bean in the system when performing resolution // we build this map lazily, as we do not have access to all beans when // the resolver is created. Calling the resolvers clear method will also // clear this map.This task is not suitable for a computing hashmap, as // the whole map should be calculated in one hit, so only a single // iteration over all beans is required this.beansByType = new LazyValueHolder<Map<Type, ArrayList<T>>>() { @Override protected Map<Type, ArrayList<T>> computeValue() { Map<Type, ArrayList<T>> map = new HashMap<Type, ArrayList<T>>(); for (T bean : beans) { mapBean(map, bean); } trimArrayListsToSize(map); return WeldCollections.immutableMapView(map); } private void mapBean(Map<Type, ArrayList<T>> map, T bean) { for (Type type : bean.getTypes()) { mapTypeToBean(map, type, bean); if (type instanceof ParameterizedType) { // we need to add the raw type as well Type rawType = ((ParameterizedType) type).getRawType(); mapTypeToBean(map, rawType, bean); } else if (type instanceof Class<?>) { // if the type is a primitive we also need to add the bean // is also resolvable from the boxed class Class<?> clazz = (Class<?>) type; if (clazz.isPrimitive()) { Class<?> wrapped = Primitives.wrap(clazz); mapTypeToBean(map, wrapped, bean); } } } } private void mapTypeToBean(Map<Type, ArrayList<T>> map, Type type, T bean) { if (!map.containsKey(type)) { map.put(type, new ArrayList<T>()); } map.get(type).add(bean); } private void trimArrayListsToSize(Map<Type, ArrayList<T>> map) { for (Entry<Type, ArrayList<T>> entry : map.entrySet()) { entry.getValue().trimToSize(); } } }; } @Override protected boolean matches(Resolvable resolvable, T bean) { AssignabilityRules rules = null; if (resolvable.isDelegate()) { rules = DelegateInjectionPointAssignabilityRules.instance(); } else { rules = BeanTypeAssignabilityRules.instance(); } return rules.matches(resolvable.getTypes(), bean.getTypes()) && Beans.containsAllQualifiers(resolvable.getQualifiers(), QualifierInstance.of(bean, store)); } @Override protected Iterable<? extends T> getAllBeans(Resolvable resolvable) { if (resolvable.getTypes().contains(Object.class) || Instance.class.equals(resolvable.getJavaClass()) || Event.class.equals(resolvable.getJavaClass()) || Provider.class.equals(resolvable.getJavaClass()) || InterceptionFactory.class.equals(resolvable.getJavaClass()) || WeldInstance.class.equals(resolvable.getJavaClass()) || resolvable.getTypes().contains(Serializable.class)) { return super.getAllBeans(resolvable); } Set<T> beans = new HashSet<T>(); for (Type type : resolvable.getTypes()) { beans.addAll(getBeans(type)); if (type instanceof ParameterizedType) { // we also need to consider the raw type Type rawType = ((ParameterizedType) type).getRawType(); beans.addAll(getBeans(rawType)); } else if (type instanceof Class<?>) { // primitives Class<?> clazz = (Class<?>) type; if (clazz.isPrimitive()) { clazz = Primitives.wrap(clazz); beans.addAll(getBeans(clazz)); } } else if (type instanceof GenericArrayType) { Class<Object> rawArrayType = Reflections.getRawType(type); beans.addAll(getBeans(rawArrayType)); } } return beans; } private List<T> getBeans(Type type) { List<T> beansForType = beansByType.get().get(type); return beansForType == null ? Collections.<T>emptyList() : beansForType; } /** * @return the manager */ protected BeanManagerImpl getBeanManager() { return beanManager; } @Override protected Set<T> filterResult(Set<T> matched) { return Beans.removeDisabledBeans(matched, beanManager); } public <X> Set<Bean<? extends X>> resolve(Set<Bean<? extends X>> beans) { if (beans.isEmpty()) { return beans; } /* * We need to defensively copy the beans set as it can be provided by * the user in which case this algorithm will have thread safety issues */ //noinspection unchecked beans = ImmutableSet.copyOf(beans); //noinspection SuspiciousMethodCalls return disambiguatedBeans.getCastValue(beans); } @Override public void clear() { super.clear(); this.disambiguatedBeans.clear(); this.beansByType.clear(); } MetaAnnotationStore getStore() { return store; } }