/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2015 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.faces.cdi; import static java.util.Optional.empty; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Queue; import java.util.logging.Level; import java.util.logging.Logger; import javax.enterprise.context.ContextNotActiveException; import javax.enterprise.context.spi.Context; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.inject.spi.Annotated; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.CDI; import javax.enterprise.inject.spi.InjectionPoint; import javax.enterprise.util.TypeLiteral; import javax.faces.component.behavior.Behavior; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.model.DataModel; import javax.faces.validator.Validator; import com.sun.faces.util.FacesLogger; import com.sun.faces.util.Util; /** * A static utility class for CDI. */ public final class CdiUtils { /** * Stores the logger. */ private static final Logger LOGGER = FacesLogger.APPLICATION_VIEW.getLogger(); private final static Type CONVERTER_TYPE = new TypeLiteral<Converter<?>>() { private static final long serialVersionUID = 1L;}.getType(); private final static Type VALIDATOR_TYPE = new TypeLiteral<Validator<?>>() { private static final long serialVersionUID = 1L;}.getType(); /** * Constructor. */ private CdiUtils() { } /** * Create a converter using the FacesConverter value attribute. * * @param beanManager the bean manager. * @param value the value attribute. * @return the converter, or null if we could not match one. */ public static Converter<?> createConverter(BeanManager beanManager, String value) { Converter<?> managedConverter = createConverter(beanManager, new FacesConverterAnnotationLiteral(value, Object.class)); if (managedConverter != null) { return new CdiConverter(value, Object.class, managedConverter); } return null; } /** * Create a converter using the FacesConverter forClass attribute. * * @param beanManager the bean manager. * @param forClass the for class. * @return the converter, or null if we could not match one. */ public static Converter<?> createConverter(BeanManager beanManager, Class<?> forClass) { Converter<?> managedConverter = createConverter(beanManager, new FacesConverterAnnotationLiteral("", forClass)); if (managedConverter != null) { return new CdiConverter("", forClass, managedConverter); } return null; } private static Converter<?> createConverter(BeanManager beanManager, Annotation qualifier) { // Try to find parameterized converter first Converter<?> managedConverter = (Converter<?>) getBeanReferenceByType( beanManager, CONVERTER_TYPE, qualifier); if (managedConverter == null) { // No parameterized converter, try raw converter managedConverter = getBeanReference( beanManager, Converter.class, qualifier); } return managedConverter; } /** * Create a behavior using the FacesBehavior value attribute. * * @param beanManager the bean manager. * @param value the value attribute. * @return the behavior, or null if we could not match one. */ public static Behavior createBehavior(BeanManager beanManager, String value) { Behavior delegatingBehavior = null; Behavior managedBehavior = getBeanReference( beanManager, Behavior.class, new FacesBehaviorAnnotationLiteral(value) ); if (managedBehavior != null) { delegatingBehavior = new CdiBehavior(value, managedBehavior); } return delegatingBehavior; } /** * Create a validator using the FacesValidator value attribute. * * @param beanManager the bean manager. * @param value the value attribute. * @return the validator, or null if we could not match one. */ public static Validator<?> createValidator(BeanManager beanManager, String value) { Annotation qualifier = new FacesValidatorAnnotationLiteral(value); // Try to find parameterized validator first Validator<?> managedValidator = (Validator<?>) getBeanReferenceByType( beanManager, VALIDATOR_TYPE, qualifier); if (managedValidator == null) { // No parameterized validator, try raw validator managedValidator = getBeanReference( beanManager, Validator.class, qualifier); } if (managedValidator != null) { return new CdiValidator(value, managedValidator); } return null; } public static <T> T getBeanReference(Class<T> type, Annotation... qualifiers) { return type.cast(getBeanReferenceByType(Util.getCdiBeanManager(FacesContext.getCurrentInstance()), type, qualifiers)); } /** * @param beanManager the bean manager * @param type the required bean type the reference must have * @param qualifier the required qualifiers the reference must have * @return a bean reference adhering to the required type and qualifiers */ public static <T> T getBeanReference(BeanManager beanManager, Class<T> type, Annotation... qualifiers) { return type.cast(getBeanReferenceByType(beanManager, type, qualifiers)); } public static Object getBeanReferenceByType(BeanManager beanManager, Type type, Annotation... qualifiers) { Object beanReference = null; Bean<?> bean = beanManager.resolve(beanManager.getBeans(type, qualifiers)); if (bean != null) { beanReference = beanManager.getReference(bean, type, beanManager.createCreationalContext(bean)); } return beanReference; } /** * Returns concrete (non-proxied) bean instance of given class in current context. * * @param type the required bean type the instance must have * @param create whether to auto-create bean if not exist * @return a bean instance adhering to the required type */ public static <T> T getBeanInstance(Class<T> type, boolean create) { BeanManager beanManager = Util.getCdiBeanManager(FacesContext.getCurrentInstance()); @SuppressWarnings("unchecked") Bean<T> bean = (Bean<T>) beanManager.resolve(beanManager.getBeans(type)); if (bean != null) { Context context = beanManager.getContext(bean.getScope()); if (create) { return context.get(bean, beanManager.createCreationalContext(bean)); } else { return context.get(bean); } } else { return null; } } /** * Finds an annotation in an Annotated, taking stereo types into account * * @param beanManager the current bean manager * @param annotated the Annotated in which to search * @param annotationType the type of the annotation to search for * @return An Optional that contains an instance of annotation type for which was searched if the annotated contained this. */ public static <A extends Annotation> Optional<A> getAnnotation(BeanManager beanManager, Annotated annotated, Class<A> annotationType) { annotated.getAnnotation(annotationType); if (annotated.getAnnotations().isEmpty()) { return empty(); } if (annotated.isAnnotationPresent(annotationType)) { return Optional.of(annotated.getAnnotation(annotationType)); } Queue<Annotation> annotations = new LinkedList<>(annotated.getAnnotations()); while (!annotations.isEmpty()) { Annotation annotation = annotations.remove(); if (annotation.annotationType().equals(annotationType)) { return Optional.of(annotationType.cast(annotation)); } try { if (beanManager.isStereotype(annotation.annotationType())) { annotations.addAll( beanManager.getStereotypeDefinition( annotation.annotationType() ) ); } } catch (Exception e) { // Log and continue, if it's not allowed to test if it's a stereo type // we're unlikely to be interested in this annotation if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.warning("Exception happened when finding an annotation: " + e); } } } return empty(); } public static DataModel<?> createDataModel(final Class<?> forClass) { List<DataModel<?>> dataModel = new ArrayList<DataModel<?>>(1); CDI<Object> cdi = CDI.current(); // Scan the map in order, the first class that is a super class or equal to the class for which // we're looking for a DataModel is the closest match, since the Map is sorted on inheritance relation getDataModelClassesMap(cdi).entrySet().stream() .filter(e -> e.getKey().isAssignableFrom(forClass)) .findFirst() .ifPresent( // Get the bean from CDI which is of the class type that we found during annotation scanning // and has the @FacesDataModel annotation, with the "forClass" attribute set to the closest // super class of our target class. e -> dataModel.add( cdi.select( e.getValue(), new FacesDataModelAnnotationLiteral(e.getKey()) ).get()) ); return dataModel.isEmpty()? null : dataModel.get(0); } @SuppressWarnings("unchecked") public static Map<Class<?>, Class<? extends DataModel<?>>> getDataModelClassesMap(CDI<Object> cdi) { BeanManager beanManager = cdi.getBeanManager(); // Get the Map with classes for which a custom DataModel implementation is available from CDI Bean<?> bean = beanManager.resolve(beanManager.getBeans("comSunFacesDataModelClassesMap")); Object beanReference = beanManager.getReference(bean, Map.class, beanManager.createCreationalContext(bean)); return (Map<Class<?>, Class<? extends DataModel<?>>>) beanReference; } /** * Returns the current injection point. */ public static InjectionPoint getCurrentInjectionPoint(BeanManager beanManager, CreationalContext<?> creationalContext) { Bean<? extends Object> bean = beanManager.resolve(beanManager.getBeans(InjectionPoint.class)); InjectionPoint injectionPoint = (InjectionPoint) beanManager.getReference(bean, InjectionPoint.class, creationalContext); if (injectionPoint == null) { // It's broken in some Weld versions. Below is a work around. bean = beanManager.resolve(beanManager.getBeans(InjectionPointGenerator.class)); injectionPoint = (InjectionPoint) beanManager.getInjectableReference(bean.getInjectionPoints().iterator().next(), creationalContext); } return injectionPoint; } /** * Returns the qualifier annotation of the given qualifier class from the given injection point. */ public static <A extends Annotation> A getQualifier(InjectionPoint injectionPoint, Class<A> qualifierClass) { for (Annotation annotation : injectionPoint.getQualifiers()) { if (qualifierClass.isAssignableFrom(annotation.getClass())) { return qualifierClass.cast(annotation); } } return null; } /** * Returns true if given scope is active in current context. */ public static <S extends Annotation> boolean isScopeActive(Class<S> scope) { BeanManager beanManager = Util.getCdiBeanManager(FacesContext.getCurrentInstance()); try { Context context = beanManager.getContext(scope); return context.isActive(); } catch (ContextNotActiveException ignore) { return false; } } }