/*
* Copyright 2014 byteslounge.com (Gonçalo Marques).
*
* 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 com.byteslounge.cdi.extension;
import java.lang.reflect.Field;
import java.util.Set;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.enterprise.inject.spi.InjectionTarget;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.byteslounge.cdi.annotation.Property;
import com.byteslounge.cdi.configuration.ExtensionConfiguration;
import com.byteslounge.cdi.converter.PropertyConverter;
import com.byteslounge.cdi.converter.PropertyConverterFactory;
import com.byteslounge.cdi.exception.PropertyResolverException;
import com.byteslounge.cdi.format.PropertyFormat;
import com.byteslounge.cdi.resolver.bean.PropertyResolverBean;
import com.byteslounge.cdi.resolver.context.ResolverContext;
/**
* Checks if the CDI injection target contains any fields annotated with
* {@link Property} and injects the respective resolved properties accordingly.
*
* @author Gonçalo Marques
* @since 1.0.0
*/
public class PropertyResolverInjectionTarget<T> implements InjectionTarget<T> {
private final InjectionTarget<T> injectionTarget;
private final AnnotatedType<T> annotatedType;
private final PropertyResolverBean propertyResolverBean;
private final PropertyFormat propertyFormat;
private static final Logger logger = LoggerFactory.getLogger(PropertyResolverInjectionTarget.class);
public PropertyResolverInjectionTarget(InjectionTarget<T> injectionTarget, AnnotatedType<T> annotatedType,
PropertyResolverBean propertyResolverBean,
PropertyFormat propertyFormat) {
this.injectionTarget = injectionTarget;
this.annotatedType = annotatedType;
this.propertyResolverBean = propertyResolverBean;
this.propertyFormat = propertyFormat;
}
/**
* See {@link InjectionTarget#dispose(Object)}
*/
@Override
public void dispose(T instance) {
injectionTarget.dispose(instance);
}
/**
* See {@link InjectionTarget#getInjectionPoints()}
*/
@Override
public Set<InjectionPoint> getInjectionPoints() {
return injectionTarget.getInjectionPoints();
}
/**
* See {@link InjectionTarget#produce(CreationalContext)}
*/
@Override
public T produce(CreationalContext<T> ctx) {
return injectionTarget.produce(ctx);
}
/**
* See {@link InjectionTarget#inject(Object, CreationalContext)}
*/
@Override
public void inject(T instance, CreationalContext<T> ctx) {
injectionTarget.inject(instance, ctx);
for (Field field : annotatedType.getJavaClass().getDeclaredFields()) {
Property annotation = field.getAnnotation(Property.class);
if (annotation != null) {
String key = annotation.value();
String bundleName = annotation.resourceBundleBaseName().length() > 0 ? annotation.resourceBundleBaseName() : ExtensionConfiguration.INSTANCE
.getResourceBundleDefaultBaseName();
if (bundleName == null) {
String errorMessage = "Property bundle name must have a configured default value (see github project instructions) or it must be configured in @"
+ Property.class.getSimpleName() + " annotation.";
logger.error(errorMessage);
throw new PropertyResolverException(errorMessage);
}
field.setAccessible(true);
String value = propertyResolverBean.invoke(ResolverContext.create(key, bundleName), ctx);
if (logger.isDebugEnabled()) {
logger.debug("Resolved property with key " + key + " to " + value);
}
if (annotation.parameters().length > 0) {
if (field.getType().equals(String.class)) {
value = propertyFormat.formatMessage(value, (Object[]) annotation.parameters());
} else {
logger.warn("Found property with defined parameters for formatting but property type is not of type " + String.class.getSimpleName()
+ ". Skipping message format... [" + field.getDeclaringClass().getSimpleName() + "." + field.getName() + "]");
}
}
PropertyConverter<?> propertyConverter = PropertyConverterFactory.getConverter(field.getType());
Object convertedValue = null;
if (propertyConverter != null) {
if (logger.isDebugEnabled()) {
logger.debug("Converting property using converter " + propertyConverter.getClass().getSimpleName());
}
convertedValue = propertyConverter.convert(value);
}
if (logger.isDebugEnabled()) {
logger.debug("Applying property value " + value + " to field " + field.getDeclaringClass().getSimpleName() + "." + field.getName());
}
try {
field.set(instance, convertedValue != null ? convertedValue : value);
} catch (IllegalAccessException e) {
throw new RuntimeException("Could not set property in managed bean field", e);
}
}
}
}
/**
* See {@link InjectionTarget#postConstruct(Object)}
*/
@Override
public void postConstruct(T instance) {
injectionTarget.postConstruct(instance);
}
/**
* See {@link InjectionTarget#preDestroy(Object)}
*/
@Override
public void preDestroy(T instance) {
injectionTarget.preDestroy(instance);
}
}