
org.richfaces.application.DependencyInjectorImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of richfaces-core Show documentation
Show all versions of richfaces-core Show documentation
The RichFaces core framework.
The newest version!
/*
* JBoss, Home of Professional Open Source
* Copyright 2013, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.richfaces.application;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import org.richfaces.log.Logger;
import org.richfaces.log.RichfacesLogger;
import org.richfaces.resource.PostConstructResource;
import org.richfaces.resource.ResourceParameter;
import org.richfaces.resource.ResourceParameterELResolver;
/**
* @author Nick Belaevski
*
*/
public class DependencyInjectorImpl implements DependencyInjector {
private static final Logger LOGGER = RichfacesLogger.APPLICATION.getLogger();
private ConcurrentMap, IntrospectionData> classesCache = new ConcurrentHashMap<>();
private void invokeMethod(Object bean, Method method) throws IllegalArgumentException, IllegalAccessException,
InvocationTargetException {
if (method != null) {
method.setAccessible(true);
method.invoke(bean);
}
}
private boolean isUncheckedException(Class> type) {
// JLS 2nd edition - 11.2 Compile-Time Checking of Exceptions
return RuntimeException.class.isAssignableFrom(type) || Error.class.isAssignableFrom(type);
}
private void verifyPostConstructMethod(Method method) {
// TODO - allow FacesContext to be passed
if (method.getParameterTypes().length != 0) {
throw new IllegalStateException(MessageFormat.format("Post-construction method {0} has one or more parameters",
method.toString()));
}
if (!Void.TYPE.equals(method.getReturnType())) {
throw new IllegalStateException(MessageFormat.format("Post-construction method {0} has incorrect return type",
method.toString()));
}
if ((method.getModifiers() & Modifier.STATIC) != 0) {
throw new IllegalStateException(MessageFormat.format("Post-construction method {0} is static", method.toString()));
}
Class>[] exceptionTypes = method.getExceptionTypes();
for (Class> exceptionType : exceptionTypes) {
if (isUncheckedException(exceptionType)) {
continue;
}
throw new IllegalStateException(MessageFormat.format("Post-construction method {0} throws checked exception",
method.toString()));
}
}
private void inspectMethod(Method method, Class extends Annotation> annotationClass, IntrospectionData introspectionData) {
Annotation annotation = method.getAnnotation(annotationClass);
if (annotation != null) {
verifyPostConstructMethod(method);
if (introspectionData.getPostConstructMethod() != null) {
throw new IllegalStateException(MessageFormat.format(
"There are two conflicting post-construction methods: {0} and {1}", method.toString(), introspectionData
.getPostConstructMethod().toString()));
}
introspectionData.setPostConstructMethod(method);
}
}
private void locatePostConstructMethods(Class> clazz, IntrospectionData introspectionData) {
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
inspectMethod(method, PostConstructResource.class, introspectionData);
}
Class> superclass = clazz.getSuperclass();
if (!Object.class.equals(superclass)) {
locatePostConstructMethods(superclass, introspectionData);
}
}
private void locateManagedPropertyFields(Class> clazz, Map fieldsMap) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
ResourceParameter dependency = field.getAnnotation(ResourceParameter.class);
if (dependency != null) {
String propertyName = field.getName();
fieldsMap.putIfAbsent(propertyName, dependency);
}
}
Class> superclass = clazz.getSuperclass();
if (!Object.class.equals(superclass)) {
locateManagedPropertyFields(superclass, fieldsMap);
}
}
private T getAnnotation(PropertyDescriptor descriptor, Class annotationClass) {
T annotation = null;
Method writeMethod = descriptor.getWriteMethod();
if (writeMethod != null) {
annotation = writeMethod.getAnnotation(annotationClass);
}
if (annotation == null) {
Method readMethod = descriptor.getReadMethod();
if (readMethod != null) {
annotation = readMethod.getAnnotation(annotationClass);
}
}
return annotation;
}
private void locateManagedPropertyDescriptors(Class> clazz, IntrospectionData introspectionData,
Map injectableFields) {
try {
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
if (beanInfo != null) {
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
if (descriptors != null) {
for (PropertyDescriptor descriptor : descriptors) {
String propertyName = descriptor.getName();
ResourceParameter dependency = injectableFields.get(propertyName);
if (dependency == null) {
dependency = getAnnotation(descriptor, ResourceParameter.class);
}
if (dependency != null) {
Injector> injector = new PropertyDependencyInjector(descriptor, dependency);
introspectionData.addInjector(propertyName, injector);
}
}
}
}
} catch (IntrospectionException e) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(e.getMessage(), e);
}
} finally {
Introspector.flushFromCaches(clazz);
}
}
protected IntrospectionData createIntrospectionData(Class> beanClass) {
IntrospectionData introspectionData = new IntrospectionData();
Map injectableFields = new HashMap<>();
locateManagedPropertyFields(beanClass, injectableFields);
locateManagedPropertyDescriptors(beanClass, introspectionData, injectableFields);
locatePostConstructMethods(beanClass, introspectionData);
return introspectionData;
}
public void inject(FacesContext context, Object bean) {
Class> beanClass = bean.getClass();
IntrospectionData introspectionData = classesCache.get(beanClass);
if (introspectionData == null) {
introspectionData = createIntrospectionData(beanClass);
classesCache.put(beanClass, introspectionData);
}
try {
Map> injectorsMap = introspectionData.getInjectorsMap();
if (!injectorsMap.isEmpty()) {
for (Injector> injector : injectorsMap.values()) {
injector.inject(context, bean);
}
}
Method postConstructMethod = introspectionData.getPostConstructMethod();
if (postConstructMethod != null) {
invokeMethod(bean, postConstructMethod);
}
} catch (IllegalArgumentException e) {
throw new FacesException(e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new FacesException(e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new FacesException(e.getMessage(), e);
}
}
private abstract static class Injector {
private PropertyDescriptor propertyDescriptor;
private T dependency;
public Injector(PropertyDescriptor propertyDescriptor, T dependency) {
super();
this.propertyDescriptor = propertyDescriptor;
this.dependency = dependency;
}
protected T getDependency() {
return dependency;
}
protected PropertyDescriptor getPropertyDescriptor() {
return propertyDescriptor;
}
protected abstract Object evaluateProperty(FacesContext context, Class> propertyType);
public void inject(FacesContext context, Object bean) throws IllegalArgumentException, IllegalAccessException,
InvocationTargetException {
Method writeMethod = propertyDescriptor.getWriteMethod();
if (writeMethod != null) {
writeMethod.invoke(bean, evaluateProperty(context, propertyDescriptor.getPropertyType()));
} else {
throw new IllegalStateException(MessageFormat.format("Write method for property {0} doesn't exist",
propertyDescriptor.getName()));
}
}
}
private static final class PropertyDependencyInjector extends Injector {
public PropertyDependencyInjector(PropertyDescriptor propertyDescriptor, ResourceParameter dependency) {
super(propertyDescriptor, dependency);
}
private Object getExpressionValue(FacesContext context, String expressionString, Class> expectedType) {
ExpressionFactory expressionFactory = context.getApplication().getExpressionFactory();
ValueExpression expression = expressionFactory.createValueExpression(context.getELContext(), expressionString,
expectedType);
return expression.getValue(context.getELContext());
}
protected Object evaluateProperty(FacesContext context, Class> propertyType) {
Class> expectedType;
if (!propertyType.isPrimitive()) {
expectedType = Object.class;
} else {
expectedType = propertyType;
}
ResourceParameter resourceParameter = getDependency();
String expression = resourceParameter.expression();
String name = resourceParameter.name();
if (expression.length() != 0 && name.length() != 0) {
throw new IllegalStateException(MessageFormat.format(
"'name' and 'expression' should not be specified simultaneously: {0}", resourceParameter));
}
Object propertyValue = null;
if (expression.length() != 0) {
propertyValue = getExpressionValue(context, expression, expectedType);
} else {
if (name.length() == 0) {
name = getPropertyDescriptor().getName();
}
Map parameters = (Map) context.getAttributes().get(
ResourceParameterELResolver.CONTEXT_ATTRIBUTE_NAME);
propertyValue = parameters.get(name);
}
if (propertyValue == null || "".equals(propertyValue)) {
String defaultValue = resourceParameter.defaultValue();
if (defaultValue != null && defaultValue.length() != 0) {
propertyValue = getExpressionValue(context, defaultValue, expectedType);
}
}
if (propertyValue != null) {
propertyValue = context.getApplication().getExpressionFactory().coerceToType(propertyValue, propertyType);
}
return propertyValue;
}
}
private static final class IntrospectionData {
private Method postConstructMethod = null;
private Map> injectorsMap = null;
public Map> getInjectorsMap() {
if (injectorsMap != null) {
return injectorsMap;
}
return Collections.emptyMap();
}
public void addInjector(String propertyName, Injector> injector) {
if (injectorsMap == null) {
injectorsMap = new HashMap<>();
}
injectorsMap.put(propertyName, injector);
}
public Method getPostConstructMethod() {
return postConstructMethod;
}
public void setPostConstructMethod(Method postConstructMethod) {
this.postConstructMethod = postConstructMethod;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy