/*
* Copyright open knowledge GmbH
*
* 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 de.openknowledge.cdi.common.property;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Properties;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.inject.Inject;
import org.apache.commons.lang.ClassUtils;
import de.openknowledge.cdi.common.property.source.PropertyProvider;
import de.openknowledge.cdi.common.qualifier.Current;
/**
* Our property source.
*
* @author Arne Limburg - open knowledge GmbH
* @author Jens Schumann - open knowledge GmbH
*/
@ApplicationScoped
public class PropertiesLoader {
@Inject
@Current
private ApplicationProperties currentValues = new ApplicationProperties();
@Inject
private PropertyProvider provider;
@Produces
@Property(name = "any")
public Properties produceWildcardProperties(InjectionPoint injectionPoint) {
return provider.getPropertyValues(injectionPoint);
}
/**
* Creates an object, assuming the object has a constructor that takes a {@link String} argument.
* The type of the object is determined from the type of the specified injection point.
* The value for the constructor argument of type {@link String} is taken from the {@link Property}
* specified at the injection point.
*
* @param injectionPoint the injection point to get the type and the property from
* @return the value which's type depends on the type of the injection point
*/
@Produces
@Property(name = "any")
public Object produceProperty(InjectionPoint injectionPoint) {
Class<?> type = toClass(injectionPoint.getType());
return newInstance(injectionPoint, getPropertyValue(injectionPoint, type), ClassUtils.primitiveToWrapper(type));
}
/**
* Creates a {@link Character} object from the specified injection point.
* The character value is taken from the {@link Property} specified at the injection point.
* This method is needed in addition to {@link #produceProperty(InjectionPoint)} since
* {@link Character} has no constructor that takes a {@link String} argument.
*
* @param injectionPoint the injection point to get the property from
* @return the character value
*/
@Produces
@Property(name = "any")
public Character produceCharacterProperty(InjectionPoint injectionPoint) {
Class<?> type = toClass(injectionPoint.getType());
String value = getPropertyValue(injectionPoint, type);
if (value.length() != 1) {
throw buildIllegalArgumentException(injectionPoint, value, type, null);
}
return value.charAt(0);
}
protected String getPropertyValue(InjectionPoint injectionPoint, Class<?> targetType) {
String value = provider.getPropertyValue(injectionPoint);
assertPrimitiveNotNull(injectionPoint, value, targetType);
currentValues.registerPropertyValue(injectionPoint, value);
return value;
}
static Class<?> toClass(Type type) {
if (type instanceof Class<?>) {
return (Class<?>)type;
} else if (type instanceof ParameterizedType) {
return toClass(((ParameterizedType)type).getRawType());
} else {
throw new IllegalArgumentException("unsupported type for property injection: " + type);
}
}
private <T> T newInstance(InjectionPoint injectionPoint, String value, Class<T> type) {
if (value == null) {
return null;
}
try {
return type.getConstructor(String.class).newInstance(value);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException(type.getName() + " must have String-constructor to be injected");
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("String-constructor must be public");
} catch (InstantiationException e) {
throw buildIllegalArgumentException(injectionPoint, value, type, e);
} catch (InvocationTargetException e) {
throw buildIllegalArgumentException(injectionPoint, value, type, e.getTargetException());
}
}
private void assertPrimitiveNotNull(InjectionPoint injectionPoint, String value, Class<?> expectedType) {
if (expectedType.isPrimitive() && value == null) {
throw buildIllegalArgumentException(injectionPoint, value, expectedType, null);
}
}
private RuntimeException buildIllegalArgumentException(InjectionPoint injectionPoint,
String value,
Class<?> expectedType,
Throwable cause) {
StringBuilder messageBuilder = new StringBuilder();
messageBuilder.append("Unable to convert ");
if (value == null) {
messageBuilder.append("null value");
} else {
messageBuilder.append('"').append(value).append('"');
}
messageBuilder.append(" to ").append(expectedType.getName());
messageBuilder.append(" for injection point ").append(injectionPoint);
return new IllegalArgumentException(messageBuilder.toString(), cause);
}
}