##### Coverage Report - com.ctrip.framework.apollo.internals.ConfigServiceLocator

Classes in this File | Line Coverage | Branch Coverage | Complexity
ConfigServiceLocator |
89% | 59/66
58% | 7/12
| 2.2;2.2
ConfigServiceLocator$1 |
100% | 1/1
N/A | N/A
| 2.2;2.2
ConfigServiceLocator$2 |
100% | 5/5
N/A | N/A
| 2.2;2.2

1 |  |
     package com.ctrip.framework.apollo.internals;
2 |  |
3 |  |
     import com.google.common.base.Joiner;
4 |  |
     import com.google.common.base.Strings;
5 |  |
     import com.google.common.collect.Lists;
6 |  |
     import com.google.common.collect.Maps;
7 |  |
     import com.google.common.escape.Escaper;
8 |  |
     import com.google.common.net.UrlEscapers;
9 |  |
     import com.google.gson.reflect.TypeToken;
10 |  |
11 |  |
     import com.ctrip.framework.apollo.core.dto.ServiceDTO;
12 |  |
     import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory;
13 |  |
     import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
14 |  |
     import com.ctrip.framework.apollo.util.ConfigUtil;
15 |  |
     import com.ctrip.framework.apollo.util.http.HttpRequest;
16 |  |
     import com.ctrip.framework.apollo.util.http.HttpResponse;
17 |  |
     import com.ctrip.framework.apollo.util.http.HttpUtil;
18 |  |
     import com.dianping.cat.Cat;
19 |  |
     import com.dianping.cat.message.Message;
20 |  |
     import com.dianping.cat.message.Transaction;
21 |  |
22 |  |
     import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
23 |  |
     import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
24 |  |
     import org.slf4j.Logger;
25 |  |
     import org.slf4j.LoggerFactory;
26 |  |
     import org.unidal.lookup.annotation.Inject;
27 |  |
     import org.unidal.lookup.annotation.Named;
28 |  |
29 |  |
     import java.lang.reflect.Type;
30 |  |
     import java.util.List;
31 |  |
     import java.util.Map;
32 |  |
     import java.util.concurrent.Executors;
33 |  |
     import java.util.concurrent.ScheduledExecutorService;
34 |  |
     import java.util.concurrent.TimeUnit;
35 |  |
     import java.util.concurrent.atomic.AtomicReference;
36 |  |
37 |  |
     @Named(type = ConfigServiceLocator.class)
