Skip to content

Latest commit

 

History

History
250 lines (199 loc) · 9.92 KB

File metadata and controls

250 lines (199 loc) · 9.92 KB

Matcher DSL

Overview

EaseAgent provides a Matcher DSL (Domain Specific Language) to define a Points implementation which is used to define classes and their methods to be enhanced.

The design of this DSL is borrowed from ByteBuddy's class and method matching DSL design. Why not use ByteBuddy's DSL interface directly?

  • Decouple from the ByteBuddy. Although ByteBuddy is an excellent byte code framework, we provide dedicated DSL to developers via abstracting it, can easily migrate to a more excellent framework in the future.
  • Provide a cleaner and easier to use API for Agent scenarios. We have defined two matchers, there are ClassMatcher and MethodMatcher.
    • The ClassMatcher is dedicated to identify classes in which methods need to be enhanced are located.
    • The MethodMatcher is dedicated to select methods located in the matched classes that need to be enhanced.

ClassMatcher

Definition

As defined by the JVM Specification for the class structure and the status of the annotations commonly used in Java, the optional elements used for class matching contain:

  • Class name.
  • Implementing Interface name.
  • Super class name.
  • Modifier, access flags, public/private/abstract/final/synthetic, and so on.
  • Annotation.

Implementation

ClassMatcher definition is provided by com.megaease.easeagent.plugin.matcher.ClassMatcher. Each condition in a matcher is a combination of logical AND.

    private String name;
    // setup the match type of name: className, super className, Interface Name or Annotation name.
    private ClassMatch matchType;

    private int modifier = Modifier.ACC_NONE;
    private int notModifier = Modifier.ACC_NONE;

There is a MethodMatcherBuilder class in com.megaease.easeagent.plugin.matcher.MethodMatcher, providing interface to build a ClassMatcher. The following methods are common condition settings in class matching,

    ClassMatcherBuilder hasSuperClass(String className)
    ClassMatcherBuilder hasClassName(String className)
    ClassMatcherBuilder hasAnnotation(String className)
    ClassMatcherBuilder hasInterface(String className)

    ClassMatcherBuilder isPublic();
    ClassMatcherBuilder isPrivate();
    ClassMatcherBuilder isAbstract();
    ClassMatcherBuilder isInterface();

    ClassMatcherBuilder notPrivate();
    ClassMatcherBuilder notAbstract();
    ClassMatcherBuilder notInterface();

Logical Operation

  • Logical AND
    As mention above, each condition in a matcher is a combination of logical AND.

    For example, the following ClassMatcher will match non-interface classes which inherit the java.lang.String and implements the java.lang.Comparable interface.

    ClassMatcher.builder()
        .hasSuperClass("java.lang.String")
        .hasInterface("java.lang.Comparable")
        .notInterface()
        .build()

In addition, ClassMatcherBuilder provides and() method which will finish current builder and start a new ClassMatcherBuilder. The previous builder will generate a Left ClassMatcher and the new builder will generate a 'Right ClassMatcher'. The Left matcher and Right matcher will be combined into one AndClassMatcher, and a method will match as long as the Left or Right matcher is a match.

For example, the following matcher will match classes that inherit the easeagent.test.TestBaseClass class and exclude classes implementing "easeagent.test.InterfaceB" interface.

    matcher = ClassMatcher.builder()
        .hasSuperClass("easeagent.test.TestBaseClass")
        .and()
        .hasInterface("easeagent.test.InterfaceB")
        .negate()
        .build();
  • Logical OR
    ClassMatcherBuilder provides or() method which will finish current builder and start a new ClassMatcherBuilder. The previous builder will generate a Left ClassMatcher and the new builder will generate a 'Right ClassMatcher'. The Left matcher and Right matcher will be combined into one OrClassMatcher, and a class will match as long as the Left or Right matcher is a match.

    For example, the following ClassMatcher will match classes that implement the javax.servlet.Filter interface or inherit the "javax.servlet.http.HttpServlet" class.

    public IClassMatcher getClassMatcher() {
        return ClassMatcher.builder()
            .hasInterface("javax.servlet.Filter")
            .or()
            .hasSuperClass("javax.servlet.http.HttpServlet")
            .build();
    }
  • Logical NOT
    The negate() method of ClassMatcherBuilder uses NegateClassMatcher to wrap the original matcher generated by the builder when no such method is applied. When a class matches the original matcher, it is unmatched by the wrap matcher; conversely, it is matched.
    For example, the following ClassMatcher will match classes that inherit the easeagent.test.TestBaseClass class, but exclude classes implementing "easeagent.test.InterfaceB" class.
    matcher = ClassMatcher.builder()
        .hasSuperClass("easeagent.test.TestBaseClass")
        .and()
        .hasInterface("easeagent.test.InterfaceB")
        .negate()
        .build();

