package com.ctrip.framework.apollo.configservice.util;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.ctrip.framework.apollo.configservice.service.AppNamespaceServiceWithCache;
import com.ctrip.framework.apollo.common.entity.AppNamespace;
import com.ctrip.framework.apollo.core.ConfigConsts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@Component
public class WatchKeysUtil {
private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);
@Autowired
private AppNamespaceServiceWithCache appNamespaceService;
/**
* Assemble watch keys for the given appId, cluster, namespace, dataCenter combination
*/
public Set<String> assembleAllWatchKeys(String appId, String clusterName, String namespace,
String dataCenter) {
Multimap<String, String> watchedKeysMap =
assembleAllWatchKeys(appId, clusterName, Sets.newHashSet(namespace), dataCenter);
return Sets.newHashSet(watchedKeysMap.get(namespace));
}
/**
* Assemble watch keys for the given appId, cluster, namespaces, dataCenter combination
*
* @return a multimap with namespace as the key and watch keys as the value
*/
public Multimap<String, String> assembleAllWatchKeys(String appId, String clusterName,
Set<String> namespaces,
String dataCenter) {
Multimap<String, String> watchedKeysMap =
assembleWatchKeys(appId, clusterName, namespaces, dataCenter);
//Every app has an 'application' namespace
if (!(namespaces.size() == 1 && namespaces.contains(ConfigConsts.NAMESPACE_APPLICATION))) {
Set<String> namespacesBelongToAppId = namespacesBelongToAppId(appId, namespaces);
Set<String> publicNamespaces = Sets.difference(namespaces, namespacesBelongToAppId);
//Listen on more namespaces if it's a public namespace
if (!publicNamespaces.isEmpty()) {
watchedKeysMap
.putAll(findPublicConfigWatchKeys(appId, clusterName, publicNamespaces, dataCenter));
}
}
return watchedKeysMap;
}
private Multimap<String, String> findPublicConfigWatchKeys(String applicationId,
String clusterName,
Set<String> namespaces,
String dataCenter) {
Multimap<String, String> watchedKeysMap = HashMultimap.create();
List<AppNamespace> appNamespaces = appNamespaceService.findPublicNamespacesByNames(namespaces);
for (AppNamespace appNamespace : appNamespaces) {
//check whether the namespace's appId equals to current one
if (Objects.equals(applicationId, appNamespace.getAppId())) {
continue;
}
String publicConfigAppId = appNamespace.getAppId();
watchedKeysMap.putAll(appNamespace.getName(),
assembleWatchKeys(publicConfigAppId, clusterName, appNamespace.getName(), dataCenter));
}
return watchedKeysMap;
}
private String assembleKey(String appId, String cluster, String namespace) {
return STRING_JOINER.join(appId, cluster, namespace);
}
private Set<String> assembleWatchKeys(String appId, String clusterName, String namespace,
String dataCenter) {
if (ConfigConsts.NO_APPID_PLACEHOLDER.equalsIgnoreCase(appId)) {
return Collections.emptySet();
}
Set<String> watchedKeys = Sets.newHashSet();
//watch specified cluster config change
if (!Objects.equals(ConfigConsts.CLUSTER_NAME_DEFAULT, clusterName)) {
watchedKeys.add(assembleKey(appId, clusterName, namespace));
}
//watch data center config change
if (!Strings.isNullOrEmpty(dataCenter) && !Objects.equals(dataCenter, clusterName)) {
watchedKeys.add(assembleKey(appId, dataCenter, namespace));
}
//watch default cluster config change
watchedKeys.add(assembleKey(appId, ConfigConsts.CLUSTER_NAME_DEFAULT, namespace));
return watchedKeys;
}
private Multimap<String, String> assembleWatchKeys(String appId, String clusterName,
Set<String> namespaces,
String dataCenter) {
Multimap<String, String> watchedKeysMap = HashMultimap.create();
for (String namespace : namespaces) {
watchedKeysMap
.putAll(namespace, assembleWatchKeys(appId, clusterName, namespace, dataCenter));
}
return watchedKeysMap;
}
private Set<String> namespacesBelongToAppId(String appId, Set<String> namespaces) {
if (ConfigConsts.NO_APPID_PLACEHOLDER.equalsIgnoreCase(appId)) {
return Collections.emptySet();
}
List<AppNamespace> appNamespaces =
appNamespaceService.findByAppIdAndNamespaces(appId, namespaces);
if (appNamespaces == null || appNamespaces.isEmpty()) {
return Collections.emptySet();
}
return FluentIterable.from(appNamespaces).transform(AppNamespace::getName).toSet();
}
}