38 | 48 |
     public class ConfigServiceLocator implements Initializable {
39 | 1 |
       private static final Logger logger = LoggerFactory.getLogger(ConfigServiceLocator.class);
40 |  |
       @Inject
41 |  |
       private HttpUtil m_httpUtil;
42 |  |
       @Inject
43 |  |
       private ConfigUtil m_configUtil;
44 |  |
       private AtomicReference<List<ServiceDTO>> m_configServices;
45 |  |
       private Type m_responseType;
46 |  |
       private ScheduledExecutorService m_executorService;
47 | 1 |
       private static final Joiner.MapJoiner MAP_JOINER = Joiner.on("&").withKeyValueSeparator("=");
48 | 1 |
       private static final Escaper queryParamEscaper = UrlEscapers.urlFormParameterEscaper();
49 |  |
50 |  |
       /**
51 |  |
        * Create a config service locator.
52 |  |
        */
53 | 14 |
       public ConfigServiceLocator() {
54 | 14 |
         List<ServiceDTO> initial = Lists.newArrayList();
55 | 14 |
         m_configServices = new AtomicReference<>(initial);
56 | 14 |
         m_responseType = new TypeToken<List<ServiceDTO>>() {
57 | 14 |
         }.getType();
58 | 28 |
         this.m_executorService = Executors.newScheduledThreadPool(1,
59 | 14 |
             ApolloThreadFactory.create("ConfigServiceLocator", true));
60 | 14 |
       }
61 |  |
62 |  |
       @Override
63 |  |
       public void initialize() throws InitializationException {
64 | 8 |
         this.tryUpdateConfigServices();
65 | 8 |
         this.schedulePeriodicRefresh();
66 | 8 |
       }
67 |  |
68 |  |
       /**
69 |  |
        * Get the config service info from remote meta server.
70 |  |
        *
71 |  |
        * @return the services dto
72 |  |
        */
73 |  |
       public List<ServiceDTO> getConfigServices() {
74 | 56 |
         if (m_configServices.get().isEmpty()) {
75 | 0 |
           updateConfigServices();
76 |  |
         }
77 |  |
78 | 56 |
         return m_configServices.get();
79 |  |
       }
80 |  |
81 |  |
       private boolean tryUpdateConfigServices() {
82 |  |
         try {
83 | 32 |
           updateConfigServices();
84 | 31 |
           return true;
85 | 0 |
         } catch (Throwable ex) {
86 |  |
           //ignore
87 |  |
         }
88 | 0 |
         return false;
89 |  |
       }
90 |  |
91 |  |
       private void schedulePeriodicRefresh() {
92 | 16 |
         this.m_executorService.scheduleAtFixedRate(
93 | 8 |
             new Runnable() {
94 |  |
               @Override
95 |  |
               public void run() {
96 | 24 |
                 logger.debug("refresh config services");
97 | 24 |
                 Cat.logEvent("Apollo.MetaService", "periodicRefresh");
98 | 24 |
                 tryUpdateConfigServices();
99 | 23 |
               }
100 | 8 |
             }, m_configUtil.getRefreshInterval(), m_configUtil.getRefreshInterval(),
101 | 8 |
             m_configUtil.getRefreshIntervalTimeUnit());
102 | 8 |
       }
103 |  |
104 |  |
       private synchronized void updateConfigServices() {
105 | 32 |
         String url = assembleMetaServiceUrl();
106 |  |
107 | 32 |
         HttpRequest request = new HttpRequest(url);
108 | 32 |
         int maxRetries = 5;
109 | 32 |
         Throwable exception = null;
110 |  |
111 | 33 |
         for (int i = 0; i < maxRetries; i++) {
112 | 33 |
           Transaction transaction = Cat.newTransaction("Apollo.MetaService", "getConfigService");
113 | 33 |
           transaction.addData("Url", url);
114 |  |
           try {
115 | 33 |
             HttpResponse<List<ServiceDTO>> response = m_httpUtil.doGet(request, m_responseType);
116 | 31 |
             transaction.setStatus(Message.SUCCESS);
117 | 31 |
             List<ServiceDTO> services = response.getBody();
118 | 31 |
             if (services == null || services.isEmpty()) {
119 | 0 |
               logConfigServiceToCat("Empty response!");
120 |  |
               continue;
121 |  |
             }
122 | 31 |
             m_configServices.set(services);
123 | 31 |
             logConfigServicesToCat(services);
124 | 31 |
             return;
125 | 2 |
           } catch (Throwable ex) {
126 | 2 |
             Cat.logError(ex);
127 | 2 |
             transaction.setStatus(ex);
128 | 2 |
             exception = ex;
129 |  |
           } finally {
130 | 33 |
             transaction.complete();
131 | 2 |
           }
132 |  |
133 |  |
           try {
134 | 2 |
             TimeUnit.SECONDS.sleep(1);
135 | 0 |
           } catch (InterruptedException ex) {
136 |  |
             //ignore
137 | 1 |
           }
138 |  |
         }
139 |  |
140 | 0 |
         throw new ApolloConfigException(
141 | 0 |
             String.format("Get config services failed from %s", url), exception);
142 |  |
       }
143 |  |
144 |  |
       private String assembleMetaServiceUrl() {
145 | 32 |
         String domainName = m_configUtil.getMetaServerDomainName();
146 | 32 |
         String appId = m_configUtil.getAppId();
147 | 32 |
         String localIp = m_configUtil.getLocalIp();
148 |  |
149 | 32 |
         Map<String, String> queryParams = Maps.newHashMap();
150 | 32 |
         queryParams.put("appId", queryParamEscaper.escape(appId));
151 | 32 |
         if (!Strings.isNullOrEmpty(localIp)) {
152 | 32 |
           queryParams.put("ip", queryParamEscaper.escape(localIp));
153 |  |
         }
154 |  |
155 | 32 |
         return domainName + "/services/config?" + MAP_JOINER.join(queryParams);
156 |  |
       }
157 |  |
158 |  |
       private void logConfigServicesToCat(List<ServiceDTO> serviceDtos) {
159 | 31 |
         for (ServiceDTO serviceDto : serviceDtos) {
160 | 31 |
           logConfigServiceToCat(serviceDto.getHomepageUrl());
161 | 31 |
         }
162 | 31 |
       }
163 |  |
164 |  |
       private void logConfigServiceToCat(String serviceUrl) {
165 | 31 |
         Cat.logEvent("Apollo.Config.Services", serviceUrl);
166 | 31 |
       }
167 |  |
     }