/* * 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.jsf.impl.injection.proxy; import org.apache.deltaspike.core.spi.activation.Deactivatable; import org.apache.deltaspike.core.util.ClassDeactivationUtils; import org.apache.deltaspike.core.util.bean.BeanBuilder; import org.apache.deltaspike.core.util.metadata.builder.AnnotatedTypeBuilder; import org.apache.deltaspike.proxy.api.DeltaSpikeProxyContextualLifecycle; import javax.enterprise.event.Observes; import javax.enterprise.inject.spi.AfterBeanDiscovery; import javax.enterprise.inject.spi.AnnotatedType; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.BeforeBeanDiscovery; import javax.enterprise.inject.spi.Extension; import javax.enterprise.inject.spi.ProcessAnnotatedType; import javax.faces.convert.Converter; import javax.faces.validator.Validator; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashSet; import java.util.Set; import java.util.logging.Logger; public class ConverterAndValidatorProxyExtension implements Extension, Deactivatable { private static final Logger LOG = Logger.getLogger(ConverterAndValidatorProxyExtension.class.getName()); private Boolean isActivated = true; private Set<Class<?>> classesToProxy = new HashSet<Class<?>>(); protected void init(@Observes BeforeBeanDiscovery beforeBeanDiscovery) { this.isActivated = ClassDeactivationUtils.isActivated(getClass()); } @SuppressWarnings("UnusedDeclaration") public <X> void findConverterAndValidatorsWhichNeedProxiesForDependencyInjectionSupport( @Observes ProcessAnnotatedType<X> pat, BeanManager beanManager) { if (!this.isActivated) { return; } Class<X> beanClass = pat.getAnnotatedType().getJavaClass(); if (!(Converter.class.isAssignableFrom(beanClass) || (Validator.class.isAssignableFrom(beanClass)))) { return; } Bean<X> bean = new BeanBuilder<X>(beanManager).readFromType(pat.getAnnotatedType()).create(); // veto normal converters/validators -> they will get excluded from the special handling later on if (!hasInjectionPoints(bean) && !hasNormalScopeAnnotation(bean, beanManager)) { pat.veto(); return; } // converters/validators without properties for tags, will be handled by the corresponding manual wrapper if (!hasPublicProperty(beanClass)) { return; } if (!(Modifier.isFinal(beanClass.getModifiers()))) { this.classesToProxy.add(beanClass); pat.veto(); } else { LOG.warning("To use dependency-injection in converters/validators with properties, " + "you they aren't allowed to be 'final'."); } } protected <X> boolean hasInjectionPoints(Bean<X> bean) { return !bean.getInjectionPoints().isEmpty(); } protected <X> boolean hasNormalScopeAnnotation(Bean<X> bean, BeanManager beanManager) { Class<? extends Annotation> scopeAnnotationClass = bean.getScope(); return scopeAnnotationClass != null && beanManager.isNormalScope(scopeAnnotationClass); } protected <X> boolean hasPublicProperty(Class<X> beanClass) { for (Method currentMethod : beanClass.getMethods()) { if (currentMethod.getName().startsWith("set") && currentMethod.getName().length() > 3 && currentMethod.getParameterTypes().length == 1 && hasGetterMethod(beanClass, currentMethod.getName().substring(3))) { return true; } } return false; } protected boolean hasGetterMethod(Class beanClass, String name) { try { if (beanClass.getMethod("get" + name) != null || beanClass.getMethod("is" + name) != null) { return true; } } catch (Exception e) { return false; } return false; } public <X> void createBeans(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) { if (!this.isActivated) { return; } for (Class<?> originalClass : this.classesToProxy) { Bean bean = createBean(originalClass, beanManager); if (bean != null) { afterBeanDiscovery.addBean(bean); } } this.classesToProxy.clear(); } protected <T> Bean<T> createBean(Class<T> beanClass, BeanManager beanManager) { Class<? extends InvocationHandler> invocationHandlerClass = Converter.class.isAssignableFrom(beanClass) ? ConverterInvocationHandler.class : ValidatorInvocationHandler.class; AnnotatedType<T> annotatedType = new AnnotatedTypeBuilder<T>().readFromType(beanClass).create(); DeltaSpikeProxyContextualLifecycle lifecycle = new DeltaSpikeProxyContextualLifecycle(beanClass, invocationHandlerClass, ConverterAndValidatorProxyFactory.getInstance(), beanManager); BeanBuilder<T> beanBuilder = new BeanBuilder<T>(beanManager) .readFromType(annotatedType) .passivationCapable(true) .beanLifecycle(lifecycle); return beanBuilder.create(); } }