Complete the agent development under SpringBoot2.x version. (#1241)

* Complete the agent development under SpringBoot2.x version.

* Rename the '@SpringBootPluginConfig' annotation.
pull/1244/head
yanrongzhen 1 year ago committed by GitHub
parent 70295dcd2f
commit ad3d103776
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -111,11 +111,11 @@ collector.is_resolve_dns_periodically=${SW_AGENT_COLLECTOR_IS_RESOLVE_DNS_PERIOD
# Logging level
logging.level=${SW_LOGGING_LEVEL:INFO}
# Logging file_name
logging.file_name=${SW_LOGGING_FILE_NAME:skywalking-api.log}
logging.file_name=${SW_LOGGING_FILE_NAME:hippo4j-api.log}
# Log output. Default is FILE. Use CONSOLE means output to stdout.
logging.output=${SW_LOGGING_OUTPUT:FILE}
# Log files directory. Default is blank string, meaning use "{theSkywalkingAgentJarDir}/logs " to output logs.
# {theSkywalkingAgentJarDir} is the directory where the skywalking agent jar file is located
# Log files directory. Default is blank string, meaning use "{theHippo4jAgentJarDir}/logs " to output logs.
# {theHippo4jAgentJarDir} is the directory where the hippo4j agent jar file is located
logging.dir=${SW_LOGGING_DIR:}
# Logger resolver: PATTERN or JSON. The default is PATTERN, which uses logging.pattern to print traditional text logs.
# JSON resolver prints logs in JSON format.

@ -96,7 +96,7 @@
</goals>
<configuration>
<target>
<delete dir="${project.basedir}/../skywalking-agent" />
<delete dir="${project.basedir}/../hippo4j-agent" />
</target>
</configuration>
</execution>

@ -48,7 +48,7 @@ public class SpringBootConfigInitializer {
return SPRING_PROPERTIES == null || SPRING_PROPERTIES.isEmpty();
}
public static synchronized void initializeConfig(SpringBootConfig springBootConfig) {
public static synchronized void initializeConfig(SpringBootConfigNode springBootConfig) {
if (SPRING_PROPERTIES != null) {
try {
LOG.info("initialize Spring Config Class {}.", springBootConfig.root());

@ -24,7 +24,7 @@ import java.lang.annotation.Target;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SpringBootConfig {
public @interface SpringBootConfigNode {
/**
* @return Class as the root to do config initialization.

@ -20,7 +20,7 @@ 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.SpringBootConfig;
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;
@ -171,7 +171,7 @@ public class AgentClassLoader extends ClassLoader {
SnifferConfigInitializer.initializeConfig(pluginConfig.root());
}
final SpringBootConfig springBootConfig = loadedClass.getAnnotation(SpringBootConfig.class);
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

@ -31,7 +31,7 @@ public class ApolloInstrumentation extends ClassInstanceMethodsEnhancePluginDefi
private static final String ENHANCE_CLASS = "com.ctrip.framework.apollo.internals.DefaultConfig";
private static final String CONSTRUCTOR_INTERCEPT_CLASS = "cn.hippo4j.agent.plugin.apollo.DefaultConfigConstructorInterceptor";
private static final String CONSTRUCTOR_INTERCEPT_CLASS = "cn.hippo4j.agent.plugin.apollo.interceptor.DefaultConfigConstructorInterceptor";
@Override
protected ClassMatch enhanceClass() {

@ -15,7 +15,7 @@
* limitations under the License.
*/
package cn.hippo4j.agent.plugin.apollo;
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;

@ -42,19 +42,6 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>${apollo.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-config-spring-boot-1x-starter</artifactId>

@ -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.boot.v1;
import cn.hippo4j.agent.plugin.spring.common.support.AbstractDynamicThreadPoolChangeHandlerSpring;
import cn.hippo4j.common.toolkit.MapUtil;
import cn.hippo4j.config.springboot.starter.config.BootstrapConfigProperties;
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.ApplicationContext;
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.SpringBoot1xBootstrapConfigPropertiesBinderAdapt.getNames;
public class DynamicThreadPoolChangeHandlerSpring1x extends AbstractDynamicThreadPoolChangeHandlerSpring {
public DynamicThreadPoolChangeHandlerSpring1x(ConfigurableApplicationContext context) {
super(context);
}
protected BootstrapConfigProperties bindProperties(Map<Object, Object> configInfo, ApplicationContext applicationContext) {
BootstrapConfigProperties bindableCoreProperties = new BootstrapConfigProperties();
if (MapUtil.isEmpty(configInfo)) {
return bindableCoreProperties;
}
RelaxedNames relaxedNames = new RelaxedNames(BootstrapConfigProperties.PREFIX);
Set<String> names = getNames(bindableCoreProperties, relaxedNames);
Map<String, Object> 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;
}
}

@ -31,8 +31,8 @@ public class EventPublishingRunListenerInstrumentation extends ClassInstanceMeth
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.EventPublishingFinishedInterceptor";
private static final String EVENT_PUBLISHING_ENVIRONMENT_PREPARED_INTERCEPTOR = "cn.hippo4j.agent.plugin.spring.boot.v1.EventPublishingRunListenerEnvironmentPreparedInterceptor";
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() {

@ -1,68 +0,0 @@
/*
* 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.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 SpringApplicationRunInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
private static final String ENHANCE_CLASS = "org.springframework.boot.SpringApplication";
private static final String SPRING_APPLICATION_RUN_INTERCEPTOR = "cn.hippo4j.agent.plugin.spring.boot.v1.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<MethodDescription> getMethodsMatcher() {
return named("run");
}
@Override
public String getMethodsInterceptor() {
return SPRING_APPLICATION_RUN_INTERCEPTOR;
}
@Override
public boolean isOverrideArgs() {
return false;
}
}
};
}
}

@ -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.plugin.spring.boot.v1.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.v1.DynamicThreadPoolChangeHandlerSpring1x;
import cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader;
import cn.hippo4j.agent.plugin.spring.common.support.IDynamicThreadPoolChangeHandlerSpring;
import cn.hippo4j.agent.plugin.spring.common.support.SpringThreadPoolRegisterSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import java.lang.reflect.Method;
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();
return ret;
}
SpringPropertiesLoader.loadSpringProperties(context.getEnvironment());
IDynamicThreadPoolChangeHandlerSpring handlerSpring1x = new DynamicThreadPoolChangeHandlerSpring1x(context);
handlerSpring1x.registerApolloConfigHandler();
return ret;
}
@Override
public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t) {
}
}

@ -14,5 +14,4 @@
# 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
spring-boot-1.x=cn.hippo4j.agent.plugin.spring.boot.v1.define.SpringApplicationRunInstrumentation
spring-boot-1.x=cn.hippo4j.agent.plugin.spring.boot.v1.define.EventPublishingRunListenerInstrumentation

@ -25,9 +25,15 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring.boot.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-config-spring-boot-starter</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

@ -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.plugin.spring.boot.v2;
import cn.hippo4j.agent.plugin.spring.common.support.AbstractDynamicThreadPoolChangeHandlerSpring;
import cn.hippo4j.config.springboot.starter.config.BootstrapConfigProperties;
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 org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.Map;
public class DynamicThreadPoolChangeHandlerSpring2x extends AbstractDynamicThreadPoolChangeHandlerSpring {
public DynamicThreadPoolChangeHandlerSpring2x(ConfigurableApplicationContext context) {
super(context);
}
@Override
protected BootstrapConfigProperties bindProperties(Map<Object, Object> configInfo, ApplicationContext applicationContext) {
BootstrapConfigProperties bindableBootstrapConfigProperties = new BootstrapConfigProperties();
ConfigurationPropertySource sources = new MapConfigurationPropertySource(configInfo);
Binder binder = new Binder(sources);
return binder.bind(BootstrapConfigProperties.PREFIX, Bindable.ofInstance(bindableBootstrapConfigProperties)).get();
}
}

@ -31,7 +31,8 @@ public class EventPublishingRunListenerInstrumentation extends ClassInstanceMeth
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.EventPublishingStartedInterceptor";
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() {
@ -58,6 +59,23 @@ public class EventPublishingRunListenerInstrumentation extends ClassInstanceMeth
return EVENT_PUBLISHING_FINISHED_INTERCEPTOR;
}
@Override
public boolean isOverrideArgs() {
return false;
}
},
new InstanceMethodsInterceptPoint() {
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
return named("environmentPrepared");
}
@Override
public String getMethodsInterceptor() {
return EVENT_PUBLISHING_ENVIRONMENT_PREPARED_INTERCEPTOR;
}
@Override
public boolean isOverrideArgs() {
return false;

@ -15,14 +15,17 @@
* limitations under the License.
*/
package cn.hippo4j.agent.plugin.spring.boot.v2;
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.common.SpringPropertiesLoader;
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.IDynamicThreadPoolChangeHandlerSpring;
import cn.hippo4j.agent.plugin.spring.common.support.SpringThreadPoolRegisterSupport;
import org.springframework.context.ConfigurableApplicationContext;
import java.lang.reflect.Method;
@ -39,7 +42,15 @@ public class EventPublishingStartedInterceptor implements InstanceMethodsAroundI
@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();
return ret;
}
SpringPropertiesLoader.loadSpringProperties(context.getEnvironment());
IDynamicThreadPoolChangeHandlerSpring handler = new DynamicThreadPoolChangeHandlerSpring2x(context);
handler.registerApolloConfigHandler();
return ret;
}

@ -17,6 +17,31 @@
<artifactId>spring-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>${apollo.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cn.hippo4j</groupId>
<artifactId>hippo4j-config-spring-boot-starter</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

@ -15,23 +15,23 @@
* limitations under the License.
*/
package cn.hippo4j.agent.plugin.spring.boot.v1;
package cn.hippo4j.agent.plugin.spring.common.conf;
import cn.hippo4j.agent.core.boot.SpringBootConfig;
import cn.hippo4j.agent.core.boot.SpringBootConfigNode;
import java.util.Arrays;
import java.util.List;
public class ApolloSpringBootProperties {
public class SpringBootConfig {
public static class Spring {
public static class Dynamic {
@SpringBootConfig(root = ApolloSpringBootProperties.class)
@SpringBootConfigNode(root = SpringBootConfig.class)
public static class Thread_Pool {
@SpringBootConfig(root = ApolloSpringBootProperties.class)
@SpringBootConfigNode(root = SpringBootConfig.class)
public static class Apollo {
public static List<String> NAMESPACE = Arrays.asList("application");

@ -15,12 +15,12 @@
* limitations under the License.
*/
package cn.hippo4j.agent.plugin.spring.boot.v1;
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.SpringEnvironmentSupport;
import cn.hippo4j.agent.plugin.spring.common.support.SpringEnvironmentSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.ConfigurableEnvironment;

@ -15,25 +15,19 @@
* limitations under the License.
*/
package cn.hippo4j.agent.plugin.spring.boot.v1;
package cn.hippo4j.agent.plugin.spring.common.support;
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.core.registry.AgentThreadPoolExecutorHolder;
import cn.hippo4j.agent.core.registry.AgentThreadPoolInstanceRegistry;
import cn.hippo4j.agent.core.util.ThreadPoolPropertyKey;
import cn.hippo4j.agent.plugin.spring.common.SpringPropertiesLoader;
import cn.hippo4j.agent.plugin.spring.common.conf.SpringBootConfig;
import cn.hippo4j.common.config.ExecutorProperties;
import cn.hippo4j.common.executor.support.BlockingQueueTypeEnum;
import cn.hippo4j.common.executor.support.RejectedPolicyTypeEnum;
import cn.hippo4j.common.executor.support.ResizableCapacityLinkedBlockingQueue;
import cn.hippo4j.common.toolkit.CollectionUtil;
import cn.hippo4j.common.toolkit.MapUtil;
import cn.hippo4j.common.toolkit.ThreadPoolExecutorUtil;
import cn.hippo4j.config.springboot.starter.config.BootstrapConfigProperties;
import cn.hippo4j.common.config.ExecutorProperties;
import cn.hippo4j.config.springboot.starter.parser.ConfigFileTypeEnum;
import cn.hippo4j.config.springboot.starter.parser.ConfigParserHandler;
import cn.hippo4j.core.executor.DynamicThreadPoolExecutor;
@ -45,24 +39,15 @@ import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import com.ctrip.framework.apollo.model.ConfigChange;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@ -70,27 +55,23 @@ import java.util.concurrent.TimeUnit;
import static cn.hippo4j.agent.core.conf.Constants.SPRING_BOOT_CONFIG_PREFIX;
import static cn.hippo4j.common.constant.ChangeThreadPoolConstants.CHANGE_DELIMITER;
import static cn.hippo4j.common.constant.ChangeThreadPoolConstants.CHANGE_THREAD_POOL_TEXT;
import static cn.hippo4j.config.springboot1x.starter.refresher.SpringBoot1xBootstrapConfigPropertiesBinderAdapt.getNames;
public class EventPublishingFinishedInterceptor implements InstanceMethodsAroundInterceptor {
public abstract class AbstractDynamicThreadPoolChangeHandlerSpring implements IDynamicThreadPoolChangeHandlerSpring {
private static final ILog FILE_LOGGER = LogManager.getLogger(EventPublishingFinishedInterceptor.class);
private static final Logger LOGGER = LoggerFactory.getLogger(EventPublishingFinishedInterceptor.class);
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDynamicThreadPoolChangeHandlerSpring.class);
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
private final ConfigurableApplicationContext applicationContext;
public AbstractDynamicThreadPoolChangeHandlerSpring(ConfigurableApplicationContext context) {
this.applicationContext = context;
}
@Override
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object ret) throws Throwable {
ConfigurableApplicationContext context = (ConfigurableApplicationContext) allArguments[0];
SpringPropertiesLoader.loadSpringProperties(context.getEnvironment());
List<String> apolloNamespaces = ApolloSpringBootProperties.Spring.Dynamic.Thread_Pool.Apollo.NAMESPACE;
public void registerApolloConfigHandler() {
List<String> apolloNamespaces = SpringBootConfig.Spring.Dynamic.Thread_Pool.Apollo.NAMESPACE;
String namespace = apolloNamespaces.get(0);
String configFileType = ApolloSpringBootProperties.Spring.Dynamic.Thread_Pool.CONFIG_FILE_TYPE;
String configFileType = SpringBootConfig.Spring.Dynamic.Thread_Pool.CONFIG_FILE_TYPE;
com.ctrip.framework.apollo.Config config = ConfigService.getConfig(String.format("%s.%s", namespace, configFileType));
ConfigChangeListener configChangeListener = configChangeEvent -> {
String replacedNamespace = namespace.replaceAll("." + configFileType, "");
@ -102,40 +83,17 @@ public class EventPublishingFinishedInterceptor implements InstanceMethodsAround
String newValue = change.getNewValue();
newChangeValueMap.put(each, newValue);
});
dynamicRefresh(configFile.getContent(), newChangeValueMap, context);
dynamicRefresh(configFile.getContent(), newChangeValueMap, applicationContext);
};
config.addChangeListener(configChangeListener);
LOGGER.info("Dynamic thread pool refresher, add apollo listener success. namespace: {}", namespace);
return ret;
LOGGER.info("[Hippo4j-Agent] Dynamic thread pool refresher, add apollo listener success. namespace: {}", namespace);
}
public BootstrapConfigProperties bindProperties(Map<Object, Object> configInfo, ApplicationContext applicationContext) {
BootstrapConfigProperties bindableCoreProperties = new BootstrapConfigProperties();
if (MapUtil.isEmpty(configInfo)) {
return bindableCoreProperties;
}
RelaxedNames relaxedNames = new RelaxedNames(BootstrapConfigProperties.PREFIX);
Set<String> names = getNames(bindableCoreProperties, relaxedNames);
Map<String, Object> 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;
}
protected abstract BootstrapConfigProperties bindProperties(Map<Object, Object> configInfo, ApplicationContext applicationContext);
public void dynamicRefresh(String configContent, Map<String, Object> newValueChangeMap, ApplicationContext context) {
private void dynamicRefresh(String configContent, Map<String, Object> newValueChangeMap, ApplicationContext context) {
try {
String configFileType = ApolloSpringBootProperties.Spring.Dynamic.Thread_Pool.CONFIG_FILE_TYPE;
String configFileType = SpringBootConfig.Spring.Dynamic.Thread_Pool.CONFIG_FILE_TYPE;
Map<Object, Object> afterConfigMap = ConfigParserHandler.getInstance().parseConfig(configContent,
ConfigFileTypeEnum.of(configFileType));
@ -319,8 +277,4 @@ public class EventPublishingFinishedInterceptor implements InstanceMethodsAround
&& Objects.equals(BlockingQueueTypeEnum.RESIZABLE_LINKED_BLOCKING_QUEUE.getName(), executor.getQueue().getClass().getSimpleName())));
}
@Override
public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t) {
}
}

@ -0,0 +1,24 @@
/*
* 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;
public interface IDynamicThreadPoolChangeHandlerSpring {
void registerApolloConfigHandler();
}

@ -15,7 +15,7 @@
* limitations under the License.
*/
package cn.hippo4j.agent.plugin.spring.common;
package cn.hippo4j.agent.plugin.spring.common.support;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;

@ -15,7 +15,7 @@
* limitations under the License.
*/
package cn.hippo4j.agent.plugin.spring.common;
package cn.hippo4j.agent.plugin.spring.common.support;
import cn.hippo4j.agent.core.boot.SpringBootConfigInitializer;
import cn.hippo4j.agent.core.logging.api.ILog;

@ -15,11 +15,8 @@
* limitations under the License.
*/
package cn.hippo4j.agent.plugin.spring.boot.v1;
package cn.hippo4j.agent.plugin.spring.common.support;
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.registry.AgentThreadPoolInstanceRegistry;
import cn.hippo4j.agent.core.util.ReflectUtil;
import cn.hippo4j.agent.core.util.ThreadPoolPropertyKey;
@ -30,30 +27,17 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class SpringApplicationRunInterceptor implements InstanceMethodsAroundInterceptor {
public class SpringThreadPoolRegisterSupport {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringApplicationRunInterceptor.class);
private static final Logger LOGGER = LoggerFactory.getLogger(SpringThreadPoolRegisterSupport.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 {
registerThreadPoolInstances();
LOGGER.info("[Hippo4j-Agent] Registered thread pool instances successfully.");
return ret;
}
private void registerThreadPoolInstances() {
public static void registerThreadPoolInstances() {
Map<ThreadPoolExecutor, Class<?>> earlyConstructMap = AgentThreadPoolInstanceRegistry.getInstance().earlyConstructMap;
for (Map.Entry<ThreadPoolExecutor, Class<?>> entry : earlyConstructMap.entrySet()) {
ThreadPoolExecutor enhancedInstance = entry.getKey();
@ -72,9 +56,10 @@ public class SpringApplicationRunInterceptor implements InstanceMethodsAroundInt
}
}
}
LOGGER.info("[Hippo4j-Agent] Registered thread pool instances successfully.");
}
private void register(String threadPoolId, ThreadPoolExecutor executor) {
public static void register(String threadPoolId, ThreadPoolExecutor executor) {
// build parameter properties.
Properties properties = new Properties();
properties.put(ThreadPoolPropertyKey.THREAD_POOL_ID, threadPoolId);
@ -92,9 +77,4 @@ public class SpringApplicationRunInterceptor implements InstanceMethodsAroundInt
AgentThreadPoolInstanceRegistry.getInstance().putHolder(threadPoolId, executor, properties);
}
@Override
public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable t) {
}
}

@ -31,7 +31,7 @@ public class ThreadPoolExecutorInstrumentation extends ClassInstanceMethodsEnhan
private static final String ENHANCE_CLASS = "java.util.concurrent.ThreadPoolExecutor";
private static final String CONSTRUCTOR_INTERCEPT_CLASS = "cn.hippo4j.agent.plugin.thread.pool.ThreadPoolExecutorConstructorMethodInterceptor";
private static final String CONSTRUCTOR_INTERCEPT_CLASS = "cn.hippo4j.agent.plugin.thread.pool.interceptor.ThreadPoolExecutorConstructorMethodInterceptor";
private static final int CONSTRUCTOR_INTERCEPT_PARAMETER_LENGTH = 7;

@ -15,7 +15,7 @@
* limitations under the License.
*/
package cn.hippo4j.agent.plugin.thread.pool;
package cn.hippo4j.agent.plugin.thread.pool.interceptor;
import cn.hippo4j.agent.core.conf.Config;
import cn.hippo4j.agent.core.logging.api.ILog;

@ -70,4 +70,21 @@
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -38,6 +38,7 @@ public class MicrometerMonitorAutoConfiguration {
@Bean
@ConditionalOnExpression("'${spring.dynamic.thread-pool.monitor.thread-pool-types:}'.contains('dynamic')")
@ConditionalOnProperty(prefix = Constants.CONFIGURATION_PROPERTIES_PREFIX, value = "enable", matchIfMissing = true, havingValue = "true")
public DynamicThreadPoolMicrometerMonitorHandler dynamicThreadPoolMicrometerMonitorHandler() {
return new DynamicThreadPoolMicrometerMonitorHandler();
}

Loading…
Cancel
Save