MethodMatcher

Definition

As defined by the JVM Specification for the method structure , the optional elements used for method matching contain:

  • Method name.
  • Modifier, public/private, and so on.
  • Argument type, the full qualified class name of argument.
  • ReturnType, the return type of method.

Implementation

MethodMatcher definition is provided by com.megaease.easeagent.plugin.matcher.MethodMatcher. Each condition in a matcher is a combination of logical AND.

    // method name
    private String name;

    // the match type of method name: equals, startWith...
    private StringMatch nameMatchType;

    // ignored when with default value
    private String returnType = null;
    // types of method arguments
    private String[] args;
    private int argsLength = -1;
    private int modifier = Modifier.ACC_NONE;
    private int notModifier = Modifier.ACC_NONE;

    // only match methods overridden form the ClassMatcher
    private IClassMatcher overriddenFrom = null;

    // assign a name for this method matcher
    private String qualifier;

There is a MethodMatcherBuilder class in com.megaease.easeagent.plugin.matcher.MethodMatcher, providing interface to build a MethodMatcher. The following methods are common condition settings in method matching,

    // method name condition
    MethodMatcherBuilder named(String methodName);
    MethodMatcherBuilder isConstruct();
    MethodMatcherBuilder nameStartWith(String methodName);
    MethodMatcherBuilder nameEndWith(String methodName);
    MethodMatcherBuilder nameContains(String methodName);

    // modifier condition
    MethodMatcherBuilder isPublic();
    MethodMatcherBuilder isPrivate();
    MethodMatcherBuilder isAbstract();
    MethodMatcherBuilder isStatic();

    MethodMatcherBuilder notPublic();
    MethodMatcherBuilder notPrivate();
    MethodMatcherBuilder notAbstract();
    MethodMatcherBuilder notStatic();

    MethodMatcherBuilder returnType(String returnType);

    // setup method arguments condition
    MethodMatcherBuilder arg(int idx, String argType);
    MethodMatcherBuilder argsLength(int length);

    // setup method matcher name
    MethodMatcherBuilder qualifier(String qualifier);

    // only match methods overridden from matched classed through cMatcher
    MethodMatcherBuilder isOverriddenFrom(IClassMatcher cMatcher);

Logical Operation

  • Logical AND
    As mention above, each condition in a matcher is a combination of logical AND.
    For example, the following MethodMatcher will match the method which named getConnection and with a return type of java.sql.Connection.
    MethodMatcher.builder()
        .named("getConnection")
        .returnType("java.sql.Connection")
        .build())

In addition, MethodMatcherBuilder provides and() method which will finish current builder and start a new MethodMatcherBuilder. The previous builder will generate a Left MethodMatcher and the new builder will generate a 'Right MethodMatcher'. The Left matcher and Right matcher will be combined into one AndMethodMatcher, and a method will match as long as the Left or Right matcher is a match.

For example, the following matcher will match public methods that named write and exclude that with void return type.

    MethodMatcher.builder()
        .named("write")
        .argsLength(1)
        .isPublic()
        .and()
        .returnType("void")
        .negate()
        .build()
  • Logical OR
    MethodMatcherBuilder provides or() method which will finish current builder and start a new MethodMatcherBuilder.
    The previous builder will generate a Left MethodMatcher and the new builder will generate a 'Right MethodMatcher'. The Left matcher and Right matcher will be combined into one OrMethodMatcher, and a method will match as long as the Left or Right matcher is a match.

    For example, the following MethodMatcher will match method that named addBatch or named "clearBatch" and then give this OrClassMatcher matcher a name batch.

    MethodMatcher.builder()
        .named("addBatch")
        .or()
        .named("clearBatch")
        .qualifier("batch")
        .build()
  • Logical NOT
    The negate() method of MethodMatcherBuilder uses NegateMethodMatcher to wrap the original matcher generated by builder when no such method is applied.
    When a method matches the original matcher, it is unmatched by the wrap matcher; conversely, it is matched.

    For example, the following matcher will match public methods that named write and exclude that with void return type.

    MethodMatcher.builder()
        .named("write")
        .argsLength(1)
        .isPublic()
        .and()
        .returnType("void")
        .negate()
        .build()

Ends.