瀏覽代碼

Use HttpURLConnection and gson to remove Spring and Jackson dependencies

Jason Song 9 年之前
父節點
當前提交
f5d513fb0a
共有 20 個文件被更改,包括 413 次插入237 次删除
  1. 13 13
      apollo-biz/src/main/java/com/ctrip/apollo/biz/service/ConfigService.java
  2. 2 18
      apollo-biz/src/test/java/com/ctrip/apollo/biz/service/ConfigServiceTest.java
  3. 16 25
      apollo-client/pom.xml
  4. 6 0
      apollo-client/src/main/java/com/ctrip/apollo/build/ComponentConfigurator.java
  5. 17 9
      apollo-client/src/main/java/com/ctrip/apollo/internals/ConfigServiceLocator.java
  6. 9 3
      apollo-client/src/main/java/com/ctrip/apollo/internals/DefaultConfig.java
  7. 22 6
      apollo-client/src/main/java/com/ctrip/apollo/internals/LocalFileConfigRepository.java
  8. 61 76
      apollo-client/src/main/java/com/ctrip/apollo/internals/RemoteConfigRepository.java
  9. 8 3
      apollo-client/src/main/java/com/ctrip/apollo/internals/SimpleConfig.java
  10. 6 7
      apollo-client/src/main/java/com/ctrip/apollo/spi/DefaultConfigFactory.java
  11. 12 15
      apollo-client/src/main/java/com/ctrip/apollo/util/ConfigUtil.java
  12. 36 0
      apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpRequest.java
  13. 22 0
      apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpResponse.java
  14. 85 0
      apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpUtil.java
  15. 22 0
      apollo-client/src/main/resources/META-INF/plexus/components.xml
  16. 1 1
      apollo-client/src/test/java/com/ctrip/apollo/internals/DefaultConfigTest.java
  17. 25 15
      apollo-client/src/test/java/com/ctrip/apollo/internals/LocalFileConfigRepositoryTest.java
  18. 44 32
      apollo-client/src/test/java/com/ctrip/apollo/internals/RemoteConfigRepositoryTest.java
  19. 2 6
      apollo-core/pom.xml
  20. 4 8
      apollo-core/src/main/java/com/ctrip/apollo/core/dto/ApolloConfig.java

+ 13 - 13
apollo-biz/src/main/java/com/ctrip/apollo/biz/service/ConfigService.java

@@ -1,17 +1,18 @@
 package com.ctrip.apollo.biz.service;
 
-import java.io.IOException;
-import java.util.Map;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Service;
+import com.google.common.collect.Maps;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
 
 import com.ctrip.apollo.biz.entity.Release;
 import com.ctrip.apollo.biz.repository.ReleaseRepository;
 import com.ctrip.apollo.core.dto.ApolloConfig;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.collect.Maps;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.lang.reflect.Type;
