ConfigurationClassContext.java
package fi.eis.libraries.di.context.configclass;
import fi.eis.libraries.di.DependencyInjection;
import fi.eis.libraries.di.context.Context;
import fi.eis.libraries.di.logger.LogLevel;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A Configuration Class is a Spring-style class that contains the required configuration
* to do dependency injection. It contains the definition of dependency associations so that
* it can be used to figure out which class depends on what.
*
* This class contains the logic to handle such classes, so we create a dependency injection context
* based on such class or several classes. This class goes through all the methods in a
* configuration class, creating the needed dependencies and instantiating the configured classes
* with their dependencies.
*/
public class ConfigurationClassContext extends Context {
private final Map<Class, Object> classObjectMap = new HashMap<>();
public ConfigurationClassContext(Class... configurationClasses) {
this(LogLevel.NONE, configurationClasses);
}
public ConfigurationClassContext(LogLevel logLevel, Class... configurationClassInstances) {
setLogLevel(logLevel);
try {
List<Map.Entry<Object, Method>> confClassCreationMethodTuples = new ArrayList<>();
for (Class configurationClass: configurationClassInstances){
Object configurationClassInstance = configurationClass.newInstance();
for (Method m : configurationClass.getMethods()) {
if (m.getDeclaringClass() != Object.class) {
logger.debug("Got method " + m);
confClassCreationMethodTuples.add(tuple(configurationClassInstance, m));
}
}
}
confClassCreationMethodTuples.sort(ConfClassCreationMethodTuplesComparator);
logger.debug("tuples: " + confClassCreationMethodTuples);
for (Map.Entry<Object,Method> confClassCreationMethodTuple: confClassCreationMethodTuples) {
Method method = confClassCreationMethodTuple.getValue();
Object instance = newInstance(confClassCreationMethodTuple.getKey(), method);
classObjectMap.put(method.getReturnType(), instance);
logger.debug("instantiated and stored instance for class " + method.getReturnType());
}
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
throw new IllegalArgumentException(e);
}
logger.debug("class-instance module: " + DependencyInjection.module(classObjectMap));
super.modules.add(DependencyInjection.module(classObjectMap)) ;
}
private static Map.Entry<Object,Method> tuple(Object object, Method method) {
return new HashMap.SimpleEntry<>(object, method);
}
private static final Comparator<Map.Entry<Object,Method>> ConfClassCreationMethodTuplesComparator =
Comparator.comparingInt(o -> o.getValue().getParameterTypes().length);
private Object newInstance(Object configurationClass, Method method) throws InstantiationException,
IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class[] parameterTypes = method.getParameterTypes();
Object[] paramArr = new Object[parameterTypes.length];
int i = 0;
for(Class c : parameterTypes) {
paramArr[i++] = classObjectMap.get(c);
}
return method.invoke(configurationClass, paramArr);
}
}