/*
* Copyright (c) 2006 Rogério Liesenfeld
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.injection;
import java.lang.reflect.*;
import java.util.*;
import java.util.Map.*;
import javax.annotation.*;
import javax.servlet.*;
import mockit.internal.reflection.*;
import mockit.internal.state.*;
import static mockit.internal.injection.InjectionPoint.*;
import static mockit.internal.util.Utilities.*;
public final class LifecycleMethods
{
@Nonnull private final List<Class<?>> classesSearched;
@Nonnull private final Map<Class<?>, Method> initializationMethods;
@Nonnull private final Map<Class<?>, Method> terminationMethods;
@Nonnull private final Map<Class<?>, Object> objectsWithTerminationMethodsToExecute;
@Nullable Object servletConfig;
LifecycleMethods()
{
classesSearched = new ArrayList<Class<?>>();
initializationMethods = new IdentityHashMap<Class<?>, Method>();
terminationMethods = new IdentityHashMap<Class<?>, Method>();
objectsWithTerminationMethodsToExecute = new IdentityHashMap<Class<?>, Object>();
}
public void findLifecycleMethods(@Nonnull Class<?> testedClass)
{
if (testedClass.isInterface() || classesSearched.contains(testedClass)) {
return;
}
boolean isServlet = isServlet(testedClass);
Class<?> classWithLifecycleMethods = testedClass;
do {
findLifecycleMethodsInSingleClass(isServlet, classWithLifecycleMethods);
classWithLifecycleMethods = classWithLifecycleMethods.getSuperclass();
}
while (classWithLifecycleMethods != Object.class);
classesSearched.add(testedClass);
}
private void findLifecycleMethodsInSingleClass(boolean isServlet, @Nonnull Class<?> classWithLifecycleMethods)
{
Method initializationMethod = null;
Method terminationMethod = null;
int methodsFoundInSameClass = 0;
for (Method method : classWithLifecycleMethods.getDeclaredMethods()) {
if (method.isSynthetic()) {
continue;
}
if (initializationMethod == null && isInitializationMethod(method, isServlet)) {
initializationMethods.put(classWithLifecycleMethods, method);
initializationMethod = method;
methodsFoundInSameClass++;
}
else if (terminationMethod == null && isTerminationMethod(method, isServlet)) {
terminationMethods.put(classWithLifecycleMethods, method);
terminationMethod = method;
methodsFoundInSameClass++;
}
if (methodsFoundInSameClass == 2) {
break;
}
}
}
private static boolean isInitializationMethod(@Nonnull Method method, boolean isServlet)
{
if (method.isAnnotationPresent(PostConstruct.class)) {
return true;
}
if (isServlet && "init".equals(method.getName())) {
Class<?>[] parameterTypes = method.getParameterTypes();
return parameterTypes.length == 1 && parameterTypes[0] == ServletConfig.class;
}
return false;
}
private static boolean isTerminationMethod(@Nonnull Method method, boolean isServlet)
{
return
method.isAnnotationPresent(PreDestroy.class) ||
isServlet && "destroy".equals(method.getName()) && method.getParameterTypes().length == 0;
}
public void executeInitializationMethodsIfAny(@Nonnull Class<?> testedClass, @Nonnull Object testedObject)
{
Class<?> superclass = testedClass.getSuperclass();
if (superclass != Object.class) {
executeInitializationMethodsIfAny(superclass, testedObject);
}
Method postConstructMethod = initializationMethods.get(testedClass);
if (postConstructMethod != null) {
executeInitializationMethod(testedObject, postConstructMethod);
}
Method preDestroyMethod = terminationMethods.get(testedClass);
if (preDestroyMethod != null) {
objectsWithTerminationMethodsToExecute.put(testedClass, testedObject);
}
}
private void executeInitializationMethod(@Nonnull Object testedObject, @Nonnull Method initializationMethod)
{
Object[] args = NO_ARGS;
if ("init".equals(initializationMethod.getName()) && initializationMethod.getParameterTypes().length == 1) {
args = new Object[] {servletConfig};
}
TestRun.exitNoMockingZone();
try {
MethodReflection.invoke(testedObject, initializationMethod, args);
}
finally {
TestRun.enterNoMockingZone();
}
}
void executeTerminationMethodsIfAny()
{
try {
for (Entry<Class<?>, Object> testedClassAndObject : objectsWithTerminationMethodsToExecute.entrySet()) {
executeTerminationMethod(testedClassAndObject.getKey(), testedClassAndObject.getValue());
}
}
finally {
objectsWithTerminationMethodsToExecute.clear();
}
}
private void executeTerminationMethod(@Nonnull Class<?> testedClass, @Nonnull Object testedObject)
{
Method terminationMethod = terminationMethods.get(testedClass);
TestRun.exitNoMockingZone();
try {
MethodReflection.invoke(testedObject, terminationMethod);
}
catch (RuntimeException ignore) {}
catch (AssertionError ignore) {}
finally {
TestRun.enterNoMockingZone();
}
}
}