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(); } }