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

Classes in this File | Line Coverage | Branch Coverage | Complexity
ConfigServiceLocator |
91% | 54/59
66% | 4/6
| 2.111111111111111;2.111
ConfigServiceLocator$1 |
100% | 1/1
N/A | N/A
| 2.111111111111111;2.111
ConfigServiceLocator$2 |
100% | 8/8
50% | 1/2
| 2.111111111111111;2.111

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