未验证 提交 c0b096fd 编写于 作者: J Jason Song 提交者: GitHub

Merge branch 'master' into apollo-logo

......@@ -3,12 +3,13 @@ Apollo(配置中心)
[![Build Status](https://travis-ci.org/ctripcorp/apollo.svg?branch=master)](https://travis-ci.org/ctripcorp/apollo)
[![GitHub release](https://img.shields.io/github/release/ctripcorp/apollo.svg)](https://github.com/ctripcorp/apollo/releases)
![maven](https://img.shields.io/maven-central/v/com.ctrip.framework.apollo/apollo.svg)
[![Coverage Status](https://coveralls.io/repos/github/ctripcorp/apollo/badge.svg?branch=master)](https://coveralls.io/github/ctripcorp/apollo?branch=master)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
<a href="https://scan.coverity.com/projects/ctripcorp-apollo">
<img alt="Coverity Scan Build Status" src="https://img.shields.io/coverity/scan/8244.svg"/>
</a>
[![codecov.io](https://codecov.io/github/ctripcorp/apollo/coverage.svg?branch=master)](https://codecov.io/github/ctripcorp/apollo?branch=master)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
......@@ -199,3 +200,4 @@ The project is licensed under the [Apache 2 license](https://github.com/ctripcor
![河姆渡](https://raw.githubusercontent.com/ctripcorp/apollo/master/doc/images/known-users/homedo.png)
![新网银行](https://raw.githubusercontent.com/ctripcorp/apollo/master/doc/images/known-users/xwbank.png)
![中旅安信云贷](https://raw.githubusercontent.com/ctripcorp/apollo/master/doc/images/known-users/ctspcl.png)
![美柚](https://raw.githubusercontent.com/ctripcorp/apollo/master/doc/images/known-users/meiyou.png)
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.11.0</version>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -7,7 +7,7 @@
FROM openjdk:8-jre-alpine
MAINTAINER ameizi <sxyx2008@163.com>
ENV VERSION 0.11.0
ENV VERSION 1.0.0
RUN echo "http://mirrors.aliyun.com/alpine/v3.6/main" > /etc/apk/repositories \
&& echo "http://mirrors.aliyun.com/alpine/v3.6/community" >> /etc/apk/repositories \
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.11.0</version>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.11.0</version>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-biz</artifactId>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.11.0</version>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -88,7 +88,7 @@ If you need this functionality, you could specify the cluster as follows:
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-client</artifactId>
<version>0.11.0</version>
<version>1.0.0</version>
</dependency>
## III. Client Usage
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.11.0</version>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -162,12 +162,20 @@ public interface Config {
public long getDurationProperty(String key, long defaultValue);
/**
* Add change listener to this config instance.
* Add change listener to this config instance, will be notified when any key is changed in this namespace.
*
* @param listener the config change listener
*/
public void addChangeListener(ConfigChangeListener listener);
/**
* Add change listener to this config instance, will only be notified when any of the interested keys is changed in this namespace.
*
* @param listener the config change listener
* @param interestedKeys the keys interested by the listener
*/
public void addChangeListener(ConfigChangeListener listener, Set<String> interestedKeys);
/**
* Return a set of the property names
*
......
......@@ -40,10 +40,11 @@ import com.google.common.collect.Sets;
public abstract class AbstractConfig implements Config {
private static final Logger logger = LoggerFactory.getLogger(AbstractConfig.class);
private static ExecutorService m_executorService;
private static final ExecutorService m_executorService;
private List<ConfigChangeListener> m_listeners = Lists.newCopyOnWriteArrayList();
private ConfigUtil m_configUtil;
private final List<ConfigChangeListener> m_listeners = Lists.newCopyOnWriteArrayList();
private final Map<ConfigChangeListener, Set<String>> m_interestedKeys = Maps.newConcurrentMap();
private final ConfigUtil m_configUtil;
private volatile Cache<String, Integer> m_integerCache;
private volatile Cache<String, Long> m_longCache;
private volatile Cache<String, Short> m_shortCache;
......@@ -53,9 +54,9 @@ public abstract class AbstractConfig implements Config {
private volatile Cache<String, Boolean> m_booleanCache;
private volatile Cache<String, Date> m_dateCache;
private volatile Cache<String, Long> m_durationCache;
private Map<String, Cache<String, String[]>> m_arrayCache;
private List<Cache> allCaches;
private AtomicLong m_configVersion; //indicate config version
private final Map<String, Cache<String, String[]>> m_arrayCache;
private final List<Cache> allCaches;
private final AtomicLong m_configVersion; //indicate config version
static {
m_executorService = Executors.newCachedThreadPool(ApolloThreadFactory
......@@ -71,8 +72,16 @@ public abstract class AbstractConfig implements Config {
@Override
public void addChangeListener(ConfigChangeListener listener) {
addChangeListener(listener, null);
}
@Override
public void addChangeListener(ConfigChangeListener listener, Set<String> interestedKeys) {
if (!m_listeners.contains(listener)) {
m_listeners.add(listener);
if (interestedKeys != null && !interestedKeys.isEmpty()) {
m_interestedKeys.put(listener, Sets.newHashSet(interestedKeys));
}
}
}
......@@ -395,6 +404,10 @@ public abstract class AbstractConfig implements Config {
protected void fireConfigChange(final ConfigChangeEvent changeEvent) {
for (final ConfigChangeListener listener : m_listeners) {
// check whether the listener is interested in this change event
if (!isConfigChangeListenerInterested(listener, changeEvent)) {
continue;
}
m_executorService.submit(new Runnable() {
@Override
public void run() {
......@@ -415,6 +428,22 @@ public abstract class AbstractConfig implements Config {
}
}
private boolean isConfigChangeListenerInterested(ConfigChangeListener configChangeListener, ConfigChangeEvent configChangeEvent) {
Set<String> interestedKeys = m_interestedKeys.get(configChangeListener);
if (interestedKeys == null || interestedKeys.isEmpty()) {
return true; // no interested keys means interested in all keys
}
for (String interestedKey : interestedKeys) {
if (configChangeEvent.isChanged(interestedKey)) {
return true;
}
}
return false;
}
List<ConfigChange> calcPropertyChanges(String namespace, Properties previous,
Properties current) {
if (previous == null) {
......
package com.ctrip.framework.apollo.internals;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.spi.MetaServerProvider;
import com.ctrip.framework.foundation.Foundation;
import com.google.common.base.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DefaultMetaServerProvider implements MetaServerProvider {
public static final int ORDER = 0;
private static final Logger logger = LoggerFactory.getLogger(DefaultMetaServerProvider.class);
private final String metaServerAddress;
public DefaultMetaServerProvider() {
metaServerAddress = initMetaServerAddress();
}
private String initMetaServerAddress() {
// 1. Get from System Property
String metaAddress = System.getProperty(ConfigConsts.APOLLO_META_KEY);
if (Strings.isNullOrEmpty(metaAddress)) {
// 2. Get from OS environment variable, which could not contain dot and is normally in UPPER case
metaAddress = System.getenv("APOLLO_META");
}
if (Strings.isNullOrEmpty(metaAddress)) {
// 3. Get from server.properties
metaAddress = Foundation.server().getProperty(ConfigConsts.APOLLO_META_KEY, null);
}
if (Strings.isNullOrEmpty(metaAddress)) {
// 4. Get from app.properties
metaAddress = Foundation.app().getProperty(ConfigConsts.APOLLO_META_KEY, null);
}
if (Strings.isNullOrEmpty(metaAddress)) {
logger.warn("Could not find meta server address, because it is not available in neither (1) JVM system property 'apollo.meta', (2) OS env variable 'APOLLO_META' (3) property 'apollo.meta' from server.properties nor (4) property 'apollo.meta' from app.properties");
} else {
metaAddress = metaAddress.trim();
logger.info("Located meta services from apollo.meta configuration: {}!", metaAddress);
}
return metaAddress;
}
@Override
public String getMetaServerAddress(Env targetEnv) {
//for default meta server provider, we don't care the actual environment
return metaServerAddress;
}
@Override
public int getOrder() {
return ORDER;
}
}
......@@ -5,8 +5,10 @@ import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.google.common.base.Preconditions;
import com.google.common.collect.Sets;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Set;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ReflectionUtils;
......@@ -51,6 +53,8 @@ public class ApolloAnnotationProcessor extends ApolloProcessor {
ReflectionUtils.makeAccessible(method);
String[] namespaces = annotation.value();
String[] annotatedInterestedKeys = annotation.interestedKeys();
Set<String> interestedKeys = annotatedInterestedKeys.length > 0 ? Sets.newHashSet(annotatedInterestedKeys) : null;
ConfigChangeListener configChangeListener = new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
......@@ -61,7 +65,11 @@ public class ApolloAnnotationProcessor extends ApolloProcessor {
for (String namespace : namespaces) {
Config config = ConfigService.getConfig(namespace);
config.addChangeListener(configChangeListener);
if (interestedKeys == null) {
config.addChangeListener(configChangeListener);
} else {
config.addChangeListener(configChangeListener, interestedKeys);
}
}
}
}
......@@ -13,11 +13,17 @@ import com.ctrip.framework.apollo.core.ConfigConsts;
*
* <p>Usage example:</p>
* <pre class="code">
* //Listener on namespaces of "someNamespace" and "anotherNamespace"
* //Listener on namespaces of "someNamespace" and "anotherNamespace", will be notified when any key is changed
* &#064;ApolloConfigChangeListener({"someNamespace","anotherNamespace"})
* private void onChange(ConfigChangeEvent changeEvent) {
* //handle change event
* }
* <br />
* //Listener on namespaces of "someNamespace" and "anotherNamespace", will only be notified when "someKey" or "anotherKey" is changed
* &#064;ApolloConfigChangeListener(value = {"someNamespace","anotherNamespace"}, interestedKeys = {"someKey", "anotherKey"})
* private void onChange(ConfigChangeEvent changeEvent) {
* //handle change event
* }
* </pre>
*
* @author Jason Song(song_s@ctrip.com)
......@@ -30,4 +36,11 @@ public @interface ApolloConfigChangeListener {
* Apollo namespace for the config, if not specified then default to application
*/
String[] value() default {ConfigConsts.NAMESPACE_APPLICATION};
/**
* The keys interested by the listener, will only be notified if any of the interested keys is changed.
* <br />
* If not specified then will be notified when any key is changed.
*/
String[] interestedKeys() default {};
}
......@@ -7,6 +7,7 @@ import com.ctrip.framework.apollo.spring.config.ConfigPropertySourceFactory;
import com.ctrip.framework.apollo.spring.config.PropertySourcesConstants;
import com.ctrip.framework.apollo.spring.util.SpringInjector;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -16,17 +17,22 @@ import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* Inject the Apollo config in Spring Boot bootstrap phase
* Initialize apollo system properties and inject the Apollo config in Spring Boot bootstrap phase
*
* <p>Configuration example:</p>
* <pre class="code">
* # will inject 'application' namespace in bootstrap phase
* # set app.id
* app.id = 100004458
* # enable apollo bootstrap config and inject 'application' namespace in bootstrap phase
* apollo.bootstrap.enabled = true
* </pre>
*
* or
*
* <pre class="code">
* # set app.id
* app.id = 100004458
* # enable apollo bootstrap config
* apollo.bootstrap.enabled = true
* # will inject 'application' and 'FX.apollo' namespaces in bootstrap phase
* apollo.bootstrap.namespaces = application,FX.apollo
......@@ -36,6 +42,8 @@ public class ApolloApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger logger = LoggerFactory.getLogger(ApolloApplicationContextInitializer.class);
private static final Splitter NAMESPACE_SPLITTER = Splitter.on(",").omitEmptyStrings().trimResults();
private static final String[] APOLLO_SYSTEM_PROPERTIES = {"app.id", ConfigConsts.APOLLO_CLUSTER_KEY,
"apollo.cacheDir", ConfigConsts.APOLLO_META_KEY};
private final ConfigPropertySourceFactory configPropertySourceFactory = SpringInjector
.getInstance(ConfigPropertySourceFactory.class);
......@@ -43,6 +51,9 @@ public class ApolloApplicationContextInitializer implements
@Override
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment environment = context.getEnvironment();
initializeSystemProperty(environment);
String enabled = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, "false");
if (!Boolean.valueOf(enabled)) {
logger.debug("Apollo bootstrap config is not enabled for context {}, see property: ${{}}", context, PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED);
......@@ -68,4 +79,27 @@ public class ApolloApplicationContextInitializer implements
environment.getPropertySources().addFirst(composite);
}
/**
* To fill system properties from environment config
*/
void initializeSystemProperty(ConfigurableEnvironment environment) {
for (String propertyName : APOLLO_SYSTEM_PROPERTIES) {
fillSystemPropertyFromEnvironment(environment, propertyName);
}
}
private void fillSystemPropertyFromEnvironment(ConfigurableEnvironment environment, String propertyName) {
if (System.getProperty(propertyName) != null) {
return;
}
String propertyValue = environment.getProperty(propertyName);
if (Strings.isNullOrEmpty(propertyValue)) {
return;
}
System.setProperty(propertyName, propertyValue);
}
}
......@@ -10,7 +10,6 @@ import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.MetaDomainConsts;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.enums.EnvUtils;
import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
import com.ctrip.framework.foundation.Foundation;
import com.google.common.base.Strings;
......@@ -98,19 +97,10 @@ public class ConfigUtil {
/**
* Get the current environment.
*
* @return the env
* @throws ApolloConfigException if env is set
* @return the env, UNKNOWN if env is not set or invalid
*/
public Env getApolloEnv() {
Env env = EnvUtils.transformEnv(Foundation.server().getEnvType());
if (env == null) {
String path = isOSWindows() ? "C:\\opt\\settings\\server.properties" :
"/opt/settings/server.properties";
String message = String.format("env is not set, please make sure it is set in %s!", path);
logger.error(message);
throw new ApolloConfigException(message);
}
return env;
return EnvUtils.transformEnv(Foundation.server().getEnvType());
}
public String getLocalIp() {
......@@ -234,8 +224,7 @@ public class ConfigUtil {
public boolean isInLocalMode() {
try {
Env env = getApolloEnv();
return env == Env.LOCAL;
return Env.LOCAL == getApolloEnv();
} catch (Throwable ex) {
//ignore
}
......
......@@ -2,14 +2,18 @@ package com.ctrip.framework.apollo.internals;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.io.File;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Collections;
......@@ -647,6 +651,56 @@ public class DefaultConfigTest {
assertEquals(PropertyChangeType.ADDED, newKeyChange.getChangeType());
}
@Test
public void testFireConfigChangeWithInterestedKeys() throws Exception {
String someKeyChanged = "someKeyChanged";
String anotherKeyChanged = "anotherKeyChanged";
String someKeyNotChanged = "someKeyNotChanged";
String someNamespace = "someNamespace";
Map<String, ConfigChange> changes = Maps.newHashMap();
changes.put(someKeyChanged, mock(ConfigChange.class));
changes.put(anotherKeyChanged, mock(ConfigChange.class));
ConfigChangeEvent someChangeEvent = new ConfigChangeEvent(someNamespace, changes);
final SettableFuture<ConfigChangeEvent> interestedInAllKeysFuture = SettableFuture.create();
ConfigChangeListener interestedInAllKeys = new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
interestedInAllKeysFuture.set(changeEvent);
}
};
final SettableFuture<ConfigChangeEvent> interestedInSomeKeyFuture = SettableFuture.create();
ConfigChangeListener interestedInSomeKey = new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
interestedInSomeKeyFuture.set(changeEvent);
}
};
final SettableFuture<ConfigChangeEvent> interestedInSomeKeyNotChangedFuture = SettableFuture.create();
ConfigChangeListener interestedInSomeKeyNotChanged = new ConfigChangeListener() {
@Override
public void onChange(ConfigChangeEvent changeEvent) {
interestedInSomeKeyNotChangedFuture.set(changeEvent);
}
};
DefaultConfig config = new DefaultConfig(someNamespace, mock(ConfigRepository.class));
config.addChangeListener(interestedInAllKeys);
config.addChangeListener(interestedInSomeKey, Sets.newHashSet(someKeyChanged));
config.addChangeListener(interestedInSomeKeyNotChanged, Sets.newHashSet(someKeyNotChanged));
config.fireConfigChange(someChangeEvent);
ConfigChangeEvent changeEvent = interestedInAllKeysFuture.get(500, TimeUnit.MILLISECONDS);
assertEquals(someChangeEvent, changeEvent);
assertEquals(someChangeEvent, interestedInSomeKeyFuture.get(500, TimeUnit.MILLISECONDS));
assertFalse(interestedInSomeKeyNotChangedFuture.isDone());
}
@Test
public void testGetPropertyNames() {
String someKeyPrefix = "someKey";
......
package com.ctrip.framework.apollo.internals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.enums.Env;
import org.junit.After;
import org.junit.Test;
public class DefaultMetaServerProviderTest {
@After
public void tearDown() throws Exception {
System.clearProperty(ConfigConsts.APOLLO_META_KEY);
}
@Test
public void testWithSystemProperty() throws Exception {
String someMetaAddress = "someMetaAddress";
Env someEnv = Env.DEV;
System.setProperty(ConfigConsts.APOLLO_META_KEY, " " + someMetaAddress + " ");
DefaultMetaServerProvider defaultMetaServerProvider = new DefaultMetaServerProvider();
assertEquals(someMetaAddress, defaultMetaServerProvider.getMetaServerAddress(someEnv));
}
@Test
public void testWithNoSystemProperty() throws Exception {
Env someEnv = Env.DEV;
DefaultMetaServerProvider defaultMetaServerProvider = new DefaultMetaServerProvider();
assertNull(defaultMetaServerProvider.getMetaServerAddress(someEnv));
}
}
package com.ctrip.framework.apollo.spring;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import java.util.List;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
......@@ -23,6 +16,17 @@ import com.ctrip.framework.apollo.spring.annotation.ApolloConfig;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.List;
import java.util.Set;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Jason Song(song_s@ctrip.com)
......@@ -200,6 +204,39 @@ public class JavaConfigAnnotationTest extends AbstractSpringIntegrationTest {
assertEquals(someEvent, bean.getSomeChangeEvent());
}
@Test
public void testApolloConfigChangeListenerWithInterestedKeys() throws Exception {
Config applicationConfig = mock(Config.class);
Config fxApolloConfig = mock(Config.class);
mockConfig(ConfigConsts.NAMESPACE_APPLICATION, applicationConfig);
mockConfig(FX_APOLLO_NAMESPACE, fxApolloConfig);
TestApolloConfigChangeListenerWithInterestedKeysBean bean = getBean(
TestApolloConfigChangeListenerWithInterestedKeysBean.class, AppConfig8.class);
final ArgumentCaptor<Set> applicationConfigInterestedKeys = ArgumentCaptor.forClass(Set.class);
final ArgumentCaptor<Set> fxApolloConfigInterestedKeys = ArgumentCaptor.forClass(Set.class);
verify(applicationConfig, times(2))
.addChangeListener(any(ConfigChangeListener.class), applicationConfigInterestedKeys.capture());
verify(fxApolloConfig, times(1))
.addChangeListener(any(ConfigChangeListener.class), fxApolloConfigInterestedKeys.capture());
assertEquals(2, applicationConfigInterestedKeys.getAllValues().size());
Set<String> result = Sets.newHashSet();
for (Set interestedKeys : applicationConfigInterestedKeys.getAllValues()) {
result.addAll(interestedKeys);
}
assertEquals(Sets.newHashSet("someKey", "anotherKey"), result);
assertEquals(1, fxApolloConfigInterestedKeys.getAllValues().size());
assertEquals(asList(Sets.newHashSet("anotherKey")), fxApolloConfigInterestedKeys.getAllValues());
}
private <T> T getBean(Class<T> beanClass, Class<?>... annotatedClasses) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(annotatedClasses);
......@@ -269,6 +306,15 @@ public class JavaConfigAnnotationTest extends AbstractSpringIntegrationTest {
}
}
@Configuration
@EnableApolloConfig
static class AppConfig8 {
@Bean
public TestApolloConfigChangeListenerWithInterestedKeysBean bean() {
return new TestApolloConfigChangeListenerWithInterestedKeysBean();
}
}
static class TestApolloConfigBean1 {
@ApolloConfig
private Config config;
......@@ -365,4 +411,16 @@ public class JavaConfigAnnotationTest extends AbstractSpringIntegrationTest {
return someChangeEvent;
}
}
static class TestApolloConfigChangeListenerWithInterestedKeysBean {
@ApolloConfigChangeListener(interestedKeys = {"someKey"})
private void someOnChange(ConfigChangeEvent changeEvent) {}
@ApolloConfigChangeListener(value = {ConfigConsts.NAMESPACE_APPLICATION, FX_APOLLO_NAMESPACE},
interestedKeys = {"anotherKey"})
private void anotherOnChange(ConfigChangeEvent changeEvent) {
}
}
}
package com.ctrip.framework.apollo.spring;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import com.google.common.collect.Sets;
import java.util.List;
import java.util.Set;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.BeanCreationException;
......@@ -125,6 +131,39 @@ public class XMLConfigAnnotationTest extends AbstractSpringIntegrationTest {
getBean("spring/XmlConfigAnnotationTest5.xml", TestApolloConfigChangeListenerBean3.class);
}
@Test
public void testApolloConfigChangeListenerWithInterestedKeys() throws Exception {
Config applicationConfig = mock(Config.class);
Config fxApolloConfig = mock(Config.class);
mockConfig(ConfigConsts.NAMESPACE_APPLICATION, applicationConfig);
mockConfig(FX_APOLLO_NAMESPACE, fxApolloConfig);
TestApolloConfigChangeListenerWithInterestedKeysBean bean = getBean(
"spring/XmlConfigAnnotationTest6.xml", TestApolloConfigChangeListenerWithInterestedKeysBean.class);
final ArgumentCaptor<Set> applicationConfigInterestedKeys = ArgumentCaptor.forClass(Set.class);
final ArgumentCaptor<Set> fxApolloConfigInterestedKeys = ArgumentCaptor.forClass(Set.class);
verify(applicationConfig, times(2))
.addChangeListener(any(ConfigChangeListener.class), applicationConfigInterestedKeys.capture());
verify(fxApolloConfig, times(1))
.addChangeListener(any(ConfigChangeListener.class), fxApolloConfigInterestedKeys.capture());
assertEquals(2, applicationConfigInterestedKeys.getAllValues().size());
Set<String> result = Sets.newHashSet();
for (Set interestedKeys : applicationConfigInterestedKeys.getAllValues()) {
result.addAll(interestedKeys);
}
assertEquals(Sets.newHashSet("someKey", "anotherKey"), result);
assertEquals(1, fxApolloConfigInterestedKeys.getAllValues().size());
assertEquals(asList(Sets.newHashSet("anotherKey")), fxApolloConfigInterestedKeys.getAllValues());
}
private <T> T getBean(String xmlLocation, Class<T> beanClass) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(xmlLocation);
......@@ -204,4 +243,15 @@ public class XMLConfigAnnotationTest extends AbstractSpringIntegrationTest {
}
}
static class TestApolloConfigChangeListenerWithInterestedKeysBean {
@ApolloConfigChangeListener(interestedKeys = {"someKey"})
private void someOnChange(ConfigChangeEvent changeEvent) {}
@ApolloConfigChangeListener(value = {ConfigConsts.NAMESPACE_APPLICATION, FX_APOLLO_NAMESPACE},
interestedKeys = {"anotherKey"})
private void anotherOnChange(ConfigChangeEvent changeEvent) {
}
}
}
package com.ctrip.framework.apollo.spring.boot;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.ctrip.framework.apollo.core.ConfigConsts;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.env.ConfigurableEnvironment;
public class ApolloApplicationContextInitializerTest {
private ApolloApplicationContextInitializer apolloApplicationContextInitializer;
@Before
public void setUp() throws Exception {
apolloApplicationContextInitializer = new ApolloApplicationContextInitializer();
}
@After
public void tearDown() throws Exception {
System.clearProperty("app.id");
System.clearProperty(ConfigConsts.APOLLO_CLUSTER_KEY);
System.clearProperty("apollo.cacheDir");
System.clearProperty(ConfigConsts.APOLLO_META_KEY);
}
@Test
public void testFillFromEnvironment() throws Exception {
String someAppId = "someAppId";
String someCluster = "someCluster";
String someCacheDir = "someCacheDir";
String someApolloMeta = "someApolloMeta";
ConfigurableEnvironment environment = mock(ConfigurableEnvironment.class);
when(environment.getProperty("app.id")).thenReturn(someAppId);
when(environment.getProperty(ConfigConsts.APOLLO_CLUSTER_KEY)).thenReturn(someCluster);
when(environment.getProperty("apollo.cacheDir")).thenReturn(someCacheDir);
when(environment.getProperty(ConfigConsts.APOLLO_META_KEY)).thenReturn(someApolloMeta);
apolloApplicationContextInitializer.initializeSystemProperty(environment);
assertEquals(someAppId, System.getProperty("app.id"));
assertEquals(someCluster, System.getProperty(ConfigConsts.APOLLO_CLUSTER_KEY));
assertEquals(someCacheDir, System.getProperty("apollo.cacheDir"));
assertEquals(someApolloMeta, System.getProperty(ConfigConsts.APOLLO_META_KEY));
}
@Test
public void testFillFromEnvironmentWithSystemPropertyAlreadyFilled() throws Exception {
String someAppId = "someAppId";
String someCluster = "someCluster";
String someCacheDir = "someCacheDir";
String someApolloMeta = "someApolloMeta";
System.setProperty("app.id", someAppId);
System.setProperty(ConfigConsts.APOLLO_CLUSTER_KEY, someCluster);
System.setProperty("apollo.cacheDir", someCacheDir);
System.setProperty(ConfigConsts.APOLLO_META_KEY, someApolloMeta);
String anotherAppId = "anotherAppId";
String anotherCluster = "anotherCluster";
String anotherCacheDir = "anotherCacheDir";
String anotherApolloMeta = "anotherApolloMeta";
ConfigurableEnvironment environment = mock(ConfigurableEnvironment.class);
when(environment.getProperty("app.id")).thenReturn(anotherAppId);
when(environment.getProperty(ConfigConsts.APOLLO_CLUSTER_KEY)).thenReturn(anotherCluster);
when(environment.getProperty("apollo.cacheDir")).thenReturn(anotherCacheDir);
when(environment.getProperty(ConfigConsts.APOLLO_META_KEY)).thenReturn(anotherApolloMeta);
apolloApplicationContextInitializer.initializeSystemProperty(environment);
assertEquals(someAppId, System.getProperty("app.id"));
assertEquals(someCluster, System.getProperty(ConfigConsts.APOLLO_CLUSTER_KEY));
assertEquals(someCacheDir, System.getProperty("apollo.cacheDir"));
assertEquals(someApolloMeta, System.getProperty(ConfigConsts.APOLLO_META_KEY));
}
@Test
public void testFillFromEnvironmentWithNoPropertyFromEnvironment() throws Exception {
ConfigurableEnvironment environment = mock(ConfigurableEnvironment.class);
apolloApplicationContextInitializer.initializeSystemProperty(environment);
assertNull(System.getProperty("app.id"));
assertNull(System.getProperty(ConfigConsts.APOLLO_CLUSTER_KEY));
assertNull(System.getProperty("apollo.cacheDir"));
assertNull(System.getProperty(ConfigConsts.APOLLO_META_KEY));
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:apollo="http://www.ctrip.com/schema/apollo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.ctrip.com/schema/apollo http://www.ctrip.com/schema/apollo.xsd">
<apollo:config/>
<bean class="com.ctrip.framework.apollo.spring.XMLConfigAnnotationTest.TestApolloConfigChangeListenerWithInterestedKeysBean"/>
</beans>
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.11.0</version>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -30,9 +30,6 @@ public class TitanSettings {
public String getTitanUrl() {
Env env = EnvUtils.transformEnv(Foundation.server().getEnvType());
if (env == null) {
return "";
}
switch (env) {
case FAT:
case FWS:
......@@ -49,9 +46,6 @@ public class TitanSettings {
public String getTitanDbname() {
Env env = EnvUtils.transformEnv(Foundation.server().getEnvType());
if (env == null) {
return "";
}
switch (env) {
case FAT:
case FWS:
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.11.0</version>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -7,7 +7,7 @@
FROM openjdk:8-jre-alpine
MAINTAINER ameizi <sxyx2008@163.com>
ENV VERSION 0.11.0
ENV VERSION 1.0.0
RUN echo "http://mirrors.aliyun.com/alpine/v3.6/main" > /etc/apk/repositories \
&& echo "http://mirrors.aliyun.com/alpine/v3.6/community" >> /etc/apk/repositories \
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.11.0</version>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
......
......@@ -5,6 +5,7 @@ public interface ConfigConsts {
String CLUSTER_NAME_DEFAULT = "default";
String CLUSTER_NAMESPACE_SEPARATOR = "+";
String APOLLO_CLUSTER_KEY = "apollo.cluster";
String APOLLO_META_KEY = "apollo.meta";
String CONFIG_FILE_CONTENT_KEY = "content";
String NO_APPID_PLACEHOLDER = "ApolloNoAppIdPlaceHolder";
long NOTIFICATION_ID_PLACEHOLDER = -1;
......
package com.ctrip.framework.apollo.core;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory;
import com.ctrip.framework.apollo.core.utils.NetUtil;
import com.ctrip.framework.apollo.core.utils.ResourceUtils;
import com.ctrip.framework.apollo.tracer.Tracer;
import com.ctrip.framework.apollo.tracer.spi.Transaction;
import com.ctrip.framework.foundation.Foundation;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.Collections;
import java.util.HashMap;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ctrip.framework.apollo.core.spi.MetaServerProvider;
import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory;
import com.ctrip.framework.apollo.core.utils.NetUtil;
import com.ctrip.framework.apollo.tracer.Tracer;
import com.ctrip.framework.apollo.tracer.spi.Transaction;
import com.ctrip.framework.foundation.internals.ServiceBootstrap;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
/**
* The meta domain will load the meta server from System environment first, if not exist, will load from
* apollo-env.properties. If neither exists, will load the default meta url.
* The meta domain will try to load the meta server address from MetaServerProviders, the default ones are:
*
* <ul>
* <li>com.ctrip.framework.apollo.core.internals.LegacyMetaServerProvider</li>
* </ul>
*
* Currently, apollo supports local/dev/fat/uat/lpt/pro environments.
* If no provider could provide the meta server url, the default meta url will be used(http://apollo.meta).
* <br />
*
* 3rd party MetaServerProvider could be injected by typical Java Service Loader pattern.
*
* @see com.ctrip.framework.apollo.core.internals.LegacyMetaServerProvider
*/
public class MetaDomainConsts {
public static final String DEFAULT_META_URL = "http://apollo.meta";
public static final String DEFAULT_META_URL = "http://config.local";
// env -> meta server address cache
private static final Map<Env, String> metaServerAddressCache = Maps.newConcurrentMap();
private static volatile List<MetaServerProvider> metaServerProviders = null;
private static final long REFRESH_INTERVAL_IN_SECOND = 60;//1 min
private static final long REFRESH_INTERVAL_IN_SECOND = 60;// 1 min
private static final Logger logger = LoggerFactory.getLogger(MetaDomainConsts.class);
private static final Map<Env, String> domains = new HashMap<>();
private static final Map<String, String> metaServerAddressCache = Maps.newConcurrentMap();
// comma separated meta server address -> selected single meta server address cache
private static final Map<String, String> selectedMetaServerAddressCache = Maps.newConcurrentMap();
private static final AtomicBoolean periodicRefreshStarted = new AtomicBoolean(false);
private static final AtomicBoolean customizedMetaServiceLogged = new AtomicBoolean(false);
static {
initialize();
}
static void initialize() {
Properties prop = new Properties();
prop = ResourceUtils.readConfigFile("apollo-env.properties", prop);
Properties env = System.getProperties();
domains.put(Env.LOCAL,
env.getProperty("local_meta", prop.getProperty("local.meta", DEFAULT_META_URL)));
domains.put(Env.DEV,
env.getProperty("dev_meta", prop.getProperty("dev.meta", DEFAULT_META_URL)));
domains.put(Env.FAT,
env.getProperty("fat_meta", prop.getProperty("fat.meta", DEFAULT_META_URL)));
domains.put(Env.UAT,
env.getProperty("uat_meta", prop.getProperty("uat.meta", DEFAULT_META_URL)));
domains.put(Env.LPT,
env.getProperty("lpt_meta", prop.getProperty("lpt.meta", DEFAULT_META_URL)));
domains.put(Env.PRO,
env.getProperty("pro_meta", prop.getProperty("pro.meta", DEFAULT_META_URL)));
}
private static final Object LOCK = new Object();
public static String getDomain(Env env) {
// 1. Get meta server address from run time configurations
String metaAddress = getCustomizedMetaServerAddress();
if (Strings.isNullOrEmpty(metaAddress)) {
// 2. Get meta server address from environment
metaAddress = domains.get(env);
}
// 3. if there is more than one address, need to select one
if (metaAddress != null && metaAddress.contains(",")) {
return selectMetaServerAddress(metaAddress);
String metaServerAddress = getMetaServerAddress(env);
// if there is more than one address, need to select one
if (metaServerAddress.contains(",")) {
return selectMetaServerAddress(metaServerAddress);
}
// 4. trim if necessary
if (metaAddress != null) {
metaAddress = metaAddress.trim();
return metaServerAddress;
}
private static String getMetaServerAddress(Env env) {
if (!metaServerAddressCache.containsKey(env)) {
initMetaServerAddress(env);
}
return metaAddress;
return metaServerAddressCache.get(env);
}
private static String getCustomizedMetaServerAddress() {
// 1. Get from System Property
String metaAddress = System.getProperty("apollo.meta");
if (Strings.isNullOrEmpty(metaAddress)) {
// 2. Get from OS environment variable
metaAddress = System.getenv("APOLLO_META");
private static void initMetaServerAddress(Env env) {
if (metaServerProviders == null) {
synchronized (LOCK) {
if (metaServerProviders == null) {
metaServerProviders = initMetaServerProviders();
}
}
}
if (Strings.isNullOrEmpty(metaAddress)) {
metaAddress = Foundation.server().getProperty("apollo.meta", null);
String metaAddress = null;
for (MetaServerProvider provider : metaServerProviders) {
metaAddress = provider.getMetaServerAddress(env);
if (!Strings.isNullOrEmpty(metaAddress)) {
logger.info("Located meta server address {} for env {} from {}", metaAddress, env,
provider.getClass().getName());
break;
}
}
if (!Strings.isNullOrEmpty(metaAddress) && customizedMetaServiceLogged.compareAndSet(false, true)) {
logger.warn("Located meta services from apollo.meta configuration: {}, will not use meta services defined in apollo-env.properties!", metaAddress);
if (Strings.isNullOrEmpty(metaAddress)) {
// Fallback to default meta address
metaAddress = DEFAULT_META_URL;
logger.warn(
"Meta server address fallback to {} for env {}, because it is not available in all MetaServerProviders",
metaAddress, env);
}
return metaAddress;
metaServerAddressCache.put(env, metaAddress.trim());
}
private static List<MetaServerProvider> initMetaServerProviders() {
Iterator<MetaServerProvider> metaServerProviderIterator = ServiceBootstrap.loadAll(MetaServerProvider.class);
List<MetaServerProvider> metaServerProviders = Lists.newArrayList(metaServerProviderIterator);
Collections.sort(metaServerProviders, new Comparator<MetaServerProvider>() {
@Override
public int compare(MetaServerProvider o1, MetaServerProvider o2) {
// the smaller order has higher priority
return Integer.compare(o1.getOrder(), o2.getOrder());
}
});
return metaServerProviders;
}
/**
......@@ -104,18 +123,18 @@ public class MetaDomainConsts {
*
* <br />
*
* In production environment, we still suggest using one single domain
* like http://config.xxx.com(backed by software load balancers like nginx) instead of multiple ip addresses
* In production environment, we still suggest using one single domain like http://config.xxx.com(backed by software
* load balancers like nginx) instead of multiple ip addresses
*/
private static String selectMetaServerAddress(String metaServerAddresses) {
String metaAddressSelected = metaServerAddressCache.get(metaServerAddresses);
String metaAddressSelected = selectedMetaServerAddressCache.get(metaServerAddresses);
if (metaAddressSelected == null) {
//initialize
// initialize
if (periodicRefreshStarted.compareAndSet(false, true)) {
schedulePeriodicRefresh();
}
updateMetaServerAddresses(metaServerAddresses);
metaAddressSelected = metaServerAddressCache.get(metaServerAddresses);
metaAddressSelected = selectedMetaServerAddressCache.get(metaServerAddresses);
}
return metaAddressSelected;
......@@ -129,7 +148,7 @@ public class MetaDomainConsts {
try {
List<String> metaServers = Lists.newArrayList(metaServerAddresses.split(","));
//random load balancing
// random load balancing
Collections.shuffle(metaServers);
boolean serverAvailable = false;
......@@ -137,22 +156,22 @@ public class MetaDomainConsts {
for (String address : metaServers) {
address = address.trim();
if (NetUtil.pingUrl(address)) {
//select the first available meta server
metaServerAddressCache.put(metaServerAddresses, address);
// select the first available meta server
selectedMetaServerAddressCache.put(metaServerAddresses, address);
serverAvailable = true;
logger.debug("Selected meta server address {} for {}", address, metaServerAddresses);
break;
}
}
//we need to make sure the map is not empty, e.g. the first update might be failed
if (!metaServerAddressCache.containsKey(metaServerAddresses)) {
metaServerAddressCache.put(metaServerAddresses, metaServers.get(0).trim());
// we need to make sure the map is not empty, e.g. the first update might be failed
if (!selectedMetaServerAddressCache.containsKey(metaServerAddresses)) {
selectedMetaServerAddressCache.put(metaServerAddresses, metaServers.get(0).trim());
}
if (!serverAvailable) {
logger.warn("Could not find available meta server for configured meta server addresses: {}, fallback to: {}", metaServerAddresses,
metaServerAddressCache.get(metaServerAddresses));
logger.warn("Could not find available meta server for configured meta server addresses: {}, fallback to: {}",
metaServerAddresses, selectedMetaServerAddressCache.get(metaServerAddresses));
}
transaction.setStatus(Transaction.SUCCESS);
......@@ -165,21 +184,19 @@ public class MetaDomainConsts {
}
private static void schedulePeriodicRefresh() {
ScheduledExecutorService scheduledExecutorService = Executors
.newScheduledThreadPool(1, ApolloThreadFactory.create("MetaServiceLocator", true));
ScheduledExecutorService scheduledExecutorService =
Executors.newScheduledThreadPool(1, ApolloThreadFactory.create("MetaServiceLocator", true));
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
for (String metaServerAddresses : metaServerAddressCache.keySet()) {
for (String metaServerAddresses : selectedMetaServerAddressCache.keySet()) {
updateMetaServerAddresses(metaServerAddresses);
}
} catch (Throwable ex) {
logger.warn(
String.format("Refreshing meta server address failed, will retry in %d seconds",
REFRESH_INTERVAL_IN_SECOND), ex
);
logger.warn(String.format("Refreshing meta server address failed, will retry in %d seconds",
REFRESH_INTERVAL_IN_SECOND), ex);
}
}
}, REFRESH_INTERVAL_IN_SECOND, REFRESH_INTERVAL_IN_SECOND, TimeUnit.SECONDS);
......
......@@ -19,11 +19,11 @@ import com.google.common.base.Preconditions;
* @author Jason Song(song_s@ctrip.com)
*/
public enum Env{
LOCAL, DEV, FWS, FAT, UAT, LPT, PRO, TOOLS;
LOCAL, DEV, FWS, FAT, UAT, LPT, PRO, TOOLS, UNKNOWN;
public static Env fromString(String env) {
Env environment = EnvUtils.transformEnv(env);
Preconditions.checkArgument(environment != null, String.format("Env %s is invalid", env));
Preconditions.checkArgument(environment != UNKNOWN, String.format("Env %s is invalid", env));
return environment;
}
}
......@@ -6,7 +6,7 @@ public final class EnvUtils {
public static Env transformEnv(String envName) {
if (StringUtils.isBlank(envName)) {
return null;
return Env.UNKNOWN;
}
switch (envName.trim().toUpperCase()) {
case "LPT":
......@@ -26,7 +26,7 @@ public final class EnvUtils {
case "TOOLS":
return Env.TOOLS;
default:
return null;
return Env.UNKNOWN;
}
}
}
package com.ctrip.framework.apollo.core.internals;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.spi.MetaServerProvider;
import com.ctrip.framework.apollo.core.utils.ResourceUtils;
/**
* For legacy meta server configuration use, i.e. apollo-env.properties
*/
public class LegacyMetaServerProvider implements MetaServerProvider {
// make it as lowest as possible, yet not the lowest
public static final int ORDER = MetaServerProvider.LOWEST_PRECEDENCE - 1;
private static final Map<Env, String> domains = new HashMap<>();
public LegacyMetaServerProvider() {
initialize();
}
private void initialize() {
Properties prop = new Properties();
prop = ResourceUtils.readConfigFile("apollo-env.properties", prop);
Properties env = System.getProperties();
domains.put(Env.LOCAL,
env.getProperty("local_meta", prop.getProperty("local.meta")));
domains.put(Env.DEV,
env.getProperty("dev_meta", prop.getProperty("dev.meta")));
domains.put(Env.FAT,
env.getProperty("fat_meta", prop.getProperty("fat.meta")));
domains.put(Env.UAT,
env.getProperty("uat_meta", prop.getProperty("uat.meta")));
domains.put(Env.LPT,
env.getProperty("lpt_meta", prop.getProperty("lpt.meta")));
domains.put(Env.PRO,
env.getProperty("pro_meta", prop.getProperty("pro.meta")));
}
@Override
public String getMetaServerAddress(Env targetEnv) {
String metaServerAddress = domains.get(targetEnv);
return metaServerAddress == null ? null : metaServerAddress.trim();
}
@Override
public int getOrder() {
return ORDER;
}
}
package com.ctrip.framework.apollo.core.spi;
import com.ctrip.framework.apollo.core.enums.Env;
public interface MetaServerProvider extends Ordered {
/**
* Provide the Apollo meta server address, could be a domain url or comma separated ip addresses, like http://1.2.3.4:8080,http://2.3.4.5:8080.
* <br/>
* In production environment, we suggest using one single domain like http://config.xxx.com(backed by software load balancers like nginx) instead of multiple ip addresses
*/
String getMetaServerAddress(Env targetEnv);
}
package com.ctrip.framework.apollo.core.spi;
/**
* {@code Ordered} is an interface that can be implemented by objects that
* should be <em>orderable</em>, for example in a {@code Collection}.
*
* <p>The actual {@link #getOrder() order} can be interpreted as prioritization,
* with the first object (with the lowest order value) having the highest
* priority.
*/
public interface Ordered {
/**
* Useful constant for the highest precedence value.
* @see java.lang.Integer#MIN_VALUE
*/
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
/**
* Useful constant for the lowest precedence value.
* @see java.lang.Integer#MAX_VALUE
*/
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
/**
* Get the order value of this object.
* <p>Higher values are interpreted as lower priority. As a consequence,
* the object with the lowest value has the highest priority (somewhat
* analogous to Servlet {@code load-on-startup} values).
* <p>Same order values will result in arbitrary sort positions for the
* affected objects.
* @return the order value
* @see #HIGHEST_PRECEDENCE
* @see #LOWEST_PRECEDENCE
*/
int getOrder();
}
......@@ -51,7 +51,7 @@ public class ResourceUtils {
if (sb.length() > 0) {
logger.debug("Reading properties: \n" + sb.toString());
} else {
logger.warn("No available properties");
logger.warn("No available properties: {}", configPath);
}
}
return props;
......
......@@ -14,7 +14,7 @@ public class ServiceBootstrap {
return iterator.next();
}
private static <S> Iterator<S> loadAll(Class<S> clazz) {
public static <S> Iterator<S> loadAll(Class<S> clazz) {
ServiceLoader<S> loader = ServiceLoader.load(clazz);
return loader.iterator();
......
......@@ -28,9 +28,6 @@ public class DefaultApplicationProvider implements ApplicationProvider {
in = DefaultApplicationProvider.class.getResourceAsStream(APP_PROPERTIES_CLASSPATH);
}
if (in == null) {
logger.warn("{} not found from classpath!", APP_PROPERTIES_CLASSPATH);
}
initialize(in);
} catch (Throwable ex) {
logger.error("Initialize DefaultApplicationProvider failed.", ex);
......
......@@ -37,7 +37,6 @@ public class DefaultServerProvider implements ServerProvider {
return;
}
logger.warn("{} does not exist or is not readable.", path);
initialize(null);
} catch (Throwable ex) {
logger.error("Initialize DefaultServerProvider failed.", ex);
......@@ -128,7 +127,7 @@ public class DefaultServerProvider implements ServerProvider {
// 4. Set environment to null.
m_env = null;
logger.warn("Environment is set to null. Because it is not available in either (1) JVM system property 'env', (2) OS env variable 'ENV' nor (3) property 'env' from the properties InputStream.");
logger.info("Environment is set to null. Because it is not available in either (1) JVM system property 'env', (2) OS env variable 'ENV' nor (3) property 'env' from the properties InputStream.");
}
private void initDataCenter() {
......
......@@ -5,6 +5,10 @@ import static org.junit.Assert.assertTrue;
import com.ctrip.framework.apollo.BaseIntegrationTest;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.internals.LegacyMetaServerProvider;
import com.ctrip.framework.apollo.core.spi.MetaServerProvider;
import com.google.common.collect.Maps;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.junit.After;
import org.junit.Test;
......@@ -15,10 +19,7 @@ public class MetaDomainTest extends BaseIntegrationTest {
@After
public void tearDown() throws Exception {
super.tearDown();
System.clearProperty("fat_meta");
System.clearProperty("uat_meta");
System.clearProperty("lpt_meta");
System.clearProperty("apollo.meta");
MockMetaServerProvider.clear();
}
@Test
......@@ -28,16 +29,6 @@ public class MetaDomainTest extends BaseIntegrationTest {
assertEquals(MetaDomainConsts.DEFAULT_META_URL, MetaDomainConsts.getDomain(Env.PRO));
}
@Test
public void testGetMetaDomainWithSystemProperty() throws Exception {
String someMeta = "some-meta";
Env someEnv = Env.DEV;
System.setProperty("apollo.meta", someMeta);
assertEquals(someMeta, MetaDomainConsts.getDomain(someEnv));
}
@Test
public void testGetValidAddress() throws Exception {
String someResponse = "some response";
......@@ -46,10 +37,8 @@ public class MetaDomainTest extends BaseIntegrationTest {
String validServer = " http://localhost:" + PORT + " ";
String invalidServer = "http://localhost:" + findFreePort();
System.setProperty("fat_meta", validServer + "," + invalidServer);
System.setProperty("uat_meta", invalidServer + "," + validServer);
MetaDomainConsts.initialize();
MockMetaServerProvider.mock(Env.FAT, validServer + "," + invalidServer);
MockMetaServerProvider.mock(Env.UAT, invalidServer + "," + validServer);
assertEquals(validServer.trim(), MetaDomainConsts.getDomain(Env.FAT));
assertEquals(validServer.trim(), MetaDomainConsts.getDomain(Env.UAT));
......@@ -60,12 +49,33 @@ public class MetaDomainTest extends BaseIntegrationTest {
String invalidServer = "http://localhost:" + findFreePort() + " ";
String anotherInvalidServer = "http://localhost:" + findFreePort() + " ";
System.setProperty("lpt_meta", invalidServer + "," + anotherInvalidServer);
MetaDomainConsts.initialize();
MockMetaServerProvider.mock(Env.LPT, invalidServer + "," + anotherInvalidServer);
String metaServer = MetaDomainConsts.getDomain(Env.LPT);
assertTrue(metaServer.equals(invalidServer.trim()) || metaServer.equals(anotherInvalidServer.trim()));
}
public static class MockMetaServerProvider implements MetaServerProvider {
private static Map<Env, String> mockMetaServerAddress = Maps.newHashMap();
private static void mock(Env env, String metaServerAddress) {
mockMetaServerAddress.put(env, metaServerAddress);
}
private static void clear() {
mockMetaServerAddress.clear();
}
@Override
public String getMetaServerAddress(Env targetEnv) {
return mockMetaServerAddress.get(targetEnv);
}
@Override
public int getOrder() {
return LegacyMetaServerProvider.ORDER - 1;// just in front of LegacyMetaServerProvider
}
}
}
package com.ctrip.framework.apollo.core.enums;
import static org.junit.Assert.*;
import org.junit.Test;
public class EnvUtilsTest {
@Test
public void testTransformEnv() throws Exception {
assertEquals(Env.DEV, EnvUtils.transformEnv(Env.DEV.name()));
assertEquals(Env.FAT, EnvUtils.transformEnv(Env.FAT.name().toLowerCase()));
assertEquals(Env.UAT, EnvUtils.transformEnv(" " + Env.UAT.name().toUpperCase() + ""));
assertEquals(Env.UNKNOWN, EnvUtils.transformEnv("someInvalidEnv"));
}
@Test
public void testFromString() throws Exception {
assertEquals(Env.DEV, Env.fromString(Env.DEV.name()));
assertEquals(Env.FAT, Env.fromString(Env.FAT.name().toLowerCase()));
assertEquals(Env.UAT, Env.fromString(" " + Env.UAT.name().toUpperCase() + ""));
}
@Test(expected = IllegalArgumentException.class)
public void testFromInvalidString() throws Exception {
Env.fromString("someInvalidEnv");
}
}
package com.ctrip.framework.apollo.core.internals;
import static org.junit.Assert.assertEquals;
import com.ctrip.framework.apollo.core.enums.Env;
import org.junit.After;
import org.junit.Test;
public class LegacyMetaServerProviderTest {
@After
public void tearDown() throws Exception {
System.clearProperty("dev_meta");
}
@Test
public void testFromPropertyFile() {
LegacyMetaServerProvider legacyMetaServerProvider = new LegacyMetaServerProvider();
assertEquals("http://localhost:8080", legacyMetaServerProvider.getMetaServerAddress(Env.LOCAL));
assertEquals("http://dev:8080", legacyMetaServerProvider.getMetaServerAddress(Env.DEV));
assertEquals(null, legacyMetaServerProvider.getMetaServerAddress(Env.PRO));
}
@Test
public void testWithSystemProperty() throws Exception {
String someDevMetaAddress = "someMetaAddress";
String someFatMetaAddress = "someFatMetaAddress";
System.setProperty("dev_meta", someDevMetaAddress);
System.setProperty("fat_meta", someFatMetaAddress);
LegacyMetaServerProvider legacyMetaServerProvider = new LegacyMetaServerProvider();
assertEquals(someDevMetaAddress, legacyMetaServerProvider.getMetaServerAddress(Env.DEV));
assertEquals(someFatMetaAddress, legacyMetaServerProvider.getMetaServerAddress(Env.FAT));
}
}
package com.ctrip.framework.foundation.internals;
import com.ctrip.framework.foundation.internals.ServiceBootstrap;
import org.junit.Test;
import java.util.ServiceConfigurationError;
......
......@@ -4,7 +4,7 @@
<parent>
<artifactId>apollo</artifactId>
<groupId>com.ctrip.framework.apollo</groupId>
<version>0.11.0</version>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apollo-demo</artifactId>
......
......@@ -4,7 +4,7 @@
<parent>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.11.0</version>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
......
local.meta=http://localhost:8080
dev.meta=${dev_meta}
fat.meta=${fat_meta}
uat.meta=${uat_meta}
lpt.meta=${lpt_meta}
pro.meta=${pro_meta}
......@@ -9,7 +9,7 @@
FROM openjdk:8-jre-alpine
MAINTAINER ameizi <sxyx2008@163.com>
ENV VERSION 0.11.0
ENV VERSION 1.0.0
RUN echo "http://mirrors.aliyun.com/alpine/v3.6/main" > /etc/apk/repositories \
&& echo "http://mirrors.aliyun.com/alpine/v3.6/community" >> /etc/apk/repositories \
......
......@@ -2,6 +2,7 @@ package com.ctrip.framework.apollo.portal.controller;
import com.ctrip.framework.apollo.common.dto.NamespaceDTO;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.core.enums.EnvUtils;
import com.ctrip.framework.apollo.core.utils.StringUtils;
import com.ctrip.framework.apollo.openapi.entity.Consumer;
......@@ -90,7 +91,7 @@ public class ConsumerController {
if (Strings.isNullOrEmpty(env)) {
continue;
}
if (null == EnvUtils.transformEnv(env)) {
if (Env.UNKNOWN == EnvUtils.transformEnv(env)) {
throw new BadRequestException(String.format("env: %s is illegal", env));
}
envList.add(env);
......
......@@ -98,7 +98,7 @@ public class PermissionController {
public NamespaceEnvRolesAssignedUsers getNamespaceEnvRoles(@PathVariable String appId, @PathVariable String env, @PathVariable String namespaceName) {
// validate env parameter
if (null == EnvUtils.transformEnv(env)) {
if (Env.UNKNOWN == EnvUtils.transformEnv(env)) {
throw new BadRequestException("env is illegal");
}
......@@ -130,7 +130,7 @@ public class PermissionController {
}
// validate env parameter
if (null == EnvUtils.transformEnv(env)) {
if (Env.UNKNOWN == EnvUtils.transformEnv(env)) {
throw new BadRequestException("env is illegal");
}
Set<String> assignedUser = rolePermissionService.assignRoleToUsers(RoleUtils.buildNamespaceRoleName(appId, namespaceName, roleType, env),
......@@ -152,7 +152,7 @@ public class PermissionController {
throw new BadRequestException("role type is illegal");
}
// validate env parameter
if (null == EnvUtils.transformEnv(env)) {
if (Env.UNKNOWN == EnvUtils.transformEnv(env)) {
throw new BadRequestException("env is illegal");
}
rolePermissionService.removeRoleFromUsers(RoleUtils.buildNamespaceRoleName(appId, namespaceName, roleType, env),
......
......@@ -5,11 +5,11 @@
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo</artifactId>
<version>0.11.0</version>
<version>1.0.0</version>
<name>Apollo</name>
<packaging>pom</packaging>
<description>Ctrip Configuration Center</description>
<url>https://ctripcorp.github.io/apollo</url>
<url>https://github.com/ctripcorp/apollo</url>
<organization>
<name>Ctrip, Inc.</name>
......@@ -85,6 +85,8 @@
<maven-war-plugin.version>3.0.0</maven-war-plugin.version>
<maven-install-plugin.version>2.5.2</maven-install-plugin.version>
<maven-deploy-plugin.version>2.8.2</maven-deploy-plugin.version>
<maven-javadoc-plugin.version>3.0.1</maven-javadoc-plugin.version>
<maven-gpg-plugin.version>1.6</maven-gpg-plugin.version>
<!-- for travis usage -->
<github.global.server>github</github.global.server>
<github.global.oauth2Token>${env.GITHUB_OAUTH_TOKEN}</github.global.oauth2Token>
......@@ -348,6 +350,30 @@
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
<executions>
<execution>
<id>attach-javadoc</id>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<doclint>none</doclint>
</configuration>
</execution>
</executions>
<configuration>
<show>public</show>
<charset>UTF-8</charset>
<encoding>UTF-8</encoding>
<docencoding>UTF-8</docencoding>
<links>
<link>http://docs.oracle.com/javase/7/docs/api</link>
</links>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
......@@ -366,6 +392,20 @@
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>${maven-gpg-plugin.version}</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
......@@ -378,29 +418,6 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.17</version>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>6.18</version>
</dependency>
<dependency>
<groupId>com.ctrip.framework.apollo</groupId>
<artifactId>apollo-buildtools</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<configuration>
<configLocation>google_checks.xml</configLocation>
<headerLocation>LICENSE-2.0.txt</headerLocation>
<failOnViolation>false</failOnViolation>
<consoleOutput>true</consoleOutput>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
......@@ -462,10 +479,6 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
......@@ -531,7 +544,6 @@
<filtering>true</filtering>
<includes>
<include>application-github.properties</include>
<include>apollo-env.properties</include>
</includes>
</resource>
<resource>
......@@ -539,7 +551,6 @@
<filtering>false</filtering>
<excludes>
<exclude>application-github.properties</exclude>
<exclude>apollo-env.properties</exclude>
</excludes>
</resource>
</resources>
......@@ -815,6 +826,21 @@
</dependency>
</dependencies>
</profile>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<reporting>
......
......@@ -37,10 +37,4 @@ call mvn clean package -DskipTests -pl apollo-portal -am -Dapollo_profile=github
echo "==== building portal finished ===="
echo "==== starting to build client ===="
call mvn clean install -DskipTests -pl apollo-client -am %META_SERVERS_OPTS%
echo "==== building client finished ===="
pause
......@@ -36,10 +36,3 @@ echo "==== starting to build portal ===="
mvn clean package -DskipTests -pl apollo-portal -am -Dapollo_profile=github,auth -Dspring_datasource_url=$apollo_portal_db_url -Dspring_datasource_username=$apollo_portal_db_username -Dspring_datasource_password=$apollo_portal_db_password $META_SERVERS_OPTS
echo "==== building portal finished ===="
echo "==== starting to build client ===="
mvn clean install -DskipTests -pl apollo-client -am $META_SERVERS_OPTS
echo "==== building client finished ===="
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册