##### Coverage Report - com.ctrip.apollo.configservice.controller.ConfigController

Classes in this File | Line Coverage | Branch Coverage | Complexity
ConfigController |
100% | 51/51
95% | 21/22
| 0.0;0
ConfigController$1 |
100% | 1/1
N/A | N/A
| 0.0;0

1 |  |
     package com.ctrip.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.apollo.biz.entity.AppNamespace;
12 |  |
     import com.ctrip.apollo.biz.entity.Release;
13 |  |
     import com.ctrip.apollo.biz.service.AppNamespaceService;
14 |  |
     import com.ctrip.apollo.biz.service.ConfigService;
15 |  |
     import com.ctrip.apollo.core.ConfigConsts;
16 |  |
     import com.ctrip.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 | 10 |
     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}", method = RequestMethod.GET)
52 |  |
       public ApolloConfig queryConfig(@PathVariable String appId, @PathVariable String clusterName,
53 |  |
                                       @RequestParam(value = "dataCenter", required = false) String dataCenter,
54 |  |
                                       @RequestParam(value = "releaseKey", defaultValue = "-1") String clientSideReleaseKey,
55 |  |
                                       HttpServletResponse response) throws IOException {
56 | 1 |
         return this.queryConfig(appId, clusterName, ConfigConsts.NAMESPACE_DEFAULT, dataCenter,
57 |  |
             clientSideReleaseKey, response);
58 |  |
       }
59 |  |
60 |  |
       @RequestMapping(value = "/{appId}/{clusterName}/{namespace}", method = RequestMethod.GET)
61 |  |
       public ApolloConfig queryConfig(@PathVariable String appId, @PathVariable String clusterName,
62 |  |
                                       @PathVariable String namespace,
63 |  |
                                       @RequestParam(value = "dataCenter", required = false) String dataCenter,
64 |  |
                                       @RequestParam(value = "releaseKey", defaultValue = "-1") String clientSideReleaseKey,
65 |  |
                                       HttpServletResponse response) throws IOException {
66 | 15 |
         List<Release> releases = Lists.newLinkedList();
67 |  |
68 | 15 |
         Release currentAppRelease = configService.findRelease(appId, clusterName, namespace);
69 |  |
70 | 15 |
         if (currentAppRelease != null) {
71 | 9 |
           releases.add(currentAppRelease);
72 |  |
         }
73 |  |
74 |  |
         //if namespace is not 'application', should check if it's a public configuration
75 | 15 |
         if (!Objects.equals(ConfigConsts.NAMESPACE_DEFAULT, namespace)) {
76 | 11 |
           Release publicRelease = this.findPublicConfig(appId, namespace, dataCenter);
77 | 11 |
           if (!Objects.isNull(publicRelease)) {
78 | 7 |
             releases.add(publicRelease);
79 |  |
           }
80 |  |
         }
81 |  |
82 | 15 |
         if (releases.isEmpty()) {
83 | 4 |
           response.sendError(HttpServletResponse.SC_NOT_FOUND,
84 | 2 |
               String.format(
85 |  |
                   "Could not load configurations with appId: %s, clusterName: %s, namespace: %s",
86 |  |
                   appId, clusterName, namespace));
87 | 4 |
           Cat.logEvent("Apollo.Config.NotFound",
88 | 2 |
               assembleKey(appId, clusterName, namespace, dataCenter));
89 | 2 |
           return null;
90 |  |
         }
91 |  |
92 | 13 |
         String mergedReleaseKey = FluentIterable.from(releases).transform(
93 | 29 |
             input -> String.valueOf(input.getReleaseKey())).join(STRING_JOINER);
94 |  |
95 | 13 |
         if (mergedReleaseKey.equals(clientSideReleaseKey)) {
96 |  |
           // Client side configuration is the same with server side, return 304
97 | 2 |
           response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
98 | 4 |
           Cat.logEvent("Apollo.Config.NotModified",
99 | 2 |
               assembleKey(appId, clusterName, namespace, dataCenter));
100 | 2 |
           return null;
101 |  |
         }
102 |  |
103 | 11 |
         ApolloConfig apolloConfig = new ApolloConfig(appId, clusterName, namespace, mergedReleaseKey);
104 | 11 |
         apolloConfig.setConfigurations(mergeReleaseConfigurations(releases));
105 |  |
106 | 11 |
         Cat.logEvent("Apollo.Config.Found", assembleKey(appId, clusterName, namespace, dataCenter));
107 | 11 |
         return apolloConfig;
108 |  |
       }
109 |  |
110 |  |
       /**
111 |  |
        * @param applicationId the application which uses public config
112 |  |
        * @param namespace     the namespace
113 |  |
        * @param dataCenter    the datacenter
114 |  |
        */
115 |  |
       private Release findPublicConfig(String applicationId, String namespace, String dataCenter) {
116 | 11 |
         AppNamespace appNamespace = appNamespaceService.findByNamespaceName(namespace);
117 |  |
118 |  |
         //check whether the namespace's appId equals to current one
119 | 11 |
         if (Objects.isNull(appNamespace) || Objects.equals(applicationId, appNamespace.getAppId())) {
120 | 4 |
           return null;
121 |  |
         }
122 |  |
123 | 7 |
         String publicConfigAppId = appNamespace.getAppId();
124 |  |
125 |  |
         //try to load via data center
126 | 7 |
         if (!Objects.isNull(dataCenter)) {
127 | 7 |
           Release dataCenterRelease =
128 | 7 |
               configService.findRelease(publicConfigAppId, dataCenter, namespace);
129 | 7 |
           if (!Objects.isNull(dataCenterRelease)) {
130 | 4 |
             return dataCenterRelease;
131 |  |
           }
132 |  |
         }
133 |  |
134 |  |
         //fallback to default release
135 | 3 |
         return configService
136 | 3 |
             .findRelease(publicConfigAppId, ConfigConsts.CLUSTER_NAME_DEFAULT, namespace);
137 |  |
       }
138 |  |
139 |  |
       /**
140 |  |
        * Merge configurations of releases.
141 |  |
        * Release in lower index override those in higher index
142 |  |
        */
143 |  |
       Map<String, String> mergeReleaseConfigurations(List<Release> releases) {
144 | 13 |
         Map<String, String> result = Maps.newHashMap();
145 | 13 |
         for (Release release : Lists.reverse(releases)) {
146 | 17 |
           result.putAll(gson.fromJson(release.getConfigurations(), configurationTypeReference));
147 | 16 |
         }
148 | 12 |
         return result;
149 |  |
       }
150 |  |
151 |  |
       private String assembleKey(String appId, String cluster, String namespace, String datacenter) {
152 | 15 |
         List<String> keyParts = Lists.newArrayList(appId, cluster, namespace);
153 | 15 |
         if (!Strings.isNullOrEmpty(datacenter)) {
154 | 11 |
           keyParts.add(datacenter);
155 |  |
         }
156 | 15 |
         return STRING_JOINER.join(keyParts);
157 |  |
       }
158 |  |
159 |  |
     }