Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.Map;

import com.alibaba.cloud.nacos.NacosConfigProperties;
import org.jspecify.annotations.Nullable;

import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
Expand Down Expand Up @@ -55,19 +56,43 @@ public class NacosPropertySource extends MapPropertySource {
*/
private final boolean isRefreshable;

/**
* File extension this property source was parsed with (e.g. {@code yaml},
* {@code properties}, {@code json}). Captured so that subsequent refreshes
* can re-parse updated config with the same loader instead of falling back
* to the properties default. The value is the same notion the rest of the
* codebase tracks as {@code NacosConfigProperties#fileExtension} and
* {@code NacosItemConfig#suffix}.
*/
private final @Nullable String fileExtension;

NacosPropertySource(String group, String dataId, Map<String, Object> source,
Date timestamp, boolean isRefreshable) {
this(group, dataId, source, timestamp, isRefreshable, null);
}

private NacosPropertySource(String group, String dataId, Map<String, Object> source,
Date timestamp, boolean isRefreshable,
@Nullable String fileExtension) {
super(String.join(NacosConfigProperties.COMMAS, dataId, group), source);
this.group = group;
this.dataId = dataId;
this.timestamp = timestamp;
this.isRefreshable = isRefreshable;
this.fileExtension = fileExtension;
}

public NacosPropertySource(List<PropertySource<?>> propertySources, String group,
String dataId, Date timestamp, boolean isRefreshable) {
this(group, dataId, getSourceMap(group, dataId, propertySources), timestamp,
isRefreshable);
isRefreshable, null);
}

public NacosPropertySource(List<PropertySource<?>> propertySources, String group,
String dataId, Date timestamp, boolean isRefreshable,
@Nullable String fileExtension) {
this(group, dataId, getSourceMap(group, dataId, propertySources), timestamp,
isRefreshable, fileExtension);
}

