/* * 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.deltaspike.core.api.provider; import org.apache.deltaspike.core.api.literal.AnyLiteral; import org.apache.deltaspike.core.api.projectstage.ProjectStage; import org.apache.deltaspike.core.util.ProjectStageProducer; import javax.enterprise.context.Dependent; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.inject.Typed; import javax.enterprise.inject.spi.AnnotatedType; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.InjectionTarget; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; /** * This class contains utility methods for resolution of contextual references in situations where no injection is * available because the current class is not managed by the CDI Container. This can happen in e.g. a JPA 2.0 * EntityListener, a ServletFilter, a Spring managed Bean, etc. * * <p> * <b>Attention:</b> This approach is intended for use in user code at runtime. If BeanProvider is used during Container * boot (in an Extension), non-portable behaviour results. The CDI specification only allows injection of the * BeanManager during CDI container boot time.</p> * * @see DependentProvider * @see BeanManagerProvider */ @Typed() public final class BeanProvider { private static final Logger LOG = Logger.getLogger(BeanProvider.class.getName()); private static final boolean LOG_DEPENDENT_WARNINGS; static { LOG_DEPENDENT_WARNINGS = Arrays.asList(ProjectStage.Development, ProjectStage.UnitTest).contains( ProjectStageProducer.getInstance().getProjectStage()); } private BeanProvider() { // this is a utility class which doesn't get instantiated. } /** * Get a Contextual Reference by its type and qualifiers. You can use this method to get contextual references of a * given type. A "Contextual Reference" is a proxy which will automatically resolve the correct contextual instance * when you access any method. * * <p> * <b>Attention:</b> You shall not use this method to manually resolve a @Dependent bean! The reason is that * contextual instances usually live in the well-defined lifecycle of their injection point (the bean they got * injected into). But if we manually resolve a @Dependent bean, then it does <b>not</b> belong to such well * defined lifecycle (because @Dependent is not @NormalScoped) and thus will not be automatically * destroyed at the end of the lifecycle. You need to manually destroy this contextual instance via * {@link javax.enterprise.context.spi.Contextual#destroy(Object, javax.enterprise.context.spi.CreationalContext)}. * Thus you also need to manually store the CreationalContext and the Bean you used to create the contextual * instance.</p> * * @param type the type of the bean in question * @param qualifiers additional qualifiers which further distinct the resolved bean * @param <T> target type * * @return the resolved Contextual Reference * * @throws IllegalStateException if the bean could not be found. * @see #getContextualReference(Class, boolean, Annotation...) */ public static <T> T getContextualReference(Class<T> type, Annotation... qualifiers) { return getContextualReference(type, false, qualifiers); } /** * {@link #getContextualReference(Class, Annotation...)} which returns <code>null</code> if the 'optional' parameter * is set to <code>true</code>. * * @param type the type of the bean in question * @param optional if <code>true</code> it will return <code>null</code> if no bean could be found or created. * Otherwise it will throw an {@code IllegalStateException} * @param qualifiers additional qualifiers which distinguish the resolved bean * @param <T> target type * * @return the resolved Contextual Reference * * @see #getContextualReference(Class, Annotation...) */ public static <T> T getContextualReference(Class<T> type, boolean optional, Annotation... qualifiers) { BeanManager beanManager = getBeanManager(); return getContextualReference(beanManager, type, optional, qualifiers); } /** * {@link #getContextualReference(Class, Annotation...)} which returns <code>null</code> if the 'optional' parameter * is set to <code>true</code>. This method is intended for usage where the BeanManger is known, e.g. in Extensions. * * @param beanManager the BeanManager to use * @param type the type of the bean in question * @param optional if <code>true</code> it will return <code>null</code> if no bean could be found or created. * Otherwise it will throw an {@code IllegalStateException} * @param qualifiers additional qualifiers which further distinct the resolved bean * @param <T> target type * * @return the resolved Contextual Reference * * @see #getContextualReference(Class, Annotation...) */ public static <T> T getContextualReference(BeanManager beanManager, Class<T> type, boolean optional, Annotation... qualifiers) { Set<Bean<?>> beans = beanManager.getBeans(type, qualifiers); if (beans == null || beans.isEmpty()) { if (optional) { return null; } throw new IllegalStateException("Could not find beans for Type=" + type + " and qualifiers:" + Arrays.toString(qualifiers)); } return getContextualReference(type, beanManager, beans); } /** * Get a Contextual Reference by its EL Name. This only works for beans with the @Named annotation. * * <p> * <b>Attention:</b> please see the notes on manually resolving @Dependent beans in * {@link #getContextualReference(Class, java.lang.annotation.Annotation...)}!</p> * * @param name the EL name of the bean * * @return the resolved Contextual Reference * * @throws IllegalStateException if the bean could not be found. * @see #getContextualReference(String, boolean) */ public static Object getContextualReference(String name) { return getContextualReference(name, false); } /** * Get a Contextual Reference by its EL Name. This only works for beans with the @Named annotation. * * <p> * <b>Attention:</b> please see the notes on manually resolving @Dependent beans in * {@link #getContextualReference(Class, java.lang.annotation.Annotation...)}!</p> * * @param name the EL name of the bean * @param optional if <code>true</code> it will return <code>null</code> if no bean could be found or created. * Otherwise it will throw an {@code IllegalStateException} * * @return the resolved Contextual Reference */ public static Object getContextualReference(String name, boolean optional) { return getContextualReference(name, optional, Object.class); } /** * Get a Contextual Reference by its EL Name. This only works for beans with the @Named annotation. * * <p> * <b>Attention:</b> please see the notes on manually resolving @Dependent beans in * {@link #getContextualReference(Class, java.lang.annotation.Annotation...)}!</p> * * @param name the EL name of the bean * @param optional if <code>true</code> it will return <code>null</code> if no bean could be found or created. * Otherwise it will throw an {@code IllegalStateException} * @param type the type of the bean in question - use {@link #getContextualReference(String, boolean)} if the * type is unknown e.g. in dyn. use-cases * @param <T> target type * * @return the resolved Contextual Reference */ public static <T> T getContextualReference(String name, boolean optional, Class<T> type) { return getContextualReference(getBeanManager(), name, optional, type); } /** * <p>Get a Contextual Reference by its EL Name. * This only works for beans with the @Named annotation.</p> * * <p><b>Attention:</b> please see the notes on manually resolving @Dependent bean * in {@link #getContextualReference(Class, boolean, java.lang.annotation.Annotation...)}!</p> * * * @param beanManager the BeanManager to use * @param name the EL name of the bean * @param optional if <code>true</code> it will return <code>null</code> if no bean could be found or created. * Otherwise it will throw an {@code IllegalStateException} * @param type the type of the bean in question - use {@link #getContextualReference(String, boolean)} * if the type is unknown e.g. in dyn. use-cases * @param <T> target type * @return the resolved Contextual Reference */ public static <T> T getContextualReference(BeanManager beanManager, String name, boolean optional, Class<T> type) { Set<Bean<?>> beans = beanManager.getBeans(name); if (beans == null || beans.isEmpty()) { if (optional) { return null; } throw new IllegalStateException("Could not find beans for Type=" + type + " and name:" + name); } return getContextualReference(type, beanManager, beans); } /** * Get the Contextual Reference for the given bean. * * <p> * <b>Attention:</b> please see the notes on manually resolving @Dependent beans in * {@link #getContextualReference(Class, java.lang.annotation.Annotation...)}!</p> * * @param type the type of the bean in question * @param bean bean definition for the contextual reference * @param <T> target type * * @return the resolved Contextual Reference */ public static <T> T getContextualReference(Class<T> type, Bean<T> bean) { return getContextualReference(type, getBeanManager(), bean); } private static <T> T getContextualReference(Class<T> type, BeanManager beanManager, Bean<?> bean) { //noinspection unchecked return getContextualReference(type, beanManager, Collections.<Bean<?>> singleton(bean)); } /** * Get a list of Contextual References by type, regardless of qualifiers (including dependent scoped beans). * * You can use this method to get all contextual references of a given type. A 'Contextual Reference' is a proxy * which will automatically resolve the correct contextual instance when you access any method. * * <p> * <b>Attention:</b> please see the notes on manually resolving @Dependent beans in * {@link #getContextualReference(Class, java.lang.annotation.Annotation...)}!</p> * <p> * <b>Attention:</b> This will also return instances of beans for which an Alternative exists! The @Alternative * resolving is only done via {@link BeanManager#resolve(java.util.Set)} which we cannot use in this case!</p> * * @param type the type of the bean in question * @param optional if <code>true</code> it will return an empty list if no bean could be found or created. Otherwise * it will throw an {@code IllegalStateException} * @param <T> target type * * @return the resolved list of Contextual Reference or an empty-list if optional is true */ public static <T> List<T> getContextualReferences(Class<T> type, boolean optional) { return getContextualReferences(type, optional, true); } /** * Get a list of Contextual References by type, regardless of the qualifier. * * Further details are available at {@link #getContextualReferences(Class, boolean)}. * <p> * <b>Attention:</b> please see the notes on manually resolving @Dependent bean in * {@link #getContextualReference(Class, java.lang.annotation.Annotation...)}!</p> * <p> * <b>Attention:</b> This will also return instances of beans for which an Alternative exists! The @Alternative * resolving is only done via {@link BeanManager#resolve(java.util.Set)} which we cannot use in this case!</p> * * @param type the type of the bean in question * @param optional if <code>true</code> it will return an empty list if no bean could be found or * created. Otherwise it will throw an {@code IllegalStateException} * @param includeDefaultScopedBeans specifies if dependent scoped beans should be included in the result * @param <T> target type * * @return the resolved list of Contextual Reference or an empty-list if optional is true */ public static <T> List<T> getContextualReferences(Class<T> type, boolean optional, boolean includeDefaultScopedBeans) { BeanManager beanManager = getBeanManager(); Set<Bean<T>> beans = getBeanDefinitions(type, optional, includeDefaultScopedBeans, beanManager); List<T> result = new ArrayList<T>(beans.size()); for (Bean<?> bean : beans) { //noinspection unchecked result.add(getContextualReference(type, beanManager, bean)); } return result; } public static <T> DependentProvider<T> getDependent(Class<T> type, Annotation... qualifiers) { BeanManager beanManager = getBeanManager(); return getDependent(beanManager, type, qualifiers); } public static <T> DependentProvider<T> getDependent(BeanManager beanManager, Class<T> type, Annotation... qualifiers) { Set<Bean<?>> beans = beanManager.getBeans(type, qualifiers); @SuppressWarnings("unchecked") Bean<T> bean = (Bean<T>) beanManager.resolve(beans); return createDependentProvider(beanManager, type, bean); } public static <T> DependentProvider<T> getDependent(String name) { BeanManager beanManager = getBeanManager(); return getDependent(beanManager, name); } public static <T> DependentProvider<T> getDependent(BeanManager beanManager, String name) { Set<Bean<?>> beans = beanManager.getBeans(name); @SuppressWarnings("unchecked") Bean<T> bean = (Bean<T>) beanManager.resolve(beans); @SuppressWarnings("unchecked") Class<T> beanClass = (Class<T>) bean.getBeanClass(); return createDependentProvider(beanManager, beanClass, bean); } private static <T> DependentProvider<T> createDependentProvider(BeanManager beanManager, Class<T> type, Bean<T> bean) { CreationalContext<T> cc = beanManager.createCreationalContext(bean); @SuppressWarnings("unchecked") T instance = (T) beanManager.getReference(bean, type, cc); return new DependentProvider<T>(bean, cc, instance); } /** * Get a set of {@link Bean} definitions by type, regardless of qualifiers. * * @param type the type of the bean in question * @param optional if <code>true</code> it will return an empty set if no bean could be found. * Otherwise it will throw an {@code IllegalStateException} * @param includeDefaultScopedBeans specifies whether dependent scoped beans should be included in the result * @param <T> target type * * @return the resolved set of {@link Bean} definitions or an empty set if optional is true */ public static <T> Set<Bean<T>> getBeanDefinitions(Class<T> type, boolean optional, boolean includeDefaultScopedBeans) { BeanManager beanManager = getBeanManager(); return getBeanDefinitions(type, optional, includeDefaultScopedBeans, beanManager); } private static <T> Set<Bean<T>> getBeanDefinitions(Class<T> type, boolean optional, boolean includeDefaultScopedBeans, BeanManager beanManager) { Set<Bean<?>> beans = beanManager.getBeans(type, new AnyLiteral()); if (beans == null || beans.isEmpty()) { if (optional) { return Collections.emptySet(); } throw new IllegalStateException("Could not find beans for Type=" + type); } if (!includeDefaultScopedBeans) { beans = filterDefaultScopedBeans(beans); } Set<Bean<T>> result = new HashSet<Bean<T>>(); for (Bean<?> bean : beans) { //noinspection unchecked @SuppressWarnings("unchecked") Bean<T> beanT = (Bean<T>) bean; result.add(beanT); } return result; } /** * Performs dependency injection on an instance. Useful for instances which aren't managed by CDI. * <p/> * <b>Attention:</b><br/> * The resulting instance isn't managed by CDI; only fields annotated with @Inject get initialized. * * @param instance current instance * @param <T> current type * * @return instance with injected fields (if possible - or null if the given instance is null) */ @SuppressWarnings("unchecked") public static <T> T injectFields(T instance) { if (instance == null) { return null; } BeanManager beanManager = getBeanManager(); CreationalContext<T> creationalContext = beanManager.createCreationalContext(null); AnnotatedType<T> annotatedType = beanManager.createAnnotatedType((Class<T>) instance.getClass()); InjectionTarget<T> injectionTarget = beanManager.createInjectionTarget(annotatedType); injectionTarget.inject(instance, creationalContext); return instance; } private static Set<Bean<?>> filterDefaultScopedBeans(Set<Bean<?>> beans) { Set<Bean<?>> result = new HashSet<Bean<?>>(beans.size()); for (Bean<?> currentBean : beans) { if (!Dependent.class.isAssignableFrom(currentBean.getScope())) { result.add(currentBean); } } return result; } /** * Internal helper method to resolve the right bean and resolve the contextual reference. * * @param type the type of the bean in question * @param beanManager current bean-manager * @param beans beans in question * @param <T> target type * @return the contextual reference */ private static <T> T getContextualReference(Class<T> type, BeanManager beanManager, Set<Bean<?>> beans) { Bean<?> bean = beanManager.resolve(beans); logWarningIfDependent(bean); CreationalContext<?> creationalContext = beanManager.createCreationalContext(bean); @SuppressWarnings({ "unchecked", "UnnecessaryLocalVariable" }) T result = (T) beanManager.getReference(bean, type, creationalContext); return result; } /** * Log a warning if the given bean is of @Dependent scope as we cannot properly clean up the contextual * instance afterwards. */ private static void logWarningIfDependent(Bean<?> bean) { if (LOG_DEPENDENT_WARNINGS && bean.getScope().equals(Dependent.class)) { LOG.log(Level.WARNING, "BeanProvider shall not be used to create @Dependent scoped beans. " + "Bean: " + bean.toString()); } } /** * Internal method to resolve the BeanManager via the {@link BeanManagerProvider}. * * @return current BeanManager */ private static BeanManager getBeanManager() { return BeanManagerProvider.getInstance().getBeanManager(); } }