From 0a3cd3bb2d9786f6d89a4bdfb17f906e3c8222a9 Mon Sep 17 00:00:00 2001 From: Pan-YuJie <646836760@qq.com> Date: Thu, 20 Jul 2023 21:57:55 +0800 Subject: [PATCH 01/14] Feature: server add Ldap user authentication --- threadpool/server/auth/pom.xml | 4 + .../auth/config/GlobalSecurityConfig.java | 39 ++++- .../auth/config/LdapConfiguration.java | 72 ++++++++ .../auth/filter/JWTAuthenticationFilter.java | 18 +- .../auth/filter/LdapAuthenticationFilter.java | 155 ++++++++++++++++++ .../cn/hippo4j/auth/model/LdapUserInfo.java | 39 +++++ .../cn/hippo4j/auth/service/LdapService.java | 28 ++++ .../auth/service/impl/LdapServiceImpl.java | 72 ++++++++ .../impl/LdapUserDetailsServiceImpl.java | 115 +++++++++++++ .../service/impl/UserDetailsServiceImpl.java | 15 +- .../toolkit/BCryptPasswordEncoderTest.java | 20 +++ .../src/main/resources/ldap-back.properties | 14 ++ 12 files changed, 584 insertions(+), 7 deletions(-) create mode 100644 threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/LdapConfiguration.java create mode 100644 threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/LdapAuthenticationFilter.java create mode 100644 threadpool/server/auth/src/main/java/cn/hippo4j/auth/model/LdapUserInfo.java create mode 100644 threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/LdapService.java create mode 100644 threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/LdapServiceImpl.java create mode 100644 threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/LdapUserDetailsServiceImpl.java create mode 100644 threadpool/server/auth/src/test/java/cn/hippo4j/auth/toolkit/BCryptPasswordEncoderTest.java create mode 100644 threadpool/server/bootstrap/src/main/resources/ldap-back.properties diff --git a/threadpool/server/auth/pom.xml b/threadpool/server/auth/pom.xml index ce8d8175..aff6a5d5 100644 --- a/threadpool/server/auth/pom.xml +++ b/threadpool/server/auth/pom.xml @@ -62,5 +62,9 @@ hippo4j-threadpool-server-common ${project.version} + + org.springframework.boot + spring-boot-starter-data-ldap + diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/GlobalSecurityConfig.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/GlobalSecurityConfig.java index b239b42c..83b3d1ca 100644 --- a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/GlobalSecurityConfig.java +++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/GlobalSecurityConfig.java @@ -20,6 +20,7 @@ package cn.hippo4j.auth.config; import cn.hippo4j.auth.constant.Constants; import cn.hippo4j.auth.filter.JWTAuthenticationFilter; import cn.hippo4j.auth.filter.JWTAuthorizationFilter; +import cn.hippo4j.auth.filter.LdapAuthenticationFilter; import cn.hippo4j.auth.security.JwtTokenManager; import cn.hippo4j.auth.service.impl.UserDetailsServiceImpl; import org.springframework.beans.factory.annotation.Value; @@ -28,6 +29,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.BeanIds; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; @@ -54,9 +56,12 @@ public class GlobalSecurityConfig extends WebSecurityConfigurerAdapter { @Value("${hippo4j.core.auth.enabled:true}") private Boolean enableAuthentication; - @Resource + @Resource(name = "userDetailsServiceImpl") private UserDetailsService userDetailsService; + @Resource(name = "ldapUserDetailsServiceImpl") + private UserDetailsService ldapUserDetailsService; + @Resource private JwtTokenManager tokenManager; @@ -93,7 +98,9 @@ public class GlobalSecurityConfig extends WebSecurityConfigurerAdapter { .antMatchers("/static/**", "/index.html", "/favicon.ico", "/avatar.jpg").permitAll() .antMatchers("/doc.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs").anonymous() .and() - .addFilter(new JWTAuthenticationFilter(authenticationManager())) + // .addFilter(new JWTAuthenticationFilter(authenticationManager())).authenticationProvider(authenticationProvider()) + .addFilter(JWTAuthenticationFilter()).authenticationProvider(ldapAuthenticationProvider()) + .addFilter(LdapAuthenticationFilter()).authenticationProvider(ldapAuthenticationProvider()) .addFilter(new JWTAuthorizationFilter(tokenManager, authenticationManager())) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); disableAuthenticationIfNeeded(http); @@ -106,6 +113,20 @@ public class GlobalSecurityConfig extends WebSecurityConfigurerAdapter { web.ignoring().antMatchers(ignores); } + private LdapAuthenticationFilter LdapAuthenticationFilter() throws Exception { + LdapAuthenticationFilter filter = new LdapAuthenticationFilter(authenticationManager()); + filter.setLdapUserDetailsService(ldapUserDetailsService); + filter.setAuthenticationManager(authenticationManagerBean()); + return filter; + } + + private JWTAuthenticationFilter JWTAuthenticationFilter() throws Exception { + JWTAuthenticationFilter filter = new JWTAuthenticationFilter(authenticationManager()); + filter.setLdapUserDetailsService(userDetailsService); + filter.setAuthenticationManager(authenticationManagerBean()); + return filter; + } + /** * Injection DaoAuthenticationProvider * Modify hideUserNotFoundExceptions initial value to false @@ -120,6 +141,20 @@ public class GlobalSecurityConfig extends WebSecurityConfigurerAdapter { return provider; } + @Bean + public DaoAuthenticationProvider ldapAuthenticationProvider() { + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(ldapUserDetailsService); + authProvider.setPasswordEncoder(bCryptPasswordEncoder()); + return authProvider; + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.authenticationProvider(authenticationProvider()) + .authenticationProvider(ldapAuthenticationProvider()); + } + private void disableAuthenticationIfNeeded(HttpSecurity http) throws Exception { if (Boolean.FALSE.equals(enableAuthentication)) { http.authorizeRequests().antMatchers("/hippo4j/v1/cs/**").permitAll(); diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/LdapConfiguration.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/LdapConfiguration.java new file mode 100644 index 00000000..1932878d --- /dev/null +++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/LdapConfiguration.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.auth.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.core.support.LdapContextSource; + +import java.util.HashMap; +import java.util.Map; + +/** + * Ldap config. + */ +@Configuration +public class LdapConfiguration { + + private LdapTemplate ldapTemplate; + + @Value("${spring.ldap.urls:}") + private String url; + + @Value("${spring.ldap.base:}") + private String base; + + @Value("${spring.ldap.embedded.credential.username:}") + private String username; + + @Value("${spring.ldap.embedded.credential.password:}") + private String password; + + @Bean + public LdapContextSource contextSource() { + LdapContextSource contextSource = new LdapContextSource(); + Map config = new HashMap<>(10); + contextSource.setUrl(url); + contextSource.setBase(base); + contextSource.setUserDn(username); + contextSource.setPassword(password); + // fix garbled characters + config.put("java.naming.ldap.attributes.binary", "objectGUID"); + + contextSource.setPooled(true); + contextSource.setBaseEnvironmentProperties(config); + return contextSource; + } + + @Bean + public LdapTemplate ldapTemplate() { + if (null == ldapTemplate) { + ldapTemplate = new LdapTemplate(contextSource()); + } + return ldapTemplate; + } +} diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/JWTAuthenticationFilter.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/JWTAuthenticationFilter.java index 408aa3d4..d9f270b5 100644 --- a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/JWTAuthenticationFilter.java +++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/JWTAuthenticationFilter.java @@ -25,23 +25,25 @@ import cn.hippo4j.auth.toolkit.ReturnT; import cn.hippo4j.common.toolkit.JSONUtil; import cn.hippo4j.server.common.base.Results; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.core.codec.DecodingException; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import javax.servlet.FilterChain; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.security.GeneralSecurityException; -import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -60,11 +62,18 @@ public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilte private final ThreadLocal rememberMe = new ThreadLocal(); + private UserDetailsService userDetailsService; + public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; super.setFilterProcessesUrl(BASE_PATH + "/auth/login"); } + public void setLdapUserDetailsService(UserDetailsService userDetailsServiceImpl) { + this.userDetailsService = userDetailsServiceImpl; + } + + @SneakyThrows @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { @@ -78,8 +87,9 @@ public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilte request.setAttribute("loginUser", loginUser); rememberMe.set(loginUser.getRememberMe()); - authenticate = authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword(), new ArrayList())); + UserDetails userDetails = userDetailsService.loadUserByUsername(loginUser.getUsername()); + authenticate = new PreAuthenticatedAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + } catch (GeneralSecurityException e) { log.warn("Password decode exception: {}", e.getMessage()); throw new DecodingException(e.getMessage()); diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/LdapAuthenticationFilter.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/LdapAuthenticationFilter.java new file mode 100644 index 00000000..9ce982a3 --- /dev/null +++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/LdapAuthenticationFilter.java @@ -0,0 +1,155 @@ +/* + * 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.auth.filter; + +import cn.hippo4j.auth.model.biz.user.JwtUser; +import cn.hippo4j.auth.model.biz.user.LoginUser; +import cn.hippo4j.auth.toolkit.AESUtil; +import cn.hippo4j.auth.toolkit.JwtTokenUtil; +import cn.hippo4j.auth.toolkit.ReturnT; +import cn.hippo4j.common.toolkit.JSONUtil; +import cn.hippo4j.server.common.base.Results; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; + +import javax.servlet.FilterChain; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import static cn.hippo4j.auth.constant.Constants.SPLIT_COMMA; +import static cn.hippo4j.common.constant.Constants.BASE_PATH; +import static cn.hippo4j.common.constant.Constants.MAP_INITIAL_CAPACITY; + +/** + * Ldap Filter + */ +@Slf4j +public class LdapAuthenticationFilter extends UsernamePasswordAuthenticationFilter { + + private final ThreadLocal rememberMe = new ThreadLocal<>(); + + private UserDetailsService ldapUserDetailsService; + + public void setLdapUserDetailsService(UserDetailsService ldapUserDetailsServiceImpl) { + this.ldapUserDetailsService = ldapUserDetailsServiceImpl; + } + + public LdapAuthenticationFilter(AuthenticationManager authenticationManager) { + super.setFilterProcessesUrl(BASE_PATH + "/auth/ldap/login"); + } + + /** + * Whether it's just the post way + */ + private boolean postOnly = true; + + + /** + * filter obtains the username and password of LDAP and assembles it on the token. + * Then give the token for authorization + */ + @Override + public Authentication attemptAuthentication(HttpServletRequest request + , HttpServletResponse response) throws AuthenticationException { + if (postOnly && !"POST".equals(request.getMethod())) { + throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); + } else { + // Get logged in information from the input stream. + Authentication authenticate = null; + try { + LoginUser loginUser = new ObjectMapper().readValue(request.getInputStream(), LoginUser.class); + String key = new StringBuffer(loginUser.getTag()).reverse().toString(); + String password = AESUtil.decrypt(loginUser.getPassword(), key); + loginUser.setPassword(password); + request.setAttribute("loginUser", loginUser); + rememberMe.set(loginUser.getRememberMe()); + // ldap validated + UserDetails userDetails = ldapUserDetailsService.loadUserByUsername(loginUser.getUsername()); + authenticate = new PreAuthenticatedAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + } catch (UsernameNotFoundException e) { + log.debug("User {} not found", e.getMessage()); + throw e; + } catch (BadCredentialsException e) { + log.debug("Bad credentials exception: {}", e.getMessage()); + throw e; + } catch (Exception e) { + log.debug("Attempt authentication error", e); + } + return authenticate; + } + } + + @Override + protected void successfulAuthentication(HttpServletRequest request, + HttpServletResponse response, + FilterChain chain, + Authentication authResult) throws IOException { + try { + JwtUser jwtUser = (JwtUser) authResult.getPrincipal(); + boolean isRemember = rememberMe.get() == 1; + String role = ""; + Collection authorities = jwtUser.getAuthorities(); + for (GrantedAuthority authority : authorities) { + role = authority.getAuthority(); + } + String token = JwtTokenUtil.createToken(jwtUser.getId(), jwtUser.getUsername(), role, isRemember); + response.setHeader("token", JwtTokenUtil.TOKEN_PREFIX + token); + response.setCharacterEncoding("UTF-8"); + Map maps = new HashMap<>(MAP_INITIAL_CAPACITY); + maps.put("data", JwtTokenUtil.TOKEN_PREFIX + token); + maps.put("roles", role.split(SPLIT_COMMA)); + response.getWriter().write(JSONUtil.toJSONString(Results.success(maps))); + } finally { + rememberMe.remove(); + } + } + + @Override + protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException { + response.setCharacterEncoding("UTF-8"); + response.getWriter().write(JSONUtil.toJSONString(new ReturnT<>(ReturnT.JWT_FAIL_CODE, getMessage(failed)))); + } + + /** + * Return different echo information to the front end according to different exception types + */ + private String getMessage(AuthenticationException failed) { + String message = "Server Error"; + if (failed instanceof UsernameNotFoundException) { + message = "用户不存在"; + } else if (failed instanceof BadCredentialsException) { + message = "密码错误"; + } + return message; + } +} diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/model/LdapUserInfo.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/model/LdapUserInfo.java new file mode 100644 index 00000000..9a09c0b8 --- /dev/null +++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/model/LdapUserInfo.java @@ -0,0 +1,39 @@ +package cn.hippo4j.auth.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; +import org.springframework.ldap.odm.annotations.Attribute; +import org.springframework.ldap.odm.annotations.DnAttribute; +import org.springframework.ldap.odm.annotations.Entry; +import org.springframework.ldap.odm.annotations.Id; + +import javax.naming.Name; + +@Data +@Entry(objectClasses = {"inetOrgPerson", "top"}) +public class LdapUserInfo { + + @JsonIgnore + @Id + private Name dn; + + @Attribute(name = "cn") + @DnAttribute(value = "cn") + private String userName; + + @Attribute(name = "sn") + private String lastName; + + @Attribute(name = "description") + private String description; + + @Attribute(name = "telephoneNumber") + private String telephoneNumber; + + @Attribute(name = "userPassword") + private String password; + + @Attribute(name = "ou") + private String organizational; + +} diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/LdapService.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/LdapService.java new file mode 100644 index 00000000..823286d3 --- /dev/null +++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/LdapService.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.auth.service; + +/** + * Ldap service. + */ +public interface LdapService { + /** + * Login ldap + */ + void login(String username, String password); +} diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/LdapServiceImpl.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/LdapServiceImpl.java new file mode 100644 index 00000000..bb43be02 --- /dev/null +++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/LdapServiceImpl.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.auth.service.impl; + +import cn.hippo4j.auth.service.LdapService; +import cn.hippo4j.server.common.base.exception.ServiceException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.ldap.AuthenticationException; +import org.springframework.ldap.UncategorizedLdapException; +import org.springframework.ldap.core.LdapTemplate; +import org.springframework.ldap.query.LdapQueryBuilder; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import static org.springframework.ldap.query.LdapQueryBuilder.query; + +@Service +@Slf4j +public class LdapServiceImpl implements LdapService { + + private final LdapTemplate ldapTemplate; + + @Value("${spring.ldap.object-class:}") + private String objectClassName; + + @Value("${spring.ldap.account-attribute:}") + private String accountAttribute; + + public LdapServiceImpl(LdapTemplate ldapTemplate) { + this.ldapTemplate = ldapTemplate; + } + + @Override + public void login(String username, String password) { + try { + ldapTemplate.authenticate(LdapQueryBuilder.query() + .where(accountAttribute).is(username) + .and(query().where("objectClass").is(objectClassName)) + , password); + log.debug("{} ldap Login successful", username); + } catch (EmptyResultDataAccessException e) { + throw new UsernameNotFoundException("ldap Can't find the user information "); + } catch (AuthenticationException e) { + log.debug("The user name or account error"); + throw new BadCredentialsException("The username or password error"); + } catch (UncategorizedLdapException e) { + log.debug("Please check whether the user name password input"); + throw new BadCredentialsException("Please check whether the username password input"); + } catch (Exception e) { + throw new ServiceException("Abnormal server"); + } + } + +} diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/LdapUserDetailsServiceImpl.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/LdapUserDetailsServiceImpl.java new file mode 100644 index 00000000..ee5411f1 --- /dev/null +++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/LdapUserDetailsServiceImpl.java @@ -0,0 +1,115 @@ +/* + * 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.auth.service.impl; + +import cn.hippo4j.auth.mapper.UserMapper; +import cn.hippo4j.auth.model.UserInfo; +import cn.hippo4j.auth.model.biz.user.JwtUser; +import cn.hippo4j.auth.model.biz.user.LoginUser; +import cn.hippo4j.auth.service.LdapService; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +/** + * User details service impl. + */ +@Slf4j +@Service +public class LdapUserDetailsServiceImpl implements UserDetailsService { + + @Value("${hippo4j.core.auth.enabled:true}") + private Boolean enableAuthentication; + + @Resource + private UserMapper userMapper; + + @Resource + private LdapService ldapService; + + @Override + public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { + JwtUser anonymous = dealWithAnonymous(); + if (!Objects.isNull(anonymous)) { + return anonymous; + } + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(requestAttributes)).getRequest(); + LoginUser loginUser = (LoginUser) request.getAttribute("loginUser"); + // ldap authentication + ldapService.login(userName, loginUser.getPassword()); + // By querying the data inventory this user does not exist + UserInfo userInfo = userMapper.selectOne(Wrappers.lambdaQuery(UserInfo.class) + .eq(UserInfo::getUserName, userName) + ); + // the database does not, create a ROLE_USER permission to the default user, password is empty + if (Objects.isNull(userInfo)) { + userInfo = new UserInfo(); + userInfo.setPassword(""); + userInfo.setUserName(loginUser.getUsername()); + userInfo.setRole("ROLE_USER"); + userMapper.insert(userInfo); + } + // structure jwtUser + JwtUser jwtUser = new JwtUser(); + jwtUser.setId(userInfo.getId()); + jwtUser.setUsername(userName); + jwtUser.setPassword(""); + Set authorities = Collections.singleton(new SimpleGrantedAuthority(userInfo.getRole() + "")); + jwtUser.setAuthorities(authorities); + return jwtUser; + } + + private JwtUser dealWithAnonymous() { + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + if (requestAttributes == null) { + return null; + } + HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest(); + LoginUser loginUser = (LoginUser) request.getAttribute("loginUser"); + if (Objects.isNull(loginUser)) { + return null; + } + if (Boolean.FALSE.equals(enableAuthentication)) { + JwtUser jwtUser = new JwtUser(); + BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); + jwtUser.setId(1L); + jwtUser.setUsername("anonymous"); + jwtUser.setPassword(bCryptPasswordEncoder.encode(loginUser.getPassword())); + Set authorities = Collections.singleton(new SimpleGrantedAuthority("ROLE_ADMIN")); + jwtUser.setAuthorities(authorities); + return jwtUser; + } + return null; + } +} diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/UserDetailsServiceImpl.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/UserDetailsServiceImpl.java index dc62a170..960c25ab 100644 --- a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/UserDetailsServiceImpl.java +++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/UserDetailsServiceImpl.java @@ -24,11 +24,13 @@ import cn.hippo4j.auth.model.biz.user.LoginUser; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @@ -43,6 +45,7 @@ import java.util.Set; * User details service impl. */ @Slf4j +@Service public class UserDetailsServiceImpl implements UserDetailsService { @Value("${hippo4j.core.auth.enabled:true}") @@ -57,10 +60,20 @@ public class UserDetailsServiceImpl implements UserDetailsService { if (!Objects.isNull(anonymous)) { return anonymous; } - UserInfo userInfo = userMapper.selectOne(Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getUserName, userName)); + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + LoginUser loginUser = (LoginUser) request.getAttribute("loginUser"); + String loginPassword = loginUser.getPassword(); + UserInfo userInfo = userMapper.selectOne(Wrappers.lambdaQuery(UserInfo.class) + .eq(UserInfo::getUserName, userName) + ); if (Objects.isNull(userInfo)) { throw new UsernameNotFoundException(userName); } + // Validation password + BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); + if (!bCryptPasswordEncoder.matches(loginPassword, userInfo.getPassword())) { + throw new BadCredentialsException(userName + "密码错误,请重新输入"); + } JwtUser jwtUser = new JwtUser(); jwtUser.setId(userInfo.getId()); jwtUser.setUsername(userName); diff --git a/threadpool/server/auth/src/test/java/cn/hippo4j/auth/toolkit/BCryptPasswordEncoderTest.java b/threadpool/server/auth/src/test/java/cn/hippo4j/auth/toolkit/BCryptPasswordEncoderTest.java new file mode 100644 index 00000000..f1d8a24b --- /dev/null +++ b/threadpool/server/auth/src/test/java/cn/hippo4j/auth/toolkit/BCryptPasswordEncoderTest.java @@ -0,0 +1,20 @@ +package cn.hippo4j.auth.toolkit; + +import cn.hippo4j.common.toolkit.Assert; +import org.junit.Test; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +public class BCryptPasswordEncoderTest { + + @Test + public void bCryptPasswordEncoderTest() { + + String password = "12345abc"; + + BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); + String encode = bCryptPasswordEncoder.encode(password); + boolean matches = bCryptPasswordEncoder.matches(password, encode); + Assert.isTrue(matches); + } + +} diff --git a/threadpool/server/bootstrap/src/main/resources/ldap-back.properties b/threadpool/server/bootstrap/src/main/resources/ldap-back.properties new file mode 100644 index 00000000..5b950ede --- /dev/null +++ b/threadpool/server/bootstrap/src/main/resources/ldap-back.properties @@ -0,0 +1,14 @@ +#*************** Ldap Sample Configurations ***************# +### This configuration file does not take effect +### Change the LDAP server information to yourself +### Configure the following configuration file into application.properties + +# Ldap Config +spring.ldap.urls=ldap://127.0.0.1:389 +spring.ldap.base=dc=xxx,dc=com +spring.ldap.embedded.credential.username=cn=xxxx,dc=xxx,dc=com +spring.ldap.embedded.credential.password=password +# Ldap Entry object-class +spring.ldap.object-class=person +# Ldap account-attribute CommonName ( cn / uid / username / ... ) +spring.ldap.account-attribute=cn From bb870d243a7258f84aca9da87c7e68e71fe34b5c Mon Sep 17 00:00:00 2001 From: Pan-YuJie <646836760@qq.com> Date: Mon, 4 Sep 2023 20:47:18 +0800 Subject: [PATCH 02/14] Fix Server-example Unknown user --- .../core/enable/BeforeCheckConfiguration.java | 7 ++- .../cn/hippo4j/auth/config/RFC7230Config.java | 45 +++++++++++++++++++ .../cn/hippo4j/auth/security/AuthManager.java | 18 ++++++-- .../service/impl/UserDetailsServiceImpl.java | 2 + 4 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/RFC7230Config.java diff --git a/threadpool/core/src/main/java/cn/hippo4j/core/enable/BeforeCheckConfiguration.java b/threadpool/core/src/main/java/cn/hippo4j/core/enable/BeforeCheckConfiguration.java index 65f0cb5d..cf56f4ae 100644 --- a/threadpool/core/src/main/java/cn/hippo4j/core/enable/BeforeCheckConfiguration.java +++ b/threadpool/core/src/main/java/cn/hippo4j/core/enable/BeforeCheckConfiguration.java @@ -28,7 +28,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.env.ConfigurableEnvironment; import java.util.Map; -import java.util.Objects; /** * Before check configuration. @@ -92,15 +91,15 @@ public class BeforeCheckConfiguration { } } Map etcd = properties.getEtcd(); - if (MapUtil.isNotEmpty(etcd)){ + if (MapUtil.isNotEmpty(etcd)) { String endpoints = etcd.get("endpoints"); - if ((StringUtil.isBlank(endpoints))){ + if ((StringUtil.isBlank(endpoints))) { throw new ConfigEmptyException( "Web server failed to start. The dynamic thread pool etcd endpoints is empty.", "Please check whether the [spring.dynamic.thread-pool.etcd.endpoints] configuration is empty or an empty string."); } String key = etcd.get("key"); - if ((StringUtil.isBlank(key))){ + if ((StringUtil.isBlank(key))) { throw new ConfigEmptyException( "Web server failed to start. The dynamic thread pool etcd key is empty.", "Please check whether the [spring.dynamic.thread-pool.etcd.key] configuration is empty or an empty string."); diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/RFC7230Config.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/RFC7230Config.java new file mode 100644 index 00000000..cce59b88 --- /dev/null +++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/RFC7230Config.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.auth.config; + +import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RFC7230Config { + + /** + * Solve the Tomcat RFC7230 problem + * + * @return + */ + @Bean + public ConfigurableServletWebServerFactory webServerFactory() { + TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); + factory.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> { + connector.setProperty("relaxedQueryChars", "|{}[](),/:;<=>?@[\\]{}\\"); + connector.setProperty("relaxedPathChars", "|{}[](),/:;<=>?@[\\]{}\\"); + connector.setProperty("rejectIllegalHeader", "false"); + }); + + return factory; + } +} diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/security/AuthManager.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/security/AuthManager.java index 765104d4..c2ac9be0 100644 --- a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/security/AuthManager.java +++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/security/AuthManager.java @@ -17,13 +17,19 @@ package cn.hippo4j.auth.security; +import cn.hippo4j.auth.model.biz.user.LoginUser; import lombok.AllArgsConstructor; import lombok.SneakyThrows; import org.springframework.expression.AccessException; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; /** * Auth manager. @@ -36,6 +42,8 @@ public class AuthManager { private final AuthenticationManager authenticationManager; + private final UserDetailsService userDetailsService; + /** * Resolve token from user. * @@ -47,9 +55,11 @@ public class AuthManager { @SneakyThrows public String resolveTokenFromUser(String userName, String rawPassword) { try { - UsernamePasswordAuthenticationToken authenticationToken = - new UsernamePasswordAuthenticationToken(userName, rawPassword); - authenticationManager.authenticate(authenticationToken); + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + LoginUser loginUser = new LoginUser(); + loginUser.setPassword(rawPassword); + request.setAttribute("loginUser", loginUser); + UserDetails userDetails = userDetailsService.loadUserByUsername(userName); } catch (AuthenticationException e) { throw new AccessException("Unknown user."); } diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/UserDetailsServiceImpl.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/UserDetailsServiceImpl.java index 5029024c..9a8e7ccd 100644 --- a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/UserDetailsServiceImpl.java +++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/service/impl/UserDetailsServiceImpl.java @@ -24,6 +24,7 @@ import cn.hippo4j.auth.model.biz.user.LoginUser; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Primary; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; @@ -46,6 +47,7 @@ import java.util.Set; */ @Slf4j @Service +@Primary public class UserDetailsServiceImpl implements UserDetailsService { @Value("${hippo4j.core.auth.enabled:true}") From 4a374aed050b2751d9916e8cd8b40b1a70d7598a Mon Sep 17 00:00:00 2001 From: Pan-YuJie <646836760@qq.com> Date: Mon, 4 Sep 2023 21:11:36 +0800 Subject: [PATCH 03/14] Remove RFC7230Config.class --- .../cn/hippo4j/auth/config/RFC7230Config.java | 45 ------------------- .../auth/filter/LdapAuthenticationFilter.java | 1 - 2 files changed, 46 deletions(-) delete mode 100644 threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/RFC7230Config.java diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/RFC7230Config.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/RFC7230Config.java deleted file mode 100644 index cce59b88..00000000 --- a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/config/RFC7230Config.java +++ /dev/null @@ -1,45 +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.auth.config; - -import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; -import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; -import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class RFC7230Config { - - /** - * Solve the Tomcat RFC7230 problem - * - * @return - */ - @Bean - public ConfigurableServletWebServerFactory webServerFactory() { - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - factory.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> { - connector.setProperty("relaxedQueryChars", "|{}[](),/:;<=>?@[\\]{}\\"); - connector.setProperty("relaxedPathChars", "|{}[](),/:;<=>?@[\\]{}\\"); - connector.setProperty("rejectIllegalHeader", "false"); - }); - - return factory; - } -} diff --git a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/LdapAuthenticationFilter.java b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/LdapAuthenticationFilter.java index 1d88fecb..1b86b34f 100644 --- a/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/LdapAuthenticationFilter.java +++ b/threadpool/server/auth/src/main/java/cn/hippo4j/auth/filter/LdapAuthenticationFilter.java @@ -73,7 +73,6 @@ public class LdapAuthenticationFilter extends UsernamePasswordAuthenticationFilt */ private boolean postOnly = true; - /** * filter obtains the username and password of LDAP and assembles it on the token. * Then give the token for authorization From aafd87079a5d604cad0f43d504eff81bd811f992 Mon Sep 17 00:00:00 2001 From: Pan-YuJie <646836760@qq.com> Date: Sun, 12 Nov 2023 23:11:22 +0800 Subject: [PATCH 04/14] Fix Log printing failure problem in agent mode --- .../hippo4j/agent/bootstrap/Hippo4jAgent.java | 19 +- .../agent/core/boot/AgentPackagePath.java | 5 +- .../agent/core/boot/ServiceManager.java | 4 +- .../boot/SpringBootConfigInitializer.java | 4 +- .../core/conf/SnifferConfigInitializer.java | 24 +- .../core/jvm/LoadedLibraryCollector.java | 12 +- .../AbstractClassEnhancePluginDefine.java | 4 +- .../core/plugin/InstrumentDebuggingClass.java | 8 +- .../agent/core/plugin/PluginBootstrap.java | 6 +- .../hippo4j/agent/core/plugin/PluginCfg.java | 4 +- .../core/plugin/PluginResourcesResolver.java | 4 +- .../agent/core/plugin/PluginSelector.java | 3 +- .../bootstrap/BootstrapInstrumentBoost.java | 4 +- .../bootstrap/BootstrapPluginLogBridge.java | 4 +- .../CacheableTransformerDecorator.java | 13 +- .../enhance/ClassEnhancePluginDefine.java | 10 +- .../interceptor/enhance/ConstructorInter.java | 4 +- .../interceptor/enhance/InstMethodsInter.java | 4 +- .../InstMethodsInterWithOverrideArgs.java | 4 +- .../enhance/StaticMethodsInter.java | 4 +- .../StaticMethodsInterWithOverrideArgs.java | 4 +- .../enhance/v2/InstMethodsInterV2.java | 4 +- .../InstMethodsInterV2WithOverrideArgs.java | 4 +- .../enhance/v2/StaticMethodsInterV2.java | 4 +- .../StaticMethodsInterV2WithOverrideArgs.java | 4 +- .../plugin/jdk9module/JDK9ModuleExporter.java | 4 +- .../core/plugin/loader/AgentClassLoader.java | 13 +- .../loader/InterceptorInstanceLoader.java | 2 +- .../plugin/match/ProtectiveShieldMatcher.java | 4 +- .../agent/core/util/CustomizeExpression.java | 4 +- .../agent/core/util/ExecutorNameUtil.java | 4 +- .../hippo4j/agent/core/util/ReflectUtil.java | 4 +- .../EventPublishingFinishedInterceptor.java | 4 +- ...ynamicThreadPoolChangeHandlerSpring2x.java | 4 +- .../boot/SpringBootV2PluginBootService.java | 4 +- .../EventPublishingStartedInterceptor.java | 4 +- .../support/SpringPropertiesLoader.java | 4 +- ...lExecutorConstructorMethodInterceptor.java | 9 +- .../src/main/resources/bootstrap.properties | 15 ++ infra/common/pom.xml | 5 + .../boot/AgentPackageNotFoundException.java | 2 +- .../hippo4j/common/boot/AgentPackagePath.java | 85 +++++++ .../hippo4j/common/boot}/ClassCacheMode.java | 7 +- .../boot/DefaultNamedThreadFactory.java | 2 +- .../java/cn/hippo4j/common}/conf/Config.java | 15 +- .../common}/conf/ConfigNotFoundException.java | 2 +- .../cn/hippo4j/common/conf/Constants.java | 39 ++++ .../conf/RuntimeContextConfiguration.java | 2 +- .../cn/hippo4j/common}/logging/api/ILog.java | 2 +- .../common}/logging/api/LogManager.java | 4 +- .../common}/logging/api/LogResolver.java | 2 +- .../common}/logging/api/NoopLogger.java | 2 +- .../common}/logging/core/AbstractLogger.java | 16 +- .../common}/logging/core/Converter.java | 2 +- .../common}/logging/core/FileWriter.java | 20 +- .../hippo4j/common}/logging/core/IWriter.java | 2 +- .../common}/logging/core/JsonLogResolver.java | 6 +- .../common}/logging/core/JsonLogger.java | 4 +- .../common}/logging/core/LogEvent.java | 2 +- .../common}/logging/core/LogLevel.java | 2 +- .../logging/core/LogMessageHolder.java | 2 +- .../common}/logging/core/LogOutput.java | 2 +- .../hippo4j/common}/logging/core/Parser.java | 4 +- .../logging/core/PatternLogResolver.java | 8 +- .../common}/logging/core/PatternLogger.java | 5 +- .../common}/logging/core/ResolverType.java | 2 +- .../common}/logging/core/SystemOutWriter.java | 2 +- .../common}/logging/core/WriterFactory.java | 16 +- .../core/converters/AgentNameConverter.java | 8 +- .../core/converters/ClassConverter.java | 6 +- .../core/converters/DateConverter.java | 6 +- .../core/converters/LevelConverter.java | 6 +- .../core/converters/LiteralConverter.java | 6 +- .../core/converters/MessageConverter.java | 6 +- .../core/converters/ThreadConverter.java | 6 +- .../core/converters/ThrowableConverter.java | 8 +- .../cn/hippo4j/common/toolkit/StringUtil.java | 65 ++++++ .../toolkit/agent/ConfigInitializer.java | 210 ++++++++++++++++++ .../hippo4j/common/toolkit/agent/Length.java | 33 +++ .../agent/PlaceholderConfigurerSupport.java | 37 +++ .../agent/PropertyPlaceholderHelper.java | 206 +++++++++++++++++ .../RunnableWithExceptionProtection.java | 43 ++++ .../DynamicThreadPoolRefreshListener.java | 11 +- pom.xml | 1 + 84 files changed, 934 insertions(+), 231 deletions(-) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/boot/AgentPackageNotFoundException.java (96%) create mode 100644 infra/common/src/main/java/cn/hippo4j/common/boot/AgentPackagePath.java rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy => infra/common/src/main/java/cn/hippo4j/common/boot}/ClassCacheMode.java (90%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/boot/DefaultNamedThreadFactory.java (97%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/conf/Config.java (95%) mode change 100755 => 100644 rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/conf/ConfigNotFoundException.java (96%) create mode 100644 infra/common/src/main/java/cn/hippo4j/common/conf/Constants.java rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/conf/RuntimeContextConfiguration.java (96%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/api/ILog.java (97%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/api/LogManager.java (95%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/api/LogResolver.java (96%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/api/NoopLogger.java (98%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/AbstractLogger.java (91%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/Converter.java (95%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/FileWriter.java (94%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/IWriter.java (95%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/JsonLogResolver.java (88%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/JsonLogger.java (96%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/LogEvent.java (97%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/LogLevel.java (95%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/LogMessageHolder.java (96%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/LogOutput.java (95%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/Parser.java (98%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/PatternLogResolver.java (85%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/PatternLogger.java (94%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/ResolverType.java (95%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/SystemOutWriter.java (96%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/WriterFactory.java (78%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/converters/AgentNameConverter.java (83%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/converters/ClassConverter.java (87%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/converters/DateConverter.java (88%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/converters/LevelConverter.java (87%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/converters/LiteralConverter.java (88%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/converters/MessageConverter.java (87%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/converters/ThreadConverter.java (87%) rename {agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core => infra/common/src/main/java/cn/hippo4j/common}/logging/core/converters/ThrowableConverter.java (88%) create mode 100644 infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/ConfigInitializer.java create mode 100644 infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/Length.java create mode 100644 infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/PlaceholderConfigurerSupport.java create mode 100644 infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/PropertyPlaceholderHelper.java create mode 100644 infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/RunnableWithExceptionProtection.java diff --git a/agent/hippo4j-agent-bootstrap/src/main/java/cn/hippo4j/agent/bootstrap/Hippo4jAgent.java b/agent/hippo4j-agent-bootstrap/src/main/java/cn/hippo4j/agent/bootstrap/Hippo4jAgent.java index 64f6ce9c..a4de6591 100644 --- a/agent/hippo4j-agent-bootstrap/src/main/java/cn/hippo4j/agent/bootstrap/Hippo4jAgent.java +++ b/agent/hippo4j-agent-bootstrap/src/main/java/cn/hippo4j/agent/bootstrap/Hippo4jAgent.java @@ -17,22 +17,17 @@ package cn.hippo4j.agent.bootstrap; -import cn.hippo4j.agent.core.boot.AgentPackageNotFoundException; import cn.hippo4j.agent.core.boot.ServiceManager; -import cn.hippo4j.agent.core.conf.Config; import cn.hippo4j.agent.core.conf.SnifferConfigInitializer; import cn.hippo4j.agent.core.jvm.LoadedLibraryCollector; -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.InstrumentDebuggingClass; -import cn.hippo4j.agent.core.plugin.PluginBootstrap; -import cn.hippo4j.agent.core.plugin.PluginException; -import cn.hippo4j.agent.core.plugin.PluginFinder; +import cn.hippo4j.agent.core.plugin.*; import cn.hippo4j.agent.core.plugin.bootstrap.BootstrapInstrumentBoost; import cn.hippo4j.agent.core.plugin.bytebuddy.CacheableTransformerDecorator; import cn.hippo4j.agent.core.plugin.jdk9module.JDK9ModuleExporter; +import cn.hippo4j.common.boot.AgentPackageNotFoundException; +import cn.hippo4j.common.conf.Config; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import net.bytebuddy.ByteBuddy; import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.description.NamedElement; @@ -49,9 +44,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import static net.bytebuddy.matcher.ElementMatchers.nameContains; -import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; -import static net.bytebuddy.matcher.ElementMatchers.not; +import static net.bytebuddy.matcher.ElementMatchers.*; /** * Hippo4j Agent diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/AgentPackagePath.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/AgentPackagePath.java index 029a4bfb..02b3c665 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/AgentPackagePath.java +++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/AgentPackagePath.java @@ -17,8 +17,9 @@ package cn.hippo4j.agent.core.boot; -import cn.hippo4j.agent.core.logging.api.ILog; -import cn.hippo4j.agent.core.logging.api.LogManager; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; +import cn.hippo4j.common.boot.AgentPackageNotFoundException; import java.io.File; import java.net.MalformedURLException; diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/ServiceManager.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/ServiceManager.java index 2026ea9f..7391f1c2 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/ServiceManager.java +++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/ServiceManager.java @@ -17,8 +17,8 @@ package cn.hippo4j.agent.core.boot; -import cn.hippo4j.agent.core.logging.api.ILog; -import cn.hippo4j.agent.core.logging.api.LogManager; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import cn.hippo4j.agent.core.plugin.loader.AgentClassLoader; import java.util.Collections; diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/SpringBootConfigInitializer.java b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/SpringBootConfigInitializer.java index eb2eafef..5d12c1e6 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/SpringBootConfigInitializer.java +++ b/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/SpringBootConfigInitializer.java @@ -17,8 +17,8 @@ package cn.hippo4j.agent.core.boot; -import cn.hippo4j.agent.core.logging.api.ILog; -import cn.hippo4j.agent.core.logging.api.LogManager; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import cn.hippo4j.agent.core.util.ConfigInitializer; import java.util.Collections; 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 index 570a68c8..b1c0a56c 100644 --- 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 @@ -17,15 +17,17 @@ 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 cn.hippo4j.common.boot.AgentPackageNotFoundException; +import cn.hippo4j.common.boot.AgentPackagePath; +import cn.hippo4j.common.conf.Config; +import cn.hippo4j.common.conf.ConfigNotFoundException; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; +import cn.hippo4j.common.logging.core.JsonLogResolver; +import cn.hippo4j.common.logging.core.PatternLogResolver; +import cn.hippo4j.common.toolkit.StringUtil; +import cn.hippo4j.common.toolkit.agent.ConfigInitializer; +import cn.hippo4j.common.toolkit.agent.PropertyPlaceholderHelper; import java.io.File; import java.io.FileInputStream; @@ -37,7 +39,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import static cn.hippo4j.agent.core.conf.Constants.SERVICE_NAME_PART_CONNECTOR; +import static cn.hippo4j.common.conf.Constants.SERVICE_NAME_PART_CONNECTOR; /** * The SnifferConfigInitializer initializes all configs in several way. @@ -204,7 +206,7 @@ public class SnifferConfigInitializer { * * @return the config file {@link InputStream}, or null if not needEnhance. */ - private static InputStreamReader loadConfig() throws AgentPackageNotFoundException, ConfigNotFoundException { + private static InputStreamReader loadConfig() throws AgentPackageNotFoundException, cn.hippo4j.common.conf.ConfigNotFoundException { String specifiedConfigPath = System.getProperty(SPECIFIED_CONFIG_PATH); File configFile = StringUtil.isEmpty(specifiedConfigPath) ? new File( AgentPackagePath.getPath(), DEFAULT_CONFIG_FILE_NAME) : new File(specifiedConfigPath); 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 index f8a466a6..6bae20aa 100644 --- 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 @@ -17,9 +17,9 @@ 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 cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -28,13 +28,7 @@ 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; +import java.util.*; public class LoadedLibraryCollector { 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 index 41384b1a..82278a6a 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.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; 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 index e82258fb..02a900cc 100644 --- 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 @@ -17,11 +17,11 @@ 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 cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; +import cn.hippo4j.common.boot.AgentPackageNotFoundException; +import cn.hippo4j.common.conf.Config; import net.bytebuddy.dynamic.DynamicType; import java.io.File; 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 index 12dffe39..f76fe19d 100644 --- 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 @@ -17,10 +17,10 @@ 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 cn.hippo4j.common.boot.AgentPackageNotFoundException; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import java.net.URL; import java.util.ArrayList; 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 index 81e5027b..fe707c9c 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import cn.hippo4j.agent.core.plugin.exception.IllegalPluginDefineException; import java.io.BufferedReader; 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 index 06058a0b..861995ee 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import cn.hippo4j.agent.core.plugin.loader.AgentClassLoader; import java.io.IOException; 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 index 8f336b36..4859909e 100644 --- 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 @@ -20,9 +20,8 @@ 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; +import static cn.hippo4j.common.conf.Config.Plugin.EXCLUDE_PLUGINS; /** * Select some plugins in activated plugins 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 index 3260e439..7138ea85 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import cn.hippo4j.agent.core.plugin.AbstractClassEnhancePluginDefine; import cn.hippo4j.agent.core.plugin.ByteBuddyCoreClasses; import cn.hippo4j.agent.core.plugin.InstrumentDebuggingClass; 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 index 868b1c2c..bdcbcfdc 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; /** * The log bridge makes the ILog accessible inside bootstrap classloader, especially for internal interceptor. 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 index 8341772c..f0ed68c6 100644 --- 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 @@ -17,21 +17,18 @@ 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 cn.hippo4j.common.boot.AgentPackageNotFoundException; +import cn.hippo4j.common.boot.ClassCacheMode; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; 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.io.*; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; import java.util.Map; 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 index 70dddbad..8378aa35 100644 --- 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 @@ -17,20 +17,16 @@ 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.*; 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 cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; 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 index d5b24c1d..7896eb22 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.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; 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 index 489c54d3..1d5dd6c1 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.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; 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 index 5febdb83..57bc7444 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.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; 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 index 3be9ccba..0a98c7de 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.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; 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 index 99f83a17..e7c11a7f 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.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; 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 index 3967736a..eea316d0 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.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; 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 index f9cd51d6..9aaa9888 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.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; 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 index 97785770..7bd4c2f1 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.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; 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 index 467af26a..ca86bf2e 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.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; 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 index 1ff3514e..07c69b54 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import cn.hippo4j.agent.core.plugin.ByteBuddyCoreClasses; import net.bytebuddy.agent.builder.AgentBuilder; 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 index 3fec67cc..d227ec7e 100644 --- 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 @@ -17,16 +17,16 @@ 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.boot.SpringBootConfigNode; 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 cn.hippo4j.common.boot.AgentPackageNotFoundException; +import cn.hippo4j.common.conf.Config; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import lombok.RequiredArgsConstructor; import java.io.BufferedInputStream; @@ -62,7 +62,7 @@ public class AgentClassLoader extends ClassLoader { private static AgentClassLoader DEFAULT_LOADER; private List classpath; - private List allJars; + private static List allJars; private ReentrantLock jarScanLock = new ReentrantLock(); public static AgentClassLoader getDefault() { @@ -192,7 +192,6 @@ public class AgentClassLoader extends ClassLoader { jarScanLock.unlock(); } } - return allJars; } 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 index cd1e0352..e5c55ad4 100644 --- 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 @@ -17,7 +17,7 @@ package cn.hippo4j.agent.core.plugin.loader; -import cn.hippo4j.agent.core.boot.AgentPackageNotFoundException; +import cn.hippo4j.common.boot.AgentPackageNotFoundException; import java.util.HashMap; import java.util.Map; 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 index d0ec36cb..7a02b300 100644 --- 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 @@ -17,8 +17,8 @@ package cn.hippo4j.agent.core.plugin.match; -import cn.hippo4j.agent.core.logging.api.ILog; -import cn.hippo4j.agent.core.logging.api.LogManager; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import net.bytebuddy.matcher.ElementMatcher; /** 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 index 4912936b..f4aebf8a 100644 --- 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 @@ -17,8 +17,8 @@ package cn.hippo4j.agent.core.util; -import cn.hippo4j.agent.core.logging.api.ILog; -import cn.hippo4j.agent.core.logging.api.LogManager; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import java.lang.reflect.Array; import java.lang.reflect.Field; 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 index 7a597187..e605e1ad 100644 --- 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 @@ -17,8 +17,8 @@ package cn.hippo4j.agent.core.util; -import cn.hippo4j.agent.core.logging.api.ILog; -import cn.hippo4j.agent.core.logging.api.LogManager; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import java.lang.reflect.Field; 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 index e6fed965..105a5274 100644 --- 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 @@ -17,8 +17,8 @@ package cn.hippo4j.agent.core.util; -import cn.hippo4j.agent.core.logging.api.ILog; -import cn.hippo4j.agent.core.logging.api.LogManager; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import java.lang.reflect.Field; import java.lang.reflect.Modifier; 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 index 777194f0..dcee8726 100644 --- 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 @@ -18,8 +18,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.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; 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 index bcda15e4..6f70bf23 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.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; diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/boot/SpringBootV2PluginBootService.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 index c9b8c85b..d4763704 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/boot/SpringBootV2PluginBootService.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 @@ -19,8 +19,8 @@ package cn.hippo4j.agent.plugin.spring.boot.v2.boot; 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; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; /** * SpringBoot v1 plugin boot service 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 index 07184692..d9df4772 100644 --- 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 @@ -17,8 +17,8 @@ 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.common.logging.api.ILog; +import cn.hippo4j.common.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; 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 index bebfb843..8b215ec0 100644 --- 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 @@ -18,8 +18,8 @@ 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 cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.PropertySource; 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 index fb4194c8..fa4424a8 100644 --- 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 @@ -17,14 +17,15 @@ 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 cn.hippo4j.common.conf.Config; +import cn.hippo4j.common.executor.ThreadPoolExecutorRegistry; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; diff --git a/examples/threadpool-example/agent/config-apollo/src/main/resources/bootstrap.properties b/examples/threadpool-example/agent/config-apollo/src/main/resources/bootstrap.properties index 88187c9c..d467930f 100644 --- a/examples/threadpool-example/agent/config-apollo/src/main/resources/bootstrap.properties +++ b/examples/threadpool-example/agent/config-apollo/src/main/resources/bootstrap.properties @@ -31,3 +31,18 @@ spring.dynamic.thread-pool.check-state-interval=3 spring.dynamic.thread-pool.apollo.namespace=application spring.dynamic.thread-pool.config-file-type=properties +spring.dynamic.thread-pool.executors[0].thread-name-prefix = DynamicThreadPoolConfig#FIELD1 +spring.dynamic.thread-pool.executors[0].core-pool-size = 2 +spring.dynamic.thread-pool.executors[0].thread-pool-id = cn.hippo4j.example.agent.config.apollo.ThreadPoolConfiguration#RUN_MESSAGE_SEND_TASK_EXECUTOR +spring.dynamic.thread-pool.executors[0].maximum-pool-size = 20 +spring.dynamic.thread-pool.executors[0].queue-capacity = 1024 +spring.dynamic.thread-pool.executors[0].blocking-queue = ResizableCapacityLinkedBlockingQueue +spring.dynamic.thread-pool.executors[0].execute-time-out = 800 +spring.dynamic.thread-pool.executors[0].rejected-handler = AbortPolicy +spring.dynamic.thread-pool.executors[0].keep-alive-time = 6691 +spring.dynamic.thread-pool.executors[0].allow-core-thread-time-out = true +spring.dynamic.thread-pool.executors[0].alarm = true +spring.dynamic.thread-pool.executors[0].active-alarm = 80 +spring.dynamic.thread-pool.executors[0].capacity-alarm = 80 +spring.dynamic.thread-pool.executors[0].notify.interval = 8 +spring.dynamic.thread-pool.executors[0].notify.receives = nobodyiam diff --git a/infra/common/pom.xml b/infra/common/pom.xml index 20f67cbb..ae48d1af 100644 --- a/infra/common/pom.xml +++ b/infra/common/pom.xml @@ -10,6 +10,11 @@ hippo4j-threadpool-infra-common + + com.google.code.gson + gson + ${gson.version} + com.fasterxml.jackson.core jackson-databind diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/AgentPackageNotFoundException.java b/infra/common/src/main/java/cn/hippo4j/common/boot/AgentPackageNotFoundException.java similarity index 96% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/AgentPackageNotFoundException.java rename to infra/common/src/main/java/cn/hippo4j/common/boot/AgentPackageNotFoundException.java index e26aae8a..273d47e6 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/AgentPackageNotFoundException.java +++ b/infra/common/src/main/java/cn/hippo4j/common/boot/AgentPackageNotFoundException.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.boot; +package cn.hippo4j.common.boot; public class AgentPackageNotFoundException extends Exception { diff --git a/infra/common/src/main/java/cn/hippo4j/common/boot/AgentPackagePath.java b/infra/common/src/main/java/cn/hippo4j/common/boot/AgentPackagePath.java new file mode 100644 index 00000000..1afbdfb7 --- /dev/null +++ b/infra/common/src/main/java/cn/hippo4j/common/boot/AgentPackagePath.java @@ -0,0 +1,85 @@ +/* + * 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.common.boot; + +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; + +/** + * AgentPackagePath is a flag and finder to locate the Hippo4j agent.jar. It gets the absolute path of the agent jar. + * The path is the required metadata for agent core looking up the plugins and toolkit activations. If the lookup + * mechanism fails, the agent will exit directly. + */ +public class AgentPackagePath { + + private static final ILog LOGGER = LogManager.getLogger(AgentPackagePath.class); + + private static File AGENT_PACKAGE_PATH; + + public static File getPath() throws AgentPackageNotFoundException { + if (AGENT_PACKAGE_PATH == null) { + AGENT_PACKAGE_PATH = findPath(); + } + return AGENT_PACKAGE_PATH; + } + + public static boolean isPathFound() { + return AGENT_PACKAGE_PATH != null; + } + + private static File findPath() throws AgentPackageNotFoundException { + String classResourcePath = AgentPackagePath.class.getName().replaceAll("\\.", "/") + ".class"; + + URL resource = ClassLoader.getSystemClassLoader().getResource(classResourcePath); + if (resource != null) { + String urlString = resource.toString(); + + LOGGER.debug("The beacon class location is {}.", urlString); + + int insidePathIndex = urlString.indexOf('!'); + boolean isInJar = insidePathIndex > -1; + + if (isInJar) { + urlString = urlString.substring(urlString.indexOf("file:"), insidePathIndex); + File agentJarFile = null; + try { + agentJarFile = new File(new URL(urlString).toURI()); + } catch (MalformedURLException | URISyntaxException e) { + LOGGER.error(e, "Can not locate agent jar file by url:" + urlString); + } + if (agentJarFile.exists()) { + return agentJarFile.getParentFile(); + } + } else { + int prefixLength = "file:".length(); + String classLocation = urlString.substring( + prefixLength, urlString.length() - classResourcePath.length()); + return new File(classLocation); + } + } + + LOGGER.error("Can not locate agent jar file."); + throw new AgentPackageNotFoundException("Can not locate agent jar file."); + } + +} diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/ClassCacheMode.java b/infra/common/src/main/java/cn/hippo4j/common/boot/ClassCacheMode.java similarity index 90% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/ClassCacheMode.java rename to infra/common/src/main/java/cn/hippo4j/common/boot/ClassCacheMode.java index d5baf606..5db729f5 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/plugin/bytebuddy/ClassCacheMode.java +++ b/infra/common/src/main/java/cn/hippo4j/common/boot/ClassCacheMode.java @@ -15,11 +15,8 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.plugin.bytebuddy; +package cn.hippo4j.common.boot; -/** - * ByteBuddy class cache mode - */ public enum ClassCacheMode { FILE, MEMORY -} +} \ No newline at end of file diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/DefaultNamedThreadFactory.java b/infra/common/src/main/java/cn/hippo4j/common/boot/DefaultNamedThreadFactory.java similarity index 97% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/DefaultNamedThreadFactory.java rename to infra/common/src/main/java/cn/hippo4j/common/boot/DefaultNamedThreadFactory.java index 41c41204..1bbe203c 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/boot/DefaultNamedThreadFactory.java +++ b/infra/common/src/main/java/cn/hippo4j/common/boot/DefaultNamedThreadFactory.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.boot; +package cn.hippo4j.common.boot; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/Config.java b/infra/common/src/main/java/cn/hippo4j/common/conf/Config.java old mode 100755 new mode 100644 similarity index 95% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/Config.java rename to infra/common/src/main/java/cn/hippo4j/common/conf/Config.java index 89e1c577..85030554 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/Config.java +++ b/infra/common/src/main/java/cn/hippo4j/common/conf/Config.java @@ -15,14 +15,13 @@ * 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; +package cn.hippo4j.common.conf; + +import cn.hippo4j.common.boot.ClassCacheMode; +import cn.hippo4j.common.logging.core.LogLevel; +import cn.hippo4j.common.logging.core.LogOutput; +import cn.hippo4j.common.logging.core.ResolverType; +import cn.hippo4j.common.toolkit.agent.Length; import java.util.Arrays; import java.util.List; diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/ConfigNotFoundException.java b/infra/common/src/main/java/cn/hippo4j/common/conf/ConfigNotFoundException.java similarity index 96% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/ConfigNotFoundException.java rename to infra/common/src/main/java/cn/hippo4j/common/conf/ConfigNotFoundException.java index 9d7d387c..b32bd864 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/ConfigNotFoundException.java +++ b/infra/common/src/main/java/cn/hippo4j/common/conf/ConfigNotFoundException.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.conf; +package cn.hippo4j.common.conf; public class ConfigNotFoundException extends Exception { diff --git a/infra/common/src/main/java/cn/hippo4j/common/conf/Constants.java b/infra/common/src/main/java/cn/hippo4j/common/conf/Constants.java new file mode 100644 index 00000000..1973cebe --- /dev/null +++ b/infra/common/src/main/java/cn/hippo4j/common/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.common.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 boolean IS_INIT_COMPLETED = false; + +} diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/RuntimeContextConfiguration.java b/infra/common/src/main/java/cn/hippo4j/common/conf/RuntimeContextConfiguration.java similarity index 96% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/RuntimeContextConfiguration.java rename to infra/common/src/main/java/cn/hippo4j/common/conf/RuntimeContextConfiguration.java index 0864baa0..372492eb 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/conf/RuntimeContextConfiguration.java +++ b/infra/common/src/main/java/cn/hippo4j/common/conf/RuntimeContextConfiguration.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.conf; +package cn.hippo4j.common.conf; public class RuntimeContextConfiguration { diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/ILog.java b/infra/common/src/main/java/cn/hippo4j/common/logging/api/ILog.java similarity index 97% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/ILog.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/api/ILog.java index 89f1031f..3bb9cba0 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/ILog.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/api/ILog.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.api; +package cn.hippo4j.common.logging.api; /** * The Log interface. It's very easy to understand, like any other log-component. Do just like log4j or log4j2 does. diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/LogManager.java b/infra/common/src/main/java/cn/hippo4j/common/logging/api/LogManager.java similarity index 95% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/LogManager.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/api/LogManager.java index cb6514bc..3c72d7ef 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/LogManager.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/api/LogManager.java @@ -15,9 +15,9 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.api; +package cn.hippo4j.common.logging.api; -import cn.hippo4j.agent.core.logging.core.PatternLogResolver; +import cn.hippo4j.common.logging.core.PatternLogResolver; /** * LogManager is the {@link LogResolver} implementation manager. By using {@link LogResolver}, {@link diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/LogResolver.java b/infra/common/src/main/java/cn/hippo4j/common/logging/api/LogResolver.java similarity index 96% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/LogResolver.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/api/LogResolver.java index 4f145dbb..a6cf32a0 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/LogResolver.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/api/LogResolver.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.api; +package cn.hippo4j.common.logging.api; /** * {@link LogResolver} just do only one thing: return the {@link ILog} implementation. diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/NoopLogger.java b/infra/common/src/main/java/cn/hippo4j/common/logging/api/NoopLogger.java similarity index 98% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/NoopLogger.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/api/NoopLogger.java index 9f544b63..99bc3db7 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/api/NoopLogger.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/api/NoopLogger.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.api; +package cn.hippo4j.common.logging.api; /** * No operation logger implementation. Just implement {@link ILog} interface, but do nothing. diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/AbstractLogger.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/AbstractLogger.java similarity index 91% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/AbstractLogger.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/AbstractLogger.java index 05a53ff0..c67121c7 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/AbstractLogger.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/AbstractLogger.java @@ -15,17 +15,11 @@ * 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; +package cn.hippo4j.common.logging.core; + +import cn.hippo4j.common.conf.Config; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.core.converters.*; import java.util.ArrayList; import java.util.HashMap; diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/Converter.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/Converter.java similarity index 95% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/Converter.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/Converter.java index 0bf8d979..27dd8981 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/Converter.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/Converter.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core; +package cn.hippo4j.common.logging.core; /** * The Converter, it is used to convert the LogEvent to the String. diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/FileWriter.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/FileWriter.java similarity index 94% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/FileWriter.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/FileWriter.java index e2fde308..6a263495 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/FileWriter.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/FileWriter.java @@ -15,18 +15,14 @@ * 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; +package cn.hippo4j.common.logging.core; + +import cn.hippo4j.common.boot.DefaultNamedThreadFactory; +import cn.hippo4j.common.conf.Config; +import cn.hippo4j.common.constant.Constants; +import cn.hippo4j.common.toolkit.agent.RunnableWithExceptionProtection; + +import java.io.*; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/IWriter.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/IWriter.java similarity index 95% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/IWriter.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/IWriter.java index 1dae6b82..d0acc94f 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/IWriter.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/IWriter.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core; +package cn.hippo4j.common.logging.core; public interface IWriter { diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/JsonLogResolver.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/JsonLogResolver.java similarity index 88% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/JsonLogResolver.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/JsonLogResolver.java index 913bc770..befbdc9a 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/JsonLogResolver.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/JsonLogResolver.java @@ -15,10 +15,10 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core; +package cn.hippo4j.common.logging.core; -import cn.hippo4j.agent.core.logging.api.ILog; -import cn.hippo4j.agent.core.logging.api.LogResolver; +import cn.hippo4j.common.logging.api.LogResolver; +import cn.hippo4j.common.logging.api.ILog; import com.google.gson.Gson; public class JsonLogResolver implements LogResolver { diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/JsonLogger.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/JsonLogger.java similarity index 96% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/JsonLogger.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/JsonLogger.java index fc657ef1..001684df 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/JsonLogger.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/JsonLogger.java @@ -15,9 +15,9 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core; +package cn.hippo4j.common.logging.core; -import cn.hippo4j.agent.core.logging.core.converters.LiteralConverter; +import cn.hippo4j.common.logging.core.converters.LiteralConverter; import com.google.gson.Gson; import java.util.HashMap; diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogEvent.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/LogEvent.java similarity index 97% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogEvent.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/LogEvent.java index 1246232d..a0aa11ab 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogEvent.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/LogEvent.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core; +package cn.hippo4j.common.logging.core; /** * The representation of logging events. This instance is pass around to the List of Converter. diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogLevel.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/LogLevel.java similarity index 95% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogLevel.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/LogLevel.java index facc35b3..73791af9 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogLevel.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/LogLevel.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core; +package cn.hippo4j.common.logging.core; public enum LogLevel { TRACE, DEBUG, INFO, WARN, ERROR, OFF diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogMessageHolder.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/LogMessageHolder.java similarity index 96% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogMessageHolder.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/LogMessageHolder.java index 244733b5..005d2d4d 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogMessageHolder.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/LogMessageHolder.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core; +package cn.hippo4j.common.logging.core; /** * The LogMessageHolder is a {@link String} holder, in order to in-process propagation String across the diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogOutput.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/LogOutput.java similarity index 95% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogOutput.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/LogOutput.java index 2bf279a8..c8ba7bc2 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/LogOutput.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/LogOutput.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core; +package cn.hippo4j.common.logging.core; public enum LogOutput { FILE, CONSOLE diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/Parser.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/Parser.java similarity index 98% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/Parser.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/Parser.java index 999b78f7..24cabc4a 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/Parser.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/Parser.java @@ -15,9 +15,9 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core; +package cn.hippo4j.common.logging.core; -import cn.hippo4j.agent.core.logging.core.converters.LiteralConverter; +import cn.hippo4j.common.logging.core.converters.LiteralConverter; import java.util.ArrayList; import java.util.List; diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/PatternLogResolver.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/PatternLogResolver.java similarity index 85% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/PatternLogResolver.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/PatternLogResolver.java index 075c2e01..0a761683 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/PatternLogResolver.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/PatternLogResolver.java @@ -15,11 +15,11 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core; +package cn.hippo4j.common.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; +import cn.hippo4j.common.conf.Config; +import cn.hippo4j.common.logging.api.LogResolver; +import cn.hippo4j.common.logging.api.ILog; public class PatternLogResolver implements LogResolver { diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/PatternLogger.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/PatternLogger.java similarity index 94% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/PatternLogger.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/PatternLogger.java index b9e4ef67..c713bb5a 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/PatternLogger.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/PatternLogger.java @@ -15,10 +15,9 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core; +package cn.hippo4j.common.logging.core; -import cn.hippo4j.agent.core.logging.api.ILog; -import cn.hippo4j.agent.core.util.StringUtil; +import cn.hippo4j.common.toolkit.StringUtil; /** * A flexible Logger configurable with pattern string. This is default implementation of {@link ILog} This can parse a diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/ResolverType.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/ResolverType.java similarity index 95% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/ResolverType.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/ResolverType.java index 734d9294..774cb310 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/ResolverType.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/ResolverType.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core; +package cn.hippo4j.common.logging.core; public enum ResolverType { JSON, PATTERN diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/SystemOutWriter.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/SystemOutWriter.java similarity index 96% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/SystemOutWriter.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/SystemOutWriter.java index e4cce119..312d8fd2 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/SystemOutWriter.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/SystemOutWriter.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core; +package cn.hippo4j.common.logging.core; import java.io.PrintStream; diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/WriterFactory.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/WriterFactory.java similarity index 78% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/WriterFactory.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/WriterFactory.java index 72f2ddee..4bf5331b 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/WriterFactory.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/WriterFactory.java @@ -15,14 +15,13 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core; +package cn.hippo4j.common.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; +import cn.hippo4j.common.boot.AgentPackageNotFoundException; +import cn.hippo4j.common.boot.AgentPackagePath; +import cn.hippo4j.common.conf.Config; +import cn.hippo4j.common.conf.Constants; +import cn.hippo4j.common.toolkit.StringUtil; public class WriterFactory { @@ -35,8 +34,7 @@ public class WriterFactory { if (WRITER != null) { return WRITER; } - if (SnifferConfigInitializer.isInitCompleted() - && PluginFinder.isPluginInitCompleted() + if (Constants.IS_INIT_COMPLETED && AgentPackagePath.isPathFound()) { if (StringUtil.isEmpty(Config.Logging.DIR)) { try { diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/AgentNameConverter.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/AgentNameConverter.java similarity index 83% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/AgentNameConverter.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/AgentNameConverter.java index 21090d25..d6015b21 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/AgentNameConverter.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/AgentNameConverter.java @@ -15,11 +15,11 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core.converters; +package cn.hippo4j.common.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; +import cn.hippo4j.common.conf.Config; +import cn.hippo4j.common.logging.core.Converter; +import cn.hippo4j.common.logging.core.LogEvent; public class AgentNameConverter implements Converter { diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ClassConverter.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/ClassConverter.java similarity index 87% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ClassConverter.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/ClassConverter.java index 5dc9951d..96dbcdc2 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ClassConverter.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/ClassConverter.java @@ -15,10 +15,10 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core.converters; +package cn.hippo4j.common.logging.core.converters; -import cn.hippo4j.agent.core.logging.core.Converter; -import cn.hippo4j.agent.core.logging.core.LogEvent; +import cn.hippo4j.common.logging.core.Converter; +import cn.hippo4j.common.logging.core.LogEvent; /** * Just return logEvent.getTargetClass(). diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/DateConverter.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/DateConverter.java similarity index 88% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/DateConverter.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/DateConverter.java index e3a68067..f18da36b 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/DateConverter.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/DateConverter.java @@ -15,10 +15,10 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core.converters; +package cn.hippo4j.common.logging.core.converters; -import cn.hippo4j.agent.core.logging.core.Converter; -import cn.hippo4j.agent.core.logging.core.LogEvent; +import cn.hippo4j.common.logging.core.Converter; +import cn.hippo4j.common.logging.core.LogEvent; import java.text.SimpleDateFormat; import java.util.Date; diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/LevelConverter.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/LevelConverter.java similarity index 87% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/LevelConverter.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/LevelConverter.java index 775c97ce..ff818c18 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/LevelConverter.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/LevelConverter.java @@ -15,10 +15,10 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core.converters; +package cn.hippo4j.common.logging.core.converters; -import cn.hippo4j.agent.core.logging.core.Converter; -import cn.hippo4j.agent.core.logging.core.LogEvent; +import cn.hippo4j.common.logging.core.Converter; +import cn.hippo4j.common.logging.core.LogEvent; /** * Just return logEvent.getLevel().name() diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/LiteralConverter.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/LiteralConverter.java similarity index 88% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/LiteralConverter.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/LiteralConverter.java index bd334955..58bb4734 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/LiteralConverter.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/LiteralConverter.java @@ -15,10 +15,10 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core.converters; +package cn.hippo4j.common.logging.core.converters; -import cn.hippo4j.agent.core.logging.core.Converter; -import cn.hippo4j.agent.core.logging.core.LogEvent; +import cn.hippo4j.common.logging.core.Converter; +import cn.hippo4j.common.logging.core.LogEvent; /** * This Converter is used to return the literal. diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/MessageConverter.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/MessageConverter.java similarity index 87% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/MessageConverter.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/MessageConverter.java index a38cc3c7..fc15990f 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/MessageConverter.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/MessageConverter.java @@ -15,10 +15,10 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core.converters; +package cn.hippo4j.common.logging.core.converters; -import cn.hippo4j.agent.core.logging.core.Converter; -import cn.hippo4j.agent.core.logging.core.LogEvent; +import cn.hippo4j.common.logging.core.Converter; +import cn.hippo4j.common.logging.core.LogEvent; /** * Just return the logEvent.getMessage() diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ThreadConverter.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/ThreadConverter.java similarity index 87% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ThreadConverter.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/ThreadConverter.java index 7bd6b16f..a22e16df 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ThreadConverter.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/ThreadConverter.java @@ -15,10 +15,10 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core.converters; +package cn.hippo4j.common.logging.core.converters; -import cn.hippo4j.agent.core.logging.core.Converter; -import cn.hippo4j.agent.core.logging.core.LogEvent; +import cn.hippo4j.common.logging.core.Converter; +import cn.hippo4j.common.logging.core.LogEvent; /** * Just return the Thread.currentThread().getName() diff --git a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ThrowableConverter.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/ThrowableConverter.java similarity index 88% rename from agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ThrowableConverter.java rename to infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/ThrowableConverter.java index 8d307f0a..3bab6e2b 100644 --- a/agent/hippo4j-agent-core/src/main/java/cn/hippo4j/agent/core/logging/core/converters/ThrowableConverter.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/converters/ThrowableConverter.java @@ -15,11 +15,11 @@ * limitations under the License. */ -package cn.hippo4j.agent.core.logging.core.converters; +package cn.hippo4j.common.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 cn.hippo4j.common.constant.Constants; +import cn.hippo4j.common.logging.core.Converter; +import cn.hippo4j.common.logging.core.LogEvent; import java.io.ByteArrayOutputStream; import java.io.IOException; diff --git a/infra/common/src/main/java/cn/hippo4j/common/toolkit/StringUtil.java b/infra/common/src/main/java/cn/hippo4j/common/toolkit/StringUtil.java index 7c74e2cc..d8506506 100644 --- a/infra/common/src/main/java/cn/hippo4j/common/toolkit/StringUtil.java +++ b/infra/common/src/main/java/cn/hippo4j/common/toolkit/StringUtil.java @@ -390,4 +390,69 @@ public class StringUtil { } return false; } + + 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/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/ConfigInitializer.java b/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/ConfigInitializer.java new file mode 100644 index 00000000..d1b250e9 --- /dev/null +++ b/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/ConfigInitializer.java @@ -0,0 +1,210 @@ +/* + * 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.common.toolkit.agent; + +import cn.hippo4j.common.toolkit.StringUtil; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.*; + +/** + * 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/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/Length.java b/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/Length.java new file mode 100644 index 00000000..f926a9da --- /dev/null +++ b/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/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.common.toolkit.agent; + +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/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/PlaceholderConfigurerSupport.java b/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/PlaceholderConfigurerSupport.java new file mode 100644 index 00000000..df6d59ec --- /dev/null +++ b/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/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.common.toolkit.agent; + +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/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/PropertyPlaceholderHelper.java b/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/PropertyPlaceholderHelper.java new file mode 100644 index 00000000..1add1039 --- /dev/null +++ b/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/PropertyPlaceholderHelper.java @@ -0,0 +1,206 @@ +/* + * 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.common.toolkit.agent; + +import cn.hippo4j.common.toolkit.StringUtil; + +import java.util.*; + +/** + * 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/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/RunnableWithExceptionProtection.java b/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/RunnableWithExceptionProtection.java new file mode 100644 index 00000000..f4179cf1 --- /dev/null +++ b/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/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.common.toolkit.agent; + +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/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/refresher/event/DynamicThreadPoolRefreshListener.java b/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/refresher/event/DynamicThreadPoolRefreshListener.java index 7a49151d..252e6d0b 100644 --- a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/refresher/event/DynamicThreadPoolRefreshListener.java +++ b/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/refresher/event/DynamicThreadPoolRefreshListener.java @@ -24,12 +24,12 @@ import cn.hippo4j.common.executor.support.RejectedPolicyTypeEnum; import cn.hippo4j.common.executor.support.ResizableCapacityLinkedBlockingQueue; import cn.hippo4j.common.extension.design.Observer; import cn.hippo4j.common.extension.design.ObserverMessage; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; import cn.hippo4j.common.model.executor.ExecutorProperties; import cn.hippo4j.common.toolkit.ThreadPoolExecutorUtil; import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; import lombok.RequiredArgsConstructor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.List; import java.util.Objects; @@ -46,7 +46,7 @@ import static cn.hippo4j.common.constant.ChangeThreadPoolConstants.CHANGE_THREAD @RequiredArgsConstructor public class DynamicThreadPoolRefreshListener implements Observer { - private static final Logger LOGGER = LoggerFactory.getLogger(DynamicThreadPoolRefreshListener.class); + private static final ILog LOG = LogManager.getLogger(DynamicThreadPoolRefreshListener.class); @Override public void accept(ObserverMessage observerMessage) { @@ -127,15 +127,14 @@ public class DynamicThreadPoolRefreshListener implements Observer queue = (ResizableCapacityLinkedBlockingQueue) executor.getQueue(); queue.setCapacity(properties.getQueueCapacity()); } else { - LOGGER.warn("The queue length cannot be modified. Queue type mismatch. Current queue type: {}", executor.getQueue().getClass().getSimpleName()); + LOG.warn("The queue length cannot be modified. Queue type mismatch. Current queue type: {}", executor.getQueue().getClass().getSimpleName()); } } } private void sendChangeNotificationMessage(ThreadPoolExecutorHolder executorHolder, ExecutorProperties properties) { ExecutorProperties executorProperties = executorHolder.getExecutorProperties(); - // TODO log cannot be printed - LOGGER.info(CHANGE_THREAD_POOL_TEXT, + LOG.info(CHANGE_THREAD_POOL_TEXT, executorHolder.getThreadPoolId(), String.format(CHANGE_DELIMITER, executorProperties.getCorePoolSize(), properties.getCorePoolSize()), String.format(CHANGE_DELIMITER, executorProperties.getMaximumPoolSize(), properties.getMaximumPoolSize()), diff --git a/pom.xml b/pom.xml index d63fbf73..6237e82f 100644 --- a/pom.xml +++ b/pom.xml @@ -78,6 +78,7 @@ 2.22.1 3.1.0 2.3.2.RELEASE + 2.8.9 From 59e89ef2d686bc80f181dbd97273e18af6eee1d7 Mon Sep 17 00:00:00 2001 From: Pan-YuJie <646836760@qq.com> Date: Mon, 13 Nov 2023 13:47:02 +0800 Subject: [PATCH 05/14] Remove import * --- .../cn/hippo4j/agent/bootstrap/Hippo4jAgent.java | 11 +++++++++-- .../agent/core/jvm/LoadedLibraryCollector.java | 8 +++++++- .../bytebuddy/CacheableTransformerDecorator.java | 6 +++++- .../enhance/ClassEnhancePluginDefine.java | 6 +++++- .../cn/hippo4j/common/logging/core/FileWriter.java | 6 +++++- .../toolkit/agent/PropertyPlaceholderHelper.java | 6 +++++- .../hippo4j/common/toolkit/MessageConvertTest.java | 12 ++++++++++-- 7 files changed, 46 insertions(+), 9 deletions(-) diff --git a/agent/hippo4j-agent-bootstrap/src/main/java/cn/hippo4j/agent/bootstrap/Hippo4jAgent.java b/agent/hippo4j-agent-bootstrap/src/main/java/cn/hippo4j/agent/bootstrap/Hippo4jAgent.java index a4de6591..4da18b4a 100644 --- a/agent/hippo4j-agent-bootstrap/src/main/java/cn/hippo4j/agent/bootstrap/Hippo4jAgent.java +++ b/agent/hippo4j-agent-bootstrap/src/main/java/cn/hippo4j/agent/bootstrap/Hippo4jAgent.java @@ -20,7 +20,12 @@ package cn.hippo4j.agent.bootstrap; import cn.hippo4j.agent.core.boot.ServiceManager; import cn.hippo4j.agent.core.conf.SnifferConfigInitializer; import cn.hippo4j.agent.core.jvm.LoadedLibraryCollector; -import cn.hippo4j.agent.core.plugin.*; +import cn.hippo4j.agent.core.plugin.AbstractClassEnhancePluginDefine; +import cn.hippo4j.agent.core.plugin.EnhanceContext; +import cn.hippo4j.agent.core.plugin.InstrumentDebuggingClass; +import cn.hippo4j.agent.core.plugin.PluginBootstrap; +import cn.hippo4j.agent.core.plugin.PluginException; +import cn.hippo4j.agent.core.plugin.PluginFinder; import cn.hippo4j.agent.core.plugin.bootstrap.BootstrapInstrumentBoost; import cn.hippo4j.agent.core.plugin.bytebuddy.CacheableTransformerDecorator; import cn.hippo4j.agent.core.plugin.jdk9module.JDK9ModuleExporter; @@ -44,7 +49,9 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import static net.bytebuddy.matcher.ElementMatchers.*; +import static net.bytebuddy.matcher.ElementMatchers.nameContains; +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; +import static net.bytebuddy.matcher.ElementMatchers.not; /** * Hippo4j Agent 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 index 6bae20aa..aebec1f0 100644 --- 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 @@ -28,7 +28,13 @@ import java.lang.management.ManagementFactory; import java.net.URL; import java.net.URLClassLoader; import java.text.SimpleDateFormat; -import java.util.*; +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 { 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 index f0ed68c6..fd653f60 100644 --- 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 @@ -28,7 +28,11 @@ import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.agent.builder.ResettableClassFileTransformer; import net.bytebuddy.utility.RandomString; -import java.io.*; +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; 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 index 8378aa35..5790f80c 100644 --- 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 @@ -21,7 +21,11 @@ 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.*; +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; diff --git a/infra/common/src/main/java/cn/hippo4j/common/logging/core/FileWriter.java b/infra/common/src/main/java/cn/hippo4j/common/logging/core/FileWriter.java index 6a263495..72397d71 100644 --- a/infra/common/src/main/java/cn/hippo4j/common/logging/core/FileWriter.java +++ b/infra/common/src/main/java/cn/hippo4j/common/logging/core/FileWriter.java @@ -22,7 +22,11 @@ import cn.hippo4j.common.conf.Config; import cn.hippo4j.common.constant.Constants; import cn.hippo4j.common.toolkit.agent.RunnableWithExceptionProtection; -import java.io.*; +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; diff --git a/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/PropertyPlaceholderHelper.java b/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/PropertyPlaceholderHelper.java index 1add1039..8d945584 100644 --- a/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/PropertyPlaceholderHelper.java +++ b/infra/common/src/main/java/cn/hippo4j/common/toolkit/agent/PropertyPlaceholderHelper.java @@ -19,7 +19,11 @@ package cn.hippo4j.common.toolkit.agent; import cn.hippo4j.common.toolkit.StringUtil; -import java.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 diff --git a/infra/common/src/test/java/cn/hippo4j/common/toolkit/MessageConvertTest.java b/infra/common/src/test/java/cn/hippo4j/common/toolkit/MessageConvertTest.java index 52470aad..4d834dcf 100644 --- a/infra/common/src/test/java/cn/hippo4j/common/toolkit/MessageConvertTest.java +++ b/infra/common/src/test/java/cn/hippo4j/common/toolkit/MessageConvertTest.java @@ -18,13 +18,21 @@ package cn.hippo4j.common.toolkit; import cn.hippo4j.common.model.ThreadPoolRunStateInfo; -import cn.hippo4j.common.monitor.*; +import cn.hippo4j.common.monitor.AbstractMessage; +import cn.hippo4j.common.monitor.Message; +import cn.hippo4j.common.monitor.MessageTypeEnum; +import cn.hippo4j.common.monitor.MessageWrapper; +import cn.hippo4j.common.monitor.RuntimeMessage; import org.junit.Test; import org.junit.jupiter.api.Assertions; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /*** * @description : Todo From eaf5f7d2e12a11989acb499fee5026fa6f3cfa0b Mon Sep 17 00:00:00 2001 From: Pan-YuJie <646836760@qq.com> Date: Fri, 9 Aug 2024 22:53:38 +0800 Subject: [PATCH 06/14] fix: The Gson version dependency in pom under the infra package is provided --- infra/common/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/infra/common/pom.xml b/infra/common/pom.xml index ae48d1af..607b5caf 100644 --- a/infra/common/pom.xml +++ b/infra/common/pom.xml @@ -14,6 +14,7 @@ com.google.code.gson gson ${gson.version} + provided com.fasterxml.jackson.core From 298f6b4cf8813eadfc5ac7036333c3d76c75a899 Mon Sep 17 00:00:00 2001 From: Pan-YuJie <646836760@qq.com> Date: Tue, 20 Aug 2024 17:38:23 +0800 Subject: [PATCH 07/14] fix:Fix send threadPool change notification message log --- .../config/refresher/event/DynamicThreadPoolRefreshListener.java | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/refresher/event/DynamicThreadPoolRefreshListener.java b/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/refresher/event/DynamicThreadPoolRefreshListener.java index 252e6d0b..0cb87c96 100644 --- a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/refresher/event/DynamicThreadPoolRefreshListener.java +++ b/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/refresher/event/DynamicThreadPoolRefreshListener.java @@ -140,6 +140,7 @@ public class DynamicThreadPoolRefreshListener implements Observer Date: Wed, 21 Aug 2024 15:19:46 +0800 Subject: [PATCH 08/14] feat:Agent Nacos dynamic refresh Initialize --- .../core/conf/SnifferConfigInitializer.java | 2 +- .../spring-boot-2x-plugin/pom.xml | 5 + ...ynamicThreadPoolChangeHandlerSpring2x.java | 138 ++++++++++++++++++ .../EventPublishingStartedInterceptor.java | 5 +- .../spring/common/conf/SpringBootConfig.java | 12 ++ .../agent/config-nacos/pom.xml | 68 +++++++++ .../AgentConfigNacosExampleApplication.java | 35 +++++ .../config/nacos/ThreadPoolConfiguration.java | 55 +++++++ .../src/main/resources/bootstrap.properties | 59 ++++++++ examples/threadpool-example/agent/pom.xml | 1 + 10 files changed, 378 insertions(+), 2 deletions(-) create mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/NacosDynamicThreadPoolChangeHandlerSpring2x.java create mode 100644 examples/threadpool-example/agent/config-nacos/pom.xml create mode 100644 examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java create mode 100644 examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/ThreadPoolConfiguration.java create mode 100644 examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties 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 index b1c0a56c..ef811800 100644 --- 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 @@ -17,8 +17,8 @@ package cn.hippo4j.agent.core.conf; +import cn.hippo4j.agent.core.boot.AgentPackagePath; import cn.hippo4j.common.boot.AgentPackageNotFoundException; -import cn.hippo4j.common.boot.AgentPackagePath; import cn.hippo4j.common.conf.Config; import cn.hippo4j.common.conf.ConfigNotFoundException; import cn.hippo4j.common.logging.api.ILog; 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 index dc3d87cf..40c0198a 100644 --- 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 @@ -43,5 +43,10 @@ apollo-client provided + + com.alibaba.nacos + nacos-client + 2.2.1 + \ 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/NacosDynamicThreadPoolChangeHandlerSpring2x.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/NacosDynamicThreadPoolChangeHandlerSpring2x.java new file mode 100644 index 00000000..e13517ad --- /dev/null +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/NacosDynamicThreadPoolChangeHandlerSpring2x.java @@ -0,0 +1,138 @@ +/* + * 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.conf.SpringBootConfig; +import cn.hippo4j.common.executor.ThreadFactoryBuilder; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; +import cn.hippo4j.threadpool.dynamic.mode.config.parser.ConfigParserHandler; +import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; +import cn.hippo4j.threadpool.dynamic.mode.config.refresher.AbstractConfigThreadPoolDynamicRefresh; +import com.alibaba.nacos.api.NacosFactory; +import com.alibaba.nacos.api.PropertyKeyConst; +import com.alibaba.nacos.api.config.ConfigService; +import com.alibaba.nacos.api.config.listener.Listener; +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.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledThreadPoolExecutor; + +import static cn.hippo4j.common.constant.Constants.DEFAULT_NAMESPACE_ID; + +/** + * NacosDynamicThreadPoolChangeHandlerSpring2x is responsible for handling dynamic thread pool + * configuration changes in a Spring environment by listening to configuration updates from Nacos. + *

+ * This class extends {@link AbstractConfigThreadPoolDynamicRefresh} and implements the logic + * to register a Nacos listener, handle configuration changes, and dynamically refresh the thread pool + * properties based on the new configuration. + *

+ * The handler is specifically tailored for use with Spring 2.x and integrates with Hippo4j's + * dynamic thread pool management system. + * + */ +public class NacosDynamicThreadPoolChangeHandlerSpring2x extends AbstractConfigThreadPoolDynamicRefresh { + + private static final ILog LOGGER = LogManager.getLogger(NacosDynamicThreadPoolChangeHandlerSpring2x.class); + + /** + * Registers a listener with Nacos to monitor for changes in the thread pool configuration. + *

+ * This method sets up the Nacos {@link ConfigService} with the server address and namespace + * from the Spring Boot configuration. It then adds a listener that will receive and process + * configuration updates, triggering a dynamic refresh of thread pool settings. + */ + @Override + public void registerListener() { + // Retrieve necessary configuration properties + String configFileType = SpringBootConfig.Spring.Dynamic.Thread_Pool.CONFIG_FILE_TYPE; + String serverAddr = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.SERVER_ADDR; + String dataId = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.DATA_ID; + String namespace = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.NAMESPACE.get(0); + namespace = namespace.equals(DEFAULT_NAMESPACE_ID) ? "" : namespace; + String group = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.GROUP; + try { + // Initialize Nacos ConfigService with the provided properties + Properties properties = new Properties(); + properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr); + properties.put(PropertyKeyConst.NAMESPACE, namespace); + ConfigService configService = NacosFactory.createConfigService(properties); + + // Define the listener to handle configuration changes + Listener configChangeListener = new Listener() { + + @Override + public void receiveConfigInfo(String configInfo) { + LOGGER.debug("Received configuration: " + configInfo); + Map changeValueMap = new HashMap<>(); + try { + // Parse the configuration and map the values to the appropriate keys + Map configInfoMap = ConfigParserHandler.getInstance().parseConfig(configInfo, configFileType); + configInfoMap.forEach((key, value) -> { + if (key instanceof String) { + changeValueMap.put((String) key, value); + } + }); + } catch (IOException e) { + LOGGER.error(e, "[Hippo4j-Agent] Dynamic thread pool refresher, Failed to resolve configuration. configFileType: {} configInfo: {} ", configFileType, configInfo); + } + // Trigger the dynamic refresh with the parsed configuration + dynamicRefresh(configFileType, configInfo, changeValueMap); + } + + @Override + public Executor getExecutor() { + return new ScheduledThreadPoolExecutor( + 1, + ThreadFactoryBuilder.builder().daemon(true).prefix("client.dynamic.refresh.agent").build()); + } + }; + // Add the listener to the Nacos ConfigService + configService.addListener(dataId, group, configChangeListener); + } catch (Exception e) { + LOGGER.error(e, "[Hippo4j-Agent] Dynamic thread pool refresher, add Nacos listener failure. namespace: {} data-id: {} group: {}", namespace, dataId, group); + } + LOGGER.info("[Hippo4j-Agent] Dynamic thread pool refresher, added Nacos listener successfully. namespace: {} data-id: {} group: {}", namespace, dataId, group); + } + + /** + * Builds and binds the {@link BootstrapConfigProperties} from the given configuration map. + *

+ * This method uses Spring's {@link Binder} to bind the configuration values to an instance + * of {@link BootstrapConfigProperties}, which can then be used to configure the thread pool + * dynamically. + * + * @param configInfo the configuration map containing properties to bind. + * @return the bound {@link BootstrapConfigProperties} instance. + */ + @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/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 index d9df4772..45423564 100644 --- 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 @@ -17,6 +17,7 @@ package cn.hippo4j.agent.plugin.spring.boot.v2.interceptor; +import cn.hippo4j.agent.plugin.spring.boot.v2.NacosDynamicThreadPoolChangeHandlerSpring2x; import cn.hippo4j.common.logging.api.ILog; import cn.hippo4j.common.logging.api.LogManager; import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance; @@ -55,7 +56,9 @@ public class EventPublishingStartedInterceptor implements InstanceMethodsAroundI return ret; } SpringPropertiesLoader.loadSpringProperties(context.getEnvironment()); - ThreadPoolDynamicRefresh dynamicRefresh = new DynamicThreadPoolChangeHandlerSpring2x(); + // ThreadPoolDynamicRefresh dynamicRefresh = new DynamicThreadPoolChangeHandlerSpring2x(); + // TODO Nacos配置 + ThreadPoolDynamicRefresh dynamicRefresh = new NacosDynamicThreadPoolChangeHandlerSpring2x(); dynamicRefresh.registerListener(); AbstractSubjectCenter.register(AbstractSubjectCenter.SubjectType.THREAD_POOL_DYNAMIC_REFRESH, new DynamicThreadPoolRefreshListener()); 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 index ca979432..9eaecfba 100644 --- 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 @@ -52,6 +52,18 @@ public class SpringBootConfig { public static List NAMESPACE = Arrays.asList("application"); } + @SpringBootConfigNode(root = SpringBootConfig.class) + public static class Nacos { + + public static String SERVER_ADDR = "localhost"; + + public static List NAMESPACE = Arrays.asList(""); + + public static String DATA_ID = ""; + + public static String GROUP = "DEFAULT_GROUP"; + } + /** * Monitor */ diff --git a/examples/threadpool-example/agent/config-nacos/pom.xml b/examples/threadpool-example/agent/config-nacos/pom.xml new file mode 100644 index 00000000..52c783aa --- /dev/null +++ b/examples/threadpool-example/agent/config-nacos/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + cn.hippo4j + hippo4j-threadpool-agent-example + ${revision} + + + hippo4j-threadpool-agent-config-nacos-example + + + true + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + com.alibaba.boot + nacos-config-spring-boot-starter + 0.2.12 + + + org.springframework.cloud + spring-cloud-context + 2.2.5.RELEASE + + + org.slf4j + slf4j-api + 1.7.21 + + + io.micrometer + micrometer-registry-prometheus + + + org.springframework.boot + spring-boot-starter-actuator + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + \ No newline at end of file diff --git a/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java b/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java new file mode 100644 index 00000000..b05949da --- /dev/null +++ b/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.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.example.agent.config.nacos; + +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.spring.context.annotation.config.EnableNacosConfig; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Agent config Nacos example application. + */ +@SpringBootApplication +@EnableNacosConfig +public class AgentConfigNacosExampleApplication { + + public static void main(String[] args) throws NacosException { + SpringApplication.run(AgentConfigNacosExampleApplication.class, args); + } +} diff --git a/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/ThreadPoolConfiguration.java b/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/ThreadPoolConfiguration.java new file mode 100644 index 00000000..9f60e753 --- /dev/null +++ b/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/ThreadPoolConfiguration.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.example.agent.config.nacos; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +@Configuration +public class ThreadPoolConfiguration { + + // ------------------------------------------------------------------------- + // 未使用 Hippo4j,原始定义线程池创建方式 + // ------------------------------------------------------------------------- + + @Bean + public ThreadPoolExecutor runMessageSendTaskExecutor() { + LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue<>(1024); + return new ThreadPoolExecutor( + 1, + 10, + 1024, + TimeUnit.SECONDS, + linkedBlockingQueue); + } + + // ------------------------------------------------------------------------- + // 演示 Agent 模式修改线程池 + // ------------------------------------------------------------------------- + + public static final ThreadPoolExecutor RUN_MESSAGE_SEND_TASK_EXECUTOR = new ThreadPoolExecutor( + 1, + 10, + 1024, + TimeUnit.SECONDS, + new LinkedBlockingQueue<>(1024)); +} diff --git a/examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties b/examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties new file mode 100644 index 00000000..f145868b --- /dev/null +++ b/examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties @@ -0,0 +1,59 @@ +server.port=8092 +server.servlet.context-path=/example + +nacos.config.auto-refresh=true +nacos.config.bootstrap.enable=true +# The following parameters are used for testing +nacos.config.server-addr=127.0.0.1:8848 +nacos.config.data-id=dynamic-threadpool-example-config +spring.profiles.active=dev +spring.application.name=hippo4j-config-nacos-spring-boot-starter-example +management.metrics.export.prometheus.enabled=true +management.server.port=29998 +management.endpoints.web.exposure.include=* +spring.dynamic.thread-pool.enable=true +spring.dynamic.thread-pool.banner=true +spring.dynamic.thread-pool.check-state-interval=3 +#spring.dynamic.thread-pool.monitor.enable=true +#spring.dynamic.thread-pool.monitor.collect-types=micrometer +#spring.dynamic.thread-pool.monitor.thread-pool-types=dynamic,web +#spring.dynamic.thread-pool.monitor.initial-delay=10000 +#spring.dynamic.thread-pool.monitor.collect-interval=5000 +#spring.dynamic.thread-pool.notify-platforms[0].platform=WECHAT +#spring.dynamic.thread-pool.notify-platforms[0].token=ac0426a5-c712-474c-9bff-72b8b8f5caff +#spring.dynamic.thread-pool.notify-platforms[1].platform=DING +#spring.dynamic.thread-pool.notify-platforms[1].token=56417ebba6a27ca352f0de77a2ae9da66d01f39610b5ee8a6033c60ef9071c55 +#spring.dynamic.thread-pool.notify-platforms[2].platform=LARK +#spring.dynamic.thread-pool.notify-platforms[2].token=2cbf2808-3839-4c26-a04d-fd201dd51f9e +spring.dynamic.thread-pool.nacos.server-addr=127.0.0.1:8848 +spring.dynamic.thread-pool.nacos.data-id=dynamic-threadpool-example-config +spring.dynamic.thread-pool.nacos.group=DEFAULT_GROUP +spring.dynamic.thread-pool.nacos.namespace=public + +spring.dynamic.thread-pool.config-file-type=properties + +spring.dynamic.thread-pool.executors[0].thread-name-prefix = DynamicThreadPoolConfig#FIELD1 +spring.dynamic.thread-pool.executors[0].core-pool-size = 2 +spring.dynamic.thread-pool.executors[0].thread-pool-id = cn.hippo4j.example.agent.config.nacos.ThreadPoolConfiguration#RUN_MESSAGE_SEND_TASK_EXECUTOR +spring.dynamic.thread-pool.executors[0].maximum-pool-size = 20 +spring.dynamic.thread-pool.executors[0].queue-capacity = 1024 +spring.dynamic.thread-pool.executors[0].blocking-queue = ResizableCapacityLinkedBlockingQueue +spring.dynamic.thread-pool.executors[0].execute-time-out = 800 +spring.dynamic.thread-pool.executors[0].rejected-handler = AbortPolicy +spring.dynamic.thread-pool.executors[0].keep-alive-time = 6691 +spring.dynamic.thread-pool.executors[0].allow-core-thread-time-out = true +spring.dynamic.thread-pool.executors[0].alarm = true +spring.dynamic.thread-pool.executors[0].active-alarm = 80 +spring.dynamic.thread-pool.executors[0].capacity-alarm = 80 +spring.dynamic.thread-pool.executors[0].notify.interval = 8 +spring.dynamic.thread-pool.executors[0].notify.receives = nobodyiam +spring.dynamic.thread-pool.executors[1].thread-pool-id = runMessageSendTaskExecutor +spring.dynamic.thread-pool.executors[1].thread-name-prefix = runMessageSendTaskExecutor +spring.dynamic.thread-pool.executors[1].core-pool-size = 3 +spring.dynamic.thread-pool.executors[1].maximum-pool-size = 4 +spring.dynamic.thread-pool.executors[1].queue-capacity = 1024 +spring.dynamic.thread-pool.executors[1].blocking-queue = ResizableCapacityLinkedBlockingQueue +spring.dynamic.thread-pool.executors[1].execute-time-out = 800 +spring.dynamic.thread-pool.executors[1].rejected-handler = AbortPolicy +spring.dynamic.thread-pool.executors[1].keep-alive-time = 6691 +spring.dynamic.thread-pool.executors[1].allow-core-thread-time-out = true diff --git a/examples/threadpool-example/agent/pom.xml b/examples/threadpool-example/agent/pom.xml index 11fbc894..8dd4320a 100644 --- a/examples/threadpool-example/agent/pom.xml +++ b/examples/threadpool-example/agent/pom.xml @@ -18,5 +18,6 @@ config-apollo + config-nacos \ No newline at end of file From 280adef6342c3d1f82d0d7896d171abff42d58f3 Mon Sep 17 00:00:00 2001 From: Pan-YuJie <646836760@qq.com> Date: Mon, 26 Aug 2024 23:29:18 +0800 Subject: [PATCH 09/14] feat:Agent dynamic alarm Initialize --- .../spring-plugin-common/pom.xml | 6 + .../alarm/AgentModeNotifyConfigBuilder.java | 200 +++++++++++++++++ .../spring/common/conf/SpringBootConfig.java | 14 ++ .../support/SpringPropertiesLoader.java | 23 +- .../SpringThreadPoolRegisterSupport.java | 169 ++++++++++++-- .../support/ThreadPoolCheckAlarmSupport.java | 130 +++++++++++ .../common/toolkit/SpringPropertyBinder.java | 208 ++++++++++++++++++ .../agent/agent-example-core/pom.xml | 47 ++++ .../core/config}/ThreadPoolConfiguration.java | 4 +- .../core/inittest/AlarmSendMessageTest.java | 87 ++++++++ .../agent/config-apollo/pom.xml | 6 + .../AgentConfigApolloExampleApplication.java | 2 +- .../agent/config-nacos/pom.xml | 5 + .../AgentConfigNacosExampleApplication.java | 2 +- .../config/nacos/ThreadPoolConfiguration.java | 55 ----- examples/threadpool-example/agent/pom.xml | 1 + .../cn/hippo4j/common/constant/Constants.java | 9 + 17 files changed, 895 insertions(+), 73 deletions(-) create mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java create mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolCheckAlarmSupport.java create mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/toolkit/SpringPropertyBinder.java create mode 100644 examples/threadpool-example/agent/agent-example-core/pom.xml rename examples/threadpool-example/agent/{config-apollo/src/main/java/cn/hippo4j/example/agent/config/apollo => agent-example-core/src/main/java/cn/hippo4j/example/agent/core/config}/ThreadPoolConfiguration.java (92%) create mode 100644 examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.java delete mode 100644 examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/ThreadPoolConfiguration.java 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 index f0336260..c3780048 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml @@ -43,5 +43,11 @@ ${project.version} provided + + cn.hippo4j + hippo4j-threadpool-kernel-alarm + ${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/alarm/AgentModeNotifyConfigBuilder.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java new file mode 100644 index 00000000..d1345334 --- /dev/null +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java @@ -0,0 +1,200 @@ +/* + * 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.alarm; + +import cn.hippo4j.agent.plugin.spring.common.support.ThreadPoolCheckAlarmSupport; +import cn.hippo4j.common.api.IExecutorProperties; +import cn.hippo4j.common.model.executor.ExecutorNotifyProperties; +import cn.hippo4j.common.model.executor.ExecutorProperties; +import cn.hippo4j.common.toolkit.CollectionUtil; +import cn.hippo4j.common.toolkit.StringUtil; +import cn.hippo4j.threadpool.dynamic.mode.config.properties.NotifyPlatformProperties; +import cn.hippo4j.threadpool.message.api.NotifyConfigBuilder; +import cn.hippo4j.threadpool.message.api.NotifyConfigDTO; +import cn.hippo4j.threadpool.message.core.service.AlarmControlHandler; +import lombok.AllArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import static cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES; +import static cn.hippo4j.common.constant.Constants.DEFAULT_INTERVAL; + +/** + * This class is responsible for building the notification configurations for thread pools in an agent mode. + * It implements the {@link NotifyConfigBuilder} interface and provides methods to build and initialize + * notification configurations for various platforms and types (e.g., ALARM, CONFIG). + * + *

The configuration is based on the properties loaded from the bootstrap configuration and includes + * handling for alarm control and notification intervals.

+ */ +@AllArgsConstructor +public class AgentModeNotifyConfigBuilder implements NotifyConfigBuilder { + + private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolCheckAlarmSupport.class); + + private final AlarmControlHandler alarmControlHandler; + + /** + * Builds the notification configurations for all executors defined in the bootstrap configuration. + * + *

This method filters the executors based on their alarm settings and constructs the notification + * configurations accordingly. If global alarm settings are disabled and there are no specific alarms + * configured for any executor, the method returns an empty map.

+ * + * @return A map containing the notification configurations, keyed by the notification type (e.g., ALARM, CONFIG). + */ + public Map> buildNotify() { + Map> resultMap = new HashMap<>(); + boolean globalAlarm = Optional.ofNullable(BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor()) + .map(ExecutorProperties::getAlarm) + .orElse(true); + List executors = BOOTSTRAP_CONFIG_PROPERTIES.getExecutors(); + if (CollectionUtil.isEmpty(executors)) { + LOGGER.warn("Failed to build notify, executors configuration is empty."); + return resultMap; + } + List actual = executors.stream() + .filter(each -> Optional.ofNullable(each.getAlarm()) + .orElse(false)) + .collect(Collectors.toList()); + if (!globalAlarm && CollectionUtil.isEmpty(actual)) { + return resultMap; + } + for (ExecutorProperties executorProperties : executors) { + Map> buildSingleNotifyConfig = buildSingleNotifyConfig(executorProperties); + initCacheAndLock(buildSingleNotifyConfig); + resultMap.putAll(buildSingleNotifyConfig); + } + + return resultMap; + } + + /** + * Builds the notification configurations for a single executor. + * + *

This method generates two types of notifications: ALARM and CONFIG. For each type, it creates + * notification configurations based on the platforms defined in the bootstrap configuration.

+ * + * @param executorProperties The properties of the executor for which to build the notification configurations. + * @return A map containing the notification configurations for the given executor, keyed by the notification type. + */ + public Map> buildSingleNotifyConfig(IExecutorProperties executorProperties) { + String threadPoolId = executorProperties.getThreadPoolId(); + Map> resultMap = new HashMap<>(); + String alarmBuildKey = threadPoolId + "+ALARM"; + List alarmNotifyConfigs = new ArrayList<>(); + List notifyPlatforms = BOOTSTRAP_CONFIG_PROPERTIES.getNotifyPlatforms(); + for (NotifyPlatformProperties platformProperties : notifyPlatforms) { + NotifyConfigDTO notifyConfig = new NotifyConfigDTO(); + notifyConfig.setPlatform(platformProperties.getPlatform()); + notifyConfig.setTpId(threadPoolId); + notifyConfig.setType("ALARM"); + notifyConfig.setSecret(platformProperties.getSecret()); + notifyConfig.setSecretKey(getToken(platformProperties)); + notifyConfig.setInterval(buildInterval(executorProperties)); + notifyConfig.setReceives(buildReceive(executorProperties)); + alarmNotifyConfigs.add(notifyConfig); + } + resultMap.put(alarmBuildKey, alarmNotifyConfigs); + String changeBuildKey = threadPoolId + "+CONFIG"; + List changeNotifyConfigs = new ArrayList<>(); + for (NotifyPlatformProperties platformProperties : notifyPlatforms) { + NotifyConfigDTO notifyConfig = new NotifyConfigDTO(); + notifyConfig.setPlatform(platformProperties.getPlatform()); + notifyConfig.setTpId(threadPoolId); + notifyConfig.setType("CONFIG"); + notifyConfig.setSecretKey(getToken(platformProperties)); + notifyConfig.setSecret(platformProperties.getSecret()); + notifyConfig.setReceives(buildReceive(executorProperties)); + changeNotifyConfigs.add(notifyConfig); + } + resultMap.put(changeBuildKey, changeNotifyConfigs); + return resultMap; + } + + /** + * Retrieves the token for the given notification platform properties. + * + *

If the token is not explicitly set, the method returns the secret key as the fallback.

+ * + * @param platformProperties The platform properties from which to retrieve the token. + * @return The token or secret key associated with the given platform properties. + */ + private String getToken(NotifyPlatformProperties platformProperties) { + return StringUtil.isNotBlank(platformProperties.getToken()) ? platformProperties.getToken() : platformProperties.getSecretKey(); + } + + /** + * Builds the notification interval for the given executor properties. + * + *

This method first checks the executor's specific notify configuration. If not set, it falls back + * to the default executor configuration in the bootstrap properties.

+ * + * @param executorProperties The properties of the executor for which to build the notification interval. + * @return The notification interval in seconds. + */ + private int buildInterval(IExecutorProperties executorProperties) { + return Optional.ofNullable(executorProperties.getNotify()) + .map(ExecutorNotifyProperties::getInterval) + .orElse(Optional.ofNullable(BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor()) + .map(ExecutorProperties::getNotify) + .map(ExecutorNotifyProperties::getInterval) + .orElse(DEFAULT_INTERVAL)); + } + + /** + * Builds the notification recipients for the given executor properties. + * + *

This method first checks the executor's specific notify configuration. If not set, it falls back + * to the default executor configuration in the bootstrap properties.

+ * + * @param executorProperties The properties of the executor for which to build the notification recipients. + * @return A string containing the recipients of the notifications. + */ + private String buildReceive(IExecutorProperties executorProperties) { + return Optional.ofNullable(executorProperties.getNotify()) + .map(ExecutorNotifyProperties::getReceives) + .orElse(Optional.ofNullable(BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor()) + .map(ExecutorProperties::getNotify) + .map(ExecutorNotifyProperties::getReceives).orElse("")); + } + + /** + * Initializes the cache and lock mechanisms for the given notification configurations. + * + *

This method is primarily responsible for setting up alarm controls based on the notification + * configurations, ensuring that the appropriate cache and lock mechanisms are initialized for + * each thread pool and platform combination.

+ * + * @param buildSingleNotifyConfig A map containing the notification configurations that need cache and lock initialization. + */ + public void initCacheAndLock(Map> buildSingleNotifyConfig) { + buildSingleNotifyConfig.forEach( + (key, val) -> val.stream() + .filter(each -> Objects.equals("ALARM", each.getType())) + .forEach(each -> alarmControlHandler.initCacheAndLock(each.getTpId(), each.getPlatform(), each.getInterval()))); + } +} 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 index 9eaecfba..b93149a2 100644 --- 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 @@ -84,5 +84,19 @@ public class SpringBootConfig { public static String CONFIG_FILE_TYPE; } } + + @SpringBootConfigNode(root = SpringBootConfig.class) + public static class Application { + + public static String name = ""; + + } + + @SpringBootConfigNode(root = SpringBootConfig.class) + public static class Profiles { + + public static String active = ""; + + } } } 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 index 8b215ec0..bb6e521f 100644 --- 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 @@ -18,16 +18,20 @@ package cn.hippo4j.agent.plugin.spring.common.support; import cn.hippo4j.agent.core.boot.SpringBootConfigInitializer; +import cn.hippo4j.agent.plugin.spring.common.toolkit.SpringPropertyBinder; import cn.hippo4j.common.logging.api.ILog; import cn.hippo4j.common.logging.api.LogManager; +import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.PropertiesPropertySource; import org.springframework.core.env.PropertySource; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Properties; +import static cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties.PREFIX; /** * Spring properties loader @@ -36,6 +40,8 @@ public class SpringPropertiesLoader { private static final ILog LOGGER = LogManager.getLogger(SpringPropertiesLoader.class); + public static BootstrapConfigProperties BOOTSTRAP_CONFIG_PROPERTIES = new BootstrapConfigProperties(); + public static void loadSpringProperties(ConfigurableEnvironment environment) { Iterator> iterator = environment.getPropertySources().iterator(); Properties properties = new Properties(); @@ -43,7 +49,15 @@ public class SpringPropertiesLoader { while (iterator.hasNext()) { propertySourceList.add(iterator.next()); } - for (int i = propertySourceList.size() - 1; i >= 0; i--) { + // Sort to ensure that the configuration in the configuration center is after the array + // To get the latest configuration information + propertySourceList.sort((o1, o2) -> { + boolean o1Contains = o1.getName().toLowerCase().contains("apollo") || o1.getName().toLowerCase().contains("nacos"); + boolean o2Contains = (o2.getName().toLowerCase().contains("apollo") || o2.getName().toLowerCase().contains("nacos")); + return Boolean.compare(o1Contains, o2Contains); + }); + + for (int i = 0; i <= propertySourceList.size() - 1; i++) { PropertySource propertySource = propertySourceList.get(i); if (!(propertySource instanceof EnumerablePropertySource)) { LOGGER.warn("Skip propertySource[{}] because {} not enumerable.", propertySource.getName(), propertySource.getClass()); @@ -65,5 +79,12 @@ public class SpringPropertiesLoader { } } SpringBootConfigInitializer.setSpringProperties(properties); + PropertiesPropertySource propertySource = new PropertiesPropertySource("customPropertySource", properties); + environment.getPropertySources().addFirst(propertySource); + // initialize BootstrapConfigProperties + BOOTSTRAP_CONFIG_PROPERTIES = SpringPropertyBinder.bindProperties(environment, PREFIX, BootstrapConfigProperties.class); + + ThreadPoolCheckAlarmSupport.enableThreadPoolCheckAlarmHandler(); } + } 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 index 65a9f4cb..2af5a280 100644 --- 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 @@ -22,17 +22,30 @@ 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.ExecutorNotifyProperties; import cn.hippo4j.common.model.executor.ExecutorProperties; -import cn.hippo4j.common.toolkit.BooleanUtil; +import cn.hippo4j.common.toolkit.StringUtil; +import cn.hippo4j.common.toolkit.ThreadPoolExecutorUtil; +import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; +import cn.hippo4j.threadpool.message.core.service.GlobalNotifyAlarmManage; + +import cn.hippo4j.threadpool.message.core.service.ThreadPoolNotifyAlarm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import static cn.hippo4j.common.constant.Constants.DYNAMIC_THREAD_POOL_EXECUTOR; /** * Spring thread pool register support @@ -41,6 +54,14 @@ public class SpringThreadPoolRegisterSupport { private static final Logger LOGGER = LoggerFactory.getLogger(SpringThreadPoolRegisterSupport.class); + private static final int DEFAULT_ACTIVE_ALARM = 80; + + private static final int DEFAULT_CAPACITY_ALARM = 80; + + private static final int DEFAULT_INTERVAL = 5; + + private static final String DEFAULT_RECEIVES = ""; + public static void registerThreadPoolInstances(ApplicationContext context) { Map> referencedClassMap = ThreadPoolExecutorRegistry.REFERENCED_CLASS_MAP; for (Map.Entry> entry : referencedClassMap.entrySet()) { @@ -52,7 +73,7 @@ public class SpringThreadPoolRegisterSupport { Object value = field.get(null); if (value == enhancedInstance) { String threadPoolId = declaredClass.getName() + "#" + field.getName(); - register(threadPoolId, enhancedInstance); + register(threadPoolId, enhancedInstance, Boolean.TRUE); break; } } catch (IllegalAccessException e) { @@ -74,25 +95,147 @@ public class SpringThreadPoolRegisterSupport { if (executor == null) { LOGGER.warn("[Hippo4j-Agent] Thread pool is null, ignore bean registration. beanName={}, beanClass={}", beanName, bean.getClass().getName()); } else { - register(beanName, executor); + register(beanName, executor, Boolean.FALSE); } } LOGGER.info("[Hippo4j-Agent] Registered thread pool instances successfully."); } - public static void register(String threadPoolId, ThreadPoolExecutor executor) { + public static void register(String threadPoolId, ThreadPoolExecutor executor, Boolean isAgentScanEnhancePool) { 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(); + ExecutorProperties executorProperties = SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES.getExecutors().stream() + .filter(each -> Objects.equals(threadPoolId, each.getThreadPoolId())) + .findFirst() + .orElse(null); + + // Determines the thread pool that is currently obtained by bean scanning + if (Objects.isNull(executorProperties)) { + if (isAgentScanEnhancePool) { + throw new RuntimeException(String.format("The thread pool id [%s] does not exist in the configuration.", threadPoolId)); + } else { + // Thread pool that do not require enhancement are skipped by the agent + return; + } + } + + try { + executorProperties = buildActualExecutorProperties(executorProperties); + // Replace the original configuration and refresh the thread pool + threadPoolParamReplace(executor, executorProperties); + } catch (Exception ex) { + LOGGER.error("[Hippo4j-Agent] Failed to initialize thread pool configuration.", ex); + } + // Build notification information entity + ThreadPoolNotifyAlarm threadPoolNotifyAlarm = buildThreadPoolNotifyAlarm(executorProperties); + GlobalNotifyAlarmManage.put(threadPoolId, threadPoolNotifyAlarm); + ThreadPoolExecutorRegistry.putHolder(threadPoolId, executor, executorProperties); } + + /** + * Thread-pool param replace. + * + * @param executor dynamic thread-pool executor + * @param executorProperties executor properties + */ + private static void threadPoolParamReplace(ThreadPoolExecutor executor, ExecutorProperties executorProperties) { + BlockingQueue workQueue = BlockingQueueTypeEnum.createBlockingQueue(executorProperties.getBlockingQueue(), executorProperties.getQueueCapacity()); + cn.hippo4j.common.toolkit.ReflectUtil.setFieldValue(executor, "workQueue", workQueue); + ThreadPoolExecutorUtil.safeSetPoolSize(executor, executorProperties.getCorePoolSize(), executorProperties.getMaximumPoolSize()); + executor.setKeepAliveTime(executorProperties.getKeepAliveTime(), TimeUnit.SECONDS); + executor.allowCoreThreadTimeOut(executorProperties.getAllowCoreThreadTimeOut()); + executor.setRejectedExecutionHandler(RejectedPolicyTypeEnum.createPolicy(executorProperties.getRejectedHandler())); + // Reflection sets the thread pool setExecuteTimeOut + if (DYNAMIC_THREAD_POOL_EXECUTOR.equals(executor.getClass().getName())) { + try { + Method setExecuteTimeOutMethod = executor.getClass().getMethod("setExecuteTimeOut", Long.class); + Long executeTimeOut = executorProperties.getExecuteTimeOut(); + if (executeTimeOut != null) { + setExecuteTimeOutMethod.invoke(executor, executeTimeOut); + } + } catch (Exception e) { + LOGGER.error("[Hippo4j-Agent] Failed to set executeTimeOut.", e); + } + } + } + + /** + * Build actual executor properties. + * + * @param executorProperties executor properties + * @return executor properties + */ + private static ExecutorProperties buildActualExecutorProperties(ExecutorProperties executorProperties) { + return Optional.ofNullable(SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor()).map(each -> buildExecutorProperties(executorProperties)).orElse(executorProperties); + } + + /** + * Build executor properties. + * + * @param executorProperties executor properties + * @return executor properties + */ + private static ExecutorProperties buildExecutorProperties(ExecutorProperties executorProperties) { + BootstrapConfigProperties configProperties = SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES; + return ExecutorProperties.builder() + .corePoolSize(Optional.ofNullable(executorProperties.getCorePoolSize()) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getCorePoolSize).get())) + .maximumPoolSize(Optional.ofNullable(executorProperties.getMaximumPoolSize()) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getMaximumPoolSize).get())) + .allowCoreThreadTimeOut(Optional.ofNullable(executorProperties.getAllowCoreThreadTimeOut()) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getAllowCoreThreadTimeOut).get())) + .keepAliveTime(Optional.ofNullable(executorProperties.getKeepAliveTime()) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getKeepAliveTime).get())) + .blockingQueue(Optional.ofNullable(executorProperties.getBlockingQueue()) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getBlockingQueue).get())) + .executeTimeOut(Optional.ofNullable(executorProperties.getExecuteTimeOut()) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getExecuteTimeOut).orElse(0L))) + .queueCapacity(Optional.ofNullable(executorProperties.getQueueCapacity()) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getQueueCapacity).get())) + .rejectedHandler(Optional.ofNullable(executorProperties.getRejectedHandler()) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getRejectedHandler).get())) + .threadNamePrefix(StringUtil.isBlank(executorProperties.getThreadNamePrefix()) ? executorProperties.getThreadPoolId() : executorProperties.getThreadNamePrefix()) + .threadPoolId(executorProperties.getThreadPoolId()) + .alarm(Optional.ofNullable(executorProperties.getAlarm()) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getAlarm).orElse(null))) + .activeAlarm(Optional.ofNullable(executorProperties.getActiveAlarm()) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getActiveAlarm).orElse(null))) + .capacityAlarm(Optional.ofNullable(executorProperties.getCapacityAlarm()) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getCapacityAlarm).orElse(null))) + .notify(Optional.ofNullable(executorProperties.getNotify()) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getNotify).orElse(null))) + .nodes(Optional.ofNullable(executorProperties.getNodes()) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getNodes).orElse(null))) + .build(); + } + + /** + * Build thread-pool notify alarm + * + * @param executorProperties executor properties + * @return thread-pool notify alarm + */ + private static ThreadPoolNotifyAlarm buildThreadPoolNotifyAlarm(ExecutorProperties executorProperties) { + BootstrapConfigProperties configProperties = SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES; + ExecutorNotifyProperties notify = Optional.ofNullable(executorProperties).map(ExecutorProperties::getNotify).orElse(null); + boolean isAlarm = Optional.ofNullable(executorProperties.getAlarm()) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getAlarm).orElse(true)); + int activeAlarm = Optional.ofNullable(executorProperties.getActiveAlarm()) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getActiveAlarm).orElse(DEFAULT_ACTIVE_ALARM)); + int capacityAlarm = Optional.ofNullable(executorProperties.getCapacityAlarm()) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getCapacityAlarm).orElse(DEFAULT_CAPACITY_ALARM)); + int interval = Optional.ofNullable(notify) + .map(ExecutorNotifyProperties::getInterval) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getNotify).map(ExecutorNotifyProperties::getInterval).orElse(DEFAULT_INTERVAL)); + String receive = Optional.ofNullable(notify) + .map(ExecutorNotifyProperties::getReceives) + .orElseGet(() -> Optional.ofNullable(configProperties.getDefaultExecutor()).map(ExecutorProperties::getNotify).map(ExecutorNotifyProperties::getReceives).orElse(DEFAULT_RECEIVES)); + ThreadPoolNotifyAlarm threadPoolNotifyAlarm = new ThreadPoolNotifyAlarm(isAlarm, activeAlarm, capacityAlarm); + threadPoolNotifyAlarm.setInterval(interval); + threadPoolNotifyAlarm.setReceives(receive); + return threadPoolNotifyAlarm; + } + } diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolCheckAlarmSupport.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolCheckAlarmSupport.java new file mode 100644 index 00000000..3fdf8ec1 --- /dev/null +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolCheckAlarmSupport.java @@ -0,0 +1,130 @@ +/* + * 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.plugin.spring.common.alarm.AgentModeNotifyConfigBuilder; +import cn.hippo4j.agent.plugin.spring.common.conf.SpringBootConfig; +import cn.hippo4j.common.propertie.EnvironmentProperties; +import cn.hippo4j.threadpool.alarm.handler.DefaultThreadPoolCheckAlarmHandler; +import cn.hippo4j.threadpool.message.api.NotifyConfigDTO; +import cn.hippo4j.threadpool.message.core.platform.DingSendMessageHandler; +import cn.hippo4j.threadpool.message.core.platform.LarkSendMessageHandler; +import cn.hippo4j.threadpool.message.core.platform.WeChatSendMessageHandler; +import cn.hippo4j.threadpool.message.core.service.AlarmControlHandler; +import cn.hippo4j.threadpool.message.core.service.SendMessageHandler; +import cn.hippo4j.threadpool.message.core.service.ThreadPoolBaseSendMessageService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Map; + +import static cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES; + +/** + * The {@code ThreadPoolCheckAlarmSupport} class provides functionality to enable and configure + * a thread pool check alarm handler. This is typically used to monitor thread pools for potential + * issues and send notifications based on the configured alert mechanisms. + */ +public class ThreadPoolCheckAlarmSupport { + + private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolCheckAlarmSupport.class); + + /** + * Enables the thread pool check alarm handler if the corresponding configuration property is set to {@code true}. + *

+ * This method performs the following actions: + *

    + *
  • Checks the value of the {@code enable} property in the bootstrap configuration. If it is {@code true}, it proceeds.
  • + *
  • Initializes environment properties needed for the monitoring process.
  • + *
  • Creates an instance of {@link AlarmControlHandler} and {@link ThreadPoolBaseSendMessageService} with necessary dependencies.
  • + *
  • Initializes and registers message handlers and notification configurations.
  • + *
  • Creates an instance of {@link DefaultThreadPoolCheckAlarmHandler} and schedules it to start monitoring the thread pool.
  • + *
+ */ + public static void enableThreadPoolCheckAlarmHandler() { + // Check if the thread pool checker is enabled in the bootstrap configuration properties + if (Boolean.TRUE.equals(BOOTSTRAP_CONFIG_PROPERTIES.getEnable())) { + + // Initialize EnvironmentProperties + initializeEnvironmentProperties(); + + // Initialize the AlarmControlHandler and ThreadPoolBaseSendMessageService + AlarmControlHandler alarmControlHandler = new AlarmControlHandler(); + ThreadPoolBaseSendMessageService threadPoolBaseSendMessageService = createThreadPoolBaseSendMessageService(alarmControlHandler); + + // Initialize the alarm platform information + initializeSendMessageHandlers(threadPoolBaseSendMessageService, alarmControlHandler); + + // Initialize the thread pool check alarm handler with necessary services + DefaultThreadPoolCheckAlarmHandler checkAlarmHandler = new DefaultThreadPoolCheckAlarmHandler(threadPoolBaseSendMessageService); + + // Run the check alarm handler to start monitoring the thread pool + checkAlarmHandler.scheduleExecute(); + } + } + + /** + * Initializes environment properties used for thread pool monitoring. + *

+ * This method sets the state check interval, item ID, application name, and active profile from the bootstrap configuration. + */ + private static void initializeEnvironmentProperties() { + EnvironmentProperties.checkStateInterval = Long.valueOf(BOOTSTRAP_CONFIG_PROPERTIES.getCheckStateInterval()); + EnvironmentProperties.itemId = BOOTSTRAP_CONFIG_PROPERTIES.getItemId(); + EnvironmentProperties.applicationName = SpringBootConfig.Spring.Application.name; + EnvironmentProperties.active = SpringBootConfig.Spring.Profiles.active; + } + + /** + * Creates and returns a new instance of {@link ThreadPoolBaseSendMessageService} with the specified {@link AlarmControlHandler}. + * + * @param alarmControlHandler The {@link AlarmControlHandler} used to control and handle alarms. + * @return A new instance of {@link ThreadPoolBaseSendMessageService}. + */ + private static ThreadPoolBaseSendMessageService createThreadPoolBaseSendMessageService(AlarmControlHandler alarmControlHandler) { + return new ThreadPoolBaseSendMessageService(alarmControlHandler); + } + + /** + * Initializes and registers the message handlers and notification configurations in the specified + * {@link ThreadPoolBaseSendMessageService}. + *

+ * This method creates instances of various {@link SendMessageHandler} implementations and registers them. + * It also constructs and registers notification configurations using the {@link AgentModeNotifyConfigBuilder}. + * + * @param threadPoolBaseSendMessageService The {@link ThreadPoolBaseSendMessageService} in which message handlers and notification configurations will be registered. + * @param alarmControlHandler The {@link AlarmControlHandler} used to handle alarms and notifications. + */ + private static void initializeSendMessageHandlers(ThreadPoolBaseSendMessageService threadPoolBaseSendMessageService, AlarmControlHandler alarmControlHandler) { + // Initialize message handlers + DingSendMessageHandler dingSendMessageHandler = new DingSendMessageHandler(); + WeChatSendMessageHandler weChatSendMessageHandler = new WeChatSendMessageHandler(); + LarkSendMessageHandler larkSendMessageHandler = new LarkSendMessageHandler(); + + // Register message handlers + threadPoolBaseSendMessageService.getSendMessageHandlers().put(dingSendMessageHandler.getType(), dingSendMessageHandler); + threadPoolBaseSendMessageService.getSendMessageHandlers().put(weChatSendMessageHandler.getType(), weChatSendMessageHandler); + threadPoolBaseSendMessageService.getSendMessageHandlers().put(larkSendMessageHandler.getType(), larkSendMessageHandler); + + // Construct and register notification configurations + AgentModeNotifyConfigBuilder notifyConfigBuilder = new AgentModeNotifyConfigBuilder(alarmControlHandler); + Map> notifyConfigs = notifyConfigBuilder.buildNotify(); + threadPoolBaseSendMessageService.getNotifyConfigs().putAll(notifyConfigs); + } +} diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/toolkit/SpringPropertyBinder.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/toolkit/SpringPropertyBinder.java new file mode 100644 index 00000000..4491eb70 --- /dev/null +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/toolkit/SpringPropertyBinder.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.plugin.spring.common.toolkit; + +import cn.hippo4j.threadpool.dynamic.mode.config.parser.ConfigFileTypeEnum; +import org.springframework.beans.BeanWrapper; +import org.springframework.beans.BeanWrapperImpl; +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertySource; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; + +import java.beans.PropertyEditorSupport; +import java.util.*; + +/** + * CustomPropertyBinder is a utility class for binding properties from Spring's Environment + * to Java objects based on a given prefix. This is useful for dynamically binding configurations + * in Spring applications where configuration properties are organized hierarchically and need to be + * mapped to corresponding Java objects. + * + *

This class handles complex property structures, including nested properties and collections, + * ensuring that all properties prefixed with the specified string are correctly bound to the target + * Java object.

+ */ +public class SpringPropertyBinder { + + /** + * Binds properties from the Spring Environment to an instance of the specified configuration class. + * + * @param environment the Spring Environment containing property sources. + * @param prefix the prefix to filter properties for binding (e.g., "spring.dynamic.thread-pool"). + * @param clazz the class type of the configuration object to bind properties to. + * @param the type of the configuration class. + * @return an instance of the configuration class with properties bound from the environment. + * @throws RuntimeException if there is an error instantiating the configuration class or binding properties. + */ + public static T bindProperties(Environment environment, String prefix, Class clazz) { + try { + // Create an instance of the target class + T instance = clazz.getDeclaredConstructor().newInstance(); + BeanWrapper beanWrapper = new BeanWrapperImpl(instance); + + // Register custom editor for ConfigFileTypeEnum to handle specific type conversions + beanWrapper.registerCustomEditor(ConfigFileTypeEnum.class, new ConfigFileTypeEnumEditor()); + + // Iterate over all property keys that match the given prefix + for (String key : getAllPropertyKeys(environment, prefix)) { + String propertyName = key.substring(prefix.length() + 1); // Remove prefix from the property key + String[] tokens = propertyName.split("\\."); // Split the property name by dot for nested properties + setPropertyValue(tokens, beanWrapper, environment.getProperty(key)); // Set the property value recursively + } + + return instance; + } catch (Exception e) { + throw new RuntimeException("Unable to bind properties to " + clazz.getName(), e); + } + } + + /** + * Recursively sets property values on the target object, handling nested properties and collections. + * + * @param tokens an array of property path tokens (e.g., ["nested", "property", "name"]). + * @param beanWrapper the BeanWrapper instance used to manipulate the target object. + * @param value the value to set on the target property. + */ + private static void setPropertyValue(String[] tokens, BeanWrapper beanWrapper, String value) { + for (int i = 0; i < tokens.length - 1; i++) { + String token = tokens[i]; + + if (token.matches(".*\\[\\d+\\]$")) { // Handle array/list property + token = token.substring(0, token.indexOf('[')); + int index = Integer.parseInt(tokens[i].substring(token.length() + 1, tokens[i].length() - 1)); + + token = convertToCamelCase(token); // Convert token to camelCase if necessary + List list = (List) beanWrapper.getPropertyValue(token); + if (list == null) { + list = new ArrayList<>(); + beanWrapper.setPropertyValue(convertToCamelCase(token), list); // Initialize the list if it's null + } + + // Ensure the list has enough size to accommodate the index + if (list.size() <= index) { + try { + // Instantiate the list element if it does not exist + list.add(index, beanWrapper.getPropertyTypeDescriptor(token) + .getElementTypeDescriptor().getType().newInstance()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + // Move the beanWrapper context to the current list element + beanWrapper = new BeanWrapperImpl(list.get(index)); + } else { // Handle simple or nested property + Object nestedObject = beanWrapper.getPropertyValue(token); + if (nestedObject == null) { + Class nestedClass = beanWrapper.getPropertyType(token); + if (Map.class.isAssignableFrom(nestedClass)) { + nestedObject = new HashMap<>(); // Initialize nested Map if necessary + } else { + try { + nestedObject = nestedClass.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + beanWrapper.setPropertyValue(convertToCamelCase(token), nestedObject); + } + // Move the beanWrapper context to the nested object + beanWrapper = new BeanWrapperImpl(nestedObject); + } + } + + // Finally, set the actual property value on the resolved object + String finalPropertyName = tokens[tokens.length - 1]; + Object currentObject = beanWrapper.getWrappedInstance(); + if (currentObject instanceof Map) { + // If the current object is a Map, set the value as a key-value pair + ((Map) currentObject).put(finalPropertyName, value); + } else { + // Otherwise, set it as a simple property + beanWrapper.setPropertyValue(convertToCamelCase(finalPropertyName), value); + } + } + + /** + * Retrieves all property keys from the environment that start with the given prefix. + * + * @param environment the Spring Environment containing property sources. + * @param prefix the prefix to filter property keys. + * @return a set of property keys that match the prefix. + */ + private static Set getAllPropertyKeys(Environment environment, String prefix) { + Set keys = new HashSet<>(); + // Iterate through all property sources in the environment + for (PropertySource propertySource : ((ConfigurableEnvironment) environment).getPropertySources()) { + if (propertySource instanceof MapPropertySource) { + Map source = ((MapPropertySource) propertySource).getSource(); + // Collect keys that start with the specified prefix + for (String key : source.keySet()) { + if (key.startsWith(prefix)) { + keys.add(key); + } + } + } + } + return keys; + } + + /** + * Converts a dashed-separated string to camelCase. + *

+ * For example, "my-property-name" -> "myPropertyName". + * + * @param dashed the dashed-separated string to be converted. + * @return the camelCase representation of the input string. + */ + private static String convertToCamelCase(String dashed) { + String[] parts = dashed.split("-"); + return Arrays.stream(parts) + .map(part -> part.substring(0, 1).toUpperCase() + part.substring(1)) // Capitalize each part + .reduce((first, second) -> first + second) // Concatenate all parts together + .map(result -> result.substring(0, 1).toLowerCase() + result.substring(1)) // Lowercase the first letter + .orElse(dashed); + } + + /** + * ConfigFileTypeEnumEditor is a custom property editor for converting string representations + * of {@link ConfigFileTypeEnum} into the corresponding enum instances. + *

+ * This editor is useful in scenarios where properties are read as strings but need to be + * converted to enum types for further processing. + */ + public static class ConfigFileTypeEnumEditor extends PropertyEditorSupport { + + /** + * Converts the given text value to the corresponding {@link ConfigFileTypeEnum} instance. + *

+ * This method overrides the default implementation to parse the input string and convert + * it into a {@link ConfigFileTypeEnum}. If the input string does not match any known enum + * value, an {@link IllegalArgumentException} will be thrown. + * + * @param text the string representation of the enum to be converted. + * @throws IllegalArgumentException if the text does not match any known enum value. + */ + @Override + public void setAsText(String text) throws IllegalArgumentException { + setValue(ConfigFileTypeEnum.of(text)); + } + } + +} diff --git a/examples/threadpool-example/agent/agent-example-core/pom.xml b/examples/threadpool-example/agent/agent-example-core/pom.xml new file mode 100644 index 00000000..abc4d722 --- /dev/null +++ b/examples/threadpool-example/agent/agent-example-core/pom.xml @@ -0,0 +1,47 @@ + + + 4.0.0 + + cn.hippo4j + hippo4j-threadpool-agent-example + 2.0.0-SNAPSHOT + + + hippo4j-agent-example-core + + + true + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-test + test + + + cn.hippo4j + hippo4j-threadpool-message + ${revision} + + + org.openjdk.jmh + jmh-core + 1.23 + test + + + org.openjdk.jmh + jmh-generator-annprocess + 1.23 + test + + + + \ No newline at end of file diff --git a/examples/threadpool-example/agent/config-apollo/src/main/java/cn/hippo4j/example/agent/config/apollo/ThreadPoolConfiguration.java b/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/config/ThreadPoolConfiguration.java similarity index 92% rename from examples/threadpool-example/agent/config-apollo/src/main/java/cn/hippo4j/example/agent/config/apollo/ThreadPoolConfiguration.java rename to examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/config/ThreadPoolConfiguration.java index cec7c3bb..77182468 100644 --- a/examples/threadpool-example/agent/config-apollo/src/main/java/cn/hippo4j/example/agent/config/apollo/ThreadPoolConfiguration.java +++ b/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/config/ThreadPoolConfiguration.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.example.agent.config.apollo; +package cn.hippo4j.example.agent.core.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -46,7 +46,7 @@ public class ThreadPoolConfiguration { // 演示 Agent 模式修改线程池 // ------------------------------------------------------------------------- - public static final ThreadPoolExecutor RUN_MESSAGE_SEND_TASK_EXECUTOR = new ThreadPoolExecutor( + public static final ThreadPoolExecutor AGENT_RUN_MESSAGE_SEND_TASK_EXECUTOR = new ThreadPoolExecutor( 1, 10, 1024, diff --git a/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.java b/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.java new file mode 100644 index 00000000..ccf385db --- /dev/null +++ b/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.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.example.agent.core.inittest; + +import cn.hippo4j.common.executor.ThreadPoolExecutorHolder; +import cn.hippo4j.common.executor.ThreadPoolExecutorRegistry; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * Test alarm send message. + */ +@Slf4j +@Component +public class AlarmSendMessageTest { + + private static final int SLEEP_TIME = 10240124; + + private static final int INITIAL_DELAY = 3; + + private static final String RUN_MESSAGE_SEND_TASK_EXECUTOR = "runMessageSendTaskExecutor"; + + private static final String AGENT_RUN_MESSAGE_SEND_TASK_EXECUTOR = "cn.hippo4j.example.agent.core.config.ThreadPoolConfiguration#AGENT_RUN_MESSAGE_SEND_TASK_EXECUTOR"; + + /** + * Test alarm notification. + * If you need to run this single test, add @PostConstruct to the method. + */ + @SuppressWarnings("all") + @PostConstruct + public void alarmSendMessageTest() { + ScheduledExecutorService scheduledThreadPool = Executors.newSingleThreadScheduledExecutor(); + scheduledThreadPool.scheduleWithFixedDelay(() -> { + ThreadPoolExecutorHolder executorHolder = ThreadPoolExecutorRegistry.getHolder(AGENT_RUN_MESSAGE_SEND_TASK_EXECUTOR); + ThreadPoolExecutor poolExecutor = executorHolder.getExecutor(); + try { + poolExecutor.execute(() -> { + try { + Thread.sleep(SLEEP_TIME); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + } catch (Exception ex) { + log.error("Throw reject policy.", ex.getMessage()); + } + }, INITIAL_DELAY, 2, TimeUnit.SECONDS); + + scheduledThreadPool.scheduleWithFixedDelay(() -> { + ThreadPoolExecutorHolder executorHolder = ThreadPoolExecutorRegistry.getHolder(RUN_MESSAGE_SEND_TASK_EXECUTOR); + ThreadPoolExecutor poolExecutor = executorHolder.getExecutor(); + try { + poolExecutor.execute(() -> { + try { + Thread.sleep(SLEEP_TIME); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + } catch (Exception ex) { + log.error("Throw reject policy.", ex.getMessage()); + } + }, INITIAL_DELAY, 2, TimeUnit.SECONDS); + + } +} diff --git a/examples/threadpool-example/agent/config-apollo/pom.xml b/examples/threadpool-example/agent/config-apollo/pom.xml index 7a7adfd4..f87d6ddf 100644 --- a/examples/threadpool-example/agent/config-apollo/pom.xml +++ b/examples/threadpool-example/agent/config-apollo/pom.xml @@ -16,6 +16,12 @@ + + cn.hippo4j + hippo4j-agent-example-core + ${revision} + + org.springframework.boot spring-boot-starter diff --git a/examples/threadpool-example/agent/config-apollo/src/main/java/cn/hippo4j/example/agent/config/apollo/AgentConfigApolloExampleApplication.java b/examples/threadpool-example/agent/config-apollo/src/main/java/cn/hippo4j/example/agent/config/apollo/AgentConfigApolloExampleApplication.java index 33283990..365282b7 100644 --- a/examples/threadpool-example/agent/config-apollo/src/main/java/cn/hippo4j/example/agent/config/apollo/AgentConfigApolloExampleApplication.java +++ b/examples/threadpool-example/agent/config-apollo/src/main/java/cn/hippo4j/example/agent/config/apollo/AgentConfigApolloExampleApplication.java @@ -23,7 +23,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; /** * Agent config apollo example application. */ -@SpringBootApplication +@SpringBootApplication(scanBasePackages = "cn.hippo4j.example.agent.core") public class AgentConfigApolloExampleApplication { public static void main(String[] args) { diff --git a/examples/threadpool-example/agent/config-nacos/pom.xml b/examples/threadpool-example/agent/config-nacos/pom.xml index 52c783aa..4240c147 100644 --- a/examples/threadpool-example/agent/config-nacos/pom.xml +++ b/examples/threadpool-example/agent/config-nacos/pom.xml @@ -16,6 +16,11 @@ + + cn.hippo4j + hippo4j-agent-example-core + ${revision} + org.springframework.boot spring-boot-starter diff --git a/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java b/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java index b05949da..e5010fbd 100644 --- a/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java +++ b/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java @@ -25,7 +25,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; /** * Agent config Nacos example application. */ -@SpringBootApplication +@SpringBootApplication(scanBasePackages = "cn.hippo4j.example.agent.core") @EnableNacosConfig public class AgentConfigNacosExampleApplication { diff --git a/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/ThreadPoolConfiguration.java b/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/ThreadPoolConfiguration.java deleted file mode 100644 index 9f60e753..00000000 --- a/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/ThreadPoolConfiguration.java +++ /dev/null @@ -1,55 +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.example.agent.config.nacos; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -@Configuration -public class ThreadPoolConfiguration { - - // ------------------------------------------------------------------------- - // 未使用 Hippo4j,原始定义线程池创建方式 - // ------------------------------------------------------------------------- - - @Bean - public ThreadPoolExecutor runMessageSendTaskExecutor() { - LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue<>(1024); - return new ThreadPoolExecutor( - 1, - 10, - 1024, - TimeUnit.SECONDS, - linkedBlockingQueue); - } - - // ------------------------------------------------------------------------- - // 演示 Agent 模式修改线程池 - // ------------------------------------------------------------------------- - - public static final ThreadPoolExecutor RUN_MESSAGE_SEND_TASK_EXECUTOR = new ThreadPoolExecutor( - 1, - 10, - 1024, - TimeUnit.SECONDS, - new LinkedBlockingQueue<>(1024)); -} diff --git a/examples/threadpool-example/agent/pom.xml b/examples/threadpool-example/agent/pom.xml index 8dd4320a..d215c979 100644 --- a/examples/threadpool-example/agent/pom.xml +++ b/examples/threadpool-example/agent/pom.xml @@ -19,5 +19,6 @@ config-apollo config-nacos + agent-example-core \ No newline at end of file diff --git a/infra/common/src/main/java/cn/hippo4j/common/constant/Constants.java b/infra/common/src/main/java/cn/hippo4j/common/constant/Constants.java index 9eeb865e..71577018 100644 --- a/infra/common/src/main/java/cn/hippo4j/common/constant/Constants.java +++ b/infra/common/src/main/java/cn/hippo4j/common/constant/Constants.java @@ -56,6 +56,8 @@ public class Constants { public static final String GENERAL_SPLIT_SYMBOL = ","; + public static final String DOT_SPLIT_SYMBOL = "."; + public static final String IDENTIFY_SLICER_SYMBOL = "_"; public static final String LONG_POLLING_LINE_SEPARATOR = "\r\n"; @@ -128,5 +130,12 @@ public class Constants { public static final String CONFIGURATION_PROPERTIES_PREFIX = "spring.dynamic.thread-pool"; + public static final String EXECUTORS = "executors"; + public static final long NO_REJECT_COUNT_NUM = -1L; + + public static final String DYNAMIC_THREAD_POOL_EXECUTOR = "cn.hippo4j.core.executor.DynamicThreadPoolExecutor"; + + public static final int DEFAULT_INTERVAL = 5; + } From a06e1844bd48339ae2269156d84161797d55fd9e Mon Sep 17 00:00:00 2001 From: Pan-YuJie <646836760@qq.com> Date: Thu, 12 Sep 2024 15:24:10 +0800 Subject: [PATCH 10/14] feat:Agent dynamic monitor Initialize --- .../spring-boot-2x-plugin/pom.xml | 8 + .../spring-plugin-common/pom.xml | 25 +++ .../alarm/AgentModeNotifyConfigBuilder.java | 200 ------------------ .../spring/common/conf/SpringBootConfig.java | 5 + .../monitor/MonitorHandlersConfigurator.java | 186 ++++++++++++++++ .../common/monitor/MonitorMetricEndpoint.java | 97 +++++++++ .../support/SpringPropertiesLoader.java | 8 +- .../support/ThreadPoolMonitorSupport.java | 137 ++++++++++++ .../core/inittest/AlarmSendMessageTest.java | 2 +- .../agent/config-nacos/pom.xml | 8 - .../monitor/MonitorCollectTypeEnum.java | 50 +++++ .../monitor/MonitorHandlerTypeEnum.java | 36 ++++ .../config/properties/MonitorProperties.java | 5 + 13 files changed, 557 insertions(+), 210 deletions(-) delete mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java create mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorHandlersConfigurator.java create mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorMetricEndpoint.java create mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java create mode 100644 infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorCollectTypeEnum.java create mode 100644 infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorHandlerTypeEnum.java 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 index 40c0198a..a05c33f3 100644 --- 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 @@ -38,11 +38,19 @@ provided + + cn.hippo4j + hippo4j-threadpool-core + ${project.version} + provided + + com.ctrip.framework.apollo apollo-client provided + com.alibaba.nacos nacos-client 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 index c3780048..c472c7b3 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml @@ -49,5 +49,30 @@ ${project.version} provided + + cn.hippo4j + hippo4j-threadpool-adapter-web + ${project.version} + provided + + + cn.hippo4j + hippo4j-threadpool-monitor-elasticsearch + ${project.version} + + + cn.hippo4j + hippo4j-threadpool-monitor-local-log + ${project.version} + + + cn.hippo4j + hippo4j-threadpool-monitor-micrometer + ${project.version} + + + io.micrometer + micrometer-registry-prometheus + \ 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/alarm/AgentModeNotifyConfigBuilder.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java deleted file mode 100644 index d1345334..00000000 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java +++ /dev/null @@ -1,200 +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.common.alarm; - -import cn.hippo4j.agent.plugin.spring.common.support.ThreadPoolCheckAlarmSupport; -import cn.hippo4j.common.api.IExecutorProperties; -import cn.hippo4j.common.model.executor.ExecutorNotifyProperties; -import cn.hippo4j.common.model.executor.ExecutorProperties; -import cn.hippo4j.common.toolkit.CollectionUtil; -import cn.hippo4j.common.toolkit.StringUtil; -import cn.hippo4j.threadpool.dynamic.mode.config.properties.NotifyPlatformProperties; -import cn.hippo4j.threadpool.message.api.NotifyConfigBuilder; -import cn.hippo4j.threadpool.message.api.NotifyConfigDTO; -import cn.hippo4j.threadpool.message.core.service.AlarmControlHandler; -import lombok.AllArgsConstructor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -import static cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES; -import static cn.hippo4j.common.constant.Constants.DEFAULT_INTERVAL; - -/** - * This class is responsible for building the notification configurations for thread pools in an agent mode. - * It implements the {@link NotifyConfigBuilder} interface and provides methods to build and initialize - * notification configurations for various platforms and types (e.g., ALARM, CONFIG). - * - *

The configuration is based on the properties loaded from the bootstrap configuration and includes - * handling for alarm control and notification intervals.

- */ -@AllArgsConstructor -public class AgentModeNotifyConfigBuilder implements NotifyConfigBuilder { - - private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolCheckAlarmSupport.class); - - private final AlarmControlHandler alarmControlHandler; - - /** - * Builds the notification configurations for all executors defined in the bootstrap configuration. - * - *

This method filters the executors based on their alarm settings and constructs the notification - * configurations accordingly. If global alarm settings are disabled and there are no specific alarms - * configured for any executor, the method returns an empty map.

- * - * @return A map containing the notification configurations, keyed by the notification type (e.g., ALARM, CONFIG). - */ - public Map> buildNotify() { - Map> resultMap = new HashMap<>(); - boolean globalAlarm = Optional.ofNullable(BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor()) - .map(ExecutorProperties::getAlarm) - .orElse(true); - List executors = BOOTSTRAP_CONFIG_PROPERTIES.getExecutors(); - if (CollectionUtil.isEmpty(executors)) { - LOGGER.warn("Failed to build notify, executors configuration is empty."); - return resultMap; - } - List actual = executors.stream() - .filter(each -> Optional.ofNullable(each.getAlarm()) - .orElse(false)) - .collect(Collectors.toList()); - if (!globalAlarm && CollectionUtil.isEmpty(actual)) { - return resultMap; - } - for (ExecutorProperties executorProperties : executors) { - Map> buildSingleNotifyConfig = buildSingleNotifyConfig(executorProperties); - initCacheAndLock(buildSingleNotifyConfig); - resultMap.putAll(buildSingleNotifyConfig); - } - - return resultMap; - } - - /** - * Builds the notification configurations for a single executor. - * - *

This method generates two types of notifications: ALARM and CONFIG. For each type, it creates - * notification configurations based on the platforms defined in the bootstrap configuration.

- * - * @param executorProperties The properties of the executor for which to build the notification configurations. - * @return A map containing the notification configurations for the given executor, keyed by the notification type. - */ - public Map> buildSingleNotifyConfig(IExecutorProperties executorProperties) { - String threadPoolId = executorProperties.getThreadPoolId(); - Map> resultMap = new HashMap<>(); - String alarmBuildKey = threadPoolId + "+ALARM"; - List alarmNotifyConfigs = new ArrayList<>(); - List notifyPlatforms = BOOTSTRAP_CONFIG_PROPERTIES.getNotifyPlatforms(); - for (NotifyPlatformProperties platformProperties : notifyPlatforms) { - NotifyConfigDTO notifyConfig = new NotifyConfigDTO(); - notifyConfig.setPlatform(platformProperties.getPlatform()); - notifyConfig.setTpId(threadPoolId); - notifyConfig.setType("ALARM"); - notifyConfig.setSecret(platformProperties.getSecret()); - notifyConfig.setSecretKey(getToken(platformProperties)); - notifyConfig.setInterval(buildInterval(executorProperties)); - notifyConfig.setReceives(buildReceive(executorProperties)); - alarmNotifyConfigs.add(notifyConfig); - } - resultMap.put(alarmBuildKey, alarmNotifyConfigs); - String changeBuildKey = threadPoolId + "+CONFIG"; - List changeNotifyConfigs = new ArrayList<>(); - for (NotifyPlatformProperties platformProperties : notifyPlatforms) { - NotifyConfigDTO notifyConfig = new NotifyConfigDTO(); - notifyConfig.setPlatform(platformProperties.getPlatform()); - notifyConfig.setTpId(threadPoolId); - notifyConfig.setType("CONFIG"); - notifyConfig.setSecretKey(getToken(platformProperties)); - notifyConfig.setSecret(platformProperties.getSecret()); - notifyConfig.setReceives(buildReceive(executorProperties)); - changeNotifyConfigs.add(notifyConfig); - } - resultMap.put(changeBuildKey, changeNotifyConfigs); - return resultMap; - } - - /** - * Retrieves the token for the given notification platform properties. - * - *

If the token is not explicitly set, the method returns the secret key as the fallback.

- * - * @param platformProperties The platform properties from which to retrieve the token. - * @return The token or secret key associated with the given platform properties. - */ - private String getToken(NotifyPlatformProperties platformProperties) { - return StringUtil.isNotBlank(platformProperties.getToken()) ? platformProperties.getToken() : platformProperties.getSecretKey(); - } - - /** - * Builds the notification interval for the given executor properties. - * - *

This method first checks the executor's specific notify configuration. If not set, it falls back - * to the default executor configuration in the bootstrap properties.

- * - * @param executorProperties The properties of the executor for which to build the notification interval. - * @return The notification interval in seconds. - */ - private int buildInterval(IExecutorProperties executorProperties) { - return Optional.ofNullable(executorProperties.getNotify()) - .map(ExecutorNotifyProperties::getInterval) - .orElse(Optional.ofNullable(BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor()) - .map(ExecutorProperties::getNotify) - .map(ExecutorNotifyProperties::getInterval) - .orElse(DEFAULT_INTERVAL)); - } - - /** - * Builds the notification recipients for the given executor properties. - * - *

This method first checks the executor's specific notify configuration. If not set, it falls back - * to the default executor configuration in the bootstrap properties.

- * - * @param executorProperties The properties of the executor for which to build the notification recipients. - * @return A string containing the recipients of the notifications. - */ - private String buildReceive(IExecutorProperties executorProperties) { - return Optional.ofNullable(executorProperties.getNotify()) - .map(ExecutorNotifyProperties::getReceives) - .orElse(Optional.ofNullable(BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor()) - .map(ExecutorProperties::getNotify) - .map(ExecutorNotifyProperties::getReceives).orElse("")); - } - - /** - * Initializes the cache and lock mechanisms for the given notification configurations. - * - *

This method is primarily responsible for setting up alarm controls based on the notification - * configurations, ensuring that the appropriate cache and lock mechanisms are initialized for - * each thread pool and platform combination.

- * - * @param buildSingleNotifyConfig A map containing the notification configurations that need cache and lock initialization. - */ - public void initCacheAndLock(Map> buildSingleNotifyConfig) { - buildSingleNotifyConfig.forEach( - (key, val) -> val.stream() - .filter(each -> Objects.equals("ALARM", each.getType())) - .forEach(each -> alarmControlHandler.initCacheAndLock(each.getTpId(), each.getPlatform(), each.getInterval()))); - } -} 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 index b93149a2..48e05894 100644 --- 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 @@ -30,11 +30,13 @@ public class SpringBootConfig { /** * Spring */ + @SpringBootConfigNode(root = SpringBootConfig.class) public static class Spring { /** * Dynamic */ + @SpringBootConfigNode(root = SpringBootConfig.class) public static class Dynamic { /** @@ -79,6 +81,9 @@ public class SpringBootConfig { public static Long initialDelay = 10000L; public static Long collectInterval = 5000L; + + public static Integer AGENT_MICROMETER_PORT; + } 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/monitor/MonitorHandlersConfigurator.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorHandlersConfigurator.java new file mode 100644 index 00000000..56e1b7f0 --- /dev/null +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorHandlersConfigurator.java @@ -0,0 +1,186 @@ +/* + * 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.monitor; + +import cn.hippo4j.common.extension.spi.ServiceLoaderRegistry; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; +import cn.hippo4j.common.monitor.MonitorCollectTypeEnum; +import cn.hippo4j.common.monitor.MonitorHandlerTypeEnum; +import cn.hippo4j.core.executor.state.ThreadPoolRunStateHandler; +import cn.hippo4j.core.toolkit.inet.InetUtils; +import cn.hippo4j.core.toolkit.inet.InetUtilsProperties; +import cn.hippo4j.monitor.elasticsearch.AdapterThreadPoolElasticSearchMonitorHandler; +import cn.hippo4j.monitor.elasticsearch.DynamicThreadPoolElasticSearchMonitorHandler; +import cn.hippo4j.monitor.elasticsearch.WebThreadPoolElasticSearchMonitorHandler; +import cn.hippo4j.monitor.local.log.AdapterThreadPoolLocalLogMonitorHandler; +import cn.hippo4j.monitor.local.log.DynamicThreadPoolLocalLogMonitorHandler; +import cn.hippo4j.monitor.local.log.WebThreadPoolLocalLogMonitorHandler; +import cn.hippo4j.monitor.micrometer.AdapterThreadPoolMicrometerMonitorHandler; +import cn.hippo4j.monitor.micrometer.DynamicThreadPoolMicrometerMonitorHandler; +import cn.hippo4j.monitor.micrometer.WebThreadPoolMicrometerMonitorHandler; +import cn.hippo4j.threadpool.dynamic.mode.config.properties.MonitorProperties; +import cn.hippo4j.threadpool.monitor.api.ThreadPoolMonitor; +import org.springframework.core.env.ConfigurableEnvironment; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; + +import static cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader.INET_UTILS_PROPERTIES; + +/** + * This class is responsible for configuring and initializing monitoring handlers + * for various types of thread pools. It maps specific monitoring types (e.g., Micrometer, + * Log, Elasticsearch) to their corresponding handler initializers and manages the + * setup process based on the provided configuration. + */ +public class MonitorHandlersConfigurator { + + private static final ILog LOGGER = LogManager.getLogger(MonitorHandlersConfigurator.class); + + // Maps thread pool types to their corresponding handler constructors + private static final Map> handlerMap = new HashMap<>(); + + static { + // Initialize the handler map with specific monitoring types + handlerMap.put(MonitorCollectTypeEnum.MICROMETER.getValue(), MonitorHandlersConfigurator::handleMicrometer); + handlerMap.put(MonitorCollectTypeEnum.LOG.getValue(), MonitorHandlersConfigurator::handleLog); + handlerMap.put(MonitorCollectTypeEnum.ELASTICSEARCH.getValue(), MonitorHandlersConfigurator::handleElasticSearch); + } + + /** + * Initializes the monitoring handlers based on the provided monitoring configuration. + *

+ * This method performs the following tasks: + *

    + *
  • Parses the configured monitoring types and thread pool types.
  • + *
  • Initializes a monitoring context with the necessary thread pool monitors and state handler.
  • + *
  • For each configured monitoring type, invokes the corresponding handler initializer + * for each relevant thread pool type.
  • + *
  • Logs a warning if an unrecognized monitoring type is encountered.
  • + *
  • Registers and adds thread pool monitors that match the configured monitoring types.
  • + *
+ * + * @param monitor The monitoring properties configuration. + * @param environment The application environment from which additional configuration can be loaded. + * @param threadPoolMonitors A list to hold the initialized thread pool monitors. + */ + public static void initializeMonitorHandlers(MonitorProperties monitor, ConfigurableEnvironment environment, List threadPoolMonitors) { + List collectTypes = Arrays.asList(monitor.getCollectTypes().split(",")); + List threadPoolTypes = Arrays.asList(monitor.getThreadPoolTypes().split(",")); + ThreadPoolRunStateHandler threadPoolRunStateHandler = new ThreadPoolRunStateHandler( + new InetUtils(INET_UTILS_PROPERTIES), environment); + + MonitorHandlerContext context = new MonitorHandlerContext(threadPoolMonitors, threadPoolRunStateHandler); + + // Initialize handlers for each configured monitoring type and thread pool type + for (String collectType : collectTypes) { + if (handlerMap.containsKey(collectType)) { + for (MonitorHandlerTypeEnum type : MonitorHandlerTypeEnum.values()) { + if (threadPoolTypes.contains(type.name().toLowerCase())) { + handlerMap.get(collectType).accept(type, context); + } + } + } else { + LOGGER.warn("[Hippo4j-Agent] MonitorConfigurator initialize Unrecognized collect type: [{}]", collectType); + } + } + + // Register and add dynamic thread pool monitors matching the configured types + Collection dynamicThreadPoolMonitors = ServiceLoaderRegistry.getSingletonServiceInstances(ThreadPoolMonitor.class); + dynamicThreadPoolMonitors.stream().filter(each -> collectTypes.contains(each.getType())).forEach(threadPoolMonitors::add); + } + + /** + * Initializes Micrometer-based monitoring handlers for the specified thread pool type. + * + * @param type The type of thread pool to be monitored. + * @param context The context containing the monitors and state handler. + */ + private static void handleMicrometer(MonitorHandlerTypeEnum type, MonitorHandlerContext context) { + switch (type) { + case DYNAMIC: + context.monitors.add(new DynamicThreadPoolMicrometerMonitorHandler(context.threadPoolRunStateHandler)); + break; + case WEB: + context.monitors.add(new WebThreadPoolMicrometerMonitorHandler()); + break; + case ADAPTER: + context.monitors.add(new AdapterThreadPoolMicrometerMonitorHandler()); + break; + } + } + + /** + * Initializes Log-based monitoring handlers for the specified thread pool type. + * + * @param type The type of thread pool to be monitored. + * @param context The context containing the monitors and state handler. + */ + private static void handleLog(MonitorHandlerTypeEnum type, MonitorHandlerContext context) { + switch (type) { + case DYNAMIC: + context.monitors.add(new DynamicThreadPoolLocalLogMonitorHandler(context.threadPoolRunStateHandler)); + break; + case WEB: + context.monitors.add(new WebThreadPoolLocalLogMonitorHandler()); + break; + case ADAPTER: + context.monitors.add(new AdapterThreadPoolLocalLogMonitorHandler()); + break; + } + } + + /** + * Initializes Elasticsearch-based monitoring handlers for the specified thread pool type. + * + * @param type The type of thread pool to be monitored. + * @param context The context containing the monitors and state handler. + */ + private static void handleElasticSearch(MonitorHandlerTypeEnum type, MonitorHandlerContext context) { + switch (type) { + case DYNAMIC: + context.monitors.add(new DynamicThreadPoolElasticSearchMonitorHandler(context.threadPoolRunStateHandler)); + break; + case WEB: + context.monitors.add(new WebThreadPoolElasticSearchMonitorHandler(context.threadPoolRunStateHandler)); + break; + case ADAPTER: + context.monitors.add(new AdapterThreadPoolElasticSearchMonitorHandler(context.threadPoolRunStateHandler)); + break; + } + } + + /** + * A helper class to manage the context in which monitoring handlers are initialized. + */ + private static class MonitorHandlerContext { + + List monitors; + ThreadPoolRunStateHandler threadPoolRunStateHandler; + + MonitorHandlerContext(List monitors, ThreadPoolRunStateHandler handler) { + this.monitors = monitors; + this.threadPoolRunStateHandler = handler; + } + } +} diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorMetricEndpoint.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorMetricEndpoint.java new file mode 100644 index 00000000..b8ae07bc --- /dev/null +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorMetricEndpoint.java @@ -0,0 +1,97 @@ +/* + * 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.monitor; + +import cn.hippo4j.agent.plugin.spring.common.conf.SpringBootConfig; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.composite.CompositeMeterRegistry; +import io.micrometer.prometheus.PrometheusConfig; +import io.micrometer.prometheus.PrometheusMeterRegistry; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; + +/** + * This class is responsible for exposing Prometheus metrics via an HTTP endpoint. + * It initializes the Prometheus registry, binds it to the global metrics registry, + * and starts an HTTP server to serve the metrics data. + */ +public class MonitorMetricEndpoint { + + private static final ILog LOGGER = LogManager.getLogger(MonitorHandlersConfigurator.class); + + /** + * Starts the Prometheus metrics HTTP server. + *

+ * This method performs the following steps: + *

    + *
  • Initializes the PrometheusMeterRegistry with the default configuration.
  • + *
  • Binds the Prometheus registry to the global CompositeMeterRegistry.
  • + *
  • Attempts to start an HTTP server on the configured port to expose the Prometheus metrics.
  • + *
+ * If the port is not configured, or if there is an error starting the server, appropriate error messages + * are logged, and the method returns without starting the server. + *

+ */ + public static void startPrometheusEndpoint() { + + // Initialize the Prometheus registry + PrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + + // Bind the Prometheus registry to the global Metrics registry + CompositeMeterRegistry globalRegistry = Metrics.globalRegistry; + globalRegistry.add(prometheusRegistry); + + // Get the configured port for the Prometheus metrics HTTP server + Integer port = SpringBootConfig.Spring.Dynamic.Thread_Pool.Monitor.AGENT_MICROMETER_PORT; + if (port == null) { + LOGGER.error( + "[Hippo4j-Agent] Failed to start Prometheus metrics endpoint server. Please configure the exposed endpoint by adding: spring.dynamic.thread-pool.monitor.port=xxx to the configuration file"); + return; + } + + // Create the HTTP server + HttpServer server = null; + try { + server = HttpServer.create(new InetSocketAddress(port), 0); + } catch (IOException e) { + LOGGER.error("[Hippo4j-Agent] Failed to start Prometheus metrics endpoint server", e); + return; + } + + // Register the /actuator/prometheus context to handle metrics requests + server.createContext("/actuator/prometheus", exchange -> { + String response = prometheusRegistry.scrape(); // Get metrics data in Prometheus format + exchange.sendResponseHeaders(200, response.getBytes().length); + try (OutputStream os = exchange.getResponseBody()) { + os.write(response.getBytes()); + } + }); + + // Start the server + server.start(); + LOGGER.info("[Hippo4j-Agent] Prometheus metrics server started on port {}", port); + } + +} 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 index bb6e521f..d9fbfac7 100644 --- 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 @@ -21,6 +21,7 @@ import cn.hippo4j.agent.core.boot.SpringBootConfigInitializer; import cn.hippo4j.agent.plugin.spring.common.toolkit.SpringPropertyBinder; import cn.hippo4j.common.logging.api.ILog; import cn.hippo4j.common.logging.api.LogManager; +import cn.hippo4j.core.toolkit.inet.InetUtilsProperties; import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; @@ -42,6 +43,8 @@ public class SpringPropertiesLoader { public static BootstrapConfigProperties BOOTSTRAP_CONFIG_PROPERTIES = new BootstrapConfigProperties(); + public static InetUtilsProperties INET_UTILS_PROPERTIES = new InetUtilsProperties(); + public static void loadSpringProperties(ConfigurableEnvironment environment) { Iterator> iterator = environment.getPropertySources().iterator(); Properties properties = new Properties(); @@ -83,8 +86,11 @@ public class SpringPropertiesLoader { environment.getPropertySources().addFirst(propertySource); // initialize BootstrapConfigProperties BOOTSTRAP_CONFIG_PROPERTIES = SpringPropertyBinder.bindProperties(environment, PREFIX, BootstrapConfigProperties.class); - + INET_UTILS_PROPERTIES = SpringPropertyBinder.bindProperties(environment, InetUtilsProperties.PREFIX, InetUtilsProperties.class); + // Enable the thread pool check alert handler ThreadPoolCheckAlarmSupport.enableThreadPoolCheckAlarmHandler(); + // Enable thread pool monitor handler + ThreadPoolMonitorSupport.enableThreadPoolMonitorHandler(environment); } } diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java new file mode 100644 index 00000000..3af1e1e6 --- /dev/null +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java @@ -0,0 +1,137 @@ +/* + * 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.plugin.spring.common.monitor.MonitorHandlersConfigurator; +import cn.hippo4j.agent.plugin.spring.common.monitor.MonitorMetricEndpoint; +import cn.hippo4j.common.executor.ThreadFactoryBuilder; +import cn.hippo4j.common.executor.ThreadPoolExecutorRegistry; +import cn.hippo4j.common.extension.spi.ServiceLoaderRegistry; +import cn.hippo4j.common.monitor.MonitorCollectTypeEnum; +import cn.hippo4j.common.toolkit.StringUtil; +import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; +import cn.hippo4j.threadpool.dynamic.mode.config.properties.MonitorProperties; +import cn.hippo4j.threadpool.monitor.api.ThreadPoolMonitor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import static cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES; + +/** + * This class provides support for monitoring dynamic thread pools in an application. + * It includes methods to initialize and enable monitoring components, and schedules + * periodic data collection from the thread pools. + */ +public class ThreadPoolMonitorSupport { + + private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolMonitorSupport.class); + + private static List threadPoolMonitors; + + static { + // Register the ThreadPoolMonitor service with the ServiceLoaderRegistry + ServiceLoaderRegistry.register(ThreadPoolMonitor.class); + } + + /** + * Enables the dynamic thread pool monitoring handler. + *

+ * This method performs the following steps: + *

    + *
  • Validates the monitoring configuration from the environment properties.
  • + *
  • Initializes monitoring components for the dynamic thread pools.
  • + *
  • Exposes metric endpoints, such as Prometheus, if configured.
  • + *
  • Schedules periodic collection of metrics from the thread pools.
  • + *
+ * If the monitoring configuration is invalid or disabled, the method returns without + * enabling the monitoring handler. + *

+ * + * @param environment The environment from which the monitoring configuration is loaded. + */ + public static void enableThreadPoolMonitorHandler(Environment environment) { + BootstrapConfigProperties properties = BOOTSTRAP_CONFIG_PROPERTIES; + MonitorProperties monitor = properties.getMonitor(); + if (Objects.isNull(monitor) + || !monitor.getEnable() + || StringUtil.isBlank(monitor.getThreadPoolTypes()) + || StringUtil.isBlank(monitor.getCollectTypes())) { + return; + } + + LOGGER.info("[Hippo4j-Agent] Start monitoring the running status of dynamic thread pools."); + threadPoolMonitors = new ArrayList<>(); + ScheduledExecutorService collectScheduledExecutor = new ScheduledThreadPoolExecutor( + 1, + r -> new Thread(r, "client.agent.scheduled.collect.data")); + + // Initialize monitoring components for the dynamic thread pools + MonitorHandlersConfigurator.initializeMonitorHandlers(monitor, (ConfigurableEnvironment) environment, threadPoolMonitors); + + // Expose metric endpoints based on the configured collect types + List collectTypes = Arrays.asList(monitor.getCollectTypes().split(",")); + if (collectTypes.contains(MonitorCollectTypeEnum.MICROMETER.getValue())) { + MonitorMetricEndpoint.startPrometheusEndpoint(); + } + + // Schedule periodic collection of metrics from the thread pools + collectScheduledExecutor.scheduleWithFixedDelay( + scheduleRunnable(), + monitor.getInitialDelay(), + monitor.getCollectInterval(), + TimeUnit.MILLISECONDS); + + if (ThreadPoolExecutorRegistry.getThreadPoolExecutorSize() > 0) { + LOGGER.info("[Hippo4j-Agent] Dynamic thread pool: [{}]. The dynamic thread pool starts data collection and reporting.", ThreadPoolExecutorRegistry.getThreadPoolExecutorSize()); + } + } + + /** + * Returns a Runnable task that collects metrics from the dynamic thread pools. + *

+ * This method is used to create a task that periodically iterates over the + * registered thread pool monitors and collects their metrics. If an exception + * occurs during the collection, it is logged. + *

+ * + * @return A Runnable task that performs the metrics collection. + */ + private static Runnable scheduleRunnable() { + return () -> { + for (ThreadPoolMonitor each : threadPoolMonitors) { + try { + each.collect(); + } catch (Exception ex) { + LOGGER.error("[Hippo4j-Agent] Error monitoring the running status of dynamic thread pool. Type: {}", each.getType(), ex); + } + } + }; + } + +} diff --git a/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.java b/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.java index ccf385db..bf67eb75 100644 --- a/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.java +++ b/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.java @@ -48,7 +48,7 @@ public class AlarmSendMessageTest { * If you need to run this single test, add @PostConstruct to the method. */ @SuppressWarnings("all") - @PostConstruct + // @PostConstruct public void alarmSendMessageTest() { ScheduledExecutorService scheduledThreadPool = Executors.newSingleThreadScheduledExecutor(); scheduledThreadPool.scheduleWithFixedDelay(() -> { diff --git a/examples/threadpool-example/agent/config-nacos/pom.xml b/examples/threadpool-example/agent/config-nacos/pom.xml index 4240c147..8134d488 100644 --- a/examples/threadpool-example/agent/config-nacos/pom.xml +++ b/examples/threadpool-example/agent/config-nacos/pom.xml @@ -44,14 +44,6 @@ slf4j-api 1.7.21 - - io.micrometer - micrometer-registry-prometheus - - - org.springframework.boot - spring-boot-starter-actuator - diff --git a/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorCollectTypeEnum.java b/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorCollectTypeEnum.java new file mode 100644 index 00000000..b7a4a8b1 --- /dev/null +++ b/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorCollectTypeEnum.java @@ -0,0 +1,50 @@ +/* + * 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.common.monitor; + +/** + * MonitorCollect type enum. + */ +public enum MonitorCollectTypeEnum { + + /** + * Micrometer + */ + MICROMETER("micrometer"), + + /** + * ELASTICSEARCH + */ + ELASTICSEARCH("elasticsearch"), + + /** + * LOG + */ + LOG("log"); + + private final String value; + + MonitorCollectTypeEnum(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + +} diff --git a/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorHandlerTypeEnum.java b/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorHandlerTypeEnum.java new file mode 100644 index 00000000..5cf7ad65 --- /dev/null +++ b/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorHandlerTypeEnum.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.common.monitor; + +/** + * MonitorHandler type enum. + */ +public enum MonitorHandlerTypeEnum { + /** + * DYNAMIC + */ + DYNAMIC, + /** + * WEB + */ + WEB, + /** + * ADAPTER + */ + ADAPTER +} diff --git a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/properties/MonitorProperties.java b/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/properties/MonitorProperties.java index 38cb1ed1..db4db930 100644 --- a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/properties/MonitorProperties.java +++ b/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/properties/MonitorProperties.java @@ -51,4 +51,9 @@ public class MonitorProperties { * Collect interval. unit: ms */ private Long collectInterval = 5000L; + + /** + * Agent micrometer exposed port + */ + private Integer agentMicrometerPort; } From fc981a9839d312d3073fc9210803f810ef357145 Mon Sep 17 00:00:00 2001 From: Pan-YuJie <646836760@qq.com> Date: Thu, 12 Sep 2024 15:36:11 +0800 Subject: [PATCH 11/14] refactor:Agent Listener logic, add configuration refreshes platform push, and carries the unique application ID --- ...ynamicThreadPoolChangeHandlerSpring2x.java | 1 + .../alarm/AgentModeNotifyConfigBuilder.java | 218 ++++++++++++++++++ .../DynamicThreadPoolRefreshListener.java | 123 +++++++++- .../support/ThreadPoolCheckAlarmSupport.java | 32 ++- 4 files changed, 362 insertions(+), 12 deletions(-) create mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java rename {kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/refresher => agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common}/event/DynamicThreadPoolRefreshListener.java (50%) diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/NacosDynamicThreadPoolChangeHandlerSpring2x.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/NacosDynamicThreadPoolChangeHandlerSpring2x.java index e13517ad..83dd7f35 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/NacosDynamicThreadPoolChangeHandlerSpring2x.java +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/NacosDynamicThreadPoolChangeHandlerSpring2x.java @@ -112,6 +112,7 @@ public class NacosDynamicThreadPoolChangeHandlerSpring2x extends AbstractConfigT }; // Add the listener to the Nacos ConfigService configService.addListener(dataId, group, configChangeListener); + LOGGER.info("[Hippo4j-Agent] Dynamic thread pool refresher, add Nacos listener successfully. namespace: {} data-id: {} group: {}", namespace, dataId, group); } catch (Exception e) { LOGGER.error(e, "[Hippo4j-Agent] Dynamic thread pool refresher, add Nacos listener failure. namespace: {} data-id: {} group: {}", namespace, dataId, group); } diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java new file mode 100644 index 00000000..9e8c1ce1 --- /dev/null +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java @@ -0,0 +1,218 @@ +/* + * 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.alarm; + +import cn.hippo4j.adapter.web.WebThreadPoolService; +import cn.hippo4j.agent.plugin.spring.common.support.ThreadPoolCheckAlarmSupport; +import cn.hippo4j.common.api.IExecutorProperties; +import cn.hippo4j.common.model.executor.ExecutorNotifyProperties; +import cn.hippo4j.common.model.executor.ExecutorProperties; +import cn.hippo4j.common.toolkit.CollectionUtil; +import cn.hippo4j.common.toolkit.StringUtil; +import cn.hippo4j.threadpool.dynamic.mode.config.properties.NotifyPlatformProperties; +import cn.hippo4j.threadpool.dynamic.mode.config.properties.WebExecutorProperties; +import cn.hippo4j.threadpool.message.api.NotifyConfigBuilder; +import cn.hippo4j.threadpool.message.api.NotifyConfigDTO; +import cn.hippo4j.threadpool.message.core.service.AlarmControlHandler; +import cn.hippo4j.threadpool.message.core.service.ThreadPoolBaseSendMessageService; +import lombok.AllArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import static cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES; +import static cn.hippo4j.common.constant.Constants.DEFAULT_INTERVAL; + +/** + * This class is responsible for building the notification configurations for thread pools in an agent mode. + * It implements the {@link NotifyConfigBuilder} interface and provides methods to build and initialize + * notification configurations for various platforms and types (e.g., ALARM, CONFIG). + * + *

The configuration is based on the properties loaded from the bootstrap configuration and includes + * handling for alarm control and notification intervals.

+ * + * TODO: This is copied from {@link cn.hippo4j.config.springboot.starter.notify.ConfigModeNotifyConfigBuilder} and can be refactored later + */ +@AllArgsConstructor +public class AgentModeNotifyConfigBuilder implements NotifyConfigBuilder { + + private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolCheckAlarmSupport.class); + + private final AlarmControlHandler alarmControlHandler; + + private final WebThreadPoolService webThreadPoolService; + + /** + * Builds the notification configurations for all executors defined in the bootstrap configuration. + * + *

This method filters the executors based on their alarm settings and constructs the notification + * configurations accordingly. If global alarm settings are disabled and there are no specific alarms + * configured for any executor, the method returns an empty map.

+ * + * @return A map containing the notification configurations, keyed by the notification type (e.g., ALARM, CONFIG). + */ + public Map> buildNotify() { + Map> resultMap = new HashMap<>(); + boolean globalAlarm = Optional.ofNullable(BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor()) + .map(ExecutorProperties::getAlarm) + .orElse(true); + List executors = BOOTSTRAP_CONFIG_PROPERTIES.getExecutors(); + if (CollectionUtil.isEmpty(executors)) { + LOGGER.warn("Failed to build notify, executors configuration is empty."); + return resultMap; + } + List actual = executors.stream() + .filter(each -> Optional.ofNullable(each.getAlarm()) + .orElse(false)) + .collect(Collectors.toList()); + if (!globalAlarm && CollectionUtil.isEmpty(actual)) { + return resultMap; + } + for (ExecutorProperties executorProperties : executors) { + Map> buildSingleNotifyConfig = buildSingleNotifyConfig(executorProperties); + initCacheAndLock(buildSingleNotifyConfig); + resultMap.putAll(buildSingleNotifyConfig); + } + // register notify config for web + WebExecutorProperties webProperties = BOOTSTRAP_CONFIG_PROPERTIES.getWeb(); + if (webProperties == null) { + return resultMap; + } + if (StringUtil.isBlank(webProperties.getThreadPoolId())) { + webProperties.setThreadPoolId(webThreadPoolService.getWebContainerType().getName()); + } + Map> webSingleNotifyConfigMap = buildSingleNotifyConfig(webProperties); + initCacheAndLock(webSingleNotifyConfigMap); + resultMap.putAll(webSingleNotifyConfigMap); + + return resultMap; + } + + /** + * Builds the notification configurations for a single executor. + * + *

This method generates two types of notifications: ALARM and CONFIG. For each type, it creates + * notification configurations based on the platforms defined in the bootstrap configuration.

+ * + * @param executorProperties The properties of the executor for which to build the notification configurations. + * @return A map containing the notification configurations for the given executor, keyed by the notification type. + */ + public Map> buildSingleNotifyConfig(IExecutorProperties executorProperties) { + String threadPoolId = executorProperties.getThreadPoolId(); + Map> resultMap = new HashMap<>(); + String alarmBuildKey = threadPoolId + "+ALARM"; + List alarmNotifyConfigs = new ArrayList<>(); + List notifyPlatforms = BOOTSTRAP_CONFIG_PROPERTIES.getNotifyPlatforms(); + for (NotifyPlatformProperties platformProperties : notifyPlatforms) { + NotifyConfigDTO notifyConfig = new NotifyConfigDTO(); + notifyConfig.setPlatform(platformProperties.getPlatform()); + notifyConfig.setTpId(threadPoolId); + notifyConfig.setType("ALARM"); + notifyConfig.setSecret(platformProperties.getSecret()); + notifyConfig.setSecretKey(getToken(platformProperties)); + notifyConfig.setInterval(buildInterval(executorProperties)); + notifyConfig.setReceives(buildReceive(executorProperties)); + alarmNotifyConfigs.add(notifyConfig); + } + resultMap.put(alarmBuildKey, alarmNotifyConfigs); + String changeBuildKey = threadPoolId + "+CONFIG"; + List changeNotifyConfigs = new ArrayList<>(); + for (NotifyPlatformProperties platformProperties : notifyPlatforms) { + NotifyConfigDTO notifyConfig = new NotifyConfigDTO(); + notifyConfig.setPlatform(platformProperties.getPlatform()); + notifyConfig.setTpId(threadPoolId); + notifyConfig.setType("CONFIG"); + notifyConfig.setSecretKey(getToken(platformProperties)); + notifyConfig.setSecret(platformProperties.getSecret()); + notifyConfig.setReceives(buildReceive(executorProperties)); + changeNotifyConfigs.add(notifyConfig); + } + resultMap.put(changeBuildKey, changeNotifyConfigs); + return resultMap; + } + + /** + * Retrieves the token for the given notification platform properties. + * + *

If the token is not explicitly set, the method returns the secret key as the fallback.

+ * + * @param platformProperties The platform properties from which to retrieve the token. + * @return The token or secret key associated with the given platform properties. + */ + private String getToken(NotifyPlatformProperties platformProperties) { + return StringUtil.isNotBlank(platformProperties.getToken()) ? platformProperties.getToken() : platformProperties.getSecretKey(); + } + + /** + * Builds the notification interval for the given executor properties. + * + *

This method first checks the executor's specific notify configuration. If not set, it falls back + * to the default executor configuration in the bootstrap properties.

+ * + * @param executorProperties The properties of the executor for which to build the notification interval. + * @return The notification interval in seconds. + */ + private int buildInterval(IExecutorProperties executorProperties) { + return Optional.ofNullable(executorProperties.getNotify()) + .map(ExecutorNotifyProperties::getInterval) + .orElse(Optional.ofNullable(BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor()) + .map(ExecutorProperties::getNotify) + .map(ExecutorNotifyProperties::getInterval) + .orElse(DEFAULT_INTERVAL)); + } + + /** + * Builds the notification recipients for the given executor properties. + * + *

This method first checks the executor's specific notify configuration. If not set, it falls back + * to the default executor configuration in the bootstrap properties.

+ * + * @param executorProperties The properties of the executor for which to build the notification recipients. + * @return A string containing the recipients of the notifications. + */ + private String buildReceive(IExecutorProperties executorProperties) { + return Optional.ofNullable(executorProperties.getNotify()) + .map(ExecutorNotifyProperties::getReceives) + .orElse(Optional.ofNullable(BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor()) + .map(ExecutorProperties::getNotify) + .map(ExecutorNotifyProperties::getReceives).orElse("")); + } + + /** + * Initializes the cache and lock mechanisms for the given notification configurations. + * + *

This method is primarily responsible for setting up alarm controls based on the notification + * configurations, ensuring that the appropriate cache and lock mechanisms are initialized for + * each thread pool and platform combination.

+ * + * @param buildSingleNotifyConfig A map containing the notification configurations that need cache and lock initialization. + */ + public void initCacheAndLock(Map> buildSingleNotifyConfig) { + buildSingleNotifyConfig.forEach( + (key, val) -> val.stream() + .filter(each -> Objects.equals("ALARM", each.getType())) + .forEach(each -> alarmControlHandler.initCacheAndLock(each.getTpId(), each.getPlatform(), each.getInterval()))); + } +} diff --git a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/refresher/event/DynamicThreadPoolRefreshListener.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/event/DynamicThreadPoolRefreshListener.java similarity index 50% rename from kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/refresher/event/DynamicThreadPoolRefreshListener.java rename to agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/event/DynamicThreadPoolRefreshListener.java index 0cb87c96..836ab712 100644 --- a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/refresher/event/DynamicThreadPoolRefreshListener.java +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/event/DynamicThreadPoolRefreshListener.java @@ -15,8 +15,11 @@ * limitations under the License. */ -package cn.hippo4j.threadpool.dynamic.mode.config.refresher.event; +package cn.hippo4j.agent.plugin.spring.common.event; +import cn.hippo4j.agent.core.util.CollectionUtil; +import cn.hippo4j.agent.plugin.spring.common.alarm.AgentModeNotifyConfigBuilder; +import cn.hippo4j.agent.plugin.spring.common.support.ThreadPoolCheckAlarmSupport; import cn.hippo4j.common.executor.ThreadPoolExecutorHolder; import cn.hippo4j.common.executor.ThreadPoolExecutorRegistry; import cn.hippo4j.common.executor.support.BlockingQueueTypeEnum; @@ -29,10 +32,17 @@ import cn.hippo4j.common.logging.api.LogManager; import cn.hippo4j.common.model.executor.ExecutorProperties; import cn.hippo4j.common.toolkit.ThreadPoolExecutorUtil; import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; +import cn.hippo4j.threadpool.message.api.NotifyConfigDTO; +import cn.hippo4j.threadpool.message.core.request.ChangeParameterNotifyRequest; +import cn.hippo4j.threadpool.message.core.service.GlobalNotifyAlarmManage; +import cn.hippo4j.threadpool.message.core.service.ThreadPoolBaseSendMessageService; +import cn.hippo4j.threadpool.message.core.service.ThreadPoolNotifyAlarm; import lombok.RequiredArgsConstructor; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -56,6 +66,10 @@ public class DynamicThreadPoolRefreshListener implements Observer> newDynamicThreadPoolNotifyMap = agentNotifyConfigBuilder.buildSingleNotifyConfig(executorProperties); + Map> notifyConfigs = threadPoolBaseSendMessageService.getNotifyConfigs(); + if (CollectionUtil.isNotEmpty(notifyConfigs)) { + for (Map.Entry> each : newDynamicThreadPoolNotifyMap.entrySet()) { + if (checkNotifyConfig) { + break; + } + List notifyConfigDTOS = notifyConfigs.get(each.getKey()); + for (NotifyConfigDTO notifyConfig : each.getValue()) { + if (!notifyConfigDTOS.contains(notifyConfig)) { + checkNotifyConfig = true; + break; + } + } + } + } + if (checkNotifyConfig) { + agentNotifyConfigBuilder.initCacheAndLock(newDynamicThreadPoolNotifyMap); + threadPoolBaseSendMessageService.putPlatform(newDynamicThreadPoolNotifyMap); + } + ThreadPoolNotifyAlarm threadPoolNotifyAlarm = GlobalNotifyAlarmManage.get(executorProperties.getThreadPoolId()); + if (threadPoolNotifyAlarm != null) { + Boolean isAlarm = executorProperties.getAlarm(); + Integer activeAlarm = executorProperties.getActiveAlarm(); + Integer capacityAlarm = + executorProperties.getCapacityAlarm(); + if ((isAlarm != null && !Objects.equals(isAlarm, threadPoolNotifyAlarm.getAlarm())) || (activeAlarm != null && !Objects.equals(activeAlarm, + threadPoolNotifyAlarm.getActiveAlarm())) || (capacityAlarm != null && !Objects.equals(capacityAlarm, threadPoolNotifyAlarm.getCapacityAlarm()))) { + checkNotifyAlarm = true; + threadPoolNotifyAlarm.setAlarm(Optional.ofNullable(isAlarm).orElse(threadPoolNotifyAlarm.getAlarm())); + threadPoolNotifyAlarm.setActiveAlarm(Optional.ofNullable(activeAlarm).orElse(threadPoolNotifyAlarm.getActiveAlarm())); + threadPoolNotifyAlarm.setCapacityAlarm(Optional.ofNullable(capacityAlarm).orElse(threadPoolNotifyAlarm.getCapacityAlarm())); + } + } + if (checkNotifyConfig || checkNotifyAlarm) { + LOG.info("[{}] Dynamic thread pool notification property changes.", executorProperties.getThreadPoolId()); + } + } + /** * Check consistency. * @@ -132,16 +196,57 @@ public class DynamicThreadPoolRefreshListener implements Observer @@ -66,7 +86,8 @@ public class ThreadPoolCheckAlarmSupport { // Initialize the AlarmControlHandler and ThreadPoolBaseSendMessageService AlarmControlHandler alarmControlHandler = new AlarmControlHandler(); - ThreadPoolBaseSendMessageService threadPoolBaseSendMessageService = createThreadPoolBaseSendMessageService(alarmControlHandler); + threadPoolBaseSendMessageService = createThreadPoolBaseSendMessageService(alarmControlHandler); + threadPoolConfigChangeHandler = new DefaultThreadPoolConfigChangeHandler(threadPoolBaseSendMessageService); // Initialize the alarm platform information initializeSendMessageHandlers(threadPoolBaseSendMessageService, alarmControlHandler); @@ -89,6 +110,10 @@ public class ThreadPoolCheckAlarmSupport { EnvironmentProperties.itemId = BOOTSTRAP_CONFIG_PROPERTIES.getItemId(); EnvironmentProperties.applicationName = SpringBootConfig.Spring.Application.name; EnvironmentProperties.active = SpringBootConfig.Spring.Profiles.active; + ConfigurableEnvironment environment = ApplicationContextHolder.getBean(ConfigurableEnvironment.class); + InetUtilsProperties inetUtilsProperties = SpringPropertyBinder.bindProperties(environment, InetUtilsProperties.PREFIX, InetUtilsProperties.class); + InetUtils inetUtils = new InetUtils(inetUtilsProperties); + IdentifyUtil.generate(environment, inetUtils); } /** @@ -123,8 +148,9 @@ public class ThreadPoolCheckAlarmSupport { threadPoolBaseSendMessageService.getSendMessageHandlers().put(larkSendMessageHandler.getType(), larkSendMessageHandler); // Construct and register notification configurations - AgentModeNotifyConfigBuilder notifyConfigBuilder = new AgentModeNotifyConfigBuilder(alarmControlHandler); - Map> notifyConfigs = notifyConfigBuilder.buildNotify(); + // TODO : register notify config for web , null Can be replaced with tomcat, jetty, undertow, etc. implementation classes + agentNotifyConfigBuilder = new AgentModeNotifyConfigBuilder(alarmControlHandler, null); + Map> notifyConfigs = agentNotifyConfigBuilder.buildNotify(); threadPoolBaseSendMessageService.getNotifyConfigs().putAll(notifyConfigs); } } From 4a31057ff794f5b3d2b924130e563d5ec9b7c007 Mon Sep 17 00:00:00 2001 From: Pan-YuJie <646836760@qq.com> Date: Sun, 15 Sep 2024 21:32:13 +0800 Subject: [PATCH 12/14] feat:Apollo Configuration Center Plugin Logic Adaptation --- .../apollo-plugin/pom.xml | 10 +++ .../ApolloDynamicThreadPoolChangeHandler.java | 87 +++++++++++++++++++ .../apollo/define/ApolloInstrumentation.java | 2 +- .../ApolloConfigConstructorInterceptor.java | 47 ++++++++++ ...nfigPropertiesLoaderCompletedListener.java | 35 ++++++++ ...ynamicThreadPoolChangeHandlerSpring1x.java | 71 --------------- .../agent/agent-example-core/pom.xml | 4 - .../config-apollo-spring-boot-1x/pom.xml | 83 ++++++++++++++++++ ...gApolloSpringBoot1xExampleApplication.java | 15 ++-- .../design/AbstractSubjectCenter.java | 16 +++- 10 files changed, 286 insertions(+), 84 deletions(-) create mode 100644 agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/ApolloDynamicThreadPoolChangeHandler.java create mode 100644 agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/ApolloConfigConstructorInterceptor.java create mode 100644 agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/listeners/ApolloConfigPropertiesLoaderCompletedListener.java delete mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/DynamicThreadPoolChangeHandlerSpring1x.java create mode 100644 examples/threadpool-example/agent/config-apollo-spring-boot-1x/pom.xml rename agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/DefaultConfigConstructorInterceptor.java => examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/apollo/v1/AgentConfigApolloSpringBoot1xExampleApplication.java (62%) diff --git a/agent/hippo4j-agent-plugin/apollo-plugin/pom.xml b/agent/hippo4j-agent-plugin/apollo-plugin/pom.xml index 90f71b3d..67ddd9c6 100644 --- a/agent/hippo4j-agent-plugin/apollo-plugin/pom.xml +++ b/agent/hippo4j-agent-plugin/apollo-plugin/pom.xml @@ -16,6 +16,16 @@ + + cn.hippo4j + hippo4j-agent-spring-plugin-common + ${project.version} + + + cn.hippo4j + hippo4j-threadpool-dynamic-mode-config + ${project.version} + com.ctrip.framework.apollo apollo-client diff --git a/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/ApolloDynamicThreadPoolChangeHandler.java b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/ApolloDynamicThreadPoolChangeHandler.java new file mode 100644 index 00000000..d6e035ff --- /dev/null +++ b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/ApolloDynamicThreadPoolChangeHandler.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.plugin.apollo; + +import cn.hippo4j.agent.plugin.spring.common.conf.SpringBootConfig; +import cn.hippo4j.agent.plugin.spring.common.toolkit.SpringPropertyBinder; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; +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.Binder; + +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 + */ +public class ApolloDynamicThreadPoolChangeHandler extends AbstractConfigThreadPoolDynamicRefresh { + + private static final ILog LOGGER = LogManager.getLogger(ApolloDynamicThreadPoolChangeHandler.class); + + /** + * Registers a listener with Apollo to monitor for changes in the thread pool configuration. + */ + @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); + } + + /** + * Builds and binds the {@link BootstrapConfigProperties} from the given configuration map. + *

+ * This method uses Spring's {@link Binder} to bind the configuration values to an instance + * of {@link BootstrapConfigProperties}, which can then be used to configure the thread pool + * dynamically. + * + * @param configInfo the configuration map containing properties to bind. + * @return the bound {@link BootstrapConfigProperties} instance. + */ + @Override + public BootstrapConfigProperties buildBootstrapProperties(Map configInfo) { + BootstrapConfigProperties bindableBootstrapConfigProperties = SpringPropertyBinder.bindProperties(configInfo, BootstrapConfigProperties.PREFIX, BootstrapConfigProperties.class); + return bindableBootstrapConfigProperties; + } +} 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 index bdf9caaa..76579b47 100644 --- 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 @@ -34,7 +34,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.interceptor.DefaultConfigConstructorInterceptor"; + private static final String CONSTRUCTOR_INTERCEPT_CLASS = "cn.hippo4j.agent.plugin.apollo.interceptor.ApolloConfigConstructorInterceptor"; @Override protected ClassMatch enhanceClass() { diff --git a/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/ApolloConfigConstructorInterceptor.java b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/ApolloConfigConstructorInterceptor.java new file mode 100644 index 00000000..16c3dced --- /dev/null +++ b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/ApolloConfigConstructorInterceptor.java @@ -0,0 +1,47 @@ +/* + * 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; +import cn.hippo4j.agent.plugin.apollo.listeners.ApolloConfigPropertiesLoaderCompletedListener; +import cn.hippo4j.common.extension.design.AbstractSubjectCenter; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Default config constructor interceptor + */ +public class ApolloConfigConstructorInterceptor implements InstanceConstructorInterceptor { + + private static final AtomicBoolean isExecuted = new AtomicBoolean(false); + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable { + + // This logic will only be executed once + if (isExecuted.compareAndSet(false, true)) { + // The Apollo plugin triggers before the Spring configuration plug-in. + // This means that when the Apollo plug-in executes, Spring's Environment is not yet ready, + // so the configuration cannot be read + // After listening to the AGENT_SPRING_PROPERTIES_LOADER_COMPLETED event, register the listener for Apollo + AbstractSubjectCenter.register(AbstractSubjectCenter.SubjectType.AGENT_SPRING_PROPERTIES_LOADER_COMPLETED, + new ApolloConfigPropertiesLoaderCompletedListener()); + } + } +} diff --git a/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/listeners/ApolloConfigPropertiesLoaderCompletedListener.java b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/listeners/ApolloConfigPropertiesLoaderCompletedListener.java new file mode 100644 index 00000000..8bef9193 --- /dev/null +++ b/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/listeners/ApolloConfigPropertiesLoaderCompletedListener.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.plugin.apollo.listeners; + +import cn.hippo4j.agent.plugin.apollo.ApolloDynamicThreadPoolChangeHandler; +import cn.hippo4j.common.extension.design.Observer; +import cn.hippo4j.common.extension.design.ObserverMessage; +import cn.hippo4j.threadpool.dynamic.api.ThreadPoolDynamicRefresh; + +/** + * Apollo Config Properties Loader Completed Listener + */ +public class ApolloConfigPropertiesLoaderCompletedListener implements Observer { + + @Override + public void accept(ObserverMessage observerMessage) { + ThreadPoolDynamicRefresh dynamicRefresh = new ApolloDynamicThreadPoolChangeHandler(); + dynamicRefresh.registerListener(); + } +} \ 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 deleted file mode 100644 index dc97aaa8..00000000 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/DynamicThreadPoolChangeHandlerSpring1x.java +++ /dev/null @@ -1,71 +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; - -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/examples/threadpool-example/agent/agent-example-core/pom.xml b/examples/threadpool-example/agent/agent-example-core/pom.xml index abc4d722..3feb4a56 100644 --- a/examples/threadpool-example/agent/agent-example-core/pom.xml +++ b/examples/threadpool-example/agent/agent-example-core/pom.xml @@ -16,10 +16,6 @@ - - org.springframework.boot - spring-boot-starter - org.springframework.boot spring-boot-starter-test diff --git a/examples/threadpool-example/agent/config-apollo-spring-boot-1x/pom.xml b/examples/threadpool-example/agent/config-apollo-spring-boot-1x/pom.xml new file mode 100644 index 00000000..6277fad0 --- /dev/null +++ b/examples/threadpool-example/agent/config-apollo-spring-boot-1x/pom.xml @@ -0,0 +1,83 @@ + + + 4.0.0 + + cn.hippo4j + hippo4j-threadpool-agent-example + 2.0.0-SNAPSHOT + + + hippo4j-threadpool-agent-config-apollo-spring-boot-1x + + + true + 1.5.22.RELEASE + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + cn.hippo4j + hippo4j-agent-example-core + ${revision} + + + com.ctrip.framework.apollo + apollo-client + + + org.slf4j + slf4j-api + 1.7.21 + + + org.springframework.cloud + spring-cloud-context + 1.3.6.RELEASE + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + \ No newline at end of file diff --git a/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/DefaultConfigConstructorInterceptor.java b/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/apollo/v1/AgentConfigApolloSpringBoot1xExampleApplication.java similarity index 62% rename from agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/DefaultConfigConstructorInterceptor.java rename to examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/apollo/v1/AgentConfigApolloSpringBoot1xExampleApplication.java index e21049a6..26896afd 100644 --- a/agent/hippo4j-agent-plugin/apollo-plugin/src/main/java/cn/hippo4j/agent/plugin/apollo/interceptor/DefaultConfigConstructorInterceptor.java +++ b/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/apollo/v1/AgentConfigApolloSpringBoot1xExampleApplication.java @@ -15,17 +15,18 @@ * limitations under the License. */ -package cn.hippo4j.agent.plugin.apollo.interceptor; +package cn.hippo4j.example.agent.config.apollo.v1; -import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance; -import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; /** - * Default config constructor interceptor + * Agent config Nacos example application. */ -public class DefaultConfigConstructorInterceptor implements InstanceConstructorInterceptor { +@SpringBootApplication(scanBasePackages = "cn.hippo4j.example.agent.core") +public class AgentConfigApolloSpringBoot1xExampleApplication { - @Override - public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable { + public static void main(String[] args) { + SpringApplication.run(AgentConfigApolloSpringBoot1xExampleApplication.class, args); } } diff --git a/infra/common/src/main/java/cn/hippo4j/common/extension/design/AbstractSubjectCenter.java b/infra/common/src/main/java/cn/hippo4j/common/extension/design/AbstractSubjectCenter.java index be8d4242..07d58a89 100644 --- a/infra/common/src/main/java/cn/hippo4j/common/extension/design/AbstractSubjectCenter.java +++ b/infra/common/src/main/java/cn/hippo4j/common/extension/design/AbstractSubjectCenter.java @@ -96,6 +96,15 @@ public class AbstractSubjectCenter { observers.remove(observer); } + /** + * get observer by subject. + * + * @param subject + */ + public static List get(SubjectType subjectType) { + return OBSERVERS_MAP.get(subjectType.name()); + } + /** * Notify. * @@ -145,6 +154,11 @@ public class AbstractSubjectCenter { /** * Thread-pool dynamic refresh. */ - THREAD_POOL_DYNAMIC_REFRESH + THREAD_POOL_DYNAMIC_REFRESH, + + /** + * Agent Spring Properties Loader Completed. + */ + AGENT_SPRING_PROPERTIES_LOADER_COMPLETED } } From 264ef3cd1f98041f4453a97c3321c1c873676105 Mon Sep 17 00:00:00 2001 From: Pan-YuJie <646836760@qq.com> Date: Sun, 15 Sep 2024 21:51:04 +0800 Subject: [PATCH 13/14] feat:Completed the implementation of Nacos Configuration Center plugin and Nacos,Apollo plugins adapted to Spring 1.x , 2.x environment --- .../hippo4j-agent-plugin/nacos-plugin/pom.xml | 37 +++++ .../NacosDynamicThreadPoolChangeHandler.java | 129 ++++++++++++++++++ .../nacos/boot/NacosPluginBootService.java | 48 +++++++ .../NacosCloudAdapterInstrumentation.java | 75 ++++++++++ .../nacos/define/NacosInstrumentation.java | 66 +++++++++ ...dapterConfigInstanceMethodInterceptor.java | 68 +++++++++ .../NacosConfigConstructorInterceptor.java | 58 ++++++++ ...nfigPropertiesLoaderCompletedListener.java | 35 +++++ .../cn.hippo4j.agent.core.boot.BootService | 17 +++ .../src/main/resources/hippo4j-plugin.def | 18 +++ agent/hippo4j-agent-plugin/pom.xml | 1 + ...=> ApplicationContextInstrumentation.java} | 67 ++++----- ...ava => ApplicationContextInterceptor.java} | 34 ++--- .../src/main/resources/hippo4j-plugin.def | 2 +- .../EventPublishingStartedInterceptor.java | 31 +++-- .../spring-plugin-common/pom.xml | 5 + .../support/SpringEnvironmentSupport.java | 2 +- .../support/SpringPropertiesLoader.java | 27 +++- .../support/ThreadPoolCheckAlarmSupport.java | 27 ++-- .../support/ThreadPoolMonitorSupport.java | 35 ++--- .../common/toolkit/SpringPropertyBinder.java | 33 +++++ .../src/main/resources/bootstrap.properties | 53 +++++++ .../src/main/resources/bootstrap.properties | 31 +++-- .../agent/config-nacos-spring-boot-1x/pom.xml | 74 ++++++++++ ...igNacosSpringBoot1xExampleApplication.java | 33 +++++ .../src/main/resources/bootstrap.properties | 60 ++++++++ .../agent/config-nacos/pom.xml | 7 +- .../AgentConfigNacosExampleApplication.java | 5 +- .../src/main/resources/bootstrap.properties | 31 +++-- examples/threadpool-example/agent/pom.xml | 2 + .../nacos/ConfigNacosExampleApplication.java | 2 +- .../DefaultThreadPoolCheckAlarmHandler.java | 13 ++ 32 files changed, 987 insertions(+), 139 deletions(-) create mode 100644 agent/hippo4j-agent-plugin/nacos-plugin/pom.xml create mode 100644 agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/NacosDynamicThreadPoolChangeHandler.java create mode 100644 agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/boot/NacosPluginBootService.java create mode 100644 agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/define/NacosCloudAdapterInstrumentation.java create mode 100644 agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/define/NacosInstrumentation.java create mode 100644 agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosCloudAdapterConfigInstanceMethodInterceptor.java create mode 100644 agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosConfigConstructorInterceptor.java create mode 100644 agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/listeners/NacosConfigPropertiesLoaderCompletedListener.java create mode 100644 agent/hippo4j-agent-plugin/nacos-plugin/src/main/resources/META-INF/services/cn.hippo4j.agent.core.boot.BootService create mode 100644 agent/hippo4j-agent-plugin/nacos-plugin/src/main/resources/hippo4j-plugin.def rename agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/define/{EventPublishingRunListenerInstrumentation.java => ApplicationContextInstrumentation.java} (51%) rename agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/interceptor/{EventPublishingFinishedInterceptor.java => ApplicationContextInterceptor.java} (64%) create mode 100644 examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/resources/bootstrap.properties create mode 100644 examples/threadpool-example/agent/config-nacos-spring-boot-1x/pom.xml create mode 100644 examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/nacos/v1/AgentConfigNacosSpringBoot1xExampleApplication.java create mode 100644 examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/resources/bootstrap.properties rename examples/threadpool-example/config/config-nacos/src/main/java/cn/hippo4j/example/{ => agent}/config/nacos/ConfigNacosExampleApplication.java (96%) diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/pom.xml b/agent/hippo4j-agent-plugin/nacos-plugin/pom.xml new file mode 100644 index 00000000..f048923f --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + cn.hippo4j + hippo4j-agent-plugin + ${revision} + + + hippo4j-agent-nacos-plugin + + + 2.2.1 + + + + + cn.hippo4j + hippo4j-agent-spring-plugin-common + ${project.version} + + + cn.hippo4j + hippo4j-threadpool-dynamic-mode-config + ${project.version} + + + com.alibaba.nacos + nacos-client + ${nacos.version} + provided + + + + \ No newline at end of file diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/NacosDynamicThreadPoolChangeHandler.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/NacosDynamicThreadPoolChangeHandler.java new file mode 100644 index 00000000..866f3ccc --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/NacosDynamicThreadPoolChangeHandler.java @@ -0,0 +1,129 @@ +/* + * 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.nacos; + +import cn.hippo4j.agent.plugin.spring.common.conf.SpringBootConfig; +import cn.hippo4j.agent.plugin.spring.common.toolkit.SpringPropertyBinder; +import cn.hippo4j.common.executor.ThreadFactoryBuilder; +import cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; +import cn.hippo4j.threadpool.dynamic.mode.config.parser.ConfigParserHandler; +import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; +import cn.hippo4j.threadpool.dynamic.mode.config.refresher.AbstractConfigThreadPoolDynamicRefresh; +import com.alibaba.nacos.api.NacosFactory; +import com.alibaba.nacos.api.PropertyKeyConst; +import com.alibaba.nacos.api.config.ConfigService; +import com.alibaba.nacos.api.config.listener.Listener; +import org.springframework.boot.context.properties.bind.Binder; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledThreadPoolExecutor; + +import static cn.hippo4j.common.constant.Constants.DEFAULT_NAMESPACE_ID; + +/** + * NacosDynamicThreadPoolChangeHandler is responsible for handling dynamic thread pool + * configuration changes in a Spring environment by listening to configuration updates from Nacos. + *

+ * This class extends {@link AbstractConfigThreadPoolDynamicRefresh} and implements the logic + * to register a Nacos listener, handle configuration changes, and dynamically refresh the thread pool + * properties based on the new configuration. + *

+ */ +public class NacosDynamicThreadPoolChangeHandler extends AbstractConfigThreadPoolDynamicRefresh { + + private static final ILog LOGGER = LogManager.getLogger(NacosDynamicThreadPoolChangeHandler.class); + + /** + * Registers a listener with Nacos to monitor for changes in the thread pool configuration. + *

+ * This method sets up the Nacos {@link ConfigService} with the server address and namespace + * from the Spring Boot configuration. It then adds a listener that will receive and process + * configuration updates, triggering a dynamic refresh of thread pool settings. + */ + @Override + public void registerListener() { + // Retrieve necessary configuration properties + String configFileType = SpringBootConfig.Spring.Dynamic.Thread_Pool.CONFIG_FILE_TYPE; + String serverAddr = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.SERVER_ADDR; + String dataId = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.DATA_ID; + String group = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.GROUP; + String namespace = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.NAMESPACE.get(0); + namespace = namespace.equals(DEFAULT_NAMESPACE_ID) ? "" : namespace; + try { + // Initialize Nacos ConfigService with the provided properties + Properties properties = new Properties(); + properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr); + properties.put(PropertyKeyConst.NAMESPACE, namespace); + ConfigService configService = NacosFactory.createConfigService(properties); + + // Define the listener to handle configuration changes + Listener configChangeListener = new Listener() { + + @Override + public void receiveConfigInfo(String configInfo) { + LOGGER.debug("Received configuration: " + configInfo); + Map changeValueMap = new HashMap<>(); + try { + // Parse the configuration and map the values to the appropriate keys + Map configInfoMap = ConfigParserHandler.getInstance().parseConfig(configInfo, configFileType); + configInfoMap.forEach((key, value) -> { + if (key instanceof String) { + changeValueMap.put((String) key, value); + } + }); + } catch (IOException e) { + LOGGER.error(e, "[Hippo4j-Agent] Dynamic thread pool refresher, Failed to resolve configuration. configFileType: {} configInfo: {} ", configFileType, configInfo); + } + // Trigger the dynamic refresh with the parsed configuration + dynamicRefresh(configFileType, configInfo, changeValueMap); + } + + @Override + public Executor getExecutor() { + return new ScheduledThreadPoolExecutor(1, ThreadFactoryBuilder.builder().daemon(true).prefix("client.dynamic.refresh.agent").build()); + } + }; + // Add the listener to the Nacos ConfigService + configService.addListener(dataId, group, configChangeListener); + LOGGER.info("[Hippo4j-Agent] Dynamic thread pool refresher, add Nacos listener successfully. namespace: {} data-id: {} group: {}", namespace, dataId, group); + } catch (Exception e) { + LOGGER.error(e, "[Hippo4j-Agent] Dynamic thread pool refresher, add Nacos listener failure. namespace: {} data-id: {} group: {}", namespace, dataId, group); + } + } + + /** + * Builds and binds the {@link BootstrapConfigProperties} from the given configuration map. + *

+ * This method uses Spring's {@link Binder} to bind the configuration values to an instance + * of {@link BootstrapConfigProperties}, which can then be used to configure the thread pool + * dynamically. + * + * @param configInfo the configuration map containing properties to bind. + * @return the bound {@link BootstrapConfigProperties} instance. + */ + @Override + public BootstrapConfigProperties buildBootstrapProperties(Map configInfo) { + BootstrapConfigProperties bindableBootstrapConfigProperties = SpringPropertyBinder.bindProperties(configInfo, BootstrapConfigProperties.PREFIX, BootstrapConfigProperties.class); + return bindableBootstrapConfigProperties; + } +} diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/boot/NacosPluginBootService.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/boot/NacosPluginBootService.java new file mode 100644 index 00000000..65fe2a47 --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/boot/NacosPluginBootService.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.nacos.boot; + +import cn.hippo4j.agent.core.boot.BootService; +import cn.hippo4j.agent.core.boot.DefaultImplementor; + +/** + * Nacos plugin boot service + */ +@DefaultImplementor +public class NacosPluginBootService 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/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/define/NacosCloudAdapterInstrumentation.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/define/NacosCloudAdapterInstrumentation.java new file mode 100644 index 00000000..6fe99477 --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/define/NacosCloudAdapterInstrumentation.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.plugin.nacos.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 cn.hippo4j.agent.core.plugin.match.NameMatch; +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; + +public class NacosCloudAdapterInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration"; + + private static final String INSTANCE_METHODS_INTERCEPT_CLASS = "cn.hippo4j.agent.plugin.nacos.interceptor.NacosCloudAdapterConfigInstanceMethodInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.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("initialize"); + } + + @Override + public String getMethodsInterceptor() { + return INSTANCE_METHODS_INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }}; + } + + @Override + protected List witnessMethods() { + return Collections.singletonList(new WitnessMethod("com.alibaba.cloud.nacos.client.NacosPropertySourceLocator", named("locate"))); + } +} diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/define/NacosInstrumentation.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/define/NacosInstrumentation.java new file mode 100644 index 00000000..d33c6031 --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/define/NacosInstrumentation.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.nacos.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; + +/** + * Nacos instrumentation + */ +public class NacosInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.alibaba.nacos.client.config.NacosConfigService"; + + private static final String CONSTRUCTOR_INTERCEPT_CLASS = "cn.hippo4j.agent.plugin.nacos.interceptor.NacosConfigConstructorInterceptor"; + + @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/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosCloudAdapterConfigInstanceMethodInterceptor.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosCloudAdapterConfigInstanceMethodInterceptor.java new file mode 100644 index 00000000..7a7cf841 --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosCloudAdapterConfigInstanceMethodInterceptor.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.nacos.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.SpringPropertiesLoader; +import cn.hippo4j.agent.plugin.spring.common.support.SpringThreadPoolRegisterSupport; +import cn.hippo4j.core.config.ApplicationContextHolder; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; + +import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Nacos Cloud config constructor interceptor + */ +public class NacosCloudAdapterConfigInstanceMethodInterceptor implements InstanceMethodsAroundInterceptor { + + private static final AtomicBoolean isExecuted = new AtomicBoolean(false); + + @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 { + // This logic will only be executed once + if (isExecuted.compareAndSet(false, true)) { + // Get the configurable Application Context + ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) allArguments[0]; + ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment(); + + // Remote Nacos configuration swiped into SpringPropertiesLoader + SpringPropertiesLoader.loadSpringProperties(environment); + // Refresh thread pool instances through configuration + SpringThreadPoolRegisterSupport.registerThreadPoolInstances(ApplicationContextHolder.getInstance()); + + } + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + + } +} diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosConfigConstructorInterceptor.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosConfigConstructorInterceptor.java new file mode 100644 index 00000000..7a629319 --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosConfigConstructorInterceptor.java @@ -0,0 +1,58 @@ +/* + * 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.nacos.interceptor; + +import cn.hippo4j.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import cn.hippo4j.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import cn.hippo4j.agent.plugin.nacos.NacosDynamicThreadPoolChangeHandler; +import cn.hippo4j.agent.plugin.nacos.listeners.NacosConfigPropertiesLoaderCompletedListener; +import cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader; +import cn.hippo4j.common.extension.design.AbstractSubjectCenter; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Nacos config constructor interceptor + */ +public class NacosConfigConstructorInterceptor implements InstanceConstructorInterceptor { + + private static final AtomicBoolean isExecuted = new AtomicBoolean(false); + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) throws Throwable { + // This logic will only be executed once + if (isExecuted.compareAndSet(false, true)) { + + // 判断 SpringPropertiesLoader 是否初始化 + AtomicBoolean active = SpringPropertiesLoader.getActive(); + + // For Nacos-Cloud, the SpringPropertiesLoader environment initialization is triggered first, and then the logic to register listeners is triggered + // For Nacos-Boot, the listener is registered first, and the SpringPropertiesLoader environment is initialized + if (Boolean.TRUE.equals(active.get())) { + new NacosDynamicThreadPoolChangeHandler().registerListener(); + return; + } + + // The Nacos plugin triggers before the Spring configuration plug-in. + // This means that when the Apollo plug-in executes, Spring's Environment is not yet ready, + // so the configuration cannot be read + // After listening to the AGENT_SPRING_PROPERTIES_LOADER_COMPLETED event, register the listener for Apollo + AbstractSubjectCenter.register(AbstractSubjectCenter.SubjectType.AGENT_SPRING_PROPERTIES_LOADER_COMPLETED, new NacosConfigPropertiesLoaderCompletedListener()); + } + } +} diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/listeners/NacosConfigPropertiesLoaderCompletedListener.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/listeners/NacosConfigPropertiesLoaderCompletedListener.java new file mode 100644 index 00000000..fa7a795b --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/listeners/NacosConfigPropertiesLoaderCompletedListener.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.plugin.nacos.listeners; + +import cn.hippo4j.agent.plugin.nacos.NacosDynamicThreadPoolChangeHandler; +import cn.hippo4j.common.extension.design.Observer; +import cn.hippo4j.common.extension.design.ObserverMessage; +import cn.hippo4j.threadpool.dynamic.api.ThreadPoolDynamicRefresh; + +/** + * Nacos Config Properties Loader Completed Listener + */ +public class NacosConfigPropertiesLoaderCompletedListener implements Observer { + + @Override + public void accept(ObserverMessage observerMessage) { + ThreadPoolDynamicRefresh dynamicRefresh = new NacosDynamicThreadPoolChangeHandler(); + dynamicRefresh.registerListener(); + } +} diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/resources/META-INF/services/cn.hippo4j.agent.core.boot.BootService b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/resources/META-INF/services/cn.hippo4j.agent.core.boot.BootService new file mode 100644 index 00000000..5337b3b0 --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-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.nacos.boot.NacosPluginBootService \ No newline at end of file diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/resources/hippo4j-plugin.def b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/resources/hippo4j-plugin.def new file mode 100644 index 00000000..fb175826 --- /dev/null +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/resources/hippo4j-plugin.def @@ -0,0 +1,18 @@ +# 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. + +nacos-plugin=cn.hippo4j.agent.plugin.nacos.define.NacosInstrumentation +nacos-cloud-adapter-plugin=cn.hippo4j.agent.plugin.nacos.define.NacosCloudAdapterInstrumentation \ No newline at end of file diff --git a/agent/hippo4j-agent-plugin/pom.xml b/agent/hippo4j-agent-plugin/pom.xml index 4a3f7954..0217b17b 100644 --- a/agent/hippo4j-agent-plugin/pom.xml +++ b/agent/hippo4j-agent-plugin/pom.xml @@ -16,6 +16,7 @@ threadpool-plugin adapter-plugins apollo-plugin + nacos-plugin 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/ApplicationContextInstrumentation.java similarity index 51% rename from agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/define/EventPublishingRunListenerInstrumentation.java rename to agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/define/ApplicationContextInstrumentation.java index 06c93998..315f3a96 100644 --- 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/ApplicationContextInstrumentation.java @@ -28,18 +28,17 @@ 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; +import static net.bytebuddy.matcher.ElementMatchers.named; /** - * Event publishing run listener instrumentation + * Application Context Refresh instrumentation */ -public class EventPublishingRunListenerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { +public class ApplicationContextInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { - private static final String ENHANCE_CLASS = "org.springframework.boot.context.event.EventPublishingRunListener"; + private static final String ENHANCE_CLASS = "org.springframework.context.support.AbstractApplicationContext"; - 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"; + private static final String APPLICATION_CONTEXT_REFRESH_INTERCEPTOR = "cn.hippo4j.agent.plugin.spring.boot.v1.interceptor.ApplicationContextInterceptor"; @Override protected ClassMatch enhanceClass() { @@ -53,47 +52,27 @@ public class EventPublishingRunListenerInstrumentation extends ClassInstanceMeth @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; - } - } - }; + return new InstanceMethodsInterceptPoint[]{new InstanceMethodsInterceptPoint() { + + @Override + public ElementMatcher getMethodsMatcher() { + return named("refresh"); + } + + @Override + public String getMethodsInterceptor() { + return APPLICATION_CONTEXT_REFRESH_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }}; } @Override protected List witnessMethods() { - return Collections.singletonList(new WitnessMethod("org.springframework.boot.context.event.EventPublishingRunListener", - named("finished"))); + 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/ApplicationContextInterceptor.java similarity index 64% rename from agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/interceptor/EventPublishingFinishedInterceptor.java rename to agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/interceptor/ApplicationContextInterceptor.java index dcee8726..8297bfb9 100644 --- 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/ApplicationContextInterceptor.java @@ -17,29 +17,25 @@ package cn.hippo4j.agent.plugin.spring.boot.v1.interceptor; -import cn.hippo4j.agent.adapter.dubbo.DubboThreadPoolAdapter; -import cn.hippo4j.common.logging.api.ILog; -import cn.hippo4j.common.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.event.DynamicThreadPoolRefreshListener; 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 cn.hippo4j.common.extension.design.AbstractSubjectCenter; +import cn.hippo4j.core.config.ApplicationContextHolder; import org.springframework.context.ConfigurableApplicationContext; import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicBoolean; /** - * Event publishing finished interceptor + * Application Context Refresh interceptor */ -public class EventPublishingFinishedInterceptor implements InstanceMethodsAroundInterceptor { +public class ApplicationContextInterceptor implements InstanceMethodsAroundInterceptor { - private static final ILog FILE_LOGGER = LogManager.getLogger(EventPublishingFinishedInterceptor.class); - private static final Logger LOGGER = LoggerFactory.getLogger(EventPublishingFinishedInterceptor.class); + private static final AtomicBoolean isExecuted = new AtomicBoolean(false); @Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { @@ -48,16 +44,22 @@ public class EventPublishingFinishedInterceptor implements InstanceMethodsAround @Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { - ConfigurableApplicationContext context = (ConfigurableApplicationContext) allArguments[0]; + // Since the refresh() method is a method of the AbstractApplicationContext class, + // the AbstractApplicationContext itself is an implementation class of the ApplicationContext. + // Therefore, can treat the class instance itself as an ApplicationContext object. + ConfigurableApplicationContext context = (ConfigurableApplicationContext) objInst; 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(); + // This logic will only be executed once + if (isExecuted.compareAndSet(false, true)) { + ApplicationContextHolder contextHolder = new ApplicationContextHolder(); + contextHolder.setApplicationContext(context); + SpringPropertiesLoader.loadSpringProperties(context.getEnvironment()); + AbstractSubjectCenter.register(AbstractSubjectCenter.SubjectType.THREAD_POOL_DYNAMIC_REFRESH, new DynamicThreadPoolRefreshListener()); + } return ret; } 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 index 8bb37e3a..403de5ce 100644 --- 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 @@ -14,4 +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 \ No newline at end of file +spring-boot-1.x=cn.hippo4j.agent.plugin.spring.boot.v1.define.ApplicationContextInstrumentation 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 index 45423564..74faef32 100644 --- 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 @@ -17,29 +17,28 @@ package cn.hippo4j.agent.plugin.spring.boot.v2.interceptor; -import cn.hippo4j.agent.plugin.spring.boot.v2.NacosDynamicThreadPoolChangeHandlerSpring2x; -import cn.hippo4j.common.logging.api.ILog; -import cn.hippo4j.common.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.event.DynamicThreadPoolRefreshListener; 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 cn.hippo4j.common.logging.api.ILog; +import cn.hippo4j.common.logging.api.LogManager; +import cn.hippo4j.core.config.ApplicationContextHolder; import org.springframework.context.ConfigurableApplicationContext; import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicBoolean; /** * Event publishing started interceptor */ -@Slf4j public class EventPublishingStartedInterceptor implements InstanceMethodsAroundInterceptor { + private static final AtomicBoolean isExecuted = new AtomicBoolean(false); + private static final ILog LOGGER = LogManager.getLogger(EventPublishingStartedInterceptor.class); @Override @@ -55,13 +54,15 @@ public class EventPublishingStartedInterceptor implements InstanceMethodsAroundI SpringThreadPoolRegisterSupport.registerThreadPoolInstances(context); return ret; } - SpringPropertiesLoader.loadSpringProperties(context.getEnvironment()); - // ThreadPoolDynamicRefresh dynamicRefresh = new DynamicThreadPoolChangeHandlerSpring2x(); - // TODO Nacos配置 - ThreadPoolDynamicRefresh dynamicRefresh = new NacosDynamicThreadPoolChangeHandlerSpring2x(); - dynamicRefresh.registerListener(); - AbstractSubjectCenter.register(AbstractSubjectCenter.SubjectType.THREAD_POOL_DYNAMIC_REFRESH, - new DynamicThreadPoolRefreshListener()); + // This logic will only be executed once + if (isExecuted.compareAndSet(false, true)) { + ApplicationContextHolder contextHolder = new ApplicationContextHolder(); + contextHolder.setApplicationContext(context); + // Load Spring Properties + SpringPropertiesLoader.loadSpringProperties(context.getEnvironment()); + // register Dynamic ThreadPool Refresh Listener + AbstractSubjectCenter.register(AbstractSubjectCenter.SubjectType.THREAD_POOL_DYNAMIC_REFRESH, new DynamicThreadPoolRefreshListener()); + } return ret; } 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 index c472c7b3..853e0706 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml @@ -55,6 +55,11 @@ ${project.version} provided + + cn.hippo4j + hippo4j-threadpool-infra-common + ${project.version} + cn.hippo4j hippo4j-threadpool-monitor-elasticsearch 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 index 4733acdc..d4b7e619 100644 --- 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 @@ -32,6 +32,6 @@ public class SpringEnvironmentSupport { 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); + environment.getPropertySources().addLast(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 index d9fbfac7..a73c800e 100644 --- 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 @@ -19,19 +19,24 @@ package cn.hippo4j.agent.plugin.spring.common.support; import cn.hippo4j.agent.core.boot.SpringBootConfigInitializer; import cn.hippo4j.agent.plugin.spring.common.toolkit.SpringPropertyBinder; +import cn.hippo4j.common.extension.design.AbstractSubjectCenter; import cn.hippo4j.common.logging.api.ILog; import cn.hippo4j.common.logging.api.LogManager; import cn.hippo4j.core.toolkit.inet.InetUtilsProperties; import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; +import lombok.Getter; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.PropertiesPropertySource; import org.springframework.core.env.PropertySource; import java.util.ArrayList; +import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Properties; +import java.util.concurrent.atomic.AtomicBoolean; + import static cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties.PREFIX; /** @@ -41,6 +46,13 @@ public class SpringPropertiesLoader { private static final ILog LOGGER = LogManager.getLogger(SpringPropertiesLoader.class); + /** + * A flag used to indicate whether loadSpringProperties() method has been called, + * Used to determine whether the SpringPropertiesLoader has been initialized + */ + @Getter + private static final AtomicBoolean active = new AtomicBoolean(Boolean.FALSE); + public static BootstrapConfigProperties BOOTSTRAP_CONFIG_PROPERTIES = new BootstrapConfigProperties(); public static InetUtilsProperties INET_UTILS_PROPERTIES = new InetUtilsProperties(); @@ -54,13 +66,11 @@ public class SpringPropertiesLoader { } // Sort to ensure that the configuration in the configuration center is after the array // To get the latest configuration information - propertySourceList.sort((o1, o2) -> { - boolean o1Contains = o1.getName().toLowerCase().contains("apollo") || o1.getName().toLowerCase().contains("nacos"); - boolean o2Contains = (o2.getName().toLowerCase().contains("apollo") || o2.getName().toLowerCase().contains("nacos")); - return Boolean.compare(o1Contains, o2Contains); - }); + propertySourceList.sort(Comparator.comparing( + // Make sure that Nacos boot's propertySource is placed first in the propertySourceList + item -> !item.getClass().getName().equals("com.alibaba.nacos.spring.core.env.NacosPropertySource"))); - for (int i = 0; i <= propertySourceList.size() - 1; i++) { + 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()); @@ -87,6 +97,11 @@ public class SpringPropertiesLoader { // initialize BootstrapConfigProperties BOOTSTRAP_CONFIG_PROPERTIES = SpringPropertyBinder.bindProperties(environment, PREFIX, BootstrapConfigProperties.class); INET_UTILS_PROPERTIES = SpringPropertyBinder.bindProperties(environment, InetUtilsProperties.PREFIX, InetUtilsProperties.class); + // Send AGENT_SPRING_PROPERTIES_LOADER_COMPLETED notification event Before active is false + if (AbstractSubjectCenter.get(AbstractSubjectCenter.SubjectType.AGENT_SPRING_PROPERTIES_LOADER_COMPLETED) != null && Boolean.FALSE.equals(active.get())) { + AbstractSubjectCenter.notify(AbstractSubjectCenter.SubjectType.AGENT_SPRING_PROPERTIES_LOADER_COMPLETED, () -> ""); + } + active.set(Boolean.TRUE); // Enable the thread pool check alert handler ThreadPoolCheckAlarmSupport.enableThreadPoolCheckAlarmHandler(); // Enable thread pool monitor handler diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolCheckAlarmSupport.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolCheckAlarmSupport.java index a5d0334a..4cbd54e3 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolCheckAlarmSupport.java +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolCheckAlarmSupport.java @@ -17,11 +17,9 @@ package cn.hippo4j.agent.plugin.spring.common.support; -import cn.hippo4j.adapter.web.jetty.DefaultJettyWebThreadPoolHandler; import cn.hippo4j.agent.plugin.spring.common.alarm.AgentModeNotifyConfigBuilder; import cn.hippo4j.agent.plugin.spring.common.conf.SpringBootConfig; import cn.hippo4j.agent.plugin.spring.common.toolkit.SpringPropertyBinder; -import cn.hippo4j.common.api.ThreadPoolConfigChange; import cn.hippo4j.common.propertie.EnvironmentProperties; import cn.hippo4j.core.config.ApplicationContextHolder; import cn.hippo4j.core.toolkit.IdentifyUtil; @@ -36,7 +34,6 @@ import cn.hippo4j.threadpool.message.core.service.AlarmControlHandler; import cn.hippo4j.threadpool.message.core.service.DefaultThreadPoolConfigChangeHandler; import cn.hippo4j.threadpool.message.core.service.SendMessageHandler; import cn.hippo4j.threadpool.message.core.service.ThreadPoolBaseSendMessageService; -import cn.hippo4j.threadpool.message.core.service.ThreadPoolSendMessageService; import lombok.Getter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,6 +62,8 @@ public class ThreadPoolCheckAlarmSupport { @Getter private static AgentModeNotifyConfigBuilder agentNotifyConfigBuilder; + private static DefaultThreadPoolCheckAlarmHandler checkAlarmHandler; + /** * Enables the thread pool check alarm handler if the corresponding configuration property is set to {@code true}. *

@@ -92,11 +91,8 @@ public class ThreadPoolCheckAlarmSupport { // Initialize the alarm platform information initializeSendMessageHandlers(threadPoolBaseSendMessageService, alarmControlHandler); - // Initialize the thread pool check alarm handler with necessary services - DefaultThreadPoolCheckAlarmHandler checkAlarmHandler = new DefaultThreadPoolCheckAlarmHandler(threadPoolBaseSendMessageService); - - // Run the check alarm handler to start monitoring the thread pool - checkAlarmHandler.scheduleExecute(); + // Execute scheduled task to check an alarm + scheduleExecute(threadPoolBaseSendMessageService); } } @@ -134,7 +130,7 @@ public class ThreadPoolCheckAlarmSupport { * It also constructs and registers notification configurations using the {@link AgentModeNotifyConfigBuilder}. * * @param threadPoolBaseSendMessageService The {@link ThreadPoolBaseSendMessageService} in which message handlers and notification configurations will be registered. - * @param alarmControlHandler The {@link AlarmControlHandler} used to handle alarms and notifications. + * @param alarmControlHandler The {@link AlarmControlHandler} used to handle alarms and notifications. */ private static void initializeSendMessageHandlers(ThreadPoolBaseSendMessageService threadPoolBaseSendMessageService, AlarmControlHandler alarmControlHandler) { // Initialize message handlers @@ -153,4 +149,17 @@ public class ThreadPoolCheckAlarmSupport { Map> notifyConfigs = agentNotifyConfigBuilder.buildNotify(); threadPoolBaseSendMessageService.getNotifyConfigs().putAll(notifyConfigs); } + + // 启动或重新启动检查任务 + public static void scheduleExecute(ThreadPoolBaseSendMessageService threadPoolBaseSendMessageService) { + // If a task is already running, cancel it first + if (checkAlarmHandler != null) { + // Shut down the thread pool and prepare to regenerate the listener thread pool + checkAlarmHandler.destroyScheduleExecute(); + } + // Initialize the thread pool check alarm handler with necessary services + checkAlarmHandler = new DefaultThreadPoolCheckAlarmHandler(threadPoolBaseSendMessageService); + // Run the check alarm handler to start monitoring the thread pool + checkAlarmHandler.scheduleExecute(); + } } diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java index 3af1e1e6..d44dbf3d 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java @@ -19,7 +19,6 @@ package cn.hippo4j.agent.plugin.spring.common.support; import cn.hippo4j.agent.plugin.spring.common.monitor.MonitorHandlersConfigurator; import cn.hippo4j.agent.plugin.spring.common.monitor.MonitorMetricEndpoint; -import cn.hippo4j.common.executor.ThreadFactoryBuilder; import cn.hippo4j.common.executor.ThreadPoolExecutorRegistry; import cn.hippo4j.common.extension.spi.ServiceLoaderRegistry; import cn.hippo4j.common.monitor.MonitorCollectTypeEnum; @@ -27,6 +26,7 @@ import cn.hippo4j.common.toolkit.StringUtil; import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; import cn.hippo4j.threadpool.dynamic.mode.config.properties.MonitorProperties; import cn.hippo4j.threadpool.monitor.api.ThreadPoolMonitor; +import lombok.Getter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.ConfigurableEnvironment; @@ -36,10 +36,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; -import java.util.Optional; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import static cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES; @@ -52,7 +52,16 @@ public class ThreadPoolMonitorSupport { private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolMonitorSupport.class); - private static List threadPoolMonitors; + /** + * A flag used to indicate whether enableThreadPoolMonitorHandler() method has been called, + * Used to determine whether the ThreadPoolMonitorHandler has been enable + */ + @Getter + private static final AtomicBoolean active = new AtomicBoolean(Boolean.FALSE); + + private static final ScheduledExecutorService collectScheduledExecutor = new ScheduledThreadPoolExecutor(1, r -> new Thread(r, "client.agent.scheduled.collect.data")); + + private static final List threadPoolMonitors = new ArrayList<>(); static { // Register the ThreadPoolMonitor service with the ServiceLoaderRegistry @@ -78,22 +87,19 @@ public class ThreadPoolMonitorSupport { public static void enableThreadPoolMonitorHandler(Environment environment) { BootstrapConfigProperties properties = BOOTSTRAP_CONFIG_PROPERTIES; MonitorProperties monitor = properties.getMonitor(); - if (Objects.isNull(monitor) - || !monitor.getEnable() - || StringUtil.isBlank(monitor.getThreadPoolTypes()) - || StringUtil.isBlank(monitor.getCollectTypes())) { + if (Objects.isNull(monitor) || !monitor.getEnable() || StringUtil.isBlank(monitor.getThreadPoolTypes()) || StringUtil.isBlank(monitor.getCollectTypes())) { return; } LOGGER.info("[Hippo4j-Agent] Start monitoring the running status of dynamic thread pools."); - threadPoolMonitors = new ArrayList<>(); - ScheduledExecutorService collectScheduledExecutor = new ScheduledThreadPoolExecutor( - 1, - r -> new Thread(r, "client.agent.scheduled.collect.data")); // Initialize monitoring components for the dynamic thread pools MonitorHandlersConfigurator.initializeMonitorHandlers(monitor, (ConfigurableEnvironment) environment, threadPoolMonitors); + // Determine whether the task is successfully enabled + // return directly if it has been enabled, and do not start the thread pool repeatedly + if (Boolean.TRUE.equals(active.get())) return; + // Expose metric endpoints based on the configured collect types List collectTypes = Arrays.asList(monitor.getCollectTypes().split(",")); if (collectTypes.contains(MonitorCollectTypeEnum.MICROMETER.getValue())) { @@ -101,12 +107,9 @@ public class ThreadPoolMonitorSupport { } // Schedule periodic collection of metrics from the thread pools - collectScheduledExecutor.scheduleWithFixedDelay( - scheduleRunnable(), - monitor.getInitialDelay(), - monitor.getCollectInterval(), - TimeUnit.MILLISECONDS); + collectScheduledExecutor.scheduleWithFixedDelay(scheduleRunnable(), monitor.getInitialDelay(), monitor.getCollectInterval(), TimeUnit.MILLISECONDS); + active.set(true); if (ThreadPoolExecutorRegistry.getThreadPoolExecutorSize() > 0) { LOGGER.info("[Hippo4j-Agent] Dynamic thread pool: [{}]. The dynamic thread pool starts data collection and reporting.", ThreadPoolExecutorRegistry.getThreadPoolExecutorSize()); } diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/toolkit/SpringPropertyBinder.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/toolkit/SpringPropertyBinder.java index 4491eb70..22a6bc62 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/toolkit/SpringPropertyBinder.java +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/toolkit/SpringPropertyBinder.java @@ -72,6 +72,39 @@ public class SpringPropertyBinder { } } + /** + * Binds properties from a map to an instance of the specified configuration class. + * + * @param configInfo a map containing property paths and their values. + * @param prefix the prefix to filter properties for binding (e.g., "spring.dynamic.thread-pool"). + * @param clazz the class type of the configuration object to bind properties to. + * @param the type of the configuration class. + * @return an instance of the configuration class with properties bound from the configInfo map. + */ + public static T bindProperties(Map configInfo, String prefix, Class clazz) { + try { + // Create an instance of the target class + T instance = clazz.getDeclaredConstructor().newInstance(); + BeanWrapper beanWrapper = new BeanWrapperImpl(instance); + + // Register custom editor for specific type conversions (if needed) + beanWrapper.registerCustomEditor(ConfigFileTypeEnum.class, new ConfigFileTypeEnumEditor()); + + // Iterate over all property keys that match the given prefix in the configInfo map + for (Map.Entry entry : configInfo.entrySet()) { + String key = entry.getKey().toString(); + if (key.startsWith(prefix)) { + String propertyName = key.substring(prefix.length() + 1); // Remove prefix from the property key + String[] tokens = propertyName.split("\\."); // Split the property name by dot for nested properties + setPropertyValue(tokens, beanWrapper, entry.getValue().toString()); // Set the property value recursively + } + } + return instance; + } catch (Exception e) { + throw new RuntimeException("Unable to bind properties to " + clazz.getName(), e); + } + } + /** * Recursively sets property values on the target object, handling nested properties and collections. * diff --git a/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/resources/bootstrap.properties b/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/resources/bootstrap.properties new file mode 100644 index 00000000..7a691206 --- /dev/null +++ b/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/resources/bootstrap.properties @@ -0,0 +1,53 @@ +server.port=8092 +server.servlet.context-path=/example + +app.id=dynamic-threadpool-example +apollo.meta=http://127.0.0.1:8080 +apollo.autoUpdateInjectedSpringProperties=true +apollo.bootstrap.enabled=true +apollo.bootstrap.namespaces=application +apollo.bootstrap.eagerLoad.enabled=true + +# The following parameters are used for testing +env=dev +apollo.configService=http://127.0.0.1:8080 +spring.profiles.active=dev +spring.application.name=hippo4j-config-apollo-spring-boot-starter-example +management.metrics.export.prometheus.enabled=true +management.server.port=29998 +management.endpoints.web.exposure.include=* + +spring.dynamic.thread-pool.enable=true +spring.dynamic.thread-pool.banner=true +spring.dynamic.thread-pool.check-state-interval=10 +spring.dynamic.thread-pool.monitor.enable=true +spring.dynamic.thread-pool.monitor.collect-types=micrometer +spring.dynamic.thread-pool.monitor.thread-pool-types=dynamic +spring.dynamic.thread-pool.monitor.initial-delay=3000 +spring.dynamic.thread-pool.monitor.collect-interval=3000 + +spring.dynamic.thread-pool.notify-platforms[0].platform=LARK +spring.dynamic.thread-pool.notify-platforms[0].token=6de41bdc-0799-45be-b128-7cddb9e777f0 +#spring.dynamic.thread-pool.notify-platforms[1].platform=WECHAT +#spring.dynamic.thread-pool.notify-platforms[1].token=ac0426a5-c712-474c-9bff-72b8b8f5caff +#spring.dynamic.thread-pool.notify-platforms[2].platform=DING +#spring.dynamic.thread-pool.notify-platforms[2].token=56417ebba6a27ca352f0de77a2ae9da66d01f39610b5ee8a6033c60ef9071c55 + +spring.dynamic.thread-pool.apollo.namespace=application +spring.dynamic.thread-pool.config-file-type=properties + +spring.dynamic.thread-pool.executors[0].thread-name-prefix = DynamicThreadPoolConfig#FIELD1 +spring.dynamic.thread-pool.executors[0].core-pool-size = 2 +spring.dynamic.thread-pool.executors[0].thread-pool-id = cn.hippo4j.example.agent.core.config.ThreadPoolConfiguration#AGENT_RUN_MESSAGE_SEND_TASK_EXECUTOR +spring.dynamic.thread-pool.executors[0].maximum-pool-size = 20 +spring.dynamic.thread-pool.executors[0].queue-capacity = 1024 +spring.dynamic.thread-pool.executors[0].blocking-queue = ResizableCapacityLinkedBlockingQueue +spring.dynamic.thread-pool.executors[0].execute-time-out = 800 +spring.dynamic.thread-pool.executors[0].rejected-handler = AbortPolicy +spring.dynamic.thread-pool.executors[0].keep-alive-time = 6691 +spring.dynamic.thread-pool.executors[0].allow-core-thread-time-out = true +spring.dynamic.thread-pool.executors[0].alarm = true +spring.dynamic.thread-pool.executors[0].active-alarm = 80 +spring.dynamic.thread-pool.executors[0].capacity-alarm = 80 +spring.dynamic.thread-pool.executors[0].notify.interval = 8 +spring.dynamic.thread-pool.executors[0].notify.receives = nobodyiam diff --git a/examples/threadpool-example/agent/config-apollo/src/main/resources/bootstrap.properties b/examples/threadpool-example/agent/config-apollo/src/main/resources/bootstrap.properties index d467930f..7a691206 100644 --- a/examples/threadpool-example/agent/config-apollo/src/main/resources/bootstrap.properties +++ b/examples/threadpool-example/agent/config-apollo/src/main/resources/bootstrap.properties @@ -1,11 +1,13 @@ server.port=8092 server.servlet.context-path=/example + app.id=dynamic-threadpool-example apollo.meta=http://127.0.0.1:8080 apollo.autoUpdateInjectedSpringProperties=true apollo.bootstrap.enabled=true apollo.bootstrap.namespaces=application apollo.bootstrap.eagerLoad.enabled=true + # The following parameters are used for testing env=dev apollo.configService=http://127.0.0.1:8080 @@ -14,26 +16,29 @@ spring.application.name=hippo4j-config-apollo-spring-boot-starter-example management.metrics.export.prometheus.enabled=true management.server.port=29998 management.endpoints.web.exposure.include=* + spring.dynamic.thread-pool.enable=true spring.dynamic.thread-pool.banner=true -spring.dynamic.thread-pool.check-state-interval=3 -#spring.dynamic.thread-pool.monitor.enable=true -#spring.dynamic.thread-pool.monitor.collect-types=micrometer -#spring.dynamic.thread-pool.monitor.thread-pool-types=dynamic,web -#spring.dynamic.thread-pool.monitor.initial-delay=10000 -#spring.dynamic.thread-pool.monitor.collect-interval=5000 -#spring.dynamic.thread-pool.notify-platforms[0].platform=WECHAT -#spring.dynamic.thread-pool.notify-platforms[0].token=ac0426a5-c712-474c-9bff-72b8b8f5caff -#spring.dynamic.thread-pool.notify-platforms[1].platform=DING -#spring.dynamic.thread-pool.notify-platforms[1].token=56417ebba6a27ca352f0de77a2ae9da66d01f39610b5ee8a6033c60ef9071c55 -#spring.dynamic.thread-pool.notify-platforms[2].platform=LARK -#spring.dynamic.thread-pool.notify-platforms[2].token=2cbf2808-3839-4c26-a04d-fd201dd51f9e +spring.dynamic.thread-pool.check-state-interval=10 +spring.dynamic.thread-pool.monitor.enable=true +spring.dynamic.thread-pool.monitor.collect-types=micrometer +spring.dynamic.thread-pool.monitor.thread-pool-types=dynamic +spring.dynamic.thread-pool.monitor.initial-delay=3000 +spring.dynamic.thread-pool.monitor.collect-interval=3000 + +spring.dynamic.thread-pool.notify-platforms[0].platform=LARK +spring.dynamic.thread-pool.notify-platforms[0].token=6de41bdc-0799-45be-b128-7cddb9e777f0 +#spring.dynamic.thread-pool.notify-platforms[1].platform=WECHAT +#spring.dynamic.thread-pool.notify-platforms[1].token=ac0426a5-c712-474c-9bff-72b8b8f5caff +#spring.dynamic.thread-pool.notify-platforms[2].platform=DING +#spring.dynamic.thread-pool.notify-platforms[2].token=56417ebba6a27ca352f0de77a2ae9da66d01f39610b5ee8a6033c60ef9071c55 + spring.dynamic.thread-pool.apollo.namespace=application spring.dynamic.thread-pool.config-file-type=properties spring.dynamic.thread-pool.executors[0].thread-name-prefix = DynamicThreadPoolConfig#FIELD1 spring.dynamic.thread-pool.executors[0].core-pool-size = 2 -spring.dynamic.thread-pool.executors[0].thread-pool-id = cn.hippo4j.example.agent.config.apollo.ThreadPoolConfiguration#RUN_MESSAGE_SEND_TASK_EXECUTOR +spring.dynamic.thread-pool.executors[0].thread-pool-id = cn.hippo4j.example.agent.core.config.ThreadPoolConfiguration#AGENT_RUN_MESSAGE_SEND_TASK_EXECUTOR spring.dynamic.thread-pool.executors[0].maximum-pool-size = 20 spring.dynamic.thread-pool.executors[0].queue-capacity = 1024 spring.dynamic.thread-pool.executors[0].blocking-queue = ResizableCapacityLinkedBlockingQueue diff --git a/examples/threadpool-example/agent/config-nacos-spring-boot-1x/pom.xml b/examples/threadpool-example/agent/config-nacos-spring-boot-1x/pom.xml new file mode 100644 index 00000000..011342c8 --- /dev/null +++ b/examples/threadpool-example/agent/config-nacos-spring-boot-1x/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + + cn.hippo4j + hippo4j-threadpool-agent-example + ${revision} + + + hippo4j-threadpool-agent-config-nacos-spring-boot-1x + + + true + 1.5.22.RELEASE + + + + + cn.hippo4j + hippo4j-agent-example-core + ${revision} + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + 1.5.1.RELEASE + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-context + 1.3.0.RELEASE + + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + + \ No newline at end of file diff --git a/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/nacos/v1/AgentConfigNacosSpringBoot1xExampleApplication.java b/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/nacos/v1/AgentConfigNacosSpringBoot1xExampleApplication.java new file mode 100644 index 00000000..eceee1b4 --- /dev/null +++ b/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/java/cn/hippo4j/example/agent/config/nacos/v1/AgentConfigNacosSpringBoot1xExampleApplication.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.example.agent.config.nacos.v1; + +import com.alibaba.nacos.api.exception.NacosException; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Agent config Nacos example application. + */ +@SpringBootApplication(scanBasePackages = "cn.hippo4j.example.agent.core") +public class AgentConfigNacosSpringBoot1xExampleApplication { + + public static void main(String[] args) throws NacosException { + SpringApplication.run(AgentConfigNacosSpringBoot1xExampleApplication.class, args); + } +} diff --git a/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/resources/bootstrap.properties b/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/resources/bootstrap.properties new file mode 100644 index 00000000..4291d582 --- /dev/null +++ b/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/resources/bootstrap.properties @@ -0,0 +1,60 @@ +server.port=8092 +server.servlet.context-path=/example +spring.profiles.active=dev +spring.application.name=hippo4j-config-nacos-spring-boot-starter-example + +# The following parameters are used for testing +spring.cloud.nacos.config.server-addr=127.0.0.1:8848 +spring.cloud.nacos.config.name=dynamic-threadpool-example-config +spring.cloud.nacos.config.file-extension=properties +spring.cloud.nacos.config.refresh.enabled=true + +spring.dynamic.thread-pool.enable=true +spring.dynamic.thread-pool.banner=true +spring.dynamic.thread-pool.check-state-interval=10 +spring.dynamic.thread-pool.monitor.enable=true +spring.dynamic.thread-pool.monitor.collect-types=micrometer +spring.dynamic.thread-pool.monitor.thread-pool-types=dynamic +spring.dynamic.thread-pool.monitor.agent-micrometer-port=29999 + +spring.dynamic.thread-pool.monitor.initial-delay=3000 +spring.dynamic.thread-pool.monitor.collect-interval=3000 +spring.dynamic.thread-pool.notify-platforms[0].platform=LARK +spring.dynamic.thread-pool.notify-platforms[0].token=6de41bdc-0799-45be-b128-7cddb9e777f0 +#spring.dynamic.thread-pool.notify-platforms[1].platform=WECHAT +#spring.dynamic.thread-pool.notify-platforms[1].token=ac0426a5-c712-474c-9bff-72b8b8f5caff +#spring.dynamic.thread-pool.notify-platforms[2].platform=DING +#spring.dynamic.thread-pool.notify-platforms[2].token=56417ebba6a27ca352f0de77a2ae9da66d01f39610b5ee8a6033c60ef9071c55 + +spring.dynamic.thread-pool.nacos.server-addr=127.0.0.1:8848 +spring.dynamic.thread-pool.nacos.data-id=dynamic-threadpool-example-config +spring.dynamic.thread-pool.nacos.group=DEFAULT_GROUP +spring.dynamic.thread-pool.nacos.namespace=public + +spring.dynamic.thread-pool.config-file-type=properties + +spring.dynamic.thread-pool.executors[0].thread-name-prefix = DynamicThreadPoolConfig#FIELD1 +spring.dynamic.thread-pool.executors[0].core-pool-size = 2 +spring.dynamic.thread-pool.executors[0].thread-pool-id = cn.hippo4j.example.agent.core.config.ThreadPoolConfiguration#AGENT_RUN_MESSAGE_SEND_TASK_EXECUTOR +spring.dynamic.thread-pool.executors[0].maximum-pool-size = 20 +spring.dynamic.thread-pool.executors[0].queue-capacity = 1024 +spring.dynamic.thread-pool.executors[0].blocking-queue = ResizableCapacityLinkedBlockingQueue +spring.dynamic.thread-pool.executors[0].execute-time-out = 800 +spring.dynamic.thread-pool.executors[0].rejected-handler = AbortPolicy +spring.dynamic.thread-pool.executors[0].keep-alive-time = 6691 +spring.dynamic.thread-pool.executors[0].allow-core-thread-time-out = true +spring.dynamic.thread-pool.executors[0].alarm = true +spring.dynamic.thread-pool.executors[0].active-alarm = 80 +spring.dynamic.thread-pool.executors[0].capacity-alarm = 80 +spring.dynamic.thread-pool.executors[0].notify.interval = 8 +spring.dynamic.thread-pool.executors[0].notify.receives = nobodyiam +spring.dynamic.thread-pool.executors[1].thread-pool-id = runMessageSendTaskExecutor +spring.dynamic.thread-pool.executors[1].thread-name-prefix = runMessageSendTaskExecutor +spring.dynamic.thread-pool.executors[1].core-pool-size = 3 +spring.dynamic.thread-pool.executors[1].maximum-pool-size = 4 +spring.dynamic.thread-pool.executors[1].queue-capacity = 1024 +spring.dynamic.thread-pool.executors[1].blocking-queue = ResizableCapacityLinkedBlockingQueue +spring.dynamic.thread-pool.executors[1].execute-time-out = 800 +spring.dynamic.thread-pool.executors[1].rejected-handler = AbortPolicy +spring.dynamic.thread-pool.executors[1].keep-alive-time = 6691 +spring.dynamic.thread-pool.executors[1].allow-core-thread-time-out = true diff --git a/examples/threadpool-example/agent/config-nacos/pom.xml b/examples/threadpool-example/agent/config-nacos/pom.xml index 8134d488..068ff153 100644 --- a/examples/threadpool-example/agent/config-nacos/pom.xml +++ b/examples/threadpool-example/agent/config-nacos/pom.xml @@ -29,10 +29,15 @@ org.springframework.boot spring-boot-starter-web - + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + 2.2.5.RELEASE org.springframework.cloud diff --git a/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java b/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java index e5010fbd..1b315207 100644 --- a/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java +++ b/examples/threadpool-example/agent/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/AgentConfigNacosExampleApplication.java @@ -17,8 +17,6 @@ package cn.hippo4j.example.agent.config.nacos; -import com.alibaba.nacos.api.exception.NacosException; -import com.alibaba.nacos.spring.context.annotation.config.EnableNacosConfig; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -26,10 +24,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; * Agent config Nacos example application. */ @SpringBootApplication(scanBasePackages = "cn.hippo4j.example.agent.core") -@EnableNacosConfig public class AgentConfigNacosExampleApplication { - public static void main(String[] args) throws NacosException { + public static void main(String[] args) { SpringApplication.run(AgentConfigNacosExampleApplication.class, args); } } diff --git a/examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties b/examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties index f145868b..c9b6820e 100644 --- a/examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties +++ b/examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties @@ -4,8 +4,12 @@ server.servlet.context-path=/example nacos.config.auto-refresh=true nacos.config.bootstrap.enable=true # The following parameters are used for testing -nacos.config.server-addr=127.0.0.1:8848 -nacos.config.data-id=dynamic-threadpool-example-config + +spring.cloud.nacos.config.server-addr=127.0.0.1:8848 +spring.cloud.nacos.config.extension-configs[0].data-id=dynamic-threadpool-example-config +spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP +spring.cloud.nacos.config.extension-configs[0].refresh=true + spring.profiles.active=dev spring.application.name=hippo4j-config-nacos-spring-boot-starter-example management.metrics.export.prometheus.enabled=true @@ -13,18 +17,21 @@ management.server.port=29998 management.endpoints.web.exposure.include=* spring.dynamic.thread-pool.enable=true spring.dynamic.thread-pool.banner=true -spring.dynamic.thread-pool.check-state-interval=3 +spring.dynamic.thread-pool.check-state-interval=10 #spring.dynamic.thread-pool.monitor.enable=true #spring.dynamic.thread-pool.monitor.collect-types=micrometer #spring.dynamic.thread-pool.monitor.thread-pool-types=dynamic,web -#spring.dynamic.thread-pool.monitor.initial-delay=10000 -#spring.dynamic.thread-pool.monitor.collect-interval=5000 -#spring.dynamic.thread-pool.notify-platforms[0].platform=WECHAT -#spring.dynamic.thread-pool.notify-platforms[0].token=ac0426a5-c712-474c-9bff-72b8b8f5caff -#spring.dynamic.thread-pool.notify-platforms[1].platform=DING -#spring.dynamic.thread-pool.notify-platforms[1].token=56417ebba6a27ca352f0de77a2ae9da66d01f39610b5ee8a6033c60ef9071c55 -#spring.dynamic.thread-pool.notify-platforms[2].platform=LARK -#spring.dynamic.thread-pool.notify-platforms[2].token=2cbf2808-3839-4c26-a04d-fd201dd51f9e +spring.dynamic.thread-pool.monitor.agent-micrometer-port=29999 + +spring.dynamic.thread-pool.monitor.initial-delay=3000 +spring.dynamic.thread-pool.monitor.collect-interval=3000 +spring.dynamic.thread-pool.notify-platforms[0].platform=LARK +spring.dynamic.thread-pool.notify-platforms[0].token=6de41bdc-0799-45be-b128-7cddb9e777f0 +#spring.dynamic.thread-pool.notify-platforms[1].platform=WECHAT +#spring.dynamic.thread-pool.notify-platforms[1].token=ac0426a5-c712-474c-9bff-72b8b8f5caff +#spring.dynamic.thread-pool.notify-platforms[2].platform=DING +#spring.dynamic.thread-pool.notify-platforms[2].token=56417ebba6a27ca352f0de77a2ae9da66d01f39610b5ee8a6033c60ef9071c55 + spring.dynamic.thread-pool.nacos.server-addr=127.0.0.1:8848 spring.dynamic.thread-pool.nacos.data-id=dynamic-threadpool-example-config spring.dynamic.thread-pool.nacos.group=DEFAULT_GROUP @@ -34,7 +41,7 @@ spring.dynamic.thread-pool.config-file-type=properties spring.dynamic.thread-pool.executors[0].thread-name-prefix = DynamicThreadPoolConfig#FIELD1 spring.dynamic.thread-pool.executors[0].core-pool-size = 2 -spring.dynamic.thread-pool.executors[0].thread-pool-id = cn.hippo4j.example.agent.config.nacos.ThreadPoolConfiguration#RUN_MESSAGE_SEND_TASK_EXECUTOR +spring.dynamic.thread-pool.executors[0].thread-pool-id = cn.hippo4j.example.agent.core.config.ThreadPoolConfiguration#AGENT_RUN_MESSAGE_SEND_TASK_EXECUTOR spring.dynamic.thread-pool.executors[0].maximum-pool-size = 20 spring.dynamic.thread-pool.executors[0].queue-capacity = 1024 spring.dynamic.thread-pool.executors[0].blocking-queue = ResizableCapacityLinkedBlockingQueue diff --git a/examples/threadpool-example/agent/pom.xml b/examples/threadpool-example/agent/pom.xml index d215c979..fcbe2ce2 100644 --- a/examples/threadpool-example/agent/pom.xml +++ b/examples/threadpool-example/agent/pom.xml @@ -20,5 +20,7 @@ config-apollo config-nacos agent-example-core + config-nacos-spring-boot-1x + config-apollo-spring-boot-1x \ No newline at end of file diff --git a/examples/threadpool-example/config/config-nacos/src/main/java/cn/hippo4j/example/config/nacos/ConfigNacosExampleApplication.java b/examples/threadpool-example/config/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/ConfigNacosExampleApplication.java similarity index 96% rename from examples/threadpool-example/config/config-nacos/src/main/java/cn/hippo4j/example/config/nacos/ConfigNacosExampleApplication.java rename to examples/threadpool-example/config/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/ConfigNacosExampleApplication.java index fda32d1e..db2f8ea9 100644 --- a/examples/threadpool-example/config/config-nacos/src/main/java/cn/hippo4j/example/config/nacos/ConfigNacosExampleApplication.java +++ b/examples/threadpool-example/config/config-nacos/src/main/java/cn/hippo4j/example/agent/config/nacos/ConfigNacosExampleApplication.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package cn.hippo4j.example.config.nacos; +package cn.hippo4j.example.agent.config.nacos; import cn.hippo4j.core.enable.EnableDynamicThreadPool; import org.springframework.boot.SpringApplication; diff --git a/kernel/alarm/src/main/java/cn/hippo4j/threadpool/alarm/handler/DefaultThreadPoolCheckAlarmHandler.java b/kernel/alarm/src/main/java/cn/hippo4j/threadpool/alarm/handler/DefaultThreadPoolCheckAlarmHandler.java index f4b7710f..0f6cba86 100644 --- a/kernel/alarm/src/main/java/cn/hippo4j/threadpool/alarm/handler/DefaultThreadPoolCheckAlarmHandler.java +++ b/kernel/alarm/src/main/java/cn/hippo4j/threadpool/alarm/handler/DefaultThreadPoolCheckAlarmHandler.java @@ -235,4 +235,17 @@ public class DefaultThreadPoolCheckAlarmHandler implements Runnable, ThreadPoolC .rejectCountNum(rejectCount) .build(); } + + /** + * Terminates the scheduled tasks and asynchronous alarm notifications by + * forcefully shutting down the respective thread pools. + * + * @see alarmNotifyExecutor + * @see asyncAlarmNotifyExecutor + */ + public void destroyScheduleExecute() { + alarmNotifyExecutor.shutdownNow(); + asyncAlarmNotifyExecutor.shutdownNow(); + } + } From 7b8a43175a8ffcb35af38adbc9680a8e12ff0f44 Mon Sep 17 00:00:00 2001 From: Pan-YuJie <646836760@qq.com> Date: Mon, 30 Sep 2024 18:36:09 +0800 Subject: [PATCH 14/14] fix: Fixed jar package mounting and startup problem in Agent mode --- .../core/conf/SnifferConfigInitializer.java | 4 +- .../NacosDynamicThreadPoolChangeHandler.java | 16 +- ...dapterConfigInstanceMethodInterceptor.java | 3 - .../NacosConfigConstructorInterceptor.java | 4 +- .../spring-boot-1x-plugin/pom.xml | 10 +- .../ApplicationContextInterceptor.java | 15 +- .../spring-boot-2x-plugin/pom.xml | 24 +-- ...ynamicThreadPoolChangeHandlerSpring2x.java | 78 ---------- ...ynamicThreadPoolChangeHandlerSpring2x.java | 139 ------------------ .../spring-plugin-common/pom.xml | 15 +- .../spring/common/conf/NacosCloudConfig.java | 51 +++++++ .../spring/common/conf/NacosConfig.java | 42 ++++++ .../monitor/MonitorHandlersConfigurator.java | 7 +- .../common/monitor/MonitorMetricEndpoint.java | 2 +- .../support/SpringPropertiesLoader.java | 3 + .../support/ThreadPoolCheckAlarmSupport.java | 4 +- .../support/ThreadPoolMonitorSupport.java | 8 +- .../threadpool-plugin/pom.xml | 1 - ...lExecutorConstructorMethodInterceptor.java | 11 +- agent/pom.xml | 1 - .../src/main/resources/bootstrap.properties | 18 ++- .../src/main/resources/bootstrap.properties | 19 ++- .../src/main/resources/bootstrap.properties | 7 +- .../src/main/resources/bootstrap.properties | 6 +- .../hippo4j/common/toolkit/http/HttpUtil.java | 21 ++- .../DefaultThreadPoolCheckAlarmHandler.java | 3 - .../mode/config/parser/JsonConfigParser.java | 3 +- .../core/platform/LarkSendMessageHandler.java | 6 +- .../core/service/SendMessageHandler.java | 7 +- .../adapter/ZipkinExecutorAdapterTest.java | 2 +- 30 files changed, 232 insertions(+), 298 deletions(-) delete mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/DynamicThreadPoolChangeHandlerSpring2x.java delete mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/NacosDynamicThreadPoolChangeHandlerSpring2x.java create mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/NacosCloudConfig.java create mode 100644 agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/NacosConfig.java 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 index ef811800..b8f8f4d0 100644 --- 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 @@ -18,6 +18,8 @@ package cn.hippo4j.agent.core.conf; import cn.hippo4j.agent.core.boot.AgentPackagePath; +import cn.hippo4j.agent.core.util.PropertyPlaceholderHelper; +import cn.hippo4j.agent.core.util.StringUtil; import cn.hippo4j.common.boot.AgentPackageNotFoundException; import cn.hippo4j.common.conf.Config; import cn.hippo4j.common.conf.ConfigNotFoundException; @@ -25,9 +27,7 @@ import cn.hippo4j.common.logging.api.ILog; import cn.hippo4j.common.logging.api.LogManager; import cn.hippo4j.common.logging.core.JsonLogResolver; import cn.hippo4j.common.logging.core.PatternLogResolver; -import cn.hippo4j.common.toolkit.StringUtil; import cn.hippo4j.common.toolkit.agent.ConfigInitializer; -import cn.hippo4j.common.toolkit.agent.PropertyPlaceholderHelper; import java.io.File; import java.io.FileInputStream; diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/NacosDynamicThreadPoolChangeHandler.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/NacosDynamicThreadPoolChangeHandler.java index 866f3ccc..8537f45c 100644 --- a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/NacosDynamicThreadPoolChangeHandler.java +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/NacosDynamicThreadPoolChangeHandler.java @@ -17,11 +17,14 @@ package cn.hippo4j.agent.plugin.nacos; +import cn.hippo4j.agent.plugin.spring.common.conf.NacosCloudConfig; +import cn.hippo4j.agent.plugin.spring.common.conf.NacosConfig; import cn.hippo4j.agent.plugin.spring.common.conf.SpringBootConfig; import cn.hippo4j.agent.plugin.spring.common.toolkit.SpringPropertyBinder; import cn.hippo4j.common.executor.ThreadFactoryBuilder; import cn.hippo4j.common.logging.api.ILog; import cn.hippo4j.common.logging.api.LogManager; +import cn.hippo4j.common.toolkit.StringUtil; import cn.hippo4j.threadpool.dynamic.mode.config.parser.ConfigParserHandler; import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; import cn.hippo4j.threadpool.dynamic.mode.config.refresher.AbstractConfigThreadPoolDynamicRefresh; @@ -34,6 +37,7 @@ import org.springframework.boot.context.properties.bind.Binder; import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.Properties; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -64,7 +68,13 @@ public class NacosDynamicThreadPoolChangeHandler extends AbstractConfigThreadPoo public void registerListener() { // Retrieve necessary configuration properties String configFileType = SpringBootConfig.Spring.Dynamic.Thread_Pool.CONFIG_FILE_TYPE; - String serverAddr = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.SERVER_ADDR; + String serverAddr = Optional.ofNullable(NacosCloudConfig.Spring.Cloud.Nacos.Config.SERVER_ADDR).filter(s -> !StringUtil.isEmpty(s)) + .orElse(Optional.ofNullable(NacosConfig.Nacos.Config.SERVER_ADDR).filter(s -> !StringUtil.isEmpty(s)) + .orElse("")); + if (StringUtil.isEmpty(serverAddr)) { + LOGGER.error("[Hippo4j-Agent] add Nacos listener failure. Nacos Registry address not configured"); + return; + } String dataId = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.DATA_ID; String group = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.GROUP; String namespace = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.NAMESPACE.get(0); @@ -105,9 +115,9 @@ public class NacosDynamicThreadPoolChangeHandler extends AbstractConfigThreadPoo }; // Add the listener to the Nacos ConfigService configService.addListener(dataId, group, configChangeListener); - LOGGER.info("[Hippo4j-Agent] Dynamic thread pool refresher, add Nacos listener successfully. namespace: {} data-id: {} group: {}", namespace, dataId, group); + LOGGER.info("[Hippo4j-Agent] Dynamic thread pool refresher, add Nacos listener successfully. serverAddr: {} namespace: {} data-id: {} group: {}", serverAddr, namespace, dataId, group); } catch (Exception e) { - LOGGER.error(e, "[Hippo4j-Agent] Dynamic thread pool refresher, add Nacos listener failure. namespace: {} data-id: {} group: {}", namespace, dataId, group); + LOGGER.error(e, "[Hippo4j-Agent] Dynamic thread pool refresher, add Nacos listener failure. serverAddr: {} namespace: {} data-id: {} group: {}", serverAddr, namespace, dataId, group); } } diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosCloudAdapterConfigInstanceMethodInterceptor.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosCloudAdapterConfigInstanceMethodInterceptor.java index 7a7cf841..9a227edf 100644 --- a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosCloudAdapterConfigInstanceMethodInterceptor.java +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosCloudAdapterConfigInstanceMethodInterceptor.java @@ -41,9 +41,6 @@ public class NacosCloudAdapterConfigInstanceMethodInterceptor implements Instanc } - /** - * - */ @Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { // This logic will only be executed once diff --git a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosConfigConstructorInterceptor.java b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosConfigConstructorInterceptor.java index 7a629319..9241710f 100644 --- a/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosConfigConstructorInterceptor.java +++ b/agent/hippo4j-agent-plugin/nacos-plugin/src/main/java/cn/hippo4j/agent/plugin/nacos/interceptor/NacosConfigConstructorInterceptor.java @@ -38,7 +38,7 @@ public class NacosConfigConstructorInterceptor implements InstanceConstructorInt // This logic will only be executed once if (isExecuted.compareAndSet(false, true)) { - // 判断 SpringPropertiesLoader 是否初始化 + // Determine whether SpringPropertiesLoader is initialized AtomicBoolean active = SpringPropertiesLoader.getActive(); // For Nacos-Cloud, the SpringPropertiesLoader environment initialization is triggered first, and then the logic to register listeners is triggered @@ -51,7 +51,7 @@ public class NacosConfigConstructorInterceptor implements InstanceConstructorInt // The Nacos plugin triggers before the Spring configuration plug-in. // This means that when the Apollo plug-in executes, Spring's Environment is not yet ready, // so the configuration cannot be read - // After listening to the AGENT_SPRING_PROPERTIES_LOADER_COMPLETED event, register the listener for Apollo + // After listening to the AGENT_SPRING_PROPERTIES_LOADER_COMPLETED event, register the listener for Nacos AbstractSubjectCenter.register(AbstractSubjectCenter.SubjectType.AGENT_SPRING_PROPERTIES_LOADER_COMPLETED, new NacosConfigPropertiesLoaderCompletedListener()); } } 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 index b3d2cbf9..1c0a7fc1 100644 --- 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 @@ -29,13 +29,6 @@ - - cn.hippo4j - hippo4j-agent-spring-plugin-common - provided - ${project.version} - - org.springframework.boot spring-boot-starter @@ -45,9 +38,8 @@ cn.hippo4j - hippo4j-threadpool-config-spring-boot-1x-starter + hippo4j-agent-spring-plugin-common ${project.version} - provided diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/interceptor/ApplicationContextInterceptor.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/interceptor/ApplicationContextInterceptor.java index 8297bfb9..b0b16665 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/interceptor/ApplicationContextInterceptor.java +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-1x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v1/interceptor/ApplicationContextInterceptor.java @@ -25,7 +25,9 @@ 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.core.config.ApplicationContextHolder; +import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.event.ContextRefreshedEvent; import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicBoolean; @@ -50,7 +52,18 @@ public class ApplicationContextInterceptor implements InstanceMethodsAroundInter ConfigurableApplicationContext context = (ConfigurableApplicationContext) objInst; if (context.getParent() != null) { // After the child container is started, the thread pool registration will be carried out - SpringThreadPoolRegisterSupport.registerThreadPoolInstances(context); + // IDEA's runtime environment or debugging mechanisms make context refresh speeds different. + // Ensure that thread pool registration logic is executed only after the context is fully started + if (context.isActive()) { + SpringThreadPoolRegisterSupport.registerThreadPoolInstances(context); + return ret; + } + // However, the packaged JAR runtime may refresh the context faster + // resulting in the context not being refreshed yet when registerThreadPoolInstances is called + // Register listener to handle the registration after the context has been fully refreshed + context.addApplicationListener((ApplicationListener) event -> { + SpringThreadPoolRegisterSupport.registerThreadPoolInstances(event.getApplicationContext()); + }); return ret; } // This logic will only be executed once 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 index a05c33f3..6fac2cc4 100644 --- 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 @@ -17,13 +17,6 @@ - - cn.hippo4j - hippo4j-agent-spring-plugin-common - provided - ${project.version} - - org.springframework.boot spring-boot-starter @@ -33,28 +26,15 @@ cn.hippo4j - hippo4j-threadpool-dynamic-mode-config + hippo4j-agent-spring-plugin-common ${project.version} - provided cn.hippo4j - hippo4j-threadpool-core + hippo4j-threadpool-dynamic-mode-config ${project.version} - provided - - com.ctrip.framework.apollo - apollo-client - provided - - - - com.alibaba.nacos - nacos-client - 2.2.1 - \ 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 deleted file mode 100644 index 6f70bf23..00000000 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/DynamicThreadPoolChangeHandlerSpring2x.java +++ /dev/null @@ -1,78 +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.v2; - -import cn.hippo4j.common.logging.api.ILog; -import cn.hippo4j.common.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/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/NacosDynamicThreadPoolChangeHandlerSpring2x.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/NacosDynamicThreadPoolChangeHandlerSpring2x.java deleted file mode 100644 index 83dd7f35..00000000 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/src/main/java/cn/hippo4j/agent/plugin/spring/boot/v2/NacosDynamicThreadPoolChangeHandlerSpring2x.java +++ /dev/null @@ -1,139 +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.v2; - -import cn.hippo4j.agent.plugin.spring.common.conf.SpringBootConfig; -import cn.hippo4j.common.executor.ThreadFactoryBuilder; -import cn.hippo4j.common.logging.api.ILog; -import cn.hippo4j.common.logging.api.LogManager; -import cn.hippo4j.threadpool.dynamic.mode.config.parser.ConfigParserHandler; -import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; -import cn.hippo4j.threadpool.dynamic.mode.config.refresher.AbstractConfigThreadPoolDynamicRefresh; -import com.alibaba.nacos.api.NacosFactory; -import com.alibaba.nacos.api.PropertyKeyConst; -import com.alibaba.nacos.api.config.ConfigService; -import com.alibaba.nacos.api.config.listener.Listener; -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.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.Executor; -import java.util.concurrent.ScheduledThreadPoolExecutor; - -import static cn.hippo4j.common.constant.Constants.DEFAULT_NAMESPACE_ID; - -/** - * NacosDynamicThreadPoolChangeHandlerSpring2x is responsible for handling dynamic thread pool - * configuration changes in a Spring environment by listening to configuration updates from Nacos. - *

- * This class extends {@link AbstractConfigThreadPoolDynamicRefresh} and implements the logic - * to register a Nacos listener, handle configuration changes, and dynamically refresh the thread pool - * properties based on the new configuration. - *

- * The handler is specifically tailored for use with Spring 2.x and integrates with Hippo4j's - * dynamic thread pool management system. - * - */ -public class NacosDynamicThreadPoolChangeHandlerSpring2x extends AbstractConfigThreadPoolDynamicRefresh { - - private static final ILog LOGGER = LogManager.getLogger(NacosDynamicThreadPoolChangeHandlerSpring2x.class); - - /** - * Registers a listener with Nacos to monitor for changes in the thread pool configuration. - *

- * This method sets up the Nacos {@link ConfigService} with the server address and namespace - * from the Spring Boot configuration. It then adds a listener that will receive and process - * configuration updates, triggering a dynamic refresh of thread pool settings. - */ - @Override - public void registerListener() { - // Retrieve necessary configuration properties - String configFileType = SpringBootConfig.Spring.Dynamic.Thread_Pool.CONFIG_FILE_TYPE; - String serverAddr = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.SERVER_ADDR; - String dataId = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.DATA_ID; - String namespace = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.NAMESPACE.get(0); - namespace = namespace.equals(DEFAULT_NAMESPACE_ID) ? "" : namespace; - String group = SpringBootConfig.Spring.Dynamic.Thread_Pool.Nacos.GROUP; - try { - // Initialize Nacos ConfigService with the provided properties - Properties properties = new Properties(); - properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr); - properties.put(PropertyKeyConst.NAMESPACE, namespace); - ConfigService configService = NacosFactory.createConfigService(properties); - - // Define the listener to handle configuration changes - Listener configChangeListener = new Listener() { - - @Override - public void receiveConfigInfo(String configInfo) { - LOGGER.debug("Received configuration: " + configInfo); - Map changeValueMap = new HashMap<>(); - try { - // Parse the configuration and map the values to the appropriate keys - Map configInfoMap = ConfigParserHandler.getInstance().parseConfig(configInfo, configFileType); - configInfoMap.forEach((key, value) -> { - if (key instanceof String) { - changeValueMap.put((String) key, value); - } - }); - } catch (IOException e) { - LOGGER.error(e, "[Hippo4j-Agent] Dynamic thread pool refresher, Failed to resolve configuration. configFileType: {} configInfo: {} ", configFileType, configInfo); - } - // Trigger the dynamic refresh with the parsed configuration - dynamicRefresh(configFileType, configInfo, changeValueMap); - } - - @Override - public Executor getExecutor() { - return new ScheduledThreadPoolExecutor( - 1, - ThreadFactoryBuilder.builder().daemon(true).prefix("client.dynamic.refresh.agent").build()); - } - }; - // Add the listener to the Nacos ConfigService - configService.addListener(dataId, group, configChangeListener); - LOGGER.info("[Hippo4j-Agent] Dynamic thread pool refresher, add Nacos listener successfully. namespace: {} data-id: {} group: {}", namespace, dataId, group); - } catch (Exception e) { - LOGGER.error(e, "[Hippo4j-Agent] Dynamic thread pool refresher, add Nacos listener failure. namespace: {} data-id: {} group: {}", namespace, dataId, group); - } - LOGGER.info("[Hippo4j-Agent] Dynamic thread pool refresher, added Nacos listener successfully. namespace: {} data-id: {} group: {}", namespace, dataId, group); - } - - /** - * Builds and binds the {@link BootstrapConfigProperties} from the given configuration map. - *

- * This method uses Spring's {@link Binder} to bind the configuration values to an instance - * of {@link BootstrapConfigProperties}, which can then be used to configure the thread pool - * dynamically. - * - * @param configInfo the configuration map containing properties to bind. - * @return the bound {@link BootstrapConfigProperties} instance. - */ - @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/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml index 853e0706..94992b95 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml @@ -35,25 +35,32 @@ cn.hippo4j hippo4j-threadpool-dynamic-api ${project.version} - provided cn.hippo4j hippo4j-threadpool-dynamic-core ${project.version} - provided cn.hippo4j hippo4j-threadpool-kernel-alarm ${project.version} - provided + + + cn.hippo4j + hippo4j-threadpool-core + ${project.version} + + + org.springframework.boot + spring-boot-starter + + cn.hippo4j hippo4j-threadpool-adapter-web ${project.version} - provided cn.hippo4j diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/NacosCloudConfig.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/NacosCloudConfig.java new file mode 100644 index 00000000..62019812 --- /dev/null +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/NacosCloudConfig.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.plugin.spring.common.conf; + +import cn.hippo4j.agent.core.boot.SpringBootConfigNode; + +/** + * Nacos Cloud config + */ +public class NacosCloudConfig { + + public static class Spring { + + /** + * Cloud + */ + public static class Cloud { + + /** + * Nacos + */ + public static class Nacos { + + /** + * Config + */ + @SpringBootConfigNode(root = NacosConfig.class) + public static class Config { + + public static String SERVER_ADDR = ""; + + } + } + } + } +} \ 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/NacosConfig.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/NacosConfig.java new file mode 100644 index 00000000..07227530 --- /dev/null +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/NacosConfig.java @@ -0,0 +1,42 @@ +/* + * 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; + +/** + * nacos config + */ +public class NacosConfig { + + /** + * Nacos + */ + public static class Nacos { + + /** + * Config + */ + @SpringBootConfigNode(root = NacosCloudConfig.class) + public static class Config { + + public static String SERVER_ADDR = ""; + + } + } +} \ 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/monitor/MonitorHandlersConfigurator.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorHandlersConfigurator.java index 56e1b7f0..30252ec6 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorHandlersConfigurator.java +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorHandlersConfigurator.java @@ -17,14 +17,13 @@ package cn.hippo4j.agent.plugin.spring.common.monitor; +import cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader; import cn.hippo4j.common.extension.spi.ServiceLoaderRegistry; import cn.hippo4j.common.logging.api.ILog; import cn.hippo4j.common.logging.api.LogManager; import cn.hippo4j.common.monitor.MonitorCollectTypeEnum; import cn.hippo4j.common.monitor.MonitorHandlerTypeEnum; import cn.hippo4j.core.executor.state.ThreadPoolRunStateHandler; -import cn.hippo4j.core.toolkit.inet.InetUtils; -import cn.hippo4j.core.toolkit.inet.InetUtilsProperties; import cn.hippo4j.monitor.elasticsearch.AdapterThreadPoolElasticSearchMonitorHandler; import cn.hippo4j.monitor.elasticsearch.DynamicThreadPoolElasticSearchMonitorHandler; import cn.hippo4j.monitor.elasticsearch.WebThreadPoolElasticSearchMonitorHandler; @@ -45,8 +44,6 @@ import java.util.List; import java.util.Map; import java.util.function.BiConsumer; -import static cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader.INET_UTILS_PROPERTIES; - /** * This class is responsible for configuring and initializing monitoring handlers * for various types of thread pools. It maps specific monitoring types (e.g., Micrometer, @@ -88,7 +85,7 @@ public class MonitorHandlersConfigurator { List collectTypes = Arrays.asList(monitor.getCollectTypes().split(",")); List threadPoolTypes = Arrays.asList(monitor.getThreadPoolTypes().split(",")); ThreadPoolRunStateHandler threadPoolRunStateHandler = new ThreadPoolRunStateHandler( - new InetUtils(INET_UTILS_PROPERTIES), environment); + SpringPropertiesLoader.inetUtils, environment); MonitorHandlerContext context = new MonitorHandlerContext(threadPoolMonitors, threadPoolRunStateHandler); diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorMetricEndpoint.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorMetricEndpoint.java index b8ae07bc..e5276104 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorMetricEndpoint.java +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorMetricEndpoint.java @@ -67,7 +67,7 @@ public class MonitorMetricEndpoint { Integer port = SpringBootConfig.Spring.Dynamic.Thread_Pool.Monitor.AGENT_MICROMETER_PORT; if (port == null) { LOGGER.error( - "[Hippo4j-Agent] Failed to start Prometheus metrics endpoint server. Please configure the exposed endpoint by adding: spring.dynamic.thread-pool.monitor.port=xxx to the configuration file"); + "[Hippo4j-Agent] Failed to start Prometheus metrics endpoint server. Please configure the exposed endpoint by adding: spring.dynamic.thread-pool.monitor.agent-micrometer-port=xxx to the configuration file"); return; } 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 index a73c800e..907b2074 100644 --- 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 @@ -22,6 +22,7 @@ import cn.hippo4j.agent.plugin.spring.common.toolkit.SpringPropertyBinder; import cn.hippo4j.common.extension.design.AbstractSubjectCenter; import cn.hippo4j.common.logging.api.ILog; import cn.hippo4j.common.logging.api.LogManager; +import cn.hippo4j.core.toolkit.inet.InetUtils; import cn.hippo4j.core.toolkit.inet.InetUtilsProperties; import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties; import lombok.Getter; @@ -57,6 +58,8 @@ public class SpringPropertiesLoader { public static InetUtilsProperties INET_UTILS_PROPERTIES = new InetUtilsProperties(); + public static InetUtils inetUtils; + public static void loadSpringProperties(ConfigurableEnvironment environment) { Iterator> iterator = environment.getPropertySources().iterator(); Properties properties = new Properties(); diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolCheckAlarmSupport.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolCheckAlarmSupport.java index 4cbd54e3..955f3bdb 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolCheckAlarmSupport.java +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolCheckAlarmSupport.java @@ -108,8 +108,8 @@ public class ThreadPoolCheckAlarmSupport { EnvironmentProperties.active = SpringBootConfig.Spring.Profiles.active; ConfigurableEnvironment environment = ApplicationContextHolder.getBean(ConfigurableEnvironment.class); InetUtilsProperties inetUtilsProperties = SpringPropertyBinder.bindProperties(environment, InetUtilsProperties.PREFIX, InetUtilsProperties.class); - InetUtils inetUtils = new InetUtils(inetUtilsProperties); - IdentifyUtil.generate(environment, inetUtils); + SpringPropertiesLoader.inetUtils = new InetUtils(inetUtilsProperties); + IdentifyUtil.generate(environment, SpringPropertiesLoader.inetUtils); } /** diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java index d44dbf3d..a45c4fa9 100644 --- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java +++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java @@ -98,7 +98,8 @@ public class ThreadPoolMonitorSupport { // Determine whether the task is successfully enabled // return directly if it has been enabled, and do not start the thread pool repeatedly - if (Boolean.TRUE.equals(active.get())) return; + if (Boolean.TRUE.equals(active.get())) + return; // Expose metric endpoints based on the configured collect types List collectTypes = Arrays.asList(monitor.getCollectTypes().split(",")); @@ -107,7 +108,8 @@ public class ThreadPoolMonitorSupport { } // Schedule periodic collection of metrics from the thread pools - collectScheduledExecutor.scheduleWithFixedDelay(scheduleRunnable(), monitor.getInitialDelay(), monitor.getCollectInterval(), TimeUnit.MILLISECONDS); + Runnable scheduledTask = scheduleRunnable(); + collectScheduledExecutor.scheduleWithFixedDelay(scheduledTask, monitor.getInitialDelay(), monitor.getCollectInterval(), TimeUnit.MILLISECONDS); active.set(true); if (ThreadPoolExecutorRegistry.getThreadPoolExecutorSize() > 0) { @@ -130,7 +132,7 @@ public class ThreadPoolMonitorSupport { for (ThreadPoolMonitor each : threadPoolMonitors) { try { each.collect(); - } catch (Exception ex) { + } catch (Throwable ex) { LOGGER.error("[Hippo4j-Agent] Error monitoring the running status of dynamic thread pool. Type: {}", each.getType(), ex); } } diff --git a/agent/hippo4j-agent-plugin/threadpool-plugin/pom.xml b/agent/hippo4j-agent-plugin/threadpool-plugin/pom.xml index b571302e..16517f4c 100644 --- a/agent/hippo4j-agent-plugin/threadpool-plugin/pom.xml +++ b/agent/hippo4j-agent-plugin/threadpool-plugin/pom.xml @@ -16,7 +16,6 @@ cn.hippo4j hippo4j-threadpool-core ${project.version} - provided cn.hippo4j 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 index fa4424a8..db1019d4 100644 --- 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 @@ -52,7 +52,16 @@ public class ThreadPoolExecutorConstructorMethodInterceptor implements InstanceC } StackTraceElement declaredClassStackTraceElement = stackTraceElements.get(0); String declaredClassName = declaredClassStackTraceElement.getClassName(); - Class declaredClass = Thread.currentThread().getContextClassLoader().loadClass(declaredClassName); + Class declaredClass = null; + try { + declaredClass = Thread.currentThread().getContextClassLoader().loadClass(declaredClassName); + } catch (ClassNotFoundException e) { + // The thread pool in the Agent plug-in is loaded by AgentclassLodaer. + // Due to the delegation model, it can only be searched upwards, so searching here will result in ClassNotFount. + // Because the parent of AgentClassLoader is AppclassLoder, it is ignored here ,skip the enhancement logic + LOGGER.debug("searching {} result in ClassNotFount , so skip the enhancement logic", declaredClassName); + return; + } ThreadPoolExecutorRegistry.REFERENCED_CLASS_MAP.put((ThreadPoolExecutor) objInst, declaredClass); } diff --git a/agent/pom.xml b/agent/pom.xml index ac0db601..18de9269 100644 --- a/agent/pom.xml +++ b/agent/pom.xml @@ -140,7 +140,6 @@ org.objenesis objenesis ${objenesis.version} - test com.github.tomakehurst diff --git a/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/resources/bootstrap.properties b/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/resources/bootstrap.properties index 7a691206..456739df 100644 --- a/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/resources/bootstrap.properties +++ b/examples/threadpool-example/agent/config-apollo-spring-boot-1x/src/main/resources/bootstrap.properties @@ -13,9 +13,6 @@ env=dev apollo.configService=http://127.0.0.1:8080 spring.profiles.active=dev spring.application.name=hippo4j-config-apollo-spring-boot-starter-example -management.metrics.export.prometheus.enabled=true -management.server.port=29998 -management.endpoints.web.exposure.include=* spring.dynamic.thread-pool.enable=true spring.dynamic.thread-pool.banner=true @@ -25,6 +22,7 @@ spring.dynamic.thread-pool.monitor.collect-types=micrometer spring.dynamic.thread-pool.monitor.thread-pool-types=dynamic spring.dynamic.thread-pool.monitor.initial-delay=3000 spring.dynamic.thread-pool.monitor.collect-interval=3000 +spring.dynamic.thread-pool.monitor.agent-micrometer-port=29999 spring.dynamic.thread-pool.notify-platforms[0].platform=LARK spring.dynamic.thread-pool.notify-platforms[0].token=6de41bdc-0799-45be-b128-7cddb9e777f0 @@ -51,3 +49,17 @@ spring.dynamic.thread-pool.executors[0].active-alarm = 80 spring.dynamic.thread-pool.executors[0].capacity-alarm = 80 spring.dynamic.thread-pool.executors[0].notify.interval = 8 spring.dynamic.thread-pool.executors[0].notify.receives = nobodyiam +spring.dynamic.thread-pool.executors[1].thread-pool-id = runMessageSendTaskExecutor +spring.dynamic.thread-pool.executors[1].thread-name-prefix = runMessageSendTaskExecutor +spring.dynamic.thread-pool.executors[1].core-pool-size = 3 +spring.dynamic.thread-pool.executors[1].maximum-pool-size = 4 +spring.dynamic.thread-pool.executors[1].queue-capacity = 1024 +spring.dynamic.thread-pool.executors[1].blocking-queue = ResizableCapacityLinkedBlockingQueue +spring.dynamic.thread-pool.executors[1].execute-time-out = 800 +spring.dynamic.thread-pool.executors[1].rejected-handler = AbortPolicy +spring.dynamic.thread-pool.executors[1].keep-alive-time = 6691 +spring.dynamic.thread-pool.executors[1].allow-core-thread-time-out = true +spring.dynamic.thread-pool.executors[1].active-alarm = 80 +spring.dynamic.thread-pool.executors[1].capacity-alarm = 80 +spring.dynamic.thread-pool.executors[1].notify.interval = 8 +spring.dynamic.thread-pool.executors[1].notify.receives = nobodyiam diff --git a/examples/threadpool-example/agent/config-apollo/src/main/resources/bootstrap.properties b/examples/threadpool-example/agent/config-apollo/src/main/resources/bootstrap.properties index 7a691206..5192a761 100644 --- a/examples/threadpool-example/agent/config-apollo/src/main/resources/bootstrap.properties +++ b/examples/threadpool-example/agent/config-apollo/src/main/resources/bootstrap.properties @@ -13,9 +13,6 @@ env=dev apollo.configService=http://127.0.0.1:8080 spring.profiles.active=dev spring.application.name=hippo4j-config-apollo-spring-boot-starter-example -management.metrics.export.prometheus.enabled=true -management.server.port=29998 -management.endpoints.web.exposure.include=* spring.dynamic.thread-pool.enable=true spring.dynamic.thread-pool.banner=true @@ -25,6 +22,7 @@ spring.dynamic.thread-pool.monitor.collect-types=micrometer spring.dynamic.thread-pool.monitor.thread-pool-types=dynamic spring.dynamic.thread-pool.monitor.initial-delay=3000 spring.dynamic.thread-pool.monitor.collect-interval=3000 +spring.dynamic.thread-pool.monitor.agent-micrometer-port=29999 spring.dynamic.thread-pool.notify-platforms[0].platform=LARK spring.dynamic.thread-pool.notify-platforms[0].token=6de41bdc-0799-45be-b128-7cddb9e777f0 @@ -51,3 +49,18 @@ spring.dynamic.thread-pool.executors[0].active-alarm = 80 spring.dynamic.thread-pool.executors[0].capacity-alarm = 80 spring.dynamic.thread-pool.executors[0].notify.interval = 8 spring.dynamic.thread-pool.executors[0].notify.receives = nobodyiam + +spring.dynamic.thread-pool.executors[1].thread-pool-id = runMessageSendTaskExecutor +spring.dynamic.thread-pool.executors[1].thread-name-prefix = runMessageSendTaskExecutor +spring.dynamic.thread-pool.executors[1].core-pool-size = 3 +spring.dynamic.thread-pool.executors[1].maximum-pool-size = 4 +spring.dynamic.thread-pool.executors[1].queue-capacity = 1024 +spring.dynamic.thread-pool.executors[1].blocking-queue = ResizableCapacityLinkedBlockingQueue +spring.dynamic.thread-pool.executors[1].execute-time-out = 800 +spring.dynamic.thread-pool.executors[1].rejected-handler = AbortPolicy +spring.dynamic.thread-pool.executors[1].keep-alive-time = 6691 +spring.dynamic.thread-pool.executors[1].allow-core-thread-time-out = true +spring.dynamic.thread-pool.executors[1].active-alarm = 80 +spring.dynamic.thread-pool.executors[1].capacity-alarm = 80 +spring.dynamic.thread-pool.executors[1].notify.interval = 8 +spring.dynamic.thread-pool.executors[1].notify.receives = nobodyiam diff --git a/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/resources/bootstrap.properties b/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/resources/bootstrap.properties index 4291d582..a16da5c4 100644 --- a/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/resources/bootstrap.properties +++ b/examples/threadpool-example/agent/config-nacos-spring-boot-1x/src/main/resources/bootstrap.properties @@ -11,7 +11,7 @@ spring.cloud.nacos.config.refresh.enabled=true spring.dynamic.thread-pool.enable=true spring.dynamic.thread-pool.banner=true -spring.dynamic.thread-pool.check-state-interval=10 +spring.dynamic.thread-pool.check-state-interval=3 spring.dynamic.thread-pool.monitor.enable=true spring.dynamic.thread-pool.monitor.collect-types=micrometer spring.dynamic.thread-pool.monitor.thread-pool-types=dynamic @@ -26,7 +26,6 @@ spring.dynamic.thread-pool.notify-platforms[0].token=6de41bdc-0799-45be-b128-7cd #spring.dynamic.thread-pool.notify-platforms[2].platform=DING #spring.dynamic.thread-pool.notify-platforms[2].token=56417ebba6a27ca352f0de77a2ae9da66d01f39610b5ee8a6033c60ef9071c55 -spring.dynamic.thread-pool.nacos.server-addr=127.0.0.1:8848 spring.dynamic.thread-pool.nacos.data-id=dynamic-threadpool-example-config spring.dynamic.thread-pool.nacos.group=DEFAULT_GROUP spring.dynamic.thread-pool.nacos.namespace=public @@ -58,3 +57,7 @@ spring.dynamic.thread-pool.executors[1].execute-time-out = 800 spring.dynamic.thread-pool.executors[1].rejected-handler = AbortPolicy spring.dynamic.thread-pool.executors[1].keep-alive-time = 6691 spring.dynamic.thread-pool.executors[1].allow-core-thread-time-out = true +spring.dynamic.thread-pool.executors[1].active-alarm = 80 +spring.dynamic.thread-pool.executors[1].capacity-alarm = 80 +spring.dynamic.thread-pool.executors[1].notify.interval = 8 +spring.dynamic.thread-pool.executors[1].notify.receives = nobodyiam diff --git a/examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties b/examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties index c9b6820e..24869387 100644 --- a/examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties +++ b/examples/threadpool-example/agent/config-nacos/src/main/resources/bootstrap.properties @@ -32,7 +32,6 @@ spring.dynamic.thread-pool.notify-platforms[0].token=6de41bdc-0799-45be-b128-7cd #spring.dynamic.thread-pool.notify-platforms[2].platform=DING #spring.dynamic.thread-pool.notify-platforms[2].token=56417ebba6a27ca352f0de77a2ae9da66d01f39610b5ee8a6033c60ef9071c55 -spring.dynamic.thread-pool.nacos.server-addr=127.0.0.1:8848 spring.dynamic.thread-pool.nacos.data-id=dynamic-threadpool-example-config spring.dynamic.thread-pool.nacos.group=DEFAULT_GROUP spring.dynamic.thread-pool.nacos.namespace=public @@ -54,6 +53,7 @@ spring.dynamic.thread-pool.executors[0].active-alarm = 80 spring.dynamic.thread-pool.executors[0].capacity-alarm = 80 spring.dynamic.thread-pool.executors[0].notify.interval = 8 spring.dynamic.thread-pool.executors[0].notify.receives = nobodyiam + spring.dynamic.thread-pool.executors[1].thread-pool-id = runMessageSendTaskExecutor spring.dynamic.thread-pool.executors[1].thread-name-prefix = runMessageSendTaskExecutor spring.dynamic.thread-pool.executors[1].core-pool-size = 3 @@ -64,3 +64,7 @@ spring.dynamic.thread-pool.executors[1].execute-time-out = 800 spring.dynamic.thread-pool.executors[1].rejected-handler = AbortPolicy spring.dynamic.thread-pool.executors[1].keep-alive-time = 6691 spring.dynamic.thread-pool.executors[1].allow-core-thread-time-out = true +spring.dynamic.thread-pool.executors[1].active-alarm = 80 +spring.dynamic.thread-pool.executors[1].capacity-alarm = 80 +spring.dynamic.thread-pool.executors[1].notify.interval = 8 +spring.dynamic.thread-pool.executors[1].notify.receives = nobodyiam diff --git a/infra/common/src/main/java/cn/hippo4j/common/toolkit/http/HttpUtil.java b/infra/common/src/main/java/cn/hippo4j/common/toolkit/http/HttpUtil.java index 2f8a475e..fdd14c9b 100644 --- a/infra/common/src/main/java/cn/hippo4j/common/toolkit/http/HttpUtil.java +++ b/infra/common/src/main/java/cn/hippo4j/common/toolkit/http/HttpUtil.java @@ -31,10 +31,13 @@ import lombok.NoArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import java.io.BufferedWriter; import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.Optional; @@ -198,6 +201,18 @@ public class HttpUtil { return executeJson(url, HttpMethod.POST, json, null); } + /** + * Send a post network request. + * + * @param url target url + * @param json json data + * @param headers headers + * @return + */ + public static String postJson(String url, String json, Map headers) { + return executeJson(url, HttpMethod.POST, json, headers); + } + /** * Send a put network request. * @@ -310,8 +325,10 @@ public class HttpUtil { byte[] b = bodyString.getBytes(); connection.setRequestProperty(CONTENT_LENGTH, String.valueOf(b.length)); OutputStream outputStream = connection.getOutputStream(); - outputStream.write(b, 0, b.length); - outputStream.flush(); + OutputStreamWriter osw = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8); + BufferedWriter writer = new BufferedWriter(osw); + writer.write(bodyString); + writer.flush(); IoUtil.closeQuietly(outputStream); } connection.connect(); diff --git a/kernel/alarm/src/main/java/cn/hippo4j/threadpool/alarm/handler/DefaultThreadPoolCheckAlarmHandler.java b/kernel/alarm/src/main/java/cn/hippo4j/threadpool/alarm/handler/DefaultThreadPoolCheckAlarmHandler.java index 0f6cba86..98947939 100644 --- a/kernel/alarm/src/main/java/cn/hippo4j/threadpool/alarm/handler/DefaultThreadPoolCheckAlarmHandler.java +++ b/kernel/alarm/src/main/java/cn/hippo4j/threadpool/alarm/handler/DefaultThreadPoolCheckAlarmHandler.java @@ -239,9 +239,6 @@ public class DefaultThreadPoolCheckAlarmHandler implements Runnable, ThreadPoolC /** * Terminates the scheduled tasks and asynchronous alarm notifications by * forcefully shutting down the respective thread pools. - * - * @see alarmNotifyExecutor - * @see asyncAlarmNotifyExecutor */ public void destroyScheduleExecute() { alarmNotifyExecutor.shutdownNow(); diff --git a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/parser/JsonConfigParser.java b/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/parser/JsonConfigParser.java index c0acf1d9..bb247068 100644 --- a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/parser/JsonConfigParser.java +++ b/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/parser/JsonConfigParser.java @@ -34,6 +34,7 @@ import java.util.List; * Json config parser. */ public class JsonConfigParser extends AbstractConfigParser { + private static final ObjectMapper MAPPER; private static final String DOT = "."; private static final String LEFT_BRACE = "{"; @@ -91,7 +92,7 @@ public class JsonConfigParser extends AbstractConfigParser { return new HashMap<>(1); } - return doParse(content,""); + return doParse(content, ""); } @Override diff --git a/kernel/message/core/src/main/java/cn/hippo4j/threadpool/message/core/platform/LarkSendMessageHandler.java b/kernel/message/core/src/main/java/cn/hippo4j/threadpool/message/core/platform/LarkSendMessageHandler.java index 4e43da9f..ff1e8a0b 100644 --- a/kernel/message/core/src/main/java/cn/hippo4j/threadpool/message/core/platform/LarkSendMessageHandler.java +++ b/kernel/message/core/src/main/java/cn/hippo4j/threadpool/message/core/platform/LarkSendMessageHandler.java @@ -43,6 +43,8 @@ import java.security.NoSuchAlgorithmException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; @@ -154,7 +156,9 @@ public class LarkSendMessageHandler implements SendMessageHandler { private void execute(String secretKey, String text) { String serverUrl = LarkAlarmConstants.LARK_BOT_URL + secretKey; try { - String responseBody = HttpUtil.postJson(serverUrl, text); + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json; charset=UTF-8"); + String responseBody = HttpUtil.postJson(serverUrl, text, headers); LarkRobotResponse response = JSONUtil.parseObject(responseBody, LarkRobotResponse.class); Assert.isTrue(response != null, "Response is null."); if (response.getCode() != 0) { diff --git a/kernel/message/core/src/main/java/cn/hippo4j/threadpool/message/core/service/SendMessageHandler.java b/kernel/message/core/src/main/java/cn/hippo4j/threadpool/message/core/service/SendMessageHandler.java index 5a8b0630..d5619b9d 100644 --- a/kernel/message/core/src/main/java/cn/hippo4j/threadpool/message/core/service/SendMessageHandler.java +++ b/kernel/message/core/src/main/java/cn/hippo4j/threadpool/message/core/service/SendMessageHandler.java @@ -17,12 +17,11 @@ package cn.hippo4j.threadpool.message.core.service; +import cn.hippo4j.threadpool.message.api.NotifyConfigDTO; import cn.hippo4j.threadpool.message.core.request.AlarmNotifyRequest; import cn.hippo4j.threadpool.message.core.request.ChangeParameterNotifyRequest; import cn.hippo4j.threadpool.message.core.request.WebChangeParameterNotifyRequest; -import cn.hippo4j.threadpool.message.api.NotifyConfigDTO; import lombok.SneakyThrows; -import org.springframework.core.io.ClassPathResource; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; @@ -70,9 +69,9 @@ public interface SendMessageHandler { default String readUtf8String(String path) { int endFlagCode = -1; String resultReadStr; - ClassPathResource classPathResource = new ClassPathResource(path); + ClassLoader classLoader = this.getClass().getClassLoader(); try ( - InputStream inputStream = classPathResource.getInputStream(); + InputStream inputStream = classLoader.getResourceAsStream(path); BufferedInputStream bis = new BufferedInputStream(inputStream); ByteArrayOutputStream buf = new ByteArrayOutputStream()) { int result = bis.read(); diff --git a/threadpool/core/src/test/java/cn/hippo4j/core/adapter/ZipkinExecutorAdapterTest.java b/threadpool/core/src/test/java/cn/hippo4j/core/adapter/ZipkinExecutorAdapterTest.java index eb27eec4..f34f3207 100644 --- a/threadpool/core/src/test/java/cn/hippo4j/core/adapter/ZipkinExecutorAdapterTest.java +++ b/threadpool/core/src/test/java/cn/hippo4j/core/adapter/ZipkinExecutorAdapterTest.java @@ -68,7 +68,7 @@ public class ZipkinExecutorAdapterTest { @Test public void testReplace() { Object executor = new CustomWrappingExecutorService(Executors.newCachedThreadPool()); - CustomWrappingExecutorService executorChange = (CustomWrappingExecutorService)executor; + CustomWrappingExecutorService executorChange = (CustomWrappingExecutorService) executor; ExecutorService beforeReplace = executorChange.delegate(); zipkinExecutorAdapter.replace(executor, dynamicThreadPool); ExecutorService afterReplace = executorChange.delegate();