Coverage Report - com.ctrip.framework.apollo.configservice.controller.ConfigController
 
Classes in this File Line Coverage Branch Coverage Complexity
ConfigController
100%
58/58
96%
27/28
0
ConfigController$1
100%
1/1
N/A
0
 
 1  
 package com.ctrip.framework.apollo.configservice.controller;
 2  
 
 3  
 import com.google.common.base.Joiner;
 4  
 import com.google.common.base.Strings;
 5  
 import com.google.common.collect.FluentIterable;
 6  
 import com.google.common.collect.Lists;
 7  
 import com.google.common.collect.Maps;
 8  
 import com.google.gson.Gson;
 9  
 import com.google.gson.reflect.TypeToken;
 10  
 
 11  
 import com.ctrip.framework.apollo.common.entity.AppNamespace;
 12  
 import com.ctrip.framework.apollo.biz.entity.Release;
 13  
 import com.ctrip.framework.apollo.biz.service.AppNamespaceService;
 14  
 import com.ctrip.framework.apollo.biz.service.ConfigService;
 15  
 import com.ctrip.framework.apollo.core.ConfigConsts;
 16  
 import com.ctrip.framework.apollo.core.dto.ApolloConfig;
 17  
 import com.dianping.cat.Cat;
 18  
 
 19  
 import org.springframework.beans.factory.annotation.Autowired;
 20  
 import org.springframework.web.bind.annotation.PathVariable;
 21  
 import org.springframework.web.bind.annotation.RequestMapping;
 22  
 import org.springframework.web.bind.annotation.RequestMethod;
 23  
 import org.springframework.web.bind.annotation.RequestParam;
 24  
 import org.springframework.web.bind.annotation.RestController;
 25  
 
 26  
 import java.io.IOException;
 27  
 import java.lang.reflect.Type;
 28  
 import java.util.List;
 29  
 import java.util.Map;
 30  
 import java.util.Objects;
 31  
 
 32  
 import javax.servlet.http.HttpServletResponse;
 33  
 
 34  
 /**
 35  
  * @author Jason Song(song_s@ctrip.com)
 36  
  */
 37  
 @RestController
 38  
 @RequestMapping("/configs")
 39  12
 public class ConfigController {
 40  
   @Autowired
 41  
   private ConfigService configService;
 42  
   @Autowired
 43  
   private AppNamespaceService appNamespaceService;
 44  
 
 45  1
   private static final Gson gson = new Gson();
 46  1
   private static final Type configurationTypeReference =
 47  1
       new TypeToken<Map<java.lang.String, java.lang.String>>() {
 48  1
       }.getType();
 49  1
   private static final Joiner STRING_JOINER = Joiner.on(ConfigConsts.CLUSTER_NAMESPACE_SEPARATOR);
 50  
 
 51  
   @RequestMapping(value = "/{appId}/{clusterName}/{namespace:.+}", method = RequestMethod.GET)
 52  
   public ApolloConfig queryConfig(@PathVariable String appId, @PathVariable String clusterName,
 53  
                                   @PathVariable String namespace,
 54  
                                   @RequestParam(value = "dataCenter", required = false) String dataCenter,
 55  
                                   @RequestParam(value = "releaseKey", defaultValue = "-1") String clientSideReleaseKey,
 56  
                                   @RequestParam(value = "ip", required = false) String clientIp,
 57  
                                   HttpServletResponse response) throws IOException {
 58  17
     List<Release> releases = Lists.newLinkedList();
 59  
 
 60  17
     Release currentAppRelease = loadConfig(appId, clusterName, namespace, dataCenter);
 61  17
     String appClusterNameLoaded = clusterName;
 62  
 
 63  17
     if (currentAppRelease != null) {
 64  11
       releases.add(currentAppRelease);
 65  
       //we have cluster search process, so the cluster name might be overridden
 66  11
       appClusterNameLoaded = currentAppRelease.getClusterName();
 67  
     }
 68  
 
 69  
     //if namespace is not 'application', should check if it's a public configuration
 70  17
     if (!Objects.equals(ConfigConsts.NAMESPACE_APPLICATION, namespace)) {
 71  11
       Release publicRelease = this.findPublicConfig(appId, clusterName, namespace, dataCenter);
 72  11
       if (!Objects.isNull(publicRelease)) {
 73  7
         releases.add(publicRelease);
 74  
       }
 75  
     }
 76  
 
 77  17
     if (releases.isEmpty()) {
 78  4
       response.sendError(HttpServletResponse.SC_NOT_FOUND,
 79  2
           String.format(
 80  
               "Could not load configurations with appId: %s, clusterName: %s, namespace: %s",
 81  
               appId, clusterName, namespace));
 82  4
       Cat.logEvent("Apollo.Config.NotFound",
 83  2
           assembleKey(appId, clusterName, namespace, dataCenter));
 84  2
       return null;
 85  
     }
 86  
 
 87  15
     String mergedReleaseKey = FluentIterable.from(releases).transform(
 88  33
         input -> String.valueOf(input.getReleaseKey())).join(STRING_JOINER);
 89  
 
 90  15
     if (mergedReleaseKey.equals(clientSideReleaseKey)) {
 91  
       // Client side configuration is the same with server side, return 304
 92  2
       response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
 93  4
       Cat.logEvent("Apollo.Config.NotModified",
 94  2
           assembleKey(appId, appClusterNameLoaded, namespace, dataCenter));
 95  2
       return null;
 96  
     }
 97  
 
 98  13
     ApolloConfig apolloConfig = new ApolloConfig(appId, appClusterNameLoaded, namespace, mergedReleaseKey);
 99  13
     apolloConfig.setConfigurations(mergeReleaseConfigurations(releases));
 100  
 
 101  13
     Cat.logEvent("Apollo.Config.Found", assembleKey(appId, appClusterNameLoaded, namespace, dataCenter));
 102  13
     return apolloConfig;
 103  
   }
 104  
 
 105  
   /**
 106  
    * @param applicationId the application which uses public config
 107  
    * @param namespace     the namespace
 108  
    * @param dataCenter    the datacenter
 109  
    */
 110  
   private Release findPublicConfig(String applicationId, String clusterName, String namespace, String dataCenter) {
 111  11
     AppNamespace appNamespace = appNamespaceService.findByNamespaceName(namespace);
 112  
 
 113  
     //check whether the namespace's appId equals to current one
 114  11
     if (Objects.isNull(appNamespace) || Objects.equals(applicationId, appNamespace.getAppId())) {
 115  4
       return null;
 116  
     }
 117  
 
 118  7
     String publicConfigAppId = appNamespace.getAppId();
 119  
 
 120  7
     return loadConfig(publicConfigAppId, clusterName, namespace, dataCenter);
 121  
   }
 122  
 
 123  
   private Release loadConfig(String appId, String clusterName, String namespace, String dataCenter) {
 124  
     //load from specified cluster fist
 125  24
     if (!Objects.equals(ConfigConsts.CLUSTER_NAME_DEFAULT, clusterName)) {
 126  17
       Release clusterRelease =
 127  17
           configService.findRelease(appId, clusterName, namespace);
 128  
 
 129  17
       if (!Objects.isNull(clusterRelease)) {
 130  6
         return clusterRelease;
 131  
       }
 132  
     }
 133  
 
 134  
     //try to load via data center
 135  18
     if (!Strings.isNullOrEmpty(dataCenter) && !Objects.equals(dataCenter, clusterName)) {
 136  16
       Release dataCenterRelease =
 137  16
           configService.findRelease(appId, dataCenter, namespace);
 138  16
       if (!Objects.isNull(dataCenterRelease)) {
 139  6
         return dataCenterRelease;
 140  
       }
 141  
     }
 142  
 
 143  
     //fallback to default release
 144  12
     return configService
 145  12
         .findRelease(appId, ConfigConsts.CLUSTER_NAME_DEFAULT, namespace);
 146  
   }
 147  
 
 148  
   /**
 149  
    * Merge configurations of releases.
 150  
    * Release in lower index override those in higher index
 151  
    */
 152  
   Map<String, String> mergeReleaseConfigurations(List<Release> releases) {
 153  15
     Map<String, String> result = Maps.newHashMap();
 154  15
     for (Release release : Lists.reverse(releases)) {
 155  19
       result.putAll(gson.fromJson(release.getConfigurations(), configurationTypeReference));
 156  18
     }
 157  14
     return result;
 158  
   }
 159  
 
 160  
   private String assembleKey(String appId, String cluster, String namespace, String datacenter) {
 161  17
     List<String> keyParts = Lists.newArrayList(appId, cluster, namespace);
 162  17
     if (!Strings.isNullOrEmpty(datacenter)) {
 163  13
       keyParts.add(datacenter);
 164  
     }
 165  17
     return STRING_JOINER.join(keyParts);
 166  
   }
 167  
 
 168  
 }