package org.needle4j.postconstruct;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import org.needle4j.NeedleContext;
import org.needle4j.ObjectUnderTestInstantiationException;
import org.needle4j.annotation.ObjectUnderTest;
import org.needle4j.configuration.NeedleConfiguration;
import org.needle4j.configuration.PostConstructExecuteStrategy;
import org.needle4j.processor.NeedleProcessor;
import org.needle4j.reflection.ReflectionUtil;
import static org.needle4j.configuration.PostConstructExecuteStrategy.ALWAYS;
import static org.needle4j.configuration.PostConstructExecuteStrategy.DEFAULT;
import static org.needle4j.configuration.PostConstructExecuteStrategy.NEVER;
/**
* Handles execution of postConstruction methods of instances marked with
* {@link ObjectUnderTest#postConstruct()}
* <p>
* Note: Behavior in an inheritance hierarchy is not defined by the common
* annotations specification
* </p>
*
* @author Jan Galinski, Holisticon AG (jan.galinski@holisticon.de)
* @author Heinz Wilming, akquinet AG (heinz.wilming@akquinet.de)
*/
public class PostConstructProcessor implements NeedleProcessor {
/**
* Internal Container of all Annotations that trigger invocation.
*/
private final Set<Class<? extends Annotation>> postConstructAnnotations = new HashSet<Class<? extends Annotation>>();
private final PostConstructExecuteStrategy postConstructExecuteStrategy;
public PostConstructProcessor(final Set<Class<?>> postConstructAnnotations) {
this(postConstructAnnotations, DEFAULT);
}
@SuppressWarnings("unchecked")
public PostConstructProcessor(final Set<Class<?>> postConstructAnnotations,
final PostConstructExecuteStrategy postConstructExecuteStrategy) {
for (final Class<?> annotation : postConstructAnnotations) {
this.postConstructAnnotations.add((Class<? extends Annotation>) annotation);
}
this.postConstructExecuteStrategy = postConstructExecuteStrategy;
}
/**
* calls process(instance) for each object under test, only if field is
* marked with {@link ObjectUnderTest}(postConstruct=true), else ignored.
*
*
* @param context
* the NeedleContext
* @throws ObjectUnderTestInstantiationException
*/
@Override
public void process(final NeedleContext context) {
if (this.postConstructExecuteStrategy == NEVER) {
return;
}
final Set<String> objectsUnderTestIds = context.getObjectsUnderTestIds();
for (String objectUnderTestId : objectsUnderTestIds) {
final ObjectUnderTest objectUnderTestAnnotation = context.getObjectUnderTestAnnotation(objectUnderTestId);
if (this.postConstructExecuteStrategy == ALWAYS ||
objectUnderTestAnnotation != null && objectUnderTestAnnotation.postConstruct()) {
try {
process(context.getObjectUnderTest(objectUnderTestId));
} catch (ObjectUnderTestInstantiationException e) {
throw new RuntimeException(e);
}
}
}
}
/**
* invokes @PostConstruct annotated method
*
* @param instance
* @throws ObjectUnderTestInstantiationException
*/
private void process(final Object instance) throws ObjectUnderTestInstantiationException {
final Set<Method> postConstructMethods = getPostConstructMethods(instance.getClass());
for (final Method method : postConstructMethods) {
try {
ReflectionUtil.invokeMethod(method, instance);
} catch (final Exception e) {
throw new ObjectUnderTestInstantiationException("error executing postConstruction method '"
+ method.getName() + "'", e);
}
}
}
/**
* @param instance
* @return all instance methods that are marked as postConstruction methods
*/
Set<Method> getPostConstructMethods(final Class<?> type) {
final Set<Method> postConstructMethods = new LinkedHashSet<Method>();
for (final Class<? extends Annotation> postConstructAnnotation : postConstructAnnotations) {
postConstructMethods.addAll(ReflectionUtil.getAllMethodsWithAnnotation(type, postConstructAnnotation));
}
return postConstructMethods;
}
}