<NME> ConfigController.java
<BEF> /*
 * Copyright 2022 Apollo Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
import java.util.List;
import java.util.Map;
import java.util.Objects;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 * 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.ctrip.framework.apollo.configservice.controller;

import com.ctrip.framework.apollo.biz.entity.Release;
import com.ctrip.framework.apollo.common.entity.AppNamespace;
import com.ctrip.framework.apollo.common.utils.WebUtils;
import com.ctrip.framework.apollo.configservice.service.AppNamespaceServiceWithCache;
import com.ctrip.framework.apollo.configservice.service.config.ConfigService;
import com.ctrip.framework.apollo.configservice.util.InstanceConfigAuditUtil;
import com.ctrip.framework.apollo.configservice.util.NamespaceUtil;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.dto.ApolloConfig;
import com.ctrip.framework.apollo.core.dto.ApolloNotificationMessages;
import com.ctrip.framework.apollo.tracer.Tracer;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author Jason Song(song_s@ctrip.com)
 */
@RestController

  private static final Type configurationTypeReference = new TypeToken<Map<String, String>>() {
      }.getType();
  private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);

  @RequestMapping(value = "/{appId}/{clusterName}/{namespace:.+}", method = RequestMethod.GET)
  public ApolloConfig queryConfig(@PathVariable String appId, @PathVariable String clusterName,
  private final InstanceConfigAuditUtil instanceConfigAuditUtil;
  private final Gson gson;

  private static final Type configurationTypeReference = new TypeToken<Map<String, String>>() {
      }.getType();

  public ConfigController(
      final ConfigService configService,
      final AppNamespaceServiceWithCache appNamespaceService,
      final NamespaceUtil namespaceUtil,
      final InstanceConfigAuditUtil instanceConfigAuditUtil,
      final Gson gson) {
    this.configService = configService;
    this.appNamespaceService = appNamespaceService;
    this.namespaceUtil = namespaceUtil;
    this.instanceConfigAuditUtil = instanceConfigAuditUtil;
    this.gson = gson;
  }

  @GetMapping(value = "/{appId}/{clusterName}/{namespace:.+}")
  public ApolloConfig queryConfig(@PathVariable String appId, @PathVariable String clusterName,
                                  @PathVariable String namespace,
                                  @RequestParam(value = "dataCenter", required = false) String dataCenter,
                                  @RequestParam(value = "releaseKey", defaultValue = "-1") String clientSideReleaseKey,
                                  @RequestParam(value = "ip", required = false) String clientIp,
                                  @RequestParam(value = "label", required = false) String clientLabel,
                                  @RequestParam(value = "messages", required = false) String messagesAsString,
                                  HttpServletRequest request, HttpServletResponse response) throws IOException {
    String originalNamespace = namespace;
    //strip out .properties suffix
    namespace = namespaceUtil.filterNamespaceName(namespace);
    //fix the character case issue, such as FX.apollo <-> fx.apollo
    namespace = namespaceUtil.normalizeNamespace(appId, namespace);

    if (Strings.isNullOrEmpty(clientIp)) {
      clientIp = WebUtils.tryToGetClientIp(request);
    }

    ApolloNotificationMessages clientMessages = transformMessages(messagesAsString);

    List<Release> releases = Lists.newLinkedList();

    String appClusterNameLoaded = clusterName;
    if (!ConfigConsts.NO_APPID_PLACEHOLDER.equalsIgnoreCase(appId)) {
      Release currentAppRelease = configService.loadConfig(appId, clientIp, clientLabel, appId, clusterName, namespace,
          dataCenter, clientMessages);

      if (currentAppRelease != null) {
        releases.add(currentAppRelease);
        //we have cluster search process, so the cluster name might be overridden

    auditReleases(appId, clusterName, dataCenter, clientIp, releases);

    String mergedReleaseKey = FluentIterable.from(releases).transform(
        input -> input.getReleaseKey()).join(STRING_JOINER);

    if (mergedReleaseKey.equals(clientSideReleaseKey)) {
      // Client side configuration is the same with server side, return 304
      if (Objects.nonNull(publicRelease)) {
        releases.add(publicRelease);
      }
    }

    if (releases.isEmpty()) {
      response.sendError(HttpServletResponse.SC_NOT_FOUND,
          String.format(
              "Could not load configurations with appId: %s, clusterName: %s, namespace: %s",
              appId, clusterName, originalNamespace));
      Tracer.logEvent("Apollo.Config.NotFound",
          assembleKey(appId, clusterName, originalNamespace, dataCenter));
      return null;
    }

    auditReleases(appId, clusterName, dataCenter, clientIp, releases);

    String mergedReleaseKey = releases.stream().map(Release::getReleaseKey)
            .collect(Collectors.joining(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR));

    if (mergedReleaseKey.equals(clientSideReleaseKey)) {
      // Client side configuration is the same with server side, return 304
      response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
      Tracer.logEvent("Apollo.Config.NotModified",
          assembleKey(appId, appClusterNameLoaded, originalNamespace, dataCenter));
      return null;
    }

    ApolloConfig apolloConfig = new ApolloConfig(appId, appClusterNameLoaded, originalNamespace,
        mergedReleaseKey);
    apolloConfig.setConfigurations(mergeReleaseConfigurations(releases));

    Tracer.logEvent("Apollo.Config.Found", assembleKey(appId, appClusterNameLoaded,
        originalNamespace, dataCenter));
    return apolloConfig;
  }

  private boolean namespaceBelongsToAppId(String appId, String namespaceName) {
    //Every app has an 'application' namespace
    if (Objects.equals(ConfigConsts.NAMESPACE_APPLICATION, namespaceName)) {
      return true;
    }

    //if no appId is present, then no other namespace belongs to it
    if (ConfigConsts.NO_APPID_PLACEHOLDER.equalsIgnoreCase(appId)) {
      return false;
    }

    AppNamespace appNamespace = appNamespaceService.findByAppIdAndNamespace(appId, namespaceName);

    return appNamespace != null;
  }

  /**
   * @param clientAppId the application which uses public config
   * @param namespace   the namespace
   * @param dataCenter  the datacenter
   */
  private Release findPublicConfig(String clientAppId, String clientIp, String clientLabel, String clusterName,
                                   String namespace, String dataCenter, ApolloNotificationMessages clientMessages) {
    AppNamespace appNamespace = appNamespaceService.findPublicNamespaceByName(namespace);

    //check whether the namespace's appId equals to current one
    if (Objects.isNull(appNamespace) || Objects.equals(clientAppId, appNamespace.getAppId())) {
      return null;
    if (!Strings.isNullOrEmpty(dataCenter)) {
      keyParts.add(dataCenter);
    }
    return STRING_JOINER.join(keyParts);
  }

  private void auditReleases(String appId, String cluster, String dataCenter, String clientIp,

  /**
   * Merge configurations of releases.
   * Release in lower index override those in higher index
   */
  Map<String, String> mergeReleaseConfigurations(List<Release> releases) {
    Map<String, String> result = Maps.newLinkedHashMap();
    for (Release release : Lists.reverse(releases)) {
      result.putAll(gson.fromJson(release.getConfigurations(), configurationTypeReference));
    }
    return result;
  }

  private String assembleKey(String appId, String cluster, String namespace, String dataCenter) {
    List<String> keyParts = Lists.newArrayList(appId, cluster, namespace);
    if (!Strings.isNullOrEmpty(dataCenter)) {
      keyParts.add(dataCenter);
    }
    return String.join(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR, keyParts);
  }

  private void auditReleases(String appId, String cluster, String dataCenter, String clientIp,
                             List<Release> releases) {
    if (Strings.isNullOrEmpty(clientIp)) {
      //no need to audit instance config when there is no ip
      return;
    }
    for (Release release : releases) {
      instanceConfigAuditUtil.audit(appId, cluster, dataCenter, clientIp, release.getAppId(),
          release.getClusterName(),
          release.getNamespaceName(), release.getReleaseKey());
    }
  }

  ApolloNotificationMessages transformMessages(String messagesAsString) {
    ApolloNotificationMessages notificationMessages = null;
    if (!Strings.isNullOrEmpty(messagesAsString)) {
      try {
        notificationMessages = gson.fromJson(messagesAsString, ApolloNotificationMessages.class);
      } catch (Throwable ex) {
        Tracer.logError(ex);
      }
    }

    return notificationMessages;
  }
}

<MSG> Merge pull request #784 from Acceml/humingreq

use java8 api to replace guava joining List to String
<DFF> @@ -5,6 +5,7 @@ import java.lang.reflect.Type;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -26,10 +27,8 @@ import com.ctrip.framework.apollo.core.ConfigConsts;
 import com.ctrip.framework.apollo.core.dto.ApolloConfig;
 import com.ctrip.framework.apollo.core.dto.ApolloNotificationMessages;
 import com.ctrip.framework.apollo.tracer.Tracer;
-import com.google.common.base.Joiner;
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
-import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.gson.Gson;
@@ -56,7 +55,6 @@ public class ConfigController {
 
   private static final Type configurationTypeReference = new TypeToken<Map<String, String>>() {
       }.getType();
-  private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);
 
   @RequestMapping(value = "/{appId}/{clusterName}/{namespace:.+}", method = RequestMethod.GET)
   public ApolloConfig queryConfig(@PathVariable String appId, @PathVariable String clusterName,
@@ -113,8 +111,8 @@ public class ConfigController {
 
     auditReleases(appId, clusterName, dataCenter, clientIp, releases);
 
-    String mergedReleaseKey = FluentIterable.from(releases).transform(
-        input -> input.getReleaseKey()).join(STRING_JOINER);
+    String mergedReleaseKey = releases.stream().map(Release::getReleaseKey)
+            .collect(Collectors.joining(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR));
 
     if (mergedReleaseKey.equals(clientSideReleaseKey)) {
       // Client side configuration is the same with server side, return 304
@@ -186,7 +184,7 @@ public class ConfigController {
     if (!Strings.isNullOrEmpty(dataCenter)) {
       keyParts.add(dataCenter);
     }
-    return STRING_JOINER.join(keyParts);
+    return keyParts.stream().collect(Collectors.joining(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR));
   }
 
   private void auditReleases(String appId, String cluster, String dataCenter, String clientIp,

{'commit': 'diff --git a/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/controller/ConfigController.java b/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/controller/ConfigController.java\nold mode 100644\nnew mode 100755\nindex ca4679f1e..add5b16fd\n--- a/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/controller/ConfigController.java\n+++ b/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/controller/ConfigController.java\n@@ -5,6 +5,7 @@ import java.lang.reflect.Type;\n import java.util.List;\n import java.util.Map;\n import java.util.Objects;\n+import java.util.stream.Collectors;\n \n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n@@ -26,10 +27,8 @@ import com.ctrip.framework.apollo.core.ConfigConsts;\n import com.ctrip.framework.apollo.core.dto.ApolloConfig;\n import com.ctrip.framework.apollo.core.dto.ApolloNotificationMessages;\n import com.ctrip.framework.apollo.tracer.Tracer;\n-import com.google.common.base.Joiner;\n import com.google.common.base.Splitter;\n import com.google.common.base.Strings;\n-import com.google.common.collect.FluentIterable;\n import com.google.common.collect.Lists;\n import com.google.common.collect.Maps;\n import com.google.gson.Gson;\n@@ -56,7 +55,6 @@ public class ConfigController {\n \n   private static final Type configurationTypeReference = new TypeToken<Map<String, String>>() {\n       }.getType();\n-  private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);\n \n   @RequestMapping(value = "/{appId}/{clusterName}/{namespace:.+}", method = RequestMethod.GET)\n   public ApolloConfig queryConfig(@PathVariable String appId, @PathVariable String clusterName,\n@@ -113,8 +111,8 @@ public class ConfigController {\n \n     auditReleases(appId, clusterName, dataCenter, clientIp, releases);\n \n-    String mergedReleaseKey = FluentIterable.from(releases).transform(\n-        input -> input.getReleaseKey()).join(STRING_JOINER);\n+    String mergedReleaseKey = releases.stream().map(Release::getReleaseKey)\n+            .collect(Collectors.joining(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR));\n \n     if (mergedReleaseKey.equals(clientSideReleaseKey)) {\n       // Client side configuration is the same with server side, return 304\n@@ -186,7 +184,7 @@ public class ConfigController {\n     if (!Strings.isNullOrEmpty(dataCenter)) {\n       keyParts.add(dataCenter);\n     }\n-    return STRING_JOINER.join(keyParts);\n+    return keyParts.stream().collect(Collectors.joining(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR));\n   }\n \n   private void auditReleases(String appId, String cluster, String dataCenter, String clientIp,\n', 'commit_subject': 'Merge pull request #784 from Acceml/humingreq', 'repo_name': 'ctripcorp/apollo', 'committer': {'name': 'GitHub', 'email': '1505422b2465e9a84f6fdfaa161078890c593f06@github.com', 'time_sec': '1507822563', 'tz_offset': '480', 'date': {'seconds': '1507822563'}}, 'commit_tree': 'c2efa4188e86f333af18f1319678b4757c76a2f7', 'commit_message': 'Merge pull request #784 from Acceml/humingreq\n\nuse java8 api to replace guava joining List to String', 'language_name': 'JavaScript', 'license': 'apache-2.0', 'hunks': '@@ -5,6 +5,7 @@ import java.lang.reflect.Type;\n import java.util.List;\n import java.util.Map;\n import java.util.Objects;\n+import java.util.stream.Collectors;\n \n import javax.servlet.http.HttpServletRequest;\n import javax.servlet.http.HttpServletResponse;\n@@ -26,10 +27,8 @@ import com.ctrip.framework.apollo.core.ConfigConsts;\n import com.ctrip.framework.apollo.core.dto.ApolloConfig;\n import com.ctrip.framework.apollo.core.dto.ApolloNotificationMessages;\n import com.ctrip.framework.apollo.tracer.Tracer;\n-import com.google.common.base.Joiner;\n import com.google.common.base.Splitter;\n import com.google.common.base.Strings;\n-import com.google.common.collect.FluentIterable;\n import com.google.common.collect.Lists;\n import com.google.common.collect.Maps;\n import com.google.gson.Gson;\n@@ -56,7 +55,6 @@ public class ConfigController {\n \n   private static final Type configurationTypeReference = new TypeToken<Map<String, String>>() {\n       }.getType();\n-  private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);\n \n   @RequestMapping(value = "/{appId}/{clusterName}/{namespace:.+}", method = RequestMethod.GET)\n   public ApolloConfig queryConfig(@PathVariable String appId, @PathVariable String clusterName,\n@@ -113,8 +111,8 @@ public class ConfigController {\n \n     auditReleases(appId, clusterName, dataCenter, clientIp, releases);\n \n-    String mergedReleaseKey = FluentIterable.from(releases).transform(\n-        input -> input.getReleaseKey()).join(STRING_JOINER);\n+    String mergedReleaseKey = releases.stream().map(Release::getReleaseKey)\n+            .collect(Collectors.joining(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR));\n \n     if (mergedReleaseKey.equals(clientSideReleaseKey)) {\n       // Client side configuration is the same with server side, return 304\n@@ -186,7 +184,7 @@ public class ConfigController {\n     if (!Strings.isNullOrEmpty(dataCenter)) {\n       keyParts.add(dataCenter);\n     }\n-    return STRING_JOINER.join(keyParts);\n+    return keyParts.stream().collect(Collectors.joining(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR));\n   }\n \n   private void auditReleases(String appId, String cluster, String dataCenter, String clientIp,\n', 'addition_count': 4, 'deletion_count': 6, 'src_file': 'a/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/controller/ConfigController.java', 'tgt_file': 'b/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/controller/ConfigController.java', 'file_extension': '.java', 'before_file': '/*\n * Copyright 2022 Apollo Authors\n *\n * Licensed under the Apache License, Version 2.0 (the "License");\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\npackage com.ctrip.framework.apollo.configservice.controller;\n\nimport com.ctrip.framework.apollo.biz.entity.Release;\nimport com.ctrip.framework.apollo.common.entity.AppNamespace;\nimport com.ctrip.framework.apollo.common.utils.WebUtils;\nimport com.ctrip.framework.apollo.configservice.service.AppNamespaceServiceWithCache;\nimport com.ctrip.framework.apollo.configservice.service.config.ConfigService;\nimport com.ctrip.framework.apollo.configservice.util.InstanceConfigAuditUtil;\nimport com.ctrip.framework.apollo.configservice.util.NamespaceUtil;\nimport com.ctrip.framework.apollo.core.ConfigConsts;\nimport com.ctrip.framework.apollo.core.dto.ApolloConfig;\nimport com.ctrip.framework.apollo.core.dto.ApolloNotificationMessages;\nimport com.ctrip.framework.apollo.tracer.Tracer;\nimport com.google.common.base.Joiner;\nimport com.google.common.base.Splitter;\nimport com.google.common.base.Strings;\nimport com.google.common.collect.FluentIterable;\nimport com.google.common.collect.Lists;\nimport com.google.common.collect.Maps;\nimport com.google.gson.Gson;\nimport com.google.gson.reflect.TypeToken;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.lang.reflect.Type;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\n\n/**\n * @author Jason Song(song_s@ctrip.com)\n */\n@RestController\n\n  private static final Type configurationTypeReference = new TypeToken<Map<String, String>>() {\n      }.getType();\n  private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);\n\n  @RequestMapping(value = "/{appId}/{clusterName}/{namespace:.+}", method = RequestMethod.GET)\n  public ApolloConfig queryConfig(@PathVariable String appId, @PathVariable String clusterName,\n  private final InstanceConfigAuditUtil instanceConfigAuditUtil;\n  private final Gson gson;\n\n  private static final Type configurationTypeReference = new TypeToken<Map<String, String>>() {\n      }.getType();\n\n  public ConfigController(\n      final ConfigService configService,\n      final AppNamespaceServiceWithCache appNamespaceService,\n      final NamespaceUtil namespaceUtil,\n      final InstanceConfigAuditUtil instanceConfigAuditUtil,\n      final Gson gson) {\n    this.configService = configService;\n    this.appNamespaceService = appNamespaceService;\n    this.namespaceUtil = namespaceUtil;\n    this.instanceConfigAuditUtil = instanceConfigAuditUtil;\n    this.gson = gson;\n  }\n\n  @GetMapping(value = "/{appId}/{clusterName}/{namespace:.+}")\n  public ApolloConfig queryConfig(@PathVariable String appId, @PathVariable String clusterName,\n                                  @PathVariable String namespace,\n                                  @RequestParam(value = "dataCenter", required = false) String dataCenter,\n                                  @RequestParam(value = "releaseKey", defaultValue = "-1") String clientSideReleaseKey,\n                                  @RequestParam(value = "ip", required = false) String clientIp,\n                                  @RequestParam(value = "label", required = false) String clientLabel,\n                                  @RequestParam(value = "messages", required = false) String messagesAsString,\n                                  HttpServletRequest request, HttpServletResponse response) throws IOException {\n    String originalNamespace = namespace;\n    //strip out .properties suffix\n    namespace = namespaceUtil.filterNamespaceName(namespace);\n    //fix the character case issue, such as FX.apollo <-> fx.apollo\n    namespace = namespaceUtil.normalizeNamespace(appId, namespace);\n\n    if (Strings.isNullOrEmpty(clientIp)) {\n      clientIp = WebUtils.tryToGetClientIp(request);\n    }\n\n    ApolloNotificationMessages clientMessages = transformMessages(messagesAsString);\n\n    List<Release> releases = Lists.newLinkedList();\n\n    String appClusterNameLoaded = clusterName;\n    if (!ConfigConsts.NO_APPID_PLACEHOLDER.equalsIgnoreCase(appId)) {\n      Release currentAppRelease = configService.loadConfig(appId, clientIp, clientLabel, appId, clusterName, namespace,\n          dataCenter, clientMessages);\n\n      if (currentAppRelease != null) {\n        releases.add(currentAppRelease);\n        //we have cluster search process, so the cluster name might be overridden\n\n    auditReleases(appId, clusterName, dataCenter, clientIp, releases);\n\n    String mergedReleaseKey = FluentIterable.from(releases).transform(\n        input -> input.getReleaseKey()).join(STRING_JOINER);\n\n    if (mergedReleaseKey.equals(clientSideReleaseKey)) {\n      // Client side configuration is the same with server side, return 304\n      if (Objects.nonNull(publicRelease)) {\n        releases.add(publicRelease);\n      }\n    }\n\n    if (releases.isEmpty()) {\n      response.sendError(HttpServletResponse.SC_NOT_FOUND,\n          String.format(\n              "Could not load configurations with appId: %s, clusterName: %s, namespace: %s",\n              appId, clusterName, originalNamespace));\n      Tracer.logEvent("Apollo.Config.NotFound",\n          assembleKey(appId, clusterName, originalNamespace, dataCenter));\n      return null;\n    }\n\n    auditReleases(appId, clusterName, dataCenter, clientIp, releases);\n\n    String mergedReleaseKey = releases.stream().map(Release::getReleaseKey)\n            .collect(Collectors.joining(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR));\n\n    if (mergedReleaseKey.equals(clientSideReleaseKey)) {\n      // Client side configuration is the same with server side, return 304\n      response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);\n      Tracer.logEvent("Apollo.Config.NotModified",\n          assembleKey(appId, appClusterNameLoaded, originalNamespace, dataCenter));\n      return null;\n    }\n\n    ApolloConfig apolloConfig = new ApolloConfig(appId, appClusterNameLoaded, originalNamespace,\n        mergedReleaseKey);\n    apolloConfig.setConfigurations(mergeReleaseConfigurations(releases));\n\n    Tracer.logEvent("Apollo.Config.Found", assembleKey(appId, appClusterNameLoaded,\n        originalNamespace, dataCenter));\n    return apolloConfig;\n  }\n\n  private boolean namespaceBelongsToAppId(String appId, String namespaceName) {\n    //Every app has an \'application\' namespace\n    if (Objects.equals(ConfigConsts.NAMESPACE_APPLICATION, namespaceName)) {\n      return true;\n    }\n\n    //if no appId is present, then no other namespace belongs to it\n    if (ConfigConsts.NO_APPID_PLACEHOLDER.equalsIgnoreCase(appId)) {\n      return false;\n    }\n\n    AppNamespace appNamespace = appNamespaceService.findByAppIdAndNamespace(appId, namespaceName);\n\n    return appNamespace != null;\n  }\n\n  /**\n   * @param clientAppId the application which uses public config\n   * @param namespace   the namespace\n   * @param dataCenter  the datacenter\n   */\n  private Release findPublicConfig(String clientAppId, String clientIp, String clientLabel, String clusterName,\n                                   String namespace, String dataCenter, ApolloNotificationMessages clientMessages) {\n    AppNamespace appNamespace = appNamespaceService.findPublicNamespaceByName(namespace);\n\n    //check whether the namespace\'s appId equals to current one\n    if (Objects.isNull(appNamespace) || Objects.equals(clientAppId, appNamespace.getAppId())) {\n      return null;\n    if (!Strings.isNullOrEmpty(dataCenter)) {\n      keyParts.add(dataCenter);\n    }\n    return STRING_JOINER.join(keyParts);\n  }\n\n  private void auditReleases(String appId, String cluster, String dataCenter, String clientIp,\n\n  /**\n   * Merge configurations of releases.\n   * Release in lower index override those in higher index\n   */\n  Map<String, String> mergeReleaseConfigurations(List<Release> releases) {\n    Map<String, String> result = Maps.newLinkedHashMap();\n    for (Release release : Lists.reverse(releases)) {\n      result.putAll(gson.fromJson(release.getConfigurations(), configurationTypeReference));\n    }\n    return result;\n  }\n\n  private String assembleKey(String appId, String cluster, String namespace, String dataCenter) {\n    List<String> keyParts = Lists.newArrayList(appId, cluster, namespace);\n    if (!Strings.isNullOrEmpty(dataCenter)) {\n      keyParts.add(dataCenter);\n    }\n    return String.join(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR, keyParts);\n  }\n\n  private void auditReleases(String appId, String cluster, String dataCenter, String clientIp,\n                             List<Release> releases) {\n    if (Strings.isNullOrEmpty(clientIp)) {\n      //no need to audit instance config when there is no ip\n      return;\n    }\n    for (Release release : releases) {\n      instanceConfigAuditUtil.audit(appId, cluster, dataCenter, clientIp, release.getAppId(),\n          release.getClusterName(),\n          release.getNamespaceName(), release.getReleaseKey());\n    }\n  }\n\n  ApolloNotificationMessages transformMessages(String messagesAsString) {\n    ApolloNotificationMessages notificationMessages = null;\n    if (!Strings.isNullOrEmpty(messagesAsString)) {\n      try {\n        notificationMessages = gson.fromJson(messagesAsString, ApolloNotificationMessages.class);\n      } catch (Throwable ex) {\n        Tracer.logError(ex);\n      }\n    }\n\n    return notificationMessages;\n  }\n}\n', 'message': 'Merge pull request #784 from Acceml/humingreq\n\nuse java8 api to replace guava joining List to String', 'author_name': 'GitHub', 'lang': 'java'}