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

com.appland.appmap.transform.annotations.HookClassSystem Maven / Gradle / Ivy

There is a newer version: 1.27.1
Show newest version
package com.appland.appmap.transform.annotations;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.tinylog.TaggedLogger;

import com.appland.appmap.config.AppMapConfig;
import com.appland.appmap.transform.annotations.AnnotationUtil.AnnotatedBehavior;
import com.appland.appmap.util.Logger;

import javassist.CtBehavior;
import javassist.CtClass;
import javassist.NotFoundException;

public class HookClassSystem extends SourceMethodSystem {
  private static final TaggedLogger logger = AppMapConfig.getLogger(null);

  private final static Boolean IGNORE_CHILDREN_DEFAULT = false;

  private String targetClass = null;
  private String targetMethod = null;
  private Boolean ignoresChildren = IGNORE_CHILDREN_DEFAULT;
  private final Integer position;
  private final static Signatures SIGNATURE_DEFAULT = null;
  private List signatures = null;

  private HookClassSystem(CtBehavior behavior, int position, List signatures) {
    super(behavior, HookClass.class);
    this.position = position;
    this.signatures = signatures;
  }

  /**
   * Factory method. Reads any relevant annotation information and caches it.
   * @param behavior The hook behavior
   * @return A new {@code HookClassSystem} if {@link HookClass} is found. Otherwise, {@code null}.
   */
  public static ISystem from(CtBehavior behavior) {
    try {
      HookClass hookClass = (HookClass) behavior.getAnnotation(HookClass.class);
      if (hookClass == null) {
        hookClass = (HookClass) behavior.getDeclaringClass().getAnnotation(HookClass.class);
      }

      if (hookClass == null) {
        return null;
      }

      if (hookClass.value() == null) {
        return null;
      }

      Boolean ignoresChildren = (Boolean) AnnotationUtil.getValue(behavior,
          IgnoreChildren.class,
          IGNORE_CHILDREN_DEFAULT);

      Integer position = AnnotationUtil.getPosition(behavior, HookClass.class, ISystem.HOOK_POSITION_DEFAULT);

      Signature[] signatures = (Signature[]) AnnotationUtil.getObject(behavior, Signatures.class, "value",
          SIGNATURE_DEFAULT);
      Signature signature = (Signature) behavior.getAnnotation(Signature.class);
      List types = null;
      if (signatures != null || signature != null) {
        types = new ArrayList();
        if (signatures != null) {
          Collections.addAll(types, signatures);
        } else if (signature != null) {
          types.add(signature);
        }
      }

      HookClassSystem system = new HookClassSystem(behavior, position, types);
      system.ignoresChildren = ignoresChildren;
      system.targetClass = hookClass.value();
      system.targetMethod = hookClass.method() == null || hookClass.method().isEmpty()
          ? behavior.getName()
          : hookClass.method();

      return system;
    } catch (Exception e) {
      Logger.println(e);
      return null;
    }
  }

  @Override
  public Boolean match(CtBehavior behavior, Map hookContext) {
    boolean ret = doMatch(behavior, hookContext);
    if (ret) {
      AnnotationUtil.setAnnotation(new AnnotatedBehavior(behavior), AppMapAgentMethod.class);
    }
    return ret;
  }

  private Boolean doMatch(CtBehavior behavior, Map hookContext) {
    logger.trace(() -> behavior.getLongName());

    String behaviorClass = behavior.getDeclaringClass().getName();
    if (this.ignoresChildren) {
      if (!behaviorClass.equals(this.targetClass)) {
        return false;
      }
    } else if (!CtClassUtil.isChildOf(behavior.getDeclaringClass(), this.targetClass)) {
      return false;
    }
    logger.trace("behaviorClass {} isChildOf targetClass: {}", behaviorClass, targetClass);

    String behaviorName = behavior.getName();
    if (logger.isTraceEnabled()) {
      logger.trace("behavior: {} hookBehavior: {}", behavior.getLongName(), getHookBehavior().getLongName());
    }

    if (!behaviorName.equals(this.targetMethod)) {
      return false;
    }

    if (signatures != null) {
      try {
        for (Signature signature : signatures) {
          if (methodMatchesSignature(behavior, signature)) {
            return true;
          }
        }
        logger.trace("signatures didn't match");
        return false;
      } catch (NotFoundException e) {
        Logger.println("Failed to find type of parameters of " + behaviorClass + "."
            + behaviorName);
        Logger.println(e);
      }
    } else {
      logger.trace("no signatures");
    }

    return true;
  }

  @Override
  public String getKey() {
    return this.targetMethod;
  }

  @Override
  public Integer getHookPosition() {
    return position;
  }

  private static Boolean methodMatchesSignature(CtBehavior behavior, Signature signature) throws NotFoundException {
    // CtBehavior.getParameterTypes finds the class of the parameter by
    // name, and so can throw a NotFoundException.
    List behaviorTypes = Arrays.asList(behavior.getParameterTypes());
    String[] types = signature.value();
    if (behaviorTypes.size() != types.length) {
      return false;
    }
    for (int i = 0; i < types.length; i++) {
      if (!CtClassUtil.isChildOf(behaviorTypes.get(i), types[i])) {
        return false;
      }
    }

    return true;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy