Merge pull request #2288 from alibaba/develop

Merge develop into develop_1.2.0
This commit is contained in:
Peter Zhu 2020-01-12 21:37:37 +08:00 committed by GitHub
commit 50e511d904
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1232 additions and 87 deletions

View File

@ -1,8 +1,6 @@
notifications:
email:
recipients:
- xchaos8@126.com
- nacos_dev@linux.alibaba.com
- dev-nacos@googlegroups.com
- mw_configcenter@list.alibaba-inc.com
on_success: change
@ -30,7 +28,7 @@ before_install:
script:
- mvn -B clean package apache-rat:check findbugs:findbugs -Dmaven.test.skip=true
- mvn -Prelease-nacos clean install -U
- mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
- mvn clean package -Pit-test
after_success:
- mvn clean package -Pit-test

View File

@ -62,6 +62,8 @@ public class Constants {
public static final String CONFIG_VERSION = "Config-Version";
public static final String CONFIG_TYPE = "Config-Type";
public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
public static final String SPACING_INTERVAL = "client-spacing-interval";

View File

@ -0,0 +1,44 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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
*
* 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 com.alibaba.nacos.api.config;
import com.alibaba.nacos.api.config.ConfigChangeItem;
import java.util.Collection;
import java.util.Map;
/**
* ConfigChangeEvent
*
* @author rushsky518
*/
public class ConfigChangeEvent {
private Map<String, ConfigChangeItem> data;
public ConfigChangeEvent(Map<String, ConfigChangeItem> data) {
this.data = data;
}
public ConfigChangeItem getChangeItem(String key) {
return data.get(key);
}
public Collection<ConfigChangeItem> getChangeItems() {
return data.values();
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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
*
* 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 com.alibaba.nacos.api.config;
/**
* ConfigChangeItem
*
* @author rushsky518
*/
public class ConfigChangeItem {
private String key;
private String oldValue;
private String newValue;
private PropertyChangeType type;
public ConfigChangeItem(String key, String oldValue, String newValue) {
this.key = key;
this.oldValue = oldValue;
this.newValue = newValue;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getOldValue() {
return oldValue;
}
public void setOldValue(String oldValue) {
this.oldValue = oldValue;
}
public String getNewValue() {
return newValue;
}
public void setNewValue(String newValue) {
this.newValue = newValue;
}
public PropertyChangeType getType() {
return type;
}
public void setType(PropertyChangeType type) {
this.type = type;
}
@Override
public String toString() {
return "ConfigChangeItem{" +
"key='" + key + '\'' +
", oldValue='" + oldValue + '\'' +
", newValue='" + newValue + '\'' +
", type=" + type +
'}';
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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
*
* 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 com.alibaba.nacos.api.config;
/**
* Property Change Type
*
* @author rushsky518
*/
public enum PropertyChangeType {
/** add */
ADDED,
/** modified */
MODIFIED,
/** deleted */
DELETED
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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
*
* 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 com.alibaba.nacos.api.config.listener;
import com.alibaba.nacos.api.config.ConfigChangeItem;
import java.io.IOException;
import java.util.Map;
/**
* ConfigChangeParser
*
* @author rushsky518
*/
public interface ConfigChangeParser {
/**
* judge type
* @param type
* @return
*/
boolean isResponsibleFor(String type);
/**
* compare old and new data
* @param oldContent
* @param newContent
* @param type
* @return
* @throws IOException
*/
Map<String, ConfigChangeItem> doParse(String oldContent, String newContent, String type) throws IOException;
}

View File

@ -116,6 +116,10 @@
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
</dependencies>

View File

@ -140,9 +140,8 @@ public class NacosConfigService implements ConfigService {
}
try {
content = worker.getServerConfig(dataId, group, tenant, timeoutMs);
cr.setContent(content);
String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
cr.setContent(ct[0]);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();

View File

@ -0,0 +1,74 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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
*
* 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 com.alibaba.nacos.client.config.impl;
import com.alibaba.nacos.api.config.ConfigChangeItem;
import com.alibaba.nacos.api.config.PropertyChangeType;
import com.alibaba.nacos.api.config.listener.ConfigChangeParser;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* AbstractConfigChangeParser
*
* @author rushsky518
*/
public abstract class AbstractConfigChangeParser implements ConfigChangeParser {
private String configType;
public AbstractConfigChangeParser(String configType) {
this.configType = configType;
}
@Override
public boolean isResponsibleFor(String type) {
return this.configType.equalsIgnoreCase(type);
}
protected Map<String, ConfigChangeItem> filterChangeData(Map oldMap, Map newMap) {
Map<String, ConfigChangeItem> result = new HashMap<String, ConfigChangeItem>(16);
for (Iterator<Map.Entry<String, Object>> entryItr = oldMap.entrySet().iterator(); entryItr.hasNext();) {
Map.Entry<String, Object> e = entryItr.next();
ConfigChangeItem cci = null;
if (newMap.containsKey(e.getKey())) {
if (e.getValue().equals(newMap.get(e.getKey()))) {
continue;
}
cci = new ConfigChangeItem(e.getKey(), e.getValue().toString(), newMap.get(e.getKey()).toString());
cci.setType(PropertyChangeType.MODIFIED);
} else {
cci = new ConfigChangeItem(e.getKey(), e.getValue().toString(), null);
cci.setType(PropertyChangeType.DELETED);
}
result.put(e.getKey(), cci);
}
for (Iterator<Map.Entry<String, Object>> entryItr = newMap.entrySet().iterator(); entryItr.hasNext();) {
Map.Entry<String, Object> e = entryItr.next();
if (!oldMap.containsKey(e.getKey())) {
ConfigChangeItem cci = new ConfigChangeItem(e.getKey(), null, e.getValue().toString());
cci.setType(PropertyChangeType.ADDED);
result.put(e.getKey(), cci);
}
}
return result;
}
}

View File

@ -16,11 +16,13 @@
package com.alibaba.nacos.client.config.impl;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.config.ConfigChangeEvent;
import com.alibaba.nacos.api.config.listener.AbstractSharedListener;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager;
import com.alibaba.nacos.client.config.filter.impl.ConfigResponse;
import com.alibaba.nacos.client.config.listener.impl.AbstractConfigChangeListener;
import com.alibaba.nacos.client.config.utils.MD5;
import com.alibaba.nacos.client.utils.LogUtils;
import com.alibaba.nacos.client.utils.TenantUtil;
@ -28,6 +30,7 @@ import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@ -59,9 +62,17 @@ public class CacheData {
return content;
}
public void setContent(String newContent) {
this.content = newContent;
this.md5 = getMd5String(content);
public void setContent(String content) {
this.content = content;
this.md5 = getMd5String(this.content);
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
/**
@ -74,7 +85,9 @@ public class CacheData {
if (null == listener) {
throw new IllegalArgumentException("listener is null");
}
ManagerListenerWrap wrap = new ManagerListenerWrap(listener, md5);
ManagerListenerWrap wrap = (listener instanceof AbstractConfigChangeListener) ?
new ManagerListenerWrap(listener, md5, content) : new ManagerListenerWrap(listener, md5);
if (listeners.addIfAbsent(wrap)) {
LOGGER.info("[{}] [add-listener] ok, tenant={}, dataId={}, group={}, cnt={}", name, tenant, dataId, group,
listeners.size());
@ -158,12 +171,12 @@ public class CacheData {
void checkListenerMd5() {
for (ManagerListenerWrap wrap : listeners) {
if (!md5.equals(wrap.lastCallMd5)) {
safeNotifyListener(dataId, group, content, md5, wrap);
safeNotifyListener(dataId, group, content, type, md5, wrap);
}
}
}
private void safeNotifyListener(final String dataId, final String group, final String content,
private void safeNotifyListener(final String dataId, final String group, final String content, final String type,
final String md5, final ManagerListenerWrap listenerWrap) {
final Listener listener = listenerWrap.listener;
@ -188,6 +201,15 @@ public class CacheData {
configFilterChainManager.doFilter(null, cr);
String contentTmp = cr.getContent();
listener.receiveConfigInfo(contentTmp);
// compare lastContent and content
if (listener instanceof AbstractConfigChangeListener) {
Map data = ConfigChangeHandler.getInstance().parseChangeData(listenerWrap.lastContent, content, type);
ConfigChangeEvent event = new ConfigChangeEvent(data);
((AbstractConfigChangeListener)listener).receiveConfigChange(event);
listenerWrap.lastContent = content;
}
listenerWrap.lastCallMd5 = md5;
LOGGER.info("[{}] [notify-ok] dataId={}, group={}, md5={}, listener={} ", name, dataId, group, md5,
listener);
@ -282,11 +304,13 @@ public class CacheData {
private volatile String content;
private int taskId;
private volatile boolean isInitializing = true;
private String type;
}
class ManagerListenerWrap {
final Listener listener;
String lastCallMd5 = CacheData.getMd5String(null);
String lastContent = null;
ManagerListenerWrap(Listener listener) {
this.listener = listener;
@ -297,6 +321,12 @@ class ManagerListenerWrap {
this.lastCallMd5 = md5;
}
ManagerListenerWrap(Listener listener, String md5, String lastContent) {
this.listener = listener;
this.lastCallMd5 = md5;
this.lastContent = lastContent;
}
@Override
public boolean equals(Object obj) {
if (null == obj || obj.getClass() != getClass()) {

View File

@ -17,6 +17,7 @@ package com.alibaba.nacos.client.config.impl;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.common.Constants;
import com.alibaba.nacos.api.config.ConfigType;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.client.config.common.GroupKey;
@ -46,6 +47,7 @@ import java.util.concurrent.atomic.AtomicReference;
import static com.alibaba.nacos.api.common.Constants.LINE_SEPARATOR;
import static com.alibaba.nacos.api.common.Constants.WORD_SEPARATOR;
import static com.alibaba.nacos.api.common.Constants.CONFIG_TYPE;
/**
* Longpolling
@ -183,8 +185,8 @@ public class ClientWorker {
cache = new CacheData(configFilterChainManager, agent.getName(), dataId, group, tenant);
// fix issue # 1317
if (enableRemoteSyncConfig) {
String content = getServerConfig(dataId, group, tenant, 3000L);
cache.setContent(content);
String[] ct = getServerConfig(dataId, group, tenant, 3000L);
cache.setContent(ct[0]);
}
}
@ -210,8 +212,9 @@ public class ClientWorker {
return cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant));
}
public String getServerConfig(String dataId, String group, String tenant, long readTimeout)
public String[] getServerConfig(String dataId, String group, String tenant, long readTimeout)
throws NacosException {
String[] ct = new String[2];
if (StringUtils.isBlank(group)) {
group = Constants.DEFAULT_GROUP;
}
@ -236,10 +239,16 @@ public class ClientWorker {
switch (result.code) {
case HttpURLConnection.HTTP_OK:
LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.content);
return result.content;
ct[0] = result.content;
if (result.headers.containsKey(CONFIG_TYPE)) {
ct[1] = result.headers.get(CONFIG_TYPE).get(0);
} else {
ct[1] = ConfigType.TEXT.getType();
}
return ct;
case HttpURLConnection.HTTP_NOT_FOUND:
LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, null);
return null;
return ct;
case HttpURLConnection.HTTP_CONFLICT: {
LOGGER.error(
"[{}] [sub-server-error] get server config being modified concurrently, dataId={}, group={}, "
@ -517,12 +526,15 @@ public class ClientWorker {
tenant = key[2];
}
try {
String content = getServerConfig(dataId, group, tenant, 3000L);
String[] ct = getServerConfig(dataId, group, tenant, 3000L);
CacheData cache = cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant));
cache.setContent(content);
LOGGER.info("[{}] [data-received] dataId={}, group={}, tenant={}, md5={}, content={}",
cache.setContent(ct[0]);
if (null != ct[1]) {
cache.setType(ct[1]);
}
LOGGER.info("[{}] [data-received] dataId={}, group={}, tenant={}, md5={}, content={}, type={}",
agent.getName(), dataId, group, tenant, cache.getMd5(),
ContentUtils.truncateContent(content));
ContentUtils.truncateContent(ct[0]), ct[1]);
} catch (NacosException ioe) {
String message = String.format(
"[%s] [get-update] get changed config exception. dataId=%s, group=%s, tenant=%s",

View File

@ -0,0 +1,67 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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
*
* 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 com.alibaba.nacos.client.config.impl;
import com.alibaba.nacos.api.config.listener.ConfigChangeParser;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Map;
import java.util.Iterator;
/**
* ConfigChangeHandler
*
* @author rushsky518
*/
public class ConfigChangeHandler {
private static class ConfigChangeHandlerHolder {
private final static ConfigChangeHandler INSTANCE = new ConfigChangeHandler();
}
private ConfigChangeHandler() {
this.parserList = new LinkedList<ConfigChangeParser>();
ServiceLoader<ConfigChangeParser> loader = ServiceLoader.load(ConfigChangeParser.class);
Iterator<ConfigChangeParser> itr = loader.iterator();
while (itr.hasNext()) {
this.parserList.add(itr.next());
}
this.parserList.add(new PropertiesChangeParser());
this.parserList.add(new YmlChangeParser());
}
public static ConfigChangeHandler getInstance() {
return ConfigChangeHandlerHolder.INSTANCE;
}
public Map parseChangeData(String oldContent, String newContent, String type) throws IOException {
for (ConfigChangeParser changeParser: this.parserList) {
if (changeParser.isResponsibleFor(type)) {
return changeParser.doParse(oldContent, newContent, type);
}
}
return Collections.emptyMap();
}
private List<ConfigChangeParser> parserList;
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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
*
* 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 com.alibaba.nacos.client.config.impl;
import com.alibaba.nacos.api.config.ConfigChangeItem;
import com.alibaba.nacos.client.utils.StringUtils;
import java.io.IOException;
import java.io.StringReader;
import java.util.Map;
import java.util.Properties;
/**
* PropertiesChangeParser
*
* @author rushsky518
*/
public class PropertiesChangeParser extends AbstractConfigChangeParser {
public PropertiesChangeParser() {
super("properties");
}
@Override
public Map<String, ConfigChangeItem> doParse(String oldContent, String newContent, String type) throws IOException {
Properties oldProps = new Properties();
Properties newProps = new Properties();
if (StringUtils.isNotBlank(oldContent)) {
oldProps.load(new StringReader(oldContent));
}
if (StringUtils.isNotBlank(newContent)) {
newProps.load(new StringReader(newContent));
}
return filterChangeData(oldProps, newProps);
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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
*
* 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 com.alibaba.nacos.client.config.impl;
import com.alibaba.nacos.api.config.ConfigChangeItem;
import com.alibaba.nacos.client.utils.StringUtils;
import org.yaml.snakeyaml.Yaml;
import java.util.*;
/**
* YmlChangeParser
*
* @author rushsky518
*/
public class YmlChangeParser extends AbstractConfigChangeParser {
public YmlChangeParser() {
super("yaml");
}
@Override
public Map<String, ConfigChangeItem> doParse(String oldContent, String newContent, String type) {
Map<String, Object> oldMap = Collections.emptyMap();
Map<String, Object> newMap = Collections.emptyMap();
if (StringUtils.isNotBlank(oldContent)) {
oldMap = (new Yaml()).load(oldContent);
oldMap = getFlattenedMap(oldMap);
}
if (StringUtils.isNotBlank(newContent)) {
newMap = (new Yaml()).load(newContent);
newMap = getFlattenedMap(newMap);
}
return filterChangeData(oldMap, newMap);
}
private final Map<String, Object> getFlattenedMap(Map<String, Object> source) {
Map<String, Object> result = new LinkedHashMap<String, Object>(128);
buildFlattenedMap(result, source, null);
return result;
}
private void buildFlattenedMap(Map<String, Object> result, Map<String, Object> source, String path) {
for (Iterator<Map.Entry<String, Object>> itr = source.entrySet().iterator(); itr.hasNext(); ) {
Map.Entry<String, Object> e = itr.next();
String key = e.getKey();
if (StringUtils.isNotBlank(path)) {
if (e.getKey().startsWith("[")) {
key = path + key;
} else {
key = path + '.' + key;
}
}
if (e.getValue() instanceof String) {
result.put(key, e.getValue());
} else if (e.getValue() instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) e.getValue();
buildFlattenedMap(result, map, key);
} else if (e.getValue() instanceof Collection) {
@SuppressWarnings("unchecked")
Collection<Object> collection = (Collection<Object>) e.getValue();
if (collection.isEmpty()) {
result.put(key, "");
} else {
int count = 0;
for (Object object : collection) {
buildFlattenedMap(result, Collections.singletonMap("[" + (count++) + "]", object), key);
}
}
} else {
result.put(key, (e.getValue() != null ? e.getValue() : ""));
}
}
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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
*
* 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 com.alibaba.nacos.client.config.listener.impl;
import com.alibaba.nacos.api.config.listener.AbstractListener;
import com.alibaba.nacos.api.config.ConfigChangeEvent;
/**
* AbstractConfigChangeListener
*
* @author rushsky518
*/
public abstract class AbstractConfigChangeListener extends AbstractListener {
/**
* handle config change
* @param event
*/
public abstract void receiveConfigChange(final ConfigChangeEvent event);
@Override
public void receiveConfigInfo(final String configInfo) {}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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
*
* 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 com.alibaba.nacos.client.config.listener.impl;
import com.alibaba.nacos.api.config.ConfigChangeItem;
import com.alibaba.nacos.client.config.impl.ConfigChangeHandler;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
import java.util.Map;
public class ConfigChangeHandlerTest {
@Test
public void testParseProperties() throws IOException {
Map properties = ConfigChangeHandler.getInstance().parseChangeData("", "app.name = nacos", "properties");
Assert.assertEquals("nacos", ((ConfigChangeItem)properties.get("app.name")).getNewValue());
}
@Test
public void testParseYaml() throws IOException {
Map properties = ConfigChangeHandler.getInstance().parseChangeData("", "app:\n name: nacos", "yaml");
Assert.assertEquals("nacos", ((ConfigChangeItem)properties.get("app.name")).getNewValue());
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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
*
* 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 com.alibaba.nacos.client.config.listener.impl;
import com.alibaba.nacos.api.config.ConfigChangeItem;
import com.alibaba.nacos.client.config.impl.PropertiesChangeParser;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
import java.util.Map;
public class PropertiesChangeParserTest {
private PropertiesChangeParser parser = new PropertiesChangeParser();
private final String type = "properties";
@Test
public void testType() {
Assert.assertEquals(true, parser.isResponsibleFor(type));
}
@Test
public void testAddKey() throws IOException {
Map<String, ConfigChangeItem> map = parser.doParse("", "app.name = nacos", type);
Assert.assertEquals(null, map.get("app.name").getOldValue());
Assert.assertEquals("nacos", map.get("app.name").getNewValue());
}
@Test
public void testRemoveKey() throws IOException {
Map<String, ConfigChangeItem> map = parser.doParse("app.name = nacos", "", type);
Assert.assertEquals("nacos", map.get("app.name").getOldValue());
Assert.assertEquals(null, map.get("app.name").getNewValue());
}
@Test
public void testModifyKey() throws IOException {
Map<String, ConfigChangeItem> map = parser.doParse("app.name = rocketMQ", "app.name = nacos", type);
Assert.assertEquals("rocketMQ", map.get("app.name").getOldValue());
Assert.assertEquals("nacos", map.get("app.name").getNewValue());
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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
*
* 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 com.alibaba.nacos.client.config.listener.impl;
import com.alibaba.nacos.api.config.ConfigChangeItem;
import com.alibaba.nacos.client.config.impl.YmlChangeParser;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
import java.util.Map;
public class YmlChangeParserTest {
private YmlChangeParser parser = new YmlChangeParser();
private final String type = "yaml";
@Test
public void testType() {
Assert.assertEquals(true, parser.isResponsibleFor(type));
}
@Test
public void testAddKey() throws IOException {
Map<String, ConfigChangeItem> map = parser.doParse("", "app:\n name: nacos", type);
Assert.assertEquals(null, map.get("app.name").getOldValue());
Assert.assertEquals("nacos", map.get("app.name").getNewValue());
}
@Test
public void testRemoveKey() throws IOException {
Map<String, ConfigChangeItem> map = parser.doParse("app:\n name: nacos", "", type);
Assert.assertEquals("nacos", map.get("app.name").getOldValue());
Assert.assertEquals(null, map.get("app.name").getNewValue());
}
@Test
public void testModifyKey() throws IOException {
Map<String, ConfigChangeItem> map = parser.doParse("app:\n name: rocketMQ", "app:\n name: nacos", type);
Assert.assertEquals("rocketMQ", map.get("app.name").getOldValue());
Assert.assertEquals("nacos", map.get("app.name").getNewValue());
}
}

View File

@ -19,8 +19,6 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.net.UnknownHostException;
/**
* Config main
*
@ -30,7 +28,7 @@ import java.net.UnknownHostException;
@SpringBootApplication
public class Config {
public static void main(String[] args) throws UnknownHostException {
public static void main(String[] args) {
SpringApplication.run(Config.class, args);
}
}

View File

@ -1,3 +1,18 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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
*
* 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 com.alibaba.nacos.config.server.auth;
/**

View File

@ -18,6 +18,7 @@ package com.alibaba.nacos.config.server.controller;
import com.alibaba.nacos.config.server.auth.ConfigResourceParser;
import com.alibaba.nacos.config.server.constant.Constants;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.config.server.controller.parameters.SameNamespaceCloneConfigBean;
import com.alibaba.nacos.config.server.model.*;
import com.alibaba.nacos.config.server.result.ResultBuilder;
import com.alibaba.nacos.config.server.result.code.ResultCodeEnum;
@ -49,6 +50,7 @@ import java.io.IOException;
import java.net.URLDecoder;
import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;
import static com.alibaba.nacos.core.utils.SystemUtils.LOCAL_IP;
@ -69,7 +71,7 @@ public class ConfigController {
private static final String EXPORT_CONFIG_FILE_NAME_EXT = ".zip";
private static final String EXPORT_CONFIG_FILE_NAME_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final String EXPORT_CONFIG_FILE_NAME_DATE_FORMAT = "yyyyMMddHHmmss";
private final ConfigServletInner inner;
@ -542,15 +544,20 @@ public class ConfigController {
return ResultBuilder.buildSuccessResult("导入成功", saveResult);
}
@GetMapping(params = "clone=true")
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
@PostMapping(params = "clone=true")
public RestResult<Map<String, Object>> cloneConfig(HttpServletRequest request,
@RequestParam(value = "src_user", required = false) String srcUser,
@RequestParam(value = "tenant", required = true) String namespace,
@RequestParam(value = "ids", required = true) List<Long> ids,
@RequestBody(required = true)
List<SameNamespaceCloneConfigBean> configBeansList,
@RequestParam(value = "policy", defaultValue = "ABORT")
SameConfigPolicy policy) throws NacosException {
Map<String, Object> failedData = new HashMap<>(4);
if(CollectionUtils.isEmpty(configBeansList)){
failedData.put("succCount", 0);
return ResultBuilder.buildResult(ResultCodeEnum.NO_SELECTED_CONFIG, failedData);
}
configBeansList.removeAll(Collections.singleton(null));
if (NAMESPACE_PUBLIC_KEY.equalsIgnoreCase(namespace)) {
namespace = "";
@ -559,8 +566,14 @@ public class ConfigController {
return ResultBuilder.buildResult(ResultCodeEnum.NAMESPACE_NOT_EXIST, failedData);
}
ids.removeAll(Collections.singleton(null));
List<ConfigAllInfo> queryedDataList = persistService.findAllConfigInfo4Export(null, null, null, null, ids);
List<Long> idList = new ArrayList<>(configBeansList.size());
Map<Long, SameNamespaceCloneConfigBean> configBeansMap = configBeansList.stream()
.collect(Collectors.toMap(SameNamespaceCloneConfigBean::getCfgId, cfg -> {
idList.add(cfg.getCfgId());
return cfg;
},(k1, k2) -> k1));
List<ConfigAllInfo> queryedDataList = persistService.findAllConfigInfo4Export(null, null, null, null, idList);
if (queryedDataList == null || queryedDataList.isEmpty()) {
failedData.put("succCount", 0);
@ -570,11 +583,12 @@ public class ConfigController {
List<ConfigAllInfo> configInfoList4Clone = new ArrayList<>(queryedDataList.size());
for (ConfigAllInfo ci : queryedDataList) {
SameNamespaceCloneConfigBean prarmBean = configBeansMap.get(ci.getId());
ConfigAllInfo ci4save = new ConfigAllInfo();
ci4save.setTenant(namespace);
ci4save.setType(ci.getType());
ci4save.setGroup(ci.getGroup());
ci4save.setDataId(ci.getDataId());
ci4save.setGroup((prarmBean != null && StringUtils.isNotBlank(prarmBean.getGroup())) ? prarmBean.getGroup() : ci.getGroup());
ci4save.setDataId((prarmBean != null && StringUtils.isNotBlank(prarmBean.getDataId())) ? prarmBean.getDataId() : ci.getDataId());
ci4save.setContent(ci.getContent());
if (StringUtils.isNotBlank(ci.getAppName())) {
ci4save.setAppName(ci.getAppName());
@ -598,7 +612,7 @@ public class ConfigController {
configInfo.getTenant(), requestIpApp, time.getTime(),
LOCAL_IP, ConfigTraceService.PERSISTENCE_EVENT_PUB, configInfo.getContent());
}
return ResultBuilder.buildSuccessResult("导入成功", saveResult);
return ResultBuilder.buildSuccessResult("克隆成功", saveResult);
}
}

View File

@ -130,6 +130,8 @@ public class ConfigServletInner {
isBeta = true;
}
}
String configType = cacheItem.getType();
response.setHeader("Config-Type", (null != configType) ? configType : "text");
}
File file = null;
ConfigInfoBase configInfoBase = null;

View File

@ -0,0 +1,55 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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
*
* 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 com.alibaba.nacos.config.server.controller.parameters;
/**
* @author klw(213539 @ qq.com)
* @ClassName: SameNamespaceCloneConfigBean
* @Description: 同namespace克隆接口的配制bean
* @date 2019/12/13 16:10
*/
public class SameNamespaceCloneConfigBean {
private Long cfgId;
private String dataId;
private String group;
public Long getCfgId() {
return cfgId;
}
public void setCfgId(Long cfgId) {
this.cfgId = cfgId;
}
public String getDataId() {
return dataId;
}
public void setDataId(String dataId) {
this.dataId = dataId;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
}

View File

@ -109,6 +109,14 @@ public class CacheItem {
this.tagLastModifiedTs = tagLastModifiedTs;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
final String groupKey;
public volatile String md5 = Constants.NULL;
public volatile long lastModifiedTs;
@ -123,5 +131,6 @@ public class CacheItem {
public volatile Map<String, String> tagMd5;
public volatile Map<String, Long> tagLastModifiedTs;
public SimpleReadWriteLock rwLock = new SimpleReadWriteLock();
public String type;
}

View File

@ -28,6 +28,8 @@ public class ConfigInfo extends ConfigInfoBase {
private String appName;
private String type;
public ConfigInfo() {
}
@ -63,6 +65,14 @@ public class ConfigInfo extends ConfigInfoBase {
this.appName = appName;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public int hashCode() {
return super.hashCode();

View File

@ -44,6 +44,8 @@ public enum ResultCodeEnum implements IResultCode {
DATA_EMPTY(100005, "导入的文件数据为空"),
NO_SELECTED_CONFIG(100006, "没有选择任何配制"),
;

View File

@ -57,9 +57,10 @@ public class ConfigService {
/**
* 保存配置文件并缓存md5.
*/
static public boolean dump(String dataId, String group, String tenant, String content, long lastModifiedTs) {
static public boolean dump(String dataId, String group, String tenant, String content, long lastModifiedTs, String type) {
String groupKey = GroupKey2.getKey(dataId, group, tenant);
makeSure(groupKey);
CacheItem ci = makeSure(groupKey);
ci.setType(type);
final int lockResult = tryWriteLock(groupKey);
assert (lockResult != 0);

View File

@ -176,7 +176,7 @@ public class DiskUtil {
}
static public File heartBeatFile() {
return new File(NACOS_HOME, "status/heartBeat.txt");
return new File(NACOS_HOME, "status" + File.separator + "heartBeat.txt");
}
static public String relativePath(String dataId, String group) {

View File

@ -113,6 +113,7 @@ public class PersistService {
info.setGroup(rs.getString("group_id"));
info.setTenant(rs.getString("tenant_id"));
info.setAppName(rs.getString("app_name"));
info.setType(rs.getString("type"));
try {
info.setContent(rs.getString("content"));
@ -235,6 +236,11 @@ public class PersistService {
} catch (SQLException e) {
// ignore
}
try {
info.setType(rs.getString("type"));
} catch (SQLException e) {
// ignore
}
return info;
}
}
@ -1325,7 +1331,7 @@ public class PersistService {
final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName");
final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags");
String sqlCount = "select count(*) from config_info";
String sql = "select ID,data_id,group_id,tenant_id,app_name,content from config_info";
String sql = "select ID,data_id,group_id,tenant_id,app_name,content,type from config_info";
StringBuilder where = new StringBuilder(" where ");
List<String> paramList = new ArrayList<String>();
paramList.add(tenantTmp);
@ -1944,7 +1950,7 @@ public class PersistService {
public Page<ConfigInfoWrapper> findAllConfigInfoFragment(final long lastMaxId, final int pageSize) {
String select
= "SELECT id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified from config_info where id > ? "
= "SELECT id,data_id,group_id,tenant_id,app_name,content,md5,gmt_modified,type from config_info where id > ? "
+ "order by id asc limit ?,?";
PaginationHelper<ConfigInfoWrapper> helper = new PaginationHelper<ConfigInfoWrapper>();
try {
@ -2927,8 +2933,8 @@ public class PersistService {
final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant;
try {
return this.jt.queryForObject(
"SELECT ID,data_id,group_id,tenant_id,app_name,content,md5 FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?",
new Object[]{dataId, group, tenantTmp}, CONFIG_INFO_ROW_MAPPER);
"SELECT ID,data_id,group_id,tenant_id,app_name,content,md5,type FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?",
new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_ROW_MAPPER);
} catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null
return null;
} catch (CannotGetJdbcConnectionException e) {

View File

@ -187,7 +187,7 @@ class DumpProcessor implements TaskProcessor {
boolean result;
if (null != cf) {
result = ConfigService.dump(dataId, group, tenant, cf.getContent(), lastModified);
result = ConfigService.dump(dataId, group, tenant, cf.getContent(), lastModified, cf.getType());
if (result) {
ConfigTraceService.logDumpEvent(dataId, group, tenant, null, lastModified, handleIp,
@ -261,7 +261,7 @@ class DumpAllProcessor implements TaskProcessor {
}
boolean result = ConfigService.dump(cf.getDataId(), cf.getGroup(), cf.getTenant(), cf.getContent(),
cf.getLastModified());
cf.getLastModified(), cf.getType());
final String content = cf.getContent();
final String md5 = MD5.getInstance().getMD5String(content);

View File

@ -76,7 +76,7 @@ public class PaginationHelper<E> {
page.setTotalCount(rowCountInt);
if (pageNo > pageCount) {
return null;
return page;
}
final int startRow = (pageNo - 1) * pageSize;
@ -121,7 +121,7 @@ public class PaginationHelper<E> {
page.setTotalCount(rowCountInt);
if (pageNo > pageCount) {
return null;
return page;
}
String selectSQL = sqlFetchRows;
@ -162,7 +162,7 @@ public class PaginationHelper<E> {
page.setTotalCount(rowCountInt);
if (pageNo > pageCount) {
return null;
return page;
}
String selectSQL = sqlFetchRows;

View File

@ -19,7 +19,6 @@ import org.apache.commons.lang3.time.FastDateFormat;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
/**
* Time util
@ -29,13 +28,12 @@ import java.util.Date;
public class TimeUtils {
public static Timestamp getCurrentTime() {
Date date = new Date();
return new Timestamp(date.getTime());
return new Timestamp(System.currentTimeMillis());
}
public static String getCurrentTimeStr() {
Calendar c = Calendar.getInstance();
c.setTime(new Date());
c.setTimeInMillis(System.currentTimeMillis());
FastDateFormat format = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");
return format.format(c.getTime());
}

View File

@ -313,6 +313,7 @@ const I18N_CONF = {
delSelectedAlertTitle: 'Delete config',
delSelectedAlertContent: 'please select the configuration to delete',
delSuccessMsg: 'delete successful',
cloneEditableTitle: 'Modify Data Id and Group (optional)',
},
NewConfig: {
newListingMain: 'Create Configuration',

View File

@ -311,6 +311,7 @@ const I18N_CONF = {
delSelectedAlertTitle: '配置删除',
delSelectedAlertContent: '请选择要删除的配置',
delSuccessMsg: '删除成功',
cloneEditableTitle: '修改 Data Id Group (可选操作)',
},
NewConfig: {
newListingMain: '新建配置',

View File

@ -39,6 +39,7 @@ import ShowCodeing from 'components/ShowCodeing';
import DeleteDialog from 'components/DeleteDialog';
import DashboardCard from './DashboardCard';
import { getParams, setParams, request, aliwareIntl } from '@/globalLib';
import axios from 'axios';
import './index.scss';
import { LANGUAGE_KEY } from '../../../constants';
@ -689,10 +690,9 @@ class ConfigurationManagement extends React.Component {
}
exportData() {
let url =
`v1/cs/configs?export=true&group=${this.group}&tenant=${getParams('namespace')}&appName=${
this.appName
}&ids=&dataId=` + this.dataId;
let url = `v1/cs/configs?export=true&group=${this.group}&tenant=${getParams(
'namespace'
)}&appName=${this.appName}&ids=&dataId=${this.dataId}`;
window.location.href = url;
}
@ -783,19 +783,54 @@ class ConfigurationManagement extends React.Component {
}
let namespaces = data.data;
let namespaceSelectData = [];
namespaces.forEach(item => {
if (self.state.nownamespace_id !== item.namespace) {
let dataItem = {};
if (item.namespaceShowName === 'public') {
dataItem.label = 'public | public';
dataItem.value = 'public';
} else {
dataItem.label = `${item.namespaceShowName} | ${item.namespace}`;
dataItem.value = item.namespace;
}
namespaceSelectData.push(dataItem);
let namespaceSelecItemRender = item => {
if (item.isCurrent) {
return <span style={{ color: '#00AA00', 'font-weight': 'bold' }}>{item.label}</span>;
} else {
return <span>{item.label}</span>;
}
};
namespaces.forEach(item => {
let dataItem = {};
dataItem.isCurrent = false;
if (self.state.nownamespace_id === item.namespace) {
dataItem.isCurrent = true;
}
if (item.namespaceShowName === 'public') {
dataItem.label = 'public | public';
dataItem.value = 'public';
} else {
dataItem.label = `${item.namespaceShowName} | ${item.namespace}`;
dataItem.value = item.namespace;
}
namespaceSelectData.push(dataItem);
});
let editableTableData = [];
let configsTableSelectedDeepCopyed = new Map();
configsTableSelected.forEach((value, key, map) => {
let dataItem = {};
dataItem.id = key;
dataItem.dataId = value.dataId;
dataItem.group = value.group;
editableTableData.push(dataItem);
configsTableSelectedDeepCopyed.set(key, JSON.parse(JSON.stringify(value)));
});
let editableTableOnBlur = (record, type, e) => {
if (type === 1) {
configsTableSelectedDeepCopyed.get(record.id).dataId = e.target.value;
} else {
configsTableSelectedDeepCopyed.get(record.id).group = e.target.value;
}
};
let renderEditableTableCellDataId = (value, index, record) => (
<Input defaultValue={value} onBlur={editableTableOnBlur.bind(this, record, 1)} />
);
let renderEditableTableCellGroup = (value, index, record) => (
<Input defaultValue={value} onBlur={editableTableOnBlur.bind(this, record, 2)} />
);
const cloneConfirm = Dialog.confirm({
title: locale.cloningConfiguration,
footer: false,
@ -822,6 +857,7 @@ class ConfigurationManagement extends React.Component {
showSearch
hasClear={false}
mode="single"
itemRender={namespaceSelecItemRender}
dataSource={namespaceSelectData}
onChange={(value, actionType, item) => {
if (value) {
@ -866,7 +902,7 @@ class ConfigurationManagement extends React.Component {
}}
/>
</div>
<div>
<div style={{ marginBottom: 10 }}>
<Button
type={'primary'}
style={{ marginRight: 10 }}
@ -878,13 +914,21 @@ class ConfigurationManagement extends React.Component {
document.getElementById('cloneTargetSpaceSelectErr').style.display = 'none';
}
let idsStr = '';
configsTableSelected.forEach((value, key, map) => {
idsStr = `${idsStr + key},`;
let clonePostData = [];
configsTableSelectedDeepCopyed.forEach((value, key, map) => {
let postDataItem = {};
postDataItem.cfgId = key;
postDataItem.dataId = value.dataId;
postDataItem.group = value.group;
clonePostData.push(postDataItem);
});
let cloneTargetSpace = self.field.getValue('cloneTargetSpace');
let sameConfigPolicy = self.field.getValue('sameConfigPolicy');
request({
url: `v1/cs/configs?clone=true&tenant=${cloneTargetSpace}&policy=${sameConfigPolicy}&ids=${idsStr}`,
url: `v1/cs/configs?clone=true&tenant=${cloneTargetSpace}&policy=${sameConfigPolicy}&namespaceId=`,
method: 'post',
data: JSON.stringify(clonePostData),
contentType: 'application/json',
beforeSend() {
self.openLoading();
},
@ -908,6 +952,25 @@ class ConfigurationManagement extends React.Component {
{locale.startCloning}
</Button>
</div>
<div style={{ marginBottom: 10 }}>
<span style={{ color: '#00AA00', 'font-weight': 'bold' }}>
{locale.cloneEditableTitle}
</span>
</div>
<div>
<Table dataSource={editableTableData}>
<Table.Column
title="Data Id"
dataIndex="dataId"
cell={renderEditableTableCellDataId}
/>
<Table.Column
title="Group"
dataIndex="group"
cell={renderEditableTableCellGroup}
/>
</Table>
</div>
</div>
),
});
@ -1089,7 +1152,9 @@ class ConfigurationManagement extends React.Component {
rowSelection.selectedRowKeys = ids;
this.setState({ rowSelection });
configsTableSelected.clear();
records.forEach(item => configsTableSelected.set(item.id, item));
records.forEach((record, i) => {
configsTableSelected.set(record.id, record);
});
}
render() {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -17,9 +17,7 @@ package com.alibaba.nacos.core.env;
import org.apache.commons.lang3.StringUtils;
import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

View File

@ -25,6 +25,7 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
@ -147,9 +148,10 @@ public class StartingSpringApplicationRunListener implements SpringApplicationRu
}
private void logFilePath() {
LOGGER.info("Nacos Log files: {}/logs/", NACOS_HOME);
LOGGER.info("Nacos Conf files: {}/conf/", NACOS_HOME);
LOGGER.info("Nacos Data files: {}/data/", NACOS_HOME);
String[] dirNames = new String[]{"logs", "conf", "data"};
for (String dirName: dirNames) {
LOGGER.info("Nacos Log files: {}{}{}{}", NACOS_HOME, File.separatorChar, dirName, File.separatorChar);
}
}
private void logStarting() {

View File

@ -7,6 +7,8 @@
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="META-INF/logback/nacos-included.xml"/>
<include optional="true" resource="META-INF/logback/config-included.xml"/>
<include optional="true" resource="META-INF/logback/naming-included.xml"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>

View File

@ -79,6 +79,10 @@ public class DistroFilter implements Filter {
if (StringUtils.isBlank(serviceName)) {
serviceName = req.getParameter("dom");
}
if (StringUtils.isNotBlank(serviceName)) {
serviceName = serviceName.trim();
}
Method method = controllerMethodsCache.getMethod(req.getMethod(), path);
if (method == null) {

View File

@ -0,0 +1,151 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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
*
* 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 com.alibaba.nacos.test.config;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigChangeEvent;
import com.alibaba.nacos.api.config.ConfigChangeItem;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.PropertyChangeType;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.client.config.listener.impl.AbstractConfigChangeListener;
import com.alibaba.nacos.config.server.Config;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Config.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ConfigLongPollReturnChanges_ITCase {
@LocalServerPort
private int port;
private ConfigService configService;
@Before
public void init() throws NacosException {
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:" + port);
properties.put(PropertyKeyConst.CONFIG_LONG_POLL_TIMEOUT, "20000");
properties.put(PropertyKeyConst.CONFIG_RETRY_TIME, "3000");
properties.put(PropertyKeyConst.MAX_RETRY, "5");
configService = NacosFactory.createConfigService(properties);
}
@Test
public void testAdd() throws InterruptedException, NacosException {
CountDownLatch latch = new CountDownLatch(1);
final String dataId = "test" + System.currentTimeMillis();
final String group = "DEFAULT_GROUP";
final String content = "config data";
configService.addListener(dataId, group, new AbstractConfigChangeListener() {
@Override
public void receiveConfigChange(ConfigChangeEvent event) {
ConfigChangeItem cci = event.getChangeItem("content");
Assert.assertEquals(null, cci.getOldValue());
Assert.assertEquals(content, cci.getNewValue());
Assert.assertEquals(PropertyChangeType.ADDED, cci.getType());
System.out.println(cci);
latch.countDown();
}
});
configService.publishConfig(dataId, group, content);
latch.await();
}
@Test
public void testModify() throws InterruptedException, NacosException {
CountDownLatch latch = new CountDownLatch(1);
final String dataId = "test" + System.currentTimeMillis();
final String group = "DEFAULT_GROUP";
final String oldData = "old data";
final String newData = "new data";
configService.publishConfig(dataId, group, oldData);
// query config immediately may return null
String config = null;
do {
TimeUnit.SECONDS.sleep(1);
config = configService.getConfig(dataId, group, 50);
} while(null == config);
configService.addListener(dataId, group, new AbstractConfigChangeListener() {
@Override
public void receiveConfigChange(ConfigChangeEvent event) {
ConfigChangeItem cci = event.getChangeItem("content");
Assert.assertEquals(oldData, cci.getOldValue());
Assert.assertEquals(newData, cci.getNewValue());
Assert.assertEquals(PropertyChangeType.MODIFIED, cci.getType());
System.out.println(cci);
latch.countDown();
}
});
configService.publishConfig(dataId, group, newData);
latch.await();
}
@Test
public void testDelete() throws InterruptedException, NacosException {
CountDownLatch latch = new CountDownLatch(1);
final String dataId = "test" + System.currentTimeMillis();
final String group = "DEFAULT_GROUP";
final String oldData = "old data";
configService.publishConfig(dataId, group, oldData);
// query config immediately may return null
String config = null;
do {
TimeUnit.SECONDS.sleep(1);
config = configService.getConfig(dataId, group, 50);
} while(null == config);
configService.addListener(dataId, group, new AbstractConfigChangeListener() {
@Override
public void receiveConfigChange(ConfigChangeEvent event) {
ConfigChangeItem cci = event.getChangeItem("content");
Assert.assertEquals(oldData, cci.getOldValue());
Assert.assertEquals(null, cci.getNewValue());
Assert.assertEquals(PropertyChangeType.DELETED, cci.getType());
System.out.println(cci);
latch.countDown();
}
});
configService.removeConfig(dataId, group);
latch.await();
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* 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
*
* 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 com.alibaba.nacos.test.config;
import com.alibaba.nacos.api.config.ConfigChangeItem;
import com.alibaba.nacos.api.config.PropertyChangeType;
import com.alibaba.nacos.api.config.listener.ConfigChangeParser;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class TextChangeParser implements ConfigChangeParser {
@Override
public boolean isResponsibleFor(String type) {
return (null == type || "text".equalsIgnoreCase(type));
}
@Override
public Map<String, ConfigChangeItem> doParse(String oldContent, String newContent, String type) throws IOException {
Map<String, ConfigChangeItem> map = new HashMap<>(4);
final String key = "content";
ConfigChangeItem cci = new ConfigChangeItem(key, oldContent, newContent);
if (null == oldContent && null != newContent) {
cci.setType(PropertyChangeType.ADDED);
} else if (null != oldContent && null != newContent && !oldContent.equals(newContent)) {
cci.setType(PropertyChangeType.MODIFIED);
} else if (null != oldContent && null == newContent) {
cci.setType(PropertyChangeType.DELETED);
}
map.put(key, cci);
return map;
}
}

View File

@ -0,0 +1 @@
com.alibaba.nacos.test.config.TextChangeParser