diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index f95530a..cc327f3 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -20,8 +20,8 @@ jobs: strategy: matrix: java: [ '17' , '21' ] - maven-profile-spring-boot: [ 'spring-boot-2.4' , 'spring-boot-2.5' , 'spring-boot-2.6' , 'spring-boot-2.7' , - 'spring-boot-3.0' , 'spring-boot-3.1' , 'spring-boot-3.2' , 'spring-boot-3.3' ] + maven-profile-spring-boot: [ 'spring-boot-3.0' , 'spring-boot-3.1' , 'spring-boot-3.2' , + 'spring-boot-3.3' , 'spring-boot-3.4' ] steps: - name: Checkout Source uses: actions/checkout@v4 @@ -39,6 +39,11 @@ jobs: --update-snapshots --file pom.xml -Drevision=0.0.1-SNAPSHOT - -DargLine="--add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED" - -P${{ matrix.maven-profile-spring-boot }} - test \ No newline at end of file + test + --activate-profiles test,coverage,${{ matrix.maven-profile-spring-boot }} + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + slug: microsphere-projects/microsphere-spring-boot \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9203168..e551b45 100644 --- a/.gitignore +++ b/.gitignore @@ -65,5 +65,8 @@ compiler/.gradle/* .extract .java-version +# vscode +.vscode/ + # others build.txt \ No newline at end of file diff --git a/README.md b/README.md index 6b23389..c7b0801 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ # microsphere-spring-boot -Microsphere Projects for Spring Boot +> Microsphere Projects for Spring Boot + +[![Maven Build](https://github.com/microsphere-projects/microsphere-spring-boot/actions/workflows/maven-build.yml/badge.svg)](https://github.com/microsphere-projects/microsphere-spring-boot/actions/workflows/maven-build.yml) +[![Codecov](https://codecov.io/gh/microsphere-projects/microsphere-spring-boot/branch/dev/graph/badge.svg)](https://app.codecov.io/gh/microsphere-projects/microsphere-spring-boot) +![Maven](https://img.shields.io/maven-central/v/io.github.microsphere-projects/microsphere-spring-boot.svg) +![License](https://img.shields.io/github/license/microsphere-projects/microsphere-spring-boot.svg) +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/microsphere-projects/microsphere-spring-boot.svg)](http://isitmaintained.com/project/microsphere-projects/microsphere-spring-boot "Average time to resolve an issue") +[![Percentage of issues still open](http://isitmaintained.com/badge/open/microsphere-projects/microsphere-spring-boot.svg)](http://isitmaintained.com/project/microsphere-projects/microsphere-spring-boot "Percentage of issues still open") diff --git a/microsphere-core-spring-boot-starter/pom.xml b/microsphere-core-spring-boot-starter/pom.xml index cb0af68..1acaa52 100644 --- a/microsphere-core-spring-boot-starter/pom.xml +++ b/microsphere-core-spring-boot-starter/pom.xml @@ -43,6 +43,12 @@ + + org.junit.jupiter + junit-jupiter + test + + org.springframework.boot spring-boot-starter-test diff --git a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/autoconfigure/ConfigurableAutoConfigurationImportFilter.java b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/autoconfigure/ConfigurableAutoConfigurationImportFilter.java index fecf5d5..d9a9708 100644 --- a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/autoconfigure/ConfigurableAutoConfigurationImportFilter.java +++ b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/autoconfigure/ConfigurableAutoConfigurationImportFilter.java @@ -8,8 +8,6 @@ import org.springframework.core.env.Environment; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; import java.util.LinkedHashSet; import java.util.Set; @@ -19,6 +17,7 @@ import static java.util.Arrays.asList; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableSet; +import static org.springframework.util.Assert.isInstanceOf; import static org.springframework.util.StringUtils.collectionToCommaDelimitedString; import static org.springframework.util.StringUtils.commaDelimitedListToSet; import static org.springframework.util.StringUtils.hasText; @@ -103,7 +102,7 @@ private static MutablePropertySources getPropertySources(Environment environment } private static ConfigurableEnvironment getConfigurableEnvironment(Environment environment) { - Assert.isInstanceOf(ConfigurableEnvironment.class, environment); + isInstanceOf(ConfigurableEnvironment.class, environment); return (ConfigurableEnvironment) environment; } diff --git a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/classloading/BannedArtifactClassLoadingListener.java b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/classloading/BannedArtifactClassLoadingListener.java index 59ac68d..78ab99e 100644 --- a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/classloading/BannedArtifactClassLoadingListener.java +++ b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/classloading/BannedArtifactClassLoadingListener.java @@ -1,9 +1,8 @@ package io.microsphere.spring.boot.classloading; import io.microsphere.classloading.BannedArtifactClassLoadingExecutor; +import io.microsphere.logging.Logger; import io.microsphere.spring.boot.listener.SpringApplicationRunListenerAdapter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.context.event.ApplicationStartingEvent; import org.springframework.context.ApplicationListener; @@ -12,6 +11,8 @@ import java.util.Arrays; +import static io.microsphere.logging.LoggerFactory.getLogger; + /** * {@link ApplicationStartingEvent ApplicationStartingEvent} {@link ApplicationListener Listener} bans * the load of Artifacts collision class @@ -21,7 +22,7 @@ */ public class BannedArtifactClassLoadingListener extends SpringApplicationRunListenerAdapter implements Ordered { - private static final Logger logger = LoggerFactory.getLogger(BannedArtifactClassLoadingListener.class); + private static final Logger logger = getLogger(BannedArtifactClassLoadingListener.class); private static final boolean artifactsBanned = Boolean.getBoolean("microsphere.artifacts.banned"); diff --git a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/OnceApplicationPreparedEventListener.java b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/OnceApplicationPreparedEventListener.java index 96c1879..7865d81 100644 --- a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/OnceApplicationPreparedEventListener.java +++ b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/OnceApplicationPreparedEventListener.java @@ -1,7 +1,6 @@ package io.microsphere.spring.boot.context; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import io.microsphere.logging.Logger; import org.springframework.boot.SpringApplication; import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.context.ApplicationListener; @@ -13,6 +12,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListSet; +import static io.microsphere.logging.LoggerFactory.getLogger; + /** * Once execution {@link ApplicationPreparedEvent} {@link ApplicationListener} * @@ -24,7 +25,7 @@ public abstract class OnceApplicationPreparedEventListener implements Applicatio private static Map, Set> listenerProcessedContextIds = new ConcurrentHashMap<>(); - protected final Logger logger = LoggerFactory.getLogger(getClass()); + protected final Logger logger = getLogger(getClass()); private final Set processedContextIds; diff --git a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/OnceMainApplicationPreparedEventListener.java b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/OnceMainApplicationPreparedEventListener.java index 3974cf7..a94ff33 100644 --- a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/OnceMainApplicationPreparedEventListener.java +++ b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/OnceMainApplicationPreparedEventListener.java @@ -6,11 +6,11 @@ import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.Environment; -import org.springframework.util.ClassUtils; import java.util.List; import static java.util.Arrays.asList; +import static org.springframework.util.ClassUtils.isPresent; /** @@ -25,7 +25,7 @@ public abstract class OnceMainApplicationPreparedEventListener extends OnceAppli private static final String BOOTSTRAP_APPLICATION_LISTENER_ENABLED_PROPERTY_NAME = "spring.cloud.bootstrap.enabled"; - private static final boolean BOOTSTRAP_APPLICATION_LISTENER_PRESENT = ClassUtils.isPresent(BOOTSTRAP_APPLICATION_LISTENER_CLASS_NAME, null); + private static final boolean BOOTSTRAP_APPLICATION_LISTENER_PRESENT = isPresent(BOOTSTRAP_APPLICATION_LISTENER_CLASS_NAME, null); private static final String BOOTSTRAP_CONTEXT_ID = "bootstrap"; diff --git a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/properties/bind/ConfigurationPropertiesBeanContext.java b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/properties/bind/ConfigurationPropertiesBeanContext.java index d9959cb..4d98edf 100644 --- a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/properties/bind/ConfigurationPropertiesBeanContext.java +++ b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/properties/bind/ConfigurationPropertiesBeanContext.java @@ -17,14 +17,12 @@ package io.microsphere.spring.boot.context.properties.bind; import io.microsphere.spring.core.convert.support.ConversionServiceResolver; -import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanWrapperImpl; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.source.ConfigurationProperty; import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.convert.ConversionService; -import org.springframework.util.ClassUtils; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; @@ -34,6 +32,8 @@ import static io.microsphere.spring.boot.context.properties.source.util.ConfigurationPropertyUtils.toDashedForm; import static org.springframework.beans.BeanUtils.copyProperties; +import static org.springframework.beans.BeanUtils.getPropertyDescriptors; +import static org.springframework.util.ClassUtils.isPrimitiveOrWrapper; /** * The context for the bean annotated {@link ConfigurationProperties @ConfigurationProperties} @@ -95,7 +95,7 @@ private void initBinding(Object bean) { private void initBinding(Class beanClass, String prefix, Map bindingPropertyNames, String nestedPath) { if (isCandidateClass(beanClass)) { - PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(beanClass); + PropertyDescriptor[] descriptors = getPropertyDescriptors(beanClass); int descriptorSize = descriptors.length; for (int i = 0; i < descriptorSize; i++) { PropertyDescriptor descriptor = descriptors[i]; @@ -117,7 +117,7 @@ private boolean isCandidateProperty(PropertyDescriptor descriptor) { } private boolean isCandidateClass(Class beanClass) { - if (ClassUtils.isPrimitiveOrWrapper(beanClass)) { + if (isPrimitiveOrWrapper(beanClass)) { return false; } if (beanClass.isInterface() || beanClass.isEnum() || beanClass.isAnnotation() || beanClass.isArray() || beanClass.isSynthetic()) { diff --git a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/properties/bind/EventPublishingConfigurationPropertiesBeanPropertyChangedListener.java b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/properties/bind/EventPublishingConfigurationPropertiesBeanPropertyChangedListener.java index 8a59423..59db6c2 100644 --- a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/properties/bind/EventPublishingConfigurationPropertiesBeanPropertyChangedListener.java +++ b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/properties/bind/EventPublishingConfigurationPropertiesBeanPropertyChangedListener.java @@ -16,8 +16,7 @@ */ package io.microsphere.spring.boot.context.properties.bind; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import io.microsphere.logging.Logger; import org.springframework.beans.BeansException; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; @@ -30,17 +29,18 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.util.Assert; import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; +import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.spring.boot.context.properties.bind.util.BindUtils.isBoundProperty; import static io.microsphere.spring.boot.context.properties.bind.util.BindUtils.isConfigurationPropertiesBean; import static io.microsphere.spring.boot.context.properties.source.util.ConfigurationPropertyUtils.getPrefix; import static io.microsphere.spring.boot.context.properties.util.ConfigurationPropertiesUtils.CONFIGURATION_PROPERTIES_CLASS; import static io.microsphere.spring.boot.context.properties.util.ConfigurationPropertiesUtils.findConfigurationProperties; +import static org.springframework.util.Assert.isInstanceOf; /** * A {@link BindListener} implementation of {@link ConfigurationProperties @ConfigurationProperties} Bean to publish @@ -54,7 +54,7 @@ */ public class EventPublishingConfigurationPropertiesBeanPropertyChangedListener implements BindListener, BeanFactoryPostProcessor, ApplicationContextAware, SmartInitializingSingleton { - private final static Logger logger = LoggerFactory.getLogger(EventPublishingConfigurationPropertiesBeanPropertyChangedListener.class); + private final static Logger logger = getLogger(EventPublishingConfigurationPropertiesBeanPropertyChangedListener.class); private static final Class CONFIGURABLE_APPLICATION_CONTEXT_CLASS = ConfigurableApplicationContext.class; @@ -126,7 +126,7 @@ private void initConfigurationPropertiesBeanContexts(ConfigurableListableBeanFac @Override public void setApplicationContext(ApplicationContext context) throws BeansException { Class expectedType = CONFIGURABLE_APPLICATION_CONTEXT_CLASS; - Assert.isInstanceOf(expectedType, context, "The 'context' argument is not an instance of " + expectedType.getName()); + isInstanceOf(expectedType, context, "The 'context' argument is not an instance of " + expectedType.getName()); this.context = expectedType.cast(context); } diff --git a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/properties/metadata/ConfigurationMetadataReader.java b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/properties/metadata/ConfigurationMetadataReader.java index f25208a..b80aac6 100644 --- a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/properties/metadata/ConfigurationMetadataReader.java +++ b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/context/properties/metadata/ConfigurationMetadataReader.java @@ -16,8 +16,7 @@ */ package io.microsphere.spring.boot.context.properties.metadata; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import io.microsphere.logging.Logger; import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata; import org.springframework.boot.configurationprocessor.metadata.JsonMarshaller; import org.springframework.context.ResourceLoaderAware; @@ -28,6 +27,7 @@ import java.io.IOException; +import static io.microsphere.logging.LoggerFactory.getLogger; import static org.springframework.core.io.support.ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX; /** @@ -38,7 +38,7 @@ */ public class ConfigurationMetadataReader implements ResourceLoaderAware { - private final static Logger logger = LoggerFactory.getLogger(ConfigurationMetadataReader.class); + private final static Logger logger = getLogger(ConfigurationMetadataReader.class); public static final String METADATA_PATH = CLASSPATH_ALL_URL_PREFIX + "/META-INF/spring-configuration-metadata.json"; diff --git a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/env/DefaultPropertiesApplicationListener.java b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/env/DefaultPropertiesApplicationListener.java index 898ede2..87584a0 100644 --- a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/env/DefaultPropertiesApplicationListener.java +++ b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/env/DefaultPropertiesApplicationListener.java @@ -1,7 +1,6 @@ package io.microsphere.spring.boot.env; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import io.microsphere.logging.Logger; import org.springframework.boot.SpringApplication; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.logging.LoggingApplicationListener; @@ -14,7 +13,6 @@ import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.ResourcePatternResolver; -import org.springframework.util.ObjectUtils; import java.io.IOException; import java.net.URL; @@ -25,11 +23,13 @@ import java.util.Properties; import java.util.Set; +import static io.microsphere.logging.LoggerFactory.getLogger; import static io.microsphere.spring.boot.util.SpringApplicationUtils.getDefaultPropertiesResources; import static io.microsphere.spring.boot.util.SpringApplicationUtils.getResourceLoader; -import static io.microsphere.spring.util.PropertySourcesUtils.getDefaultProperties; -import static io.microsphere.spring.util.ResourceLoaderUtils.getResourcePatternResolver; +import static io.microsphere.spring.core.env.PropertySourcesUtils.getDefaultProperties; +import static io.microsphere.spring.core.io.ResourceLoaderUtils.getResourcePatternResolver; import static org.springframework.core.io.support.SpringFactoriesLoader.loadFactories; +import static org.springframework.util.ObjectUtils.containsElement; /** * Listable {@link ApplicationEnvironmentPreparedEvent} {@link ApplicationListener} Class @@ -44,7 +44,7 @@ public class DefaultPropertiesApplicationListener implements ApplicationListener public static final int DEFAULT_ORDER = LoggingApplicationListener.LOWEST_PRECEDENCE - 1; - private static final Logger logger = LoggerFactory.getLogger(DefaultPropertiesApplicationListener.class); + private static final Logger logger = getLogger(DefaultPropertiesApplicationListener.class); private int order = DEFAULT_ORDER; @@ -81,7 +81,7 @@ private void loadDefaultPropertiesResources(List propertyS ResourceLoader resourceLoader, Map defaultProperties) { Set defaultPropertiesResources = getDefaultPropertiesResources(); - logger.debug("Start loading from SpringApplicationUtils. GetDefaultPropertiesResources () 'defaultProperties resources: {}", defaultPropertiesResources); + logger.debug("Start loading from SpringApplicationUtils.loadDefaultPropertiesResources() 'defaultProperties resources: {}", defaultPropertiesResources); loadDefaultProperties(defaultPropertiesResources, propertySourceLoaders, resourceLoader, defaultProperties); } @@ -178,7 +178,7 @@ private void merge(EnumerablePropertySource propertySource, MapMercy + * @see PropertySourceLoader + * @since 1.0.0 + */ +public class PropertySourceLoaders implements PropertySourceLoader { + + private static final Logger logger = getLogger(PropertySourceLoaders.class); + + private final ResourceLoader resourceLoader; + + private final List loaders; + + public PropertySourceLoaders() { + this(getDefaultClassLoader()); + } + + public PropertySourceLoaders(ClassLoader classLoader) { + this(new DefaultResourceLoader(classLoader)); + } + + public PropertySourceLoaders(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + this.loaders = loadFactories(PropertySourceLoader.class, resourceLoader.getClassLoader()); + } + + @Override + public String[] getFileExtensions() { + String[] fileExtensions = loaders.stream() + .map(PropertySourceLoader::getFileExtensions) + .map(Arrays::asList) + .flatMap(List::stream) + .toArray(String[]::new); + return fileExtensions; + } + + @Override + public List> load(String name, Resource resource) throws IOException { + List> propertySources = new LinkedList<>(); + URL url = resource.getURL(); + for (PropertySourceLoader loader : loaders) { + if (supports(loader, url)) { + propertySources.addAll(loader.load(name, resource)); + } + } + return propertySources; + } + + /** + * Reload the {@link PropertySource} as an instance of {@link PropertySource} with {@link OriginLookup} + * + * @param propertySource {@link PropertySource} + * @return an instance of {@link PropertySource} with {@link OriginLookup} + * @throws IOException + */ + public PropertySource reloadAsOriginTracked(PropertySource propertySource) throws IOException { + if (propertySource instanceof OriginLookup) { + debug("The PropertySource[name : '{}', class : '{}'] is already an instance of OriginLookup", + propertySource.getName(), propertySource.getClass().getName()); + return propertySource; + } + // the name is source from Resource#getDescription() + String name = propertySource.getName(); + String location = substringBetween(name, "[", "]"); + // the location or uri can be resolved from FileSystemResource, ClassPathResource and UrlResource + if (hasText(location)) { + return loadAsOriginTracked(name, location); + } + return propertySource; + } + + /** + * Load the {@link PropertySource} as an instance of {@link PropertySource} with {@link OriginLookup} + * + * @param name the name of {@link PropertySource} + * @param location the location of {@link Resource} for {@link PropertySource} + * @return an instance of {@link PropertySource} with {@link OriginLookup} + * @throws IOException + */ + public PropertySource loadAsOriginTracked(String name, String location) throws IOException { + Resource resource = resourceLoader.getResource(location); + List> propertySources = load(name, resource); + int size = propertySources.size(); + if (size > 1) { + throw new IllegalStateException("The resource : " + resource + " can load more than one PropertySource"); + } + return propertySources.get(0); + } + + private boolean supports(PropertySourceLoader loader, URL resourceURL) { + String[] fileExtensions = loader.getFileExtensions(); + String path = resourceURL.getPath(); + boolean supported = false; + for (String fileExtension : fileExtensions) { + if (path.endsWith(fileExtension)) { + supported = true; + break; + } + } + return supported; + } + + private void debug(String messagePattern, Object... args) { + if (logger.isDebugEnabled()) { + logger.debug(messagePattern, args); + } + } +} diff --git a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/env/config/OriginTrackedConfigurationPropertyInitializer.java b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/env/config/OriginTrackedConfigurationPropertyInitializer.java new file mode 100644 index 0000000..f179b4e --- /dev/null +++ b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/env/config/OriginTrackedConfigurationPropertyInitializer.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 io.microsphere.spring.boot.env.config; + +import io.microsphere.logging.Logger; +import io.microsphere.spring.boot.env.PropertySourceLoaders; +import io.microsphere.spring.context.event.BeanFactoryListenerAdapter; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.boot.env.OriginTrackedMapPropertySource; +import org.springframework.boot.origin.Origin; +import org.springframework.boot.origin.OriginLookup; +import org.springframework.boot.origin.OriginTrackedValue; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.support.ResourcePropertySource; + +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.Map; + +import static io.microsphere.logging.LoggerFactory.getLogger; +import static io.microsphere.spring.beans.factory.support.BeanRegistrar.registerBean; + +/** + * {@link ApplicationContextInitializer} class supports origin tracked configuration property. + * + * @author Mercy + * @see ConfigurableEnvironment + * @since ApplicationContextInitializer + */ +public class OriginTrackedConfigurationPropertyInitializer implements BeanFactoryListenerAdapter, ApplicationContextInitializer { + + public static final String BEAN_NAME = "originTrackedConfigurationPropertyInitializer"; + + private static final Logger logger = getLogger(OriginTrackedConfigurationPropertyInitializer.class); + + private ConfigurableApplicationContext applicationContext; + + private PropertySourceLoaders propertySourceLoaders; + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + this.applicationContext = applicationContext; + this.propertySourceLoaders = new PropertySourceLoaders(applicationContext.getClassLoader()); + ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory(); + BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; + registerBean(registry, BEAN_NAME, this); + } + + @Override + public void onBeanFactoryConfigurationFrozen(ConfigurableListableBeanFactory beanFactory) { + ConfigurableEnvironment environment = applicationContext.getEnvironment(); + MutablePropertySources propertySources = environment.getPropertySources(); + initializePropertySources(propertySources); + } + + private void initializePropertySources(MutablePropertySources propertySources) { + for (PropertySource propertySource : propertySources) { + if (isPropertySourceCandidate(propertySource)) { + String name = propertySource.getName(); + try { + PropertySource originTrackedPropertySource = createOriginTrackedPropertySource(propertySource); + propertySources.replace(name, originTrackedPropertySource); + } catch (IOException e) { + logger.error("Failed to create the origin tracked PropertySource[name : '{}', class : '{}']", + name, propertySource.getClass().getName()); + } + } + } + } + + private boolean isPropertySourceCandidate(PropertySource propertySource) { + return (propertySource instanceof EnumerablePropertySource) && + !(propertySource instanceof OriginLookup); + } + + private PropertySource createOriginTrackedPropertySource(PropertySource propertySource) throws IOException { + if (propertySource instanceof ResourcePropertySource) { + return propertySourceLoaders.reloadAsOriginTracked(propertySource); + } + + EnumerablePropertySource enumerablePropertySource = (EnumerablePropertySource) propertySource; + String[] propertyNames = enumerablePropertySource.getPropertyNames(); + int size = propertyNames.length; + Map source = new LinkedHashMap<>(size); + for (int i = 0; i < size; i++) { + String propertyName = propertyNames[i]; + Object propertyValue = enumerablePropertySource.getProperty(propertyName); + // Skip if propertyValue is OriginTrackedValue + if (propertyValue instanceof OriginTrackedValue) { + continue; + } + Origin origin = resolveOrigin(propertySource); + // propertyValue with origin + propertyValue = OriginTrackedValue.of(propertyValue, origin); + source.put(propertyName, propertyValue); + } + return new OriginTrackedMapPropertySource(propertySource.getName(), source); + } + + private Origin resolveOrigin(PropertySource propertySource) { + // TODO more Origin implementations + return new NamedOrigin(propertySource.getName()); + } + + static class NamedOrigin implements Origin { + + private final String name; + + NamedOrigin(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + } +} diff --git a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/env/config/metadata/ConfigurationMetadataRepository.java b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/env/config/metadata/ConfigurationMetadataRepository.java new file mode 100644 index 0000000..44efe6e --- /dev/null +++ b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/env/config/metadata/ConfigurationMetadataRepository.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 io.microsphere.spring.boot.env.config.metadata; + +import io.microsphere.spring.boot.context.properties.metadata.ConfigurationMetadataReader; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata; +import org.springframework.boot.configurationprocessor.metadata.ItemHint; +import org.springframework.boot.configurationprocessor.metadata.ItemMetadata; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; + +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static java.util.Collections.emptyList; +import static org.springframework.boot.configurationprocessor.metadata.ItemMetadata.ItemType.GROUP; +import static org.springframework.boot.configurationprocessor.metadata.ItemMetadata.ItemType.PROPERTY; + +/** + * The Repository for {@link ConfigurationMetadata} + * + * @author Mercy + * @see ConfigurationMetadata + * @see ConfigurationMetadataReader + * @since 1.0.0 + */ +public class ConfigurationMetadataRepository implements CommandLineRunner { + + private final ConfigurationMetadataReader configurationMetadataReader; + + private Map namedGroups; + + private Map namedProperties; + + private Map> namedHints; + + public ConfigurationMetadataRepository(ConfigurationMetadataReader configurationMetadataReader) { + this.configurationMetadataReader = configurationMetadataReader; + } + + @NonNull + public Set getPropertyGroups() { + return this.namedGroups.keySet(); + } + + @NonNull + public Set getPropertyNames() { + return this.namedProperties.keySet(); + } + + @NonNull + public Collection getGroups() { + return this.namedGroups.values(); + } + + @NonNull + public Collection getProperties() { + return this.namedProperties.values(); + } + + @Nullable + public ItemMetadata getGroup(String name) { + return this.namedGroups.get(name); + } + + @Nullable + public ItemMetadata getProperty(String name) { + return this.namedProperties.get(name); + } + + @NonNull + public List getHints(String name) { + return this.namedHints.getOrDefault(name, emptyList()); + } + + @NonNull + public ConfigurationMetadataReader getConfigurationMetadataReader() { + return configurationMetadataReader; + } + + @Override + public void run(String... args) throws Exception { + ConfigurationMetadata configurationMetadata = this.configurationMetadataReader.read(); + // ConfigurationMetadata can't return the underlying items as Map + init(configurationMetadata); + } + + private void init(ConfigurationMetadata configurationMetadata) { + List items = configurationMetadata.getItems(); + initNamedGroupItems(items); + initNamedPropertyItems(items); + initNamedHints(configurationMetadata.getHints()); + } + + private void initNamedGroupItems(List items) { + this.namedGroups = createNamedItems(items, GROUP); + } + + private void initNamedPropertyItems(List items) { + this.namedProperties = createNamedItems(items, PROPERTY); + } + + private void initNamedHints(List items) { + Map> namedHints = new LinkedHashMap<>(items.size()); + items.stream().forEach(itemHint -> { + List itemHints = namedHints.computeIfAbsent(itemHint.getName(), i -> new LinkedList<>()); + itemHints.add(itemHint); + }); + this.namedHints = namedHints; + } + + private Map createNamedItems(List items, ItemMetadata.ItemType itemType) { + Map namedItems = new LinkedHashMap<>(items.size()); + items.stream().filter(item -> item.isOfItemType(itemType)).forEach(item -> { + namedItems.put(item.getName(), item); + }); + return namedItems; + } +} diff --git a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/report/ConditionsReportMessageBuilder.java b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/report/ConditionsReportMessageBuilder.java index e65ff70..98174ba 100644 --- a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/report/ConditionsReportMessageBuilder.java +++ b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/report/ConditionsReportMessageBuilder.java @@ -1,6 +1,5 @@ package io.microsphere.spring.boot.report; -import io.microsphere.text.FormatUtils; import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.ConfigurableEnvironment; @@ -11,6 +10,8 @@ import java.util.Map; import java.util.Set; +import static io.microsphere.text.FormatUtils.format; + /** * Spring Boot Conditions Report builder * @@ -106,6 +107,6 @@ private Set getBasePackages(ConfigurableApplicationContext context) { } private void appendLine(StringBuilder stringBuilder, String text, Object... args) { - stringBuilder.append(FormatUtils.format(text, args)).append(System.lineSeparator()); + stringBuilder.append(format(text, args)).append(System.lineSeparator()); } } diff --git a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/util/SpringApplicationUtils.java b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/util/SpringApplicationUtils.java index ca9d8f5..6e93f34 100644 --- a/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/util/SpringApplicationUtils.java +++ b/microsphere-core-spring-boot-starter/src/main/java/io/microsphere/spring/boot/util/SpringApplicationUtils.java @@ -3,12 +3,13 @@ import org.springframework.boot.SpringApplication; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.ResourceLoader; -import org.springframework.util.StringUtils; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; +import static org.springframework.util.StringUtils.hasText; + /** * {@link SpringApplication} Utilities class * @@ -29,7 +30,7 @@ private SpringApplicationUtils() throws InstantiationException { * @param resourceLocation "defaultProperties" resource path */ public static void addDefaultPropertiesResource(String resourceLocation) { - if (StringUtils.hasText(resourceLocation)) { + if (hasText(resourceLocation)) { defaultPropertiesResources.add(resourceLocation); } } diff --git a/microsphere-core-spring-boot-starter/src/main/resources/META-INF/spring.factories b/microsphere-core-spring-boot-starter/src/main/resources/META-INF/spring.factories index fe59ee9..aeb2f19 100644 --- a/microsphere-core-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ b/microsphere-core-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -1,6 +1,10 @@ # ApplicationContextInitializer org.springframework.context.ApplicationContextInitializer=\ -io.microsphere.spring.boot.report.ConditionEvaluationReportInitializer +io.microsphere.spring.context.event.EventPublishingBeanInitializer,\ +io.microsphere.spring.boot.report.ConditionEvaluationReportInitializer,\ +io.microsphere.spring.beans.factory.support.ListenableAutowireCandidateResolverInitializer,\ +io.microsphere.spring.core.env.ListenableConfigurableEnvironmentInitializer,\ +io.microsphere.spring.boot.env.config.OriginTrackedConfigurationPropertyInitializer # SpringApplicationRunListener org.springframework.boot.SpringApplicationRunListener=\ @@ -27,4 +31,5 @@ io.microsphere.spring.boot.autoconfigure.ConfigurableAutoConfigurationImportFilt # DefaultPropertiesPostProcessor io.microsphere.spring.boot.env.DefaultPropertiesPostProcessor=\ -io.microsphere.spring.boot.env.SpringApplicationDefaultPropertiesPostProcessor \ No newline at end of file +io.microsphere.spring.boot.env.SpringApplicationDefaultPropertiesPostProcessor + diff --git a/microsphere-core-spring-boot-starter/src/test/java/io/microsphere/spring/boot/autoconfigure/ApplicationAutoConfigurationTest.java b/microsphere-core-spring-boot-starter/src/test/java/io/microsphere/spring/boot/autoconfigure/ApplicationAutoConfigurationTest.java index 6bf3184..66093b8 100644 --- a/microsphere-core-spring-boot-starter/src/test/java/io/microsphere/spring/boot/autoconfigure/ApplicationAutoConfigurationTest.java +++ b/microsphere-core-spring-boot-starter/src/test/java/io/microsphere/spring/boot/autoconfigure/ApplicationAutoConfigurationTest.java @@ -22,7 +22,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; @@ -65,7 +64,7 @@ static class TestConfig { @Autowired private ObjectProvider loggingBeanListenerObjectProvider; - @Resource(name = "io.microsphere.spring.context.event.BeanTimeStatistics#0") + @Resource private BeanTimeStatistics beanTimeStatistics; @Resource(type = LoggingBeanListener.class) @@ -77,8 +76,7 @@ public void setLoggingBeanListener(LoggingBeanListener loggingBeanListener) { } public TestConfig(ObjectProvider beanListeners, - ObjectProvider> beanListenersList, - @Qualifier("io.microsphere.spring.context.event.LoggingBeanListener#0") LoggingBeanListener loggingBeanListener) { + ObjectProvider> beanListenersList) { this.beanListeners = beanListeners; this.beanListenersList = beanListenersList; } diff --git a/microsphere-core-spring-boot-starter/src/test/java/io/microsphere/spring/boot/context/config/BindableConfigurationBeanBinderTest.java b/microsphere-core-spring-boot-starter/src/test/java/io/microsphere/spring/boot/context/config/BindableConfigurationBeanBinderTest.java index 05ee67a..dbb82f2 100644 --- a/microsphere-core-spring-boot-starter/src/test/java/io/microsphere/spring/boot/context/config/BindableConfigurationBeanBinderTest.java +++ b/microsphere-core-spring-boot-starter/src/test/java/io/microsphere/spring/boot/context/config/BindableConfigurationBeanBinderTest.java @@ -28,7 +28,8 @@ import java.util.Map; -import static io.microsphere.spring.util.PropertySourcesUtils.getSubProperties; +import static io.microsphere.spring.core.env.PropertySourcesUtils.getSubProperties; +import static java.lang.Integer.valueOf; import static org.junit.jupiter.api.Assertions.assertEquals; /** @@ -59,6 +60,6 @@ void testBind() { Map properties = getSubProperties(environment.getPropertySources(), "user"); beanBinder.bind(properties, true, true, user); assertEquals("mercyblitz", user.getName()); - assertEquals(37, user.getAge()); + assertEquals(valueOf(37), user.getAge()); } } diff --git a/microsphere-core-spring-boot-starter/src/test/java/io/microsphere/spring/boot/context/properties/ListenableConfigurationPropertiesBindHandlerAdvisorTest.java b/microsphere-core-spring-boot-starter/src/test/java/io/microsphere/spring/boot/context/properties/ListenableConfigurationPropertiesBindHandlerAdvisorTest.java index 328c753..24faedb 100644 --- a/microsphere-core-spring-boot-starter/src/test/java/io/microsphere/spring/boot/context/properties/ListenableConfigurationPropertiesBindHandlerAdvisorTest.java +++ b/microsphere-core-spring-boot-starter/src/test/java/io/microsphere/spring/boot/context/properties/ListenableConfigurationPropertiesBindHandlerAdvisorTest.java @@ -16,10 +16,9 @@ */ package io.microsphere.spring.boot.context.properties; +import io.microsphere.logging.Logger; import io.microsphere.spring.boot.context.properties.bind.BindListener; import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.bind.BindContext; @@ -29,6 +28,8 @@ import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; +import static io.microsphere.logging.LoggerFactory.getLogger; + /** * {@link ListenableConfigurationPropertiesBindHandlerAdvisor} Test * @@ -50,7 +51,7 @@ @EnableConfigurationProperties public class ListenableConfigurationPropertiesBindHandlerAdvisorTest { - private static final Logger logger = LoggerFactory.getLogger(ListenableConfigurationPropertiesBindHandlerAdvisorTest.class); + private static final Logger logger = getLogger(ListenableConfigurationPropertiesBindHandlerAdvisorTest.class); @Test public void test() { diff --git a/microsphere-core-spring-boot-starter/src/test/java/io/microsphere/spring/boot/env/PropertySourceLoadersTest.java b/microsphere-core-spring-boot-starter/src/test/java/io/microsphere/spring/boot/env/PropertySourceLoadersTest.java new file mode 100644 index 0000000..6482b93 --- /dev/null +++ b/microsphere-core-spring-boot-starter/src/test/java/io/microsphere/spring/boot/env/PropertySourceLoadersTest.java @@ -0,0 +1,86 @@ +/* + * 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 io.microsphere.spring.boot.env; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.env.OriginTrackedMapPropertySource; +import org.springframework.boot.origin.OriginLookup; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; + +import java.io.IOException; +import java.util.List; + +import static io.microsphere.util.ArrayUtils.of; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * {@link PropertySourceLoaders} + * + * @author Mercy + * @see PropertySourceLoaders + * @since 1.0.0 + */ +public class PropertySourceLoadersTest { + + private static final PropertySourceLoaders propertySourceLoaders = new PropertySourceLoaders(); + + private static final String TEST_PROPERTY_NAME = "core"; + + private static final String TEST_RESOURCE_LOCATION = "classpath:/config/default/core.properties"; + + @Test + public void testGetFileExtensions() { + String[] fileExtensions = propertySourceLoaders.getFileExtensions(); + assertArrayEquals(of("properties", "xml", "yml", "yaml"), fileExtensions); + } + + @Test + public void testLoad() throws IOException { + ResourceLoader resourceLoader = new DefaultResourceLoader(); + Resource resource = resourceLoader.getResource(TEST_RESOURCE_LOCATION); + List> propertySources = propertySourceLoaders.load(TEST_PROPERTY_NAME, resource); + assertEquals(1, propertySources.size()); + + PropertySource propertySource = propertySources.get(0); + assertPropertySource(propertySource); + } + + @Test + public void testLoadAsOriginTracked() throws IOException { + PropertySource propertySource = propertySourceLoaders.loadAsOriginTracked(TEST_PROPERTY_NAME, TEST_RESOURCE_LOCATION); + assertTrue(propertySource instanceof OriginLookup); + assertPropertySource(propertySource); + } + + @Test + public void testReloadAsOriginTracked() throws IOException { + PropertySource propertySource = propertySourceLoaders.loadAsOriginTracked(TEST_PROPERTY_NAME, TEST_RESOURCE_LOCATION); + assertSame(propertySource, propertySourceLoaders.reloadAsOriginTracked(propertySource)); + } + + private void assertPropertySource(PropertySource propertySource) { + assertTrue(propertySource instanceof OriginTrackedMapPropertySource); + assertEquals(TEST_PROPERTY_NAME, propertySource.getName()); + assertEquals("graceful", propertySource.getProperty("server.shutdown")); + } +} diff --git a/microsphere-core-spring-boot-starter/src/test/resources/META-INF/spring.factories b/microsphere-core-spring-boot-starter/src/test/resources/META-INF/spring.factories index c4d0955..3e82d88 100644 --- a/microsphere-core-spring-boot-starter/src/test/resources/META-INF/spring.factories +++ b/microsphere-core-spring-boot-starter/src/test/resources/META-INF/spring.factories @@ -9,5 +9,4 @@ io.microsphere.spring.context.event.BeanTimeStatistics # BeanFactoryListener io.microsphere.spring.context.event.BeanFactoryListener=\ -io.microsphere.spring.context.event.LoggingBeanFactoryListener,\ -io.microsphere.spring.context.event.ParallelPreInstantiationSingletonsBeanFactoryListener \ No newline at end of file +io.microsphere.spring.context.event.LoggingBeanFactoryListener \ No newline at end of file diff --git a/microsphere-core-spring-boot-starter/src/test/resources/logback.xml b/microsphere-core-spring-boot-starter/src/test/resources/logback.xml new file mode 100644 index 0000000..fbbd2bd --- /dev/null +++ b/microsphere-core-spring-boot-starter/src/test/resources/logback.xml @@ -0,0 +1,18 @@ + + + + + + + ${ENCODER_PATTERN} + + + + + + + + + + + \ No newline at end of file diff --git a/microsphere-spring-boot-actuator/pom.xml b/microsphere-spring-boot-actuator/pom.xml index 0d65ebf..3391a58 100644 --- a/microsphere-spring-boot-actuator/pom.xml +++ b/microsphere-spring-boot-actuator/pom.xml @@ -27,6 +27,13 @@ ${revision} + + + org.jolokia + jolokia-core + ${jolokia.version} + + org.springframework.boot @@ -46,7 +53,19 @@ true + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.junit.jupiter + junit-jupiter + test + + org.springframework.boot spring-boot-starter-test diff --git a/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/MonitoredThreadPoolTaskScheduler.java b/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/MonitoredThreadPoolTaskScheduler.java index 77af057..08064f7 100644 --- a/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/MonitoredThreadPoolTaskScheduler.java +++ b/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/MonitoredThreadPoolTaskScheduler.java @@ -18,7 +18,6 @@ import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics; -import io.micrometer.core.instrument.internal.TimedScheduledExecutorService; import io.microsphere.concurrent.DelegatingScheduledExecutorService; import org.springframework.beans.BeansException; import org.springframework.beans.factory.SmartInitializingSingleton; @@ -38,7 +37,7 @@ * @author Mercy * @see ThreadPoolTaskScheduler * @see ExecutorServiceMetrics - * @see TimedScheduledExecutorService + * @see io.micrometer.core.instrument.internal.TimedScheduledExecutorService * @since 1.0.0 */ public class MonitoredThreadPoolTaskScheduler extends ThreadPoolTaskScheduler implements ApplicationContextAware, SmartInitializingSingleton { diff --git a/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/autoconfigure/ActuatorEndpointsAutoConfiguration.java b/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/autoconfigure/ActuatorEndpointsAutoConfiguration.java index 464a927..428ad89 100644 --- a/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/autoconfigure/ActuatorEndpointsAutoConfiguration.java +++ b/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/autoconfigure/ActuatorEndpointsAutoConfiguration.java @@ -16,17 +16,21 @@ */ package io.microsphere.spring.boot.actuate.autoconfigure; +import io.microsphere.spring.boot.actuate.condition.ConditionalOnConfigurationProcessorPresent; import io.microsphere.spring.boot.actuate.endpoint.ArtifactsEndpoint; +import io.microsphere.spring.boot.actuate.endpoint.ConfigurationMetadataEndpoint; import io.microsphere.spring.boot.actuate.endpoint.WebEndpoints; +import io.microsphere.spring.boot.context.properties.metadata.ConfigurationMetadataReader; +import io.microsphere.spring.boot.env.config.metadata.ConfigurationMetadataRepository; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; -import org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints; import org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; /** * Actuator {@link Endpoint @Endpoint} Auto-Configuration class @@ -38,6 +42,7 @@ @ConditionalOnClass(name = { "org.springframework.boot.actuate.endpoint.annotation.Endpoint" }) +@Import(value = {ActuatorEndpointsAutoConfiguration.ConfigurationProcessorConfiguration.class}) public class ActuatorEndpointsAutoConfiguration implements BeanClassLoaderAware { private ClassLoader classLoader; @@ -57,6 +62,30 @@ public WebEndpoints webEndpoints(WebEndpointsSupplier webEndpointsSupplier) { return new WebEndpoints(webEndpointsSupplier); } + @ConditionalOnConfigurationProcessorPresent + static class ConfigurationProcessorConfiguration { + + @Bean + @ConditionalOnMissingBean + public ConfigurationMetadataReader configurationMetadataReader() { + return new ConfigurationMetadataReader(); + } + + @Bean + @ConditionalOnMissingBean + public ConfigurationMetadataRepository configurationMetadataRepository(ConfigurationMetadataReader configurationMetadataReader) { + return new ConfigurationMetadataRepository(configurationMetadataReader); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnAvailableEndpoint + public ConfigurationMetadataEndpoint configurationMetadataEndpoint(ConfigurationMetadataRepository configurationMetadataRepository) { + return new ConfigurationMetadataEndpoint(configurationMetadataRepository); + } + + } + @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; diff --git a/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/condition/ConditionalOnConfigurationProcessorPresent.java b/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/condition/ConditionalOnConfigurationProcessorPresent.java new file mode 100644 index 0000000..c45d278 --- /dev/null +++ b/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/condition/ConditionalOnConfigurationProcessorPresent.java @@ -0,0 +1,41 @@ +/* + * 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 io.microsphere.spring.boot.actuate.condition; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Conditional; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * {@link Conditional @Conditional} that checks whether the artifact "org.springframework.boot:spring-boot-configuration-processor" + * is present + * + * @author Mercy + * @see Conditional + * @since 1.0.0 + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@ConditionalOnClass(name = "org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata") +public @interface ConditionalOnConfigurationProcessorPresent { +} diff --git a/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/endpoint/ConfigurationMetadataEndpoint.java b/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/endpoint/ConfigurationMetadataEndpoint.java new file mode 100644 index 0000000..f67e152 --- /dev/null +++ b/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/endpoint/ConfigurationMetadataEndpoint.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.microsphere.spring.boot.actuate.endpoint; + +import io.microsphere.spring.boot.env.config.metadata.ConfigurationMetadataRepository; +import org.springframework.boot.actuate.endpoint.OperationResponseBody; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; +import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata; +import org.springframework.boot.configurationprocessor.metadata.ItemMetadata; + +import java.util.Collection; + +/** + * {@link Endpoint @Endpoint} to expose the {@link ConfigurationMetadata Configuration Metadata} that was generated by + * "org.springframework.boot:spring-boot-configuration-processor" + * + * @author Mercy + * @see ConfigurationMetadata + * @see ConfigurationMetadataRepository + * @since 1.0.0 + */ +@Endpoint(id = "configMetadata") +public class ConfigurationMetadataEndpoint { + + private final ConfigurationMetadataRepository configurationMetadataRepository; + + public ConfigurationMetadataEndpoint(ConfigurationMetadataRepository configurationMetadataRepository) { + this.configurationMetadataRepository = configurationMetadataRepository; + } + + @ReadOperation + public ConfigurationMetadataDescriptor getConfigurationMetadata() { + ConfigurationMetadataDescriptor configurationMetadata = new ConfigurationMetadataDescriptor(); + configurationMetadata.groups = this.configurationMetadataRepository.getGroups(); + configurationMetadata.properties = this.configurationMetadataRepository.getProperties(); + return configurationMetadata; + } + + /** + * The Descriptor class for {@link ConfigurationMetadata} + */ + public static class ConfigurationMetadataDescriptor implements OperationResponseBody { + + private Collection groups; + + private Collection properties; + + public Collection getGroups() { + return groups; + } + + public void setGroups(Collection groups) { + this.groups = groups; + } + + public Collection getProperties() { + return properties; + } + + public void setProperties(Collection properties) { + this.properties = properties; + } + } +} diff --git a/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/endpoint/ConfigurationPropertiesEndpoint.java b/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/endpoint/ConfigurationPropertiesEndpoint.java new file mode 100644 index 0000000..8e6c1e0 --- /dev/null +++ b/microsphere-spring-boot-actuator/src/main/java/io/microsphere/spring/boot/actuate/endpoint/ConfigurationPropertiesEndpoint.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 io.microsphere.spring.boot.actuate.endpoint; + +import io.microsphere.spring.boot.env.config.metadata.ConfigurationMetadataRepository; +import io.microsphere.spring.config.ConfigurationProperty; +import org.springframework.boot.actuate.endpoint.OperationResponseBody; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; +import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.event.EventListener; + +import java.util.List; + +/** + * {@link Endpoint @Endpoint} to expose the configuration properties. + * + * @author Mercy + * @see ConfigurationMetadata + * @see ConfigurationProperties + * @since 1.0.0 + */ +@Endpoint(id = "configProperties") +public class ConfigurationPropertiesEndpoint { + + private final ConfigurationMetadataRepository configurationMetadataRepository; + + public ConfigurationPropertiesEndpoint(ConfigurationMetadataRepository configurationMetadataRepository) { + this.configurationMetadataRepository = configurationMetadataRepository; + } + + @EventListener(ApplicationReadyEvent.class) + public void onApplicationReadyEvent(ApplicationReadyEvent event) { + ConfigurableApplicationContext context = event.getApplicationContext(); + } + + @ReadOperation + public ConfigurationPropertiesDescriptor getConfigurationProperties() { + ConfigurationPropertiesDescriptor configurationProperties = new ConfigurationPropertiesDescriptor(); + return configurationProperties; + } + + + public static class ConfigurationPropertiesDescriptor implements OperationResponseBody { + + private List configurationProperties; + + public List getConfigurationProperties() { + return configurationProperties; + } + + public void setConfigurationProperties(List configurationProperties) { + this.configurationProperties = configurationProperties; + } + } + + +} diff --git a/microsphere-spring-boot-actuator/src/main/resources/META-INF/config/default/endpoints.properties b/microsphere-spring-boot-actuator/src/main/resources/META-INF/config/default/endpoints.properties index 091964e..8aa300d 100644 --- a/microsphere-spring-boot-actuator/src/main/resources/META-INF/config/default/endpoints.properties +++ b/microsphere-spring-boot-actuator/src/main/resources/META-INF/config/default/endpoints.properties @@ -75,6 +75,12 @@ management.endpoint.artifacts.enabled = true management.endpoints.web.path-mapping.artifacts = microsphere/artifacts management.endpoint.artifacts.cache.time-to-live = ${microsphere.cache.long-long-time-to-live} +### WebEndpoints Endpoint management.endpoint.webEndpoints.enabled = true management.endpoints.web.path-mapping.webEndpoints = microsphere/web/endpoints management.endpoint.webEndpoints.cache.time-to-live = ${microsphere.cache.short-time-to-live} + +### ConfigurationMetadata Endpoint +management.endpoint.configMetadata.enabled = true +management.endpoints.web.path-mapping.configMetadata = microsphere/config/metadata +management.endpoint.configMetadata.cache.time-to-live = ${microsphere.cache.long-long-time-to-live} \ No newline at end of file diff --git a/microsphere-spring-boot-actuator/src/test/java/io/microsphere/spring/boot/actuate/autoconfigure/ActuatorEndpointsAutoConfigurationTest.java b/microsphere-spring-boot-actuator/src/test/java/io/microsphere/spring/boot/actuate/autoconfigure/ActuatorEndpointsAutoConfigurationTest.java index 8c8b1cd..7ce1881 100644 --- a/microsphere-spring-boot-actuator/src/test/java/io/microsphere/spring/boot/actuate/autoconfigure/ActuatorEndpointsAutoConfigurationTest.java +++ b/microsphere-spring-boot-actuator/src/test/java/io/microsphere/spring/boot/actuate/autoconfigure/ActuatorEndpointsAutoConfigurationTest.java @@ -1,6 +1,7 @@ package io.microsphere.spring.boot.actuate.autoconfigure; import io.microsphere.spring.boot.actuate.endpoint.ArtifactsEndpoint; +import io.microsphere.spring.boot.actuate.endpoint.ConfigurationMetadataEndpoint; import io.microsphere.spring.boot.actuate.endpoint.WebEndpoints; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -9,6 +10,7 @@ import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.PropertySource; +import org.springframework.test.context.TestPropertySource; import java.util.Map; @@ -25,10 +27,15 @@ webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { ActuatorEndpointsAutoConfigurationTest.class, - }) + }, + properties = { + "management.endpoint.loggers.enabled=false" + } +) @PropertySource(value = "classpath:META-INF/config/default/endpoints.properties") +@TestPropertySource(value = "classpath:META-INF/config/default/endpoints.properties") @EnableAutoConfiguration -class ActuatorEndpointsAutoConfigurationTest { +public class ActuatorEndpointsAutoConfigurationTest { @Autowired private ArtifactsEndpoint artifactsEndpoint; @@ -36,6 +43,9 @@ class ActuatorEndpointsAutoConfigurationTest { @Autowired private WebEndpoints webEndpoints; + @Autowired + private ConfigurationMetadataEndpoint configurationMetadataEndpoint; + @Test void testArtifactsEndpoint() { assertFalse(artifactsEndpoint.getArtifactMetaInfoList().isEmpty()); @@ -47,6 +57,13 @@ public void testInvokeReadOperations() { assertFalse(aggregatedResults.isEmpty()); } + @Test + public void testGetConfigurationMetadata() { + ConfigurationMetadataEndpoint.ConfigurationMetadataDescriptor configurationMetadata = configurationMetadataEndpoint.getConfigurationMetadata(); + assertFalse(configurationMetadata.getGroups().isEmpty()); + assertFalse(configurationMetadata.getProperties().isEmpty()); + } + public static void main(String[] args) { new SpringApplicationBuilder(ActuatorEndpointsAutoConfigurationTest.class) .web(WebApplicationType.SERVLET) diff --git a/microsphere-spring-boot-actuator/src/test/java/io/microsphere/spring/boot/actuate/env/DefaultPropertiesTest.java b/microsphere-spring-boot-actuator/src/test/java/io/microsphere/spring/boot/actuate/env/DefaultPropertiesTest.java index 175ba78..8b38131 100644 --- a/microsphere-spring-boot-actuator/src/test/java/io/microsphere/spring/boot/actuate/env/DefaultPropertiesTest.java +++ b/microsphere-spring-boot-actuator/src/test/java/io/microsphere/spring/boot/actuate/env/DefaultPropertiesTest.java @@ -57,7 +57,7 @@ public void testEndpointsDefaultProperties() { assertProperty("management.endpoint.httptrace.enabled", "false"); assertProperty("management.endpoint.info.enabled", "true"); assertProperty("management.endpoint.integrationgraph.enabled", "false"); - assertProperty("management.endpoint.loggers.enabled", "true"); + assertProperty("management.endpoint.loggers.enabled", "false"); assertProperty("management.endpoint.liquibase.enabled", "false"); assertProperty("management.endpoint.metrics.enabled", "true"); assertProperty("management.endpoint.mappings.enabled", "true"); @@ -74,6 +74,6 @@ public void testEndpointsDefaultProperties() { } private void assertProperty(String propertyName, String expectedValue) { - assertEquals(environment.getRequiredProperty(propertyName), expectedValue); + assertEquals(expectedValue, environment.getRequiredProperty(propertyName)); } } diff --git a/microsphere-spring-boot-actuator/src/test/resources/application.properties b/microsphere-spring-boot-actuator/src/test/resources/application.properties new file mode 100644 index 0000000..1636c41 --- /dev/null +++ b/microsphere-spring-boot-actuator/src/test/resources/application.properties @@ -0,0 +1 @@ +management.endpoint.loggers.enabled = false \ No newline at end of file diff --git a/microsphere-spring-boot-parent/pom.xml b/microsphere-spring-boot-parent/pom.xml index 548a5fc..9e7756c 100644 --- a/microsphere-spring-boot-parent/pom.xml +++ b/microsphere-spring-boot-parent/pom.xml @@ -19,7 +19,8 @@ Microsphere Spring Boot Parent - 2.0.0-SNAPSHOT + 0.2.0 + 1.7.2 @@ -45,34 +46,6 @@ - - spring-boot-2.4 - - 2.4.13 - - - - - spring-boot-2.5 - - 2.5.15 - - - - - spring-boot-2.6 - - 2.6.15 - - - - - spring-boot-2.7 - - 2.7.18 - - - spring-boot-3.0 @@ -90,17 +63,24 @@ spring-boot-3.2 - 3.2.7 + 3.2.12 spring-boot-3.3 + + 3.3.7 + + + + + spring-boot-3.4 true - 3.3.1 + 3.4.1 diff --git a/pom.xml b/pom.xml index 2bed7fa..4f47016 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.github.microsphere-projects microsphere-build - 0.0.21 + 0.1.1 4.0.0