##### 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.0;0
ConfigController$1 |
100% | 1/1
N/A | N/A
| 0.0;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 |  |
     }