> SPRING_BOOT_CONFIG_LIST = Collections.synchronizedSet(new HashSet<>());
+
+ private static final long MAX_CACHE_TIME = 30L * 60L * 1000L; // half an hour.
+
+ private static final int MAX_CACHE_SIZE = 1000;
+
+ private static long PROPERTIES_LOAD_TIME;
+
+ public static Properties SPRING_PROPERTIES = null;
+
+ private SpringBootConfigInitializer() {
+
+ }
+
+ public static boolean isSpringPropertiesEmpty() {
+ return SPRING_PROPERTIES == null || SPRING_PROPERTIES.isEmpty();
+ }
+
+ public static synchronized void initializeConfig(SpringBootConfigNode springBootConfig) {
+ if (SPRING_PROPERTIES != null) {
+ try {
+ LOG.info("initialize Spring Config Class {}.", springBootConfig.root());
+ ConfigInitializer.initialize(SPRING_PROPERTIES, springBootConfig.root(), true);
+ } catch (Throwable e) {
+ LOG.error(e, "Failed to set the agent settings {} to Config={} ", SPRING_PROPERTIES, springBootConfig.root());
+ }
+ }
+ boolean isStarting = PROPERTIES_LOAD_TIME == 0L;
+ boolean overtime = System.currentTimeMillis() - PROPERTIES_LOAD_TIME > MAX_CACHE_TIME;
+ boolean oversize = SPRING_BOOT_CONFIG_LIST.size() > MAX_CACHE_SIZE;
+ // avoid memory leak.
+ if (isStarting || (!oversize && !overtime)) {
+ SPRING_BOOT_CONFIG_LIST.add(springBootConfig.root());
+ } else {
+ LOG.warn("spirng Config Class is skipped {}.", springBootConfig.root());
+ }
+ }
+
+ public static synchronized void setSpringProperties(Properties properties) {
+ if (properties != null && (SPRING_PROPERTIES == null || properties.size() > SPRING_PROPERTIES.size())) {
+ LOG.info("set Spring Config Properties before : {}.", SPRING_PROPERTIES);
+ SPRING_PROPERTIES = properties;
+ LOG.info("set Spring Config Properties after : {}.", SPRING_PROPERTIES);
+ PROPERTIES_LOAD_TIME = System.currentTimeMillis();
+ }
+ for (Class> clazz : SPRING_BOOT_CONFIG_LIST) {
+ try {
+ LOG.info("initialize Spring Config Class in loop {}.", clazz);
+ ConfigInitializer.initialize(SPRING_PROPERTIES, clazz);
+ } catch (Throwable e) {
+ LOG.error(e, "Failed to set the agent Config={} from settings {}", clazz, properties);
+ }
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/SpringBootConfigNode.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/SpringBootConfigNode.java
new file mode 100644
index 00000000..01350ca2
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/SpringBootConfigNode.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.boot;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SpringBootConfigNode {
+
+ /**
+ * @return Class as the root to do config initialization.
+ */
+ Class> root();
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/Config.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/Config.java
new file mode 100755
index 00000000..89e1c577
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/Config.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.conf;
+
+import cn.hippo4j.agent.core.logging.core.LogLevel;
+import cn.hippo4j.agent.core.logging.core.LogOutput;
+import cn.hippo4j.agent.core.logging.core.ResolverType;
+import cn.hippo4j.agent.core.logging.core.WriterFactory;
+import cn.hippo4j.agent.core.plugin.bytebuddy.ClassCacheMode;
+import cn.hippo4j.agent.core.util.Length;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This is the core config in hippo4j agent.
+ */
+public class Config {
+
+ public static class Agent {
+
+ /**
+ * Namespace represents a subnet, such as kubernetes namespace, or 172.10.*.*.
+ *
+ * @since 8.10.0 namespace would be added as {@link #SERVICE_NAME} suffix.
+ *
+ * Removed namespace isolating headers in cross process propagation. The HEADER name was
+ * `HeaderName:Namespace`.
+ */
+ @Length(20)
+ public static String NAMESPACE = "";
+
+ /**
+ * Service name is showed on the UI. Suggestion: set a unique name for each service, service instance nodes
+ * share the same code
+ *
+ * @since 8.10.0 ${service name} = [${group name}::]${logic name}|${NAMESPACE}|${CLUSTER}
+ *
+ * The group name, namespace and cluster are optional. Once they are all blank, service name would be the final
+ * name.
+ */
+ @Length(50)
+ public static String SERVICE_NAME = "";
+
+ /**
+ * Cluster defines the physical cluster in a data center or same network segment. In one cluster, IP address
+ * should be unique identify.
+ *
+ * The cluster name would be
+ *
+ * 1. Add as {@link #SERVICE_NAME} suffix.
+ *
+ * 2. Add as exit span's peer, ${CLUSTER} / original peer
+ *
+ * 3. Cross Process Propagation Header's value addressUsedAtClient[index=8] (Target address of this request used
+ * on the client end).
+ *
+ * @since 8.10.0
+ */
+ @Length(20)
+ public static String CLUSTER = "";
+
+ /**
+ * If true, Hippo4j agent will save all instrumented classes files in `/debugging` folder. Hippo4j team
+ * may ask for these files in order to resolve compatible problem.
+ */
+ public static boolean IS_OPEN_DEBUGGING_CLASS = false;
+
+ /**
+ * If true, Hippo4j agent will cache all instrumented classes to memory or disk files (decided by class cache
+ * mode), allow other javaagent to enhance those classes that enhanced by Hippo4j agent.
+ */
+ public static boolean IS_CACHE_ENHANCED_CLASS = false;
+
+ /**
+ * The instrumented classes cache mode: MEMORY or FILE MEMORY: cache class bytes to memory, if instrumented
+ * classes is too many or too large, it may take up more memory FILE: cache class bytes in `/class-cache`
+ * folder, automatically clean up cached class files when the application exits
+ */
+ public static ClassCacheMode CLASS_CACHE_MODE = ClassCacheMode.MEMORY;
+ }
+
+ public static class Logging {
+
+ /**
+ * Log file name.
+ */
+ public static String FILE_NAME = "hippo4j-api.log";
+
+ /**
+ * Log files directory. Default is blank string, means, use "{theHippo4jAgentJarDir}/logs " to output logs.
+ * {theHippo4jAgentJarDir} is the directory where the hippo4j agent jar file is located.
+ *
+ * Ref to {@link WriterFactory#getLogWriter()}
+ */
+ public static String DIR = "";
+
+ /**
+ * The max size of log file. If the size is bigger than this, archive the current file, and write into a new
+ * file.
+ */
+ public static int MAX_FILE_SIZE = 300 * 1024 * 1024;
+
+ /**
+ * The max history log files. When rollover happened, if log files exceed this number, then the oldest file will
+ * be delete. Negative or zero means off, by default.
+ */
+ public static int MAX_HISTORY_FILES = -1;
+
+ /**
+ * The log level. Default is debug.
+ */
+ public static LogLevel LEVEL = LogLevel.DEBUG;
+
+ /**
+ * The log output. Default is FILE.
+ */
+ public static LogOutput OUTPUT = LogOutput.FILE;
+
+ /**
+ * The log resolver type. Default is PATTERN which will create PatternLogResolver later.
+ */
+ public static ResolverType RESOLVER = ResolverType.PATTERN;
+
+ /**
+ * The log patten. Default is "%level %timestamp %thread %class : %msg %throwable". Each conversion specifiers
+ * starts with a percent sign '%' and fis followed by conversion word. There are some default conversion
+ * specifiers: %thread = ThreadName %level = LogLevel {@link LogLevel} %timestamp = The now() who format is
+ * 'yyyy-MM-dd HH:mm:ss:SSS' %class = SimpleName of TargetClass %msg = Message of user input %throwable =
+ * Throwable of user input %agent_name = ServiceName of Agent {@link Agent#SERVICE_NAME}
+ *
+ * @see cn.hippo4j.agent.core.logging.core.PatternLogger#DEFAULT_CONVERTER_MAP
+ */
+ public static String PATTERN = "%level %timestamp %thread %class : %msg %throwable";
+ }
+
+ public static class Plugin {
+
+ /**
+ * Control the length of the peer field.
+ */
+ public static int PEER_MAX_LENGTH = 200;
+
+ /**
+ * Exclude activated plugins
+ */
+ public static String EXCLUDE_PLUGINS = "";
+
+ /**
+ * Mount the folders of the plugins. The folder path is relative to agent.jar.
+ */
+ public static List MOUNT = Arrays.asList("plugins", "activations");
+
+ public static class ThreadPool {
+
+ public static List EXCLUDE_PACKAGE_PREFIX = Arrays.asList(
+ "java", "sun", "okhttp3", "retrofit2", "reactor",
+ "org.apache", "io.netty", "org.springframework", "com.ctrip", "com.google",
+ "io.undertow", "org.xnio", "org.jboss", "com.zaxxer", "org.redisson", "com.alibaba",
+ "com.netflix", "com.mysql", "rx.internal", "io.shardingjdbc", "org.drools", "org.elasticsearch",
+ "ch.qos.logback", "net.sf.ehcache");
+ }
+
+ public static class Apollo {
+
+ public static class App {
+
+ public static String ID;
+ }
+ public static String META;
+
+ public static class BootStrap {
+
+ public static boolean ENABLED = false;
+
+ public static List NAMESPACES;
+ }
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/ConfigNotFoundException.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/ConfigNotFoundException.java
new file mode 100644
index 00000000..9d7d387c
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/ConfigNotFoundException.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.conf;
+
+public class ConfigNotFoundException extends Exception {
+
+ public ConfigNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ConfigNotFoundException(String message) {
+ super(message);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/Constants.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/Constants.java
new file mode 100644
index 00000000..25f3d7bb
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/Constants.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.conf;
+
+public class Constants {
+
+ public static String PATH_SEPARATOR = System.getProperty("file.separator", "/");
+
+ public static String LINE_SEPARATOR = System.getProperty("line.separator", "\n");
+
+ public static String EMPTY_STRING = "";
+
+ public static char SERVICE_NAME_PART_CONNECTOR = '|';
+
+ // The name of the layer that represents agent-installed services,
+ // which is defined at
+ // https://github.com/apache/skywalking/blob/85ce1645be53e46286f36c0ea206c60db2d1a716/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/Layer.java#L30
+ public static String EVENT_LAYER_NAME = "GENERAL";
+
+ public static int NULL_VALUE = 0;
+
+ public static String SPRING_BOOT_CONFIG_PREFIX = "spring.dynamic.thread-pool";
+
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/RuntimeContextConfiguration.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/RuntimeContextConfiguration.java
new file mode 100644
index 00000000..0864baa0
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/RuntimeContextConfiguration.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.conf;
+
+public class RuntimeContextConfiguration {
+
+ public static String[] NEED_PROPAGATE_CONTEXT_KEY = new String[]{
+ "SW_REQUEST",
+ "SW_RESPONSE",
+ "SW_WEBFLUX_REQUEST_KEY"
+ };
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/SnifferConfigInitializer.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/SnifferConfigInitializer.java
new file mode 100644
index 00000000..570a68c8
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/SnifferConfigInitializer.java
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.conf;
+
+import cn.hippo4j.agent.core.boot.AgentPackageNotFoundException;
+import cn.hippo4j.agent.core.boot.AgentPackagePath;
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.logging.core.JsonLogResolver;
+import cn.hippo4j.agent.core.logging.core.PatternLogResolver;
+import cn.hippo4j.agent.core.util.ConfigInitializer;
+import cn.hippo4j.agent.core.util.PropertyPlaceholderHelper;
+import cn.hippo4j.agent.core.util.StringUtil;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import static cn.hippo4j.agent.core.conf.Constants.SERVICE_NAME_PART_CONNECTOR;
+
+/**
+ * The SnifferConfigInitializer
initializes all configs in several way.
+ */
+public class SnifferConfigInitializer {
+
+ private static ILog LOGGER = LogManager.getLogger(SnifferConfigInitializer.class);
+ private static final String SPECIFIED_CONFIG_PATH = "hippo4j_config";
+ private static final String DEFAULT_CONFIG_FILE_NAME = "/config/agent.config";
+ private static final String ENV_KEY_PREFIX = "hippo4j.";
+ private static Properties AGENT_SETTINGS;
+ private static boolean IS_INIT_COMPLETED = false;
+
+ /**
+ * If the specified agent config path is set, the agent will try to locate the specified agent config. If the
+ * specified agent config path is not set , the agent will try to locate `agent.config`, which should be in the
+ * /config directory of agent package.
+ *
+ * Also try to override the config by system.properties. All the keys in this place should start with {@link
+ * #ENV_KEY_PREFIX}. e.g. in env `hippo4j.agent.service_name=yourAppName` to override `agent.service_name` in
+ * config file.
+ *
+ * At the end, `agent.service_name` and `collector.servers` must not be blank.
+ */
+ public static void initializeCoreConfig(String agentOptions) {
+ AGENT_SETTINGS = new Properties();
+ try (final InputStreamReader configFileStream = loadConfig()) {
+ AGENT_SETTINGS.load(configFileStream);
+ for (String key : AGENT_SETTINGS.stringPropertyNames()) {
+ String value = (String) AGENT_SETTINGS.get(key);
+ AGENT_SETTINGS.put(key, PropertyPlaceholderHelper.INSTANCE.replacePlaceholders(value, AGENT_SETTINGS));
+ }
+
+ } catch (Exception e) {
+ LOGGER.error(e, "Failed to read the config file, hippo4j is going to run in default config.");
+ }
+
+ try {
+ overrideConfigBySystemProp();
+ } catch (Exception e) {
+ LOGGER.error(e, "Failed to read the system properties.");
+ }
+
+ agentOptions = StringUtil.trim(agentOptions, ',');
+ if (!StringUtil.isEmpty(agentOptions)) {
+ try {
+ agentOptions = agentOptions.trim();
+ LOGGER.info("Agent options is {}.", agentOptions);
+
+ overrideConfigByAgentOptions(agentOptions);
+ } catch (Exception e) {
+ LOGGER.error(e, "Failed to parse the agent options, val is {}.", agentOptions);
+ }
+ }
+
+ initializeConfig(Config.class);
+ // reconfigure logger after config initialization
+ configureLogger();
+ LOGGER = LogManager.getLogger(SnifferConfigInitializer.class);
+
+ if (StringUtil.isEmpty(Config.Agent.SERVICE_NAME)) {
+ throw new ExceptionInInitializerError("`agent.service_name` is missing.");
+ } else {
+ if (StringUtil.isNotEmpty(Config.Agent.NAMESPACE) || StringUtil.isNotEmpty(Config.Agent.CLUSTER)) {
+ Config.Agent.SERVICE_NAME = StringUtil.join(
+ SERVICE_NAME_PART_CONNECTOR,
+ Config.Agent.SERVICE_NAME,
+ Config.Agent.NAMESPACE,
+ Config.Agent.CLUSTER);
+ }
+ }
+ // if (StringUtil.isEmpty(Config.Collector.BACKEND_SERVICE)) {
+ // throw new ExceptionInInitializerError("`collector.backend_service` is missing.");
+ // }
+ if (Config.Plugin.PEER_MAX_LENGTH <= 3) {
+ LOGGER.warn(
+ "PEER_MAX_LENGTH configuration:{} error, the default value of 200 will be used.",
+ Config.Plugin.PEER_MAX_LENGTH);
+ Config.Plugin.PEER_MAX_LENGTH = 200;
+ }
+
+ IS_INIT_COMPLETED = true;
+ }
+
+ /**
+ * Initialize field values of any given config class.
+ *
+ * @param configClass to host the settings for code access.
+ */
+ public static void initializeConfig(Class configClass) {
+ if (AGENT_SETTINGS == null) {
+ LOGGER.error("Plugin configs have to be initialized after core config initialization.");
+ return;
+ }
+ try {
+ ConfigInitializer.initialize(AGENT_SETTINGS, configClass);
+ } catch (IllegalAccessException e) {
+ LOGGER.error(e,
+ "Failed to set the agent settings {}"
+ + " to Config={} ",
+ AGENT_SETTINGS, configClass);
+ }
+ }
+
+ private static void overrideConfigByAgentOptions(String agentOptions) throws IllegalArgumentException {
+ for (List terms : parseAgentOptions(agentOptions)) {
+ if (terms.size() != 2) {
+ throw new IllegalArgumentException("[" + terms + "] is not a key-value pair.");
+ }
+ AGENT_SETTINGS.put(terms.get(0), terms.get(1));
+ }
+ }
+
+ private static List> parseAgentOptions(String agentOptions) {
+ List> options = new ArrayList<>();
+ List terms = new ArrayList<>();
+ boolean isInQuotes = false;
+ StringBuilder currentTerm = new StringBuilder();
+ for (char c : agentOptions.toCharArray()) {
+ if (c == '\'' || c == '"') {
+ isInQuotes = !isInQuotes;
+ } else if (c == '=' && !isInQuotes) { // key-value pair uses '=' as separator
+ terms.add(currentTerm.toString());
+ currentTerm = new StringBuilder();
+ } else if (c == ',' && !isInQuotes) { // multiple options use ',' as separator
+ terms.add(currentTerm.toString());
+ currentTerm = new StringBuilder();
+
+ options.add(terms);
+ terms = new ArrayList<>();
+ } else {
+ currentTerm.append(c);
+ }
+ }
+ // add the last term and option without separator
+ terms.add(currentTerm.toString());
+ options.add(terms);
+ return options;
+ }
+
+ public static boolean isInitCompleted() {
+ return IS_INIT_COMPLETED;
+ }
+
+ /**
+ * Override the config by system properties. The property key must start with `hippo4j`, the result should be as
+ * same as in `agent.config`
+ *
+ * such as: Property key of `agent.service_name` should be `hippo4j.agent.service_name`
+ */
+ private static void overrideConfigBySystemProp() {
+ Properties systemProperties = System.getProperties();
+ for (final Map.Entry prop : systemProperties.entrySet()) {
+ String key = prop.getKey().toString();
+ if (key.startsWith(ENV_KEY_PREFIX)) {
+ String realKey = key.substring(ENV_KEY_PREFIX.length());
+ AGENT_SETTINGS.put(realKey, prop.getValue());
+ }
+ }
+ }
+
+ /**
+ * Load the specified config file or default config file
+ *
+ * @return the config file {@link InputStream}, or null if not needEnhance.
+ */
+ private static InputStreamReader loadConfig() throws AgentPackageNotFoundException, ConfigNotFoundException {
+ String specifiedConfigPath = System.getProperty(SPECIFIED_CONFIG_PATH);
+ File configFile = StringUtil.isEmpty(specifiedConfigPath) ? new File(
+ AgentPackagePath.getPath(), DEFAULT_CONFIG_FILE_NAME) : new File(specifiedConfigPath);
+
+ if (configFile.exists() && configFile.isFile()) {
+ try {
+ LOGGER.info("Config file found in {}.", configFile);
+
+ return new InputStreamReader(new FileInputStream(configFile), StandardCharsets.UTF_8);
+ } catch (FileNotFoundException e) {
+ throw new ConfigNotFoundException("Failed to load agent.config", e);
+ }
+ }
+ throw new ConfigNotFoundException("Failed to load agent.config.");
+ }
+
+ static void configureLogger() {
+ switch (Config.Logging.RESOLVER) {
+ case JSON:
+ LogManager.setLogResolver(new JsonLogResolver());
+ break;
+ case PATTERN:
+ default:
+ LogManager.setLogResolver(new PatternLogResolver());
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/dynamic/AgentConfigChangeWatcher.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/dynamic/AgentConfigChangeWatcher.java
new file mode 100644
index 00000000..e896584d
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/dynamic/AgentConfigChangeWatcher.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.conf.dynamic;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+public abstract class AgentConfigChangeWatcher {
+
+ // Config key, should match KEY in the Table of Agent Configuration Properties.
+ private final String propertyKey;
+
+ public AgentConfigChangeWatcher(String propertyKey) {
+ this.propertyKey = propertyKey;
+ }
+
+ /**
+ * Notify the watcher, the new value received.
+ *
+ * @param value of new.
+ */
+ public abstract void notify(ConfigChangeEvent value);
+
+ /**
+ * @return current value of current config.
+ */
+ public abstract String value();
+
+ @Override
+ public String toString() {
+ return "AgentConfigChangeWatcher{"
+ + "propertyKey='" + propertyKey + '\''
+ + '}';
+ }
+
+ @Getter
+ @RequiredArgsConstructor
+ public static class ConfigChangeEvent {
+
+ private final String newValue;
+ private final EventType eventType;
+ }
+
+ public enum EventType {
+ ADD, MODIFY, DELETE
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/jvm/LoadedLibraryCollector.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/jvm/LoadedLibraryCollector.java
new file mode 100644
index 00000000..f8a466a6
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/jvm/LoadedLibraryCollector.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.jvm;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.util.CollectionUtil;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.io.File;
+import java.lang.management.ManagementFactory;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class LoadedLibraryCollector {
+
+ private static final ILog LOGGER = LogManager.getLogger(LoadedLibraryCollector.class);
+ private static final String JAR_SEPARATOR = "!";
+ private static Set CURRENT_URL_CLASSLOADER_SET = new HashSet<>();
+ private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create();
+ /**
+ * Prevent OOM in special scenes
+ */
+ private static int CURRENT_URL_CLASSLOADER_SET_MAX_SIZE = 50;
+
+ public static void registerURLClassLoader(ClassLoader classLoader) {
+ if (CURRENT_URL_CLASSLOADER_SET.size() < CURRENT_URL_CLASSLOADER_SET_MAX_SIZE && classLoader instanceof URLClassLoader) {
+ CURRENT_URL_CLASSLOADER_SET.add(classLoader);
+ }
+ }
+
+ private static String getVmStartTime() {
+ long startTime = ManagementFactory.getRuntimeMXBean().getStartTime();
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(startTime));
+ }
+
+ private static List getVmArgs() {
+ List vmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();
+ List sortedVmArgs = new ArrayList<>(vmArgs);
+ Collections.sort(sortedVmArgs);
+ return sortedVmArgs;
+ }
+
+ private static List getLibJarNames() {
+ List classLoaderUrls = loadClassLoaderUrls();
+ return extractLibJarNamesFromURLs(classLoaderUrls);
+ }
+
+ private static List loadClassLoaderUrls() {
+ List classLoaderUrls = new ArrayList<>();
+ for (ClassLoader classLoader : CURRENT_URL_CLASSLOADER_SET) {
+ try {
+ URLClassLoader webappClassLoader = (URLClassLoader) classLoader;
+ URL[] urls = webappClassLoader.getURLs();
+ classLoaderUrls.addAll(Arrays.asList(urls));
+ } catch (Exception e) {
+ LOGGER.warn("Load classloader urls exception: {}", e.getMessage());
+ }
+ }
+ return classLoaderUrls;
+ }
+
+ private static List extractLibJarNamesFromURLs(List urls) {
+ Set libJarNames = new HashSet<>();
+ for (URL url : urls) {
+ try {
+ String libJarName = extractLibJarName(url);
+ if (libJarName.endsWith(".jar")) {
+ libJarNames.add(libJarName);
+ }
+ } catch (Exception e) {
+ LOGGER.warn("Extracting library name exception: {}", e.getMessage());
+ }
+ }
+ List sortedLibJarNames = new ArrayList<>(libJarNames.size());
+ if (!CollectionUtil.isEmpty(libJarNames)) {
+ sortedLibJarNames.addAll(libJarNames);
+ Collections.sort(sortedLibJarNames);
+ }
+ return sortedLibJarNames;
+ }
+
+ private static String extractLibJarName(URL url) {
+ String protocol = url.getProtocol();
+ if (protocol.equals("file")) {
+ return extractNameFromFile(url.toString());
+ } else if (protocol.equals("jar")) {
+ return extractNameFromJar(url.toString());
+ } else {
+ return "";
+ }
+ }
+
+ private static String extractNameFromFile(String fileUri) {
+ int lastIndexOfSeparator = fileUri.lastIndexOf(File.separator);
+ if (lastIndexOfSeparator < 0) {
+ return fileUri;
+ } else {
+ return fileUri.substring(lastIndexOfSeparator + 1);
+ }
+ }
+
+ private static String extractNameFromJar(String jarUri) {
+ String uri = jarUri.substring(0, jarUri.lastIndexOf(JAR_SEPARATOR));
+ return extractNameFromFile(uri);
+ }
+
+}
\ No newline at end of file
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/ILog.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/ILog.java
new file mode 100644
index 00000000..89f1031f
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/ILog.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.api;
+
+/**
+ * The Log interface. It's very easy to understand, like any other log-component. Do just like log4j or log4j2 does.
+ *
+ */
+public interface ILog {
+
+ void info(String format);
+
+ void info(String format, Object... arguments);
+
+ void info(Throwable t, String format, Object... arguments);
+
+ void warn(String format, Object... arguments);
+
+ void warn(Throwable e, String format, Object... arguments);
+
+ void error(String format, Throwable e);
+
+ void error(Throwable e, String format, Object... arguments);
+
+ boolean isDebugEnable();
+
+ boolean isInfoEnable();
+
+ boolean isWarnEnable();
+
+ boolean isErrorEnable();
+
+ boolean isTraceEnabled();
+
+ void debug(String format);
+
+ void debug(String format, Object... arguments);
+
+ void debug(Throwable t, String format, Object... arguments);
+
+ void error(String format);
+
+ void trace(String format);
+
+ void trace(String format, Object... arguments);
+
+ void trace(Throwable t, String format, Object... arguments);
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/LogManager.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/LogManager.java
new file mode 100644
index 00000000..cb6514bc
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/LogManager.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.api;
+
+import cn.hippo4j.agent.core.logging.core.PatternLogResolver;
+
+/**
+ * LogManager is the {@link LogResolver} implementation manager. By using {@link LogResolver}, {@link
+ * LogManager#getLogger(Class)} returns a {@link ILog} implementation. This module use this class as the main entrance,
+ * and block the implementation detail about log-component. In different modules, like server or sniffer, it will use
+ * different implementations.
+ *
+ *
If no {@link LogResolver} is registered, return {@link NoopLogger#INSTANCE} to avoid
+ * {@link NullPointerException}. If {@link LogManager#setLogResolver(LogResolver)} is called twice, the second will
+ * override the first without any warning or exception.
+ *
+ *
Created by xin on 2016/11/10.
+ */
+public class LogManager {
+
+ private static LogResolver RESOLVER = new PatternLogResolver();
+
+ public static void setLogResolver(LogResolver resolver) {
+ LogManager.RESOLVER = resolver;
+ }
+
+ public static ILog getLogger(Class> clazz) {
+ if (RESOLVER == null) {
+ return NoopLogger.INSTANCE;
+ }
+ return LogManager.RESOLVER.getLogger(clazz);
+ }
+
+ public static ILog getLogger(String clazz) {
+ if (RESOLVER == null) {
+ return NoopLogger.INSTANCE;
+ }
+ return LogManager.RESOLVER.getLogger(clazz);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/LogResolver.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/LogResolver.java
new file mode 100644
index 00000000..4f145dbb
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/LogResolver.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.api;
+
+/**
+ * {@link LogResolver} just do only one thing: return the {@link ILog} implementation.
+ *
+ */
+public interface LogResolver {
+
+ /**
+ * @param clazz the class is showed in log message.
+ * @return {@link ILog} implementation.
+ */
+ ILog getLogger(Class> clazz);
+
+ /**
+ * @param clazz the class is showed in log message.
+ * @return {@link ILog} implementation.
+ */
+ ILog getLogger(String clazz);
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/NoopLogger.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/NoopLogger.java
new file mode 100644
index 00000000..9f544b63
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/NoopLogger.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.api;
+
+/**
+ * No operation logger implementation. Just implement {@link ILog} interface, but do nothing.
+ *
+ */
+public enum NoopLogger implements ILog {
+
+ INSTANCE;
+
+ @Override
+ public void info(String message) {
+
+ }
+
+ @Override
+ public void info(String format, Object... arguments) {
+
+ }
+
+ @Override
+ public void info(final Throwable t, final String format, final Object... arguments) {
+
+ }
+
+ @Override
+ public void warn(String format, Object... arguments) {
+
+ }
+
+ @Override
+ public void error(String format, Throwable e) {
+
+ }
+
+ @Override
+ public boolean isDebugEnable() {
+ return false;
+ }
+
+ @Override
+ public boolean isInfoEnable() {
+ return false;
+ }
+
+ @Override
+ public boolean isWarnEnable() {
+ return false;
+ }
+
+ @Override
+ public boolean isErrorEnable() {
+ return false;
+ }
+
+ @Override
+ public boolean isTraceEnabled() {
+ return false;
+ }
+
+ @Override
+ public void debug(String format) {
+
+ }
+
+ @Override
+ public void debug(String format, Object... arguments) {
+
+ }
+
+ @Override
+ public void debug(final Throwable t, final String format, final Object... arguments) {
+
+ }
+
+ @Override
+ public void error(String format) {
+
+ }
+
+ @Override
+ public void trace(final String format) {
+
+ }
+
+ @Override
+ public void trace(final String format, final Object... arguments) {
+
+ }
+
+ @Override
+ public void trace(final Throwable t, final String format, final Object... arguments) {
+
+ }
+
+ @Override
+ public void error(Throwable e, String format, Object... arguments) {
+
+ }
+
+ @Override
+ public void warn(Throwable e, String format, Object... arguments) {
+
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/AbstractLogger.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/AbstractLogger.java
new file mode 100644
index 00000000..05a53ff0
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/AbstractLogger.java
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core;
+
+import cn.hippo4j.agent.core.conf.Config;
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.core.converters.AgentNameConverter;
+import cn.hippo4j.agent.core.logging.core.converters.ClassConverter;
+import cn.hippo4j.agent.core.logging.core.converters.DateConverter;
+import cn.hippo4j.agent.core.logging.core.converters.LevelConverter;
+import cn.hippo4j.agent.core.logging.core.converters.MessageConverter;
+import cn.hippo4j.agent.core.logging.core.converters.ThreadConverter;
+import cn.hippo4j.agent.core.logging.core.converters.ThrowableConverter;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+
+/**
+ * An abstract class to simplify the real implementation of the loggers.
+ * It hold the class name of the logger, and is responsible for log level check,
+ * message interpolation, etc.
+ */
+public abstract class AbstractLogger implements ILog {
+
+ public static final Map> DEFAULT_CONVERTER_MAP = new HashMap<>();
+ protected List converters = new ArrayList<>();
+
+ static {
+ DEFAULT_CONVERTER_MAP.put("thread", ThreadConverter.class);
+ DEFAULT_CONVERTER_MAP.put("level", LevelConverter.class);
+ DEFAULT_CONVERTER_MAP.put("agent_name", AgentNameConverter.class);
+ DEFAULT_CONVERTER_MAP.put("timestamp", DateConverter.class);
+ DEFAULT_CONVERTER_MAP.put("msg", MessageConverter.class);
+ DEFAULT_CONVERTER_MAP.put("throwable", ThrowableConverter.class);
+ DEFAULT_CONVERTER_MAP.put("class", ClassConverter.class);
+ }
+
+ protected final String targetClass;
+
+ public AbstractLogger(String targetClass) {
+ this.targetClass = targetClass;
+ }
+
+ @Override
+ public void info(String message) {
+ if (this.isInfoEnable()) {
+ this.logger(LogLevel.INFO, message, null);
+ }
+ }
+
+ @Override
+ public void info(String message, Object... objects) {
+ if (this.isInfoEnable()) {
+ this.logger(LogLevel.INFO, replaceParam(message, objects), null);
+ }
+ }
+
+ @Override
+ public void info(final Throwable throwable, final String message, final Object... objects) {
+ if (this.isInfoEnable()) {
+ this.logger(LogLevel.INFO, replaceParam(message, objects), throwable);
+ }
+ }
+
+ @Override
+ public void warn(String message, Object... objects) {
+ if (this.isWarnEnable()) {
+ this.logger(LogLevel.WARN, replaceParam(message, objects), null);
+ }
+ }
+
+ @Override
+ public void warn(Throwable throwable, String message, Object... objects) {
+ if (this.isWarnEnable()) {
+ this.logger(LogLevel.WARN, replaceParam(message, objects), throwable);
+ }
+ }
+
+ @Override
+ public void error(String message, Throwable throwable) {
+ if (this.isErrorEnable()) {
+ this.logger(LogLevel.ERROR, message, throwable);
+ }
+ }
+
+ @Override
+ public void error(Throwable throwable, String message, Object... objects) {
+ if (this.isErrorEnable()) {
+ this.logger(LogLevel.ERROR, replaceParam(message, objects), throwable);
+ }
+ }
+
+ @Override
+ public void error(String message) {
+ if (this.isErrorEnable()) {
+ this.logger(LogLevel.ERROR, message, null);
+ }
+ }
+
+ @Override
+ public void debug(String message) {
+ if (this.isDebugEnable()) {
+ this.logger(LogLevel.DEBUG, message, null);
+ }
+ }
+
+ @Override
+ public void debug(String message, Object... objects) {
+ if (this.isDebugEnable()) {
+ this.logger(LogLevel.DEBUG, replaceParam(message, objects), null);
+ }
+ }
+
+ @Override
+ public void debug(Throwable throwable, String message, Object... objects) {
+ if (this.isDebugEnable()) {
+ this.logger(LogLevel.DEBUG, replaceParam(message, objects), throwable);
+ }
+ }
+
+ @Override
+ public boolean isDebugEnable() {
+ return LogLevel.DEBUG.compareTo(Config.Logging.LEVEL) >= 0;
+ }
+
+ @Override
+ public boolean isInfoEnable() {
+ return LogLevel.INFO.compareTo(Config.Logging.LEVEL) >= 0;
+ }
+
+ @Override
+ public boolean isWarnEnable() {
+ return LogLevel.WARN.compareTo(Config.Logging.LEVEL) >= 0;
+ }
+
+ @Override
+ public boolean isErrorEnable() {
+ return LogLevel.ERROR.compareTo(Config.Logging.LEVEL) >= 0;
+ }
+
+ @Override
+ public boolean isTraceEnabled() {
+ return LogLevel.TRACE.compareTo(Config.Logging.LEVEL) >= 0;
+ }
+
+ @Override
+ public void trace(final String message) {
+ if (this.isTraceEnabled()) {
+ this.logger(LogLevel.TRACE, message, null);
+ }
+ }
+
+ @Override
+ public void trace(final String message, final Object... objects) {
+ if (this.isTraceEnabled()) {
+ this.logger(LogLevel.TRACE, replaceParam(message, objects), null);
+ }
+ }
+
+ @Override
+ public void trace(final Throwable throwable, final String message, final Object... objects) {
+ if (this.isTraceEnabled()) {
+ this.logger(LogLevel.TRACE, replaceParam(message, objects), throwable);
+ }
+ }
+
+ protected String replaceParam(String message, Object... parameters) {
+ if (message == null) {
+ return message;
+ }
+ int startSize = 0;
+ int parametersIndex = 0;
+ int index;
+ String tmpMessage = message;
+ while ((index = message.indexOf("{}", startSize)) != -1) {
+ if (parametersIndex >= parameters.length) {
+ break;
+ }
+ /**
+ * @Fix the Illegal group reference issue
+ */
+ tmpMessage = tmpMessage.replaceFirst("\\{\\}", Matcher.quoteReplacement(String.valueOf(parameters[parametersIndex++])));
+ startSize = index + 2;
+ }
+ return tmpMessage;
+ }
+
+ protected void logger(LogLevel level, String message, Throwable e) {
+ WriterFactory.getLogWriter().write(this.format(level, message, e));
+ }
+
+ /**
+ * The abstract method left for real loggers.
+ * Any implementation MUST return string, which will be directly transferred to log destination,
+ * i.e. log files OR stdout
+ *
+ * @param level log level
+ * @param message log message, which has been interpolated with user-defined parameters.
+ * @param e throwable if exists
+ * @return string representation of the log, for example, raw json string for {@link JsonLogger}
+ */
+ protected abstract String format(LogLevel level, String message, Throwable e);
+
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/Converter.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/Converter.java
new file mode 100644
index 00000000..0bf8d979
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/Converter.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core;
+
+/**
+ * The Converter, it is used to convert the LogEvent to the String.
+ * For JsonLogger, the `getKey()` method is used to generate the key for json.
+ */
+public interface Converter {
+
+ String convert(LogEvent logEvent);
+
+ String getKey();
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/FileWriter.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/FileWriter.java
new file mode 100644
index 00000000..e2fde308
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/FileWriter.java
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core;
+
+import cn.hippo4j.agent.core.boot.DefaultNamedThreadFactory;
+import cn.hippo4j.agent.core.conf.Config;
+import cn.hippo4j.agent.core.conf.Constants;
+import cn.hippo4j.agent.core.util.RunnableWithExceptionProtection;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+/**
+ * The FileWriter
support async file output, by using a queue as buffer.
+ */
+public class FileWriter implements IWriter {
+
+ private static FileWriter INSTANCE;
+ private static final Object CREATE_LOCK = new Object();
+ private FileOutputStream fileOutputStream;
+ private ArrayBlockingQueue logBuffer;
+ private volatile int fileSize;
+ private Pattern filenamePattern = Pattern.compile(Config.Logging.FILE_NAME + "\\.\\d{4}_\\d{2}_\\d{2}_\\d{2}_\\d{2}_\\d{2}");
+
+ public static FileWriter get() {
+ if (INSTANCE == null) {
+ synchronized (CREATE_LOCK) {
+ if (INSTANCE == null) {
+ INSTANCE = new FileWriter();
+ }
+ }
+ }
+ return INSTANCE;
+ }
+
+ private FileWriter() {
+ logBuffer = new ArrayBlockingQueue(1024);
+ final ArrayList outputLogs = new ArrayList(200);
+ Executors.newSingleThreadScheduledExecutor(new DefaultNamedThreadFactory("LogFileWriter"))
+ .scheduleAtFixedRate(new RunnableWithExceptionProtection(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ logBuffer.drainTo(outputLogs);
+ for (String log : outputLogs) {
+ writeToFile(log + Constants.LINE_SEPARATOR);
+ }
+ try {
+ if (fileOutputStream != null) {
+ fileOutputStream.flush();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } finally {
+ outputLogs.clear();
+ }
+ }
+ }, new RunnableWithExceptionProtection.CallbackWhenException() {
+
+ @Override
+ public void handle(Throwable t) {
+ }
+ }), 0, 1, TimeUnit.SECONDS);
+ }
+
+ /**
+ * @param message to be written into the file.
+ */
+ private void writeToFile(String message) {
+ if (prepareWriteStream()) {
+ try {
+ fileOutputStream.write(message.getBytes());
+ fileSize += message.length();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ switchFile();
+ }
+ }
+ }
+
+ private void switchFile() {
+ if (fileSize > Config.Logging.MAX_FILE_SIZE) {
+ forceExecute(new Callable() {
+
+ @Override
+ public Object call() throws Exception {
+ fileOutputStream.flush();
+ return null;
+ }
+ });
+ forceExecute(new Callable() {
+
+ @Override
+ public Object call() throws Exception {
+ fileOutputStream.close();
+ return null;
+ }
+ });
+ forceExecute(new Callable() {
+
+ @Override
+ public Object call() throws Exception {
+ new File(Config.Logging.DIR, Config.Logging.FILE_NAME).renameTo(new File(Config.Logging.DIR, Config.Logging.FILE_NAME + new SimpleDateFormat(".yyyy_MM_dd_HH_mm_ss")
+ .format(new Date())));
+ return null;
+ }
+ });
+ forceExecute(new Callable() {
+
+ @Override
+ public Object call() throws Exception {
+ fileOutputStream = null;
+ return null;
+ }
+ });
+
+ if (Config.Logging.MAX_HISTORY_FILES > 0) {
+ deleteExpiredFiles();
+ }
+ }
+ }
+
+ /**
+ * load history log file name array
+ *
+ * @return history log file name array
+ */
+ private String[] getHistoryFilePath() {
+ File path = new File(Config.Logging.DIR);
+ String[] pathArr = path.list(new FilenameFilter() {
+
+ @Override
+ public boolean accept(File dir, String name) {
+ return filenamePattern.matcher(name).matches();
+ }
+ });
+
+ return pathArr;
+ }
+
+ /**
+ * delete expired log files
+ */
+ private void deleteExpiredFiles() {
+ String[] historyFileArr = getHistoryFilePath();
+ if (historyFileArr != null && historyFileArr.length > Config.Logging.MAX_HISTORY_FILES) {
+
+ Arrays.sort(historyFileArr, new Comparator() {
+
+ @Override
+ public int compare(String o1, String o2) {
+ return o2.compareTo(o1);
+ }
+ });
+
+ for (int i = Config.Logging.MAX_HISTORY_FILES; i < historyFileArr.length; i++) {
+ File expiredFile = new File(Config.Logging.DIR, historyFileArr[i]);
+ expiredFile.delete();
+ }
+ }
+ }
+
+ private void forceExecute(Callable callable) {
+ try {
+ callable.call();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @return true if stream is prepared ready.
+ */
+ private boolean prepareWriteStream() {
+ if (fileOutputStream != null) {
+ return true;
+ }
+ File logFilePath = new File(Config.Logging.DIR);
+ if (!logFilePath.exists()) {
+ logFilePath.mkdirs();
+ } else if (!logFilePath.isDirectory()) {
+ System.err.println("Log dir(" + Config.Logging.DIR + ") is not a directory.");
+ }
+ try {
+ fileOutputStream = new FileOutputStream(new File(logFilePath, Config.Logging.FILE_NAME), true);
+ fileSize = Long.valueOf(new File(logFilePath, Config.Logging.FILE_NAME).length()).intValue();
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ return fileOutputStream != null;
+ }
+
+ /**
+ * Write log to the queue. W/ performance trade off.
+ *
+ * @param message to log
+ */
+ @Override
+ public void write(String message) {
+ logBuffer.offer(message);
+ }
+}
diff --git a/hippo4j-common/src/test/java/cn/hippo4j/common/executor/ExecutorFactoryTest.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/IWriter.java
similarity index 88%
rename from hippo4j-common/src/test/java/cn/hippo4j/common/executor/ExecutorFactoryTest.java
rename to agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/IWriter.java
index 8cd8e010..1dae6b82 100644
--- a/hippo4j-common/src/test/java/cn/hippo4j/common/executor/ExecutorFactoryTest.java
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/IWriter.java
@@ -15,7 +15,9 @@
* limitations under the License.
*/
-package cn.hippo4j.common.executor;
+package cn.hippo4j.agent.core.logging.core;
-public final class ExecutorFactoryTest {
+public interface IWriter {
+
+ void write(String message);
}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/JsonLogResolver.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/JsonLogResolver.java
new file mode 100644
index 00000000..913bc770
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/JsonLogResolver.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogResolver;
+import com.google.gson.Gson;
+
+public class JsonLogResolver implements LogResolver {
+
+ private static final Gson GSON = new Gson();
+
+ @Override
+ public ILog getLogger(Class> aClass) {
+ return new JsonLogger(aClass, GSON);
+ }
+
+ @Override
+ public ILog getLogger(String s) {
+ return new JsonLogger(s, GSON);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/JsonLogger.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/JsonLogger.java
new file mode 100644
index 00000000..fc657ef1
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/JsonLogger.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core;
+
+import cn.hippo4j.agent.core.logging.core.converters.LiteralConverter;
+import com.google.gson.Gson;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An alternative logger for the Hippo4j agent. The default layout is
+ * {
+ * "@timestamp": "", // timestamp
+ * "logger": "", // name of the Logger
+ * "level": "", // info|debug|warn|error
+ * "thread": "", // thread where the log method is called
+ * "message": "", // your log message
+ * "throwable": "",
+ * "agent_name" "service_name"
+ * }
+ */
+public class JsonLogger extends AbstractLogger {
+
+ private final Gson gson;
+
+ public JsonLogger(Class> targetClass, Gson gson) {
+ this(targetClass.getSimpleName(), gson);
+ }
+
+ /**
+ * In the Constructor, the instances of converters are created,
+ * except those {@link LiteralConverter} since this class is used
+ * only the literals in {@link PatternLogger} ,
+ * and thus should not be added to the json log.
+ *
+ * @param targetClass the logger class
+ * @param gson instance of Gson works as json serializer
+ */
+ public JsonLogger(String targetClass, Gson gson) {
+ super(targetClass);
+ this.gson = gson;
+ for (Map.Entry> entry : DEFAULT_CONVERTER_MAP.entrySet()) {
+ final Class extends Converter> converterClass = entry.getValue();
+ try {
+ if (converters instanceof LiteralConverter) {
+ continue;
+ }
+ converters.add(converterClass.newInstance());
+ } catch (IllegalAccessException | InstantiationException e) {
+ throw new IllegalStateException("Create Converter error. Class: " + converterClass, e);
+ }
+ }
+ }
+
+ @Override
+ protected String format(LogLevel level, String message, Throwable e) {
+ LogEvent logEvent = new LogEvent(level, message, e, this.targetClass);
+ Map log = new HashMap<>(this.converters.size());
+ for (Converter converter : this.converters) {
+ log.put(converter.getKey(), converter.convert(logEvent));
+ }
+ return this.gson.toJson(log);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogEvent.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogEvent.java
new file mode 100644
index 00000000..1246232d
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogEvent.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core;
+
+/**
+ * The representation of logging events. This instance is pass around to the List of Converter.
+ */
+public class LogEvent {
+
+ public LogEvent(LogLevel level, String message, Throwable throwable, String targetClass) {
+ this.level = level;
+ this.message = message;
+ this.throwable = throwable;
+ this.targetClass = targetClass;
+ }
+
+ private LogLevel level;
+ private String message;
+ private Throwable throwable;
+ private String targetClass;
+
+ public String getTargetClass() {
+ return targetClass;
+ }
+
+ public void setTargetClass(String targetClass) {
+ this.targetClass = targetClass;
+ }
+
+ public LogLevel getLevel() {
+ return level;
+ }
+
+ public void setLevel(LogLevel level) {
+ this.level = level;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public Throwable getThrowable() {
+ return throwable;
+ }
+
+ public void setThrowable(Throwable throwable) {
+ this.throwable = throwable;
+ }
+}
diff --git a/hippo4j-auth/src/test/java/cn/hippo4j/auth/toolkit/JwtTokenUtilTest.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogLevel.java
similarity index 88%
rename from hippo4j-auth/src/test/java/cn/hippo4j/auth/toolkit/JwtTokenUtilTest.java
rename to agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogLevel.java
index 28c8b227..facc35b3 100644
--- a/hippo4j-auth/src/test/java/cn/hippo4j/auth/toolkit/JwtTokenUtilTest.java
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogLevel.java
@@ -15,7 +15,8 @@
* limitations under the License.
*/
-package cn.hippo4j.auth.toolkit;
+package cn.hippo4j.agent.core.logging.core;
-public final class JwtTokenUtilTest {
+public enum LogLevel {
+ TRACE, DEBUG, INFO, WARN, ERROR, OFF
}
diff --git a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/ExecutorContext.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogMessageHolder.java
similarity index 68%
rename from hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/ExecutorContext.java
rename to agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogMessageHolder.java
index 7cd2db56..244733b5 100644
--- a/hippo4j-core/src/main/java/cn/hippo4j/core/executor/support/ExecutorContext.java
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogMessageHolder.java
@@ -15,23 +15,21 @@
* limitations under the License.
*/
-package cn.hippo4j.core.executor.support;
-
-import org.slf4j.MDC;
-
-import static cn.hippo4j.common.constant.Constants.EXECUTE_TIMEOUT_TRACE;
+package cn.hippo4j.agent.core.logging.core;
/**
- * Executor context.
+ * The LogMessageHolder
is a {@link String} holder, in order to in-process propagation String across the
+ * disruptor queue.
*/
-public class ExecutorContext {
+public class LogMessageHolder {
+
+ private String message;
+
+ public String getMessage() {
+ return message;
+ }
- /**
- * Put execute timeout trace.
- *
- * @param executeTimeoutTrace
- */
- public static void putExecuteTimeoutTrace(String executeTimeoutTrace) {
- MDC.put(EXECUTE_TIMEOUT_TRACE, executeTimeoutTrace);
+ public void setMessage(String message) {
+ this.message = message;
}
}
diff --git a/hippo4j-common/src/test/java/cn/hippo4j/common/executor/ThreadPoolManagerTest.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogOutput.java
similarity index 90%
rename from hippo4j-common/src/test/java/cn/hippo4j/common/executor/ThreadPoolManagerTest.java
rename to agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogOutput.java
index de10bbd9..2bf279a8 100644
--- a/hippo4j-common/src/test/java/cn/hippo4j/common/executor/ThreadPoolManagerTest.java
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogOutput.java
@@ -15,7 +15,8 @@
* limitations under the License.
*/
-package cn.hippo4j.common.executor;
+package cn.hippo4j.agent.core.logging.core;
-public final class ThreadPoolManagerTest {
+public enum LogOutput {
+ FILE, CONSOLE
}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/Parser.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/Parser.java
new file mode 100644
index 00000000..999b78f7
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/Parser.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core;
+
+import cn.hippo4j.agent.core.logging.core.converters.LiteralConverter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Parser of LogPattern. It is used to parse a pattern to the List of Converter.
+ */
+public class Parser {
+
+ private final Map> convertMaps;
+
+ enum State {
+ LITERAL_STATE, KEYWORD_STATE
+ }
+
+ public static final char ESCAPE_CHAR = '\\';
+ public static final char PERCENT_CHAR = '%';
+
+ private final String pattern;
+ private final int patternLength;
+ private int pointer = 0;
+ private State state = State.LITERAL_STATE;
+
+ public Parser(String pattern, Map> convertMaps) {
+ if (pattern == null || pattern.length() == 0) {
+ throw new IllegalArgumentException("null or empty pattern string not allowed");
+ }
+ this.convertMaps = convertMaps;
+ this.pattern = pattern;
+ this.patternLength = pattern.length();
+ }
+
+ public List parse() {
+ List patternConverters = new ArrayList();
+ StringBuilder buf = new StringBuilder();
+ while (pointer < patternLength) {
+ char c = pattern.charAt(pointer);
+ pointer++;
+ switch (state) {
+ case LITERAL_STATE:
+ handleLiteralState(c, buf, patternConverters);
+ break;
+ case KEYWORD_STATE:
+ handleKeywordState(c, buf, patternConverters);
+ break;
+ default:
+ }
+ }
+
+ switch (state) {
+ case LITERAL_STATE:
+ addConverter(buf, patternConverters, LiteralConverter.class);
+ break;
+ case KEYWORD_STATE:
+ addConverterWithKeyword(buf, patternConverters);
+ break;
+ default:
+ }
+ return combineLiteral(patternConverters);
+ }
+
+ private List combineLiteral(List patternConverters) {
+ List converterList = new ArrayList();
+ StringBuilder stringBuilder = new StringBuilder();
+ for (Converter patternConverter : patternConverters) {
+ if (patternConverter instanceof LiteralConverter) {
+ stringBuilder.append(patternConverter.convert(null));
+ } else {
+ if (stringBuilder.length() > 0) {
+ converterList.add(new LiteralConverter(stringBuilder.toString()));
+ stringBuilder.setLength(0);
+ }
+ converterList.add(patternConverter);
+ }
+ }
+ return converterList;
+ }
+
+ private void handleKeywordState(char c, StringBuilder buf, List patternConverters) {
+ if (Character.isJavaIdentifierPart(c)) {
+ buf.append(c);
+ } else if (c == PERCENT_CHAR) {
+ addConverterWithKeyword(buf, patternConverters);
+ } else {
+ addConverterWithKeyword(buf, patternConverters);
+ if (c == ESCAPE_CHAR) {
+ escape("%", buf);
+ } else {
+ buf.append(c);
+ }
+ state = State.LITERAL_STATE;
+ }
+ }
+
+ private void addConverterWithKeyword(StringBuilder buf, List patternConverters) {
+ String keyword = buf.toString();
+ if (convertMaps.containsKey(keyword)) {
+ addConverter(buf, patternConverters, convertMaps.get(keyword));
+ } else {
+ buf.insert(0, "%");
+ addConverter(buf, patternConverters, LiteralConverter.class);
+ }
+ }
+
+ private void handleLiteralState(char c, StringBuilder buf, List patternConverters) {
+ switch (c) {
+ case ESCAPE_CHAR:
+ escape("%", buf);
+ break;
+ case PERCENT_CHAR:
+ addConverter(buf, patternConverters, LiteralConverter.class);
+ state = State.KEYWORD_STATE;
+ break;
+ default:
+ buf.append(c);
+ }
+
+ }
+
+ private void escape(String escapeChars, StringBuilder buf) {
+ if (pointer < patternLength) {
+ char next = pattern.charAt(pointer++);
+ escape(escapeChars, buf, next);
+ }
+ }
+
+ private void addConverter(StringBuilder buf, List patternConverters, Class extends Converter> aClass) {
+ if (buf.length() > 0) {
+ String result = buf.toString();
+ if (LiteralConverter.class.equals(aClass)) {
+ patternConverters.add(new LiteralConverter(result));
+ } else {
+ try {
+ patternConverters.add(aClass.newInstance());
+ } catch (Exception e) {
+ throw new IllegalStateException("Create Converter error. Class: " + aClass, e);
+ }
+ }
+ buf.setLength(0);
+ }
+ }
+
+ private void escape(String escapeChars, StringBuilder buf, char next) {
+ if (escapeChars.indexOf(next) >= 0) {
+ buf.append(next);
+ } else {
+ switch (next) {
+ case '_':
+ // the \_ sequence is swallowed
+ break;
+ case '\\':
+ buf.append(next);
+ break;
+ case 't':
+ buf.append('\t');
+ break;
+ case 'r':
+ buf.append('\r');
+ break;
+ case 'n':
+ buf.append('\n');
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal char " + next + ". It not allowed as escape characters.");
+ }
+ }
+ }
+
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/PatternLogResolver.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/PatternLogResolver.java
new file mode 100644
index 00000000..075c2e01
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/PatternLogResolver.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core;
+
+import cn.hippo4j.agent.core.conf.Config;
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogResolver;
+
+public class PatternLogResolver implements LogResolver {
+
+ @Override
+ public ILog getLogger(Class> clazz) {
+ return new PatternLogger(clazz, Config.Logging.PATTERN);
+ }
+
+ @Override
+ public ILog getLogger(String clazz) {
+ return new PatternLogger(clazz, Config.Logging.PATTERN);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/PatternLogger.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/PatternLogger.java
new file mode 100644
index 00000000..b9e4ef67
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/PatternLogger.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.util.StringUtil;
+
+/**
+ * A flexible Logger configurable with pattern string. This is default implementation of {@link ILog} This can parse a
+ * pattern to the List of converter with Parser. We package LogEvent with message, level,timestamp ..., passing around
+ * to the List of converter to concat actually Log-String.
+ */
+public class PatternLogger extends AbstractLogger {
+
+ public static final String DEFAULT_PATTERN = "%level %timestamp %thread %class : %msg %throwable";
+
+ private String pattern;
+
+ public PatternLogger(Class> targetClass, String pattern) {
+ this(targetClass.getSimpleName(), pattern);
+ }
+
+ public PatternLogger(String targetClass, String pattern) {
+ super(targetClass);
+ this.setPattern(pattern);
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ public void setPattern(String pattern) {
+ if (StringUtil.isEmpty(pattern)) {
+ pattern = DEFAULT_PATTERN;
+ }
+ this.pattern = pattern;
+ this.converters = new Parser(pattern, DEFAULT_CONVERTER_MAP).parse();
+ }
+
+ @Override
+ protected String format(LogLevel level, String message, Throwable t) {
+ LogEvent logEvent = new LogEvent(level, message, t, targetClass);
+ StringBuilder stringBuilder = new StringBuilder();
+ for (Converter converter : this.converters) {
+ stringBuilder.append(converter.convert(logEvent));
+ }
+ return stringBuilder.toString();
+ }
+}
diff --git a/hippo4j-auth/src/test/java/cn/hippo4j/auth/secuity/JwtTokenManagerTest.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/ResolverType.java
similarity index 90%
rename from hippo4j-auth/src/test/java/cn/hippo4j/auth/secuity/JwtTokenManagerTest.java
rename to agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/ResolverType.java
index 060e1632..734d9294 100644
--- a/hippo4j-auth/src/test/java/cn/hippo4j/auth/secuity/JwtTokenManagerTest.java
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/ResolverType.java
@@ -15,7 +15,8 @@
* limitations under the License.
*/
-package cn.hippo4j.auth.secuity;
+package cn.hippo4j.agent.core.logging.core;
-public final class JwtTokenManagerTest {
+public enum ResolverType {
+ JSON, PATTERN
}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/SystemOutWriter.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/SystemOutWriter.java
new file mode 100644
index 00000000..e4cce119
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/SystemOutWriter.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core;
+
+import java.io.PrintStream;
+
+public enum SystemOutWriter implements IWriter {
+
+ INSTANCE;
+
+ /**
+ * Tricky codes for avoiding style-check. Because, in here, "system.out.println" is the only choice to output logs.
+ */
+ @Override
+ public void write(String message) {
+ PrintStream out = System.out;
+ out.println(message);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/WriterFactory.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/WriterFactory.java
new file mode 100644
index 00000000..72f2ddee
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/WriterFactory.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core;
+
+import cn.hippo4j.agent.core.boot.AgentPackageNotFoundException;
+import cn.hippo4j.agent.core.boot.AgentPackagePath;
+import cn.hippo4j.agent.core.conf.Config;
+import cn.hippo4j.agent.core.conf.SnifferConfigInitializer;
+import cn.hippo4j.agent.core.plugin.PluginFinder;
+import cn.hippo4j.agent.core.util.StringUtil;
+
+public class WriterFactory {
+
+ private static IWriter WRITER;
+
+ public static IWriter getLogWriter() {
+
+ switch (Config.Logging.OUTPUT) {
+ case FILE:
+ if (WRITER != null) {
+ return WRITER;
+ }
+ if (SnifferConfigInitializer.isInitCompleted()
+ && PluginFinder.isPluginInitCompleted()
+ && AgentPackagePath.isPathFound()) {
+ if (StringUtil.isEmpty(Config.Logging.DIR)) {
+ try {
+ Config.Logging.DIR = AgentPackagePath.getPath() + "/logs";
+ } catch (AgentPackageNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ WRITER = FileWriter.get();
+ } else {
+ return SystemOutWriter.INSTANCE;
+ }
+ break;
+ default:
+ return SystemOutWriter.INSTANCE;
+
+ }
+ return WRITER;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/AgentNameConverter.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/AgentNameConverter.java
new file mode 100644
index 00000000..21090d25
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/AgentNameConverter.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core.converters;
+
+import cn.hippo4j.agent.core.conf.Config;
+import cn.hippo4j.agent.core.logging.core.Converter;
+import cn.hippo4j.agent.core.logging.core.LogEvent;
+
+public class AgentNameConverter implements Converter {
+
+ @Override
+ public String convert(LogEvent logEvent) {
+ return Config.Agent.SERVICE_NAME;
+ }
+
+ @Override
+ public String getKey() {
+ return "agent_name";
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ClassConverter.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ClassConverter.java
new file mode 100644
index 00000000..5dc9951d
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ClassConverter.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core.converters;
+
+import cn.hippo4j.agent.core.logging.core.Converter;
+import cn.hippo4j.agent.core.logging.core.LogEvent;
+
+/**
+ * Just return logEvent.getTargetClass().
+ */
+public class ClassConverter implements Converter {
+
+ @Override
+ public String convert(LogEvent logEvent) {
+ return logEvent.getTargetClass();
+ }
+
+ @Override
+ public String getKey() {
+ return "logger";
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/DateConverter.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/DateConverter.java
new file mode 100644
index 00000000..e3a68067
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/DateConverter.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core.converters;
+
+import cn.hippo4j.agent.core.logging.core.Converter;
+import cn.hippo4j.agent.core.logging.core.LogEvent;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * The Converter is used to return a now date with format.
+ */
+public class DateConverter implements Converter {
+
+ @Override
+ public String convert(LogEvent logEvent) {
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
+ }
+
+ @Override
+ public String getKey() {
+ return "@timestamp";
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/LevelConverter.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/LevelConverter.java
new file mode 100644
index 00000000..775c97ce
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/LevelConverter.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core.converters;
+
+import cn.hippo4j.agent.core.logging.core.Converter;
+import cn.hippo4j.agent.core.logging.core.LogEvent;
+
+/**
+ * Just return logEvent.getLevel().name()
+ */
+public class LevelConverter implements Converter {
+
+ @Override
+ public String convert(LogEvent logEvent) {
+ return logEvent.getLevel().name();
+ }
+
+ @Override
+ public String getKey() {
+ return "level";
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/LiteralConverter.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/LiteralConverter.java
new file mode 100644
index 00000000..bd334955
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/LiteralConverter.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core.converters;
+
+import cn.hippo4j.agent.core.logging.core.Converter;
+import cn.hippo4j.agent.core.logging.core.LogEvent;
+
+/**
+ * This Converter is used to return the literal.
+ */
+public class LiteralConverter implements Converter {
+
+ private final String literal;
+
+ public LiteralConverter(String literal) {
+ this.literal = literal;
+ }
+
+ @Override
+ public String convert(LogEvent logEvent) {
+ return literal;
+ }
+
+ @Override
+ public String getKey() {
+ return "";
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/MessageConverter.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/MessageConverter.java
new file mode 100644
index 00000000..a38cc3c7
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/MessageConverter.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core.converters;
+
+import cn.hippo4j.agent.core.logging.core.Converter;
+import cn.hippo4j.agent.core.logging.core.LogEvent;
+
+/**
+ * Just return the logEvent.getMessage()
+ */
+public class MessageConverter implements Converter {
+
+ @Override
+ public String convert(LogEvent logEvent) {
+ return logEvent.getMessage();
+ }
+
+ @Override
+ public String getKey() {
+ return "message";
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ThreadConverter.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ThreadConverter.java
new file mode 100644
index 00000000..7bd6b16f
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ThreadConverter.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core.converters;
+
+import cn.hippo4j.agent.core.logging.core.Converter;
+import cn.hippo4j.agent.core.logging.core.LogEvent;
+
+/**
+ * Just return the Thread.currentThread().getName()
+ */
+public class ThreadConverter implements Converter {
+
+ @Override
+ public String convert(LogEvent logEvent) {
+ return Thread.currentThread().getName();
+ }
+
+ @Override
+ public String getKey() {
+ return "thread";
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ThrowableConverter.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ThrowableConverter.java
new file mode 100644
index 00000000..8d307f0a
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ThrowableConverter.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.logging.core.converters;
+
+import cn.hippo4j.agent.core.conf.Constants;
+import cn.hippo4j.agent.core.logging.core.Converter;
+import cn.hippo4j.agent.core.logging.core.LogEvent;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Return the StackTrace of String with logEvent.getThrowable()
+ */
+public class ThrowableConverter implements Converter {
+
+ @Override
+ public String convert(LogEvent logEvent) {
+ Throwable t = logEvent.getThrowable();
+ return t == null ? "" : format(t);
+ }
+
+ public static String format(Throwable t) {
+ ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ t.printStackTrace(new java.io.PrintWriter(buf, true));
+ String expMessage = buf.toString();
+ try {
+ buf.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return Constants.LINE_SEPARATOR + expMessage;
+ }
+
+ @Override
+ public String getKey() {
+ return "throwable";
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/os/OSUtil.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/os/OSUtil.java
new file mode 100644
index 00000000..c3040fff
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/os/OSUtil.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.os;
+
+import java.lang.management.ManagementFactory;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+
+public class OSUtil {
+
+ private static volatile String OS_NAME;
+ private static volatile String HOST_NAME;
+ private static volatile List IPV4_LIST;
+ private static volatile int PROCESS_NO = 0;
+
+ public static String getOsName() {
+ if (OS_NAME == null) {
+ OS_NAME = System.getProperty("os.name");
+ }
+ return OS_NAME;
+ }
+
+ public static String getHostName() {
+ if (HOST_NAME == null) {
+ try {
+ InetAddress host = InetAddress.getLocalHost();
+ HOST_NAME = host.getHostName();
+ } catch (UnknownHostException e) {
+ HOST_NAME = "unknown";
+ }
+ }
+ return HOST_NAME;
+ }
+
+ public static List getAllIPV4() {
+ if (IPV4_LIST == null) {
+ IPV4_LIST = new LinkedList<>();
+ try {
+ Enumeration interfs = NetworkInterface.getNetworkInterfaces();
+ while (interfs.hasMoreElements()) {
+ NetworkInterface networkInterface = interfs.nextElement();
+ Enumeration inetAddresses = networkInterface.getInetAddresses();
+ while (inetAddresses.hasMoreElements()) {
+ InetAddress address = inetAddresses.nextElement();
+ if (address instanceof Inet4Address) {
+ String addressStr = address.getHostAddress();
+ if ("127.0.0.1".equals(addressStr)) {
+ continue;
+ } else if ("localhost".equals(addressStr)) {
+ continue;
+ }
+ IPV4_LIST.add(addressStr);
+ }
+ }
+ }
+ } catch (SocketException e) {
+
+ }
+ }
+ return IPV4_LIST;
+ }
+
+ public static String getIPV4() {
+ final List allIPV4 = getAllIPV4();
+ if (allIPV4.size() > 0) {
+ return allIPV4.get(0);
+ } else {
+ return "no-hostname";
+ }
+ }
+
+ public static int getProcessNo() {
+ if (PROCESS_NO == 0) {
+ try {
+ PROCESS_NO = Integer.parseInt(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]);
+ } catch (Exception e) {
+ PROCESS_NO = -1;
+ }
+ }
+ return PROCESS_NO;
+ }
+
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/os/ProcessorUtil.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/os/ProcessorUtil.java
new file mode 100644
index 00000000..30abb493
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/os/ProcessorUtil.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.os;
+
+import java.lang.management.ManagementFactory;
+
+public class ProcessorUtil {
+
+ public static int getNumberOfProcessors() {
+ return ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/AbstractClassEnhancePluginDefine.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/AbstractClassEnhancePluginDefine.java
new file mode 100644
index 00000000..41384b1a
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/AbstractClassEnhancePluginDefine.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.ClassEnhancePluginDefine;
+import cn.hippo4j.agent.core.plugin.interceptor.v2.InstanceMethodsInterceptV2Point;
+import cn.hippo4j.agent.core.plugin.interceptor.v2.StaticMethodsInterceptV2Point;
+import cn.hippo4j.agent.core.plugin.match.ClassMatch;
+import cn.hippo4j.agent.core.util.CollectionUtil;
+import cn.hippo4j.agent.core.util.StringUtil;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.dynamic.DynamicType;
+
+import java.util.List;
+
+/**
+ * Basic abstract class of all sky-walking auto-instrumentation plugins.
+ *
+ * It provides the outline of enhancing the target class. If you want to know more about enhancing, you should go to see
+ * {@link ClassEnhancePluginDefine}
+ */
+public abstract class AbstractClassEnhancePluginDefine {
+
+ private static final ILog LOGGER = LogManager.getLogger(AbstractClassEnhancePluginDefine.class);
+
+ /**
+ * New field name.
+ */
+ public static final String CONTEXT_ATTR_NAME = "_$EnhancedClassField_ws";
+
+ /**
+ * Main entrance of enhancing the class.
+ *
+ * @param typeDescription target class description.
+ * @param builder byte-buddy's builder to manipulate target class's bytecode.
+ * @param classLoader load the given transformClass
+ * @return the new builder, or null
if not be enhanced.
+ * @throws PluginException when set builder failure.
+ */
+ public DynamicType.Builder> define(TypeDescription typeDescription, DynamicType.Builder> builder,
+ ClassLoader classLoader, EnhanceContext context) throws PluginException {
+ String interceptorDefineClassName = this.getClass().getName();
+ String transformClassName = typeDescription.getTypeName();
+ if (StringUtil.isEmpty(transformClassName)) {
+ LOGGER.warn("classname of being intercepted is not defined by {}.", interceptorDefineClassName);
+ return null;
+ }
+
+ LOGGER.debug("prepare to enhance class {} by {}.", transformClassName, interceptorDefineClassName);
+ WitnessFinder finder = WitnessFinder.INSTANCE;
+ /**
+ * find witness classes for enhance class
+ */
+ String[] witnessClasses = witnessClasses();
+ if (witnessClasses != null) {
+ for (String witnessClass : witnessClasses) {
+ if (!finder.exist(witnessClass, classLoader)) {
+ LOGGER.warn("enhance class {} by plugin {} is not activated. Witness class {} does not exist.", transformClassName, interceptorDefineClassName, witnessClass);
+ return null;
+ }
+ }
+ }
+ List witnessMethods = witnessMethods();
+ if (!CollectionUtil.isEmpty(witnessMethods)) {
+ for (WitnessMethod witnessMethod : witnessMethods) {
+ if (!finder.exist(witnessMethod, classLoader)) {
+ LOGGER.warn("enhance class {} by plugin {} is not activated. Witness method {} does not exist.", transformClassName, interceptorDefineClassName, witnessMethod);
+ return null;
+ }
+ }
+ }
+
+ /**
+ * find origin class source code for interceptor
+ */
+ DynamicType.Builder> newClassBuilder = this.enhance(typeDescription, builder, classLoader, context);
+
+ context.initializationStageCompleted();
+ LOGGER.debug("enhance class {} by {} completely.", transformClassName, interceptorDefineClassName);
+
+ return newClassBuilder;
+ }
+
+ /**
+ * Begin to define how to enhance class. After invoke this method, only means definition is finished.
+ *
+ * @param typeDescription target class description
+ * @param newClassBuilder byte-buddy's builder to manipulate class bytecode.
+ * @return new byte-buddy's builder for further manipulation.
+ */
+ protected DynamicType.Builder> enhance(TypeDescription typeDescription, DynamicType.Builder> newClassBuilder,
+ ClassLoader classLoader, EnhanceContext context) throws PluginException {
+ newClassBuilder = this.enhanceClass(typeDescription, newClassBuilder, classLoader);
+
+ newClassBuilder = this.enhanceInstance(typeDescription, newClassBuilder, classLoader, context);
+
+ return newClassBuilder;
+ }
+
+ /**
+ * Enhance a class to intercept constructors and class instance methods.
+ *
+ * @param typeDescription target class description
+ * @param newClassBuilder byte-buddy's builder to manipulate class bytecode.
+ * @return new byte-buddy's builder for further manipulation.
+ */
+ protected abstract DynamicType.Builder> enhanceInstance(TypeDescription typeDescription,
+ DynamicType.Builder> newClassBuilder, ClassLoader classLoader,
+ EnhanceContext context) throws PluginException;
+
+ /**
+ * Enhance a class to intercept class static methods.
+ *
+ * @param typeDescription target class description
+ * @param newClassBuilder byte-buddy's builder to manipulate class bytecode.
+ * @return new byte-buddy's builder for further manipulation.
+ */
+ protected abstract DynamicType.Builder> enhanceClass(TypeDescription typeDescription, DynamicType.Builder> newClassBuilder,
+ ClassLoader classLoader) throws PluginException;
+
+ /**
+ * Define the {@link ClassMatch} for filtering class.
+ *
+ * @return {@link ClassMatch}
+ */
+ protected abstract ClassMatch enhanceClass();
+
+ /**
+ * Witness classname list. Why need witness classname? Let's see like this: A library existed two released versions
+ * (like 1.0, 2.0), which include the same target classes, but because of version iterator, they may have the same
+ * name, but different methods, or different method arguments list. So, if I want to target the particular version
+ * (let's say 1.0 for example), version number is obvious not an option, this is the moment you need "Witness
+ * classes". You can add any classes only in this particular release version ( something like class
+ * com.company.1.x.A, only in 1.0 ), and you can achieve the goal.
+ */
+ protected String[] witnessClasses() {
+ return new String[]{};
+ }
+
+ protected List witnessMethods() {
+ return null;
+ }
+
+ public boolean isBootstrapInstrumentation() {
+ return false;
+ }
+
+ /**
+ * Constructor methods intercept point. See {@link ConstructorInterceptPoint}
+ *
+ * @return collections of {@link ConstructorInterceptPoint}
+ */
+ public abstract ConstructorInterceptPoint[] getConstructorsInterceptPoints();
+
+ /**
+ * Instance methods intercept point. See {@link InstanceMethodsInterceptPoint}
+ *
+ * @return collections of {@link InstanceMethodsInterceptPoint}
+ */
+ public abstract InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints();
+
+ /**
+ * Instance methods intercept v2 point. See {@link InstanceMethodsInterceptV2Point}
+ *
+ * @return collections of {@link InstanceMethodsInterceptV2Point}
+ */
+ public abstract InstanceMethodsInterceptV2Point[] getInstanceMethodsInterceptV2Points();
+
+ /**
+ * Static methods intercept point. See {@link StaticMethodsInterceptPoint}
+ *
+ * @return collections of {@link StaticMethodsInterceptPoint}
+ */
+ public abstract StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints();
+
+ /**
+ * Instance methods intercept v2 point. See {@link InstanceMethodsInterceptV2Point}
+ *
+ * @return collections of {@link InstanceMethodsInterceptV2Point}
+ */
+ public abstract StaticMethodsInterceptV2Point[] getStaticMethodsInterceptV2Points();
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/ByteBuddyCoreClasses.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/ByteBuddyCoreClasses.java
new file mode 100644
index 00000000..95c5ef9f
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/ByteBuddyCoreClasses.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin;
+
+/**
+ * All ByteBuddy core classes required to expose, including open edge for JDK 9+ module, or Bootstrap instrumentation.
+ */
+public class ByteBuddyCoreClasses {
+
+ private static final String SHADE_PACKAGE = "cn.hippo4j.agent.dependencies.";
+
+ public static final String[] CLASSES = {
+ SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.RuntimeType",
+ SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.This",
+ SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.AllArguments",
+ SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.AllArguments$Assignment",
+ SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.SuperCall",
+ SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.Origin",
+ SHADE_PACKAGE + "net.bytebuddy.implementation.bind.annotation.Morph",
+ };
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/DynamicPluginLoader.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/DynamicPluginLoader.java
new file mode 100755
index 00000000..30ff5a94
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/DynamicPluginLoader.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin;
+
+import cn.hippo4j.agent.core.plugin.loader.AgentClassLoader;
+import cn.hippo4j.agent.core.plugin.loader.InstrumentationLoader;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ServiceLoader;
+
+/**
+ * The plugin can be inserted into the kernel by implementing this spi return PluginDefine list.
+ */
+
+public enum DynamicPluginLoader {
+
+ INSTANCE;
+
+ public List load(AgentClassLoader classLoader) {
+ List all = new ArrayList();
+ for (InstrumentationLoader instrumentationLoader : ServiceLoader.load(InstrumentationLoader.class, classLoader)) {
+ List plugins = instrumentationLoader.load(classLoader);
+ if (plugins != null && !plugins.isEmpty()) {
+ all.addAll(plugins);
+ }
+ }
+ return all;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/EnhanceContext.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/EnhanceContext.java
new file mode 100644
index 00000000..c903ce86
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/EnhanceContext.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin;
+
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.ClassEnhancePluginDefine;
+
+/**
+ * The EnhanceContext
represents the context or status for processing a class.
+ *
+ * Based on this context, the plugin core {@link ClassEnhancePluginDefine} knows how to process the specific steps for
+ * every particular plugin.
+ */
+public class EnhanceContext {
+
+ private boolean isEnhanced = false;
+ /**
+ * The object has already been enhanced or extended. e.g. added the new field, or implemented the new interface
+ */
+ private boolean objectExtended = false;
+
+ public boolean isEnhanced() {
+ return isEnhanced;
+ }
+
+ public void initializationStageCompleted() {
+ isEnhanced = true;
+ }
+
+ public boolean isObjectExtended() {
+ return objectExtended;
+ }
+
+ public void extendObjectCompleted() {
+ objectExtended = true;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/InstrumentDebuggingClass.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/InstrumentDebuggingClass.java
new file mode 100644
index 00000000..e82258fb
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/InstrumentDebuggingClass.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin;
+
+import cn.hippo4j.agent.core.boot.AgentPackageNotFoundException;
+import cn.hippo4j.agent.core.boot.AgentPackagePath;
+import cn.hippo4j.agent.core.conf.Config;
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import net.bytebuddy.dynamic.DynamicType;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * The manipulated class output. Write the dynamic classes to the `debugging` folder, when we need to do some debug and
+ * recheck.
+ */
+public enum InstrumentDebuggingClass {
+
+ INSTANCE;
+
+ private static final ILog LOGGER = LogManager.getLogger(InstrumentDebuggingClass.class);
+ private File debuggingClassesRootPath;
+
+ public void log(DynamicType dynamicType) {
+ if (!Config.Agent.IS_OPEN_DEBUGGING_CLASS) {
+ return;
+ }
+
+ /**
+ * try to do I/O things in synchronized way, to avoid unexpected situations.
+ */
+ synchronized (INSTANCE) {
+ try {
+ if (debuggingClassesRootPath == null) {
+ try {
+ debuggingClassesRootPath = new File(AgentPackagePath.getPath(), "/debugging");
+ if (!debuggingClassesRootPath.exists()) {
+ debuggingClassesRootPath.mkdir();
+ }
+ } catch (AgentPackageNotFoundException e) {
+ LOGGER.error(e, "Can't find the root path for creating /debugging folder.");
+ }
+ }
+
+ try {
+ dynamicType.saveIn(debuggingClassesRootPath);
+ } catch (IOException e) {
+ LOGGER.error(e, "Can't save class {} to file." + dynamicType.getTypeDescription().getActualName());
+ }
+ } catch (Throwable t) {
+ LOGGER.error(t, "Save debugging classes fail.");
+ }
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginBootstrap.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginBootstrap.java
new file mode 100644
index 00000000..12dffe39
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginBootstrap.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin;
+
+import cn.hippo4j.agent.core.boot.AgentPackageNotFoundException;
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.loader.AgentClassLoader;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Plugins finder. Use {@link PluginResourcesResolver} to find all plugins, and ask {@link PluginCfg} to load all plugin
+ * definitions.
+ */
+public class PluginBootstrap {
+
+ private static final ILog LOGGER = LogManager.getLogger(PluginBootstrap.class);
+
+ /**
+ * load all plugins.
+ *
+ * @return plugin definition list.
+ */
+ public List loadPlugins() throws AgentPackageNotFoundException {
+ AgentClassLoader.initDefaultLoader();
+
+ PluginResourcesResolver resolver = new PluginResourcesResolver();
+ List resources = resolver.getResources();
+
+ if (resources == null || resources.size() == 0) {
+ LOGGER.info("no plugin files (hippo4j-plugin.def) found, continue to start application.");
+ return new ArrayList();
+ }
+
+ for (URL pluginUrl : resources) {
+ try {
+ PluginCfg.INSTANCE.load(pluginUrl.openStream());
+ } catch (Throwable t) {
+ LOGGER.error(t, "plugin file [{}] init failure.", pluginUrl);
+ }
+ }
+
+ List pluginClassList = PluginCfg.INSTANCE.getPluginClassList();
+
+ List plugins = new ArrayList();
+ for (PluginDefine pluginDefine : pluginClassList) {
+ try {
+ LOGGER.debug("loading plugin class {}.", pluginDefine.getDefineClass());
+ AbstractClassEnhancePluginDefine plugin = (AbstractClassEnhancePluginDefine) Class.forName(pluginDefine.getDefineClass(), true, AgentClassLoader
+ .getDefault()).newInstance();
+ plugins.add(plugin);
+ } catch (Throwable t) {
+ LOGGER.error(t, "load plugin [{}] failure.", pluginDefine.getDefineClass());
+ }
+ }
+
+ plugins.addAll(DynamicPluginLoader.INSTANCE.load(AgentClassLoader.getDefault()));
+
+ return plugins;
+
+ }
+
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginCfg.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginCfg.java
new file mode 100644
index 00000000..81e5027b
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginCfg.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.exception.IllegalPluginDefineException;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+public enum PluginCfg {
+
+ INSTANCE;
+
+ private static final ILog LOGGER = LogManager.getLogger(PluginCfg.class);
+
+ private List pluginClassList = new ArrayList();
+ private PluginSelector pluginSelector = new PluginSelector();
+
+ void load(InputStream input) throws IOException {
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(input));
+ String pluginDefine;
+ while ((pluginDefine = reader.readLine()) != null) {
+ try {
+ if (pluginDefine.trim().length() == 0 || pluginDefine.startsWith("#")) {
+ continue;
+ }
+ PluginDefine plugin = PluginDefine.build(pluginDefine);
+ pluginClassList.add(plugin);
+ } catch (IllegalPluginDefineException e) {
+ LOGGER.error(e, "Failed to format plugin({}) define.", pluginDefine);
+ }
+ }
+ pluginClassList = pluginSelector.select(pluginClassList);
+ } finally {
+ input.close();
+ }
+ }
+
+ public List getPluginClassList() {
+ return pluginClassList;
+ }
+
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginDefine.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginDefine.java
new file mode 100644
index 00000000..79501e09
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginDefine.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin;
+
+import cn.hippo4j.agent.core.plugin.exception.IllegalPluginDefineException;
+import cn.hippo4j.agent.core.util.StringUtil;
+
+public class PluginDefine {
+
+ /**
+ * Plugin name.
+ */
+ private String name;
+
+ /**
+ * The class name of plugin defined.
+ */
+ private String defineClass;
+
+ private PluginDefine(String name, String defineClass) {
+ this.name = name;
+ this.defineClass = defineClass;
+ }
+
+ public static PluginDefine build(String define) throws IllegalPluginDefineException {
+ if (StringUtil.isEmpty(define)) {
+ throw new IllegalPluginDefineException(define);
+ }
+
+ String[] pluginDefine = define.split("=");
+ if (pluginDefine.length != 2) {
+ throw new IllegalPluginDefineException(define);
+ }
+
+ String pluginName = pluginDefine[0];
+ String defineClass = pluginDefine[1];
+ return new PluginDefine(pluginName, defineClass);
+ }
+
+ public String getDefineClass() {
+ return defineClass;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginException.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginException.java
new file mode 100644
index 00000000..6b69a009
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginException.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin;
+
+public class PluginException extends RuntimeException {
+
+ private static final long serialVersionUID = -6020188711867490724L;
+
+ public PluginException(String message) {
+ super(message);
+ }
+
+ public PluginException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginFinder.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginFinder.java
new file mode 100644
index 00000000..588d4f5f
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginFinder.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin;
+
+import cn.hippo4j.agent.core.plugin.bytebuddy.AbstractJunction;
+import cn.hippo4j.agent.core.plugin.match.ClassMatch;
+import cn.hippo4j.agent.core.plugin.match.IndirectMatch;
+import cn.hippo4j.agent.core.plugin.match.NameMatch;
+import cn.hippo4j.agent.core.plugin.match.ProtectiveShieldMatcher;
+import net.bytebuddy.description.NamedElement;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import static net.bytebuddy.matcher.ElementMatchers.isInterface;
+import static net.bytebuddy.matcher.ElementMatchers.not;
+
+/**
+ * The PluginFinder
represents a finder , which assist to find the one from the given {@link
+ * AbstractClassEnhancePluginDefine} list.
+ */
+public class PluginFinder {
+
+ private final Map> nameMatchDefine = new HashMap>();
+ private final List signatureMatchDefine = new ArrayList();
+ private final List bootstrapClassMatchDefine = new ArrayList();
+ private static boolean IS_PLUGIN_INIT_COMPLETED = false;
+
+ public PluginFinder(List plugins) {
+ for (AbstractClassEnhancePluginDefine plugin : plugins) {
+ ClassMatch match = plugin.enhanceClass();
+
+ if (match == null) {
+ continue;
+ }
+
+ if (match instanceof NameMatch) {
+ NameMatch nameMatch = (NameMatch) match;
+ LinkedList pluginDefines = nameMatchDefine.get(nameMatch.getClassName());
+ if (pluginDefines == null) {
+ pluginDefines = new LinkedList();
+ nameMatchDefine.put(nameMatch.getClassName(), pluginDefines);
+ }
+ pluginDefines.add(plugin);
+ } else {
+ signatureMatchDefine.add(plugin);
+ }
+
+ if (plugin.isBootstrapInstrumentation()) {
+ bootstrapClassMatchDefine.add(plugin);
+ }
+ }
+ }
+
+ public List find(TypeDescription typeDescription) {
+ List matchedPlugins = new LinkedList();
+ String typeName = typeDescription.getTypeName();
+ if (nameMatchDefine.containsKey(typeName)) {
+ matchedPlugins.addAll(nameMatchDefine.get(typeName));
+ }
+
+ for (AbstractClassEnhancePluginDefine pluginDefine : signatureMatchDefine) {
+ IndirectMatch match = (IndirectMatch) pluginDefine.enhanceClass();
+ if (match.isMatch(typeDescription)) {
+ matchedPlugins.add(pluginDefine);
+ }
+ }
+
+ return matchedPlugins;
+ }
+
+ public ElementMatcher super TypeDescription> buildMatch() {
+ ElementMatcher.Junction judge = new AbstractJunction() {
+
+ @Override
+ public boolean matches(NamedElement target) {
+ return nameMatchDefine.containsKey(target.getActualName());
+ }
+ };
+ judge = judge.and(not(isInterface()));
+ for (AbstractClassEnhancePluginDefine define : signatureMatchDefine) {
+ ClassMatch match = define.enhanceClass();
+ if (match instanceof IndirectMatch) {
+ judge = judge.or(((IndirectMatch) match).buildJunction());
+ }
+ }
+ return new ProtectiveShieldMatcher(judge);
+ }
+
+ public List getBootstrapClassMatchDefine() {
+ return bootstrapClassMatchDefine;
+ }
+
+ public static void pluginInitCompleted() {
+ IS_PLUGIN_INIT_COMPLETED = true;
+ }
+
+ public static boolean isPluginInitCompleted() {
+ return IS_PLUGIN_INIT_COMPLETED;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginResourcesResolver.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginResourcesResolver.java
new file mode 100644
index 00000000..06058a0b
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginResourcesResolver.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.loader.AgentClassLoader;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * Use the current classloader to read all plugin define file. The file must be named 'hippo4j-plugin.def'
+ */
+public class PluginResourcesResolver {
+
+ private static final ILog LOGGER = LogManager.getLogger(PluginResourcesResolver.class);
+
+ public List getResources() {
+ List cfgUrlPaths = new ArrayList();
+ Enumeration urls;
+ try {
+ urls = AgentClassLoader.getDefault().getResources("hippo4j-plugin.def");
+
+ while (urls.hasMoreElements()) {
+ URL pluginUrl = urls.nextElement();
+ cfgUrlPaths.add(pluginUrl);
+ LOGGER.info("find hippo4j plugin define in {}", pluginUrl);
+ }
+
+ return cfgUrlPaths;
+ } catch (IOException e) {
+ LOGGER.error("read resources failure.", e);
+ }
+ return null;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginSelector.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginSelector.java
new file mode 100644
index 00000000..8f336b36
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/PluginSelector.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import cn.hippo4j.agent.core.conf.Config;
+
+import static cn.hippo4j.agent.core.conf.Config.Plugin.EXCLUDE_PLUGINS;
+
+/**
+ * Select some plugins in activated plugins
+ */
+public class PluginSelector {
+
+ /**
+ * Exclude activated plugins
+ *
+ * @param pluginDefines the pluginDefines is loaded from activations directory or plugins directory
+ * @return real activate plugins
+ * @see Config.Plugin#EXCLUDE_PLUGINS
+ */
+ public List select(List pluginDefines) {
+ if (!EXCLUDE_PLUGINS.isEmpty()) {
+ List excludes = Arrays.asList(EXCLUDE_PLUGINS.toLowerCase().split(","));
+ return pluginDefines.stream()
+ .filter(item -> !excludes.contains(item.getName().toLowerCase()))
+ .collect(Collectors.toList());
+ }
+ return pluginDefines;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/WitnessFinder.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/WitnessFinder.java
new file mode 100644
index 00000000..a7d30be2
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/WitnessFinder.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin;
+
+import net.bytebuddy.pool.TypePool;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The WitnessFinder
represents a pool of {@link TypePool}s, each {@link TypePool} matches a {@link
+ * ClassLoader}, which helps to find the class declaration existed or not.
+ */
+public enum WitnessFinder {
+
+ INSTANCE;
+
+ private final Map poolMap = new HashMap();
+
+ /**
+ * @param classLoader for finding the witnessClass
+ * @return true, if the given witnessClass exists, through the given classLoader.
+ */
+ public boolean exist(String witnessClass, ClassLoader classLoader) {
+ return getResolution(witnessClass, classLoader)
+ .isResolved();
+ }
+
+ /**
+ * get TypePool.Resolution of the witness class
+ * @param witnessClass class name
+ * @param classLoader classLoader for finding the witnessClass
+ * @return TypePool.Resolution
+ */
+ private TypePool.Resolution getResolution(String witnessClass, ClassLoader classLoader) {
+ ClassLoader mappingKey = classLoader == null ? NullClassLoader.INSTANCE : classLoader;
+ if (!poolMap.containsKey(mappingKey)) {
+ synchronized (poolMap) {
+ if (!poolMap.containsKey(mappingKey)) {
+ TypePool classTypePool = classLoader == null ? TypePool.Default.ofBootLoader() : TypePool.Default.of(classLoader);
+ poolMap.put(mappingKey, classTypePool);
+ }
+ }
+ }
+ TypePool typePool = poolMap.get(mappingKey);
+ return typePool.describe(witnessClass);
+ }
+
+ /**
+ * @param classLoader for finding the witness method
+ * @return true, if the given witness method exists, through the given classLoader.
+ */
+ public boolean exist(WitnessMethod witnessMethod, ClassLoader classLoader) {
+ TypePool.Resolution resolution = getResolution(witnessMethod.getDeclaringClassName(), classLoader);
+ if (!resolution.isResolved()) {
+ return false;
+ }
+ return !resolution.resolve()
+ .getDeclaredMethods()
+ .filter(witnessMethod.getElementMatcher())
+ .isEmpty();
+ }
+
+}
+
+final class NullClassLoader extends ClassLoader {
+
+ static NullClassLoader INSTANCE = new NullClassLoader();
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/WitnessMethod.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/WitnessMethod.java
new file mode 100644
index 00000000..46c62d2b
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/WitnessMethod.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.ToString;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+/**
+ * Witness Method for plugin activation
+ */
+@ToString
+@RequiredArgsConstructor
+public class WitnessMethod {
+
+ /**
+ * the class or interface name where the witness method is declared.
+ */
+ @Getter
+ private final String declaringClassName;
+ /**
+ * matcher to match the witness method
+ */
+ @Getter
+ private final ElementMatcher super MethodDescription.InDefinedShape> elementMatcher;
+
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/BootstrapInstrumentBoost.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/BootstrapInstrumentBoost.java
new file mode 100644
index 00000000..3260e439
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/BootstrapInstrumentBoost.java
@@ -0,0 +1,301 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bootstrap;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.AbstractClassEnhancePluginDefine;
+import cn.hippo4j.agent.core.plugin.ByteBuddyCoreClasses;
+import cn.hippo4j.agent.core.plugin.InstrumentDebuggingClass;
+import cn.hippo4j.agent.core.plugin.PluginException;
+import cn.hippo4j.agent.core.plugin.PluginFinder;
+import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.v2.InstanceMethodsInterceptV2Point;
+import cn.hippo4j.agent.core.plugin.interceptor.v2.StaticMethodsInterceptV2Point;
+import cn.hippo4j.agent.core.plugin.jdk9module.JDK9ModuleExporter;
+import cn.hippo4j.agent.core.plugin.loader.AgentClassLoader;
+import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.agent.builder.AgentBuilder;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.dynamic.ClassFileLocator;
+import net.bytebuddy.dynamic.DynamicType;
+import net.bytebuddy.dynamic.loading.ClassInjector;
+import net.bytebuddy.pool.TypePool;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.instrument.Instrumentation;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+
+/**
+ * If there is Bootstrap instrumentation plugin declared in plugin list, BootstrapInstrumentBoost inject the necessary
+ * classes into bootstrap class loader, including generated dynamic delegate classes.
+ */
+public class BootstrapInstrumentBoost {
+
+ private static final ILog LOGGER = LogManager.getLogger(BootstrapInstrumentBoost.class);
+
+ private static final String[] HIGH_PRIORITY_CLASSES = {
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist",
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor",
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor",
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.StaticMethodsAroundInterceptor",
+ "cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog",
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance",
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.OverrideCallable",
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult",
+
+ // interceptor v2
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2",
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.StaticMethodsAroundInterceptorV2",
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext",
+ };
+
+ private static String INSTANCE_METHOD_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.InstanceMethodInterTemplate";
+ private static String INSTANCE_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.InstanceMethodInterWithOverrideArgsTemplate";
+ private static String CONSTRUCTOR_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.ConstructorInterTemplate";
+ private static String STATIC_METHOD_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.StaticMethodInterTemplate";
+ private static String STATIC_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.StaticMethodInterWithOverrideArgsTemplate";
+
+ private static String INSTANCE_METHOD_V2_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.v2.InstanceMethodInterV2Template";
+ private static String INSTANCE_METHOD_V2_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.v2.InstanceMethodInterV2WithOverrideArgsTemplate";
+ private static String STATIC_METHOD_V2_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.v2.StaticMethodInterV2Template";
+ private static String STATIC_METHOD_V2_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE = "cn.hippo4j.agent.core.plugin.bootstrap.template.v2.StaticMethodInterV2WithOverrideArgsTemplate";
+
+ public static AgentBuilder inject(PluginFinder pluginFinder, Instrumentation instrumentation,
+ AgentBuilder agentBuilder, JDK9ModuleExporter.EdgeClasses edgeClasses) throws PluginException {
+ Map classesTypeMap = new LinkedHashMap<>();
+
+ if (!prepareJREInstrumentation(pluginFinder, classesTypeMap)) {
+ return agentBuilder;
+ }
+
+ if (!prepareJREInstrumentationV2(pluginFinder, classesTypeMap)) {
+ return agentBuilder;
+ }
+
+ for (String highPriorityClass : HIGH_PRIORITY_CLASSES) {
+ loadHighPriorityClass(classesTypeMap, highPriorityClass);
+ }
+ for (String highPriorityClass : ByteBuddyCoreClasses.CLASSES) {
+ loadHighPriorityClass(classesTypeMap, highPriorityClass);
+ }
+
+ /**
+ * Prepare to open edge of necessary classes.
+ */
+ for (String generatedClass : classesTypeMap.keySet()) {
+ edgeClasses.add(generatedClass);
+ }
+
+ /**
+ * Inject the classes into bootstrap class loader by using Unsafe Strategy.
+ * ByteBuddy adapts the sun.misc.Unsafe and jdk.internal.misc.Unsafe automatically.
+ */
+ ClassInjector.UsingUnsafe.Factory factory = ClassInjector.UsingUnsafe.Factory.resolve(instrumentation);
+ factory.make(null, null).injectRaw(classesTypeMap);
+ agentBuilder = agentBuilder.with(new AgentBuilder.InjectionStrategy.UsingUnsafe.OfFactory(factory));
+
+ return agentBuilder;
+ }
+
+ /**
+ * Get the delegate class name.
+ *
+ * @param methodsInterceptor of original interceptor in the plugin
+ * @return generated delegate class name
+ */
+ public static String internalDelegate(String methodsInterceptor) {
+ return methodsInterceptor + "_internal";
+ }
+
+ /**
+ * Load the delegate class from current class loader, mostly should be AppClassLoader.
+ *
+ * @param methodsInterceptor of original interceptor in the plugin
+ * @return generated delegate class
+ */
+ public static Class forInternalDelegateClass(String methodsInterceptor) {
+ try {
+ return Class.forName(internalDelegate(methodsInterceptor));
+ } catch (ClassNotFoundException e) {
+ throw new PluginException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Generate dynamic delegate for ByteBuddy
+ *
+ * @param pluginFinder gets the whole plugin list.
+ * @param classesTypeMap hosts the class binary.
+ * @return true if have JRE instrumentation requirement.
+ * @throws PluginException when generate failure.
+ */
+ private static boolean prepareJREInstrumentation(PluginFinder pluginFinder,
+ Map classesTypeMap) throws PluginException {
+ TypePool typePool = TypePool.Default.of(BootstrapInstrumentBoost.class.getClassLoader());
+ List bootstrapClassMatchDefines = pluginFinder.getBootstrapClassMatchDefine();
+ for (AbstractClassEnhancePluginDefine define : bootstrapClassMatchDefines) {
+ if (Objects.nonNull(define.getInstanceMethodsInterceptPoints())) {
+ for (InstanceMethodsInterceptPoint point : define.getInstanceMethodsInterceptPoints()) {
+ if (point.isOverrideArgs()) {
+ generateDelegator(
+ classesTypeMap, typePool, INSTANCE_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE, point
+ .getMethodsInterceptor());
+ } else {
+ generateDelegator(
+ classesTypeMap, typePool, INSTANCE_METHOD_DELEGATE_TEMPLATE, point.getMethodsInterceptor());
+ }
+ }
+ }
+
+ if (Objects.nonNull(define.getConstructorsInterceptPoints())) {
+ for (ConstructorInterceptPoint point : define.getConstructorsInterceptPoints()) {
+ generateDelegator(
+ classesTypeMap, typePool, CONSTRUCTOR_DELEGATE_TEMPLATE, point.getConstructorInterceptor());
+ }
+ }
+
+ if (Objects.nonNull(define.getStaticMethodsInterceptPoints())) {
+ for (StaticMethodsInterceptPoint point : define.getStaticMethodsInterceptPoints()) {
+ if (point.isOverrideArgs()) {
+ generateDelegator(
+ classesTypeMap, typePool, STATIC_METHOD_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE, point
+ .getMethodsInterceptor());
+ } else {
+ generateDelegator(
+ classesTypeMap, typePool, STATIC_METHOD_DELEGATE_TEMPLATE, point.getMethodsInterceptor());
+ }
+ }
+ }
+ }
+ return bootstrapClassMatchDefines.size() > 0;
+ }
+
+ private static boolean prepareJREInstrumentationV2(PluginFinder pluginFinder,
+ Map classesTypeMap) throws PluginException {
+ TypePool typePool = TypePool.Default.of(BootstrapInstrumentBoost.class.getClassLoader());
+ List bootstrapClassMatchDefines = pluginFinder.getBootstrapClassMatchDefine();
+ for (AbstractClassEnhancePluginDefine define : bootstrapClassMatchDefines) {
+ if (Objects.nonNull(define.getInstanceMethodsInterceptV2Points())) {
+ for (InstanceMethodsInterceptV2Point point : define.getInstanceMethodsInterceptV2Points()) {
+ if (point.isOverrideArgs()) {
+ generateDelegator(classesTypeMap, typePool,
+ INSTANCE_METHOD_V2_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE,
+ point.getMethodsInterceptorV2());
+ } else {
+ generateDelegator(
+ classesTypeMap, typePool, INSTANCE_METHOD_V2_DELEGATE_TEMPLATE,
+ point.getMethodsInterceptorV2());
+ }
+ }
+ }
+
+ if (Objects.nonNull(define.getStaticMethodsInterceptV2Points())) {
+ for (StaticMethodsInterceptV2Point point : define.getStaticMethodsInterceptV2Points()) {
+ if (point.isOverrideArgs()) {
+ generateDelegator(classesTypeMap, typePool,
+ STATIC_METHOD_V2_WITH_OVERRIDE_ARGS_DELEGATE_TEMPLATE,
+ point.getMethodsInterceptorV2());
+ } else {
+ generateDelegator(
+ classesTypeMap, typePool, STATIC_METHOD_V2_DELEGATE_TEMPLATE,
+ point.getMethodsInterceptorV2());
+ }
+ }
+ }
+ }
+ return bootstrapClassMatchDefines.size() > 0;
+ }
+
+ /**
+ * Generate the delegator class based on given template class. This is preparation stage level code generation.
+ *
+ * One key step to avoid class confliction between AppClassLoader and BootstrapClassLoader
+ *
+ * @param classesTypeMap hosts injected binary of generated class
+ * @param typePool to generate new class
+ * @param templateClassName represents the class as template in this generation process. The templates are
+ * pre-defined in Hippo4j agent core.
+ */
+ private static void generateDelegator(Map classesTypeMap, TypePool typePool,
+ String templateClassName, String methodsInterceptor) {
+ String internalInterceptorName = internalDelegate(methodsInterceptor);
+ try {
+ TypeDescription templateTypeDescription = typePool.describe(templateClassName).resolve();
+
+ DynamicType.Unloaded interceptorType = new ByteBuddy().redefine(templateTypeDescription, ClassFileLocator.ForClassLoader
+ .of(BootstrapInstrumentBoost.class.getClassLoader()))
+ .name(internalInterceptorName)
+ .field(named("TARGET_INTERCEPTOR"))
+ .value(methodsInterceptor)
+ .make();
+
+ classesTypeMap.put(internalInterceptorName, interceptorType.getBytes());
+
+ InstrumentDebuggingClass.INSTANCE.log(interceptorType);
+ } catch (Exception e) {
+ throw new PluginException("Generate Dynamic plugin failure", e);
+ }
+ }
+
+ /**
+ * The class loaded by this method means it only should be loaded once in Bootstrap classloader, when bootstrap
+ * instrumentation active by any plugin
+ *
+ * @param loadedTypeMap hosts all injected class
+ * @param className to load
+ */
+ private static void loadHighPriorityClass(Map loadedTypeMap,
+ String className) throws PluginException {
+ byte[] enhancedInstanceClassFile;
+ try {
+ String classResourceName = className.replaceAll("\\.", "/") + ".class";
+ InputStream resourceAsStream = AgentClassLoader.getDefault().getResourceAsStream(classResourceName);
+
+ if (resourceAsStream == null) {
+ throw new PluginException("High priority class " + className + " not found.");
+ }
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ byte[] buffer = new byte[1024];
+ int len;
+
+ // read bytes from the input stream and store them in buffer
+ while ((len = resourceAsStream.read(buffer)) != -1) {
+ // write bytes from the buffer into output stream
+ os.write(buffer, 0, len);
+ }
+
+ enhancedInstanceClassFile = os.toByteArray();
+ } catch (IOException e) {
+ throw new PluginException(e.getMessage(), e);
+ }
+
+ loadedTypeMap.put(className, enhancedInstanceClassFile);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/BootstrapPluginLogBridge.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/BootstrapPluginLogBridge.java
new file mode 100644
index 00000000..868b1c2c
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/BootstrapPluginLogBridge.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bootstrap;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+
+/**
+ * The log bridge makes the ILog accessible inside bootstrap classloader, especially for internal interceptor.
+ */
+public class BootstrapPluginLogBridge implements IBootstrapLog {
+
+ public static IBootstrapLog getLogger(String clazz) {
+ return new BootstrapPluginLogBridge(clazz);
+ }
+
+ private final ILog logger;
+
+ private BootstrapPluginLogBridge(String clazz) {
+ logger = LogManager.getLogger(clazz);
+ }
+
+ @Override
+ public void info(String format) {
+ logger.info(format);
+ }
+
+ @Override
+ public void info(String format, Object... arguments) {
+ logger.info(format, arguments);
+ }
+
+ @Override
+ public void warn(String format, Object... arguments) {
+ logger.warn(format, arguments);
+ }
+
+ @Override
+ public void warn(Throwable e, String format, Object... arguments) {
+ logger.warn(e, format, arguments);
+ }
+
+ @Override
+ public void error(String format, Throwable e) {
+ logger.error(format, e);
+ }
+
+ @Override
+ public void error(Throwable e, String format, Object... arguments) {
+ logger.error(e, format, arguments);
+ }
+
+ @Override
+ public boolean isDebugEnable() {
+ return logger.isDebugEnable();
+ }
+
+ @Override
+ public boolean isInfoEnable() {
+ return logger.isInfoEnable();
+ }
+
+ @Override
+ public boolean isWarnEnable() {
+ return logger.isWarnEnable();
+ }
+
+ @Override
+ public boolean isErrorEnable() {
+ return logger.isErrorEnable();
+ }
+
+ @Override
+ public void debug(String format) {
+ logger.debug(format);
+ }
+
+ @Override
+ public void debug(String format, Object... arguments) {
+ logger.debug(format, arguments);
+ }
+
+ @Override
+ public void error(String format) {
+ logger.error(format);
+ }
+}
diff --git a/hippo4j-config/src/main/java/cn/hippo4j/config/event/ConfigDataChangeEvent.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/IBootstrapLog.java
similarity index 52%
rename from hippo4j-config/src/main/java/cn/hippo4j/config/event/ConfigDataChangeEvent.java
rename to agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/IBootstrapLog.java
index 0c3cd1e1..52645b44 100644
--- a/hippo4j-config/src/main/java/cn/hippo4j/config/event/ConfigDataChangeEvent.java
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/IBootstrapLog.java
@@ -15,30 +15,38 @@
* limitations under the License.
*/
-package cn.hippo4j.config.event;
-
-import org.springframework.util.StringUtils;
+package cn.hippo4j.agent.core.plugin.bootstrap;
/**
- * Config data change event.
+ * The log interface used in bootstrap internal interceptors.
+ *
+ * Never used in any plugin or tracing core.
*/
-public class ConfigDataChangeEvent extends AbstractEvent {
+public interface IBootstrapLog {
+
+ void info(String format);
+
+ void info(String format, Object... arguments);
+
+ void warn(String format, Object... arguments);
+
+ void warn(Throwable e, String format, Object... arguments);
+
+ void error(String format, Throwable e);
+
+ void error(Throwable e, String format, Object... arguments);
+
+ boolean isDebugEnable();
+
+ boolean isInfoEnable();
- public final String tenantId;
+ boolean isWarnEnable();
- public final String itemId;
+ boolean isErrorEnable();
- public final String tpId;
+ void debug(String format);
- public final long lastModifiedTs;
+ void debug(String format, Object... arguments);
- public ConfigDataChangeEvent(String tenantId, String itemId, String tpId, Long gmtModified) {
- if (StringUtils.isEmpty(tenantId) || StringUtils.isEmpty(itemId) || StringUtils.isEmpty(tpId)) {
- throw new IllegalArgumentException("DataId is null or group is null");
- }
- this.tenantId = tenantId;
- this.itemId = itemId;
- this.tpId = tpId;
- this.lastModifiedTs = gmtModified;
- }
+ void error(String format);
}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/ConstructorInterTemplate.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/ConstructorInterTemplate.java
new file mode 100644
index 00000000..3bca42a0
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/ConstructorInterTemplate.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bootstrap.template;
+
+import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.This;
+
+public class ConstructorInterTemplate {
+
+ /**
+ * This field is never set in the template, but has value in the runtime.
+ */
+ private static String TARGET_INTERCEPTOR;
+
+ private static InstanceConstructorInterceptor INTERCEPTOR;
+ private static IBootstrapLog LOGGER;
+
+ /**
+ * Intercept the target constructor.
+ *
+ * @param obj target class instance.
+ * @param allArguments all constructor arguments
+ */
+ @RuntimeType
+ public static void intercept(@This Object obj, @AllArguments Object[] allArguments) {
+ try {
+ prepare();
+
+ EnhancedInstance targetObject = (EnhancedInstance) obj;
+
+ if (INTERCEPTOR == null) {
+ return;
+ }
+ INTERCEPTOR.onConstruct(targetObject, allArguments);
+ } catch (Throwable t) {
+ LOGGER.error("ConstructorInter failure.", t);
+ }
+ }
+
+ /**
+ * Prepare the context. Link to the agent core in AppClassLoader.
+ */
+ private static void prepare() {
+ if (INTERCEPTOR == null) {
+ ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
+
+ if (loader != null) {
+ IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
+ if (logger != null) {
+ LOGGER = logger;
+
+ INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
+ }
+ } else {
+ LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
+ }
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/InstanceMethodInterTemplate.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/InstanceMethodInterTemplate.java
new file mode 100644
index 00000000..aa9cb6f3
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/InstanceMethodInterTemplate.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bootstrap.template;
+
+import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.SuperCall;
+import net.bytebuddy.implementation.bind.annotation.This;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.Callable;
+
+public class InstanceMethodInterTemplate {
+
+ /**
+ * This field is never set in the template, but has value in the runtime.
+ */
+ private static String TARGET_INTERCEPTOR;
+
+ private static InstanceMethodsAroundInterceptor INTERCEPTOR;
+ private static IBootstrapLog LOGGER;
+
+ /**
+ * Intercept the target instance method.
+ *
+ * @param obj target class instance.
+ * @param allArguments all method arguments
+ * @param method method description.
+ * @param zuper the origin call ref.
+ * @return the return value of target instance method.
+ * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
+ * bug, if anything triggers this condition ).
+ */
+ @RuntimeType
+ public static Object intercept(@This Object obj, @AllArguments Object[] allArguments, @SuperCall Callable> zuper,
+ @Origin Method method) throws Throwable {
+ EnhancedInstance targetObject = (EnhancedInstance) obj;
+
+ prepare();
+
+ MethodInterceptResult result = new MethodInterceptResult();
+ try {
+ if (INTERCEPTOR != null) {
+ INTERCEPTOR.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);
+ }
+ } catch (Throwable t) {
+ if (LOGGER != null) {
+ LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());
+ }
+ }
+
+ Object ret = null;
+ try {
+ if (!result.isContinue()) {
+ ret = result._ret();
+ } else {
+ ret = zuper.call();
+ }
+ } catch (Throwable t) {
+ try {
+ if (INTERCEPTOR != null) {
+ INTERCEPTOR.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t);
+ }
+ } catch (Throwable t2) {
+ if (LOGGER != null) {
+ LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
+ }
+ }
+ throw t;
+ } finally {
+ try {
+ if (INTERCEPTOR != null) {
+ ret = INTERCEPTOR.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);
+ }
+ } catch (Throwable t) {
+ if (LOGGER != null) {
+ LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Prepare the context. Link to the agent core in AppClassLoader.
+ */
+ private static void prepare() {
+ if (INTERCEPTOR == null) {
+ ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
+
+ if (loader != null) {
+ IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
+ if (logger != null) {
+ LOGGER = logger;
+
+ INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
+ }
+ } else {
+ LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
+ }
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/InstanceMethodInterWithOverrideArgsTemplate.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/InstanceMethodInterWithOverrideArgsTemplate.java
new file mode 100644
index 00000000..78ef6751
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/InstanceMethodInterWithOverrideArgsTemplate.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bootstrap.template;
+
+import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.OverrideCallable;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Morph;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.This;
+
+import java.lang.reflect.Method;
+
+public class InstanceMethodInterWithOverrideArgsTemplate {
+
+ /**
+ * This field is never set in the template, but has value in the runtime.
+ */
+ private static String TARGET_INTERCEPTOR;
+
+ private static InstanceMethodsAroundInterceptor INTERCEPTOR;
+ private static IBootstrapLog LOGGER;
+
+ /**
+ * Intercept the target instance method.
+ *
+ * @param obj target class instance.
+ * @param allArguments all method arguments
+ * @param method method description.
+ * @param zuper the origin call ref.
+ * @return the return value of target instance method.
+ * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
+ * bug, if anything triggers this condition ).
+ */
+ @RuntimeType
+ public static Object intercept(@This Object obj, @AllArguments Object[] allArguments, @Morph OverrideCallable zuper,
+ @Origin Method method) throws Throwable {
+ EnhancedInstance targetObject = (EnhancedInstance) obj;
+
+ prepare();
+
+ MethodInterceptResult result = new MethodInterceptResult();
+ try {
+ if (INTERCEPTOR != null) {
+ INTERCEPTOR.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);
+ }
+ } catch (Throwable t) {
+ if (LOGGER != null) {
+ LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());
+ }
+ }
+
+ Object ret = null;
+ try {
+ if (!result.isContinue()) {
+ ret = result._ret();
+ } else {
+ ret = zuper.call(allArguments);
+ }
+ } catch (Throwable t) {
+ try {
+ if (INTERCEPTOR != null) {
+ INTERCEPTOR.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t);
+ }
+ } catch (Throwable t2) {
+ if (LOGGER != null) {
+ LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
+ }
+ }
+ throw t;
+ } finally {
+ try {
+ if (INTERCEPTOR != null) {
+ ret = INTERCEPTOR.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);
+ }
+ } catch (Throwable t) {
+ if (LOGGER != null) {
+ LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Prepare the context. Link to the agent core in AppClassLoader.
+ */
+ private static void prepare() {
+ if (INTERCEPTOR == null) {
+ ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
+
+ if (loader != null) {
+ IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
+ if (logger != null) {
+ LOGGER = logger;
+
+ INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
+ }
+ } else {
+ LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
+ }
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/StaticMethodInterTemplate.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/StaticMethodInterTemplate.java
new file mode 100644
index 00000000..df1abaf2
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/StaticMethodInterTemplate.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bootstrap.template;
+
+import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.StaticMethodsAroundInterceptor;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.SuperCall;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.Callable;
+
+public class StaticMethodInterTemplate {
+
+ /**
+ * This field is never set in the template, but has value in the runtime.
+ */
+ private static String TARGET_INTERCEPTOR;
+
+ private static StaticMethodsAroundInterceptor INTERCEPTOR;
+ private static IBootstrapLog LOGGER;
+
+ /**
+ * Intercept the target static method.
+ *
+ * @param clazz target class
+ * @param allArguments all method arguments
+ * @param method method description.
+ * @param zuper the origin call ref.
+ * @return the return value of target static method.
+ * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
+ * bug, if anything triggers this condition ).
+ */
+ @RuntimeType
+ public static Object intercept(@Origin Class> clazz, @AllArguments Object[] allArguments, @Origin Method method,
+ @SuperCall Callable> zuper) throws Throwable {
+ prepare();
+
+ MethodInterceptResult result = new MethodInterceptResult();
+ try {
+ if (INTERCEPTOR != null) {
+ INTERCEPTOR.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), result);
+ }
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName());
+ }
+
+ Object ret = null;
+ try {
+ if (!result.isContinue()) {
+ ret = result._ret();
+ } else {
+ ret = zuper.call();
+ }
+ } catch (Throwable t) {
+ try {
+ if (INTERCEPTOR != null) {
+ INTERCEPTOR.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t);
+ }
+ } catch (Throwable t2) {
+ LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage());
+ }
+ throw t;
+ } finally {
+ try {
+ if (INTERCEPTOR != null) {
+ ret = INTERCEPTOR.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret);
+ }
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage());
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Prepare the context. Link to the agent core in AppClassLoader.
+ */
+ private static void prepare() {
+ if (INTERCEPTOR == null) {
+ ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
+
+ if (loader != null) {
+ IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
+ if (logger != null) {
+ LOGGER = logger;
+
+ INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
+ }
+ } else {
+ LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
+ }
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/StaticMethodInterWithOverrideArgsTemplate.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/StaticMethodInterWithOverrideArgsTemplate.java
new file mode 100644
index 00000000..a3b0b92f
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/StaticMethodInterWithOverrideArgsTemplate.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bootstrap.template;
+
+import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.OverrideCallable;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.StaticMethodsAroundInterceptor;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Morph;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+
+import java.lang.reflect.Method;
+
+public class StaticMethodInterWithOverrideArgsTemplate {
+
+ /**
+ * This field is never set in the template, but has value in the runtime.
+ */
+ private static String TARGET_INTERCEPTOR;
+
+ private static StaticMethodsAroundInterceptor INTERCEPTOR;
+ private static IBootstrapLog LOGGER;
+
+ /**
+ * Intercept the target static method.
+ *
+ * @param clazz target class
+ * @param allArguments all method arguments
+ * @param method method description.
+ * @param zuper the origin call ref.
+ * @return the return value of target static method.
+ * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
+ * bug, if anything triggers this condition ).
+ */
+ @RuntimeType
+ public static Object intercept(@Origin Class> clazz, @AllArguments Object[] allArguments, @Origin Method method,
+ @Morph OverrideCallable zuper) throws Throwable {
+ prepare();
+
+ MethodInterceptResult result = new MethodInterceptResult();
+ try {
+ if (INTERCEPTOR != null) {
+ INTERCEPTOR.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), result);
+ }
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName());
+ }
+
+ Object ret = null;
+ try {
+ if (!result.isContinue()) {
+ ret = result._ret();
+ } else {
+ ret = zuper.call(allArguments);
+ }
+ } catch (Throwable t) {
+ try {
+ if (INTERCEPTOR != null) {
+ INTERCEPTOR.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t);
+ }
+ } catch (Throwable t2) {
+ LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage());
+ }
+ throw t;
+ } finally {
+ try {
+ if (INTERCEPTOR != null) {
+ ret = INTERCEPTOR.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret);
+ }
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage());
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Prepare the context. Link to the agent core in AppClassLoader.
+ */
+ private static void prepare() {
+ if (INTERCEPTOR == null) {
+ ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
+
+ if (loader != null) {
+ IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
+ if (logger != null) {
+ LOGGER = logger;
+
+ INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
+ }
+ } else {
+ LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
+ }
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/v2/InstanceMethodInterV2Template.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/v2/InstanceMethodInterV2Template.java
new file mode 100644
index 00000000..0bc6205e
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/v2/InstanceMethodInterV2Template.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bootstrap.template.v2;
+
+import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.SuperCall;
+import net.bytebuddy.implementation.bind.annotation.This;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.Callable;
+
+/**
+ * This class wouldn't be loaded in real env. This is a class template for dynamic class generation.
+ */
+public class InstanceMethodInterV2Template {
+
+ /**
+ * This field is never set in the template, but has value in the runtime.
+ */
+ private static String TARGET_INTERCEPTOR;
+
+ private static InstanceMethodsAroundInterceptorV2 INTERCEPTOR;
+ private static IBootstrapLog LOGGER;
+
+ /**
+ * Intercept the target instance method.
+ *
+ * @param obj target class instance.
+ * @param allArguments all method arguments
+ * @param method method description.
+ * @param zuper the origin call ref.
+ * @return the return value of target instance method.
+ * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
+ * bug, if anything triggers this condition ).
+ */
+ @RuntimeType
+ public static Object intercept(@This Object obj, @AllArguments Object[] allArguments, @SuperCall Callable> zuper,
+ @Origin Method method) throws Throwable {
+ EnhancedInstance targetObject = (EnhancedInstance) obj;
+
+ prepare();
+
+ MethodInvocationContext context = new MethodInvocationContext();
+ try {
+ if (INTERCEPTOR != null) {
+ INTERCEPTOR.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), context);
+ }
+ } catch (Throwable t) {
+ if (LOGGER != null) {
+ LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());
+ }
+ }
+
+ Object ret = null;
+ try {
+ if (!context.isContinue()) {
+ ret = context._ret();
+ } else {
+ ret = zuper.call();
+ }
+ } catch (Throwable t) {
+ try {
+ if (INTERCEPTOR != null) {
+ INTERCEPTOR.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t, context);
+ }
+ } catch (Throwable t2) {
+ if (LOGGER != null) {
+ LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
+ }
+ }
+ throw t;
+ } finally {
+ try {
+ if (INTERCEPTOR != null) {
+ ret = INTERCEPTOR.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret, context);
+ }
+ } catch (Throwable t) {
+ if (LOGGER != null) {
+ LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Prepare the context. Link to the agent core in AppClassLoader.
+ */
+ private static void prepare() {
+ if (INTERCEPTOR == null) {
+ ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
+
+ if (loader != null) {
+ IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
+ if (logger != null) {
+ LOGGER = logger;
+
+ INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
+ }
+ } else {
+ LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
+ }
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/v2/InstanceMethodInterV2WithOverrideArgsTemplate.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/v2/InstanceMethodInterV2WithOverrideArgsTemplate.java
new file mode 100644
index 00000000..6e15b671
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/v2/InstanceMethodInterV2WithOverrideArgsTemplate.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bootstrap.template.v2;
+
+import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.OverrideCallable;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.InstanceMethodsAroundInterceptorV2;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Morph;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.This;
+
+import java.lang.reflect.Method;
+
+/**
+ * This class wouldn't be loaded in real env. This is a class template for dynamic class generation.
+ */
+public class InstanceMethodInterV2WithOverrideArgsTemplate {
+
+ /**
+ * This field is never set in the template, but has value in the runtime.
+ */
+ private static String TARGET_INTERCEPTOR;
+
+ private static InstanceMethodsAroundInterceptorV2 INTERCEPTOR;
+ private static IBootstrapLog LOGGER;
+
+ /**
+ * Intercept the target instance method.
+ *
+ * @param obj target class instance.
+ * @param allArguments all method arguments
+ * @param method method description.
+ * @param zuper the origin call ref.
+ * @return the return value of target instance method.
+ * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
+ * bug, if anything triggers this condition ).
+ */
+ @RuntimeType
+ public static Object intercept(@This Object obj, @AllArguments Object[] allArguments, @Morph OverrideCallable zuper,
+ @Origin Method method) throws Throwable {
+ EnhancedInstance targetObject = (EnhancedInstance) obj;
+
+ prepare();
+
+ MethodInvocationContext context = new MethodInvocationContext();
+ try {
+ if (INTERCEPTOR != null) {
+ INTERCEPTOR.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), context);
+ }
+ } catch (Throwable t) {
+ if (LOGGER != null) {
+ LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());
+ }
+ }
+
+ Object ret = null;
+ try {
+ if (!context.isContinue()) {
+ ret = context._ret();
+ } else {
+ ret = zuper.call(allArguments);
+ }
+ } catch (Throwable t) {
+ try {
+ if (INTERCEPTOR != null) {
+ INTERCEPTOR.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t, context);
+ }
+ } catch (Throwable t2) {
+ if (LOGGER != null) {
+ LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
+ }
+ }
+ throw t;
+ } finally {
+ try {
+ if (INTERCEPTOR != null) {
+ ret = INTERCEPTOR.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret, context);
+ }
+ } catch (Throwable t) {
+ if (LOGGER != null) {
+ LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Prepare the context. Link to the agent core in AppClassLoader.
+ */
+ private static void prepare() {
+ if (INTERCEPTOR == null) {
+ ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
+
+ if (loader != null) {
+ IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
+ if (logger != null) {
+ LOGGER = logger;
+
+ INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
+ }
+ } else {
+ LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
+ }
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/v2/StaticMethodInterV2Template.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/v2/StaticMethodInterV2Template.java
new file mode 100644
index 00000000..7693c597
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/v2/StaticMethodInterV2Template.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bootstrap.template.v2;
+
+import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.StaticMethodsAroundInterceptorV2;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.SuperCall;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.Callable;
+
+/**
+ * This class wouldn't be loaded in real env. This is a class template for dynamic class generation.
+ */
+public class StaticMethodInterV2Template {
+
+ /**
+ * This field is never set in the template, but has value in the runtime.
+ */
+ private static String TARGET_INTERCEPTOR;
+
+ private static StaticMethodsAroundInterceptorV2 INTERCEPTOR;
+ private static IBootstrapLog LOGGER;
+
+ /**
+ * Intercept the target static method.
+ *
+ * @param clazz target class
+ * @param allArguments all method arguments
+ * @param method method description.
+ * @param zuper the origin call ref.
+ * @return the return value of target static method.
+ * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
+ * bug, if anything triggers this condition ).
+ */
+ @RuntimeType
+ public static Object intercept(@Origin Class> clazz, @AllArguments Object[] allArguments, @Origin Method method,
+ @SuperCall Callable> zuper) throws Throwable {
+ prepare();
+
+ MethodInvocationContext context = new MethodInvocationContext();
+ try {
+ if (INTERCEPTOR != null) {
+ INTERCEPTOR.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), context);
+ }
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName());
+ }
+
+ Object ret = null;
+ try {
+ if (!context.isContinue()) {
+ ret = context._ret();
+ } else {
+ ret = zuper.call();
+ }
+ } catch (Throwable t) {
+ try {
+ if (INTERCEPTOR != null) {
+ INTERCEPTOR.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t, context);
+ }
+ } catch (Throwable t2) {
+ LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage());
+ }
+ throw t;
+ } finally {
+ try {
+ if (INTERCEPTOR != null) {
+ ret = INTERCEPTOR.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret, context);
+ }
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage());
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Prepare the context. Link to the agent core in AppClassLoader.
+ */
+ private static void prepare() {
+ if (INTERCEPTOR == null) {
+ ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
+
+ if (loader != null) {
+ IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
+ if (logger != null) {
+ LOGGER = logger;
+
+ INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
+ }
+ } else {
+ LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
+ }
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/v2/StaticMethodInterV2WithOverrideArgsTemplate.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/v2/StaticMethodInterV2WithOverrideArgsTemplate.java
new file mode 100644
index 00000000..22da45bb
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bootstrap/template/v2/StaticMethodInterV2WithOverrideArgsTemplate.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bootstrap.template.v2;
+
+import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.BootstrapInterRuntimeAssist;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.OverrideCallable;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.MethodInvocationContext;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.v2.StaticMethodsAroundInterceptorV2;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Morph;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+
+import java.lang.reflect.Method;
+
+/**
+ * This class wouldn't be loaded in real env. This is a class template for dynamic class generation.
+ */
+public class StaticMethodInterV2WithOverrideArgsTemplate {
+
+ /**
+ * This field is never set in the template, but has value in the runtime.
+ */
+ private static String TARGET_INTERCEPTOR;
+
+ private static StaticMethodsAroundInterceptorV2 INTERCEPTOR;
+ private static IBootstrapLog LOGGER;
+
+ /**
+ * Intercept the target static method.
+ *
+ * @param clazz target class
+ * @param allArguments all method arguments
+ * @param method method description.
+ * @param zuper the origin call ref.
+ * @return the return value of target static method.
+ * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
+ * bug, if anything triggers this condition ).
+ */
+ @RuntimeType
+ public static Object intercept(@Origin Class> clazz, @AllArguments Object[] allArguments, @Origin Method method,
+ @Morph OverrideCallable zuper) throws Throwable {
+ prepare();
+
+ MethodInvocationContext context = new MethodInvocationContext();
+ try {
+ if (INTERCEPTOR != null) {
+ INTERCEPTOR.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), context);
+ }
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName());
+ }
+
+ Object ret = null;
+ try {
+ if (!context.isContinue()) {
+ ret = context._ret();
+ } else {
+ ret = zuper.call(allArguments);
+ }
+ } catch (Throwable t) {
+ try {
+ if (INTERCEPTOR != null) {
+ INTERCEPTOR.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t, context);
+ }
+ } catch (Throwable t2) {
+ LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage());
+ }
+ throw t;
+ } finally {
+ try {
+ if (INTERCEPTOR != null) {
+ ret = INTERCEPTOR.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret, context);
+ }
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage());
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Prepare the context. Link to the agent core in AppClassLoader.
+ */
+ private static void prepare() {
+ if (INTERCEPTOR == null) {
+ ClassLoader loader = BootstrapInterRuntimeAssist.getAgentClassLoader();
+
+ if (loader != null) {
+ IBootstrapLog logger = BootstrapInterRuntimeAssist.getLogger(loader, TARGET_INTERCEPTOR);
+ if (logger != null) {
+ LOGGER = logger;
+
+ INTERCEPTOR = BootstrapInterRuntimeAssist.createInterceptor(loader, TARGET_INTERCEPTOR, LOGGER);
+ }
+ } else {
+ LOGGER.error("Runtime ClassLoader not found when create {}." + TARGET_INTERCEPTOR);
+ }
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/AbstractJunction.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/AbstractJunction.java
new file mode 100644
index 00000000..4ae6ffcf
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/AbstractJunction.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bytebuddy;
+
+import net.bytebuddy.matcher.ElementMatcher;
+
+public abstract class AbstractJunction implements ElementMatcher.Junction {
+
+ @Override
+ public Junction and(ElementMatcher super U> other) {
+ return new Conjunction(this, other);
+ }
+
+ @Override
+ public Junction or(ElementMatcher super U> other) {
+ return new Disjunction(this, other);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/AnnotationTypeNameMatch.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/AnnotationTypeNameMatch.java
new file mode 100644
index 00000000..a1c1f69b
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/AnnotationTypeNameMatch.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bytebuddy;
+
+import net.bytebuddy.description.annotation.AnnotationDescription;
+import net.bytebuddy.description.annotation.AnnotationSource;
+import net.bytebuddy.matcher.CollectionItemMatcher;
+import net.bytebuddy.matcher.DeclaringAnnotationMatcher;
+import net.bytebuddy.matcher.ElementMatcher;
+
+/**
+ * Annotation Type match. Similar with {@link net.bytebuddy.matcher.ElementMatchers#isAnnotatedWith}, the only different
+ * between them is this match use {@link String} to declare the type, instead of {@link Class}. This can avoid the
+ * classloader risk.
+ *
+ * 2019-08-15
+ */
+public class AnnotationTypeNameMatch implements ElementMatcher {
+
+ /**
+ * the target annotation type
+ */
+ private String annotationTypeName;
+
+ /**
+ * declare the match target method with the certain type.
+ *
+ * @param annotationTypeName target annotation type
+ */
+ private AnnotationTypeNameMatch(String annotationTypeName) {
+ this.annotationTypeName = annotationTypeName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean matches(T target) {
+ return target.getAnnotationType().asErasure().getName().equals(annotationTypeName);
+ }
+
+ /**
+ * The static method to create {@link AnnotationTypeNameMatch} This is a delegate method to follow byte-buddy {@link
+ * ElementMatcher}'s code style.
+ *
+ * @param annotationTypeName target annotation type
+ * @param The type of the object that is being matched.
+ * @return new {@link AnnotationTypeNameMatch} instance.
+ */
+ public static Junction isAnnotatedWithType(
+ String annotationTypeName) {
+ final AnnotationTypeNameMatch matcher = new AnnotationTypeNameMatch(annotationTypeName);
+ return new DeclaringAnnotationMatcher(new CollectionItemMatcher(matcher));
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/ArgumentTypeNameMatch.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/ArgumentTypeNameMatch.java
new file mode 100644
index 00000000..250370f3
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/ArgumentTypeNameMatch.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bytebuddy;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.description.method.ParameterList;
+import net.bytebuddy.matcher.ElementMatcher;
+
+/**
+ * Argument Type match. Similar with {@link net.bytebuddy.matcher.ElementMatchers#takesArgument}, the only different
+ * between them is this match use {@link String} to declare the type, instead of {@link Class}. This can avoid the
+ * classloader risk.
+ *
+ */
+public class ArgumentTypeNameMatch implements ElementMatcher {
+
+ /**
+ * the index of arguments list.
+ */
+ private int index;
+
+ /**
+ * the target argument type at {@link ArgumentTypeNameMatch#index} of the arguments list.
+ */
+ private String argumentTypeName;
+
+ /**
+ * declare the match target method with the certain index and type.
+ *
+ * @param index the index of arguments list.
+ * @param argumentTypeName target argument type
+ */
+ private ArgumentTypeNameMatch(int index, String argumentTypeName) {
+ ArrayTypeNameChecker.check(argumentTypeName);
+
+ this.index = index;
+ this.argumentTypeName = argumentTypeName;
+ }
+
+ /**
+ * Match the target method.
+ *
+ * @param target target method description.
+ * @return true if matched. or false.
+ */
+ @Override
+ public boolean matches(MethodDescription target) {
+ ParameterList> parameters = target.getParameters();
+ if (parameters.size() > index) {
+ return parameters.get(index).getType().asErasure().getName().equals(argumentTypeName);
+ }
+
+ return false;
+ }
+
+ /**
+ * The static method to create {@link ArgumentTypeNameMatch} This is a delegate method to follow byte-buddy {@link
+ * ElementMatcher}'s code style.
+ *
+ * @param index the index of arguments list.
+ * @param argumentTypeName target argument type
+ * @return new {@link ArgumentTypeNameMatch} instance.
+ */
+ public static ElementMatcher takesArgumentWithType(int index, String argumentTypeName) {
+ return new ArgumentTypeNameMatch(index, argumentTypeName);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/ArrayTypeNameChecker.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/ArrayTypeNameChecker.java
new file mode 100644
index 00000000..907b9687
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/ArrayTypeNameChecker.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bytebuddy;
+
+public class ArrayTypeNameChecker {
+
+ public static void check(String typeName) {
+ if (typeName.endsWith("[]")) {
+ throw new IllegalArgumentException("Please use [Lxxx; to define an Array type, and ref to JVM Specification for details");
+ }
+ }
+}
\ No newline at end of file
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/CacheableTransformerDecorator.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/CacheableTransformerDecorator.java
new file mode 100644
index 00000000..8341772c
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/CacheableTransformerDecorator.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bytebuddy;
+
+import cn.hippo4j.agent.core.boot.AgentPackageNotFoundException;
+import cn.hippo4j.agent.core.boot.AgentPackagePath;
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.util.FileUtils;
+import cn.hippo4j.agent.core.util.IOUtils;
+import net.bytebuddy.agent.builder.AgentBuilder;
+import net.bytebuddy.agent.builder.ResettableClassFileTransformer;
+import net.bytebuddy.utility.RandomString;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Wrapper classFileTransformer of ByteBuddy, save the enhanced bytecode to memory cache or file cache,
+ * and automatically load the previously generated bytecode during the second retransform,
+ * to solve the problem that ByteBuddy generates auxiliary classes with different random names every time.
+ * Allow other javaagent to enhance those classes that enhanced by Hippo4j agent.
+ */
+public class CacheableTransformerDecorator implements AgentBuilder.TransformerDecorator {
+
+ private static final ILog LOGGER = LogManager.getLogger(CacheableTransformerDecorator.class);
+
+ private final ClassCacheMode cacheMode;
+ private ClassCacheResolver cacheResolver;
+
+ public CacheableTransformerDecorator(ClassCacheMode cacheMode) throws IOException {
+ this.cacheMode = cacheMode;
+ initClassCache();
+ }
+
+ private void initClassCache() throws IOException {
+ if (this.cacheMode.equals(ClassCacheMode.FILE)) {
+ String cacheDirBase = null;
+ try {
+ cacheDirBase = AgentPackagePath.getPath() + "/class-cache";
+ } catch (AgentPackageNotFoundException e) {
+ throw new IOException("Can't find the root path for creating /class-cache folder.");
+ }
+ File cacheDir = new File(cacheDirBase + "/class-cache-" + RandomString.make());
+ if (!cacheDir.exists()) {
+ cacheDir.mkdirs();
+ }
+ if (!cacheDir.exists()) {
+ throw new IOException("Create class cache dir failure");
+ }
+
+ cacheResolver = new FileCacheResolver(cacheDir);
+ } else {
+ cacheResolver = new MemoryCacheResolver();
+ }
+ }
+
+ @Override
+ public ResettableClassFileTransformer decorate(ResettableClassFileTransformer classFileTransformer) {
+ return new ResettableClassFileTransformer.WithDelegation(classFileTransformer) {
+
+ @Override
+ public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
+ // load from cache
+ byte[] classCache = cacheResolver.getClassCache(loader, className);
+ if (classCache != null) {
+ return classCache;
+ }
+
+ // transform class
+ classfileBuffer = classFileTransformer.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
+
+ // save to cache
+ if (classfileBuffer != null) {
+ cacheResolver.putClassCache(loader, className, classfileBuffer);
+ }
+
+ return classfileBuffer;
+ }
+ };
+ }
+
+ private static String getClassLoaderHash(ClassLoader loader) {
+ String classloader;
+ if (loader != null) {
+ classloader = Integer.toHexString(loader.hashCode());
+ } else {
+ // classloader is null for BootstrapClassLoader
+ classloader = "00000000";
+ }
+ return classloader;
+ }
+
+ interface ClassCacheResolver {
+
+ byte[] getClassCache(ClassLoader loader, String className);
+
+ void putClassCache(ClassLoader loader, String className, byte[] classfileBuffer);
+ }
+
+ static class MemoryCacheResolver implements ClassCacheResolver {
+
+ // classloaderHashcode@className -> class bytes
+ private Map classCacheMap = new ConcurrentHashMap();
+
+ @Override
+ public byte[] getClassCache(ClassLoader loader, String className) {
+ String cacheKey = getCacheKey(loader, className);
+ return classCacheMap.get(cacheKey);
+ }
+
+ @Override
+ public void putClassCache(ClassLoader loader, String className, byte[] classfileBuffer) {
+ String cacheKey = getCacheKey(loader, className);
+ classCacheMap.put(cacheKey, classfileBuffer);
+ }
+
+ private String getCacheKey(ClassLoader loader, String className) {
+ return getClassLoaderHash(loader) + "@" + className;
+ }
+ }
+
+ static class FileCacheResolver implements ClassCacheResolver {
+
+ private final File cacheDir;
+
+ FileCacheResolver(File cacheDir) {
+ this.cacheDir = cacheDir;
+
+ // clean cache dir on exit
+ FileUtils.deleteDirectoryOnExit(cacheDir);
+ }
+
+ @Override
+ public byte[] getClassCache(ClassLoader loader, String className) {
+ // load from cache
+ File cacheFile = getCacheFile(loader, className);
+ if (cacheFile.exists()) {
+ FileInputStream fileInputStream = null;
+ try {
+ fileInputStream = new FileInputStream(cacheFile);
+ return IOUtils.toByteArray(fileInputStream);
+ } catch (IOException e) {
+ LOGGER.error("load class bytes from cache file failure", e);
+ } finally {
+ IOUtils.closeQuietly(fileInputStream);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void putClassCache(ClassLoader loader, String className, byte[] classfileBuffer) {
+ File cacheFile = getCacheFile(loader, className);
+ cacheFile.getParentFile().mkdirs();
+ FileOutputStream output = null;
+ try {
+ output = new FileOutputStream(cacheFile);
+ IOUtils.copy(new ByteArrayInputStream(classfileBuffer), output);
+ } catch (IOException e) {
+ LOGGER.error("save class bytes to cache file failure", e);
+ } finally {
+ IOUtils.closeQuietly(output);
+ }
+ }
+
+ private File getCacheFile(ClassLoader loader, String className) {
+ String filename = getClassLoaderHash(loader) + "/" + className.replace('.', '/') + ".class";
+ return new File(cacheDir, filename);
+ }
+
+ }
+}
diff --git a/hippo4j-config/src/test/java/cn/hippo4j/config/toolkit/Md5ConfigUtilTest.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/ClassCacheMode.java
similarity index 86%
rename from hippo4j-config/src/test/java/cn/hippo4j/config/toolkit/Md5ConfigUtilTest.java
rename to agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/ClassCacheMode.java
index c351aa50..d5baf606 100644
--- a/hippo4j-config/src/test/java/cn/hippo4j/config/toolkit/Md5ConfigUtilTest.java
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/ClassCacheMode.java
@@ -15,10 +15,11 @@
* limitations under the License.
*/
-package cn.hippo4j.config.toolkit;
+package cn.hippo4j.agent.core.plugin.bytebuddy;
/**
- * Md5ConfigUtil Test
+ * ByteBuddy class cache mode
*/
-public class Md5ConfigUtilTest {
+public enum ClassCacheMode {
+ FILE, MEMORY
}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/ReturnTypeNameMatch.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/ReturnTypeNameMatch.java
new file mode 100644
index 00000000..38318020
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/ReturnTypeNameMatch.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.bytebuddy;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+/**
+ * Return Type match. Similar with {@link net.bytebuddy.matcher.ElementMatchers#returns}, the only different between
+ * them is this match use {@link String} to declare the type, instead of {@link Class}. This can avoid the classloader
+ * risk.
+ *
+ * 2019-08-15
+ */
+public class ReturnTypeNameMatch implements ElementMatcher {
+
+ /**
+ * the target return type
+ */
+ private String returnTypeName;
+
+ /**
+ * declare the match target method with the certain type.
+ *
+ * @param returnTypeName target return type
+ */
+ private ReturnTypeNameMatch(String returnTypeName) {
+ ArrayTypeNameChecker.check(returnTypeName);
+ this.returnTypeName = returnTypeName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean matches(MethodDescription target) {
+ return target.getReturnType().asErasure().getName().equals(returnTypeName);
+ }
+
+ /**
+ * The static method to create {@link ReturnTypeNameMatch} This is a delegate method to follow byte-buddy {@link
+ * ElementMatcher}'s code style.
+ *
+ * @param returnTypeName target return type
+ * @return new {@link ReturnTypeNameMatch} instance.
+ */
+ public static ElementMatcher returnsWithType(String returnTypeName) {
+ return new ReturnTypeNameMatch(returnTypeName);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/exception/IllegalPluginDefineException.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/exception/IllegalPluginDefineException.java
new file mode 100644
index 00000000..a23d7240
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/exception/IllegalPluginDefineException.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.exception;
+
+/**
+ * Thrown to indicate that a illegal format plugin definition has been defined in hippo4j-plugin.define.
+ */
+public class IllegalPluginDefineException extends Exception {
+
+ public IllegalPluginDefineException(String define) {
+ super("Illegal plugin define : " + define);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/ConstructorInterceptPoint.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/ConstructorInterceptPoint.java
new file mode 100644
index 00000000..330aa7d0
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/ConstructorInterceptPoint.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+/**
+ * One of the three "Intercept Point". "Intercept Point" is a definition about where and how intercept happens. In this
+ * "Intercept Point", the definition targets class's constructors, and the interceptor.
+ *
+ * ref to two others: {@link StaticMethodsInterceptPoint} and {@link InstanceMethodsInterceptPoint}
+ *
+ */
+public interface ConstructorInterceptPoint {
+
+ /**
+ * Constructor matcher
+ *
+ * @return matcher instance.
+ */
+ ElementMatcher getConstructorMatcher();
+
+ /**
+ * @return represents a class name, the class instance must be a instance of {@link
+ * cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor}
+ */
+ String getConstructorInterceptor();
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/DeclaredInstanceMethodsInterceptPoint.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/DeclaredInstanceMethodsInterceptPoint.java
new file mode 100644
index 00000000..5ff4368a
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/DeclaredInstanceMethodsInterceptPoint.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor;
+
+/**
+ * this interface for those who only want to enhance declared method in case of some unexpected issue, such as spring
+ * controller
+ */
+public interface DeclaredInstanceMethodsInterceptPoint extends InstanceMethodsInterceptPoint {
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/EnhanceException.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/EnhanceException.java
new file mode 100644
index 00000000..605bd8f5
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/EnhanceException.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor;
+
+import cn.hippo4j.agent.core.plugin.PluginException;
+
+public class EnhanceException extends PluginException {
+
+ private static final long serialVersionUID = -2234782755784217255L;
+
+ public EnhanceException(String message) {
+ super(message);
+ }
+
+ public EnhanceException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/InstanceMethodsInterceptPoint.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/InstanceMethodsInterceptPoint.java
new file mode 100644
index 00000000..396f471c
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/InstanceMethodsInterceptPoint.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+/**
+ * One of the three "Intercept Point". "Intercept Point" is a definition about where and how intercept happens. In this
+ * "Intercept Point", the definition targets class's instance methods, and the interceptor.
+ *
+ * ref to two others: {@link ConstructorInterceptPoint} and {@link StaticMethodsInterceptPoint}
+ *
+ */
+public interface InstanceMethodsInterceptPoint {
+
+ /**
+ * class instance methods matcher.
+ *
+ * @return methods matcher
+ */
+ ElementMatcher getMethodsMatcher();
+
+ /**
+ * @return represents a class name, the class instance must instanceof InstanceMethodsAroundInterceptor.
+ */
+ String getMethodsInterceptor();
+
+ boolean isOverrideArgs();
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/StaticMethodsInterceptPoint.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/StaticMethodsInterceptPoint.java
new file mode 100644
index 00000000..73babfa5
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/StaticMethodsInterceptPoint.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+/**
+ * One of the three "Intercept Point". "Intercept Point" is a definition about where and how intercept happens. In this
+ * "Intercept Point", the definition targets class's static methods, and the interceptor.
+ *
+ * ref to two others: {@link ConstructorInterceptPoint} and {@link InstanceMethodsInterceptPoint}
+ *
+ */
+public interface StaticMethodsInterceptPoint {
+
+ /**
+ * static methods matcher.
+ *
+ * @return matcher instance.
+ */
+ ElementMatcher getMethodsMatcher();
+
+ /**
+ * @return represents a class name, the class instance must instanceof StaticMethodsAroundInterceptor.
+ */
+ String getMethodsInterceptor();
+
+ boolean isOverrideArgs();
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/BootstrapInterRuntimeAssist.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/BootstrapInterRuntimeAssist.java
new file mode 100644
index 00000000..a1c5d38a
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/BootstrapInterRuntimeAssist.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance;
+
+import cn.hippo4j.agent.core.plugin.bootstrap.IBootstrapLog;
+
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * This assist help all bootstrap class core interceptor.
+ */
+public class BootstrapInterRuntimeAssist {
+
+ private static final String AGENT_CLASSLOADER_DEFAULT = "cn.hippo4j.agent.core.plugin.loader.AgentClassLoader";
+ private static final String DEFAULT_AGENT_CLASSLOADER_INSTANCE = "DEFAULT_LOADER";
+ private static final String LOG_MANAGER_CLASS = "cn.hippo4j.agent.core.plugin.bootstrap.BootstrapPluginLogBridge";
+ private static final String LOG_MANAGER_GET_LOGGER_METHOD = "getLogger";
+ private static final PrintStream OUT = System.out;
+
+ public static ClassLoader getAgentClassLoader() {
+ try {
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ if (loader == null) {
+ return null;
+ }
+ Class> agentClassLoaderClass = Class.forName(AGENT_CLASSLOADER_DEFAULT, true, loader);
+ Field defaultLoaderField = agentClassLoaderClass.getDeclaredField(DEFAULT_AGENT_CLASSLOADER_INSTANCE);
+ defaultLoaderField.setAccessible(true);
+ ClassLoader defaultAgentClassLoader = (ClassLoader) defaultLoaderField.get(null);
+
+ return defaultAgentClassLoader;
+ } catch (Exception e) {
+ e.printStackTrace(OUT);
+ return null;
+ }
+ }
+
+ public static IBootstrapLog getLogger(ClassLoader defaultAgentClassLoader, String interceptor) {
+ try {
+ Class> logManagerClass = Class.forName(LOG_MANAGER_CLASS, true, defaultAgentClassLoader);
+ Method getLogger = logManagerClass.getMethod(LOG_MANAGER_GET_LOGGER_METHOD, String.class);
+ return (IBootstrapLog) getLogger.invoke(null, interceptor + "_internal");
+ } catch (Exception e) {
+ e.printStackTrace(OUT);
+ return null;
+ }
+ }
+
+ public static T createInterceptor(ClassLoader defaultAgentClassLoader, String className, IBootstrapLog log) {
+ try {
+ Class> interceptor = Class.forName(className, true, defaultAgentClassLoader);
+ return (T) interceptor.newInstance();
+ } catch (Exception e) {
+ log.error(e, "Interceptor[{}] not found", className);
+ }
+ return null;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/ClassEnhancePluginDefine.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/ClassEnhancePluginDefine.java
new file mode 100644
index 00000000..70dddbad
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/ClassEnhancePluginDefine.java
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.AbstractClassEnhancePluginDefine;
+import cn.hippo4j.agent.core.plugin.EnhanceContext;
+import cn.hippo4j.agent.core.plugin.PluginException;
+import cn.hippo4j.agent.core.plugin.bootstrap.BootstrapInstrumentBoost;
+import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.DeclaredInstanceMethodsInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.EnhanceException;
+import cn.hippo4j.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.v2.InstanceMethodsInterceptV2Point;
+import cn.hippo4j.agent.core.plugin.interceptor.v2.StaticMethodsInterceptV2Point;
+import cn.hippo4j.agent.core.util.StringUtil;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.dynamic.DynamicType;
+import net.bytebuddy.implementation.FieldAccessor;
+import net.bytebuddy.implementation.MethodDelegation;
+import net.bytebuddy.implementation.SuperMethodCall;
+import net.bytebuddy.implementation.bind.annotation.Morph;
+import net.bytebuddy.matcher.ElementMatcher;
+import net.bytebuddy.matcher.ElementMatchers;
+
+import static net.bytebuddy.jar.asm.Opcodes.ACC_PRIVATE;
+import static net.bytebuddy.jar.asm.Opcodes.ACC_VOLATILE;
+import static net.bytebuddy.matcher.ElementMatchers.isStatic;
+import static net.bytebuddy.matcher.ElementMatchers.not;
+
+/**
+ * This class controls all enhance operations, including enhance constructors, instance methods and static methods. All
+ * the enhances base on three types interceptor point: {@link ConstructorInterceptPoint}, {@link
+ * InstanceMethodsInterceptPoint} and {@link StaticMethodsInterceptPoint} If plugin is going to enhance constructors,
+ * instance methods, or both, {@link ClassEnhancePluginDefine} will add a field of {@link Object} type.
+ */
+public abstract class ClassEnhancePluginDefine extends AbstractClassEnhancePluginDefine {
+
+ private static final ILog LOGGER = LogManager.getLogger(ClassEnhancePluginDefine.class);
+
+ /**
+ * Enhance a class to intercept constructors and class instance methods.
+ *
+ * @param typeDescription target class description
+ * @param newClassBuilder byte-buddy's builder to manipulate class bytecode.
+ * @return new byte-buddy's builder for further manipulation.
+ */
+ @Override
+ protected DynamicType.Builder> enhanceInstance(TypeDescription typeDescription,
+ DynamicType.Builder> newClassBuilder, ClassLoader classLoader,
+ EnhanceContext context) throws PluginException {
+ ConstructorInterceptPoint[] constructorInterceptPoints = getConstructorsInterceptPoints();
+ InstanceMethodsInterceptPoint[] instanceMethodsInterceptPoints = getInstanceMethodsInterceptPoints();
+ String enhanceOriginClassName = typeDescription.getTypeName();
+ boolean existedConstructorInterceptPoint = false;
+ if (constructorInterceptPoints != null && constructorInterceptPoints.length > 0) {
+ existedConstructorInterceptPoint = true;
+ }
+ boolean existedMethodsInterceptPoints = false;
+ if (instanceMethodsInterceptPoints != null && instanceMethodsInterceptPoints.length > 0) {
+ existedMethodsInterceptPoints = true;
+ }
+
+ /**
+ * nothing need to be enhanced in class instance, maybe need enhance static methods.
+ */
+ if (!existedConstructorInterceptPoint && !existedMethodsInterceptPoints) {
+ return newClassBuilder;
+ }
+
+ /**
+ * Manipulate class source code.
+ *
+ * new class need:
+ * 1.Add field, name {@link #CONTEXT_ATTR_NAME}.
+ * 2.Add a field accessor for this field.
+ *
+ * And make sure the source codes manipulation only occurs once.
+ *
+ */
+ if (!typeDescription.isAssignableTo(EnhancedInstance.class)) {
+ if (!context.isObjectExtended()) {
+ newClassBuilder = newClassBuilder.defineField(
+ CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE)
+ .implement(EnhancedInstance.class)
+ .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME));
+ context.extendObjectCompleted();
+ }
+ }
+
+ /**
+ * 2. enhance constructors
+ */
+ if (existedConstructorInterceptPoint) {
+ for (ConstructorInterceptPoint constructorInterceptPoint : constructorInterceptPoints) {
+ if (isBootstrapInstrumentation()) {
+ newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher())
+ .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration()
+ .to(BootstrapInstrumentBoost
+ .forInternalDelegateClass(constructorInterceptPoint
+ .getConstructorInterceptor()))));
+ } else {
+ newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher())
+ .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration()
+ .to(new ConstructorInter(constructorInterceptPoint
+ .getConstructorInterceptor(), classLoader))));
+ }
+ }
+ }
+
+ /**
+ * 3. enhance instance methods
+ */
+ if (existedMethodsInterceptPoints) {
+ for (InstanceMethodsInterceptPoint instanceMethodsInterceptPoint : instanceMethodsInterceptPoints) {
+ String interceptor = instanceMethodsInterceptPoint.getMethodsInterceptor();
+ if (StringUtil.isEmpty(interceptor)) {
+ throw new EnhanceException("no InstanceMethodsAroundInterceptor define to enhance class " + enhanceOriginClassName);
+ }
+ ElementMatcher.Junction junction = not(isStatic()).and(instanceMethodsInterceptPoint.getMethodsMatcher());
+ if (instanceMethodsInterceptPoint instanceof DeclaredInstanceMethodsInterceptPoint) {
+ junction = junction.and(ElementMatchers.isDeclaredBy(typeDescription));
+ }
+ if (instanceMethodsInterceptPoint.isOverrideArgs()) {
+ if (isBootstrapInstrumentation()) {
+ newClassBuilder = newClassBuilder.method(junction)
+ .intercept(MethodDelegation.withDefaultConfiguration()
+ .withBinders(Morph.Binder.install(OverrideCallable.class))
+ .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
+ } else {
+ newClassBuilder = newClassBuilder.method(junction)
+ .intercept(MethodDelegation.withDefaultConfiguration()
+ .withBinders(Morph.Binder.install(OverrideCallable.class))
+ .to(new InstMethodsInterWithOverrideArgs(interceptor, classLoader)));
+ }
+ } else {
+ if (isBootstrapInstrumentation()) {
+ newClassBuilder = newClassBuilder.method(junction)
+ .intercept(MethodDelegation.withDefaultConfiguration()
+ .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
+ } else {
+ newClassBuilder = newClassBuilder.method(junction)
+ .intercept(MethodDelegation.withDefaultConfiguration()
+ .to(new InstMethodsInter(interceptor, classLoader)));
+ }
+ }
+ }
+ }
+
+ return newClassBuilder;
+ }
+
+ /**
+ * Enhance a class to intercept class static methods.
+ *
+ * @param typeDescription target class description
+ * @param newClassBuilder byte-buddy's builder to manipulate class bytecode.
+ * @return new byte-buddy's builder for further manipulation.
+ */
+ @Override
+ protected DynamicType.Builder> enhanceClass(TypeDescription typeDescription, DynamicType.Builder> newClassBuilder,
+ ClassLoader classLoader) throws PluginException {
+ StaticMethodsInterceptPoint[] staticMethodsInterceptPoints = getStaticMethodsInterceptPoints();
+ String enhanceOriginClassName = typeDescription.getTypeName();
+ if (staticMethodsInterceptPoints == null || staticMethodsInterceptPoints.length == 0) {
+ return newClassBuilder;
+ }
+
+ for (StaticMethodsInterceptPoint staticMethodsInterceptPoint : staticMethodsInterceptPoints) {
+ String interceptor = staticMethodsInterceptPoint.getMethodsInterceptor();
+ if (StringUtil.isEmpty(interceptor)) {
+ throw new EnhanceException("no StaticMethodsAroundInterceptor define to enhance class " + enhanceOriginClassName);
+ }
+
+ if (staticMethodsInterceptPoint.isOverrideArgs()) {
+ if (isBootstrapInstrumentation()) {
+ newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
+ .intercept(MethodDelegation.withDefaultConfiguration()
+ .withBinders(Morph.Binder.install(OverrideCallable.class))
+ .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
+ } else {
+ newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
+ .intercept(MethodDelegation.withDefaultConfiguration()
+ .withBinders(Morph.Binder.install(OverrideCallable.class))
+ .to(new StaticMethodsInterWithOverrideArgs(interceptor)));
+ }
+ } else {
+ if (isBootstrapInstrumentation()) {
+ newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
+ .intercept(MethodDelegation.withDefaultConfiguration()
+ .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
+ } else {
+ newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
+ .intercept(MethodDelegation.withDefaultConfiguration()
+ .to(new StaticMethodsInter(interceptor)));
+ }
+ }
+
+ }
+
+ return newClassBuilder;
+ }
+
+ /**
+ * @return null, means enhance no v2 instance methods.
+ */
+ @Override
+ public InstanceMethodsInterceptV2Point[] getInstanceMethodsInterceptV2Points() {
+ return null;
+ }
+
+ /**
+ * @return null, means enhance no v2 static methods.
+ */
+ @Override
+ public StaticMethodsInterceptV2Point[] getStaticMethodsInterceptV2Points() {
+ return null;
+ }
+
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/ClassInstanceMethodsEnhancePluginDefine.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/ClassInstanceMethodsEnhancePluginDefine.java
new file mode 100644
index 00000000..f8b5c8c3
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/ClassInstanceMethodsEnhancePluginDefine.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance;
+
+import cn.hippo4j.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
+
+/**
+ * Plugins, which only need enhance class instance methods. Actually, inherit from {@link
+ * ClassInstanceMethodsEnhancePluginDefine} has no differences with inherit from {@link ClassEnhancePluginDefine}. Just
+ * override {@link ClassEnhancePluginDefine#getStaticMethodsInterceptPoints}, and return NULL, which means nothing to
+ * enhance.
+ */
+public abstract class ClassInstanceMethodsEnhancePluginDefine extends ClassEnhancePluginDefine {
+
+ /**
+ * @return null, means enhance no static methods.
+ */
+ @Override
+ public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
+ return null;
+ }
+
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/ClassStaticMethodsEnhancePluginDefine.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/ClassStaticMethodsEnhancePluginDefine.java
new file mode 100644
index 00000000..c5b04513
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/ClassStaticMethodsEnhancePluginDefine.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance;
+
+import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+
+/**
+ * Plugins, which only need enhance class static methods. Actually, inherit from {@link
+ * ClassStaticMethodsEnhancePluginDefine} has no differences with inherit from {@link ClassEnhancePluginDefine}. Just
+ * override {@link ClassEnhancePluginDefine#getConstructorsInterceptPoints} and {@link
+ * ClassEnhancePluginDefine#getInstanceMethodsInterceptPoints}, and return NULL, which means nothing to enhance.
+ */
+public abstract class ClassStaticMethodsEnhancePluginDefine extends ClassEnhancePluginDefine {
+
+ /**
+ * @return null, means enhance no constructors.
+ */
+ @Override
+ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return null;
+ }
+
+ /**
+ * @return null, means enhance no instance methods.
+ */
+ @Override
+ public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return null;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/ConstructorInter.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/ConstructorInter.java
new file mode 100644
index 00000000..d5b24c1d
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/ConstructorInter.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.PluginException;
+import cn.hippo4j.agent.core.plugin.loader.InterceptorInstanceLoader;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.This;
+
+/**
+ * The actual byte-buddy's interceptor to intercept constructor methods. In this class, it provides a bridge between
+ * byte-buddy and sky-walking plugin.
+ */
+public class ConstructorInter {
+
+ private static final ILog LOGGER = LogManager.getLogger(ConstructorInter.class);
+
+ /**
+ * An {@link InstanceConstructorInterceptor} This name should only stay in {@link String}, the real {@link Class}
+ * type will trigger classloader failure. If you want to know more, please check on books about Classloader or
+ * Classloader appointment mechanism.
+ */
+ private InstanceConstructorInterceptor interceptor;
+
+ /**
+ * @param constructorInterceptorClassName class full name.
+ */
+ public ConstructorInter(String constructorInterceptorClassName, ClassLoader classLoader) throws PluginException {
+ try {
+ interceptor = InterceptorInstanceLoader.load(constructorInterceptorClassName, classLoader);
+ } catch (Throwable t) {
+ throw new PluginException("Can't create InstanceConstructorInterceptorV2.", t);
+ }
+ }
+
+ /**
+ * Intercept the target constructor.
+ *
+ * @param obj target class instance.
+ * @param allArguments all constructor arguments
+ */
+ @RuntimeType
+ public void intercept(@This Object obj, @AllArguments Object[] allArguments) {
+ try {
+ EnhancedInstance targetObject = (EnhancedInstance) obj;
+
+ interceptor.onConstruct(targetObject, allArguments);
+ } catch (Throwable t) {
+ LOGGER.error("ConstructorInter failure.", t);
+ }
+
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/EnhancedInstance.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/EnhancedInstance.java
new file mode 100644
index 00000000..c6b7900b
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/EnhancedInstance.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance;
+
+public interface EnhancedInstance {
+
+ Object getHippo4jDynamicField();
+
+ void setHippo4jDynamicField(Object value);
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/InstMethodsInter.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/InstMethodsInter.java
new file mode 100644
index 00000000..489c54d3
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/InstMethodsInter.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.PluginException;
+import cn.hippo4j.agent.core.plugin.loader.InterceptorInstanceLoader;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.SuperCall;
+import net.bytebuddy.implementation.bind.annotation.This;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.Callable;
+
+/**
+ * The actual byte-buddy's interceptor to intercept class instance methods. In this class, it provides a bridge between
+ * byte-buddy and sky-walking plugin.
+ */
+public class InstMethodsInter {
+
+ private static final ILog LOGGER = LogManager.getLogger(InstMethodsInter.class);
+
+ /**
+ * An {@link InstanceMethodsAroundInterceptor} This name should only stay in {@link String}, the real {@link Class}
+ * type will trigger classloader failure. If you want to know more, please check on books about Classloader or
+ * Classloader appointment mechanism.
+ */
+ private InstanceMethodsAroundInterceptor interceptor;
+
+ /**
+ * @param instanceMethodsAroundInterceptorClassName class full name.
+ */
+ public InstMethodsInter(String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) {
+ try {
+ interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader);
+ } catch (Throwable t) {
+ throw new PluginException("Can't create InstanceMethodsAroundInterceptor.", t);
+ }
+ }
+
+ /**
+ * Intercept the target instance method.
+ *
+ * @param obj target class instance.
+ * @param allArguments all method arguments
+ * @param method method description.
+ * @param zuper the origin call ref.
+ * @return the return value of target instance method.
+ * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
+ * bug, if anything triggers this condition ).
+ */
+ @RuntimeType
+ public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @SuperCall Callable> zuper,
+ @Origin Method method) throws Throwable {
+ EnhancedInstance targetObject = (EnhancedInstance) obj;
+
+ MethodInterceptResult result = new MethodInterceptResult();
+ try {
+ interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());
+ }
+
+ Object ret = null;
+ try {
+ if (!result.isContinue()) {
+ ret = result._ret();
+ } else {
+ ret = zuper.call();
+ }
+ } catch (Throwable t) {
+ try {
+ interceptor.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t);
+ } catch (Throwable t2) {
+ LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
+ }
+ throw t;
+ } finally {
+ try {
+ ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
+ }
+ }
+ return ret;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/InstMethodsInterWithOverrideArgs.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/InstMethodsInterWithOverrideArgs.java
new file mode 100644
index 00000000..5febdb83
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/InstMethodsInterWithOverrideArgs.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.PluginException;
+import cn.hippo4j.agent.core.plugin.loader.InterceptorInstanceLoader;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Morph;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.This;
+
+import java.lang.reflect.Method;
+
+/**
+ * The actual byte-buddy's interceptor to intercept class instance methods. In this class, it provides a bridge between
+ * byte-buddy and sky-walking plugin.
+ */
+public class InstMethodsInterWithOverrideArgs {
+
+ private static final ILog LOGGER = LogManager.getLogger(InstMethodsInterWithOverrideArgs.class);
+
+ /**
+ * An {@link InstanceMethodsAroundInterceptor} This name should only stay in {@link String}, the real {@link Class}
+ * type will trigger classloader failure. If you want to know more, please check on books about Classloader or
+ * Classloader appointment mechanism.
+ */
+ private InstanceMethodsAroundInterceptor interceptor;
+
+ /**
+ * @param instanceMethodsAroundInterceptorClassName class full name.
+ */
+ public InstMethodsInterWithOverrideArgs(String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) {
+ try {
+ interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader);
+ } catch (Throwable t) {
+ throw new PluginException("Can't create InstanceMethodsAroundInterceptor.", t);
+ }
+ }
+
+ /**
+ * Intercept the target instance method.
+ *
+ * @param obj target class instance.
+ * @param allArguments all method arguments
+ * @param method method description.
+ * @param zuper the origin call ref.
+ * @return the return value of target instance method.
+ * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
+ * bug, if anything triggers this condition ).
+ */
+ @RuntimeType
+ public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @Origin Method method,
+ @Morph OverrideCallable zuper) throws Throwable {
+ EnhancedInstance targetObject = (EnhancedInstance) obj;
+
+ MethodInterceptResult result = new MethodInterceptResult();
+ try {
+ interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), result);
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());
+ }
+
+ Object ret = null;
+ try {
+ if (!result.isContinue()) {
+ ret = result._ret();
+ } else {
+ ret = zuper.call(allArguments);
+ }
+ } catch (Throwable t) {
+ try {
+ interceptor.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t);
+ } catch (Throwable t2) {
+ LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
+ }
+ throw t;
+ } finally {
+ try {
+ ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret);
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
+ }
+ }
+ return ret;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/InstanceConstructorInterceptor.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/InstanceConstructorInterceptor.java
new file mode 100644
index 00000000..7cc3472c
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/InstanceConstructorInterceptor.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance;
+
+/**
+ * The instance constructor's interceptor interface. Any plugin, which wants to intercept constructor, must implement
+ * this interface.
+ *
+ */
+public interface InstanceConstructorInterceptor {
+
+ /**
+ * Called after the origin constructor invocation.
+ */
+ void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable;
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/InstanceMethodsAroundInterceptor.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/InstanceMethodsAroundInterceptor.java
new file mode 100644
index 00000000..97e13ad8
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/InstanceMethodsAroundInterceptor.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance;
+
+import java.lang.reflect.Method;
+
+/**
+ * A interceptor, which intercept method's invocation. The target methods will be defined in {@link
+ * ClassEnhancePluginDefine}'s subclass, most likely in {@link ClassInstanceMethodsEnhancePluginDefine}
+ */
+public interface InstanceMethodsAroundInterceptor {
+
+ /**
+ * called before target method invocation.
+ *
+ * @param result change this result, if you want to truncate the method.
+ */
+ void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes,
+ MethodInterceptResult result) throws Throwable;
+
+ /**
+ * called after target method invocation. Even method's invocation triggers an exception.
+ *
+ * @param ret the method's original return value. May be null if the method triggers an exception.
+ * @return the method's actual return value.
+ */
+ Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes,
+ Object ret) throws Throwable;
+
+ /**
+ * called when occur exception.
+ *
+ * @param t the exception occur.
+ */
+ void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
+ Class>[] argumentsTypes, Throwable t);
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/MethodInterceptResult.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/MethodInterceptResult.java
new file mode 100644
index 00000000..e647f2bc
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/MethodInterceptResult.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance;
+
+import java.lang.reflect.Method;
+
+/**
+ * This is a method return value manipulator. When a interceptor's method, such as {@link
+ * InstanceMethodsAroundInterceptor#beforeMethod(EnhancedInstance, Method, Object[], Class[], MethodInterceptResult)}
+ * (org.apache.skywalking.apm.agent.core.plugin.interceptor.EnhancedClassInstanceContext, has this as a method argument,
+ * the interceptor can manipulate the method's return value.
The new value set to this object, by {@link
+ * MethodInterceptResult#defineReturnValue(Object)}, will override the origin return value.
+ */
+public class MethodInterceptResult {
+
+ private boolean isContinue = true;
+
+ private Object ret = null;
+
+ /**
+ * define the new return value.
+ *
+ * @param ret new return value.
+ */
+ public void defineReturnValue(Object ret) {
+ this.isContinue = false;
+ this.ret = ret;
+ }
+
+ /**
+ * @return true, will trigger method interceptor({@link InstMethodsInter} and {@link StaticMethodsInter}) to invoke
+ * the origin method. Otherwise, not.
+ */
+ public boolean isContinue() {
+ return isContinue;
+ }
+
+ /**
+ * @return the new return value.
+ */
+ public Object _ret() {
+ return ret;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/OverrideCallable.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/OverrideCallable.java
new file mode 100644
index 00000000..8a488dd4
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/OverrideCallable.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance;
+
+public interface OverrideCallable {
+
+ Object call(Object[] args);
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/StaticMethodsAroundInterceptor.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/StaticMethodsAroundInterceptor.java
new file mode 100644
index 00000000..d6398f19
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/StaticMethodsAroundInterceptor.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance;
+
+import java.lang.reflect.Method;
+
+/**
+ * The static method's interceptor interface. Any plugin, which wants to intercept static methods, must implement this
+ * interface.
+ */
+public interface StaticMethodsAroundInterceptor {
+
+ /**
+ * called before target method invocation.
+ *
+ * @param result change this result, if you want to truncate the method.
+ */
+ void beforeMethod(Class clazz, Method method, Object[] allArguments, Class>[] parameterTypes,
+ MethodInterceptResult result);
+
+ /**
+ * called after target method invocation. Even method's invocation triggers an exception.
+ *
+ * @param ret the method's original return value.
+ * @return the method's actual return value.
+ */
+ Object afterMethod(Class clazz, Method method, Object[] allArguments, Class>[] parameterTypes, Object ret);
+
+ /**
+ * called when occur exception.
+ *
+ * @param t the exception occur.
+ */
+ void handleMethodException(Class clazz, Method method, Object[] allArguments, Class>[] parameterTypes,
+ Throwable t);
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/StaticMethodsInter.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/StaticMethodsInter.java
new file mode 100644
index 00000000..3be9ccba
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/StaticMethodsInter.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.loader.InterceptorInstanceLoader;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.SuperCall;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.Callable;
+
+/**
+ * The actual byte-buddy's interceptor to intercept class static methods. In this class, it provides a bridge between
+ * byte-buddy and sky-walking plugin.
+ */
+public class StaticMethodsInter {
+
+ private static final ILog LOGGER = LogManager.getLogger(StaticMethodsInter.class);
+
+ /**
+ * A class full name, and instanceof {@link StaticMethodsAroundInterceptor} This name should only stay in {@link
+ * String}, the real {@link Class} type will trigger classloader failure. If you want to know more, please check on
+ * books about Classloader or Classloader appointment mechanism.
+ */
+ private String staticMethodsAroundInterceptorClassName;
+
+ /**
+ * Set the name of {@link StaticMethodsInter#staticMethodsAroundInterceptorClassName}
+ *
+ * @param staticMethodsAroundInterceptorClassName class full name.
+ */
+ public StaticMethodsInter(String staticMethodsAroundInterceptorClassName) {
+ this.staticMethodsAroundInterceptorClassName = staticMethodsAroundInterceptorClassName;
+ }
+
+ /**
+ * Intercept the target static method.
+ *
+ * @param clazz target class
+ * @param allArguments all method arguments
+ * @param method method description.
+ * @param zuper the origin call ref.
+ * @return the return value of target static method.
+ * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
+ * bug, if anything triggers this condition ).
+ */
+ @RuntimeType
+ public Object intercept(@Origin Class> clazz, @AllArguments Object[] allArguments, @Origin Method method,
+ @SuperCall Callable> zuper) throws Throwable {
+ StaticMethodsAroundInterceptor interceptor = InterceptorInstanceLoader.load(staticMethodsAroundInterceptorClassName, clazz
+ .getClassLoader());
+
+ MethodInterceptResult result = new MethodInterceptResult();
+ try {
+ interceptor.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), result);
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName());
+ }
+
+ Object ret = null;
+ try {
+ if (!result.isContinue()) {
+ ret = result._ret();
+ } else {
+ ret = zuper.call();
+ }
+ } catch (Throwable t) {
+ try {
+ interceptor.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t);
+ } catch (Throwable t2) {
+ LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage());
+ }
+ throw t;
+ } finally {
+ try {
+ ret = interceptor.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret);
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage());
+ }
+ }
+ return ret;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/StaticMethodsInterWithOverrideArgs.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/StaticMethodsInterWithOverrideArgs.java
new file mode 100644
index 00000000..99f83a17
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/StaticMethodsInterWithOverrideArgs.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.loader.InterceptorInstanceLoader;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Morph;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+
+import java.lang.reflect.Method;
+
+/**
+ * The actual byte-buddy's interceptor to intercept class static methods. In this class, it provides a bridge between
+ * byte-buddy and sky-walking plugin.
+ */
+public class StaticMethodsInterWithOverrideArgs {
+
+ private static final ILog LOGGER = LogManager.getLogger(StaticMethodsInterWithOverrideArgs.class);
+
+ /**
+ * A class full name, and instanceof {@link StaticMethodsAroundInterceptor} This name should only stay in {@link
+ * String}, the real {@link Class} type will trigger classloader failure. If you want to know more, please check on
+ * books about Classloader or Classloader appointment mechanism.
+ */
+ private String staticMethodsAroundInterceptorClassName;
+
+ /**
+ * Set the name of {@link StaticMethodsInterWithOverrideArgs#staticMethodsAroundInterceptorClassName}
+ *
+ * @param staticMethodsAroundInterceptorClassName class full name.
+ */
+ public StaticMethodsInterWithOverrideArgs(String staticMethodsAroundInterceptorClassName) {
+ this.staticMethodsAroundInterceptorClassName = staticMethodsAroundInterceptorClassName;
+ }
+
+ /**
+ * Intercept the target static method.
+ *
+ * @param clazz target class
+ * @param allArguments all method arguments
+ * @param method method description.
+ * @param zuper the origin call ref.
+ * @return the return value of target static method.
+ * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
+ * bug, if anything triggers this condition ).
+ */
+ @RuntimeType
+ public Object intercept(@Origin Class> clazz, @AllArguments Object[] allArguments, @Origin Method method,
+ @Morph OverrideCallable zuper) throws Throwable {
+ StaticMethodsAroundInterceptor interceptor = InterceptorInstanceLoader.load(staticMethodsAroundInterceptorClassName, clazz
+ .getClassLoader());
+
+ MethodInterceptResult result = new MethodInterceptResult();
+ try {
+ interceptor.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), result);
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName());
+ }
+
+ Object ret = null;
+ try {
+ if (!result.isContinue()) {
+ ret = result._ret();
+ } else {
+ ret = zuper.call(allArguments);
+ }
+ } catch (Throwable t) {
+ try {
+ interceptor.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t);
+ } catch (Throwable t2) {
+ LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage());
+ }
+ throw t;
+ } finally {
+ try {
+ ret = interceptor.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret);
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage());
+ }
+ }
+ return ret;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/ClassEnhancePluginDefineV2.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/ClassEnhancePluginDefineV2.java
new file mode 100644
index 00000000..75da0262
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/ClassEnhancePluginDefineV2.java
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance.v2;
+
+import cn.hippo4j.agent.core.plugin.AbstractClassEnhancePluginDefine;
+import cn.hippo4j.agent.core.plugin.EnhanceContext;
+import cn.hippo4j.agent.core.plugin.PluginException;
+import cn.hippo4j.agent.core.plugin.bootstrap.BootstrapInstrumentBoost;
+import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.EnhanceException;
+import cn.hippo4j.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.ConstructorInter;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.OverrideCallable;
+import cn.hippo4j.agent.core.plugin.interceptor.v2.ConstructorInterceptV2Point;
+import cn.hippo4j.agent.core.plugin.interceptor.v2.DeclaredInstanceMethodsInterceptV2Point;
+import cn.hippo4j.agent.core.plugin.interceptor.v2.InstanceMethodsInterceptV2Point;
+import cn.hippo4j.agent.core.plugin.interceptor.v2.StaticMethodsInterceptV2Point;
+import cn.hippo4j.agent.core.util.StringUtil;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.dynamic.DynamicType;
+import net.bytebuddy.implementation.FieldAccessor;
+import net.bytebuddy.implementation.MethodDelegation;
+import net.bytebuddy.implementation.SuperMethodCall;
+import net.bytebuddy.implementation.bind.annotation.Morph;
+import net.bytebuddy.matcher.ElementMatcher;
+import net.bytebuddy.matcher.ElementMatchers;
+
+import static net.bytebuddy.jar.asm.Opcodes.ACC_PRIVATE;
+import static net.bytebuddy.jar.asm.Opcodes.ACC_VOLATILE;
+import static net.bytebuddy.matcher.ElementMatchers.isStatic;
+import static net.bytebuddy.matcher.ElementMatchers.not;
+
+/**
+ * This class controls all enhance operations, including enhance constructors, instance methods and static methods. All
+ * the enhances base on three types interceptor point: {@link ConstructorInterceptV2Point}, {@link
+ * InstanceMethodsInterceptV2Point} and {@link StaticMethodsInterceptV2Point} If plugin is going to enhance constructors,
+ * instance methods, or both, {@link ClassEnhancePluginDefineV2} will add a field of {@link Object} type.
+ */
+public abstract class ClassEnhancePluginDefineV2 extends AbstractClassEnhancePluginDefine {
+
+ @Override
+ protected DynamicType.Builder> enhanceClass(TypeDescription typeDescription,
+ DynamicType.Builder> newClassBuilder,
+ ClassLoader classLoader) throws PluginException {
+ StaticMethodsInterceptV2Point[] staticMethodsInterceptV2Points = getStaticMethodsInterceptV2Points();
+ String enhanceOriginClassName = typeDescription.getTypeName();
+ if (staticMethodsInterceptV2Points == null || staticMethodsInterceptV2Points.length == 0) {
+ return newClassBuilder;
+ }
+
+ for (StaticMethodsInterceptV2Point staticMethodsInterceptV2Point : staticMethodsInterceptV2Points) {
+ String interceptor = staticMethodsInterceptV2Point.getMethodsInterceptorV2();
+ if (StringUtil.isEmpty(interceptor)) {
+ throw new EnhanceException(
+ "no StaticMethodsAroundInterceptorV2 define to enhance class " + enhanceOriginClassName);
+ }
+
+ if (staticMethodsInterceptV2Point.isOverrideArgs()) {
+ if (isBootstrapInstrumentation()) {
+ newClassBuilder = newClassBuilder.method(
+ isStatic().and(staticMethodsInterceptV2Point.getMethodsMatcher()))
+ .intercept(MethodDelegation.withDefaultConfiguration()
+ .withBinders(Morph.Binder.install(OverrideCallable.class))
+ .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
+ } else {
+ newClassBuilder = newClassBuilder.method(
+ isStatic().and(staticMethodsInterceptV2Point.getMethodsMatcher()))
+ .intercept(MethodDelegation.withDefaultConfiguration()
+ .withBinders(Morph.Binder.install(OverrideCallable.class))
+ .to(new StaticMethodsInterV2WithOverrideArgs(interceptor)));
+ }
+ } else {
+ if (isBootstrapInstrumentation()) {
+ newClassBuilder = newClassBuilder.method(
+ isStatic().and(staticMethodsInterceptV2Point.getMethodsMatcher()))
+ .intercept(MethodDelegation.withDefaultConfiguration()
+ .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
+ } else {
+ newClassBuilder = newClassBuilder.method(
+ isStatic().and(staticMethodsInterceptV2Point.getMethodsMatcher()))
+ .intercept(MethodDelegation.withDefaultConfiguration()
+ .to(new StaticMethodsInterV2(interceptor)));
+ }
+ }
+
+ }
+
+ return newClassBuilder;
+ }
+
+ @Override
+ protected DynamicType.Builder> enhanceInstance(TypeDescription typeDescription,
+ DynamicType.Builder> newClassBuilder, ClassLoader classLoader,
+ EnhanceContext context) throws PluginException {
+ ConstructorInterceptPoint[] constructorInterceptPoints = getConstructorsInterceptPoints();
+ InstanceMethodsInterceptV2Point[] instanceMethodsInterceptV2Points = getInstanceMethodsInterceptV2Points();
+ String enhanceOriginClassName = typeDescription.getTypeName();
+
+ boolean existedConstructorInterceptPoint = false;
+ if (constructorInterceptPoints != null && constructorInterceptPoints.length > 0) {
+ existedConstructorInterceptPoint = true;
+ }
+ boolean existedMethodsInterceptV2Points = false;
+ if (instanceMethodsInterceptV2Points != null && instanceMethodsInterceptV2Points.length > 0) {
+ existedMethodsInterceptV2Points = true;
+ }
+
+ if (!existedConstructorInterceptPoint && !existedMethodsInterceptV2Points) {
+ return newClassBuilder;
+ }
+
+ if (!typeDescription.isAssignableTo(EnhancedInstance.class)) {
+ if (!context.isObjectExtended()) {
+ newClassBuilder = newClassBuilder.defineField(
+ CONTEXT_ATTR_NAME, Object.class, ACC_PRIVATE | ACC_VOLATILE)
+ .implement(EnhancedInstance.class)
+ .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME));
+ context.extendObjectCompleted();
+ }
+ }
+
+ if (existedConstructorInterceptPoint) {
+ for (ConstructorInterceptPoint constructorInterceptPoint : constructorInterceptPoints) {
+ if (isBootstrapInstrumentation()) {
+ newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher())
+ .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration()
+ .to(BootstrapInstrumentBoost
+ .forInternalDelegateClass(constructorInterceptPoint
+ .getConstructorInterceptor()))));
+ } else {
+ newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher())
+ .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration()
+ .to(new ConstructorInter(constructorInterceptPoint
+ .getConstructorInterceptor(), classLoader))));
+ }
+ }
+ }
+
+ if (existedMethodsInterceptV2Points) {
+ for (InstanceMethodsInterceptV2Point instanceMethodsInterceptV2Point : instanceMethodsInterceptV2Points) {
+ String interceptor = instanceMethodsInterceptV2Point.getMethodsInterceptorV2();
+ if (StringUtil.isEmpty(interceptor)) {
+ throw new EnhanceException(
+ "no InstanceMethodsAroundInterceptorV2 define to enhance class " + enhanceOriginClassName);
+ }
+ ElementMatcher.Junction junction = not(isStatic()).and(
+ instanceMethodsInterceptV2Point.getMethodsMatcher());
+ if (instanceMethodsInterceptV2Point instanceof DeclaredInstanceMethodsInterceptV2Point) {
+ junction = junction.and(ElementMatchers.isDeclaredBy(typeDescription));
+ }
+ if (instanceMethodsInterceptV2Point.isOverrideArgs()) {
+ if (isBootstrapInstrumentation()) {
+ newClassBuilder = newClassBuilder.method(junction)
+ .intercept(MethodDelegation.withDefaultConfiguration()
+ .withBinders(Morph.Binder.install(OverrideCallable.class))
+ .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
+ } else {
+ newClassBuilder = newClassBuilder.method(junction)
+ .intercept(MethodDelegation.withDefaultConfiguration()
+ .withBinders(Morph.Binder.install(OverrideCallable.class))
+ .to(new InstMethodsInterV2WithOverrideArgs(interceptor, classLoader)));
+ }
+ } else {
+ if (isBootstrapInstrumentation()) {
+ newClassBuilder = newClassBuilder.method(junction)
+ .intercept(MethodDelegation.withDefaultConfiguration()
+ .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
+ } else {
+ newClassBuilder = newClassBuilder.method(junction)
+ .intercept(MethodDelegation.withDefaultConfiguration()
+ .to(new InstMethodsInterV2(interceptor, classLoader)));
+ }
+ }
+ }
+ }
+
+ return newClassBuilder;
+ }
+
+ @Override
+ public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return null;
+ }
+
+ @Override
+ public StaticMethodsInterceptPoint[] getStaticMethodsInterceptPoints() {
+ return null;
+ }
+}
diff --git a/hippo4j-common/src/main/java/cn/hippo4j/common/api/ThreadPoolInitRefresh.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/ClassInstanceMethodsEnhancePluginDefineV2.java
similarity index 52%
rename from hippo4j-common/src/main/java/cn/hippo4j/common/api/ThreadPoolInitRefresh.java
rename to agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/ClassInstanceMethodsEnhancePluginDefineV2.java
index e3b752ea..2f2c7300 100644
--- a/hippo4j-common/src/main/java/cn/hippo4j/common/api/ThreadPoolInitRefresh.java
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/ClassInstanceMethodsEnhancePluginDefineV2.java
@@ -15,37 +15,23 @@
* limitations under the License.
*/
-package cn.hippo4j.common.api;
+package cn.hippo4j.agent.core.plugin.interceptor.enhance.v2;
-import org.springframework.boot.ApplicationArguments;
-import org.springframework.boot.ApplicationRunner;
+import cn.hippo4j.agent.core.plugin.interceptor.v2.StaticMethodsInterceptV2Point;
/**
- * when init thread-pool dynamic refresh.
+ * Plugins, which only need enhance class instance methods. Actually, inherit from {@link
+ * ClassInstanceMethodsEnhancePluginDefineV2} has no differences with inherit from {@link ClassEnhancePluginDefineV2}.
+ * Just override {@link ClassEnhancePluginDefineV2#getStaticMethodsInterceptPoints}, and return NULL, which means nothing
+ * to enhance.
*/
-public interface ThreadPoolInitRefresh extends ApplicationRunner {
+public abstract class ClassInstanceMethodsEnhancePluginDefineV2 extends ClassEnhancePluginDefineV2 {
/**
- * Initializes the thread pool after system startup
- *
- * @param context new properties
+ * @return null, means enhance no v2 static methods.
*/
- void initRefresh(String context);
-
- /**
- * get from the Configuration center
- *
- * @return new properties
- * @throws Exception exception
- */
- String getProperties() throws Exception;
-
@Override
- default void run(ApplicationArguments args) throws Exception {
- String properties = getProperties();
- if (properties == null) {
- return;
- }
- initRefresh(properties);
+ public StaticMethodsInterceptV2Point[] getStaticMethodsInterceptV2Points() {
+ return null;
}
}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/ClassStaticMethodsEnhancePluginDefineV2.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/ClassStaticMethodsEnhancePluginDefineV2.java
new file mode 100644
index 00000000..294e70d8
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/ClassStaticMethodsEnhancePluginDefineV2.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance.v2;
+
+import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.v2.InstanceMethodsInterceptV2Point;
+
+/**
+ * Plugins, which only need enhance class static methods. Actually, inherit from {@link
+ * ClassStaticMethodsEnhancePluginDefineV2} has no differences with inherit from {@link ClassEnhancePluginDefineV2}. Just
+ * override {@link ClassEnhancePluginDefineV2#getConstructorsInterceptPoints} and {@link
+ * ClassEnhancePluginDefineV2#getInstanceMethodsInterceptV2Points}, and return NULL, which means nothing to enhance.
+ */
+public abstract class ClassStaticMethodsEnhancePluginDefineV2 extends ClassEnhancePluginDefineV2 {
+
+ /**
+ * @return null, means enhance no constructors.
+ */
+ @Override
+ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return null;
+ }
+
+ /**
+ * @return null, means enhance no v2 instance methods.
+ */
+ @Override
+ public InstanceMethodsInterceptV2Point[] getInstanceMethodsInterceptV2Points() {
+ return null;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/InstMethodsInterV2.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/InstMethodsInterV2.java
new file mode 100644
index 00000000..3967736a
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/InstMethodsInterV2.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance.v2;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.PluginException;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import cn.hippo4j.agent.core.plugin.loader.InterceptorInstanceLoader;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.SuperCall;
+import net.bytebuddy.implementation.bind.annotation.This;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.Callable;
+
+/**
+ * The actual byte-buddy's interceptor to intercept class instance methods. In this class, it provides a bridge between
+ * byte-buddy and sky-walking plugin.
+ */
+public class InstMethodsInterV2 {
+
+ private static final ILog LOGGER = LogManager.getLogger(InstMethodsInterV2.class);
+
+ private InstanceMethodsAroundInterceptorV2 interceptor;
+
+ public InstMethodsInterV2(String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) {
+ try {
+ interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader);
+ } catch (Throwable t) {
+ throw new PluginException("Can't create InstanceMethodsAroundInterceptor.", t);
+ }
+ }
+
+ @RuntimeType
+ public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @SuperCall Callable> zuper,
+ @Origin Method method) throws Throwable {
+ EnhancedInstance targetObject = (EnhancedInstance) obj;
+
+ MethodInvocationContext context = new MethodInvocationContext();
+ try {
+ interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), context);
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());
+ }
+
+ Object ret = null;
+ try {
+ if (!context.isContinue()) {
+ ret = context._ret();
+ } else {
+ ret = zuper.call();
+ }
+ } catch (Throwable t) {
+ try {
+ interceptor.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t, context);
+ } catch (Throwable t2) {
+ LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
+ }
+ throw t;
+ } finally {
+ try {
+ ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret, context);
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
+ }
+ }
+ return ret;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/InstMethodsInterV2WithOverrideArgs.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/InstMethodsInterV2WithOverrideArgs.java
new file mode 100644
index 00000000..f9cd51d6
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/InstMethodsInterV2WithOverrideArgs.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance.v2;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.PluginException;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.OverrideCallable;
+import cn.hippo4j.agent.core.plugin.loader.InterceptorInstanceLoader;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Morph;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.This;
+
+import java.lang.reflect.Method;
+
+/**
+ * The actual byte-buddy's interceptor to intercept class instance methods. In this class, it provides a bridge between
+ * byte-buddy and sky-walking plugin.
+ */
+public class InstMethodsInterV2WithOverrideArgs {
+
+ private static final ILog LOGGER = LogManager.getLogger(InstMethodsInterV2WithOverrideArgs.class);
+
+ /**
+ * An {@link InstanceMethodsAroundInterceptorV2} This name should only stay in {@link String}, the real {@link Class}
+ * type will trigger classloader failure. If you want to know more, please check on books about Classloader or
+ * Classloader appointment mechanism.
+ */
+ private InstanceMethodsAroundInterceptorV2 interceptor;
+
+ /**
+ * @param instanceMethodsAroundInterceptorClassName class full name.
+ */
+ public InstMethodsInterV2WithOverrideArgs(String instanceMethodsAroundInterceptorClassName, ClassLoader classLoader) {
+ try {
+ interceptor = InterceptorInstanceLoader.load(instanceMethodsAroundInterceptorClassName, classLoader);
+ } catch (Throwable t) {
+ throw new PluginException("Can't create InstanceMethodsAroundInterceptor.", t);
+ }
+ }
+
+ /**
+ * Intercept the target instance method.
+ *
+ * @param obj target class instance.
+ * @param allArguments all method arguments
+ * @param method method description.
+ * @param zuper the origin call ref.
+ * @return the return value of target instance method.
+ * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
+ * bug, if anything triggers this condition ).
+ */
+ @RuntimeType
+ public Object intercept(@This Object obj, @AllArguments Object[] allArguments, @Origin Method method,
+ @Morph OverrideCallable zuper) throws Throwable {
+ EnhancedInstance targetObject = (EnhancedInstance) obj;
+
+ MethodInvocationContext context = new MethodInvocationContext();
+ try {
+ interceptor.beforeMethod(targetObject, method, allArguments, method.getParameterTypes(), context);
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] before method[{}] intercept failure", obj.getClass(), method.getName());
+ }
+
+ Object ret = null;
+ try {
+ if (!context.isContinue()) {
+ ret = context._ret();
+ } else {
+ ret = zuper.call(allArguments);
+ }
+ } catch (Throwable t) {
+ try {
+ interceptor.handleMethodException(targetObject, method, allArguments, method.getParameterTypes(), t, context);
+ } catch (Throwable t2) {
+ LOGGER.error(t2, "class[{}] handle method[{}] exception failure", obj.getClass(), method.getName());
+ }
+ throw t;
+ } finally {
+ try {
+ ret = interceptor.afterMethod(targetObject, method, allArguments, method.getParameterTypes(), ret, context);
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] after method[{}] intercept failure", obj.getClass(), method.getName());
+ }
+ }
+ return ret;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/InstanceMethodsAroundInterceptorV2.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/InstanceMethodsAroundInterceptorV2.java
new file mode 100644
index 00000000..b732300f
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/InstanceMethodsAroundInterceptorV2.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance.v2;
+
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+
+import java.lang.reflect.Method;
+
+/**
+ * A v2 interceptor, which intercept method's invocation. The target methods will be defined in {@link
+ * ClassEnhancePluginDefineV2}'s subclass, most likely in {@link ClassInstanceMethodsEnhancePluginDefine}
+ */
+public interface InstanceMethodsAroundInterceptorV2 {
+
+ /**
+ * called before target method invocation.
+ *
+ * @param context the method invocation context including result context.
+ */
+ void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes,
+ MethodInvocationContext context) throws Throwable;
+
+ /**
+ * called after target method invocation. Even method's invocation triggers an exception.
+ *
+ * @param ret the method's original return value. May be null if the method triggers an exception.
+ * @return the method's actual return value.
+ */
+ Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes,
+ Object ret, MethodInvocationContext context) throws Throwable;
+
+ /**
+ * called when occur exception.
+ *
+ * @param t the exception occur.
+ */
+ void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
+ Class>[] argumentsTypes, Throwable t, MethodInvocationContext context);
+
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/MethodInvocationContext.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/MethodInvocationContext.java
new file mode 100644
index 00000000..a6647aa8
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/MethodInvocationContext.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance.v2;
+
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * MethodInvocationContext holds the reference to propagate it between beforeMethod and afterMethod/handleMethodException
+ */
+@Setter
+@Getter
+public class MethodInvocationContext extends MethodInterceptResult {
+
+ /**
+ * A pointer for the propagating context
+ */
+ private Object context;
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/StaticMethodsAroundInterceptorV2.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/StaticMethodsAroundInterceptorV2.java
new file mode 100644
index 00000000..55685f07
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/StaticMethodsAroundInterceptorV2.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance.v2;
+
+import java.lang.reflect.Method;
+
+/**
+ * The static method's interceptor v2 interface. Any plugin, which wants to intercept static methods, must implement this
+ * interface.
+ */
+public interface StaticMethodsAroundInterceptorV2 {
+
+ /**
+ * called before target method invocation.
+ *
+ * @param context the method invocation context including result context.
+ */
+ void beforeMethod(Class clazz, Method method, Object[] allArguments, Class>[] parameterTypes,
+ MethodInvocationContext context);
+
+ /**
+ * called after target method invocation. Even method's invocation triggers an exception.
+ *
+ * @param ret the method's original return value.
+ * @return the method's actual return value.
+ */
+ Object afterMethod(Class clazz, Method method, Object[] allArguments, Class>[] parameterTypes, Object ret,
+ MethodInvocationContext context);
+
+ /**
+ * called when occur exception.
+ *
+ * @param t the exception occur.
+ */
+ void handleMethodException(Class clazz, Method method, Object[] allArguments, Class>[] parameterTypes,
+ Throwable t, MethodInvocationContext context);
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/StaticMethodsInterV2.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/StaticMethodsInterV2.java
new file mode 100644
index 00000000..97785770
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/StaticMethodsInterV2.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance.v2;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.loader.InterceptorInstanceLoader;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.SuperCall;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.Callable;
+
+/**
+ * The actual byte-buddy's interceptor to intercept class instance methods. In this class, it provides a bridge between
+ * byte-buddy and sky-walking plugin.
+ */
+public class StaticMethodsInterV2 {
+
+ private static final ILog LOGGER = LogManager.getLogger(StaticMethodsInterV2.class);
+
+ /**
+ * A class full name, and instanceof {@link StaticMethodsAroundInterceptorV2} This name should only stay in {@link
+ * String}, the real {@link Class} type will trigger classloader failure. If you want to know more, please check on
+ * books about Classloader or Classloader appointment mechanism.
+ */
+ private String staticMethodsAroundInterceptorClassName;
+
+ /**
+ * Set the name of {@link StaticMethodsInterV2#staticMethodsAroundInterceptorClassName}
+ *
+ * @param staticMethodsAroundInterceptorClassName class full name.
+ */
+ public StaticMethodsInterV2(String staticMethodsAroundInterceptorClassName) {
+ this.staticMethodsAroundInterceptorClassName = staticMethodsAroundInterceptorClassName;
+ }
+
+ /**
+ * Intercept the target static method.
+ *
+ * @param clazz target class
+ * @param allArguments all method arguments
+ * @param method method description.
+ * @param zuper the origin call ref.
+ * @return the return value of target static method.
+ * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
+ * bug, if anything triggers this condition ).
+ */
+ @RuntimeType
+ public Object intercept(@Origin Class> clazz, @AllArguments Object[] allArguments, @Origin Method method,
+ @SuperCall Callable> zuper) throws Throwable {
+ StaticMethodsAroundInterceptorV2 interceptor = InterceptorInstanceLoader.load(staticMethodsAroundInterceptorClassName,
+ clazz.getClassLoader());
+
+ MethodInvocationContext context = new MethodInvocationContext();
+ try {
+ interceptor.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), context);
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName());
+ }
+
+ Object ret = null;
+ try {
+ if (!context.isContinue()) {
+ ret = context._ret();
+ } else {
+ ret = zuper.call();
+ }
+ } catch (Throwable t) {
+ try {
+ interceptor.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t, context);
+ } catch (Throwable t2) {
+ LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage());
+ }
+ throw t;
+ } finally {
+ try {
+ ret = interceptor.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret, context);
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage());
+ }
+ }
+ return ret;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/StaticMethodsInterV2WithOverrideArgs.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/StaticMethodsInterV2WithOverrideArgs.java
new file mode 100644
index 00000000..467af26a
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/enhance/v2/StaticMethodsInterV2WithOverrideArgs.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.enhance.v2;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.OverrideCallable;
+import cn.hippo4j.agent.core.plugin.loader.InterceptorInstanceLoader;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Morph;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+
+import java.lang.reflect.Method;
+
+/**
+ * The actual byte-buddy's interceptor to intercept class instance methods. In this class, it provides a bridge between
+ * byte-buddy and sky-walking plugin.
+ */
+public class StaticMethodsInterV2WithOverrideArgs {
+
+ private static final ILog LOGGER = LogManager.getLogger(StaticMethodsInterV2WithOverrideArgs.class);
+
+ /**
+ * A class full name, and instanceof {@link StaticMethodsAroundInterceptorV2} This name should only stay in {@link
+ * String}, the real {@link Class} type will trigger classloader failure. If you want to know more, please check on
+ * books about Classloader or Classloader appointment mechanism.
+ */
+ private String staticMethodsAroundInterceptorClassName;
+
+ /**
+ * Set the name of {@link StaticMethodsInterV2WithOverrideArgs#staticMethodsAroundInterceptorClassName}
+ *
+ * @param staticMethodsAroundInterceptorClassName class full name.
+ */
+ public StaticMethodsInterV2WithOverrideArgs(String staticMethodsAroundInterceptorClassName) {
+ this.staticMethodsAroundInterceptorClassName = staticMethodsAroundInterceptorClassName;
+ }
+
+ /**
+ * Intercept the target static method.
+ *
+ * @param clazz target class
+ * @param allArguments all method arguments
+ * @param method method description.
+ * @param zuper the origin call ref.
+ * @return the return value of target static method.
+ * @throws Exception only throw exception because of zuper.call() or unexpected exception in sky-walking ( This is a
+ * bug, if anything triggers this condition ).
+ */
+ @RuntimeType
+ public Object intercept(@Origin Class> clazz, @AllArguments Object[] allArguments, @Origin Method method,
+ @Morph OverrideCallable zuper) throws Throwable {
+ StaticMethodsAroundInterceptorV2 interceptor = InterceptorInstanceLoader.load(staticMethodsAroundInterceptorClassName,
+ clazz.getClassLoader());
+
+ MethodInvocationContext context = new MethodInvocationContext();
+ try {
+ interceptor.beforeMethod(clazz, method, allArguments, method.getParameterTypes(), context);
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] before static method[{}] intercept failure", clazz, method.getName());
+ }
+
+ Object ret = null;
+ try {
+ if (!context.isContinue()) {
+ ret = context._ret();
+ } else {
+ ret = zuper.call(allArguments);
+ }
+ } catch (Throwable t) {
+ try {
+ interceptor.handleMethodException(clazz, method, allArguments, method.getParameterTypes(), t, context);
+ } catch (Throwable t2) {
+ LOGGER.error(t2, "class[{}] handle static method[{}] exception failure", clazz, method.getName(), t2.getMessage());
+ }
+ throw t;
+ } finally {
+ try {
+ ret = interceptor.afterMethod(clazz, method, allArguments, method.getParameterTypes(), ret, context);
+ } catch (Throwable t) {
+ LOGGER.error(t, "class[{}] after static method[{}] intercept failure:{}", clazz, method.getName(), t.getMessage());
+ }
+ }
+ return ret;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/v2/ConstructorInterceptV2Point.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/v2/ConstructorInterceptV2Point.java
new file mode 100644
index 00000000..e073e7c1
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/v2/ConstructorInterceptV2Point.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.v2;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+public interface ConstructorInterceptV2Point {
+
+ /**
+ * Constructor matcher
+ *
+ * @return matcher instance.
+ */
+ ElementMatcher getConstructorMatcher();
+
+ /**
+ * @return represents a class name, the class instance must be a instance of {@link
+ * cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor}
+ */
+ String getConstructorInterceptorV2();
+
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/v2/DeclaredInstanceMethodsInterceptV2Point.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/v2/DeclaredInstanceMethodsInterceptV2Point.java
new file mode 100644
index 00000000..02754d87
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/v2/DeclaredInstanceMethodsInterceptV2Point.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.v2;
+
+/**
+ * this interface for those who only want to enhance declared method in case of some unexpected issue, such as spring
+ * controller
+ */
+public interface DeclaredInstanceMethodsInterceptV2Point extends InstanceMethodsInterceptV2Point {
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/v2/InstanceMethodsInterceptV2Point.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/v2/InstanceMethodsInterceptV2Point.java
new file mode 100644
index 00000000..6d4844cb
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/v2/InstanceMethodsInterceptV2Point.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.v2;
+
+import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+/**
+ * One of the three "Intercept Point". "Intercept Point" is a definition about where and how intercept happens. In this
+ * "Intercept Point", the definition targets class's instance methods, and the interceptor.
+ *
+ * ref to two others: {@link ConstructorInterceptPoint} and {@link StaticMethodsInterceptV2Point}
+ *
+ */
+public interface InstanceMethodsInterceptV2Point {
+
+ /**
+ * class instance methods matcher.
+ *
+ * @return methods matcher
+ */
+ ElementMatcher getMethodsMatcher();
+
+ /**
+ * @return represents a class name, the class instance must instanceof InstanceMethodsAroundInterceptorV2.
+ */
+ String getMethodsInterceptorV2();
+
+ boolean isOverrideArgs();
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/v2/StaticMethodsInterceptV2Point.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/v2/StaticMethodsInterceptV2Point.java
new file mode 100644
index 00000000..aa6e5d56
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/interceptor/v2/StaticMethodsInterceptV2Point.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.interceptor.v2;
+
+import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+/**
+ * One of the three "Intercept Point". "Intercept Point" is a definition about where and how intercept happens. In this
+ * "Intercept Point", the definition targets class's static methods, and the interceptor.
+ *
+ * ref to two others: {@link ConstructorInterceptPoint} and {@link InstanceMethodsInterceptV2Point}
+ *
+ */
+public interface StaticMethodsInterceptV2Point {
+
+ /**
+ * static methods matcher.
+ *
+ * @return matcher instance.
+ */
+ ElementMatcher getMethodsMatcher();
+
+ /**
+ * @return represents a class name, the class instance must instanceof StaticMethodsAroundInterceptorV2.
+ */
+ String getMethodsInterceptorV2();
+
+ boolean isOverrideArgs();
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/jdk9module/JDK9ModuleExporter.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/jdk9module/JDK9ModuleExporter.java
new file mode 100644
index 00000000..1ff3514e
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/jdk9module/JDK9ModuleExporter.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.jdk9module;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.ByteBuddyCoreClasses;
+import net.bytebuddy.agent.builder.AgentBuilder;
+
+import java.lang.instrument.Instrumentation;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Since JDK 9, module concept has been introduced. By supporting that, agent core needs to open the read edge
+ */
+public class JDK9ModuleExporter {
+
+ private static final ILog LOGGER = LogManager.getLogger(JDK9ModuleExporter.class);
+
+ private static final String[] HIGH_PRIORITY_CLASSES = {
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance",
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult",
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.OverrideCallable",
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.ConstructorInter",
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.InstMethodsInter",
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.InstMethodsInterWithOverrideArgs",
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.StaticMethodsInter",
+ "cn.hippo4j.agent.core.plugin.interceptor.enhance.StaticMethodsInterWithOverrideArgs",
+ };
+
+ /**
+ * Assures that all modules of the supplied types are read by the module of any instrumented type. JDK Module system
+ * was introduced since JDK9.
+ *
+ * The following codes work only JDK Module system exist.
+ */
+ public static AgentBuilder openReadEdge(Instrumentation instrumentation, AgentBuilder agentBuilder,
+ EdgeClasses classes) {
+ for (String className : classes.classes) {
+ try {
+ agentBuilder = agentBuilder.assureReadEdgeFromAndTo(instrumentation, Class.forName(className));
+ } catch (ClassNotFoundException e) {
+ throw new UnsupportedOperationException("Fail to open read edge for class " + className + " to public access in JDK9+", e);
+ }
+ }
+ for (String className : HIGH_PRIORITY_CLASSES) {
+ try {
+ agentBuilder = agentBuilder.assureReadEdgeFromAndTo(instrumentation, Class.forName(className));
+ } catch (ClassNotFoundException e) {
+ throw new UnsupportedOperationException("Fail to open read edge for class " + className + " to public access in JDK9+", e);
+ }
+ }
+
+ return agentBuilder;
+ }
+
+ public static class EdgeClasses {
+
+ private List classes = new ArrayList();
+
+ public EdgeClasses() {
+ for (String className : ByteBuddyCoreClasses.CLASSES) {
+ add(className);
+ }
+ }
+
+ public void add(String className) {
+ if (!classes.contains(className)) {
+ classes.add(className);
+ }
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/loader/AgentClassLoader.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/loader/AgentClassLoader.java
new file mode 100644
index 00000000..3fec67cc
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/loader/AgentClassLoader.java
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.loader;
+
+import cn.hippo4j.agent.core.boot.AgentPackageNotFoundException;
+import cn.hippo4j.agent.core.boot.AgentPackagePath;
+import cn.hippo4j.agent.core.boot.PluginConfig;
+import cn.hippo4j.agent.core.boot.SpringBootConfigNode;
+import cn.hippo4j.agent.core.boot.SpringBootConfigInitializer;
+import cn.hippo4j.agent.core.conf.Config;
+import cn.hippo4j.agent.core.conf.SnifferConfigInitializer;
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.PluginBootstrap;
+import lombok.RequiredArgsConstructor;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * The AgentClassLoader
represents a classloader, which is in charge of finding plugins and interceptors.
+ */
+public class AgentClassLoader extends ClassLoader {
+
+ static {
+ /*
+ * Try to solve the classloader dead lock. See https://github.com/apache/skywalking/pull/2016
+ */
+ registerAsParallelCapable();
+ }
+
+ private static final ILog LOGGER = LogManager.getLogger(AgentClassLoader.class);
+ /**
+ * The default class loader for the agent.
+ */
+ private static AgentClassLoader DEFAULT_LOADER;
+
+ private List classpath;
+ private List allJars;
+ private ReentrantLock jarScanLock = new ReentrantLock();
+
+ public static AgentClassLoader getDefault() {
+ return DEFAULT_LOADER;
+ }
+
+ /**
+ * Init the default class loader.
+ *
+ * @throws AgentPackageNotFoundException if agent package is not found.
+ */
+ public static void initDefaultLoader() throws AgentPackageNotFoundException {
+ if (DEFAULT_LOADER == null) {
+ synchronized (AgentClassLoader.class) {
+ if (DEFAULT_LOADER == null) {
+ DEFAULT_LOADER = new AgentClassLoader(PluginBootstrap.class.getClassLoader());
+ }
+ }
+ }
+ }
+
+ public AgentClassLoader(ClassLoader parent) throws AgentPackageNotFoundException {
+ super(parent);
+ File agentDictionary = AgentPackagePath.getPath();
+ classpath = new LinkedList<>();
+ Config.Plugin.MOUNT.forEach(mountFolder -> classpath.add(new File(agentDictionary, mountFolder)));
+ }
+
+ @Override
+ protected Class> findClass(String name) throws ClassNotFoundException {
+ List allJars = getAllJars();
+ String path = name.replace('.', '/').concat(".class");
+ for (Jar jar : allJars) {
+ JarEntry entry = jar.jarFile.getJarEntry(path);
+ if (entry == null) {
+ continue;
+ }
+ try {
+ URL classFileUrl = new URL("jar:file:" + jar.sourceFile.getAbsolutePath() + "!/" + path);
+ byte[] data;
+ try (
+ final BufferedInputStream is = new BufferedInputStream(
+ classFileUrl.openStream());
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ int ch;
+ while ((ch = is.read()) != -1) {
+ baos.write(ch);
+ }
+ data = baos.toByteArray();
+ }
+ return processLoadedClass(defineClass(name, data, 0, data.length));
+ } catch (IOException e) {
+ LOGGER.error(e, "find class fail.");
+ }
+ }
+ throw new ClassNotFoundException("Can't find " + name);
+ }
+
+ @Override
+ protected URL findResource(String name) {
+ List allJars = getAllJars();
+ for (Jar jar : allJars) {
+ JarEntry entry = jar.jarFile.getJarEntry(name);
+ if (entry != null) {
+ try {
+ return new URL("jar:file:" + jar.sourceFile.getAbsolutePath() + "!/" + name);
+ } catch (MalformedURLException ignored) {
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected Enumeration findResources(String name) throws IOException {
+ List allResources = new LinkedList<>();
+ List allJars = getAllJars();
+ for (Jar jar : allJars) {
+ JarEntry entry = jar.jarFile.getJarEntry(name);
+ if (entry != null) {
+ allResources.add(new URL("jar:file:" + jar.sourceFile.getAbsolutePath() + "!/" + name));
+ }
+ }
+
+ final Iterator iterator = allResources.iterator();
+ return new Enumeration() {
+
+ @Override
+ public boolean hasMoreElements() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public URL nextElement() {
+ return iterator.next();
+ }
+ };
+ }
+
+ private Class> processLoadedClass(Class> loadedClass) {
+ final PluginConfig pluginConfig = loadedClass.getAnnotation(PluginConfig.class);
+ if (pluginConfig != null) {
+ // Set up the plugin config when loaded by class loader at the first time.
+ // Agent class loader just loaded limited classes in the plugin jar(s), so the cost of this
+ // isAssignableFrom would be also very limited.
+ SnifferConfigInitializer.initializeConfig(pluginConfig.root());
+ }
+
+ final SpringBootConfigNode springBootConfig = loadedClass.getAnnotation(SpringBootConfigNode.class);
+ if (springBootConfig != null) {
+ // Set up the plugin config when loaded by spring environment is prepared, just scan in here.
+ // Agent class loader just loaded limited classes in the plugin jar(s), so the cost of this
+ // isAssignableFrom would be also very limited.
+ SpringBootConfigInitializer.initializeConfig(springBootConfig);
+ }
+ return loadedClass;
+ }
+
+ private List getAllJars() {
+ if (allJars == null) {
+ jarScanLock.lock();
+ try {
+ if (allJars == null) {
+ allJars = doGetJars();
+ }
+ } finally {
+ jarScanLock.unlock();
+ }
+ }
+
+ return allJars;
+ }
+
+ private LinkedList doGetJars() {
+ LinkedList jars = new LinkedList<>();
+ for (File path : classpath) {
+ if (path.exists() && path.isDirectory()) {
+ String[] jarFileNames = path.list((dir, name) -> name.endsWith(".jar"));
+ for (String fileName : jarFileNames) {
+ try {
+ File file = new File(path, fileName);
+ Jar jar = new Jar(new JarFile(file), file);
+ jars.add(jar);
+ LOGGER.info("{} loaded.", file.toString());
+ } catch (IOException e) {
+ LOGGER.error(e, "{} jar file can't be resolved", fileName);
+ }
+ }
+ }
+ }
+ return jars;
+ }
+
+ @RequiredArgsConstructor
+ private static class Jar {
+
+ private final JarFile jarFile;
+ private final File sourceFile;
+ }
+}
\ No newline at end of file
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/loader/InstrumentationLoader.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/loader/InstrumentationLoader.java
new file mode 100755
index 00000000..9fa470b4
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/loader/InstrumentationLoader.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.loader;
+
+import cn.hippo4j.agent.core.plugin.AbstractClassEnhancePluginDefine;
+
+import java.util.List;
+
+/**
+ * the spi of the InstrumentationLoader.
+ */
+
+public interface InstrumentationLoader {
+
+ List load(AgentClassLoader classLoader);
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/loader/InterceptorInstanceLoader.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/loader/InterceptorInstanceLoader.java
new file mode 100644
index 00000000..cd1e0352
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/loader/InterceptorInstanceLoader.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.loader;
+
+import cn.hippo4j.agent.core.boot.AgentPackageNotFoundException;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * The InterceptorInstanceLoader
is a classes finder and container.
+ *
+ * This is a very important class in sky-walking's auto-instrumentation mechanism. If you want to fully understand why
+ * need this, and how it works, you need have knowledge about Classloader appointment mechanism.
+ *
+ */
+public class InterceptorInstanceLoader {
+
+ private static ConcurrentHashMap INSTANCE_CACHE = new ConcurrentHashMap();
+ private static ReentrantLock INSTANCE_LOAD_LOCK = new ReentrantLock();
+ private static Map EXTEND_PLUGIN_CLASSLOADERS = new HashMap();
+
+ /**
+ * Load an instance of interceptor, and keep it singleton. Create {@link AgentClassLoader} for each
+ * targetClassLoader, as an extend classloader. It can load interceptor classes from plugins, activations folders.
+ *
+ * @param className the interceptor class, which is expected to be found
+ * @param targetClassLoader the class loader for current application context
+ * @param expected type
+ * @return the type reference.
+ */
+ public static T load(String className,
+ ClassLoader targetClassLoader) throws IllegalAccessException, InstantiationException, ClassNotFoundException, AgentPackageNotFoundException {
+ if (targetClassLoader == null) {
+ targetClassLoader = InterceptorInstanceLoader.class.getClassLoader();
+ }
+ String instanceKey = className + "_OF_" + targetClassLoader.getClass()
+ .getName() + "@"
+ + Integer.toHexString(targetClassLoader
+ .hashCode());
+ Object inst = INSTANCE_CACHE.get(instanceKey);
+ if (inst == null) {
+ INSTANCE_LOAD_LOCK.lock();
+ ClassLoader pluginLoader;
+ try {
+ pluginLoader = EXTEND_PLUGIN_CLASSLOADERS.get(targetClassLoader);
+ if (pluginLoader == null) {
+ pluginLoader = new AgentClassLoader(targetClassLoader);
+ EXTEND_PLUGIN_CLASSLOADERS.put(targetClassLoader, pluginLoader);
+ }
+ } finally {
+ INSTANCE_LOAD_LOCK.unlock();
+ }
+ inst = Class.forName(className, true, pluginLoader).newInstance();
+ if (inst != null) {
+ INSTANCE_CACHE.put(instanceKey, inst);
+ }
+ }
+
+ return (T) inst;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/ClassAnnotationMatch.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/ClassAnnotationMatch.java
new file mode 100644
index 00000000..ab181816
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/ClassAnnotationMatch.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.match;
+
+import net.bytebuddy.description.annotation.AnnotationDescription;
+import net.bytebuddy.description.annotation.AnnotationList;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
+import static net.bytebuddy.matcher.ElementMatchers.isInterface;
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static net.bytebuddy.matcher.ElementMatchers.not;
+
+/**
+ * Match the class by the given annotations in class.
+ */
+public class ClassAnnotationMatch implements IndirectMatch {
+
+ private String[] annotations;
+
+ private ClassAnnotationMatch(String[] annotations) {
+ if (annotations == null || annotations.length == 0) {
+ throw new IllegalArgumentException("annotations is null");
+ }
+ this.annotations = annotations;
+ }
+
+ @Override
+ public ElementMatcher.Junction buildJunction() {
+ ElementMatcher.Junction junction = null;
+ for (String annotation : annotations) {
+ if (junction == null) {
+ junction = buildEachAnnotation(annotation);
+ } else {
+ junction = junction.and(buildEachAnnotation(annotation));
+ }
+ }
+ junction = junction.and(not(isInterface()));
+ return junction;
+ }
+
+ @Override
+ public boolean isMatch(TypeDescription typeDescription) {
+ List annotationList = new ArrayList(Arrays.asList(annotations));
+ AnnotationList declaredAnnotations = typeDescription.getDeclaredAnnotations();
+ for (AnnotationDescription annotation : declaredAnnotations) {
+ annotationList.remove(annotation.getAnnotationType().getActualName());
+ }
+ return annotationList.isEmpty();
+ }
+
+ private ElementMatcher.Junction buildEachAnnotation(String annotationName) {
+ return isAnnotatedWith(named(annotationName));
+ }
+
+ public static ClassAnnotationMatch byClassAnnotationMatch(String... annotations) {
+ return new ClassAnnotationMatch(annotations);
+ }
+}
diff --git a/hippo4j-common/src/test/java/cn/hippo4j/common/web/ResultsTest.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/ClassMatch.java
similarity index 91%
rename from hippo4j-common/src/test/java/cn/hippo4j/common/web/ResultsTest.java
rename to agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/ClassMatch.java
index 44369db3..750bcf63 100644
--- a/hippo4j-common/src/test/java/cn/hippo4j/common/web/ResultsTest.java
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/ClassMatch.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package cn.hippo4j.common.web;
+package cn.hippo4j.agent.core.plugin.match;
-public final class ResultsTest {
+public interface ClassMatch {
}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/HierarchyMatch.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/HierarchyMatch.java
new file mode 100644
index 00000000..759f38ba
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/HierarchyMatch.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.match;
+
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.description.type.TypeList;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
+import static net.bytebuddy.matcher.ElementMatchers.isInterface;
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static net.bytebuddy.matcher.ElementMatchers.not;
+
+/**
+ * Match the class by the given super class or interfaces.
+ */
+public class HierarchyMatch implements IndirectMatch {
+
+ private String[] parentTypes;
+
+ private HierarchyMatch(String[] parentTypes) {
+ if (parentTypes == null || parentTypes.length == 0) {
+ throw new IllegalArgumentException("parentTypes is null");
+ }
+ this.parentTypes = parentTypes;
+ }
+
+ @Override
+ public ElementMatcher.Junction buildJunction() {
+ ElementMatcher.Junction junction = null;
+ for (String superTypeName : parentTypes) {
+ if (junction == null) {
+ junction = buildSuperClassMatcher(superTypeName);
+ } else {
+ junction = junction.and(buildSuperClassMatcher(superTypeName));
+ }
+ }
+ junction = junction.and(not(isInterface()));
+ return junction;
+ }
+
+ private ElementMatcher.Junction buildSuperClassMatcher(String superTypeName) {
+ return hasSuperType(named(superTypeName));
+ }
+
+ @Override
+ public boolean isMatch(TypeDescription typeDescription) {
+ List parentTypes = new ArrayList(Arrays.asList(this.parentTypes));
+
+ TypeList.Generic implInterfaces = typeDescription.getInterfaces();
+ for (TypeDescription.Generic implInterface : implInterfaces) {
+ matchHierarchyClass(implInterface, parentTypes);
+ }
+
+ if (typeDescription.getSuperClass() != null) {
+ matchHierarchyClass(typeDescription.getSuperClass(), parentTypes);
+ }
+
+ return parentTypes.size() == 0;
+
+ }
+
+ private void matchHierarchyClass(TypeDescription.Generic clazz, List parentTypes) {
+ parentTypes.remove(clazz.asRawType().getTypeName());
+ if (parentTypes.size() == 0) {
+ return;
+ }
+
+ for (TypeDescription.Generic generic : clazz.getInterfaces()) {
+ matchHierarchyClass(generic, parentTypes);
+ }
+
+ TypeDescription.Generic superClazz = clazz.getSuperClass();
+ if (superClazz != null && !clazz.getTypeName().equals("java.lang.Object")) {
+ matchHierarchyClass(superClazz, parentTypes);
+ }
+
+ }
+
+ public static IndirectMatch byHierarchyMatch(String... parentTypes) {
+ return new HierarchyMatch(parentTypes);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/IndirectMatch.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/IndirectMatch.java
new file mode 100644
index 00000000..a400fdfc
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/IndirectMatch.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.match;
+
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+/**
+ * All implementations can't direct match the class like {@link NameMatch} did.
+ */
+public interface IndirectMatch extends ClassMatch {
+
+ ElementMatcher.Junction buildJunction();
+
+ boolean isMatch(TypeDescription typeDescription);
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/MethodAnnotationMatch.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/MethodAnnotationMatch.java
new file mode 100644
index 00000000..71c79747
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/MethodAnnotationMatch.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.match;
+
+import net.bytebuddy.description.annotation.AnnotationDescription;
+import net.bytebuddy.description.annotation.AnnotationList;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import net.bytebuddy.matcher.ElementMatchers;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static net.bytebuddy.matcher.ElementMatchers.declaresMethod;
+import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
+import static net.bytebuddy.matcher.ElementMatchers.isInterface;
+import static net.bytebuddy.matcher.ElementMatchers.named;
+
+/**
+ * Match the class, which has methods with the certain annotations. This is a very complex match.
+ */
+public class MethodAnnotationMatch implements IndirectMatch {
+
+ private String[] annotations;
+
+ private MethodAnnotationMatch(String[] annotations) {
+ if (annotations == null || annotations.length == 0) {
+ throw new IllegalArgumentException("annotations is null");
+ }
+ this.annotations = annotations;
+ }
+
+ @Override
+ public ElementMatcher.Junction buildJunction() {
+ ElementMatcher.Junction junction = null;
+ for (String annotation : annotations) {
+ if (junction == null) {
+ junction = buildEachAnnotation(annotation);
+ } else {
+ junction = junction.and(buildEachAnnotation(annotation));
+ }
+ }
+ junction = declaresMethod(junction).and(ElementMatchers.not(isInterface()));
+ return junction;
+ }
+
+ @Override
+ public boolean isMatch(TypeDescription typeDescription) {
+ for (MethodDescription.InDefinedShape methodDescription : typeDescription.getDeclaredMethods()) {
+ List annotationList = new ArrayList(Arrays.asList(annotations));
+
+ AnnotationList declaredAnnotations = methodDescription.getDeclaredAnnotations();
+ for (AnnotationDescription annotation : declaredAnnotations) {
+ annotationList.remove(annotation.getAnnotationType().getActualName());
+ }
+ if (annotationList.isEmpty()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private ElementMatcher.Junction buildEachAnnotation(String annotationName) {
+ return isAnnotatedWith(named(annotationName));
+ }
+
+ public static IndirectMatch byMethodAnnotationMatch(String... annotations) {
+ return new MethodAnnotationMatch(annotations);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/MethodInheritanceAnnotationMatcher.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/MethodInheritanceAnnotationMatcher.java
new file mode 100644
index 00000000..94fd02ae
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/MethodInheritanceAnnotationMatcher.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.match;
+
+import net.bytebuddy.build.HashCodeAndEqualsPlugin;
+import net.bytebuddy.description.annotation.AnnotationList;
+import net.bytebuddy.description.annotation.AnnotationSource;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.description.method.MethodList;
+import net.bytebuddy.description.method.ParameterList;
+import net.bytebuddy.description.type.TypeDefinition;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.description.type.TypeList;
+import net.bytebuddy.matcher.CollectionItemMatcher;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import java.util.Objects;
+
+import static net.bytebuddy.matcher.ElementMatchers.annotationType;
+
+/**
+ * Matching used to match method annotations, Can match annotations on interface methods
+ */
+@HashCodeAndEqualsPlugin.Enhance
+public class MethodInheritanceAnnotationMatcher extends ElementMatcher.Junction.AbstractBase {
+
+ /**
+ * The matcher to be applied to the provided annotation list.
+ */
+ private final ElementMatcher super AnnotationList> matcher;
+
+ /**
+ * Creates a new matcher for the annotations of an annotated element.
+ *
+ * @param matcher The matcher to be applied to the provided annotation list.
+ */
+ public MethodInheritanceAnnotationMatcher(ElementMatcher super AnnotationList> matcher) {
+ this.matcher = matcher;
+ }
+
+ @Override
+ public boolean matches(T target) {
+ if (matcher.matches(target.getDeclaredAnnotations())) {
+ return true;
+ }
+ String name = target.getName();
+ ParameterList> parameters = target.getParameters();
+
+ TypeDefinition declaringType = target.getDeclaringType();
+ return recursiveMatches(declaringType, name, parameters);
+ }
+
+ private boolean recursiveMatches(TypeDefinition typeDefinition, String methodName, ParameterList> parameters) {
+ TypeList.Generic interfaces = typeDefinition.getInterfaces();
+ for (TypeDescription.Generic implInterface : interfaces) {
+ if (recursiveMatches(implInterface, methodName, parameters)) {
+ return true;
+ }
+ MethodList declaredMethods = implInterface.getDeclaredMethods();
+ for (MethodDescription declaredMethod : declaredMethods) {
+ if (Objects.equals(declaredMethod.getName(), methodName) && parameterEquals(parameters, declaredMethod.getParameters())) {
+ return matcher.matches(declaredMethod.getDeclaredAnnotations());
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean parameterEquals(ParameterList> source, ParameterList> impl) {
+ if (source.size() != impl.size()) {
+ return false;
+ }
+ for (int i = 0; i < source.size(); i++) {
+ if (!Objects.equals(source.get(i).getType(), impl.get(i).getType())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static Junction byMethodInheritanceAnnotationMatcher(
+ ElementMatcher super TypeDescription> matcher) {
+ return new MethodInheritanceAnnotationMatcher(new CollectionItemMatcher<>(annotationType(matcher)));
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/MultiClassNameMatch.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/MultiClassNameMatch.java
new file mode 100644
index 00000000..8ebeba25
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/MultiClassNameMatch.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.match;
+
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+
+/**
+ * Match class with a given set of classes.
+ */
+public class MultiClassNameMatch implements IndirectMatch {
+
+ private List matchClassNames;
+
+ private MultiClassNameMatch(String[] classNames) {
+ if (classNames == null || classNames.length == 0) {
+ throw new IllegalArgumentException("match class names is null");
+ }
+ this.matchClassNames = Arrays.asList(classNames);
+ }
+
+ @Override
+ public ElementMatcher.Junction buildJunction() {
+ ElementMatcher.Junction junction = null;
+ for (String name : matchClassNames) {
+ if (junction == null) {
+ junction = named(name);
+ } else {
+ junction = junction.or(named(name));
+ }
+ }
+ return junction;
+ }
+
+ @Override
+ public boolean isMatch(TypeDescription typeDescription) {
+ return matchClassNames.contains(typeDescription.getTypeName());
+ }
+
+ public static IndirectMatch byMultiClassMatch(String... classNames) {
+ return new MultiClassNameMatch(classNames);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/NameMatch.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/NameMatch.java
new file mode 100644
index 00000000..2dd9b11d
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/NameMatch.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.match;
+
+/**
+ * Match the class with an explicit class name.
+ */
+public class NameMatch implements ClassMatch {
+
+ private String className;
+
+ private NameMatch(String className) {
+ this.className = className;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public static NameMatch byName(String className) {
+ return new NameMatch(className);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/PrefixMatch.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/PrefixMatch.java
new file mode 100644
index 00000000..d47d0409
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/PrefixMatch.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.match;
+
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import net.bytebuddy.matcher.ElementMatchers;
+
+/**
+ * Match classes by any one of the given {@link #prefixes}
+ */
+@SuppressWarnings("rawtypes")
+public class PrefixMatch implements IndirectMatch {
+
+ private String[] prefixes;
+
+ private PrefixMatch(String... prefixes) {
+ if (prefixes == null || prefixes.length == 0) {
+ throw new IllegalArgumentException("prefixes argument is null or empty");
+ }
+ this.prefixes = prefixes;
+ }
+
+ @Override
+ public ElementMatcher.Junction buildJunction() {
+ ElementMatcher.Junction junction = null;
+
+ for (String prefix : prefixes) {
+ if (junction == null) {
+ junction = ElementMatchers.nameStartsWith(prefix);
+ } else {
+ junction = junction.or(ElementMatchers.nameStartsWith(prefix));
+ }
+ }
+
+ return junction;
+ }
+
+ @Override
+ public boolean isMatch(TypeDescription typeDescription) {
+ for (final String prefix : prefixes) {
+ if (typeDescription.getName().startsWith(prefix)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static PrefixMatch nameStartsWith(final String... prefixes) {
+ return new PrefixMatch(prefixes);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/ProtectiveShieldMatcher.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/ProtectiveShieldMatcher.java
new file mode 100644
index 00000000..d0ec36cb
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/ProtectiveShieldMatcher.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.match;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import net.bytebuddy.matcher.ElementMatcher;
+
+/**
+ * In some cases, some frameworks and libraries use some binary codes tech too. From the community feedback, some of
+ * them have compatible issues with byte-buddy core, which trigger "Can't resolve type description" exception.
+ *
+ * So I build this protective shield by a nested matcher. When the origin matcher(s) can't resolve the type, the
+ * Hippo4j agent ignores this types.
+ *
+ * Notice: this ignore mechanism may miss some instrumentations, but at most cases, it's same. If missing happens,
+ * please pay attention to the WARNING logs.
+ */
+public class ProtectiveShieldMatcher extends ElementMatcher.Junction.AbstractBase {
+
+ private static final ILog LOGGER = LogManager.getLogger(ProtectiveShieldMatcher.class);
+
+ private final ElementMatcher super T> matcher;
+
+ public ProtectiveShieldMatcher(ElementMatcher super T> matcher) {
+ this.matcher = matcher;
+ }
+
+ @Override
+ public boolean matches(T target) {
+ try {
+ return this.matcher.matches(target);
+ } catch (Throwable t) {
+ if (LOGGER.isDebugEnable()) {
+ LOGGER.debug(t, "Byte-buddy occurs exception when match type.");
+ }
+ return false;
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/RegexMatch.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/RegexMatch.java
new file mode 100644
index 00000000..e3310b24
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/RegexMatch.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.match;
+
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import static net.bytebuddy.matcher.ElementMatchers.nameMatches;
+
+/**
+ * Match the class by given class name regex expression.
+ */
+public class RegexMatch implements IndirectMatch {
+
+ private String[] regexExpressions;
+
+ private RegexMatch(String... regexExpressions) {
+ if (regexExpressions == null || regexExpressions.length == 0) {
+ throw new IllegalArgumentException("annotations is null");
+ }
+ this.regexExpressions = regexExpressions;
+ }
+
+ @Override
+ public ElementMatcher.Junction buildJunction() {
+ ElementMatcher.Junction regexJunction = null;
+ for (String regexExpression : regexExpressions) {
+ if (regexJunction == null) {
+ regexJunction = nameMatches(regexExpression);
+ } else {
+ regexJunction = regexJunction.or(nameMatches(regexExpression));
+ }
+ }
+ return regexJunction;
+ }
+
+ @Override
+ public boolean isMatch(TypeDescription typeDescription) {
+ boolean isMatch = false;
+ for (String matchExpression : regexExpressions) {
+ isMatch = typeDescription.getTypeName().matches(matchExpression);
+ if (isMatch) {
+ break;
+ }
+ }
+ return isMatch;
+ }
+
+ public static RegexMatch byRegexMatch(String... regexExpressions) {
+ return new RegexMatch(regexExpressions);
+ }
+}
\ No newline at end of file
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/logical/LogicalAndMatch.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/logical/LogicalAndMatch.java
new file mode 100644
index 00000000..8f5f05f5
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/logical/LogicalAndMatch.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.match.logical;
+
+import cn.hippo4j.agent.core.plugin.match.IndirectMatch;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+/**
+ * Match classes by multiple criteria with AND conjunction
+ */
+public class LogicalAndMatch implements IndirectMatch {
+
+ private final IndirectMatch[] indirectMatches;
+
+ /**
+ * Don't instantiate this class directly, use {@link LogicalMatchOperation} instead
+ *
+ * @param indirectMatches the matching criteria to conjunct with AND
+ */
+ LogicalAndMatch(final IndirectMatch... indirectMatches) {
+ this.indirectMatches = indirectMatches;
+ }
+
+ @Override
+ public ElementMatcher.Junction buildJunction() {
+ ElementMatcher.Junction junction = null;
+
+ for (final IndirectMatch indirectMatch : indirectMatches) {
+ if (junction == null) {
+ junction = indirectMatch.buildJunction();
+ } else {
+ junction = junction.and(indirectMatch.buildJunction());
+ }
+ }
+
+ return junction;
+ }
+
+ @Override
+ public boolean isMatch(final TypeDescription typeDescription) {
+ for (final IndirectMatch indirectMatch : indirectMatches) {
+ if (!indirectMatch.isMatch(typeDescription)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/logical/LogicalMatchOperation.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/logical/LogicalMatchOperation.java
new file mode 100644
index 00000000..26637d8d
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/logical/LogicalMatchOperation.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.match.logical;
+
+import cn.hippo4j.agent.core.plugin.match.IndirectMatch;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import net.bytebuddy.matcher.NegatingMatcher;
+
+/**
+ * Util class to help to construct logical operations on {@link cn.hippo4j.agent.core.plugin.match.ClassMatch}s
+ */
+public class LogicalMatchOperation {
+
+ public static IndirectMatch and(final IndirectMatch... matches) {
+ return new LogicalAndMatch(matches);
+ }
+
+ public static IndirectMatch or(final IndirectMatch... matches) {
+ return new LogicalOrMatch(matches);
+ }
+
+ public static IndirectMatch not(final IndirectMatch match) {
+ return new IndirectMatch() {
+
+ @Override
+ public ElementMatcher.Junction buildJunction() {
+ return new NegatingMatcher(match.buildJunction());
+ }
+
+ @Override
+ public boolean isMatch(final TypeDescription typeDescription) {
+ return !match.isMatch(typeDescription);
+ }
+ };
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/logical/LogicalOrMatch.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/logical/LogicalOrMatch.java
new file mode 100644
index 00000000..35549a3a
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/match/logical/LogicalOrMatch.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.plugin.match.logical;
+
+import cn.hippo4j.agent.core.plugin.match.IndirectMatch;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+/**
+ * Match classes by multiple criteria with OR conjunction
+ */
+public class LogicalOrMatch implements IndirectMatch {
+
+ private final IndirectMatch[] indirectMatches;
+
+ /**
+ * Don't instantiate this class directly, use {@link LogicalMatchOperation} instead
+ *
+ * @param indirectMatches the matching criteria to conjunct with OR
+ */
+ LogicalOrMatch(final IndirectMatch... indirectMatches) {
+ this.indirectMatches = indirectMatches;
+ }
+
+ @Override
+ public ElementMatcher.Junction buildJunction() {
+ ElementMatcher.Junction junction = null;
+
+ for (final IndirectMatch indirectMatch : indirectMatches) {
+ if (junction == null) {
+ junction = indirectMatch.buildJunction();
+ } else {
+ junction = junction.or(indirectMatch.buildJunction());
+ }
+ }
+
+ return junction;
+ }
+
+ @Override
+ public boolean isMatch(final TypeDescription typeDescription) {
+ for (final IndirectMatch indirectMatch : indirectMatches) {
+ if (indirectMatch.isMatch(typeDescription)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/AgentThreadPoolConstants.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/AgentThreadPoolConstants.java
new file mode 100644
index 00000000..bdfbdaac
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/AgentThreadPoolConstants.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+public interface AgentThreadPoolConstants {
+
+ String TOMCAT_NAME_PREFIX = "namePrefix";
+ String DUBBO_NAME_PREFIX = "mPrefix";
+ String DUBBO_THREAD_NAME = "DubboServerHandler";
+ String THREAD_POOL_NAME_DUBBO = "dubbo";
+
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/CollectionUtil.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/CollectionUtil.java
new file mode 100644
index 00000000..587c84f8
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/CollectionUtil.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Some utility methods for collections. Reinvent the wheels because importing third-party libs just for some methods is
+ * not worthwhile in agent side
+ *
+ * @since 7.0.0
+ */
+public final class CollectionUtil {
+
+ public static String toString(final Map map) {
+ return map.entrySet()
+ .stream()
+ .map(entry -> entry.getKey() + "=" + Arrays.toString(entry.getValue()))
+ .collect(Collectors.joining("\n"));
+ }
+
+ @SuppressWarnings("rawtypes")
+ public static boolean isEmpty(Collection collection) {
+ return collection == null || collection.isEmpty();
+ }
+
+ /**
+ * Is empty.
+ *
+ * @param map
+ * @return
+ */
+ public static boolean isEmpty(Map, ?> map) {
+ return map == null || map.isEmpty();
+ }
+
+ /**
+ * Is not empty.
+ *
+ * @param map
+ * @return
+ */
+ public static boolean isNotEmpty(Map, ?> map) {
+ return !isEmpty(map);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/ConfigInitializer.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/ConfigInitializer.java
new file mode 100644
index 00000000..5559db3c
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/ConfigInitializer.java
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+
+/**
+ * Init a class's static fields by a {@link Properties}, including static fields and static inner classes.
+ *
+ */
+public class ConfigInitializer {
+
+ public static void initialize(Properties properties, Class> rootConfigType) throws IllegalAccessException {
+ initNextLevel(properties, rootConfigType, new ConfigDesc(), false);
+ }
+
+ public static void initialize(Properties properties, Class> rootConfigType, boolean isSpringProperties) throws IllegalAccessException {
+ initNextLevel(properties, rootConfigType, new ConfigDesc(), isSpringProperties);
+ }
+
+ private static void initNextLevel(Properties properties, Class> recentConfigType,
+ ConfigDesc parentDesc, boolean isSpringProperties) throws IllegalArgumentException, IllegalAccessException {
+ for (Field field : recentConfigType.getFields()) {
+ if (Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) {
+ String configKey = (parentDesc + "." + (isSpringProperties ? field.getName().replace("_", "-") : field.getName())).toLowerCase();
+ Class> type = field.getType();
+
+ if (type.equals(Map.class)) {
+ /*
+ * Map config format is, config_key[map_key]=map_value Such as plugin.opgroup.resttemplate.rule[abc]=/url/path
+ */
+ // Deduct two generic types of the map
+ ParameterizedType genericType = (ParameterizedType) field.getGenericType();
+ Type[] argumentTypes = genericType.getActualTypeArguments();
+
+ Type keyType = null;
+ Type valueType = null;
+ if (argumentTypes != null && argumentTypes.length == 2) {
+ // Get key type and value type of the map
+ keyType = argumentTypes[0];
+ valueType = argumentTypes[1];
+ }
+ Map map = (Map) field.get(null);
+ // Set the map from config key and properties
+ setForMapType(configKey, map, properties, keyType, valueType);
+ } else {
+ /*
+ * Others typical field type
+ */
+ String value = properties.getProperty(configKey);
+ // Convert the value into real type
+ final Length lengthDefine = field.getAnnotation(Length.class);
+ if (lengthDefine != null) {
+ if (value != null && value.length() > lengthDefine.value()) {
+ value = value.substring(0, lengthDefine.value());
+ }
+ }
+ Object convertedValue = convertToTypicalType(type, value);
+ if (convertedValue != null) {
+ field.set(null, convertedValue);
+ }
+ }
+ }
+ }
+ for (Class> innerConfiguration : recentConfigType.getClasses()) {
+ String simpleName = innerConfiguration.getSimpleName();
+ String description = isSpringProperties ? simpleName.replace("_", "-") : simpleName;
+ parentDesc.append(description);
+ initNextLevel(properties, innerConfiguration, parentDesc, isSpringProperties);
+ parentDesc.removeLastDesc();
+ }
+ }
+
+ /**
+ * Convert string value to typical type.
+ *
+ * @param type type to convert
+ * @param value string value to be converted
+ * @return converted value or null
+ */
+ private static Object convertToTypicalType(Type type, String value) {
+ if (value == null || type == null) {
+ return null;
+ }
+
+ Object result = null;
+ if (String.class.equals(type)) {
+ result = value;
+ } else if (int.class.equals(type) || Integer.class.equals(type)) {
+ result = Integer.valueOf(value);
+ } else if (long.class.equals(type) || Long.class.equals(type)) {
+ result = Long.valueOf(value);
+ } else if (boolean.class.equals(type) || Boolean.class.equals(type)) {
+ result = Boolean.valueOf(value);
+ } else if (float.class.equals(type) || Float.class.equals(type)) {
+ result = Float.valueOf(value);
+ } else if (double.class.equals(type) || Double.class.equals(type)) {
+ result = Double.valueOf(value);
+ } else if (List.class.equals(type)) {
+ result = convert2List(value);
+ } else if (type instanceof Class) {
+ Class> clazz = (Class>) type;
+ if (clazz.isEnum()) {
+ result = Enum.valueOf((Class) type, value.toUpperCase());
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Set map items.
+ *
+ * @param configKey config key must not be null
+ * @param map map to set must not be null
+ * @param properties properties must not be null
+ * @param keyType key type of the map
+ * @param valueType value type of the map
+ */
+ private static void setForMapType(String configKey, Map map, Properties properties,
+ final Type keyType, final Type valueType) {
+
+ Objects.requireNonNull(configKey);
+ Objects.requireNonNull(map);
+ Objects.requireNonNull(properties);
+
+ String prefix = configKey + "[";
+ String suffix = "]";
+
+ properties.forEach((propertyKey, propertyValue) -> {
+ String propertyStringKey = propertyKey.toString();
+ if (propertyStringKey.startsWith(prefix) && propertyStringKey.endsWith(suffix)) {
+ String itemKey = propertyStringKey.substring(
+ prefix.length(), propertyStringKey.length() - suffix.length());
+ Object keyObj;
+ Object valueObj;
+
+ keyObj = convertToTypicalType(keyType, itemKey);
+ valueObj = convertToTypicalType(valueType, propertyValue.toString());
+
+ if (keyObj == null) {
+ keyObj = itemKey;
+ }
+
+ if (valueObj == null) {
+ valueObj = propertyValue;
+ }
+
+ map.put(keyObj, valueObj);
+ }
+ });
+ }
+
+ private static List convert2List(String value) {
+ if (StringUtil.isEmpty(value)) {
+ return Collections.emptyList();
+ }
+ List result = new LinkedList<>();
+
+ String[] segments = value.split(",");
+ for (String segment : segments) {
+ String trimmedSegment = segment.trim();
+ if (StringUtil.isNotEmpty(trimmedSegment)) {
+ result.add(trimmedSegment);
+ }
+ }
+ return result;
+ }
+
+}
+
+class ConfigDesc {
+
+ private LinkedList descs = new LinkedList<>();
+
+ void append(String currentDesc) {
+ if (StringUtil.isNotEmpty(currentDesc)) {
+ descs.addLast(currentDesc);
+ }
+ }
+
+ void removeLastDesc() {
+ descs.removeLast();
+ }
+
+ @Override
+ public String toString() {
+ return String.join(".", descs);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/CustomizeExpression.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/CustomizeExpression.java
new file mode 100644
index 00000000..4912936b
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/CustomizeExpression.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * a simple parsing expression
+ */
+
+public class CustomizeExpression {
+
+ private static final ILog LOGGER = LogManager.getLogger(CustomizeExpression.class);
+
+ public static Map evaluationContext(Object[] allArguments) {
+ Map context = new HashMap<>();
+ if (allArguments == null) {
+ return context;
+ }
+ for (int i = 0; i < allArguments.length; i++) {
+ context.put("arg[" + i + "]", allArguments[i]);
+ }
+ return context;
+ }
+
+ public static Map evaluationReturnContext(Object ret) {
+ Map context = new HashMap<>();
+ context.put("returnedObj", ret);
+ return context;
+ }
+
+ public static String parseExpression(String expression, Map context) {
+ try {
+ String[] es = expression.split("\\.");
+ Object o = context.get(es[0]);
+ return o == null ? "null" : String.valueOf(parse(es, o, 0));
+ } catch (Exception e) {
+ LOGGER.debug("parse expression error, expression is {}, exception is {}", expression, e.getMessage());
+ }
+ return "null";
+ }
+
+ private static Object parse(String[] expressions, Object o, int i) {
+ int next = i + 1;
+ if (next == expressions.length) {
+ return o;
+ } else {
+ o = parse0(expressions[next], o);
+ return o == null ? "null" : parse(expressions, o, next);
+ }
+ }
+
+ private static Object parse0(String expression, Object o) {
+ if (o instanceof Map) {
+ return matcherMap(expression, o);
+ } else if (o instanceof List) {
+ return matcherList(expression, o);
+ } else if (o.getClass().isArray()) {
+ return matcherArray(expression, o);
+ } else {
+ return matcherDefault(expression, o);
+ }
+ }
+
+ private static Object matcherMap(String expression, Object o) {
+ String key = expression.replace("['", "").replace("']", "");
+ return ((Map) o).get(key);
+ }
+
+ private static Object matcherList(String expression, Object o) {
+ int index = Integer.parseInt(expression.replace("[", "").replace("]", ""));
+ List l = (List) o;
+ return l != null && l.size() > index ? l.get(index) : null;
+ }
+
+ private static Object matcherArray(String expression, Object o) {
+ int index = Integer.parseInt(expression.replace("[", "").replace("]", ""));
+ return o != null && Array.getLength(o) > index ? Array.get(o, index) : null;
+ }
+
+ private static Object matcherDefault(String expression, Object o) {
+ try {
+ if (expression.contains("()")) {
+ Method m = o.getClass().getMethod(expression.replace("()", ""));
+ m.setAccessible(true);
+ return m.invoke(o);
+ } else {
+ Field f = o.getClass().getDeclaredField(expression);
+ f.setAccessible(true);
+ return f.get(o);
+ }
+ } catch (Exception e) {
+ LOGGER.debug("matcher default error, expression is {}, object is {}, expression is {}", expression, o, e.getMessage());
+ }
+ return null;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/ExecutorNameUtil.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/ExecutorNameUtil.java
new file mode 100644
index 00000000..7a597187
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/ExecutorNameUtil.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+
+import java.lang.reflect.Field;
+
+public class ExecutorNameUtil {
+
+ private static final ILog LOGGER = LogManager.getLogger(ExecutorNameUtil.class);
+
+ public static boolean isTomcatExecutor(Object threadFactory) {
+ try {
+ if ("org.apache.tomcat.util.threads.TaskThreadFactory".equals(threadFactory.getClass().getName())) {
+ Field namePrefixField = threadFactory.getClass().getDeclaredField(AgentThreadPoolConstants.TOMCAT_NAME_PREFIX);
+ namePrefixField.setAccessible(true);
+ String namePrefix = (String) namePrefixField.get(threadFactory);
+ if (RegexUtil.isTomcatNameMatch(namePrefix)) {
+ return true;
+ }
+ }
+ } catch (Throwable t) {
+ LOGGER.error("Fail to put tomcat executor", t);
+ }
+ return false;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/FileUtils.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/FileUtils.java
new file mode 100644
index 00000000..2d954dda
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/FileUtils.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+import java.io.File;
+import java.nio.file.Files;
+
+public class FileUtils {
+
+ /**
+ * delete directories and files recursively
+ *
+ * @param dir directory to delete
+ */
+ public static void deleteDirectory(File dir) {
+ File[] files = dir.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ if (!Files.isSymbolicLink(file.toPath())) {
+ if (file.isDirectory()) {
+ deleteDirectory(file);
+ } else {
+ file.delete();
+ }
+ } else {
+ file.delete();
+ }
+ }
+ }
+ dir.delete();
+ }
+
+ public static void deleteDirectoryOnExit(File dir) {
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+
+ @Override
+ public void run() {
+ FileUtils.deleteDirectory(dir);
+ }
+ });
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/IOUtils.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/IOUtils.java
new file mode 100644
index 00000000..7c89756b
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/IOUtils.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Copied from commons-io-2.2 (org.apache.commons.io.IOUtils)
+ * Origin license: http://www.apache.org/licenses/LICENSE-2.0
+ * @version $Id: IOUtils.java 1304177 2012-03-23 03:36:44Z ggregory $
+ */
+public class IOUtils {
+
+ private static final int EOF = -1;
+
+ /**
+ * The default buffer size ({@value}) to use for
+ * {@link #copyLarge(InputStream, OutputStream)}
+ */
+ private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
+
+ /**
+ * Get the contents of an InputStream
as a byte[]
.
+ *
+ * This method buffers the input internally, so there is no need to use a
+ * BufferedInputStream
.
+ *
+ * @param input the InputStream
to read from
+ * @return the requested byte array
+ * @throws NullPointerException if the input is null
+ * @throws IOException if an I/O error occurs
+ */
+ public static byte[] toByteArray(InputStream input) throws IOException {
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ copy(input, output);
+ return output.toByteArray();
+ }
+
+ /**
+ * Copy bytes from an InputStream
to an
+ * OutputStream
.
+ *
+ * This method buffers the input internally, so there is no need to use a
+ * BufferedInputStream
.
+ *
+ * Large streams (over 2GB) will return a bytes copied value of
+ * -1
after the copy has completed since the correct
+ * number of bytes cannot be returned as an int. For large streams
+ * use the copyLarge(InputStream, OutputStream)
method.
+ *
+ * @param input the InputStream
to read from
+ * @param output the OutputStream
to write to
+ * @return the number of bytes copied, or -1 if > Integer.MAX_VALUE
+ * @throws NullPointerException if the input or output is null
+ * @throws IOException if an I/O error occurs
+ * @since 1.1
+ */
+ public static int copy(InputStream input, OutputStream output) throws IOException {
+ long count = copyLarge(input, output);
+ if (count > Integer.MAX_VALUE) {
+ return -1;
+ }
+ return (int) count;
+ }
+
+ /**
+ * Copy bytes from a large (over 2GB) InputStream
to an
+ * OutputStream
.
+ *
+ * This method buffers the input internally, so there is no need to use a
+ * BufferedInputStream
.
+ *
+ * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}.
+ *
+ * @param input the InputStream
to read from
+ * @param output the OutputStream
to write to
+ * @return the number of bytes copied
+ * @throws NullPointerException if the input or output is null
+ * @throws IOException if an I/O error occurs
+ * @since 1.3
+ */
+ public static long copyLarge(InputStream input, OutputStream output) throws IOException {
+ return copyLarge(input, output, new byte[DEFAULT_BUFFER_SIZE]);
+ }
+
+ /**
+ * Copy bytes from a large (over 2GB) InputStream
to an
+ * OutputStream
.
+ *
+ * This method uses the provided buffer, so there is no need to use a
+ * BufferedInputStream
.
+ *
+ *
+ * @param input the InputStream
to read from
+ * @param output the OutputStream
to write to
+ * @param buffer the buffer to use for the copy
+ * @return the number of bytes copied
+ * @throws NullPointerException if the input or output is null
+ * @throws IOException if an I/O error occurs
+ * @since 2.2
+ */
+ public static long copyLarge(InputStream input, OutputStream output, byte[] buffer) throws IOException {
+ long count = 0;
+ int n = 0;
+ while (EOF != (n = input.read(buffer))) {
+ output.write(buffer, 0, n);
+ count += n;
+ }
+ return count;
+ }
+
+ /**
+ * close streams
+ * @param closeable the closeable handler
+ */
+ public static void closeQuietly(Closeable closeable) {
+ try {
+ if (closeable != null) {
+ closeable.close();
+ }
+ } catch (IOException ex) {
+ // ignore ex
+ }
+ }
+
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/Length.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/Length.java
new file mode 100644
index 00000000..4226d4d7
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/Length.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The length rule of the target field.
+ */
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Length {
+
+ int value();
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/MethodUtil.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/MethodUtil.java
new file mode 100644
index 00000000..bd7e13f6
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/MethodUtil.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+import java.lang.reflect.Method;
+
+/**
+ * According to the input parameter, return the OperationName for the span record, It can determine the unique method
+ */
+
+public class MethodUtil {
+
+ public static String generateOperationName(Method method) {
+ StringBuilder operationName = new StringBuilder(method.getDeclaringClass()
+ .getName() + "." + method.getName() + "(");
+ Class>[] parameterTypes = method.getParameterTypes();
+ for (int i = 0; i < parameterTypes.length; i++) {
+ operationName.append(parameterTypes[i].getName());
+ if (i < (parameterTypes.length - 1)) {
+ operationName.append(",");
+ }
+ }
+ operationName.append(")");
+ return operationName.toString();
+ }
+
+ /**
+ * This is a low-performance method, recommend to use this when have to, make sure it is only executed once and the
+ * result is being cached.
+ */
+ public static boolean isMethodExist(ClassLoader classLoader, String className, String methodName,
+ String... parameterTypes) {
+ try {
+ Class> clazz = Class.forName(className, true, classLoader);
+ if (parameterTypes == null || parameterTypes.length == 0) {
+ clazz.getDeclaredMethod(methodName);
+ return true;
+ } else {
+ Method[] declaredMethods = clazz.getDeclaredMethods();
+ for (Method declaredMethod : declaredMethods) {
+ if (declaredMethod.getName().equals(methodName)
+ && isParameterTypesEquals(declaredMethod.getParameterTypes(), parameterTypes)) {
+ return true;
+ }
+ }
+ }
+ } catch (Exception e) {
+ // ignore
+ }
+ return false;
+ }
+
+ private static boolean isParameterTypesEquals(Class>[] parameterTypeClazz, String[] parameterTypeString) {
+ if (parameterTypeClazz == null) {
+ return false;
+ }
+ if (parameterTypeClazz.length != parameterTypeString.length) {
+ return false;
+ }
+ for (int i = 0; i < parameterTypeClazz.length; i++) {
+ if (!parameterTypeClazz[i].getName().equals(parameterTypeString[i])) {
+ return false;
+ }
+ }
+ return true;
+
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/PlaceholderConfigurerSupport.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/PlaceholderConfigurerSupport.java
new file mode 100644
index 00000000..3b2f6ffd
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/PlaceholderConfigurerSupport.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+public class PlaceholderConfigurerSupport {
+
+ /**
+ * Default placeholder prefix: {@value}
+ */
+ public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
+
+ /**
+ * Default placeholder suffix: {@value}
+ */
+ public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
+
+ /**
+ * Default value separator: {@value}
+ */
+ public static final String DEFAULT_VALUE_SEPARATOR = ":";
+
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/PrivateKeyUtil.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/PrivateKeyUtil.java
new file mode 100644
index 00000000..48df0f19
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/PrivateKeyUtil.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Base64;
+
+/**
+ * Util intends to parse PKCS#1 and PKCS#8 at same time.
+ */
+public class PrivateKeyUtil {
+
+ private static final String PKCS_1_PEM_HEADER = "-----BEGIN RSA PRIVATE KEY-----";
+ private static final String PKCS_1_PEM_FOOTER = "-----END RSA PRIVATE KEY-----";
+ private static final String PKCS_8_PEM_HEADER = "-----BEGIN PRIVATE KEY-----";
+ private static final String PKCS_8_PEM_FOOTER = "-----END PRIVATE KEY-----";
+
+ /**
+ * Load a RSA decryption key from a file (PEM or DER).
+ */
+ public static InputStream loadDecryptionKey(String keyFilePath) throws IOException {
+ byte[] keyDataBytes = Files.readAllBytes(Paths.get(keyFilePath));
+ String keyDataString = new String(keyDataBytes, StandardCharsets.UTF_8);
+
+ if (keyDataString.contains(PKCS_1_PEM_HEADER)) {
+ // OpenSSL / PKCS#1 Base64 PEM encoded file
+ keyDataString = keyDataString.replace(PKCS_1_PEM_HEADER, "");
+ keyDataString = keyDataString.replace(PKCS_1_PEM_FOOTER, "");
+ keyDataString = keyDataString.replace("\n", "");
+ return readPkcs1PrivateKey(Base64.getDecoder().decode(keyDataString));
+ }
+
+ return new ByteArrayInputStream(keyDataString.getBytes());
+ }
+
+ /**
+ * Create a InputStream instance from raw PKCS#1 bytes. Raw Java API can't recognize ASN.1 format, so we should
+ * convert it into a pkcs#8 format Java can understand.
+ */
+ private static InputStream readPkcs1PrivateKey(byte[] pkcs1Bytes) {
+ int pkcs1Length = pkcs1Bytes.length;
+ int totalLength = pkcs1Length + 22;
+ byte[] pkcs8Header = new byte[]{
+ 0x30, (byte) 0x82, (byte) ((totalLength >> 8) & 0xff), (byte) (totalLength & 0xff), // Sequence + total length
+ 0x2, 0x1, 0x0, // Integer (0)
+ 0x30, 0xD, 0x6, 0x9, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0xD, 0x1, 0x1, 0x1, 0x5, 0x0, // Sequence: 1.2.840.113549.1.1.1, NULL
+ 0x4, (byte) 0x82, (byte) ((pkcs1Length >> 8) & 0xff), (byte) (pkcs1Length & 0xff) // Octet string + length
+ };
+ StringBuilder pkcs8 = new StringBuilder(PKCS_8_PEM_HEADER);
+ pkcs8.append("\n").append(new String(Base64.getEncoder().encode(join(pkcs8Header, pkcs1Bytes))));
+ pkcs8.append("\n").append(PKCS_8_PEM_FOOTER);
+ return new ByteArrayInputStream(pkcs8.toString().getBytes());
+ }
+
+ private static byte[] join(byte[] byteArray1, byte[] byteArray2) {
+ byte[] bytes = new byte[byteArray1.length + byteArray2.length];
+ System.arraycopy(byteArray1, 0, bytes, 0, byteArray1.length);
+ System.arraycopy(byteArray2, 0, bytes, byteArray1.length, byteArray2.length);
+ return bytes;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/PropertyPlaceholderHelper.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/PropertyPlaceholderHelper.java
new file mode 100644
index 00000000..39e09629
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/PropertyPlaceholderHelper.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * Utility class for working with Strings that have placeholder values in them. A placeholder takes the form {@code
+ * ${name}}. Using {@code PropertyPlaceholderHelper} these placeholders can be substituted for user-supplied values.
+ * Values for substitution can be supplied using a {@link Properties} instance or using a {@link PlaceholderResolver}.
+ */
+public enum PropertyPlaceholderHelper {
+
+ INSTANCE(
+ PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_PREFIX,
+ PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_SUFFIX, PlaceholderConfigurerSupport.DEFAULT_VALUE_SEPARATOR,
+ true);
+
+ private final String placeholderPrefix;
+
+ private final String placeholderSuffix;
+
+ private final String simplePrefix;
+
+ private final String valueSeparator;
+
+ private final boolean ignoreUnresolvablePlaceholders;
+
+ /**
+ * Creates a new {@code PropertyPlaceholderHelper} that uses the supplied prefix and suffix.
+ *
+ * @param placeholderPrefix the prefix that denotes the start of a placeholder
+ * @param placeholderSuffix the suffix that denotes the end of a placeholder
+ * @param valueSeparator the separating character between the placeholder variable and the
+ * associated default value, if any
+ * @param ignoreUnresolvablePlaceholders indicates whether unresolvable placeholders should be ignored ({@code
+ * true}) or cause an exception ({@code false})
+ */
+ PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix, String valueSeparator,
+ boolean ignoreUnresolvablePlaceholders) {
+ if (StringUtil.isEmpty(placeholderPrefix) || StringUtil.isEmpty(placeholderSuffix)) {
+ throw new UnsupportedOperationException("'placeholderPrefix or placeholderSuffix' must not be null");
+ }
+
+ final Map wellKnownSimplePrefixes = new HashMap(4);
+
+ wellKnownSimplePrefixes.put("}", "{");
+ wellKnownSimplePrefixes.put("]", "[");
+ wellKnownSimplePrefixes.put(")", "(");
+
+ this.placeholderPrefix = placeholderPrefix;
+ this.placeholderSuffix = placeholderSuffix;
+ String simplePrefixForSuffix = wellKnownSimplePrefixes.get(this.placeholderSuffix);
+ if (simplePrefixForSuffix != null && this.placeholderPrefix.endsWith(simplePrefixForSuffix)) {
+ this.simplePrefix = simplePrefixForSuffix;
+ } else {
+ this.simplePrefix = this.placeholderPrefix;
+ }
+ this.valueSeparator = valueSeparator;
+ this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
+ }
+
+ /**
+ * Replaces all placeholders of format {@code ${name}} with the corresponding property from the supplied {@link
+ * Properties}.
+ *
+ * @param value the value containing the placeholders to be replaced
+ * @param properties the {@code Properties} to use for replacement
+ * @return the supplied value with placeholders replaced inline
+ */
+ public String replacePlaceholders(String value, final Properties properties) {
+ return replacePlaceholders(value, new PlaceholderResolver() {
+
+ @Override
+ public String resolvePlaceholder(String placeholderName) {
+ return getConfigValue(placeholderName, properties);
+ }
+ });
+ }
+
+ private String getConfigValue(String key, final Properties properties) {
+ String value = System.getProperty(key);
+ if (value == null) {
+ value = System.getenv(key);
+ }
+ if (value == null) {
+ value = properties.getProperty(key);
+ }
+ return value;
+ }
+
+ /**
+ * Replaces all placeholders of format {@code ${name}} with the value returned from the supplied {@link
+ * PlaceholderResolver}.
+ *
+ * @param value the value containing the placeholders to be replaced
+ * @param placeholderResolver the {@code PlaceholderResolver} to use for replacement
+ * @return the supplied value with placeholders replaced inline
+ */
+ public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
+ return parseStringValue(value, placeholderResolver, new HashSet());
+ }
+
+ protected String parseStringValue(String value, PlaceholderResolver placeholderResolver,
+ Set visitedPlaceholders) {
+
+ StringBuilder result = new StringBuilder(value);
+
+ int startIndex = value.indexOf(this.placeholderPrefix);
+ while (startIndex != -1) {
+ int endIndex = findPlaceholderEndIndex(result, startIndex);
+ if (endIndex != -1) {
+ String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
+ String originalPlaceholder = placeholder;
+ if (!visitedPlaceholders.add(originalPlaceholder)) {
+ throw new IllegalArgumentException(
+ "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
+ }
+ // Recursive invocation, parsing placeholders contained in the placeholder key.
+ placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
+ // Now obtain the value for the fully resolved key...
+ String propVal = placeholderResolver.resolvePlaceholder(placeholder);
+ if (propVal == null && this.valueSeparator != null) {
+ int separatorIndex = placeholder.indexOf(this.valueSeparator);
+ if (separatorIndex != -1) {
+ String actualPlaceholder = placeholder.substring(0, separatorIndex);
+ String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
+ propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
+ if (propVal == null) {
+ propVal = defaultValue;
+ }
+ }
+ }
+ if (propVal != null) {
+ // Recursive invocation, parsing placeholders contained in the
+ // previously resolved placeholder value.
+ propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
+ result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
+ startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
+ } else if (this.ignoreUnresolvablePlaceholders) {
+ // Proceed with unprocessed value.
+ startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
+ } else {
+ throw new IllegalArgumentException(
+ "Could not resolve placeholder '" + placeholder + "'" + " in value \"" + value + "\"");
+ }
+ visitedPlaceholders.remove(originalPlaceholder);
+ } else {
+ startIndex = -1;
+ }
+ }
+ return result.toString();
+ }
+
+ private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
+ int index = startIndex + this.placeholderPrefix.length();
+ int withinNestedPlaceholder = 0;
+ while (index < buf.length()) {
+ if (StringUtil.substringMatch(buf, index, this.placeholderSuffix)) {
+ if (withinNestedPlaceholder > 0) {
+ withinNestedPlaceholder--;
+ index = index + this.placeholderSuffix.length();
+ } else {
+ return index;
+ }
+ } else if (StringUtil.substringMatch(buf, index, this.simplePrefix)) {
+ withinNestedPlaceholder++;
+ index = index + this.simplePrefix.length();
+ } else {
+ index++;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Strategy interface used to resolve replacement values for placeholders contained in Strings.
+ */
+ public interface PlaceholderResolver {
+
+ /**
+ * Resolve the supplied placeholder name to the replacement value.
+ *
+ * @param placeholderName the name of the placeholder to resolve
+ * @return the replacement value, or {@code null} if no replacement is to be made
+ */
+ String resolvePlaceholder(String placeholderName);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/ReflectUtil.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/ReflectUtil.java
new file mode 100644
index 00000000..e6fed965
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/ReflectUtil.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ReflectUtil {
+
+ private static final ILog LOGGER = LogManager.getLogger(ReflectUtil.class);
+
+ public static List getStaticFieldsFromType(Class> clazz, Class> declaredType) {
+ Field[] fields = clazz.getDeclaredFields();
+ List result = new ArrayList<>();
+ for (Field field : fields) {
+ if (field.getType().isAssignableFrom(declaredType)
+ && Modifier.isStatic(field.getModifiers())) {
+ field.setAccessible(true);
+ result.add(field);
+ }
+ }
+ return result;
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/RegexUtil.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/RegexUtil.java
new file mode 100644
index 00000000..a3576643
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/RegexUtil.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+import java.util.regex.Pattern;
+
+public class RegexUtil {
+
+ private static final String TOMCAT_NAME_PATTERN_STRING = "http\\S+nio\\S+-exec-";
+ private static final Pattern TOMCAT_NAME_PATTERN = Pattern.compile(TOMCAT_NAME_PATTERN_STRING);
+
+ public static boolean isTomcatNameMatch(String executorName) {
+ return TOMCAT_NAME_PATTERN.matcher(executorName).find();
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/RunnableWithExceptionProtection.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/RunnableWithExceptionProtection.java
new file mode 100644
index 00000000..5a3d392b
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/RunnableWithExceptionProtection.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+public class RunnableWithExceptionProtection implements Runnable {
+
+ private Runnable run;
+ private CallbackWhenException callback;
+
+ public RunnableWithExceptionProtection(Runnable run, CallbackWhenException callback) {
+ this.run = run;
+ this.callback = callback;
+ }
+
+ @Override
+ public void run() {
+ try {
+ run.run();
+ } catch (Throwable t) {
+ callback.handle(t);
+ }
+ }
+
+ public interface CallbackWhenException {
+
+ void handle(Throwable t);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/StringUtil.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/StringUtil.java
new file mode 100644
index 00000000..040f915c
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/StringUtil.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+import java.util.function.Consumer;
+
+public final class StringUtil {
+
+ public static boolean isEmpty(String str) {
+ return str == null || str.length() == 0;
+ }
+
+ public static boolean isNotEmpty(String str) {
+ return !isEmpty(str);
+ }
+
+ public static boolean isBlank(String str) {
+ return str == null || isEmpty(str.trim());
+ }
+
+ public static boolean isNotBlank(String str) {
+ return !isBlank(str);
+ }
+
+ public static void setIfPresent(String value, Consumer setter) {
+ if (isNotEmpty(value)) {
+ setter.accept(value);
+ }
+ }
+
+ public static String join(final char delimiter, final String... strings) {
+ if (strings.length == 0) {
+ return null;
+ }
+ if (strings.length == 1) {
+ return strings[0];
+ }
+ int length = strings.length - 1;
+ for (final String s : strings) {
+ if (s == null) {
+ continue;
+ }
+ length += s.length();
+ }
+ final StringBuilder sb = new StringBuilder(length);
+ if (strings[0] != null) {
+ sb.append(strings[0]);
+ }
+ for (int i = 1; i < strings.length; ++i) {
+ if (!isEmpty(strings[i])) {
+ sb.append(delimiter).append(strings[i]);
+ } else {
+ sb.append(delimiter);
+ }
+ }
+ return sb.toString();
+ }
+
+ public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {
+ if (index + substring.length() > str.length()) {
+ return false;
+ }
+ for (int i = 0; i < substring.length(); i++) {
+ if (str.charAt(index + i) != substring.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static String cut(String str, int threshold) {
+ if (isEmpty(str) || str.length() <= threshold) {
+ return str;
+ }
+ return str.substring(0, threshold);
+ }
+
+ public static String trim(final String str, final char ch) {
+ if (isEmpty(str)) {
+ return null;
+ }
+
+ final char[] chars = str.toCharArray();
+
+ int i = 0, j = chars.length - 1;
+ // noinspection StatementWithEmptyBody
+ for (; i < chars.length && chars[i] == ch; i++) {
+ }
+ // noinspection StatementWithEmptyBody
+ for (; j > 0 && chars[j] == ch; j--) {
+ }
+
+ return new String(chars, i, j - i + 1);
+ }
+}
diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/ThreadPoolPropertyKey.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/ThreadPoolPropertyKey.java
new file mode 100644
index 00000000..aa493f9e
--- /dev/null
+++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/util/ThreadPoolPropertyKey.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.core.util;
+
+public interface ThreadPoolPropertyKey {
+
+ String THREAD_POOL_ID = "threadPoolId";
+
+ String CORE_POOL_SIZE = "corePoolSize";
+
+ String MAXIMUM_POOL_SIZE = "maximumPoolSize";
+
+ String ALLOW_CORE_THREAD_TIME_OUT = "allowCoreThreadTimeOut";
+
+ String KEEP_ALIVE_TIME = "keepAliveTime";
+
+ String BLOCKING_QUEUE = "blockingQueue";
+
+ String QUEUE_CAPACITY = "queueCapacity";
+
+ String REJECTED_HANDLER = "rejectedHandler";
+
+ String EXECUTE_TIME_OUT = "executeTimeOut";
+}
diff --git a/agent/hippo4j-agent-plugin/adapter-plugins/dubbo-plugin/pom.xml b/agent/hippo4j-agent-plugin/adapter-plugins/dubbo-plugin/pom.xml
new file mode 100644
index 00000000..5304c6eb
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/adapter-plugins/dubbo-plugin/pom.xml
@@ -0,0 +1,29 @@
+
+
+
+ hippo4j-agent-adapter-plugins
+ cn.hippo4j
+ ${revision}
+
+ 4.0.0
+
+ dubbo-plugin
+
+
+
+ org.apache.dubbo
+ dubbo
+ provided
+
+
+
+ cn.hippo4j
+ hippo4j-threadpool-infra-common
+ ${project.version}
+
+
+
+
+
\ No newline at end of file
diff --git a/agent/hippo4j-agent-plugin/adapter-plugins/dubbo-plugin/src/main/java/cn/hippo4j/agent/adapter/dubbo/DubboThreadPoolAdapter.java b/agent/hippo4j-agent-plugin/adapter-plugins/dubbo-plugin/src/main/java/cn/hippo4j/agent/adapter/dubbo/DubboThreadPoolAdapter.java
new file mode 100644
index 00000000..6ba5045e
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/adapter-plugins/dubbo-plugin/src/main/java/cn/hippo4j/agent/adapter/dubbo/DubboThreadPoolAdapter.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.adapter.dubbo;
+
+import cn.hippo4j.common.executor.ThreadPoolExecutorRegistry;
+import cn.hippo4j.common.executor.support.BlockingQueueTypeEnum;
+import cn.hippo4j.common.executor.support.RejectedPolicyTypeEnum;
+import cn.hippo4j.common.model.executor.ExecutorProperties;
+import cn.hippo4j.common.toolkit.BooleanUtil;
+import cn.hippo4j.common.toolkit.ReflectUtil;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadPoolExecutor;
+import org.apache.dubbo.common.Version;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.common.store.DataStore;
+import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;
+
+/**
+ * Dubbo thread-pool adapter.
+ */
+public class DubboThreadPoolAdapter {
+
+ public static void registerExecutors() {
+ boolean isLegacyVersion = true;
+ String poolKey = ExecutorService.class.getName();
+ // Since 2.7.5, Dubbo has changed the way thread pools are used
+ // fixed https://github.com/opengoofy/hippo4j/issues/708
+ try {
+ if (Version.getIntVersion(Version.getVersion()) < 2070500) {
+ isLegacyVersion = false;
+ }
+ } catch (Exception ex) {
+ }
+
+ try {
+ if (isLegacyVersion) {
+ DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
+ Map executors = dataStore.get(poolKey);
+ executors.forEach((key, value) -> putHolder(mark() + key, (ThreadPoolExecutor) value));
+ return;
+ }
+ ExecutorRepository executorRepository = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
+ ConcurrentMap> data =
+ (ConcurrentMap>) ReflectUtil.getFieldValue(executorRepository, "data");
+ ConcurrentMap executorServiceMap = data.get(poolKey);
+ executorServiceMap.forEach((key, value) -> putHolder(mark() + key, (ThreadPoolExecutor) value));
+ } catch (Exception ex) {
+ }
+ }
+
+ private static void putHolder(String executorName, ThreadPoolExecutor executor) {
+ if (executor == null) {
+ return;
+ }
+ ExecutorProperties executorProperties = ExecutorProperties.builder()
+ .threadPoolId(executorName)
+ .corePoolSize(executor.getCorePoolSize())
+ .maximumPoolSize(executor.getMaximumPoolSize())
+ .allowCoreThreadTimeOut(BooleanUtil.toBoolean(String.valueOf(executor.allowsCoreThreadTimeOut())))
+ .blockingQueue(BlockingQueueTypeEnum.getBlockingQueueTypeEnumByName(executor.getQueue().getClass().getSimpleName()).getName())
+ .queueCapacity(executor.getQueue().remainingCapacity())
+ .rejectedHandler(RejectedPolicyTypeEnum.getRejectedPolicyTypeEnumByName(executor.getRejectedExecutionHandler().getClass().getSimpleName()).getName())
+ .build();
+ ThreadPoolExecutorRegistry.putHolder(executorName, executor, executorProperties);
+ }
+
+ public static String mark() {
+ return "Dubbo";
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/adapter-plugins/pom.xml b/agent/hippo4j-agent-plugin/adapter-plugins/pom.xml
new file mode 100644
index 00000000..1c52b2e9
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/adapter-plugins/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ cn.hippo4j
+ hippo4j-agent-plugin
+ ${revision}
+
+
+ hippo4j-agent-adapter-plugins
+ pom
+
+
+ 8
+ 8
+ UTF-8
+
+
+
+ dubbo-plugin
+
+
+
\ No newline at end of file
diff --git a/agent/hippo4j-agent-plugin/apollo-plugin/pom.xml b/agent/hippo4j-agent-plugin/apollo-plugin/pom.xml
new file mode 100644
index 00000000..90f71b3d
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/apollo-plugin/pom.xml
@@ -0,0 +1,27 @@
+
+
+ 4.0.0
+
+ cn.hippo4j
+ hippo4j-agent-plugin
+ ${revision}
+
+
+ hippo4j-agent-apollo-plugin
+
+
+ 1.9.1
+
+
+
+
+ com.ctrip.framework.apollo
+ apollo-client
+ ${apollo.version}
+ provided
+
+
+
+
\ No newline at end of file
diff --git a/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/boot/ApolloPluginBootService.java b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/boot/ApolloPluginBootService.java
new file mode 100644
index 00000000..20d7551f
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/boot/ApolloPluginBootService.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.apollo.boot;
+
+import cn.hippo4j.agent.core.boot.BootService;
+import cn.hippo4j.agent.core.boot.DefaultImplementor;
+
+/**
+ * Apollo plugin boot service
+ */
+@DefaultImplementor
+public class ApolloPluginBootService implements BootService {
+
+ @Override
+ public void prepare() throws Throwable {
+
+ }
+
+ @Override
+ public void boot() throws Throwable {
+
+ }
+
+ @Override
+ public void onComplete() throws Throwable {
+
+ }
+
+ @Override
+ public void shutdown() throws Throwable {
+
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/define/ApolloInstrumentation.java b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/define/ApolloInstrumentation.java
new file mode 100644
index 00000000..bdf9caaa
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/define/ApolloInstrumentation.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.apollo.define;
+
+import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
+import cn.hippo4j.agent.core.plugin.match.ClassMatch;
+import cn.hippo4j.agent.core.plugin.match.NameMatch;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import static net.bytebuddy.matcher.ElementMatchers.any;
+
+/**
+ * Apollo instrumentation
+ */
+public class ApolloInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+ private static final String ENHANCE_CLASS = "com.ctrip.framework.apollo.internals.DefaultConfig";
+
+ private static final String CONSTRUCTOR_INTERCEPT_CLASS = "cn.hippo4j.agent.plugin.apollo.interceptor.DefaultConfigConstructorInterceptor";
+
+ @Override
+ protected ClassMatch enhanceClass() {
+ return NameMatch.byName(ENHANCE_CLASS);
+ }
+
+ @Override
+ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return new ConstructorInterceptPoint[]{
+ new ConstructorInterceptPoint() {
+
+ @Override
+ public ElementMatcher getConstructorMatcher() {
+ return any();
+ }
+
+ @Override
+ public String getConstructorInterceptor() {
+ return CONSTRUCTOR_INTERCEPT_CLASS;
+ }
+ }
+ };
+ }
+
+ @Override
+ public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[0];
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/DefaultConfigConstructorInterceptor.java b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/DefaultConfigConstructorInterceptor.java
new file mode 100644
index 00000000..e21049a6
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/DefaultConfigConstructorInterceptor.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.apollo.interceptor;
+
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
+
+/**
+ * Default config constructor interceptor
+ */
+public class DefaultConfigConstructorInterceptor implements InstanceConstructorInterceptor {
+
+ @Override
+ public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable {
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/apollo-plugin/src/main/resources/META-INF/services/cn.hippo4j.agent.core.boot.BootService b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/resources/META-INF/services/cn.hippo4j.agent.core.boot.BootService
new file mode 100644
index 00000000..8832c467
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/resources/META-INF/services/cn.hippo4j.agent.core.boot.BootService
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+cn.hippo4j.agent.plugin.apollo.boot.ApolloPluginBootService
\ No newline at end of file
diff --git a/agent/hippo4j-agent-plugin/apollo-plugin/src/main/resources/hippo4j-plugin.def b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/resources/hippo4j-plugin.def
new file mode 100644
index 00000000..ede6239b
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/resources/hippo4j-plugin.def
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apollo-plugin=cn.hippo4j.agent.plugin.apollo.define.ApolloInstrumentation
\ No newline at end of file
diff --git a/agent/hippo4j-agent-plugin/pom.xml b/agent/hippo4j-agent-plugin/pom.xml
new file mode 100644
index 00000000..4a3f7954
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/pom.xml
@@ -0,0 +1,118 @@
+
+
+ 4.0.0
+
+ cn.hippo4j
+ hippo4j-agent
+ ${revision}
+
+
+ hippo4j-agent-plugin
+ pom
+
+ spring-plugins
+ threadpool-plugin
+ adapter-plugins
+ apollo-plugin
+
+
+
+ UTF-8
+
+ net.bytebuddy
+ ${shade.package}.${shade.net.bytebuddy.source}
+
+ ${project.build.directory}${sdk.plugin.related.dir}/../../../hippo4j-agent
+
+ ${agent.package.dest.dir}/plugins
+
+ 1.0b3
+ 1.8.1
+
+
+
+
+ cn.hippo4j
+ hippo4j-agent-core
+ ${project.version}
+ provided
+
+
+ net.bytebuddy
+ byte-buddy
+ provided
+
+
+
+
+
+
+ maven-shade-plugin
+
+
+ package
+
+ shade
+
+
+ false
+ true
+ true
+ true
+
+
+ ${shade.net.bytebuddy.source}
+ ${shade.net.bytebuddy.target}
+
+
+
+
+
+
+
+ maven-antrun-plugin
+
+
+ package
+
+ run
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ant-contrib
+ ant-contrib
+ ${ant-contrib.version}
+
+
+ ant
+ ant
+
+
+
+
+ org.apache.ant
+ ant-nodeps
+ ${ant-nodeps.version}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/pom.xml b/agent/hippo4j-agent-plugin/spring-plugins/pom.xml
new file mode 100644
index 00000000..42b725b5
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ cn.hippo4j
+ hippo4j-agent-plugin
+ ${revision}
+
+
+ hippo4j-agent-spring-plugins
+ pom
+
+
+ spring-boot-1x-plugin
+ spring-boot-2x-plugin
+ spring-plugin-common
+
+
+
+ UTF-8
+ /..
+
+
\ No newline at end of file
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/pom.xml b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/pom.xml
new file mode 100644
index 00000000..b3d2cbf9
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/pom.xml
@@ -0,0 +1,60 @@
+
+
+ 4.0.0
+
+ cn.hippo4j
+ hippo4j-agent-spring-plugins
+ ${revision}
+
+
+ hippo4j-agent-spring-boot-1x-plugin
+
+
+ 1.5.22.RELEASE
+ 1.9.1
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring.boot.version}
+ pom
+ import
+
+
+
+
+
+
+ cn.hippo4j
+ hippo4j-agent-spring-plugin-common
+ provided
+ ${project.version}
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+ ${spring.boot.version}
+ provided
+
+
+
+ cn.hippo4j
+ hippo4j-threadpool-config-spring-boot-1x-starter
+ ${project.version}
+ provided
+
+
+
+ cn.hippo4j
+ dubbo-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/DynamicThreadPoolChangeHandlerSpring1x.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/DynamicThreadPoolChangeHandlerSpring1x.java
new file mode 100644
index 00000000..dc97aaa8
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/DynamicThreadPoolChangeHandlerSpring1x.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.spring.boot.v1;
+
+import cn.hippo4j.common.toolkit.MapUtil;
+import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties;
+import cn.hippo4j.threadpool.dynamic.mode.config.refresher.AbstractConfigThreadPoolDynamicRefresh;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.PropertyValues;
+import org.springframework.beans.support.ResourceEditorRegistrar;
+import org.springframework.boot.bind.CustomPropertyNamePatternsMatcher;
+import org.springframework.boot.bind.RelaxedDataBinder;
+import org.springframework.boot.bind.RelaxedNames;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.env.MapPropertySource;
+import org.springframework.core.env.MutablePropertySources;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static cn.hippo4j.config.springboot1x.starter.refresher.SpringBoot1xBootstrapConfigPropertiesBinderAdapter.getNames;
+
+/**
+ * Dynamic thread pool change handler spring 1x
+ */
+@RequiredArgsConstructor
+public class DynamicThreadPoolChangeHandlerSpring1x extends AbstractConfigThreadPoolDynamicRefresh {
+
+ private final ConfigurableApplicationContext applicationContext;
+
+ @Override
+ public BootstrapConfigProperties buildBootstrapProperties(Map configInfo) {
+ BootstrapConfigProperties bindableCoreProperties = new BootstrapConfigProperties();
+ if (MapUtil.isEmpty(configInfo)) {
+ return bindableCoreProperties;
+ }
+ RelaxedNames relaxedNames = new RelaxedNames(BootstrapConfigProperties.PREFIX);
+ Set names = getNames(bindableCoreProperties, relaxedNames);
+ Map stringConfigInfo = new HashMap<>(configInfo.size());
+ configInfo.forEach((key, value) -> stringConfigInfo.put(key.toString(), value));
+ MapPropertySource test = new MapPropertySource("Hippo4j", stringConfigInfo);
+ MutablePropertySources propertySources = new MutablePropertySources();
+ propertySources.addFirst(test);
+ PropertyValues propertyValues = CustomPropertyNamePatternsMatcher.getPropertySourcesPropertyValues(names, propertySources);
+ RelaxedDataBinder dataBinder = new RelaxedDataBinder(bindableCoreProperties, BootstrapConfigProperties.PREFIX);
+ dataBinder.setAutoGrowCollectionLimit(Integer.MAX_VALUE);
+ dataBinder.setIgnoreNestedProperties(false);
+ dataBinder.setIgnoreInvalidFields(false);
+ dataBinder.setIgnoreUnknownFields(true);
+ ResourceEditorRegistrar resourceEditorRegistrar = new ResourceEditorRegistrar(applicationContext, applicationContext.getEnvironment());
+ resourceEditorRegistrar.registerCustomEditors(dataBinder);
+ dataBinder.bind(propertyValues);
+ return bindableCoreProperties;
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/define/EventPublishingRunListenerInstrumentation.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/define/EventPublishingRunListenerInstrumentation.java
new file mode 100644
index 00000000..06c93998
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/define/EventPublishingRunListenerInstrumentation.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.spring.boot.v1.define;
+
+import cn.hippo4j.agent.core.plugin.WitnessMethod;
+import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
+import cn.hippo4j.agent.core.plugin.match.ClassMatch;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import java.util.Collections;
+import java.util.List;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static cn.hippo4j.agent.core.plugin.match.NameMatch.byName;
+
+/**
+ * Event publishing run listener instrumentation
+ */
+public class EventPublishingRunListenerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+ private static final String ENHANCE_CLASS = "org.springframework.boot.context.event.EventPublishingRunListener";
+
+ private static final String EVENT_PUBLISHING_FINISHED_INTERCEPTOR = "cn.hippo4j.agent.plugin.spring.boot.v1.interceptor.EventPublishingFinishedInterceptor";
+ private static final String EVENT_PUBLISHING_ENVIRONMENT_PREPARED_INTERCEPTOR = "cn.hippo4j.agent.plugin.spring.common.interceptor.EventPublishingRunListenerEnvironmentPreparedInterceptor";
+
+ @Override
+ protected ClassMatch enhanceClass() {
+ return byName(ENHANCE_CLASS);
+ }
+
+ @Override
+ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return new ConstructorInterceptPoint[0];
+ }
+
+ @Override
+ public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[]{
+ new InstanceMethodsInterceptPoint() {
+
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named("finished");
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return EVENT_PUBLISHING_FINISHED_INTERCEPTOR;
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return false;
+ }
+ },
+ new InstanceMethodsInterceptPoint() {
+
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named("environmentPrepared");
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return EVENT_PUBLISHING_ENVIRONMENT_PREPARED_INTERCEPTOR;
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return false;
+ }
+ }
+ };
+ }
+
+ @Override
+ protected List witnessMethods() {
+ return Collections.singletonList(new WitnessMethod("org.springframework.boot.context.event.EventPublishingRunListener",
+ named("finished")));
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/interceptor/EventPublishingFinishedInterceptor.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/interceptor/EventPublishingFinishedInterceptor.java
new file mode 100644
index 00000000..777194f0
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/interceptor/EventPublishingFinishedInterceptor.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.spring.boot.v1.interceptor;
+
+import cn.hippo4j.agent.adapter.dubbo.DubboThreadPoolAdapter;
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import cn.hippo4j.agent.plugin.spring.boot.v1.DynamicThreadPoolChangeHandlerSpring1x;
+import cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader;
+import cn.hippo4j.agent.plugin.spring.common.support.SpringThreadPoolRegisterSupport;
+import cn.hippo4j.threadpool.dynamic.api.ThreadPoolDynamicRefresh;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ConfigurableApplicationContext;
+
+import java.lang.reflect.Method;
+
+/**
+ * Event publishing finished interceptor
+ */
+public class EventPublishingFinishedInterceptor implements InstanceMethodsAroundInterceptor {
+
+ private static final ILog FILE_LOGGER = LogManager.getLogger(EventPublishingFinishedInterceptor.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(EventPublishingFinishedInterceptor.class);
+
+ @Override
+ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
+
+ }
+
+ @Override
+ public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes, Object ret) throws Throwable {
+ ConfigurableApplicationContext context = (ConfigurableApplicationContext) allArguments[0];
+ if (context.getParent() != null) {
+ // After the child container is started, the thread pool registration will be carried out
+ SpringThreadPoolRegisterSupport.registerThreadPoolInstances(context);
+ return ret;
+ }
+ SpringPropertiesLoader.loadSpringProperties(context.getEnvironment());
+ ThreadPoolDynamicRefresh dynamicRefreshSpring1x = new DynamicThreadPoolChangeHandlerSpring1x(context);
+ dynamicRefreshSpring1x.registerListener();
+ DubboThreadPoolAdapter.registerExecutors();
+ return ret;
+ }
+
+ @Override
+ public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes, Throwable t) {
+
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/resources/hippo4j-plugin.def b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/resources/hippo4j-plugin.def
new file mode 100644
index 00000000..8bb37e3a
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/resources/hippo4j-plugin.def
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+spring-boot-1.x=cn.hippo4j.agent.plugin.spring.boot.v1.define.EventPublishingRunListenerInstrumentation
\ No newline at end of file
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/pom.xml b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/pom.xml
new file mode 100644
index 00000000..dc3d87cf
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/pom.xml
@@ -0,0 +1,47 @@
+
+
+ 4.0.0
+
+ cn.hippo4j
+ hippo4j-agent-spring-plugins
+ ${revision}
+
+
+ hippo4j-agent-spring-boot-2x-plugin
+ jar
+
+
+ 2.3.2.RELEASE
+
+
+
+
+ cn.hippo4j
+ hippo4j-agent-spring-plugin-common
+ provided
+ ${project.version}
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+ ${spring.boot.version}
+ provided
+
+
+
+ cn.hippo4j
+ hippo4j-threadpool-dynamic-mode-config
+ ${project.version}
+ provided
+
+
+
+ com.ctrip.framework.apollo
+ apollo-client
+ provided
+
+
+
\ No newline at end of file
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/DynamicThreadPoolChangeHandlerSpring2x.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/DynamicThreadPoolChangeHandlerSpring2x.java
new file mode 100644
index 00000000..bcda15e4
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/DynamicThreadPoolChangeHandlerSpring2x.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.spring.boot.v2;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.plugin.spring.common.conf.SpringBootConfig;
+import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties;
+import cn.hippo4j.threadpool.dynamic.mode.config.refresher.AbstractConfigThreadPoolDynamicRefresh;
+import com.ctrip.framework.apollo.Config;
+import com.ctrip.framework.apollo.ConfigChangeListener;
+import com.ctrip.framework.apollo.ConfigFile;
+import com.ctrip.framework.apollo.ConfigService;
+import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
+import com.ctrip.framework.apollo.model.ConfigChange;
+import org.springframework.boot.context.properties.bind.Bindable;
+import org.springframework.boot.context.properties.bind.Binder;
+import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
+import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static cn.hippo4j.agent.core.conf.Constants.SPRING_BOOT_CONFIG_PREFIX;
+
+/**
+ * Dynamic thread pool change handler spring 2x
+ */
+public class DynamicThreadPoolChangeHandlerSpring2x extends AbstractConfigThreadPoolDynamicRefresh {
+
+ private static ILog LOGGER = LogManager.getLogger(DynamicThreadPoolChangeHandlerSpring2x.class);
+
+ @Override
+ public void registerListener() {
+ List apolloNamespaces = SpringBootConfig.Spring.Dynamic.Thread_Pool.Apollo.NAMESPACE;
+ String namespace = apolloNamespaces.get(0);
+ String configFileType = SpringBootConfig.Spring.Dynamic.Thread_Pool.CONFIG_FILE_TYPE;
+ Config config = ConfigService.getConfig(String.format("%s.%s", namespace, configFileType));
+ ConfigChangeListener configChangeListener = configChangeEvent -> {
+ String replacedNamespace = namespace.replaceAll("." + configFileType, "");
+ ConfigFileFormat configFileFormat = ConfigFileFormat.fromString(configFileType);
+ ConfigFile configFile = ConfigService.getConfigFile(replacedNamespace, configFileFormat);
+ Map newChangeValueMap = new HashMap<>();
+ configChangeEvent.changedKeys().stream().filter(each -> each.contains(SPRING_BOOT_CONFIG_PREFIX)).forEach(each -> {
+ ConfigChange change = configChangeEvent.getChange(each);
+ String newValue = change.getNewValue();
+ newChangeValueMap.put(each, newValue);
+ });
+ dynamicRefresh(configFileType, configFile.getContent(), newChangeValueMap);
+ };
+ config.addChangeListener(configChangeListener);
+ LOGGER.info("[Hippo4j-Agent] Dynamic thread pool refresher, add apollo listener success. namespace: {}", namespace);
+ }
+
+ @Override
+ public BootstrapConfigProperties buildBootstrapProperties(Map configInfo) {
+ BootstrapConfigProperties bindableBootstrapConfigProperties = new BootstrapConfigProperties();
+ ConfigurationPropertySource sources = new MapConfigurationPropertySource(configInfo);
+ Binder binder = new Binder(sources);
+ return binder.bind(BootstrapConfigProperties.PREFIX, Bindable.ofInstance(bindableBootstrapConfigProperties)).get();
+ }
+}
diff --git a/hippo4j-adapter/hippo4j-adapter-kafka/src/main/java/cn/hippo4j/adapter/kafka/KafkaThreadPoolAdapter.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/boot/SpringBootV2PluginBootService.java
similarity index 51%
rename from hippo4j-adapter/hippo4j-adapter-kafka/src/main/java/cn/hippo4j/adapter/kafka/KafkaThreadPoolAdapter.java
rename to agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/boot/SpringBootV2PluginBootService.java
index e27ced8a..c9b8c85b 100644
--- a/hippo4j-adapter/hippo4j-adapter-kafka/src/main/java/cn/hippo4j/adapter/kafka/KafkaThreadPoolAdapter.java
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/boot/SpringBootV2PluginBootService.java
@@ -15,38 +15,38 @@
* limitations under the License.
*/
-package cn.hippo4j.adapter.kafka;
+package cn.hippo4j.agent.plugin.spring.boot.v2.boot;
-import cn.hippo4j.adapter.base.ThreadPoolAdapter;
-import cn.hippo4j.adapter.base.ThreadPoolAdapterParameter;
-import cn.hippo4j.adapter.base.ThreadPoolAdapterState;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.boot.context.event.ApplicationStartedEvent;
-import org.springframework.context.ApplicationListener;
+import cn.hippo4j.agent.core.boot.BootService;
+import cn.hippo4j.agent.core.boot.DefaultImplementor;
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
/**
- * Kafka thread-pool adapter.
+ * SpringBoot v1 plugin boot service
*/
-@Slf4j
-public class KafkaThreadPoolAdapter implements ThreadPoolAdapter, ApplicationListener {
+@DefaultImplementor
+public class SpringBootV2PluginBootService implements BootService {
+
+ private static final ILog LOGGER = LogManager.getLogger(SpringBootV2PluginBootService.class);
@Override
- public String mark() {
- return "Kafka";
+ public void prepare() throws Throwable {
+
}
@Override
- public ThreadPoolAdapterState getThreadPoolState(String identify) {
- return null;
+ public void boot() throws Throwable {
+ LOGGER.info("Loader SpringBootV2PluginBootService...");
}
@Override
- public boolean updateThreadPool(ThreadPoolAdapterParameter threadPoolAdapterParameter) {
- return false;
+ public void onComplete() throws Throwable {
+
}
@Override
- public void onApplicationEvent(ApplicationStartedEvent event) {
+ public void shutdown() throws Throwable {
}
}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/define/EventPublishingRunListenerInstrumentation.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/define/EventPublishingRunListenerInstrumentation.java
new file mode 100644
index 00000000..34d36c39
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/define/EventPublishingRunListenerInstrumentation.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.spring.boot.v2.define;
+
+import cn.hippo4j.agent.core.plugin.WitnessMethod;
+import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
+import cn.hippo4j.agent.core.plugin.match.ClassMatch;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import java.util.Collections;
+import java.util.List;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static cn.hippo4j.agent.core.plugin.match.NameMatch.byName;
+
+/**
+ * Event publishing run listener instrumentation
+ */
+public class EventPublishingRunListenerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+ private static final String ENHANCE_CLASS = "org.springframework.boot.context.event.EventPublishingRunListener";
+
+ private static final String EVENT_PUBLISHING_FINISHED_INTERCEPTOR = "cn.hippo4j.agent.plugin.spring.boot.v2.interceptor.EventPublishingStartedInterceptor";
+ private static final String EVENT_PUBLISHING_ENVIRONMENT_PREPARED_INTERCEPTOR = "cn.hippo4j.agent.plugin.spring.common.interceptor.EventPublishingRunListenerEnvironmentPreparedInterceptor";
+
+ @Override
+ protected ClassMatch enhanceClass() {
+ return byName(ENHANCE_CLASS);
+ }
+
+ @Override
+ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return new ConstructorInterceptPoint[0];
+ }
+
+ @Override
+ public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[]{
+ new InstanceMethodsInterceptPoint() {
+
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named("started");
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return EVENT_PUBLISHING_FINISHED_INTERCEPTOR;
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return false;
+ }
+ },
+ new InstanceMethodsInterceptPoint() {
+
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named("environmentPrepared");
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return EVENT_PUBLISHING_ENVIRONMENT_PREPARED_INTERCEPTOR;
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return false;
+ }
+ }
+ };
+ }
+
+ @Override
+ protected List witnessMethods() {
+ return Collections.singletonList(new WitnessMethod("org.springframework.boot.context.event.EventPublishingRunListener",
+ named("started")));
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/interceptor/EventPublishingStartedInterceptor.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/interceptor/EventPublishingStartedInterceptor.java
new file mode 100644
index 00000000..07184692
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/interceptor/EventPublishingStartedInterceptor.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.spring.boot.v2.interceptor;
+
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import cn.hippo4j.agent.plugin.spring.boot.v2.DynamicThreadPoolChangeHandlerSpring2x;
+import cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader;
+import cn.hippo4j.agent.plugin.spring.common.support.SpringThreadPoolRegisterSupport;
+import cn.hippo4j.common.extension.design.AbstractSubjectCenter;
+import cn.hippo4j.threadpool.dynamic.api.ThreadPoolDynamicRefresh;
+import cn.hippo4j.threadpool.dynamic.mode.config.refresher.event.DynamicThreadPoolRefreshListener;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ConfigurableApplicationContext;
+
+import java.lang.reflect.Method;
+
+/**
+ * Event publishing started interceptor
+ */
+@Slf4j
+public class EventPublishingStartedInterceptor implements InstanceMethodsAroundInterceptor {
+
+ private static final ILog LOGGER = LogManager.getLogger(EventPublishingStartedInterceptor.class);
+
+ @Override
+ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
+
+ }
+
+ @Override
+ public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes, Object ret) throws Throwable {
+ ConfigurableApplicationContext context = (ConfigurableApplicationContext) allArguments[0];
+ if (context.getParent() != null) {
+ // After the child container is started, the thread pool registration will be carried out
+ SpringThreadPoolRegisterSupport.registerThreadPoolInstances(context);
+ return ret;
+ }
+ SpringPropertiesLoader.loadSpringProperties(context.getEnvironment());
+ ThreadPoolDynamicRefresh dynamicRefresh = new DynamicThreadPoolChangeHandlerSpring2x();
+ dynamicRefresh.registerListener();
+ AbstractSubjectCenter.register(AbstractSubjectCenter.SubjectType.THREAD_POOL_DYNAMIC_REFRESH,
+ new DynamicThreadPoolRefreshListener());
+
+ return ret;
+ }
+
+ @Override
+ public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes, Throwable t) {
+
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/resources/META-INF/services/cn.hippo4j.agent.core.boot.BootService b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/resources/META-INF/services/cn.hippo4j.agent.core.boot.BootService
new file mode 100644
index 00000000..687ec9f8
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/resources/META-INF/services/cn.hippo4j.agent.core.boot.BootService
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+cn.hippo4j.agent.plugin.spring.boot.v2.boot.SpringBootV2PluginBootService
\ No newline at end of file
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/resources/hippo4j-plugin.def b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/resources/hippo4j-plugin.def
new file mode 100644
index 00000000..8dad75ea
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/resources/hippo4j-plugin.def
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+spring-boot-2.x=cn.hippo4j.agent.plugin.spring.boot.v2.define.EventPublishingRunListenerInstrumentation
\ No newline at end of file
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml
new file mode 100644
index 00000000..f0336260
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml
@@ -0,0 +1,47 @@
+
+
+ 4.0.0
+
+ cn.hippo4j
+ hippo4j-agent-spring-plugins
+ ${revision}
+
+
+ hippo4j-agent-spring-plugin-common
+
+
+
+ org.springframework
+ spring-core
+ provided
+
+
+ org.springframework
+ spring-context
+ provided
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+ org.slf4j
+ slf4j-api
+
+
+ cn.hippo4j
+ hippo4j-threadpool-dynamic-api
+ ${project.version}
+ provided
+
+
+ cn.hippo4j
+ hippo4j-threadpool-dynamic-core
+ ${project.version}
+ provided
+
+
+
\ No newline at end of file
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/SpringBootConfig.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/SpringBootConfig.java
new file mode 100644
index 00000000..ca979432
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/SpringBootConfig.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.spring.common.conf;
+
+import cn.hippo4j.agent.core.boot.SpringBootConfigNode;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Spring boot config
+ */
+public class SpringBootConfig {
+
+ /**
+ * Spring
+ */
+ public static class Spring {
+
+ /**
+ * Dynamic
+ */
+ public static class Dynamic {
+
+ /**
+ * ThreadPool
+ */
+ @SpringBootConfigNode(root = SpringBootConfig.class)
+ public static class Thread_Pool {
+
+ /**
+ * Apollo
+ */
+ @SpringBootConfigNode(root = SpringBootConfig.class)
+ public static class Apollo {
+
+ public static List NAMESPACE = Arrays.asList("application");
+ }
+
+ /**
+ * Monitor
+ */
+ @SpringBootConfigNode(root = SpringBootConfig.class)
+ public static class Monitor {
+
+ public static Boolean enable = Boolean.TRUE;
+
+ public static String collectTypes = "micrometer";
+
+ public static String threadPoolTypes = "dynamic";
+
+ public static Long initialDelay = 10000L;
+
+ public static Long collectInterval = 5000L;
+ }
+
+ public static String CONFIG_FILE_TYPE;
+ }
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/define/SpringApplicationInstrumentation.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/define/SpringApplicationInstrumentation.java
new file mode 100644
index 00000000..bcd7f5c3
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/define/SpringApplicationInstrumentation.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.spring.common.define;
+
+import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
+import cn.hippo4j.agent.core.plugin.match.ClassMatch;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import static cn.hippo4j.agent.core.plugin.match.NameMatch.byName;
+import static net.bytebuddy.matcher.ElementMatchers.named;
+
+public class SpringApplicationInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+ private static final String ENHANCE_CLASS = "org.springframework.boot.SpringApplication";
+
+ private static final String RUN_INTERCEPTOR = "cn.hippo4j.agent.plugin.spring.common.interceptor.SpringApplicationRunInterceptor";
+
+ @Override
+ protected ClassMatch enhanceClass() {
+ return byName(ENHANCE_CLASS);
+ }
+
+ @Override
+ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return new ConstructorInterceptPoint[0];
+ }
+
+ @Override
+ public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[]{
+ new InstanceMethodsInterceptPoint() {
+
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named("run");
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return RUN_INTERCEPTOR;
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return false;
+ }
+ }
+ };
+ }
+
+ @Override
+ protected String[] witnessClasses() {
+ return new String[]{"org.springframework.boot.SpringApplication"};
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/interceptor/EventPublishingRunListenerEnvironmentPreparedInterceptor.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/interceptor/EventPublishingRunListenerEnvironmentPreparedInterceptor.java
new file mode 100644
index 00000000..2b70e22a
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/interceptor/EventPublishingRunListenerEnvironmentPreparedInterceptor.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.spring.common.interceptor;
+
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import cn.hippo4j.agent.plugin.spring.common.support.SpringEnvironmentSupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.env.ConfigurableEnvironment;
+
+import java.lang.reflect.Method;
+
+/**
+ * Event publishing run listener environment prepared interceptor
+ */
+public class EventPublishingRunListenerEnvironmentPreparedInterceptor implements InstanceMethodsAroundInterceptor {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EventPublishingRunListenerEnvironmentPreparedInterceptor.class);
+
+ @Override
+ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
+
+ }
+
+ @Override
+ public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes, Object ret) throws Throwable {
+ ConfigurableEnvironment environment = (ConfigurableEnvironment) allArguments[0];
+ SpringEnvironmentSupport.disableNonAgentSwitch(environment);
+ LOGGER.info("[Hippo4j-Agent] Switch off in non-Agent mode.");
+ return ret;
+ }
+
+ @Override
+ public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes, Throwable t) {
+
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringEnvironmentSupport.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringEnvironmentSupport.java
new file mode 100644
index 00000000..4733acdc
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringEnvironmentSupport.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.spring.common.support;
+
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.MapPropertySource;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Spring environment support
+ */
+public class SpringEnvironmentSupport {
+
+ public static void disableNonAgentSwitch(ConfigurableEnvironment environment) {
+ Map map = new HashMap<>();
+ map.put("spring.dynamic.thread-pool.enable", false); // Switch off in non-Agent mode
+ MapPropertySource propertySource = new MapPropertySource("Hippo4j-Agent-Properties", map);
+ environment.getPropertySources().addFirst(propertySource);
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringPropertiesLoader.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringPropertiesLoader.java
new file mode 100644
index 00000000..bebfb843
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringPropertiesLoader.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.spring.common.support;
+
+import cn.hippo4j.agent.core.boot.SpringBootConfigInitializer;
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.EnumerablePropertySource;
+import org.springframework.core.env.PropertySource;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Spring properties loader
+ */
+public class SpringPropertiesLoader {
+
+ private static final ILog LOGGER = LogManager.getLogger(SpringPropertiesLoader.class);
+
+ public static void loadSpringProperties(ConfigurableEnvironment environment) {
+ Iterator> iterator = environment.getPropertySources().iterator();
+ Properties properties = new Properties();
+ List> propertySourceList = new ArrayList<>();
+ while (iterator.hasNext()) {
+ propertySourceList.add(iterator.next());
+ }
+ for (int i = propertySourceList.size() - 1; i >= 0; i--) {
+ PropertySource> propertySource = propertySourceList.get(i);
+ if (!(propertySource instanceof EnumerablePropertySource)) {
+ LOGGER.warn("Skip propertySource[{}] because {} not enumerable.", propertySource.getName(), propertySource.getClass());
+ continue;
+ }
+ LOGGER.info("Load propertySource[{}] into SpringProperties.", propertySource.getName());
+ EnumerablePropertySource> enumerablePropertySource = (EnumerablePropertySource>) propertySource;
+ String[] keys = enumerablePropertySource.getPropertyNames();
+ for (String key : keys) {
+ Object value = null;
+ try {
+ value = enumerablePropertySource.getProperty(key);
+ if (value != null) {
+ properties.put(key.toLowerCase(), value.toString());
+ }
+ } catch (Throwable e) {
+ LOGGER.warn("Put property to spring properties failed, key=[{}], value=[{}]", key, value);
+ }
+ }
+ }
+ SpringBootConfigInitializer.setSpringProperties(properties);
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringThreadPoolRegisterSupport.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringThreadPoolRegisterSupport.java
new file mode 100644
index 00000000..65a9f4cb
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringThreadPoolRegisterSupport.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.spring.common.support;
+
+import cn.hippo4j.agent.core.util.ReflectUtil;
+import cn.hippo4j.common.executor.ThreadPoolExecutorRegistry;
+import cn.hippo4j.common.executor.support.BlockingQueueTypeEnum;
+import cn.hippo4j.common.executor.support.RejectedPolicyTypeEnum;
+import cn.hippo4j.common.handler.DynamicThreadPoolAdapterChoose;
+import cn.hippo4j.common.model.executor.ExecutorProperties;
+import cn.hippo4j.common.toolkit.BooleanUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContext;
+
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * Spring thread pool register support
+ */
+public class SpringThreadPoolRegisterSupport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SpringThreadPoolRegisterSupport.class);
+
+ public static void registerThreadPoolInstances(ApplicationContext context) {
+ Map> referencedClassMap = ThreadPoolExecutorRegistry.REFERENCED_CLASS_MAP;
+ for (Map.Entry> entry : referencedClassMap.entrySet()) {
+ ThreadPoolExecutor enhancedInstance = entry.getKey();
+ Class> declaredClass = entry.getValue();
+ List declaredFields = ReflectUtil.getStaticFieldsFromType(declaredClass, ThreadPoolExecutor.class);
+ for (Field field : declaredFields) {
+ try {
+ Object value = field.get(null);
+ if (value == enhancedInstance) {
+ String threadPoolId = declaredClass.getName() + "#" + field.getName();
+ register(threadPoolId, enhancedInstance);
+ break;
+ }
+ } catch (IllegalAccessException e) {
+ LOGGER.error("Get static field error.", e);
+ }
+ }
+ }
+
+ Map beansWithAnnotation = context.getBeansOfType(Executor.class);
+ for (Map.Entry entry : beansWithAnnotation.entrySet()) {
+ String beanName = entry.getKey();
+ Executor bean = entry.getValue();
+ ThreadPoolExecutor executor;
+ if (DynamicThreadPoolAdapterChoose.match(bean)) {
+ executor = DynamicThreadPoolAdapterChoose.unwrap(bean);
+ } else {
+ executor = (ThreadPoolExecutor) bean;
+ }
+ if (executor == null) {
+ LOGGER.warn("[Hippo4j-Agent] Thread pool is null, ignore bean registration. beanName={}, beanClass={}", beanName, bean.getClass().getName());
+ } else {
+ register(beanName, executor);
+ }
+ }
+ LOGGER.info("[Hippo4j-Agent] Registered thread pool instances successfully.");
+ }
+
+ public static void register(String threadPoolId, ThreadPoolExecutor executor) {
+ if (executor == null) {
+ return;
+ }
+ ExecutorProperties executorProperties = ExecutorProperties.builder()
+ .threadPoolId(threadPoolId)
+ .corePoolSize(executor.getCorePoolSize())
+ .maximumPoolSize(executor.getMaximumPoolSize())
+ .allowCoreThreadTimeOut(BooleanUtil.toBoolean(String.valueOf(executor.allowsCoreThreadTimeOut())))
+ .blockingQueue(BlockingQueueTypeEnum.getBlockingQueueTypeEnumByName(executor.getQueue().getClass().getSimpleName()).getName())
+ .queueCapacity(executor.getQueue().remainingCapacity())
+ .rejectedHandler(RejectedPolicyTypeEnum.getRejectedPolicyTypeEnumByName(executor.getRejectedExecutionHandler().getClass().getSimpleName()).getName())
+ .build();
+ ThreadPoolExecutorRegistry.putHolder(threadPoolId, executor, executorProperties);
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/threadpool-plugin/pom.xml b/agent/hippo4j-agent-plugin/threadpool-plugin/pom.xml
new file mode 100644
index 00000000..b571302e
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/threadpool-plugin/pom.xml
@@ -0,0 +1,27 @@
+
+
+ 4.0.0
+
+ cn.hippo4j
+ hippo4j-agent-plugin
+ ${revision}
+
+
+ hippo4j-agent-threadpool-plugin
+
+
+
+ cn.hippo4j
+ hippo4j-threadpool-core
+ ${project.version}
+ provided
+
+
+ cn.hippo4j
+ hippo4j-threadpool-infra-common
+ ${project.version}
+
+
+
\ No newline at end of file
diff --git a/agent/hippo4j-agent-plugin/threadpool-plugin/src/main/java/cn/hippo4j/agent/plugin/thread/pool/define/ThreadPoolExecutorInstrumentation.java b/agent/hippo4j-agent-plugin/threadpool-plugin/src/main/java/cn/hippo4j/agent/plugin/thread/pool/define/ThreadPoolExecutorInstrumentation.java
new file mode 100644
index 00000000..0480cdc7
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/threadpool-plugin/src/main/java/cn/hippo4j/agent/plugin/thread/pool/define/ThreadPoolExecutorInstrumentation.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.thread.pool.define;
+
+import cn.hippo4j.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
+import cn.hippo4j.agent.core.plugin.match.ClassMatch;
+import cn.hippo4j.agent.core.plugin.match.NameMatch;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
+
+/**
+ * Thread pool executor instrumentation
+ */
+public class ThreadPoolExecutorInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+ private static final String ENHANCE_CLASS = "java.util.concurrent.ThreadPoolExecutor";
+
+ private static final String CONSTRUCTOR_INTERCEPT_CLASS = "cn.hippo4j.agent.plugin.thread.pool.interceptor.ThreadPoolExecutorConstructorMethodInterceptor";
+
+ private static final int CONSTRUCTOR_INTERCEPT_PARAMETER_LENGTH = 7;
+
+ @Override
+ protected ClassMatch enhanceClass() {
+ return NameMatch.byName(ENHANCE_CLASS);
+ }
+
+ @Override
+ public boolean isBootstrapInstrumentation() {
+ return true;
+ }
+
+ /**
+ * The constructor method that only intercepts all parameters prevents repeated interception.
+ */
+ @Override
+ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return new ConstructorInterceptPoint[]{
+ new ConstructorInterceptPoint() {
+
+ @Override
+ public ElementMatcher getConstructorMatcher() {
+ return takesArguments(CONSTRUCTOR_INTERCEPT_PARAMETER_LENGTH);
+ }
+
+ @Override
+ public String getConstructorInterceptor() {
+ return CONSTRUCTOR_INTERCEPT_CLASS;
+ }
+ }
+ };
+ }
+
+ @Override
+ public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[0];
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/threadpool-plugin/src/main/java/cn/hippo4j/agent/plugin/thread/pool/interceptor/ThreadPoolExecutorConstructorMethodInterceptor.java b/agent/hippo4j-agent-plugin/threadpool-plugin/src/main/java/cn/hippo4j/agent/plugin/thread/pool/interceptor/ThreadPoolExecutorConstructorMethodInterceptor.java
new file mode 100644
index 00000000..fb4194c8
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/threadpool-plugin/src/main/java/cn/hippo4j/agent/plugin/thread/pool/interceptor/ThreadPoolExecutorConstructorMethodInterceptor.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.thread.pool.interceptor;
+
+import cn.hippo4j.agent.core.conf.Config;
+import cn.hippo4j.agent.core.logging.api.ILog;
+import cn.hippo4j.agent.core.logging.api.LogManager;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
+import cn.hippo4j.common.executor.ThreadPoolExecutorRegistry;
+import cn.hippo4j.agent.core.util.CollectionUtil;
+import cn.hippo4j.agent.core.util.StringUtil;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * Thread pool executor constructor method interceptor
+ */
+public class ThreadPoolExecutorConstructorMethodInterceptor implements InstanceConstructorInterceptor {
+
+ private static final ILog LOGGER = LogManager.getLogger(ThreadPoolExecutorConstructorMethodInterceptor.class);
+
+ private static final List EXCLUDE_STACK_TRACE_ELEMENT_CLASS_PREFIX = Arrays.asList("java", "cn.hippo4j.agent");
+
+ private static final int INITIAL_CAPACITY = 3;
+
+ @Override
+ public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable {
+
+ List stackTraceElements = getStackTraceElements();
+ if (CollectionUtil.isEmpty(stackTraceElements)) {
+ return;
+ }
+ StackTraceElement declaredClassStackTraceElement = stackTraceElements.get(0);
+ String declaredClassName = declaredClassStackTraceElement.getClassName();
+ Class> declaredClass = Thread.currentThread().getContextClassLoader().loadClass(declaredClassName);
+ ThreadPoolExecutorRegistry.REFERENCED_CLASS_MAP.put((ThreadPoolExecutor) objInst, declaredClass);
+ }
+
+ private List getStackTraceElements() {
+ StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
+ int i;
+ for (i = 0; i < stackTraceElements.length; i++) {
+ String fullClassName = stackTraceElements[i].getClassName();
+ if (isBusinessStackTraceClassName(fullClassName)) {
+ if (isExcludeThreadPoolClass(fullClassName)) {
+ return Collections.emptyList();
+ } else {
+ break;
+ }
+ }
+ }
+
+ List result = new ArrayList<>(INITIAL_CAPACITY); // Find up to three layers
+ for (int j = 0; i < stackTraceElements.length && j < INITIAL_CAPACITY; i++, j++) {
+ String fullClassName = stackTraceElements[i].getClassName();
+ if (isExcludeThreadPoolClass(fullClassName)) {
+ break;
+ } else {
+ result.add(stackTraceElements[i]);
+ }
+ }
+
+ return result;
+ }
+
+ private boolean isBusinessStackTraceClassName(String className) {
+ for (String prefix : EXCLUDE_STACK_TRACE_ELEMENT_CLASS_PREFIX) {
+ if (className.startsWith(prefix)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ private boolean isExcludeThreadPoolClass(String className) {
+ if (StringUtil.isBlank(className)) {
+ return true;
+ }
+ List excludePackagePrefix = Config.Plugin.ThreadPool.EXCLUDE_PACKAGE_PREFIX;
+ for (String excludePrefix : excludePackagePrefix) {
+ if (className.startsWith(excludePrefix)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/threadpool-plugin/src/main/resources/hippo4j-plugin.def b/agent/hippo4j-agent-plugin/threadpool-plugin/src/main/resources/hippo4j-plugin.def
new file mode 100644
index 00000000..d84d9489
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/threadpool-plugin/src/main/resources/hippo4j-plugin.def
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+thread-pool-plugin=cn.hippo4j.agent.plugin.thread.pool.define.ThreadPoolExecutorInstrumentation
\ No newline at end of file
diff --git a/agent/pom.xml b/agent/pom.xml
new file mode 100644
index 00000000..0d899eab
--- /dev/null
+++ b/agent/pom.xml
@@ -0,0 +1,271 @@
+
+
+ 4.0.0
+
+ cn.hippo4j
+ hippo4j-all
+ ${revision}
+
+ hippo4j-agent
+ pom
+
+
+ hippo4j-agent-core
+ hippo4j-agent-plugin
+ hippo4j-agent-bootstrap
+
+
+
+ cn.hippo4j.agent.dependencies
+ 1.8
+ 4.13.1
+ 2.0.7
+ 3.5.13
+ 1.18.20
+
+
+ 1.12.13
+ 1.44.0
+ 4.1.79.Final
+ 2.8.9
+ 1.6.2
+ 1.3.2
+ 3.1
+
+ 6.0.53
+
+
+ 0.6.1
+ 1.6.0
+ 1.8
+ 2.10
+ 2.8.2
+ 3.1.0
+ 2.22.0
+ 3.2.0
+ 3.1.0
+ 3.1.1
+ 3.0.0-M2
+ 3.8.0
+ 3.1.0
+ 3.0.1
+ 2.5
+ 4.3.0
+ 3.1.0
+ 1.33
+ 1.5
+ true
+ 1.7.25
+ 30.1.1-jre
+ 2.16.0
+
+
+
+
+
+ cn.hippo4j
+ hippo4j-agent-core
+ ${project.version}
+
+
+ cn.hippo4j
+ hippo4j-threadpool-common
+ ${project.version}
+ provided
+
+
+ com.google.code.gson
+ gson
+ ${gson.version}
+
+
+ com.google.guava
+ guava
+ ${guava.version}
+
+
+ net.bytebuddy
+ byte-buddy
+ ${bytebuddy.version}
+
+
+ org.slf4j
+ slf4j-api
+ ${slf4j.version}
+
+
+ org.openjdk.jmh
+ jmh-core
+ ${jmh.version}
+ test
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
+
+ javax.annotation
+ javax.annotation-api
+ ${javax.annotation-api.version}
+ provided
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+ org.powermock
+ powermock-module-junit4
+ ${powermock.version}
+ test
+
+
+ org.powermock
+ powermock-api-mockito2
+ ${powermock.version}
+ test
+
+
+ net.bytebuddy
+ byte-buddy-agent
+ ${bytebuddy.version}
+
+
+ org.objenesis
+ objenesis
+ ${objenesis.version}
+ test
+
+
+ com.github.tomakehurst
+ wiremock
+ ${wiremock.version}
+ test
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ ${jmh.version}
+ test
+
+
+ cn.hippo4j
+ dubbo-plugin
+ ${project.version}
+
+
+
+
+
+ ${project.artifactId}-${project.version}
+
+
+
+
+
+ io.takari
+ maven
+ ${takari-maven-plugin.version}
+
+
+ maven-antrun-plugin
+ ${maven-antrun-plugin.version}
+
+
+ maven-deploy-plugin
+ ${maven-deploy-plugin.version}
+
+
+ maven-assembly-plugin
+ ${maven-assembly-plugin.version}
+
+
+ maven-failsafe-plugin
+ ${maven-failsafe-plugin.version}
+
+
+ maven-jar-plugin
+ ${maven-jar-plugin.version}
+
+ true
+
+
+
+ maven-shade-plugin
+ ${maven-shade-plugin.version}
+
+
+
+
+
+ kr.motd.maven
+ os-maven-plugin
+ ${os-maven-plugin.version}
+
+
+ initialize
+
+ detect
+
+
+
+
+
+ maven-enforcer-plugin
+ ${maven-enforcer-plugin.version}
+
+
+ enforce-java
+
+ enforce
+
+ validate
+
+
+