private static Map<String, Object> getSourceMap(String group, String dataId,
Expand Down Expand Up @@ -130,4 +155,14 @@ public boolean isRefreshable() {
return isRefreshable;
}

/**
* Returns the file extension this property source was parsed with, or
* {@code null} if it was built through a constructor that does not carry
* the extension. Used internally by the refresh listener to re-parse
* pushed Nacos content with the original format.
*/
public @Nullable String getFileExtension() {
return fileExtension;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public NacosPropertySource build(String dataId, String group, String fileExtensi
List<PropertySource<?>> propertySources = loadNacosData(dataId, group,
fileExtension);
NacosPropertySource nacosPropertySource = new NacosPropertySource(propertySources,
group, dataId, new Date(), isRefreshable);
group, dataId, new Date(), isRefreshable, fileExtension);
NacosPropertySourceRepository.collectNacosPropertySource(nacosPropertySource);
return nacosPropertySource;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public NacosConfigDataLoader(DeferredLogFactory logFactory) {

NacosPropertySource propertySource = new NacosPropertySource(propertySources,
config.getGroup(), config.getDataId(), new Date(),
config.isRefreshEnabled());
config.isRefreshEnabled(), config.getSuffix());

NacosPropertySourceRepository.collectNacosPropertySource(propertySource);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.util.StringUtils;

public class NacosPropertySourceRefreshListener implements BeanPostProcessor, SmartApplicationListener, ApplicationContextAware {

Expand Down Expand Up @@ -114,9 +115,25 @@ public void handle(NacosConfigRefreshEvent event) {
log.warn("Event dataId or group is null, skipping refresh");
return;
}
NacosPropertySource newProperSource = nacosPropertySourceBuilder.build(dataId, group, "properties", ((NacosPropertySource) prevpropertySource).isRefreshable());
NacosPropertySource prevNacosPropertySource = (NacosPropertySource) prevpropertySource;
String fileExtension = prevNacosPropertySource.getFileExtension();
if (!StringUtils.hasText(fileExtension)) {
// The source was built through the legacy constructor (e.g. by
// third-party code) and did not record its format. We cannot
// safely guess the extension from global configuration here,
// because shared-configs / extension-configs may declare their
// own suffix per entry. Fall back to "properties" — the
// historical default — and log so the case is diagnosable.
log.warn(
"Previous Nacos property source did not record a file extension,"
+ " falling back to 'properties'; non-properties formats"
+ " may be mis-parsed. sourceName={}", sourceName);
fileExtension = "properties";
}
NacosPropertySource newProperSource = nacosPropertySourceBuilder.build(dataId, group, fileExtension, prevNacosPropertySource.isRefreshable());
target.replace(sourceName, newProperSource);
log.info("Replace Nacos Property Source : " + sourceName);
log.info("Replace Nacos Property Source: {} (fileExtension={})",
sourceName, fileExtension);
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2013-present the original author or authors.
*
* Licensed 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
*
* https://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 com.alibaba.cloud.nacos.client;

import com.alibaba.nacos.api.config.ConfigService;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests for {@link NacosPropertySourceBuilder}.
*/
public class NacosPropertySourceBuilderTest {

@Test
void builtPropertySourcePreservesFileExtension() throws Exception {
ConfigService configService = Mockito.mock(ConfigService.class);
Mockito.when(configService.getConfig(ArgumentMatchers.anyString(),
ArgumentMatchers.anyString(), ArgumentMatchers.anyLong()))
.thenReturn("key: value");
NacosPropertySourceBuilder builder = new NacosPropertySourceBuilder(configService,
1000L);

NacosPropertySource source = builder.build("app.yaml", "DEFAULT_GROUP", "yaml",
true);

assertThat(source.getFileExtension()).isEqualTo("yaml");
assertThat(source.getDataId()).isEqualTo("app.yaml");
assertThat(source.getGroup()).isEqualTo("DEFAULT_GROUP");
assertThat(source.isRefreshable()).isTrue();
}

@Test
void builtPropertySourcePreservesPropertiesExtension() throws Exception {
ConfigService configService = Mockito.mock(ConfigService.class);
Mockito.when(configService.getConfig(ArgumentMatchers.anyString(),
ArgumentMatchers.anyString(), ArgumentMatchers.anyLong()))
.thenReturn("key=value");
NacosPropertySourceBuilder builder = new NacosPropertySourceBuilder(configService,
1000L);

NacosPropertySource source = builder.build("app", "DEFAULT_GROUP", "properties",
false);

assertThat(source.getFileExtension()).isEqualTo("properties");
assertThat(source.isRefreshable()).isFalse();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2013-present the original author or authors.
*
* Licensed 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
*
* https://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 com.alibaba.cloud.nacos.client;

import java.util.Collections;
import java.util.Date;
import java.util.List;

import org.junit.jupiter.api.Test;

import org.springframework.core.env.PropertySource;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests for {@link NacosPropertySource}.
*/
public class NacosPropertySourceTest {

@Test
void fileExtensionIsNullWhenUsingLegacyConstructor() {
List<PropertySource<?>> sources = Collections.emptyList();
NacosPropertySource source = new NacosPropertySource(sources, "DEFAULT_GROUP",
"app.yaml", new Date(), true);
assertThat(source.getFileExtension()).isNull();
}

@Test
void fileExtensionIsPreservedWhenProvided() {
List<PropertySource<?>> sources = Collections.emptyList();
NacosPropertySource source = new NacosPropertySource(sources, "DEFAULT_GROUP",
"app.yaml", new Date(), true, "yaml");
assertThat(source.getFileExtension()).isEqualTo("yaml");
}

@Test
void fileExtensionCanBeNullExplicitly() {
List<PropertySource<?>> sources = Collections.emptyList();
NacosPropertySource source = new NacosPropertySource(sources, "DEFAULT_GROUP",
"app", new Date(), true, null);
assertThat(source.getFileExtension()).isNull();
}

@Test
void otherPropertiesAreNotAffectedByNewConstructor() {
Date timestamp = new Date();
List<PropertySource<?>> sources = Collections.emptyList();
NacosPropertySource source = new NacosPropertySource(sources, "MY_GROUP",
"app.json", timestamp, false, "json");
assertThat(source.getGroup()).isEqualTo("MY_GROUP");
assertThat(source.getDataId()).isEqualTo("app.json");
assertThat(source.getTimestamp()).isEqualTo(timestamp);
assertThat(source.isRefreshable()).isFalse();
assertThat(source.getFileExtension()).isEqualTo("json");
}

}
Loading
Loading