mockit.internal.injection.LifecycleMethods Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmockit Show documentation
Show all versions of jmockit Show documentation
JMockit is a Java toolkit for automated developer testing.
It contains mocking/faking APIs and a code coverage tool, supporting both JUnit and TestNG.
The mocking APIs allow all kinds of Java code, without testability restrictions, to be tested
in isolation from selected dependencies.
/*
* Copyright (c) 2006 JMockit developers
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit.internal.injection;
import java.lang.annotation.*;
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> classesSearched;
@Nonnull private final Map, Method> initializationMethods;
@Nonnull private final Map, Method> terminationMethods;
@Nonnull private final Map, Object> objectsWithTerminationMethodsToExecute;
@Nullable Object servletConfig;
LifecycleMethods() {
classesSearched = new ArrayList<>();
initializationMethods = new IdentityHashMap<>();
terminationMethods = new IdentityHashMap<>();
objectsWithTerminationMethodsToExecute = new IdentityHashMap<>();
}
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 (hasLifecycleAnnotation(method, true)) {
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 hasLifecycleAnnotation(@Nonnull Method method, boolean postConstruct) {
try {
Class extends Annotation> lifecycleAnnotation = postConstruct ? PostConstruct.class : PreDestroy.class;
if (method.isAnnotationPresent(lifecycleAnnotation)) {
return true;
}
}
catch (NoClassDefFoundError ignore) { /* can occur on JDK 9 */ }
return false;
}
private static boolean isTerminationMethod(@Nonnull Method method, boolean isServlet) {
return
hasLifecycleAnnotation(method, false) ||
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, 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 | AssertionError ignore) {}
finally {
TestRun.enterNoMockingZone();
}
}
}