Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Load property source from remote config server
- Loading branch information
Jason Song
committed
Mar 17, 2016
1 parent
46baa01
commit a74c713
Showing
28 changed files
with
712 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
apollo-client/src/main/java/com/ctrip/apollo/client/constants/Constants.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.ctrip.apollo.client.constants; | ||
|
||
/** | ||
* @author Jason Song(song_s@ctrip.com) | ||
*/ | ||
public class Constants { | ||
public static final String APP_ID = "appId"; | ||
public static final String VERSION = "version"; | ||
public static final String DEFAULT_VERSION_NAME = "latest-release"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
124 changes: 123 additions & 1 deletion
124
apollo-client/src/main/java/com/ctrip/apollo/client/loader/impl/RemoteConfigLoader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,139 @@ | ||
package com.ctrip.apollo.client.loader.impl; | ||
|
||
import com.ctrip.apollo.client.loader.ConfigLoader; | ||
import com.ctrip.apollo.client.model.ApolloRegistry; | ||
import com.ctrip.apollo.client.util.ConfigUtil; | ||
import com.ctrip.apollo.core.environment.Environment; | ||
import com.ctrip.apollo.core.environment.PropertySource; | ||
import com.google.common.collect.Lists; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.core.env.CompositePropertySource; | ||
import org.springframework.core.env.MapPropertySource; | ||
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 java.io.IOException; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.concurrent.*; | ||
import java.util.concurrent.atomic.AtomicLong; | ||
|
||
/** | ||
* Load config from remote config server | ||
* | ||
* @author Jason Song(song_s@ctrip.com) | ||
*/ | ||
public class RemoteConfigLoader implements ConfigLoader { | ||
private static final String PROPERTY_SOURCE_NAME = "ApolloRemoteConfigProperties"; | ||
private static final Logger logger = LoggerFactory.getLogger(RemoteConfigLoader.class); | ||
private final RestTemplate restTemplate; | ||
private final ConfigUtil configUtil; | ||
private final ExecutorService executorService; | ||
private final AtomicLong counter; | ||
|
||
public RemoteConfigLoader() { | ||
this(new RestTemplate(), ConfigUtil.getInstance()); | ||
} | ||
|
||
public RemoteConfigLoader(RestTemplate restTemplate, ConfigUtil configUtil) { | ||
this.restTemplate = restTemplate; | ||
this.configUtil = configUtil; | ||
this.counter = new AtomicLong(); | ||
this.executorService = Executors.newFixedThreadPool(5, new ThreadFactory() { | ||
@Override | ||
public Thread newThread(Runnable r) { | ||
Thread thread = new Thread(r, "RemoteConfigLoader-" + counter.incrementAndGet()); | ||
return thread; | ||
} | ||
}); | ||
} | ||
|
||
@Override | ||
public CompositePropertySource loadPropertySource() { | ||
return null; | ||
CompositePropertySource composite = new CompositePropertySource(PROPERTY_SOURCE_NAME); | ||
List<ApolloRegistry> apolloRegistries; | ||
try { | ||
apolloRegistries = configUtil.loadApolloRegistries(); | ||
} catch (IOException e) { | ||
throw new RuntimeException("Load apollo config registry failed", e); | ||
} | ||
|
||
if (apolloRegistries == null || apolloRegistries.isEmpty()) { | ||
logger.warn("No Apollo Registry found!"); | ||
return composite; | ||
} | ||
|
||
try { | ||
doLoadRemoteApolloConfig(apolloRegistries, composite); | ||
} catch (Throwable throwable) { | ||
throw new RuntimeException("Load remote property source failed", throwable); | ||
} | ||
return composite; | ||
} | ||
|
||
void doLoadRemoteApolloConfig(List<ApolloRegistry> apolloRegistries, CompositePropertySource compositePropertySource) throws Throwable { | ||
List<Future<CompositePropertySource>> futures = Lists.newArrayList(); | ||
for (final ApolloRegistry apolloRegistry : apolloRegistries) { | ||
futures.add(executorService.submit(new Callable<CompositePropertySource>() { | ||
@Override | ||
public CompositePropertySource call() throws Exception { | ||
return loadSingleApolloConfig(apolloRegistry.getAppId(), apolloRegistry.getVersion()); | ||
} | ||
})); | ||
} | ||
for (Future<CompositePropertySource> future : futures) { | ||
try { | ||
compositePropertySource.addPropertySource(future.get()); | ||
} catch (ExecutionException e) { | ||
throw e.getCause(); | ||
} | ||
} | ||
} | ||
|
||
CompositePropertySource loadSingleApolloConfig(String appId, String version) { | ||
CompositePropertySource composite = new CompositePropertySource(appId + "-" + version); | ||
Environment result = | ||
this.getRemoteEnvironment(restTemplate, configUtil.getConfigServerUrl(), appId, configUtil.getCluster(), version); | ||
if (result == null) { | ||
logger.error("Loaded environment as null..."); | ||
return composite; | ||
} | ||
logger.info("Loaded environment: name={}, cluster={}, label={}, version={}", result.getName(), result.getProfiles(), result.getLabel(), result.getVersion()); | ||
|
||
for (PropertySource source : result.getPropertySources()) { | ||
composite.addPropertySource(new MapPropertySource(source.getName(), source.getSource())); | ||
} | ||
return composite; | ||
} | ||
|
||
Environment getRemoteEnvironment(RestTemplate restTemplate, String uri, String name, String cluster, String release) { | ||
logger.info("Loading environment from {}, name={}, cluster={}, release={}", uri, name, cluster, release); | ||
String path = "/{name}/{cluster}"; | ||
Object[] args = new String[] {name, cluster}; | ||
if (StringUtils.hasText(release)) { | ||
args = new String[] {name, cluster, release}; | ||
path = path + "/{release}"; | ||
} | ||
ResponseEntity<Environment> response = null; | ||
|
||
try { | ||
// TODO retry | ||
response = restTemplate.exchange(uri | ||
+ path, HttpMethod.GET, new HttpEntity<Void>((Void) null), Environment.class, args); | ||
} catch (Exception e) { | ||
throw e; | ||
} | ||
|
||
if (response == null || response.getStatusCode() != HttpStatus.OK) { | ||
return null; | ||
} | ||
Environment result = response.getBody(); | ||
return result; | ||
} | ||
|
||
} |
25 changes: 25 additions & 0 deletions
25
apollo-client/src/main/java/com/ctrip/apollo/client/model/ApolloRegistry.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.ctrip.apollo.client.model; | ||
|
||
/** | ||
* @author Jason Song(song_s@ctrip.com) | ||
*/ | ||
public class ApolloRegistry { | ||
private String appId; | ||
private String version; | ||
|
||
public String getAppId() { | ||
return appId; | ||
} | ||
|
||
public String getVersion() { | ||
return version; | ||
} | ||
|
||
public void setAppId(String appId) { | ||
this.appId = appId; | ||
} | ||
|
||
public void setVersion(String version) { | ||
this.version = version; | ||
} | ||
} |
80 changes: 80 additions & 0 deletions
80
apollo-client/src/main/java/com/ctrip/apollo/client/util/ConfigUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package com.ctrip.apollo.client.util; | ||
|
||
import com.ctrip.apollo.client.constants.Constants; | ||
import com.ctrip.apollo.client.model.ApolloRegistry; | ||
import com.google.common.base.Function; | ||
import com.google.common.base.Predicates; | ||
import com.google.common.collect.FluentIterable; | ||
import org.springframework.context.ApplicationContext; | ||
import org.springframework.core.io.Resource; | ||
import org.springframework.core.io.support.EncodedResource; | ||
import org.springframework.core.io.support.PropertiesLoaderUtils; | ||
|
||
import java.io.IOException; | ||
import java.net.URL; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Properties; | ||
|
||
/** | ||
* @author Jason Song(song_s@ctrip.com) | ||
*/ | ||
public class ConfigUtil { | ||
public static final String APOLLO_PROPERTY = "apollo.properties"; | ||
private static ConfigUtil configUtil = new ConfigUtil(); | ||
private ApplicationContext applicationContext; | ||
|
||
private ConfigUtil() { | ||
} | ||
|
||
public static ConfigUtil getInstance() { | ||
return configUtil; | ||
} | ||
|
||
public String getConfigServerUrl() { | ||
// TODO return the meta server url based on different environments | ||
return "http://localhost:8888"; | ||
} | ||
|
||
public String getCluster() { | ||
// TODO return the actual cluster | ||
return "default"; | ||
} | ||
|
||
public void setApplicationContext(ApplicationContext applicationContext) { | ||
this.applicationContext = applicationContext; | ||
} | ||
|
||
public List<ApolloRegistry> loadApolloRegistries() throws IOException { | ||
List<URL> resourceUrls = | ||
Collections.list(Thread.currentThread().getContextClassLoader().getResources(APOLLO_PROPERTY)); | ||
List<ApolloRegistry> registries = | ||
FluentIterable.from(resourceUrls).transform(new Function<URL, ApolloRegistry>() { | ||
@Override | ||
public ApolloRegistry apply(URL input) { | ||
Properties properties = loadPropertiesFromResourceURL(input); | ||
if (properties == null || !properties.containsKey(Constants.APP_ID)) { | ||
return null; | ||
} | ||
ApolloRegistry registry = new ApolloRegistry(); | ||
registry.setAppId(properties.getProperty(Constants.APP_ID)); | ||
registry.setVersion(properties.getProperty(Constants.VERSION, Constants.DEFAULT_VERSION_NAME)); | ||
return registry; | ||
} | ||
}).filter(Predicates.notNull()).toList(); | ||
return registries; | ||
} | ||
|
||
Properties loadPropertiesFromResourceURL(URL resourceUrl) { | ||
Resource resource = applicationContext.getResource(resourceUrl.toExternalForm()); | ||
if (resource == null || !resource.exists()) { | ||
return null; | ||
} | ||
try { | ||
return PropertiesLoaderUtils.loadProperties(new EncodedResource(resource, "UTF-8")); | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
} | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
appId=apollo | ||
version=master |
15 changes: 15 additions & 0 deletions
15
apollo-client/src/test/java/com/ctrip/apollo/client/AllTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.ctrip.apollo.client; | ||
|
||
import com.ctrip.apollo.client.loader.impl.RemoteConfigLoaderTest; | ||
import com.ctrip.apollo.client.util.ConfigUtilTest; | ||
import org.junit.runner.RunWith; | ||
import org.junit.runners.Suite; | ||
import org.junit.runners.Suite.SuiteClasses; | ||
|
||
@RunWith(Suite.class) | ||
@SuiteClasses({ | ||
ApolloConfigTest.class, RemoteConfigLoaderTest.class, ConfigUtilTest.class | ||
}) | ||
public class AllTests { | ||
|
||
} |
Oops, something went wrong.