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 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 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();
      }
   }

   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 - 2024 Weber Informatics LLC | Privacy Policy