+import java.util.Map;
 
 /**
  * Config Service
@@ -24,10 +25,9 @@ public class ConfigService {
   @Autowired
   private ReleaseRepository releaseRepository;
 
-  private ObjectMapper objectMapper = new ObjectMapper();
+  private Gson gson = new Gson();
 
-  private TypeReference<Map<String, String>> configurationTypeReference =
-      new TypeReference<Map<String, String>>() {};
+  private Type configurationTypeReference = new TypeToken<Map<String, String>>(){}.getType();
 
   public Release findRelease(String appId, String clusterName, String namespaceName) {
     Release release = releaseRepository.findLatest(appId, clusterName, namespaceName);
@@ -49,8 +49,8 @@ public class ConfigService {
 
   Map<String, String> transformConfigurationToMap(String configurations) {
     try {
-      return objectMapper.readValue(configurations, configurationTypeReference);
-    } catch (IOException e) {
+      return gson.fromJson(configurations, configurationTypeReference);
+    } catch (Throwable e) {
       e.printStackTrace();
       return Maps.newHashMap();
     }

+ 2 - 18
apollo-biz/src/test/java/com/ctrip/apollo/biz/service/ConfigServiceTest.java

@@ -1,11 +1,9 @@
 package com.ctrip.apollo.biz.service;
 
 import com.google.common.collect.Maps;
+
 import com.ctrip.apollo.biz.entity.Release;
 import com.ctrip.apollo.biz.repository.ReleaseRepository;
-import com.ctrip.apollo.biz.service.ConfigService;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -14,16 +12,10 @@ import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
 import org.springframework.test.util.ReflectionTestUtils;
 
-import java.io.IOException;
 import java.util.Map;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.anyObject;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 /**
  * @author Jason Song(song_s@ctrip.com)
@@ -32,8 +24,6 @@ import static org.mockito.Mockito.when;
 public class ConfigServiceTest {
   @Mock
   private ReleaseRepository releaseRepository;
-  @Mock
-  private ObjectMapper objectMapper;
   private ConfigService configService;
 
   @Before
@@ -41,7 +31,6 @@ public class ConfigServiceTest {
     configService = new ConfigService();
     ReflectionTestUtils
         .setField(configService, "releaseRepository", releaseRepository);
-    ReflectionTestUtils.setField(configService, "objectMapper", objectMapper);
   }
 
 //  @Test
@@ -131,21 +120,16 @@ public class ConfigServiceTest {
   public void testTransformConfigurationToMapSuccessful() throws Exception {
     String someValidConfiguration = "{\"apollo.bar\": \"foo\"}";
     Map<String, String> someMap = Maps.newHashMap();
-    when(objectMapper.readValue(eq(someValidConfiguration), (TypeReference) anyObject()))
-        .thenReturn(someMap);
+    someMap.put("apollo.bar", "foo");
 
     Map<String, String> result = configService.transformConfigurationToMap(someValidConfiguration);
 
     assertEquals(someMap, result);
-    verify(objectMapper, times(1))
-        .readValue(eq(someValidConfiguration), (TypeReference) anyObject());
   }
 
   @Test
   public void testTransformConfigurationToMapFailed() throws Exception {
     String someInvalidConfiguration = "xxx";
-    when(objectMapper.readValue(eq(someInvalidConfiguration), (TypeReference) anyObject()))
-        .thenThrow(IOException.class);
 
     Map<String, String>
         result =

+ 16 - 25
apollo-client/pom.xml

@@ -24,38 +24,29 @@
             <groupId>com.dianping.cat</groupId>
             <artifactId>cat-client</artifactId>
         </dependency>
-        <!-- spring -->
         <dependency>
-            <groupId>org.springframework</groupId>
-            <artifactId>spring-web</artifactId>
-        </dependency>
-        <!-- end of spring -->
-        <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-        </dependency>
-        <!-- end of util -->
-        <!-- test -->
-        <dependency>
-            <groupId>commons-logging</groupId>
-            <artifactId>commons-logging</artifactId>
-            <scope>test</scope>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.logging.log4j</groupId>
-            <artifactId>log4j-core</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.logging.log4j</groupId>
-            <artifactId>log4j-api</artifactId>
-            <scope>test</scope>
+            <artifactId>log4j-slf4j-impl</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-api</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-api</artifactId>
+                </exclusion>
+            </exclusions>
+            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.logging.log4j</groupId>
-            <artifactId>log4j-slf4j-impl</artifactId>
-            <scope>test</scope>
+            <artifactId>log4j-core</artifactId>
+            <scope>provided</scope>
         </dependency>
-        <!-- end of test -->
     </dependencies>
 </project>

+ 6 - 0
apollo-client/src/main/java/com/ctrip/apollo/build/ComponentConfigurator.java

@@ -1,9 +1,12 @@
 package com.ctrip.apollo.build;
 
+import com.ctrip.apollo.internals.ConfigServiceLocator;
 import com.ctrip.apollo.internals.DefaultConfigManager;
 import com.ctrip.apollo.spi.DefaultConfigFactory;
 import com.ctrip.apollo.spi.DefaultConfigFactoryManager;
 import com.ctrip.apollo.spi.DefaultConfigRegistry;
+import com.ctrip.apollo.util.ConfigUtil;
+import com.ctrip.apollo.util.http.HttpUtil;
 
 import org.unidal.lookup.configuration.AbstractResourceConfigurator;
 import org.unidal.lookup.configuration.Component;
@@ -27,6 +30,9 @@ public class ComponentConfigurator extends AbstractResourceConfigurator {
     all.add(A(DefaultConfigFactory.class));
     all.add(A(DefaultConfigRegistry.class));
     all.add(A(DefaultConfigFactoryManager.class));
+    all.add(A(ConfigUtil.class));
+    all.add(A(ConfigServiceLocator.class));
+    all.add(A(HttpUtil.class));
 
     return all;
   }

+ 17 - 9
apollo-client/src/main/java/com/ctrip/apollo/internals/ConfigServiceLocator.java

@@ -2,38 +2,46 @@ package com.ctrip.apollo.internals;
 
 import com.ctrip.apollo.core.dto.ServiceDTO;
 import com.ctrip.apollo.env.ClientEnvironment;
+import com.ctrip.apollo.util.http.HttpRequest;
+import com.ctrip.apollo.util.http.HttpResponse;
+import com.ctrip.apollo.util.http.HttpUtil;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.web.client.RestTemplate;
+import org.unidal.lookup.annotation.Inject;
+import org.unidal.lookup.annotation.Named;
 
-import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
 
+@Named(type = ConfigServiceLocator.class)
 public class ConfigServiceLocator {
-
   private static final Logger logger = LoggerFactory.getLogger(ConfigServiceLocator.class);
-
-  private RestTemplate restTemplate = new RestTemplate();
-
+  @Inject
+  private HttpUtil m_httpUtil;
   private List<ServiceDTO> serviceCaches = new ArrayList<>();
 
   public List<ServiceDTO> getConfigServices() {
     ClientEnvironment env = ClientEnvironment.getInstance();
     String domainName = env.getMetaServerDomainName();
     String url = domainName + "/services/config";
+
+    HttpRequest request = new HttpRequest(url);
+
     try {
-      ServiceDTO[] services = restTemplate.getForObject(new URI(url), ServiceDTO[].class);
+      HttpResponse<ServiceDTO[]> response = m_httpUtil.doGet(request, ServiceDTO[].class);
+      ServiceDTO[] services = response.getBody();
       if (services != null && services.length > 0) {
         serviceCaches.clear();
         for (ServiceDTO service : services) {
           serviceCaches.add(service);
         }
       }
-    } catch (Exception ex) {
-      logger.warn(ex.getMessage());
+    } catch (Throwable t) {
+      logger.error("Get config services failed", t);
+      throw new RuntimeException("Get config services failed", t);
     }
+
     return serviceCaches;
   }
 }

+ 9 - 3
apollo-client/src/main/java/com/ctrip/apollo/internals/DefaultConfig.java

@@ -4,6 +4,9 @@ import com.ctrip.apollo.Config;
 import com.ctrip.apollo.core.utils.ClassLoaderUtil;
 import com.dianping.cat.Cat;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Properties;
@@ -12,6 +15,7 @@ import java.util.Properties;
  * @author Jason Song(song_s@ctrip.com)
  */
 public class DefaultConfig implements Config {
+  private static final Logger logger = LoggerFactory.getLogger(DefaultConfig.class);
   private final String m_namespace;
   private Properties m_resourceProperties;
   private Properties m_configProperties;
@@ -28,9 +32,10 @@ public class DefaultConfig implements Config {
     try {
       m_configProperties = m_configRepository.loadConfig();
     } catch (Throwable ex) {
-      throw new RuntimeException(
-          String.format("Init Apollo Local Config failed - namespace: %s",
-              m_namespace), ex);
+      String message = String.format("Init Apollo Local Config failed - namespace: %s",
+          m_namespace);
+      logger.error(message, ex);
+      throw new RuntimeException(message, ex);
     }
   }
 
@@ -74,6 +79,7 @@ public class DefaultConfig implements Config {
       try {
         properties.load(in);
       } catch (IOException e) {
+        logger.error("Load resource config for namespace {} failed", namespace, e);
         Cat.logError(e);
       } finally {
         try {

+ 22 - 6
apollo-client/src/main/java/com/ctrip/apollo/internals/LocalFileConfigRepository.java

@@ -5,9 +5,14 @@ import com.google.common.base.Preconditions;
 import com.ctrip.apollo.util.ConfigUtil;
 import com.dianping.cat.Cat;
 
+import org.codehaus.plexus.PlexusContainer;
+import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.unidal.lookup.ContainerLoader;
+
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -18,16 +23,23 @@ import java.util.Properties;
  * @author Jason Song(song_s@ctrip.com)
  */
 public class LocalFileConfigRepository implements ConfigRepository {
+  private static final Logger logger = LoggerFactory.getLogger(LocalFileConfigRepository.class);
+  private PlexusContainer m_container;
   private final String m_namespace;
   private final File m_baseDir;
   private final ConfigUtil m_configUtil;
   private Properties m_fileProperties;
   private ConfigRepository m_fallback;
 
-  public LocalFileConfigRepository(File baseDir, String namespace, ConfigUtil configUtil) {
+  public LocalFileConfigRepository(File baseDir, String namespace) {
     m_baseDir = baseDir;
     m_namespace = namespace;
-    m_configUtil = configUtil;
+    m_container = ContainerLoader.getDefaultContainer();
+    try {
+      m_configUtil = m_container.lookup(ConfigUtil.class);
+    } catch (ComponentLookupException e) {
+      throw new IllegalStateException("Unable to load component!", e);
+    }
   }
 
   @Override
@@ -53,6 +65,7 @@ public class LocalFileConfigRepository implements ConfigRepository {
         persistLocalCacheFile(m_baseDir, m_namespace);
         return;
       } catch (Throwable ex) {
+        logger.error("Load config from fallback loader failed", ex);
         Cat.logError(ex);
       }
     }
@@ -79,6 +92,7 @@ public class LocalFileConfigRepository implements ConfigRepository {
         properties = new Properties();
         properties.load(in);
       } catch (IOException e) {
+        logger.error("Loading config from local cache file {} failed", file.getAbsolutePath(), e);
         Cat.logError(e);
         throw e;
       } finally {
@@ -91,7 +105,10 @@ public class LocalFileConfigRepository implements ConfigRepository {
         }
       }
     } else {
-      //TODO error handling
+      String message =
+          String.format("Cannot read from local cache file %s", file.getAbsolutePath());
+      logger.error(message);
+      throw new RuntimeException(message);
     }
 
     return properties;
@@ -108,9 +125,8 @@ public class LocalFileConfigRepository implements ConfigRepository {
     try {
       out = new FileOutputStream(file);
       m_fileProperties.store(out, "Persisted by DefaultConfig");
-    } catch (FileNotFoundException ex) {
-      Cat.logError(ex);
     } catch (IOException ex) {
+      logger.error("Persist local cache file {} failed", file.getAbsolutePath(), ex);
       Cat.logError(ex);
     } finally {
       if (out != null) {

+ 61 - 76
apollo-client/src/main/java/com/ctrip/apollo/internals/RemoteConfigRepository.java

@@ -1,52 +1,56 @@
 package com.ctrip.apollo.internals;
 
-import com.google.common.collect.Maps;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
 
 import com.ctrip.apollo.core.dto.ApolloConfig;
 import com.ctrip.apollo.core.dto.ServiceDTO;
 import com.ctrip.apollo.util.ConfigUtil;
+import com.ctrip.apollo.util.http.HttpRequest;
+import com.ctrip.apollo.util.http.HttpResponse;
+import com.ctrip.apollo.util.http.HttpUtil;
 
+import org.codehaus.plexus.PlexusContainer;
+import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.util.StringUtils;
-import org.springframework.web.client.RestTemplate;
+import org.unidal.lookup.ContainerLoader;
 
 import java.util.List;
-import java.util.Map;
 import java.util.Properties;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * @author Jason Song(song_s@ctrip.com)
  */
 public class RemoteConfigRepository implements ConfigRepository {
   private static final Logger logger = LoggerFactory.getLogger(RemoteConfigRepository.class);
-  private RestTemplate m_restTemplate;
+  private PlexusContainer m_container;
   private ConfigServiceLocator m_serviceLocator;
+  private HttpUtil m_httpUtil;
   private ConfigUtil m_configUtil;
-  private Properties m_remoteProperties;
+  private AtomicReference<ApolloConfig> m_configCache;
   private String m_namespace;
 
-  public RemoteConfigRepository(RestTemplate restTemplate,
-                                ConfigServiceLocator serviceLocator,
-                                ConfigUtil configUtil, String namespace) {
-    m_restTemplate = restTemplate;
-    m_serviceLocator = serviceLocator;
-    m_configUtil = configUtil;
+  public RemoteConfigRepository(String namespace) {
     m_namespace = namespace;
+    m_configCache = new AtomicReference<>();
+    m_container = ContainerLoader.getDefaultContainer();
+    try {
+      m_configUtil = m_container.lookup(ConfigUtil.class);
+      m_httpUtil = m_container.lookup(HttpUtil.class);
+      m_serviceLocator = m_container.lookup(ConfigServiceLocator.class);
+    } catch (ComponentLookupException e) {
+      throw new IllegalStateException("Unable to load component!", e);
+    }
   }
 
   @Override
   public Properties loadConfig() {
-    if (m_remoteProperties == null) {
+    if (m_configCache.get() == null) {
       initRemoteConfig();
     }
-    Properties result = new Properties();
-    result.putAll(m_remoteProperties);
-    return result;
+    return transformApolloConfigToProperties(m_configCache.get());
   }
 
   @Override
@@ -55,79 +59,60 @@ public class RemoteConfigRepository implements ConfigRepository {
   }
 
   private void initRemoteConfig() {
-    ApolloConfig apolloConfig = this.loadApolloConfig();
-    m_remoteProperties = new Properties();
-    m_remoteProperties.putAll(apolloConfig.getConfigurations());
+    m_configCache.set(this.loadApolloConfig());
+  }
+
+  private Properties transformApolloConfigToProperties(ApolloConfig apolloConfig) {
+    Properties result = new Properties();
+    result.putAll(apolloConfig.getConfigurations());
+    return result;
   }
 
 
   private ApolloConfig loadApolloConfig() {
     String appId = m_configUtil.getAppId();
     String cluster = m_configUtil.getCluster();
+    String uri = getConfigServiceUrl();
+
+    logger.info("Loading config from {}, appId={}, cluster={}, namespace={}", uri, appId, cluster,
+        m_namespace);
+    HttpRequest request =
+        new HttpRequest(assembleUrl(uri, appId, cluster, m_namespace, m_configCache.get()));
+
     try {
-      ApolloConfig result =
-          this.getRemoteConfig(m_restTemplate, getConfigServiceUrl(),
-              appId, cluster,
-              m_namespace,
-              null);
-      if (result == null) {
-        return null;
+      HttpResponse<ApolloConfig> response = m_httpUtil.doGet(request, ApolloConfig.class);
+      if (response.getStatusCode() == 304) {
+        return m_configCache.get();
       }
-      logger.info("Loaded config: {}", result);
-      return result;
-    } catch (Throwable ex) {
-      throw new RuntimeException(
+
+      logger.info("Loaded config: {}", response.getBody());
+
+      return response.getBody();
+    } catch (Throwable t) {
+      String message =
           String.format("Load Apollo Config failed - appId: %s, cluster: %s, namespace: %s", appId,
-              cluster, m_namespace), ex);
+              cluster, m_namespace);
+      logger.error(message, t);
+      throw new RuntimeException(message, t);
     }
   }
 
+  private String assembleUrl(String uri, String appId, String cluster, String namespace,
+                             ApolloConfig previousConfig) {
+    String path = "/config/%s/%s";
+    List<String> params = Lists.newArrayList(appId, cluster);
 
-  private ApolloConfig getRemoteConfig(RestTemplate restTemplate, String uri,
-                                       String appId, String cluster, String namespace,
-                                       ApolloConfig previousConfig) {
-
-    logger.info("Loading config from {}, appId={}, cluster={}, namespace={}", uri, appId, cluster,
-        namespace);
-    String path = "/config/{appId}/{cluster}";
-    Map<String, Object> paramMap = Maps.newHashMap();
-    paramMap.put("appId", appId);
-    paramMap.put("cluster", cluster);
-
-    if (StringUtils.hasText(namespace)) {
-      path = path + "/{namespace}";
-      paramMap.put("namespace", namespace);
+    if (!Strings.isNullOrEmpty(namespace)) {
+      path = path + "/%s";
+      params.add(namespace);
     }
     if (previousConfig != null) {
-      path = path + "?releaseId={releaseId}";
-      paramMap.put("releaseId", previousConfig.getReleaseId());
+      path = path + "?releaseId=%s";
+      params.add(String.valueOf(previousConfig.getReleaseId()));
     }
 
-    ResponseEntity<ApolloConfig> response;
-
-    try {
-      // TODO retry
-      response = restTemplate.exchange(uri
-          + path, HttpMethod.GET, new HttpEntity<Void>((Void) null), ApolloConfig.class, paramMap);
-    } catch (Throwable ex) {
-      throw ex;
-    }
-
-    if (response == null) {
-      throw new RuntimeException("Load apollo config failed, response is null");
-    }
-
-    if (response.getStatusCode() == HttpStatus.NOT_MODIFIED) {
-      return null;
-    }
-
-    if (response.getStatusCode() != HttpStatus.OK) {
-      throw new RuntimeException(
-          String.format("Load apollo config failed, response status %s", response.getStatusCode()));
-    }
-
-    ApolloConfig result = response.getBody();
-    return result;
+    String pathExpanded = String.format(path, params.toArray());
+    return uri + pathExpanded;
   }
 
   private String getConfigServiceUrl() {

+ 8 - 3
apollo-client/src/main/java/com/ctrip/apollo/internals/SimpleConfig.java

@@ -2,12 +2,16 @@ package com.ctrip.apollo.internals;
 
 import com.ctrip.apollo.Config;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import java.util.Properties;
 
 /**
  * @author Jason Song(song_s@ctrip.com)
  */
 public class SimpleConfig implements Config {
+  private static final Logger logger = LoggerFactory.getLogger(SimpleConfig.class);
   private String m_namespace;
   private ConfigRepository m_configRepository;
   private Properties m_configProperties;
@@ -22,9 +26,10 @@ public class SimpleConfig implements Config {
     try {
       m_configProperties = m_configRepository.loadConfig();
     } catch (Throwable ex) {
-      throw new RuntimeException(
-          String.format("Init Apollo Remote Config failed - namespace: %s",
-              m_namespace), ex);
+      String message = String.format("Init Apollo Remote Config failed - namespace: %s",
+          m_namespace);
+      logger.error(message, ex);
+      throw new RuntimeException(message, ex);
     }
   }
 

+ 6 - 7
apollo-client/src/main/java/com/ctrip/apollo/spi/DefaultConfigFactory.java

@@ -2,11 +2,10 @@ package com.ctrip.apollo.spi;
 
 import com.ctrip.apollo.Config;
 import com.ctrip.apollo.core.utils.ClassLoaderUtil;
-import com.ctrip.apollo.internals.*;
+import com.ctrip.apollo.internals.DefaultConfig;
+import com.ctrip.apollo.internals.LocalFileConfigRepository;
 import com.ctrip.apollo.internals.RemoteConfigRepository;
-import com.ctrip.apollo.util.ConfigUtil;
 
-import org.springframework.web.client.RestTemplate;
 import org.unidal.lookup.annotation.Named;
 
 import java.io.File;
@@ -28,20 +27,20 @@ public class DefaultConfigFactory implements ConfigFactory {
 
   @Override
   public Config create(String namespace) {
-    DefaultConfig defaultConfig = new DefaultConfig(namespace, createLocalConfigRepository(namespace));
+    DefaultConfig defaultConfig =
+        new DefaultConfig(namespace, createLocalConfigRepository(namespace));
     return defaultConfig;
   }
 
   LocalFileConfigRepository createLocalConfigRepository(String namespace) {
     LocalFileConfigRepository
         localFileConfigLoader =
-        new LocalFileConfigRepository(m_baseDir, namespace, ConfigUtil.getInstance());
+        new LocalFileConfigRepository(m_baseDir, namespace);
     localFileConfigLoader.setFallback(createRemoteConfigRepository(namespace));
     return localFileConfigLoader;
   }
 
   RemoteConfigRepository createRemoteConfigRepository(String namespace) {
-    return new RemoteConfigRepository(new RestTemplate(), new ConfigServiceLocator(),
-        ConfigUtil.getInstance(), namespace);
+    return new RemoteConfigRepository(namespace);
   }
 }

+ 12 - 15
apollo-client/src/main/java/com/ctrip/apollo/util/ConfigUtil.java

@@ -1,30 +1,19 @@
 package com.ctrip.apollo.util;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.unidal.lookup.annotation.Named;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.Properties;
 import java.util.concurrent.TimeUnit;
 
 /**
  * @author Jason Song(song_s@ctrip.com)
  */
+@Named(type = ConfigUtil.class)
 public class ConfigUtil {
   //TODO read from config?
   private static final int refreshInterval = 5;
   private static final TimeUnit refreshIntervalTimeUnit = TimeUnit.MINUTES;
-
-  private static ConfigUtil configUtil = new ConfigUtil();
-
-  private ConfigUtil() {
-  }
-
-  public static ConfigUtil getInstance() {
-    return configUtil;
-  }
+  private static final int connectTimeout = 5000; //5 seconds
+  private static final int readTimeout = 10000; //10 seconds
 
   public String getAppId() {
     // TODO return the actual app id
@@ -36,6 +25,14 @@ public class ConfigUtil {
     return "default";
   }
 
+  public int getConnectTimeout() {
+    return connectTimeout;
+  }
+
+  public int getReadTimeout() {
+    return readTimeout;
+  }
+
   public int getRefreshInterval() {
     return refreshInterval;
   }

+ 36 - 0
apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpRequest.java

@@ -0,0 +1,36 @@
+package com.ctrip.apollo.util.http;
+
+/**
+ * @author Jason Song(song_s@ctrip.com)
+ */
+public class HttpRequest {
+  private String m_url;
+  private int m_connectTimeout;
+  private int m_readTimeout;
+
+  public HttpRequest(String url) {
+    this.m_url = url;
+    m_connectTimeout = -1;
+    m_readTimeout = -1;
+  }
+
+  public String getUrl() {
+    return m_url;
+  }
+
+  public int getConnectTimeout() {
+    return m_connectTimeout;
+  }
+
+  public void setConnectTimeout(int connectTimeout) {
+    this.m_connectTimeout = connectTimeout;
+  }
+
+  public int getReadTimeout() {
+    return m_readTimeout;
+  }
+
+  public void setReadTimeout(int readTimeout) {
+    this.m_readTimeout = readTimeout;
+  }
+}

+ 22 - 0
apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpResponse.java

@@ -0,0 +1,22 @@
+package com.ctrip.apollo.util.http;
+
+/**
+ * @author Jason Song(song_s@ctrip.com)
+ */
+public class HttpResponse<T> {
+  private final int m_statusCode;
+  private final T m_body;
+
+  public HttpResponse(int statusCode, T body) {
+    this.m_statusCode = statusCode;
+    this.m_body = body;
+  }
+
+  public int getStatusCode() {
+    return m_statusCode;
+  }
+
+  public T getBody() {
+    return m_body;
+  }
+}

+ 85 - 0
apollo-client/src/main/java/com/ctrip/apollo/util/http/HttpUtil.java

@@ -0,0 +1,85 @@
+package com.ctrip.apollo.util.http;
+
+import com.google.common.base.Charsets;
+import com.google.gson.Gson;
+
+import com.ctrip.apollo.util.ConfigUtil;
+
+import org.unidal.helper.Files;
+import org.unidal.lookup.annotation.Inject;
+import org.unidal.lookup.annotation.Named;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+/**
+ * @author Jason Song(song_s@ctrip.com)
+ */
+@Named(type = HttpUtil.class)
+public class HttpUtil {
+  @Inject
+  private ConfigUtil m_configUtil;
+  private Gson gson;
+
+  public HttpUtil() {
+    gson = new Gson();
+  }
+
+  /**
+   * Do get operation for the http request
+   *
+   * @throws RuntimeException if any error happened or response code is neither 200 nor 304
+   */
+  public <T> HttpResponse<T> doGet(HttpRequest httpRequest, Class<T> responseType) {
+    InputStream is = null;
+    try {
+      HttpURLConnection conn = (HttpURLConnection) new URL(httpRequest.getUrl()).openConnection();
+
+      conn.setRequestMethod("GET");
+
+      if (httpRequest.getConnectTimeout() < 0) {
+        conn.setConnectTimeout(m_configUtil.getConnectTimeout());
+      } else {
+        conn.setConnectTimeout(httpRequest.getConnectTimeout());
+      }
+
+      if (httpRequest.getReadTimeout() < 0) {
+        conn.setReadTimeout(m_configUtil.getReadTimeout());
+      } else {
+        conn.setReadTimeout(httpRequest.getReadTimeout());
+      }
+
+      conn.connect();
+
+      int statusCode = conn.getResponseCode();
+
+      if (statusCode == 200) {
+        is = conn.getInputStream();
+        String content = Files.IO.INSTANCE.readFrom(is, Charsets.UTF_8.name());
+        return new HttpResponse<>(statusCode, gson.fromJson(content, responseType));
+      }
+
+      if (statusCode == 304) {
+        return new HttpResponse<>(statusCode, null);
+      }
+
+      throw new RuntimeException(
+          String.format("Get operation failed, status code - %d", statusCode));
+
+    } catch (Throwable ex) {
+      throw new RuntimeException("Could not complete get operation", ex);
+    } finally {
+      if (is != null) {
+        try {
+          is.close();
+        } catch (IOException e) {
+          //ignore
+        }
+      }
+    }
+
+  }
+
+}

+ 22 - 0
apollo-client/src/main/resources/META-INF/plexus/components.xml

@@ -26,5 +26,27 @@
 				</requirement>
 			</requirements>
 		</component>
+		<component>
+			<role>com.ctrip.apollo.util.ConfigUtil</role>
+			<implementation>com.ctrip.apollo.util.ConfigUtil</implementation>
+		</component>
+		<component>
+			<role>com.ctrip.apollo.internals.ConfigServiceLocator</role>
+			<implementation>com.ctrip.apollo.internals.ConfigServiceLocator</implementation>
+			<requirements>
+				<requirement>
+					<role>com.ctrip.apollo.util.http.HttpUtil</role>
+				</requirement>
+			</requirements>
+		</component>
+		<component>
+			<role>com.ctrip.apollo.util.http.HttpUtil</role>
+			<implementation>com.ctrip.apollo.util.http.HttpUtil</implementation>
+			<requirements>
+				<requirement>
+					<role>com.ctrip.apollo.util.ConfigUtil</role>
+				</requirement>
+			</requirements>
+		</component>
 	</components>
 </plexus>

+ 1 - 1
apollo-client/src/test/java/com/ctrip/apollo/internals/DefaultConfigTest.java

@@ -28,7 +28,7 @@ public class DefaultConfigTest {
   @Before
   public void setUp() throws Exception {
     someResourceDir = new File(ClassLoaderUtil.getClassPath() + "/META-INF/config");
-    someResourceDir.mkdir();
+    someResourceDir.mkdirs();
     someNamespace = "someName";
     configRepository = mock(ConfigRepository.class);
   }

+ 25 - 15
apollo-client/src/test/java/com/ctrip/apollo/internals/LocalFileConfigRepositoryTest.java

@@ -8,6 +8,7 @@ import com.ctrip.apollo.util.ConfigUtil;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.unidal.lookup.ComponentTestCase;
 
 import java.io.File;
 import java.util.Properties;
@@ -21,15 +22,17 @@ import static org.mockito.Mockito.when;
 /**
  * Created by Jason on 4/9/16.
  */
-public class LocalFileConfigRepositoryTest {
+public class LocalFileConfigRepositoryTest extends ComponentTestCase {
   private File someBaseDir;
   private String someNamespace;
   private ConfigRepository fallbackRepo;
   private Properties someProperties;
-  private ConfigUtil someConfigUtil;
+  private static String someAppId = "someApp";
+  private static String someCluster = "someCluster";
 
   @Before
   public void setUp() throws Exception {
+    super.setUp();
     someBaseDir = new File("src/test/resources/config-cache");
     someBaseDir.mkdir();
 
@@ -39,12 +42,7 @@ public class LocalFileConfigRepositoryTest {
     fallbackRepo = mock(ConfigRepository.class);
     when(fallbackRepo.loadConfig()).thenReturn(someProperties);
 
-    String someAppId = "someApp";
-    String someCluster = "someCluster";
-    someConfigUtil = mock(ConfigUtil.class);
-    when(someConfigUtil.getAppId()).thenReturn(someAppId);
-    when(someConfigUtil.getCluster()).thenReturn(someCluster);
-
+    defineComponent(ConfigUtil.class, MockConfigUtil.class);
   }
 
   @After
@@ -66,8 +64,8 @@ public class LocalFileConfigRepositoryTest {
   }
 
   private String assembleLocalCacheFileName() {
-    return String.format("%s-%s-%s.properties", someConfigUtil.getAppId(),
-            someConfigUtil.getCluster(), someNamespace);
+    return String.format("%s-%s-%s.properties", someAppId,
+            someCluster, someNamespace);
   }
 
   @Test
@@ -84,7 +82,7 @@ public class LocalFileConfigRepositoryTest {
 
     Files.write(someKey + "=" + someValue, file, Charsets.UTF_8);
 
-    LocalFileConfigRepository localRepo = new LocalFileConfigRepository(someBaseDir, someNamespace, someConfigUtil);
+    LocalFileConfigRepository localRepo = new LocalFileConfigRepository(someBaseDir, someNamespace);
     Properties properties = localRepo.loadConfig();
 
     assertEquals(someValue, properties.getProperty(someKey));
@@ -95,7 +93,7 @@ public class LocalFileConfigRepositoryTest {
   public void testLoadConfigWithNoLocalFile() throws Exception {
     LocalFileConfigRepository
         localFileConfigRepository =
-        new LocalFileConfigRepository(someBaseDir, someNamespace, someConfigUtil);
+        new LocalFileConfigRepository(someBaseDir, someNamespace);
 
     localFileConfigRepository.setFallback(fallbackRepo);
 
@@ -109,7 +107,7 @@ public class LocalFileConfigRepositoryTest {
   @Test
   public void testLoadConfigWithNoLocalFileMultipleTimes() throws Exception {
     LocalFileConfigRepository localRepo =
-        new LocalFileConfigRepository(someBaseDir, someNamespace, someConfigUtil);
+        new LocalFileConfigRepository(someBaseDir, someNamespace);
 
     localRepo.setFallback(fallbackRepo);
 
@@ -117,7 +115,7 @@ public class LocalFileConfigRepositoryTest {
 
     LocalFileConfigRepository
         anotherLocalRepoWithNoFallback =
-        new LocalFileConfigRepository(someBaseDir, someNamespace, someConfigUtil);
+        new LocalFileConfigRepository(someBaseDir, someNamespace);
 
     Properties anotherProperties = anotherLocalRepoWithNoFallback.loadConfig();
 
@@ -127,4 +125,16 @@ public class LocalFileConfigRepositoryTest {
 
   }
 
-}
+  public static class MockConfigUtil extends ConfigUtil {
+    @Override
+    public String getAppId() {
+      return someAppId;
+    }
+
+    @Override
+    public String getCluster() {
+      return someCluster;
+    }
+  }
+
+}

+ 44 - 32
apollo-client/src/test/java/com/ctrip/apollo/internals/RemoteConfigRepositoryTest.java

@@ -6,26 +6,22 @@ import com.google.common.collect.Maps;
 import com.ctrip.apollo.core.dto.ApolloConfig;
 import com.ctrip.apollo.core.dto.ServiceDTO;
 import com.ctrip.apollo.util.ConfigUtil;
+import com.ctrip.apollo.util.http.HttpRequest;
+import com.ctrip.apollo.util.http.HttpResponse;
+import com.ctrip.apollo.util.http.HttpUtil;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.client.RestTemplate;
+import org.unidal.lookup.ComponentTestCase;
 
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 
 import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyMap;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -33,27 +29,23 @@ import static org.mockito.Mockito.when;
  * Created by Jason on 4/9/16.
  */
 @RunWith(MockitoJUnitRunner.class)
-public class RemoteConfigRepositoryTest {
-  @Mock
-  private RestTemplate restTemplate;
+public class RemoteConfigRepositoryTest extends ComponentTestCase {
   @Mock
   private ConfigServiceLocator configServiceLocator;
   private String someNamespace;
   @Mock
-  private ResponseEntity<ApolloConfig> someResponse;
+  private static HttpResponse<ApolloConfig> someResponse;
   @Mock
   private ConfigUtil someConfigUtil;
 
   @Before
   public void setUp() throws Exception {
+    super.setUp();
     someNamespace = "someName";
-    String someServerUrl = "http://someServer";
-    mockConfigServiceLocator(someServerUrl);
 
-    String someAppId = "someApp";
-    String someCluster = "someCluster";
-    when(someConfigUtil.getAppId()).thenReturn(someAppId);
-    when(someConfigUtil.getCluster()).thenReturn(someCluster);
+    defineComponent(ConfigUtil.class, MockConfigUtil.class);
+    defineComponent(ConfigServiceLocator.class, MockConfigServiceLocator.class);
+    defineComponent(HttpUtil.class, MockHttpUtil.class);
   }
 
   @Test
@@ -64,13 +56,10 @@ public class RemoteConfigRepositoryTest {
     configurations.put(someKey, someValue);
     ApolloConfig someApolloConfig = assembleApolloConfig(configurations);
 
-    when(someResponse.getStatusCode()).thenReturn(HttpStatus.OK);
+    when(someResponse.getStatusCode()).thenReturn(200);
     when(someResponse.getBody()).thenReturn(someApolloConfig);
-    when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class),
-            eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
-
 
-    RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(restTemplate, configServiceLocator, someConfigUtil, someNamespace);
+    RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(someNamespace);
     Properties config = remoteConfigRepository.loadConfig();
 
     assertEquals(configurations, config);
@@ -79,11 +68,9 @@ public class RemoteConfigRepositoryTest {
   @Test(expected = RuntimeException.class)
   public void testGetRemoteConfigWithServerError() throws Exception {
 
-    when(someResponse.getStatusCode()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR);
-    when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class),
-            eq(ApolloConfig.class), anyMap())).thenReturn(someResponse);
+    when(someResponse.getStatusCode()).thenReturn(500);
 
-    RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(restTemplate, configServiceLocator, someConfigUtil, someNamespace);
+    RemoteConfigRepository remoteConfigRepository = new RemoteConfigRepository(someNamespace);
     remoteConfigRepository.loadConfig();
   }
 
@@ -99,10 +86,35 @@ public class RemoteConfigRepositoryTest {
     return apolloConfig;
   }
 
-  private void mockConfigServiceLocator(String serverUrl) {
-    ServiceDTO serviceDTO = mock(ServiceDTO.class);
+  public static class MockConfigUtil extends ConfigUtil {
+    @Override
+    public String getAppId() {
+      return "someApp";
+    }
 
-    when(serviceDTO.getHomepageUrl()).thenReturn(serverUrl);
-    when(configServiceLocator.getConfigServices()).thenReturn(Lists.newArrayList(serviceDTO));
+    @Override
+    public String getCluster() {
+      return "someCluster";
+    }
   }
+
+  public static class MockConfigServiceLocator extends ConfigServiceLocator {
+    @Override
+    public List<ServiceDTO> getConfigServices() {
+      String someServerUrl = "http://someServer";
+
+      ServiceDTO serviceDTO = mock(ServiceDTO.class);
+
+      when(serviceDTO.getHomepageUrl()).thenReturn(someServerUrl);
+      return Lists.newArrayList(serviceDTO);
+    }
+  }
+
+  public static class MockHttpUtil extends HttpUtil {
+    @Override
+    public <T> HttpResponse<T> doGet(HttpRequest httpRequest, Class<T> responseType) {
+      return (HttpResponse<T>) someResponse;
+    }
+  }
+
 }

+ 2 - 6
apollo-core/pom.xml

@@ -15,12 +15,8 @@
     <dependencies>
         <!-- json -->
         <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-annotations</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-databind</artifactId>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
         </dependency>
         <!-- end of json -->
         <!-- util -->

+ 4 - 8
apollo-core/src/main/java/com/ctrip/apollo/core/dto/ApolloConfig.java

@@ -2,9 +2,6 @@ package com.ctrip.apollo.core.dto;
 
 import com.google.common.base.MoreObjects;
 
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
 import java.util.Map;
 
 /**
@@ -22,11 +19,10 @@ public class ApolloConfig {
 
   private long releaseId;
 
-  @JsonCreator
-  public ApolloConfig(@JsonProperty("appId") String appId,
-                      @JsonProperty("cluster") String cluster,
-                      @JsonProperty("namespace") String namespace,
-                      @JsonProperty("releaseId") long releaseId) {
+  public ApolloConfig(String appId,
+                      String cluster,
+                      String namespace,
+                      long releaseId) {
     super();
     this.appId = appId;
     this.cluster = cluster;