Project: EaseAgent (Observability and Analysis / Observability)
File: matcher-DSL.md

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. 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. ClassMatcher definition is provided by `com.megaease.easeagent.plugin.matcher.ClassMatcher`. Each condition in a matcher is a combination of `logical AND`. ```java 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, ```java 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 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. ```java 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. ```java 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. ```java 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

For example, the following ClassMatcher will match classes that inherit the `easeagent.test.TestBaseClass` class, but exclude classes implementing "easeagent.test.InterfaceB" class. ```java matcher = ClassMatcher.builder() .hasSuperClass("easeagent.test.TestBaseClass") .and() .hasInterface("easeagent.test.InterfaceB") .negate() .build(); ``` 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. MethodMatcher definition is provided by `com.megaease.easeagent.plugin.matcher.MethodMatcher`. Each condition in a matcher is a combination of `logical AND`. ```java // 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, ```java // 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 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`. ```java 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. ```java 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`. ```java 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. ```java MethodMatcher.builder() .named("write") .argsLength(1) .isPublic() .and() .returnType("void") .negate() .build() ``` Ends.