Package org.jtester.module.core.helper

Source Code of org.jtester.module.core.helper.InjectionModuleHelper

package org.jtester.module.core.helper;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Set;

import ognl.DefaultMemberAccess;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;

import org.jtester.bytecode.reflector.helper.FieldHelper;
import org.jtester.exception.JTesterException;
import org.jtester.utility.AnnotationUtils;
import org.jtester.utility.JTesterLogger;

/**
* Class containing static methods that implement explicit injection using OGNL
* expressions, and auto-injection by type.
*/
@SuppressWarnings("rawtypes")
public class InjectionModuleHelper {

  /**
   * Explicit injection of the objectToInject into the specified property of
   * the target. The property should be a correct OGNL expression.
   *
   * @param objectToInject
   *            The object that is injected
   * @param target
   *            The target object
   * @param property
   *            The OGNL expression that defines where the object will be
   *            injected, not null
   * @return The object that was replaced by the injection
   */
  public static Object injectInto(Object objectToInject, Object target, String property) {
    if (target == null) {
      throw new JTesterException("Target for injection should not be null");
    }
    try {
      OgnlContext ognlContext = new OgnlContext();
      ognlContext.setMemberAccess(new DefaultMemberAccess(true));
      Object ognlExpression = Ognl.parseExpression(property);

      Object oldValue = null;
      try {
        Ognl.getValue(ognlExpression, ognlContext, target);

      } catch (Throwable e) {
        JTesterLogger
            .warn("Unable to retrieve current value of field to inject into. Will not be able to restore value after injection.",
                e);
      }
      Ognl.setValue(ognlExpression, ognlContext, target, objectToInject);
      return oldValue;

    } catch (OgnlException e) {
      throw new JTesterException("Failed to set value using OGNL expression " + property, e);
    }
  }

  /**
   * Performs auto-injection by type of the objectToInject on the target
   * object.
   *
   * @param objectToInject
   *            The object that is injected
   * @param objectToInjectType
   *            The type of the object. This should be the type of the object
   *            or one of his super-types or implemented interfaces. This type
   *            is used for property type matching on the target object
   * @param target
   *            The object into which the objectToInject is injected
   * @param propertyAccess
   *            Defines if field or setter injection is used
   * @return The object that was replaced by the injection
   */
  public static Object injectIntoByType(Object objectToInject, Type objectToInjectType, Object target) {
    if (target == null) {
      throw new JTesterException("Target for injection should not be null");
    }
    return injectIntoFieldByType(objectToInject, objectToInjectType, target, target.getClass());
  }

  public static void injectIntoAnnotated(Object objectToInject, Object target, Class<? extends Annotation> annotation) {
    injectIntoAnnotatedFields(objectToInject, target, annotation);
    injectIntoAnnotatedMethods(objectToInject, target, annotation);
  }

  public static void injectIntoAnnotatedMethods(Object objectToInject, Object target,
      Class<? extends Annotation> annotation) {
    Set<Method> annotatedMethods = AnnotationUtils.getMethodsAnnotatedWith(target.getClass(), annotation);
    for (Method annotatedMethod : annotatedMethods) {
      try {
        annotatedMethod.invoke(target, objectToInject);
      } catch (IllegalArgumentException e) {
        throw new JTesterException(
            "Method "
                + annotatedMethod.getName()
                + " annotated with "
                + annotation.getName()
                + " must have exactly one argument with a type equal to or a superclass / implemented interface of "
                + objectToInject.getClass().getSimpleName());
      } catch (IllegalAccessException e) {
        throw new JTesterException("Unable to inject value into following method annotated with "
            + annotation.getName() + ": " + annotatedMethod.getName(), e);
      } catch (InvocationTargetException e) {
        throw new JTesterException("Unable to inject value into following method annotated with "
            + annotation.getName() + ": " + annotatedMethod.getName(), e);
      }
    }
  }

  public static void injectIntoAnnotatedFields(Object objectToInject, Object target,
      Class<? extends Annotation> annotation) {
    Set<Field> annotatedFields = AnnotationUtils.getFieldsAnnotatedWith(target.getClass(), annotation);
    for (Field annotatedField : annotatedFields) {
      FieldHelper.setFieldValue(target, annotatedField, objectToInject);
    }
  }

  /**
   * Performs auto-injection on a field by type of the objectToInject into the
   * given target object or targetClass, depending on the value of isStatic.
   * The object is injected on one single field, if there is more than one
   * candidate field, a {@link JTesterException} is thrown. We try to inject
   * the object on the most specific field, this means that when there are
   * muliple fields of one of the super-types or implemented interfaces of the
   * field, the one that is lowest in the hierarchy is chosen (if possible,
   * otherwise, a {@link JTesterException} is thrown.
   *
   * @param objectToInject
   *            The object that is injected
   * @param objectToInjectType
   *            The type of the object that is injected
   * @param target
   *            The target object (only used when isStatic is false)
   * @param targetClass
   *            The target class (only used when isStatis is true)
   * @param isStatic
   *            Indicates wether injection should be performed on the target
   *            object or on the target class
   * @return The object that was replaced by the injection
   */
  private static Object injectIntoFieldByType(Object objectToInject, Type objectToInjectType, Object target,
      Class targetClass) {
    // Try to find a field with an exact matching type
    Field fieldToInjectTo = null;
    Set<Field> fieldsWithExactType = FieldHelper.getFieldsOfType(targetClass, objectToInjectType);
    if (fieldsWithExactType.size() > 1) {
      StringBuilder message = new StringBuilder("More than one field with type " + objectToInjectType
          + " found in " + targetClass.getSimpleName() + ".");
      if (objectToInjectType instanceof Class) {
        message.append(" If the target is a generic type, this can be caused by type erasure.");
      }
      message.append(" Specify the target field explicitly instead of injecting into by type.");
      throw new JTesterException(message.toString());

    } else if (fieldsWithExactType.size() == 1) {
      fieldToInjectTo = fieldsWithExactType.iterator().next();

    } else {
      // Try to find a supertype field:
      // If one field exist that has a type which is more specific than
      // all other fields of the given type,
      // this one is taken. Otherwise, an exception is thrown
      Set<Field> fieldsOfType = FieldHelper.getFieldsAssignableFrom(targetClass, objectToInjectType);
      if (fieldsOfType.size() == 0) {
        throw new JTesterException("No field with (super)type " + objectToInjectType + " found in "
            + targetClass.getSimpleName());
      }
      for (Field field : fieldsOfType) {
        boolean moreSpecific = true;
        for (Field compareToField : fieldsOfType) {
          if (field != compareToField) {
            if (field.getClass().isAssignableFrom(compareToField.getClass())) {
              moreSpecific = false;
              break;
            }
          }
        }
        if (moreSpecific) {
          fieldToInjectTo = field;
          break;
        }
      }
      if (fieldToInjectTo == null) {
        throw new JTesterException("Multiple candidate target fields found in " + targetClass.getSimpleName()
            + ", with none of them more specific than all others.");
      }
    }

    // Field to inject into found, inject the object and return old value
    Object oldValue = null;
    try {
      oldValue = FieldHelper.getFieldValue(target, fieldToInjectTo);

    } catch (Throwable e) {
      JTesterLogger
          .warn("Unable to retrieve current value of field to inject into. Will not be able to restore value after injection.",
              e);
    }
    FieldHelper.setFieldValue(target, fieldToInjectTo, objectToInject);
    return oldValue;
  }
}
TOP

Related Classes of org.jtester.module.core.helper.InjectionModuleHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.