All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mockit.internal.injection.LifecycleMethods Maven / Gradle / Ivy

/*
 * Copyright (c) 2006 JMockit developers
 * This file is subject to the terms of the MIT license (see LICENSE.txt).
 */
package mockit.internal.injection;

import static mockit.internal.injection.InjectionPoint.SERVLET_CLASS;
import static mockit.internal.injection.InjectionPoint.isServlet;
import static mockit.internal.reflection.ParameterReflection.getParameterCount;
import static mockit.internal.util.Utilities.NO_ARGS;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.servlet.ServletConfig;

import mockit.internal.reflection.MethodReflection;
import mockit.internal.state.TestRun;

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
    private 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 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()) && getParameterCount(method) == 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()) && getParameterCount(initializationMethod) == 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();
        }
    }

    void getServletConfigForInitMethodsIfAny(@NonNull List injectables,
            @NonNull Object testClassInstance) {
        if (SERVLET_CLASS != null) {
            for (InjectionProvider injectable : injectables) {
                if (injectable.getDeclaredType() == ServletConfig.class) {
                    servletConfig = injectable.getValue(testClassInstance);
                    break;
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy