feat: support config event (#1532)

pull/1533/head
shedfreewu 5 months ago committed by GitHub
parent c7aa67dc93
commit 355716e191
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -11,3 +11,4 @@
- [docs:update JDK version configuration in GitHub Actions.](https://github.com/Tencent/spring-cloud-tencent/pull/1510) - [docs:update JDK version configuration in GitHub Actions.](https://github.com/Tencent/spring-cloud-tencent/pull/1510)
- [fix:fix watch tsf config, fix bean refresh with RefreshScope and ConfigurationProperties.](https://github.com/Tencent/spring-cloud-tencent/pull/1511) - [fix:fix watch tsf config, fix bean refresh with RefreshScope and ConfigurationProperties.](https://github.com/Tencent/spring-cloud-tencent/pull/1511)
- [docs:simplify GitHub Actions.](https://github.com/Tencent/spring-cloud-tencent/pull/1514) - [docs:simplify GitHub Actions.](https://github.com/Tencent/spring-cloud-tencent/pull/1514)
- [feat: support config event.](https://github.com/Tencent/spring-cloud-tencent/pull/1532)

@ -29,6 +29,7 @@ import com.tencent.cloud.polaris.config.logger.PolarisConfigLoggerApplicationLis
import com.tencent.cloud.polaris.config.spring.annotation.SpringValueProcessor; import com.tencent.cloud.polaris.config.spring.annotation.SpringValueProcessor;
import com.tencent.cloud.polaris.config.spring.property.PlaceholderHelper; import com.tencent.cloud.polaris.config.spring.property.PlaceholderHelper;
import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry; import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry;
import com.tencent.cloud.polaris.context.PolarisSDKContextManager;
import com.tencent.polaris.configuration.api.core.ConfigFileService; import com.tencent.polaris.configuration.api.core.ConfigFileService;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
@ -77,9 +78,9 @@ public class PolarisConfigAutoConfiguration {
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT) @ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
public PolarisConfigPropertyRefresher polarisRefreshContextPropertySourceAutoRefresher( public PolarisConfigPropertyRefresher polarisRefreshContextPropertySourceAutoRefresher(
PolarisConfigProperties polarisConfigProperties, SpringValueRegistry springValueRegistry, PolarisConfigProperties polarisConfigProperties, SpringValueRegistry springValueRegistry,
ConfigFileService configFileService, ContextRefresher contextRefresher) { ConfigFileService configFileService, ContextRefresher contextRefresher, PolarisSDKContextManager polarisSDKContextManager) {
return new PolarisRefreshEntireContextRefresher(polarisConfigProperties, return new PolarisRefreshEntireContextRefresher(polarisConfigProperties,
springValueRegistry, configFileService, contextRefresher); springValueRegistry, configFileService, contextRefresher, polarisSDKContextManager.getSDKContext());
} }
@Bean @Bean
@ -105,9 +106,10 @@ public class PolarisConfigAutoConfiguration {
@Bean @Bean
public PolarisConfigPropertyRefresher polarisReflectPropertySourceAutoRefresher( public PolarisConfigPropertyRefresher polarisReflectPropertySourceAutoRefresher(
PolarisConfigProperties polarisConfigProperties, SpringValueRegistry springValueRegistry, PolarisConfigProperties polarisConfigProperties, SpringValueRegistry springValueRegistry,
PlaceholderHelper placeholderHelper, ConfigFileService configFileService, ContextRefresher contextRefresher) { PlaceholderHelper placeholderHelper, ConfigFileService configFileService,
ContextRefresher contextRefresher, PolarisSDKContextManager polarisSDKContextManager) {
return new PolarisRefreshAffectedContextRefresher(polarisConfigProperties, return new PolarisRefreshAffectedContextRefresher(polarisConfigProperties,
springValueRegistry, placeholderHelper, configFileService, contextRefresher); springValueRegistry, placeholderHelper, configFileService, contextRefresher, polarisSDKContextManager.getSDKContext());
} }
} }
} }

@ -17,6 +17,7 @@
package com.tencent.cloud.polaris.config.adapter; package com.tencent.cloud.polaris.config.adapter;
import java.time.LocalDateTime;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -29,10 +30,16 @@ import java.util.stream.Collectors;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.config.logger.PolarisConfigLoggerContext; import com.tencent.cloud.polaris.config.logger.PolarisConfigLoggerContext;
import com.tencent.cloud.polaris.config.utils.PolarisPropertySourceUtils; import com.tencent.cloud.polaris.config.utils.PolarisPropertySourceUtils;
import com.tencent.polaris.api.plugin.configuration.ConfigFile;
import com.tencent.polaris.api.plugin.event.ConfigEvent;
import com.tencent.polaris.api.plugin.event.EventConstants;
import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.client.flow.BaseFlow;
import com.tencent.polaris.configuration.api.core.ConfigFileGroup; import com.tencent.polaris.configuration.api.core.ConfigFileGroup;
import com.tencent.polaris.configuration.api.core.ConfigFileMetadata; import com.tencent.polaris.configuration.api.core.ConfigFileMetadata;
import com.tencent.polaris.configuration.api.core.ConfigFileService; import com.tencent.polaris.configuration.api.core.ConfigFileService;
import com.tencent.polaris.configuration.api.core.ConfigKVFile; import com.tencent.polaris.configuration.api.core.ConfigKVFile;
import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeEvent;
import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeListener; import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeListener;
import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo; import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo;
import com.tencent.polaris.configuration.client.internal.CompositeConfigFile; import com.tencent.polaris.configuration.client.internal.CompositeConfigFile;
@ -61,11 +68,13 @@ public abstract class PolarisConfigPropertyAutoRefresher implements ApplicationL
// this class provides customized logic for some customers to configure special business group files // this class provides customized logic for some customers to configure special business group files
private final PolarisConfigCustomExtensionLayer polarisConfigCustomExtensionLayer = PolarisServiceLoaderUtil.getPolarisConfigCustomExtensionLayer(); private final PolarisConfigCustomExtensionLayer polarisConfigCustomExtensionLayer = PolarisServiceLoaderUtil.getPolarisConfigCustomExtensionLayer();
private final ConfigFileService configFileService; private final ConfigFileService configFileService;
private final SDKContext context;
public PolarisConfigPropertyAutoRefresher(PolarisConfigProperties polarisConfigProperties, public PolarisConfigPropertyAutoRefresher(PolarisConfigProperties polarisConfigProperties,
ConfigFileService configFileService) { ConfigFileService configFileService, SDKContext context) {
this.polarisConfigProperties = polarisConfigProperties; this.polarisConfigProperties = polarisConfigProperties;
this.configFileService = configFileService; this.configFileService = configFileService;
this.context = context;
} }
@Override @Override
@ -157,6 +166,8 @@ public abstract class PolarisConfigPropertyAutoRefresher implements ApplicationL
} }
} }
refreshConfigurationProperties(changedKeys); refreshConfigurationProperties(changedKeys);
reportEvent(added);
} }
catch (Exception e) { catch (Exception e) {
LOGGER.error("[SCT Config] receive onChange exception,", e); LOGGER.error("[SCT Config] receive onChange exception,", e);
@ -235,6 +246,9 @@ public abstract class PolarisConfigPropertyAutoRefresher implements ApplicationL
} }
// update @ConfigurationProperties beans // update @ConfigurationProperties beans
refreshConfigurationProperties(configKVFileChangeEvent.changedKeys()); refreshConfigurationProperties(configKVFileChangeEvent.changedKeys());
reportEvent(listenPolarisPropertySource, configKVFileChangeEvent);
}); });
} }
@ -278,6 +292,38 @@ public abstract class PolarisConfigPropertyAutoRefresher implements ApplicationL
return added; return added;
} }
private void reportEvent(PolarisPropertySource polarisPropertySource, ConfigKVFileChangeEvent configKVFileChangeEvent) {
ConfigEvent.Builder builder = new ConfigEvent.Builder()
.withTimestamp(LocalDateTime.now())
.withEventType(EventConstants.EventType.CONFIG)
.withEventName(EventConstants.EventName.ConfigUpdated)
.withClientId(context.getExtensions().getValueContext().getClientId())
.withClientIp(context.getExtensions().getValueContext().getHost())
.withNamespace(polarisPropertySource.getNamespace())
.withConfigGroup(polarisPropertySource.getGroup())
.withConfigVersion(Optional.ofNullable(configKVFileChangeEvent.getConfigFile()).map(ConfigFile::getName).orElse(null))
.withConfigFileName(polarisPropertySource.getFileName());
BaseFlow.reportConfigEvent(context.getExtensions(), builder.build());
}
private void reportEvent(Map<String, ConfigFileMetadata> added) {
for (ConfigFileMetadata configFileMetadata : added.values()) {
ConfigEvent.Builder builder = new ConfigEvent.Builder()
.withTimestamp(LocalDateTime.now())
.withEventType(EventConstants.EventType.CONFIG)
.withEventName(EventConstants.EventName.ConfigUpdated)
.withClientId(context.getExtensions().getValueContext().getClientId())
.withClientIp(context.getExtensions().getValueContext().getHost())
.withNamespace(configFileMetadata.getNamespace())
.withConfigGroup(configFileMetadata.getFileGroup())
.withConfigVersion(configFileMetadata.getFileVersion())
.withConfigFileName(configFileMetadata.getFileName());
BaseFlow.reportConfigEvent(context.getExtensions(), builder.build());
}
}
/** /**
* Just for junit test. * Just for junit test.
*/ */

@ -24,6 +24,7 @@ import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.config.spring.property.PlaceholderHelper; import com.tencent.cloud.polaris.config.spring.property.PlaceholderHelper;
import com.tencent.cloud.polaris.config.spring.property.SpringValue; import com.tencent.cloud.polaris.config.spring.property.SpringValue;
import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry; import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry;
import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.configuration.api.core.ConfigFileService; import com.tencent.polaris.configuration.api.core.ConfigFileService;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -63,8 +64,8 @@ public class PolarisRefreshAffectedContextRefresher extends PolarisConfigPropert
public PolarisRefreshAffectedContextRefresher(PolarisConfigProperties polarisConfigProperties, public PolarisRefreshAffectedContextRefresher(PolarisConfigProperties polarisConfigProperties,
SpringValueRegistry springValueRegistry, PlaceholderHelper placeholderHelper, SpringValueRegistry springValueRegistry, PlaceholderHelper placeholderHelper,
ConfigFileService configFileService, ContextRefresher contextRefresher) { ConfigFileService configFileService, ContextRefresher contextRefresher, SDKContext context) {
super(polarisConfigProperties, configFileService); super(polarisConfigProperties, configFileService, context);
this.springValueRegistry = springValueRegistry; this.springValueRegistry = springValueRegistry;
this.placeholderHelper = placeholderHelper; this.placeholderHelper = placeholderHelper;
this.contextRefresher = contextRefresher; this.contextRefresher = contextRefresher;

@ -22,6 +22,7 @@ import java.util.Set;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry; import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry;
import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.configuration.api.core.ConfigFileService; import com.tencent.polaris.configuration.api.core.ConfigFileService;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
@ -46,9 +47,10 @@ public class PolarisRefreshEntireContextRefresher extends PolarisConfigPropertyA
private ConfigurableApplicationContext context; private ConfigurableApplicationContext context;
public PolarisRefreshEntireContextRefresher(PolarisConfigProperties polarisConfigProperties, public PolarisRefreshEntireContextRefresher(PolarisConfigProperties polarisConfigProperties,
SpringValueRegistry springValueRegistry, ConfigFileService configFileService, ContextRefresher contextRefresher) { SpringValueRegistry springValueRegistry, ConfigFileService configFileService,
ContextRefresher contextRefresher, SDKContext context) {
super(polarisConfigProperties, configFileService); super(polarisConfigProperties, configFileService, context);
this.springValueRegistry = springValueRegistry; this.springValueRegistry = springValueRegistry;
this.contextRefresher = contextRefresher; this.contextRefresher = contextRefresher;
} }

@ -192,4 +192,9 @@ public class MockedConfigKVFile implements ConfigKVFile {
public String getFileName() { public String getFileName() {
return fileName; return fileName;
} }
@Override
public String getFileVersion() {
return "";
}
} }

@ -31,6 +31,9 @@ import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.config.spring.property.PlaceholderHelper; import com.tencent.cloud.polaris.config.spring.property.PlaceholderHelper;
import com.tencent.cloud.polaris.config.spring.property.SpringValue; import com.tencent.cloud.polaris.config.spring.property.SpringValue;
import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry; import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry;
import com.tencent.polaris.api.plugin.common.ValueContext;
import com.tencent.polaris.api.plugin.compose.Extensions;
import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.configuration.api.core.ChangeType; import com.tencent.polaris.configuration.api.core.ChangeType;
import com.tencent.polaris.configuration.api.core.ConfigFileService; import com.tencent.polaris.configuration.api.core.ConfigFileService;
import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeEvent; import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeEvent;
@ -80,6 +83,15 @@ public class PolarisPropertiesSourceAutoRefresherTest {
@Mock @Mock
private ContextRefresher contextRefresher; private ContextRefresher contextRefresher;
@Mock
private SDKContext sdkContext;
@Mock
private Extensions extensions;
@Mock
private ValueContext valueContext;
@BeforeEach @BeforeEach
public void setUp() { public void setUp() {
PolarisPropertySourceManager.clearPropertySources(); PolarisPropertySourceManager.clearPropertySources();
@ -88,7 +100,7 @@ public class PolarisPropertiesSourceAutoRefresherTest {
@Test @Test
public void testConfigFileChanged() throws Exception { public void testConfigFileChanged() throws Exception {
PolarisRefreshAffectedContextRefresher refresher = new PolarisRefreshAffectedContextRefresher( PolarisRefreshAffectedContextRefresher refresher = new PolarisRefreshAffectedContextRefresher(
polarisConfigProperties, springValueRegistry, placeholderHelper, configFileService, contextRefresher); polarisConfigProperties, springValueRegistry, placeholderHelper, configFileService, contextRefresher, sdkContext);
ConfigurableApplicationContext applicationContext = mock(ConfigurableApplicationContext.class); ConfigurableApplicationContext applicationContext = mock(ConfigurableApplicationContext.class);
ConfigurableListableBeanFactory beanFactory = mock(ConfigurableListableBeanFactory.class); ConfigurableListableBeanFactory beanFactory = mock(ConfigurableListableBeanFactory.class);
TypeConverter typeConverter = mock(TypeConverter.class); TypeConverter typeConverter = mock(TypeConverter.class);
@ -107,6 +119,10 @@ public class PolarisPropertiesSourceAutoRefresherTest {
when(springValueRegistry.get(any(), any())).thenReturn(springValues); when(springValueRegistry.get(any(), any())).thenReturn(springValues);
when(polarisConfigProperties.isAutoRefresh()).thenReturn(true); when(polarisConfigProperties.isAutoRefresh()).thenReturn(true);
when(sdkContext.getExtensions()).thenReturn(extensions);
when(extensions.getValueContext()).thenReturn(valueContext);
when(valueContext.getClientId()).thenReturn("mockClientId");
when(valueContext.getHost()).thenReturn("mockHost");
Map<String, Object> content = new HashMap<>(); Map<String, Object> content = new HashMap<>();
content.put("k1", "v1"); content.put("k1", "v1");
@ -129,7 +145,7 @@ public class PolarisPropertiesSourceAutoRefresherTest {
changeInfos.put("k4", changeInfo2); changeInfos.put("k4", changeInfo2);
changeInfos.put("logging.level.root", changeInfoLogger); changeInfos.put("logging.level.root", changeInfoLogger);
ConfigKVFileChangeEvent event = new ConfigKVFileChangeEvent(changeInfos); ConfigKVFileChangeEvent event = new ConfigKVFileChangeEvent(changeInfos, null);
refresher.onApplicationEvent(null); refresher.onApplicationEvent(null);
file.fireChangeListener(event); file.fireChangeListener(event);
@ -143,7 +159,7 @@ public class PolarisPropertiesSourceAutoRefresherTest {
@Test @Test
public void testConfigFileGroupChanged() throws Exception { public void testConfigFileGroupChanged() throws Exception {
PolarisRefreshAffectedContextRefresher refresher = new PolarisRefreshAffectedContextRefresher( PolarisRefreshAffectedContextRefresher refresher = new PolarisRefreshAffectedContextRefresher(
polarisConfigProperties, springValueRegistry, placeholderHelper, configFileService, contextRefresher); polarisConfigProperties, springValueRegistry, placeholderHelper, configFileService, contextRefresher, sdkContext);
ConfigurableApplicationContext applicationContext = mock(ConfigurableApplicationContext.class); ConfigurableApplicationContext applicationContext = mock(ConfigurableApplicationContext.class);
ConfigurableListableBeanFactory beanFactory = mock(ConfigurableListableBeanFactory.class); ConfigurableListableBeanFactory beanFactory = mock(ConfigurableListableBeanFactory.class);
TypeConverter typeConverter = mock(TypeConverter.class); TypeConverter typeConverter = mock(TypeConverter.class);
@ -189,6 +205,8 @@ public class PolarisPropertiesSourceAutoRefresherTest {
when(configFileService.getConfigPropertiesFile(testNamespace, testFileGroup, "file2.properties")) when(configFileService.getConfigPropertiesFile(testNamespace, testFileGroup, "file2.properties"))
.thenReturn(file2); .thenReturn(file2);
when(sdkContext.getExtensions()).thenReturn(extensions);
when(extensions.getValueContext()).thenReturn(valueContext);
revisableConfigFileGroup.updateConfigFileList(Arrays.asList(file, file2), "v2"); revisableConfigFileGroup.updateConfigFileList(Arrays.asList(file, file2), "v2");
Thread.sleep(5000); Thread.sleep(5000);
@ -206,7 +224,7 @@ public class PolarisPropertiesSourceAutoRefresherTest {
changeInfos.put("k1", changeInfo); changeInfos.put("k1", changeInfo);
changeInfos.put("k3.1", changeInfo2); changeInfos.put("k3.1", changeInfo2);
changeInfos.put("k4", changeInfo3); changeInfos.put("k4", changeInfo3);
ConfigKVFileChangeEvent event = new ConfigKVFileChangeEvent(changeInfos); ConfigKVFileChangeEvent event = new ConfigKVFileChangeEvent(changeInfos, null);
file2.fireChangeListener(event); file2.fireChangeListener(event);
Thread.sleep(5000); Thread.sleep(5000);

@ -25,6 +25,7 @@ import java.util.Set;
import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; import com.tencent.cloud.polaris.config.config.PolarisConfigProperties;
import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry; import com.tencent.cloud.polaris.config.spring.property.SpringValueRegistry;
import com.tencent.polaris.client.api.SDKContext;
import com.tencent.polaris.configuration.api.core.ConfigFileService; import com.tencent.polaris.configuration.api.core.ConfigFileService;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -70,6 +71,9 @@ public class PolarisRefreshEntireContextRefresherTest {
private PolarisRefreshEntireContextRefresher refresher; private PolarisRefreshEntireContextRefresher refresher;
@Mock
private SDKContext context;
@BeforeEach @BeforeEach
void setUp() { void setUp() {
MockitoAnnotations.openMocks(this); MockitoAnnotations.openMocks(this);
@ -77,7 +81,8 @@ public class PolarisRefreshEntireContextRefresherTest {
polarisConfigProperties, polarisConfigProperties,
springValueRegistry, springValueRegistry,
configFileService, configFileService,
contextRefresher contextRefresher,
context
); );
refresher.setApplicationContext(applicationContext); refresher.setApplicationContext(applicationContext);
} }

@ -21,9 +21,8 @@ import java.time.LocalDateTime;
import com.tencent.cloud.polaris.PolarisDiscoveryProperties; import com.tencent.cloud.polaris.PolarisDiscoveryProperties;
import com.tencent.polaris.api.plugin.compose.Extensions; import com.tencent.polaris.api.plugin.compose.Extensions;
import com.tencent.polaris.api.plugin.event.EventConstants;
import com.tencent.polaris.api.plugin.event.FlowEvent; import com.tencent.polaris.api.plugin.event.FlowEvent;
import com.tencent.polaris.api.plugin.event.FlowEventConstants;
import com.tencent.polaris.api.pojo.ServiceEventKey;
import com.tencent.polaris.assembly.api.AssemblyAPI; import com.tencent.polaris.assembly.api.AssemblyAPI;
import com.tencent.polaris.client.flow.BaseFlow; import com.tencent.polaris.client.flow.BaseFlow;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -96,8 +95,8 @@ public class PolarisAutoServiceRegistration extends AbstractAutoServiceRegistrat
&& this.registration.getPolarisContext().getExtensions() != null) { && this.registration.getPolarisContext().getExtensions() != null) {
Extensions extensions = this.registration.getPolarisContext().getExtensions(); Extensions extensions = this.registration.getPolarisContext().getExtensions();
FlowEvent.Builder flowEventBuilder = new FlowEvent.Builder() FlowEvent.Builder flowEventBuilder = new FlowEvent.Builder()
.withEventType(ServiceEventKey.EventType.INSTANCE) .withEventType(EventConstants.EventType.INSTANCE)
.withEventName(FlowEventConstants.EventName.InstanceThreadEnd) .withEventName(EventConstants.EventName.InstanceThreadEnd)
.withTimestamp(LocalDateTime.now()) .withTimestamp(LocalDateTime.now())
.withClientId(extensions.getValueContext().getClientId()) .withClientId(extensions.getValueContext().getClientId())
.withClientIp(extensions.getValueContext().getHost()) .withClientIp(extensions.getValueContext().getHost())

@ -74,7 +74,7 @@
<revision>2.0.1.0-2021.0.9-RC2</revision> <revision>2.0.1.0-2021.0.9-RC2</revision>
<!-- Polaris SDK version --> <!-- Polaris SDK version -->
<polaris.version>2.0.1.0-RC1</polaris.version> <polaris.version>2.0.1.0-SNAPSHOT</polaris.version>
<!-- Dependencies --> <!-- Dependencies -->
<bcpkix-jdk18on.version>1.78.1</bcpkix-jdk18on.version> <bcpkix-jdk18on.version>1.78.1</bcpkix-jdk18on.version>

Loading…
Cancel
Save