Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
apollo
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
openSource
apollo
Commits
49c1b308
Commit
49c1b308
authored
Mar 23, 2016
by
Jason Song
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add refresh config support and refactor
parent
51978dd6
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
855 additions
and
207 deletions
+855
-207
import.sql
apollo-biz/src/main/resources/import.sql
+1
-0
ApolloConfigManager.java
...ain/java/com/ctrip/apollo/client/ApolloConfigManager.java
+38
-15
PropertyChangeType.java
...ava/com/ctrip/apollo/client/enums/PropertyChangeType.java
+21
-0
ConfigLoader.java
...ain/java/com/ctrip/apollo/client/loader/ConfigLoader.java
+5
-6
ConfigLoaderFactory.java
...a/com/ctrip/apollo/client/loader/ConfigLoaderFactory.java
+22
-1
ConfigLoaderManager.java
...a/com/ctrip/apollo/client/loader/ConfigLoaderManager.java
+190
-0
AbstractConfigLoader.java
...ctrip/apollo/client/loader/impl/AbstractConfigLoader.java
+35
-0
InMemoryConfigLoader.java
...ctrip/apollo/client/loader/impl/InMemoryConfigLoader.java
+15
-0
LocalFileConfigLoader.java
...trip/apollo/client/loader/impl/LocalFileConfigLoader.java
+4
-6
RemoteConfigLoader.java
...m/ctrip/apollo/client/loader/impl/RemoteConfigLoader.java
+38
-93
ApolloRegistry.java
...in/java/com/ctrip/apollo/client/model/ApolloRegistry.java
+11
-0
PropertyChange.java
...in/java/com/ctrip/apollo/client/model/PropertyChange.java
+52
-0
PropertySourceReloadResult.java
...ctrip/apollo/client/model/PropertySourceReloadResult.java
+44
-0
ConfigUtil.java
...rc/main/java/com/ctrip/apollo/client/util/ConfigUtil.java
+1
-1
AllTests.java
...lient/src/test/java/com/ctrip/apollo/client/AllTests.java
+3
-1
ApolloConfigManagerTest.java
...java/com/ctrip/apollo/client/ApolloConfigManagerTest.java
+49
-10
ConfigLoaderManagerTest.java
...m/ctrip/apollo/client/loader/ConfigLoaderManagerTest.java
+198
-0
RemoteConfigLoaderTest.java
...rip/apollo/client/loader/impl/RemoteConfigLoaderTest.java
+65
-37
ConfigController.java
...rip/apollo/configservice/controller/ConfigController.java
+2
-5
ApolloConfig.java
...src/main/java/com/ctrip/apollo/core/dto/ApolloConfig.java
+23
-1
DemoController.java
...java/com/ctrip/apollo/demo/controller/DemoController.java
+8
-5
app.js
apollo-demo/src/main/webapp/s/scripts/app.js
+21
-19
list.html
apollo-demo/src/main/webapp/s/templates/list.html
+9
-7
No files found.
apollo-biz/src/main/resources/import.sql
View file @
49c1b308
...
...
@@ -6,3 +6,4 @@ INSERT INTO Version (AppId, IsDeleted, Name, ReleaseId) VALUES (102, 0, '1.0', 2
INSERT
INTO
RELEASESNAPSHOT
(
ClusterName
,
IsDeleted
,
ReleaseId
,
Configurations
)
VALUES
(
'default'
,
0
,
1
,
'{"apollo.foo":"bar", "apollo.bar":"foo"}'
);
INSERT
INTO
RELEASESNAPSHOT
(
ClusterName
,
IsDeleted
,
ReleaseId
,
Configurations
)
VALUES
(
'default'
,
0
,
2
,
'{"demo.foo":"demo1", "demo.bar":"demo2"}'
);
INSERT
INTO
RELEASESNAPSHOT
(
ClusterName
,
IsDeleted
,
ReleaseId
,
Configurations
)
VALUES
(
'default'
,
0
,
3
,
'{"apollo.foo":"another bar", "apollo.bar_new":"foo"}'
);
apollo-client/src/main/java/com/ctrip/apollo/client/ApolloConfigManager.java
View file @
49c1b308
package
com
.
ctrip
.
apollo
.
client
;
import
com.ctrip.apollo.client.loader.ConfigLoader
;
import
com.ctrip.apollo.client.loader.ConfigLoaderFactory
;
import
com.ctrip.apollo.client.loader.ConfigLoaderManager
;
import
com.ctrip.apollo.client.model.PropertyChange
;
import
com.ctrip.apollo.client.model.PropertySourceReloadResult
;
import
com.ctrip.apollo.client.util.ConfigUtil
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.BeansException
;
import
org.springframework.beans.factory.config.BeanDefinition
;
import
org.springframework.beans.factory.config.ConfigurableListableBeanFactory
;
...
...
@@ -19,6 +23,7 @@ import org.springframework.core.PriorityOrdered;
import
org.springframework.core.env.CompositePropertySource
;
import
org.springframework.core.env.MutablePropertySources
;
import
java.util.List
;
import
java.util.concurrent.atomic.AtomicReference
;
/**
...
...
@@ -27,19 +32,18 @@ import java.util.concurrent.atomic.AtomicReference;
* @author Jason Song(song_s@ctrip.com)
*/
public
class
ApolloConfigManager
implements
BeanDefinitionRegistryPostProcessor
,
PriorityOrdered
,
ApplicationContextAware
{
p
ublic
static
final
String
APOLLO_PROPERTY_SOURCE_NAME
=
"ApolloConfigProperties"
;
p
rivate
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
ApolloConfigManager
.
class
)
;
private
static
AtomicReference
<
ApolloConfigManager
>
singletonProtector
=
new
AtomicReference
<
ApolloConfigManager
>();
private
ConfigLoader
configLoad
er
;
private
ConfigLoader
Manager
configLoaderManag
er
;
private
ConfigurableApplicationContext
applicationContext
;
private
CompositePropertySource
currentPropertySource
;
private
RefreshScope
scope
;
public
ApolloConfigManager
()
{
if
(!
singletonProtector
.
compareAndSet
(
null
,
this
))
{
throw
new
IllegalStateException
(
"There should be only one ApolloConfigManager instance!"
);
}
this
.
configLoader
=
ConfigLoaderFactory
.
getInstance
().
getRemoteConfigLoad
er
();
this
.
configLoader
Manager
=
ConfigLoaderFactory
.
getInstance
().
getConfigLoaderManag
er
();
}
@Override
...
...
@@ -97,23 +101,42 @@ public class ApolloConfigManager implements BeanDefinitionRegistryPostProcessor,
}
/**
* Prepare property sources
* First try to load from remote
* If loading from remote failed, then fall back to local cached properties
* Initialize property sources
*/
void
initializePropertySource
()
{
currentPropertySource
=
loadPropertySource
();
//TODO stop application from starting when config cannot be loaded?
CompositePropertySource
result
=
this
.
configLoaderManager
.
loadPropertySource
();
updateEnvironmentPropertySource
(
result
);
}
private
void
updateEnvironmentPropertySource
(
CompositePropertySource
currentPropertySource
)
{
MutablePropertySources
currentPropertySources
=
applicationContext
.
getEnvironment
().
getPropertySources
();
if
(
currentPropertySources
.
contains
(
currentPropertySource
.
getName
()))
{
currentPropertySources
.
remove
(
currentPropertySource
.
getName
());
currentPropertySources
.
replace
(
currentPropertySource
.
getName
(),
currentPropertySource
);
return
;
}
currentPropertySources
.
addFirst
(
currentPropertySource
);
}
CompositePropertySource
loadPropertySource
()
{
CompositePropertySource
compositePropertySource
=
new
CompositePropertySource
(
APOLLO_PROPERTY_SOURCE_NAME
);
compositePropertySource
.
addPropertySource
(
configLoader
.
loadPropertySource
());
return
compositePropertySource
;
public
List
<
PropertyChange
>
updatePropertySource
()
{
PropertySourceReloadResult
result
=
this
.
configLoaderManager
.
reloadPropertySource
();
if
(
result
.
hasChanges
())
{
updateEnvironmentPropertySource
(
result
.
getPropertySource
());
refreshBeans
();
}
return
result
.
getChanges
();
}
private
void
refreshBeans
()
{
if
(
this
.
scope
==
null
)
{
this
.
scope
=
applicationContext
.
getBean
(
"refreshScope"
,
RefreshScope
.
class
);
}
if
(
this
.
scope
==
null
)
{
logger
.
error
(
"Could not get refresh scope object, skip refresh beans"
);
}
this
.
scope
.
refreshAll
();
}
}
apollo-client/src/main/java/com/ctrip/apollo/client/enums/PropertyChangeType.java
0 → 100644
View file @
49c1b308
package
com
.
ctrip
.
apollo
.
client
.
enums
;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public
enum
PropertyChangeType
{
NEW
(
"New"
),
MODIFIED
(
"Modified"
),
DELETED
(
"Deleted"
);
private
String
type
;
PropertyChangeType
(
String
type
)
{
this
.
type
=
type
;
}
public
String
getType
()
{
return
type
;
}
}
apollo-client/src/main/java/com/ctrip/apollo/client/loader/ConfigLoader.java
View file @
49c1b308
package
com
.
ctrip
.
apollo
.
client
.
loader
;
import
org.springframework.core.env.CompositePropertySource
;
import
com.ctrip.apollo.client.model.ApolloRegistry
;
import
com.ctrip.apollo.core.dto.ApolloConfig
;
/**
* @author Jason Song(songs_ctrip.com)
*/
public
interface
ConfigLoader
{
/**
* Load property source for client use
* @return property source
*/
CompositePropertySource
loadPropertySource
();
ApolloConfig
loadApolloConfig
(
ApolloRegistry
apolloRegistry
,
ApolloConfig
previous
);
void
setFallBackLoader
(
ConfigLoader
configLoader
);
}
apollo-client/src/main/java/com/ctrip/apollo/client/loader/ConfigLoaderFactory.java
View file @
49c1b308
package
com
.
ctrip
.
apollo
.
client
.
loader
;
import
com.ctrip.apollo.client.loader.impl.InMemoryConfigLoader
;
import
com.ctrip.apollo.client.loader.impl.LocalFileConfigLoader
;
import
com.ctrip.apollo.client.loader.impl.RemoteConfigLoader
;
import
com.ctrip.apollo.client.util.ConfigUtil
;
import
org.springframework.web.client.RestTemplate
;
/**
* @author Jason Song(song_s@ctrip.com)
...
...
@@ -15,7 +19,24 @@ public class ConfigLoaderFactory {
return
configLoaderFactory
;
}
public
ConfigLoader
getLocalFileConfigLoader
()
{
ConfigLoader
configLoader
=
new
LocalFileConfigLoader
();
return
configLoader
;
}
public
ConfigLoader
getInMemoryConfigLoader
()
{
ConfigLoader
inMemoryConfigLoader
=
new
InMemoryConfigLoader
();
inMemoryConfigLoader
.
setFallBackLoader
(
getLocalFileConfigLoader
());
return
inMemoryConfigLoader
;
}
public
ConfigLoader
getRemoteConfigLoader
()
{
return
new
RemoteConfigLoader
();
ConfigLoader
remoteConfigLoader
=
new
RemoteConfigLoader
(
new
RestTemplate
(),
ConfigUtil
.
getInstance
());
// remoteConfigLoader.setFallBackLoader(getInMemoryConfigLoader());
return
remoteConfigLoader
;
}
public
ConfigLoaderManager
getConfigLoaderManager
()
{
return
new
ConfigLoaderManager
(
getRemoteConfigLoader
(),
ConfigUtil
.
getInstance
());
}
}
apollo-client/src/main/java/com/ctrip/apollo/client/loader/ConfigLoaderManager.java
0 → 100644
View file @
49c1b308
package
com
.
ctrip
.
apollo
.
client
.
loader
;
import
com.ctrip.apollo.client.enums.PropertyChangeType
;
import
com.ctrip.apollo.client.model.ApolloRegistry
;
import
com.ctrip.apollo.client.model.PropertyChange
;
import
com.ctrip.apollo.client.model.PropertySourceReloadResult
;
import
com.ctrip.apollo.client.util.ConfigUtil
;
import
com.ctrip.apollo.core.dto.ApolloConfig
;
import
com.google.common.collect.Lists
;
import
com.google.common.collect.Maps
;
import
com.google.common.collect.Sets
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.core.env.CompositePropertySource
;
import
org.springframework.core.env.MapPropertySource
;
import
java.io.IOException
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
import
java.util.concurrent.*
;
import
java.util.concurrent.atomic.AtomicLong
;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public
class
ConfigLoaderManager
{
public
static
final
String
APOLLO_PROPERTY_SOURCE_NAME
=
"ApolloConfigProperties"
;
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
ConfigLoaderManager
.
class
);
private
ConfigLoader
configLoader
;
private
ConfigUtil
configUtil
;
private
final
ExecutorService
executorService
;
private
final
AtomicLong
counter
;
private
Map
<
ApolloRegistry
,
ApolloConfig
>
currentApolloRegistryConfigCache
;
private
Map
<
ApolloRegistry
,
ApolloConfig
>
previousApolloRegistryConfigCache
;
private
List
<
ApolloRegistry
>
apolloRegistries
;
public
ConfigLoaderManager
(
ConfigLoader
configLoader
,
ConfigUtil
configUtil
)
{
this
.
configLoader
=
configLoader
;
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
,
"ConfigLoaderManager-"
+
counter
.
incrementAndGet
());
return
thread
;
}
});
this
.
currentApolloRegistryConfigCache
=
Maps
.
newConcurrentMap
();
}
public
CompositePropertySource
loadPropertySource
()
{
try
{
apolloRegistries
=
configUtil
.
loadApolloRegistries
();
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
"Load apollo config registry failed"
,
e
);
}
return
loadPropertySourceWithApolloRegistries
(
apolloRegistries
);
}
public
PropertySourceReloadResult
reloadPropertySource
()
{
CompositePropertySource
composite
=
loadPropertySourceWithApolloRegistries
(
apolloRegistries
);
List
<
ApolloConfig
>
previous
=
Lists
.
newArrayList
(
this
.
previousApolloRegistryConfigCache
.
values
());
List
<
ApolloConfig
>
current
=
Lists
.
newArrayList
(
this
.
currentApolloRegistryConfigCache
.
values
());
return
new
PropertySourceReloadResult
(
composite
,
calcPropertyChanges
(
previous
,
current
));
}
/**
* Load property source with apollo registries provided
* Should not be invoked in parallel since there are some operations like create/destroy cache,
* writing to files etc.
* @param apolloRegistries
* @return
*/
private
synchronized
CompositePropertySource
loadPropertySourceWithApolloRegistries
(
List
<
ApolloRegistry
>
apolloRegistries
)
{
resetApolloRegistryConfigCache
();
CompositePropertySource
composite
=
new
CompositePropertySource
(
APOLLO_PROPERTY_SOURCE_NAME
);
if
(
apolloRegistries
==
null
||
apolloRegistries
.
isEmpty
())
{
logger
.
warn
(
"No Apollo Registry found!"
);
return
composite
;
}
try
{
List
<
ApolloConfig
>
apolloConfigList
=
loadApolloConfigs
(
apolloRegistries
);
Collections
.
sort
(
apolloConfigList
);
for
(
ApolloConfig
apolloConfig
:
apolloConfigList
)
{
composite
.
addPropertySource
(
new
MapPropertySource
(
assemblePropertySourceName
(
apolloConfig
),
apolloConfig
.
getConfigurations
()));
}
return
composite
;
}
catch
(
Throwable
throwable
)
{
throw
new
RuntimeException
(
"Load apollo configs failed"
,
throwable
);
}
}
List
<
PropertyChange
>
calcPropertyChanges
(
List
<
ApolloConfig
>
previous
,
List
<
ApolloConfig
>
current
)
{
Map
<
String
,
Object
>
previousMap
=
collectConfigurations
(
previous
);
Map
<
String
,
Object
>
currentMap
=
collectConfigurations
(
current
);
Set
<
String
>
previousKeys
=
previousMap
.
keySet
();
Set
<
String
>
currentKeys
=
currentMap
.
keySet
();
Set
<
String
>
commonKeys
=
Sets
.
intersection
(
previousKeys
,
currentKeys
);
Set
<
String
>
newKeys
=
Sets
.
difference
(
currentKeys
,
commonKeys
);
Set
<
String
>
removedKeys
=
Sets
.
difference
(
previousKeys
,
commonKeys
);
List
<
PropertyChange
>
changes
=
Lists
.
newArrayList
();
for
(
String
newKey
:
newKeys
)
{
changes
.
add
(
new
PropertyChange
(
newKey
,
null
,
currentMap
.
get
(
newKey
),
PropertyChangeType
.
NEW
));
}
for
(
String
removedKey
:
removedKeys
)
{
changes
.
add
(
new
PropertyChange
(
removedKey
,
previousMap
.
get
(
removedKey
),
null
,
PropertyChangeType
.
DELETED
));
}
for
(
String
commonKey
:
commonKeys
)
{
if
(
previousMap
.
get
(
commonKey
).
equals
(
currentMap
.
get
(
commonKey
)))
{
continue
;
}
changes
.
add
(
new
PropertyChange
(
commonKey
,
previousMap
.
get
(
commonKey
),
currentMap
.
get
(
commonKey
),
PropertyChangeType
.
MODIFIED
));
}
return
changes
;
}
Map
<
String
,
Object
>
collectConfigurations
(
List
<
ApolloConfig
>
apolloConfigs
)
{
Collections
.
sort
(
apolloConfigs
);
Map
<
String
,
Object
>
configMap
=
Maps
.
newHashMap
();
for
(
int
i
=
apolloConfigs
.
size
()
-
1
;
i
>
-
1
;
i
--)
{
configMap
.
putAll
(
apolloConfigs
.
get
(
i
).
getConfigurations
());
}
return
configMap
;
}
List
<
ApolloConfig
>
loadApolloConfigs
(
List
<
ApolloRegistry
>
apolloRegistries
)
throws
Throwable
{
List
<
Future
<
ApolloConfig
>>
futures
=
Lists
.
newArrayList
();
for
(
final
ApolloRegistry
apolloRegistry
:
apolloRegistries
)
{
futures
.
add
(
executorService
.
submit
(
new
Callable
<
ApolloConfig
>()
{
@Override
public
ApolloConfig
call
()
throws
Exception
{
return
loadSingleApolloConfig
(
apolloRegistry
);
}
}));
}
List
<
ApolloConfig
>
apolloConfigList
=
Lists
.
newArrayList
();
for
(
Future
<
ApolloConfig
>
future
:
futures
)
{
try
{
ApolloConfig
result
=
future
.
get
();
if
(
result
==
null
)
{
continue
;
}
apolloConfigList
.
add
(
result
);
}
catch
(
ExecutionException
e
)
{
throw
e
.
getCause
();
}
}
return
apolloConfigList
;
}
ApolloConfig
loadSingleApolloConfig
(
ApolloRegistry
apolloRegistry
)
{
ApolloConfig
result
=
configLoader
.
loadApolloConfig
(
apolloRegistry
,
getPreviousApolloConfig
(
apolloRegistry
));
if
(
result
==
null
)
{
logger
.
error
(
"Loaded config null..."
);
return
null
;
}
logger
.
info
(
"Loaded config: {}"
,
result
);
updateCurrentApolloConfigCache
(
apolloRegistry
,
result
);
return
result
;
}
void
resetApolloRegistryConfigCache
()
{
this
.
previousApolloRegistryConfigCache
=
currentApolloRegistryConfigCache
;
this
.
currentApolloRegistryConfigCache
=
Maps
.
newConcurrentMap
();
}
ApolloConfig
getPreviousApolloConfig
(
ApolloRegistry
apolloRegistry
)
{
return
previousApolloRegistryConfigCache
.
get
(
apolloRegistry
);
}
void
updateCurrentApolloConfigCache
(
ApolloRegistry
apolloRegistry
,
ApolloConfig
apolloConfig
)
{
currentApolloRegistryConfigCache
.
put
(
apolloRegistry
,
apolloConfig
);
}
private
String
assemblePropertySourceName
(
ApolloConfig
apolloConfig
)
{
return
String
.
format
(
"%d-%s-%s-%d"
,
apolloConfig
.
getAppId
(),
apolloConfig
.
getCluster
(),
apolloConfig
.
getVersion
(),
apolloConfig
.
getReleaseId
());
}
}
apollo-client/src/main/java/com/ctrip/apollo/client/loader/impl/AbstractConfigLoader.java
0 → 100644
View file @
49c1b308
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.core.dto.ApolloConfig
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public
abstract
class
AbstractConfigLoader
implements
ConfigLoader
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
AbstractConfigLoader
.
class
);
private
ConfigLoader
fallback
;
@Override
public
ApolloConfig
loadApolloConfig
(
ApolloRegistry
apolloRegistry
,
ApolloConfig
previous
)
{
try
{
return
doLoadApolloConfig
(
apolloRegistry
,
previous
);
}
catch
(
Throwable
e
)
{
if
(
this
.
fallback
==
null
)
{
throw
new
RuntimeException
(
String
.
format
(
"Load Apollo Config failed - %s"
,
apolloRegistry
.
toString
()),
e
);
}
logger
.
error
(
"Load Config via {} failed, try to use its fallback {} to load"
,
getClass
().
getSimpleName
(),
fallback
.
getClass
().
getSimpleName
(),
e
);
return
this
.
fallback
.
loadApolloConfig
(
apolloRegistry
,
previous
);
}
}
protected
abstract
ApolloConfig
doLoadApolloConfig
(
ApolloRegistry
apolloRegistry
,
ApolloConfig
previous
);
@Override
public
void
setFallBackLoader
(
ConfigLoader
configLoader
)
{
this
.
fallback
=
configLoader
;
}
}
apollo-client/src/main/java/com/ctrip/apollo/client/loader/impl/InMemoryConfigLoader.java
0 → 100644
View file @
49c1b308
package
com
.
ctrip
.
apollo
.
client
.
loader
.
impl
;
import
com.ctrip.apollo.client.model.ApolloRegistry
;
import
com.ctrip.apollo.core.dto.ApolloConfig
;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public
class
InMemoryConfigLoader
extends
AbstractConfigLoader
{
@Override
protected
ApolloConfig
doLoadApolloConfig
(
ApolloRegistry
apolloRegistry
,
ApolloConfig
previous
)
{
return
null
;
}
}
apollo-client/src/main/java/com/ctrip/apollo/client/loader/impl/LocalConfigLoader.java
→
apollo-client/src/main/java/com/ctrip/apollo/client/loader/impl/Local
File
ConfigLoader.java
View file @
49c1b308
package
com
.
ctrip
.
apollo
.
client
.
loader
.
impl
;
import
com.ctrip.apollo.client.
loader.ConfigLoader
;
import
org.springframework.core.env.CompositePropertySource
;
import
com.ctrip.apollo.client.
model.ApolloRegistry
;
import
com.ctrip.apollo.core.dto.ApolloConfig
;
/**
* Load config from local backup file
* @author Jason Song(song_s@ctrip.com)
*/
public
class
LocalConfigLoader
implements
ConfigLoader
{
private
static
final
String
PROPERTY_SOURCE_NAME
=
"ApolloLocalConfigProperties"
;
public
class
LocalFileConfigLoader
extends
AbstractConfigLoader
{
@Override
public
CompositePropertySource
loadPropertySource
(
)
{
public
ApolloConfig
doLoadApolloConfig
(
ApolloRegistry
apolloRegistry
,
ApolloConfig
previous
)
{
return
null
;
}
}
apollo-client/src/main/java/com/ctrip/apollo/client/loader/impl/RemoteConfigLoader.java
View file @
49c1b308
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.dto.ApolloConfig
;
import
com.google.common.collect.
List
s
;
import
com.google.common.collect.
Map
s
;
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
;
...
...
@@ -16,126 +13,74 @@ 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.concurrent.*
;
import
java.util.concurrent.atomic.AtomicLong
;
import
java.util.Map
;
/**
* 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"
;
public
class
RemoteConfigLoader
extends
AbstractConfigLoader
{
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
()
{
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
<
MapPropertySource
>>
futures
=
Lists
.
newArrayList
();
for
(
final
ApolloRegistry
apolloRegistry
:
apolloRegistries
)
{
futures
.
add
(
executorService
.
submit
(
new
Callable
<
MapPropertySource
>()
{
@Override
public
MapPropertySource
call
()
throws
Exception
{
return
loadSingleApolloConfig
(
apolloRegistry
.
getAppId
(),
apolloRegistry
.
getVersion
());
}
}));
}
for
(
Future
<
MapPropertySource
>
future
:
futures
)
{
try
{
MapPropertySource
result
=
future
.
get
();
if
(
result
==
null
)
{
continue
;
}
compositePropertySource
.
addPropertySource
(
result
);
}
catch
(
ExecutionException
e
)
{
throw
e
.
getCause
();
}
}
}
MapPropertySource
loadSingleApolloConfig
(
long
appId
,
String
version
)
{
ApolloConfig
result
=
this
.
getRemoteConfig
(
restTemplate
,
configUtil
.
getConfigServerUrl
(),
appId
,
configUtil
.
getCluster
(),
version
);
if
(
result
==
null
)
{
logger
.
error
(
"Loaded config null..."
);
return
null
;
}
logger
.
info
(
"Loaded config: {}"
,
result
);
return
new
MapPropertySource
(
assemblePropertySourceName
(
result
),
result
.
getConfigurations
());
}
ApolloConfig
getRemoteConfig
(
RestTemplate
restTemplate
,
String
uri
,
String
cluster
,
ApolloRegistry
apolloRegistry
,
ApolloConfig
previousConfig
)
{
long
appId
=
apolloRegistry
.
getAppId
();
String
version
=
apolloRegistry
.
getVersion
();
private
String
assemblePropertySourceName
(
ApolloConfig
apolloConfig
)
{
return
String
.
format
(
"%d-%s-%s-%d"
,
apolloConfig
.
getAppId
(),
apolloConfig
.
getCluster
(),
apolloConfig
.
getVersion
(),
apolloConfig
.
getReleaseId
());
}
ApolloConfig
getRemoteConfig
(
RestTemplate
restTemplate
,
String
uri
,
long
appId
,
String
cluster
,
String
version
)
{
logger
.
info
(
"Loading config from {}, appId={}, cluster={}, version={}"
,
uri
,
appId
,
cluster
,
version
);
String
path
=
"config/{appId}/{cluster}"
;
Object
[]
args
=
new
String
[]
{
String
.
valueOf
(
appId
),
cluster
};
String
path
=
"/config/{appId}/{cluster}"
;
Map
<
String
,
Object
>
paramMap
=
Maps
.
newHashMap
();
paramMap
.
put
(
"appId"
,
appId
);
paramMap
.
put
(
"cluster"
,
cluster
);
if
(
StringUtils
.
hasText
(
version
))
{
args
=
new
String
[]
{
String
.
valueOf
(
appId
),
cluster
,
version
};
path
=
path
+
"/{version}"
;
paramMap
.
put
(
"version"
,
version
);
}
if
(
previousConfig
!=
null
)
{
path
=
path
+
"?releaseId={releaseId}"
;
paramMap
.
put
(
"releaseId"
,
previousConfig
.
getReleaseId
());
}
ResponseEntity
<
ApolloConfig
>
response
=
null
;
ResponseEntity
<
ApolloConfig
>
response
;
try
{
// TODO retry
response
=
restTemplate
.
exchange
(
uri
+
path
,
HttpMethod
.
GET
,
new
HttpEntity
<
Void
>((
Void
)
null
),
ApolloConfig
.
class
,
args
);
}
catch
(
Exception
e
)
{
+
path
,
HttpMethod
.
GET
,
new
HttpEntity
<
Void
>((
Void
)
null
),
ApolloConfig
.
class
,
paramMap
);
}
catch
(
Throwable
e
)
{
throw
e
;
}
if
(
response
==
null
||
response
.
getStatusCode
()
!=
HttpStatus
.
OK
)
{
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
;
}
@Override
protected
ApolloConfig
doLoadApolloConfig
(
ApolloRegistry
apolloRegistry
,
ApolloConfig
previous
)
{
ApolloConfig
result
=
this
.
getRemoteConfig
(
restTemplate
,
configUtil
.
getConfigServerUrl
(),
configUtil
.
getCluster
(),
apolloRegistry
,
previous
);
//When remote server return 304, we need to return the previous result
return
result
==
null
?
previous
:
result
;
}
}
apollo-client/src/main/java/com/ctrip/apollo/client/model/ApolloRegistry.java
View file @
49c1b308
package
com
.
ctrip
.
apollo
.
client
.
model
;
import
com.google.common.base.MoreObjects
;
/**
* @author Jason Song(song_s@ctrip.com)
*/
...
...
@@ -22,4 +24,13 @@ public class ApolloRegistry {
public
void
setVersion
(
String
version
)
{
this
.
version
=
version
;
}
@Override
public
String
toString
()
{
return
MoreObjects
.
toStringHelper
(
this
)
.
omitNullValues
()
.
add
(
"appId"
,
appId
)
.
add
(
"version"
,
version
)
.
toString
();
}
}
apollo-client/src/main/java/com/ctrip/apollo/client/model/PropertyChange.java
0 → 100644
View file @
49c1b308
package
com
.
ctrip
.
apollo
.
client
.
model
;
import
com.ctrip.apollo.client.enums.PropertyChangeType
;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public
class
PropertyChange
{
private
String
propertyName
;
private
Object
oldValue
;
private
Object
newValue
;
private
PropertyChangeType
changeType
;
public
PropertyChange
(
String
propertyName
,
Object
oldValue
,
Object
newValue
,
PropertyChangeType
changeType
)
{
this
.
propertyName
=
propertyName
;
this
.
oldValue
=
oldValue
;
this
.
newValue
=
newValue
;
this
.
changeType
=
changeType
;
}
public
String
getPropertyName
()
{
return
propertyName
;
}
public
void
setPropertyName
(
String
propertyName
)
{
this
.
propertyName
=
propertyName
;
}
public
Object
getOldValue
()
{
return
oldValue
;
}
public
void
setOldValue
(
Object
oldValue
)
{
this
.
oldValue
=
oldValue
;
}
public
Object
getNewValue
()
{
return
newValue
;
}
public
void
setNewValue
(
Object
newValue
)
{
this
.
newValue
=
newValue
;
}
public
PropertyChangeType
getChangeType
()
{
return
changeType
;
}
public
void
setChangeType
(
PropertyChangeType
changeType
)
{
this
.
changeType
=
changeType
;
}
}
apollo-client/src/main/java/com/ctrip/apollo/client/model/PropertySourceReloadResult.java
0 → 100644
View file @
49c1b308
package
com
.
ctrip
.
apollo
.
client
.
model
;
import
com.google.common.collect.Lists
;
import
org.springframework.core.env.CompositePropertySource
;
import
java.util.List
;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public
class
PropertySourceReloadResult
{
private
CompositePropertySource
propertySource
;
private
List
<
PropertyChange
>
changes
;
public
PropertySourceReloadResult
(
CompositePropertySource
propertySource
)
{
this
.
propertySource
=
propertySource
;
changes
=
Lists
.
newArrayList
();
}
public
PropertySourceReloadResult
(
CompositePropertySource
propertySource
,
List
<
PropertyChange
>
changes
)
{
this
.
propertySource
=
propertySource
;
this
.
changes
=
changes
;
}
public
CompositePropertySource
getPropertySource
()
{
return
propertySource
;
}
public
void
setPropertySource
(
CompositePropertySource
propertySource
)
{
this
.
propertySource
=
propertySource
;
}
public
List
<
PropertyChange
>
getChanges
()
{
return
changes
;
}
public
void
setChanges
(
List
<
PropertyChange
>
changes
)
{
this
.
changes
=
changes
;
}
public
boolean
hasChanges
()
{
return
!
changes
.
isEmpty
();
}
}
apollo-client/src/main/java/com/ctrip/apollo/client/util/ConfigUtil.java
View file @
49c1b308
...
...
@@ -33,7 +33,7 @@ public class ConfigUtil {
public
String
getConfigServerUrl
()
{
// TODO return the meta server url based on different environments
return
"http://localhost
:8888
"
;
return
"http://localhost"
;
}
public
String
getCluster
()
{
...
...
apollo-client/src/test/java/com/ctrip/apollo/client/AllTests.java
View file @
49c1b308
package
com
.
ctrip
.
apollo
.
client
;
import
com.ctrip.apollo.client.loader.ConfigLoaderManagerTest
;
import
com.ctrip.apollo.client.loader.impl.RemoteConfigLoaderTest
;
import
com.ctrip.apollo.client.util.ConfigUtilTest
;
import
org.junit.runner.RunWith
;
...
...
@@ -8,7 +9,8 @@ import org.junit.runners.Suite.SuiteClasses;
@RunWith
(
Suite
.
class
)
@SuiteClasses
({
ApolloConfigManagerTest
.
class
,
RemoteConfigLoaderTest
.
class
,
ConfigUtilTest
.
class
ApolloConfigManagerTest
.
class
,
ConfigLoaderManagerTest
.
class
,
RemoteConfigLoaderTest
.
class
,
ConfigUtilTest
.
class
})
public
class
AllTests
{
...
...
apollo-client/src/test/java/com/ctrip/apollo/client/ApolloConfigManagerTest.java
View file @
49c1b308
package
com
.
ctrip
.
apollo
.
client
;
import
com.ctrip.apollo.client.loader.ConfigLoader
;
import
com.ctrip.apollo.client.loader.ConfigLoaderManager
;
import
com.ctrip.apollo.client.model.PropertyChange
;
import
com.ctrip.apollo.client.model.PropertySourceReloadResult
;
import
org.junit.After
;
import
org.junit.Before
;
import
org.junit.BeforeClass
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.mockito.ArgumentCaptor
;
...
...
@@ -11,6 +12,7 @@ import org.mockito.Mock;
import
org.mockito.runners.MockitoJUnitRunner
;
import
org.springframework.beans.factory.config.BeanDefinition
;
import
org.springframework.beans.factory.support.BeanDefinitionRegistry
;
import
org.springframework.cloud.context.scope.refresh.RefreshScope
;
import
org.springframework.context.ApplicationContext
;
import
org.springframework.context.ConfigurableApplicationContext
;
import
org.springframework.core.env.CompositePropertySource
;
...
...
@@ -18,10 +20,11 @@ import org.springframework.core.env.ConfigurableEnvironment;
import
org.springframework.core.env.MutablePropertySources
;
import
org.springframework.test.util.ReflectionTestUtils
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.concurrent.atomic.AtomicReference
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
import
static
org
.
mockito
.
Mockito
.*;
/**
...
...
@@ -31,7 +34,7 @@ import static org.mockito.Mockito.*;
public
class
ApolloConfigManagerTest
{
private
ApolloConfigManager
apolloConfigManager
;
@Mock
private
ConfigLoader
configLoad
er
;
private
ConfigLoader
Manager
configLoaderManag
er
;
@Mock
private
ConfigurableApplicationContext
applicationContext
;
@Mock
...
...
@@ -40,6 +43,8 @@ public class ApolloConfigManagerTest {
private
MutablePropertySources
mutablePropertySources
;
@Mock
private
BeanDefinitionRegistry
beanDefinitionRegistry
;
@Mock
private
RefreshScope
scope
;
@Before
public
void
setUp
()
{
...
...
@@ -49,7 +54,8 @@ public class ApolloConfigManagerTest {
when
(
env
.
getPropertySources
()).
thenReturn
(
mutablePropertySources
);
apolloConfigManager
.
setApplicationContext
(
applicationContext
);
ReflectionTestUtils
.
setField
(
apolloConfigManager
,
"configLoader"
,
configLoader
);
ReflectionTestUtils
.
setField
(
apolloConfigManager
,
"configLoaderManager"
,
configLoaderManager
);
ReflectionTestUtils
.
setField
(
apolloConfigManager
,
"scope"
,
scope
);
}
@After
...
...
@@ -66,21 +72,20 @@ public class ApolloConfigManagerTest {
}
@Test
public
void
test
Prepar
ePropertySourceSuccessfully
()
{
public
void
test
Initializ
ePropertySourceSuccessfully
()
{
CompositePropertySource
somePropertySource
=
mock
(
CompositePropertySource
.
class
);
final
ArgumentCaptor
<
CompositePropertySource
>
captor
=
ArgumentCaptor
.
forClass
(
CompositePropertySource
.
class
);
when
(
configLoader
.
loadPropertySource
()).
thenReturn
(
somePropertySource
);
when
(
configLoader
Manager
.
loadPropertySource
()).
thenReturn
(
somePropertySource
);
apolloConfigManager
.
initializePropertySource
();
verify
(
configLoader
,
times
(
1
)).
loadPropertySource
();
verify
(
configLoader
Manager
,
times
(
1
)).
loadPropertySource
();
verify
(
mutablePropertySources
,
times
(
1
)).
addFirst
(
captor
.
capture
());
final
CompositePropertySource
insertedPropertySource
=
captor
.
getValue
();
assertEquals
(
ApolloConfigManager
.
APOLLO_PROPERTY_SOURCE_NAME
,
insertedPropertySource
.
getName
());
assertTrue
(
insertedPropertySource
.
getPropertySources
().
contains
(
somePropertySource
));
assertEquals
(
insertedPropertySource
,
somePropertySource
);
}
@Test
...
...
@@ -92,4 +97,38 @@ public class ApolloConfigManagerTest {
verify
(
beanDefinitionRegistry
,
times
(
2
)).
registerBeanDefinition
(
anyString
(),
any
(
BeanDefinition
.
class
));
}
@Test
public
void
testUpdatePropertySourceWithChanges
()
throws
Exception
{
PropertySourceReloadResult
somePropertySourceReloadResult
=
mock
(
PropertySourceReloadResult
.
class
);
CompositePropertySource
somePropertySource
=
mock
(
CompositePropertySource
.
class
);
List
<
PropertyChange
>
someChanges
=
mock
(
List
.
class
);
when
(
somePropertySourceReloadResult
.
hasChanges
()).
thenReturn
(
true
);
when
(
somePropertySourceReloadResult
.
getPropertySource
()).
thenReturn
(
somePropertySource
);
when
(
somePropertySourceReloadResult
.
getChanges
()).
thenReturn
(
someChanges
);
when
(
configLoaderManager
.
reloadPropertySource
()).
thenReturn
(
somePropertySourceReloadResult
);
List
<
PropertyChange
>
result
=
apolloConfigManager
.
updatePropertySource
();
assertEquals
(
someChanges
,
result
);
verify
(
scope
,
times
(
1
)).
refreshAll
();
}
@Test
public
void
testUpdatePropertySourceWithNoChange
()
throws
Exception
{
PropertySourceReloadResult
somePropertySourceReloadResult
=
mock
(
PropertySourceReloadResult
.
class
);
CompositePropertySource
somePropertySource
=
mock
(
CompositePropertySource
.
class
);
List
<
PropertyChange
>
emptyChanges
=
Collections
.
emptyList
();
when
(
somePropertySourceReloadResult
.
hasChanges
()).
thenReturn
(
false
);
when
(
somePropertySourceReloadResult
.
getPropertySource
()).
thenReturn
(
somePropertySource
);
when
(
somePropertySourceReloadResult
.
getChanges
()).
thenReturn
(
emptyChanges
);
when
(
configLoaderManager
.
reloadPropertySource
()).
thenReturn
(
somePropertySourceReloadResult
);
List
<
PropertyChange
>
result
=
apolloConfigManager
.
updatePropertySource
();
assertEquals
(
emptyChanges
,
result
);
verify
(
scope
,
never
()).
refreshAll
();
}
}
apollo-client/src/test/java/com/ctrip/apollo/client/loader/ConfigLoaderManagerTest.java
0 → 100644
View file @
49c1b308
package
com
.
ctrip
.
apollo
.
client
.
loader
;
import
com.ctrip.apollo.client.enums.PropertyChangeType
;
import
com.ctrip.apollo.client.model.ApolloRegistry
;
import
com.ctrip.apollo.client.model.PropertyChange
;
import
com.ctrip.apollo.client.model.PropertySourceReloadResult
;
import
com.ctrip.apollo.client.util.ConfigUtil
;
import
com.ctrip.apollo.core.dto.ApolloConfig
;
import
com.google.common.base.Function
;
import
com.google.common.collect.FluentIterable
;
import
com.google.common.collect.Lists
;
import
com.google.common.collect.Maps
;
import
com.google.common.collect.ObjectArrays
;
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.core.env.CompositePropertySource
;
import
org.springframework.core.env.MapPropertySource
;
import
org.springframework.core.env.PropertySource
;
import
org.springframework.test.util.ReflectionTestUtils
;
import
java.util.List
;
import
java.util.Map
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
import
static
org
.
mockito
.
Mockito
.*;
/**
* @author Jason Song(song_s@ctrip.com)
*/
@RunWith
(
MockitoJUnitRunner
.
class
)
public
class
ConfigLoaderManagerTest
{
private
ConfigLoaderManager
configLoaderManager
;
@Mock
private
ConfigLoader
configLoader
;
@Mock
private
ConfigUtil
configUtil
;
@Before
public
void
setUp
()
{
configLoaderManager
=
spy
(
new
ConfigLoaderManager
(
configLoader
,
configUtil
));
}
@Test
public
void
testLoadPropertySource
()
throws
Exception
{
long
someAppId
=
100
;
long
anotherAppId
=
101
;
ApolloRegistry
someApolloRegistry
=
assembleSomeApolloRegistry
(
someAppId
,
"someVersion"
);
ApolloRegistry
anotherApolloRegistry
=
assembleSomeApolloRegistry
(
anotherAppId
,
"anotherVersion"
);
ApolloConfig
someApolloConfig
=
mock
(
ApolloConfig
.
class
);
ApolloConfig
anotherApolloConfig
=
mock
(
ApolloConfig
.
class
);
Map
<
String
,
Object
>
someMap
=
mock
(
Map
.
class
);
Map
<
String
,
Object
>
anotherMap
=
mock
(
Map
.
class
);
when
(
someApolloConfig
.
getAppId
()).
thenReturn
(
someAppId
);
when
(
someApolloConfig
.
getAppId
()).
thenReturn
(
anotherAppId
);
when
(
configUtil
.
loadApolloRegistries
()).
thenReturn
(
Lists
.
newArrayList
(
someApolloRegistry
,
anotherApolloRegistry
));
doReturn
(
someApolloConfig
).
when
(
configLoaderManager
).
loadSingleApolloConfig
(
someApolloRegistry
);
doReturn
(
anotherApolloConfig
).
when
(
configLoaderManager
).
loadSingleApolloConfig
(
anotherApolloRegistry
);
when
(
someApolloConfig
.
getConfigurations
()).
thenReturn
(
someMap
);
when
(
anotherApolloConfig
.
getConfigurations
()).
thenReturn
(
anotherMap
);
CompositePropertySource
result
=
configLoaderManager
.
loadPropertySource
();
assertEquals
(
2
,
result
.
getPropertySources
().
size
());
List
<
Map
<
String
,
Object
>>
resultMaps
=
FluentIterable
.
from
(
result
.
getPropertySources
()).
transform
(
new
Function
<
PropertySource
<?>,
Map
<
String
,
Object
>>()
{
@Override
public
Map
<
String
,
Object
>
apply
(
PropertySource
<?>
input
)
{
return
(
Map
<
String
,
Object
>)
input
.
getSource
();
}
}).
toList
();
assertTrue
(
resultMaps
.
containsAll
(
Lists
.
newArrayList
(
someMap
,
anotherMap
)));
}
@Test
(
expected
=
RuntimeException
.
class
)
public
void
testLoadPropertySourceWithError
()
throws
Exception
{
Exception
someException
=
mock
(
Exception
.
class
);
long
someAppId
=
100
;
ApolloRegistry
someApolloRegistry
=
assembleSomeApolloRegistry
(
someAppId
,
"someVersion"
);
when
(
configUtil
.
loadApolloRegistries
()).
thenReturn
(
Lists
.
newArrayList
(
someApolloRegistry
));
doThrow
(
someException
).
when
(
configLoaderManager
).
loadSingleApolloConfig
(
someApolloRegistry
);
configLoaderManager
.
loadPropertySource
();
}
@Test
public
void
testLoadApolloConfigsWithNoApolloRegistry
()
throws
Exception
{
when
(
configUtil
.
loadApolloRegistries
()).
thenReturn
(
null
);
CompositePropertySource
result
=
configLoaderManager
.
loadPropertySource
();
assertTrue
(
result
.
getPropertySources
().
isEmpty
());
}
@Test
public
void
testLoadSingleApolloConfig
()
throws
Exception
{
ApolloConfig
someApolloConfig
=
mock
(
ApolloConfig
.
class
);
Map
<
String
,
Object
>
someMap
=
Maps
.
newHashMap
();
long
someAppId
=
100
;
ApolloRegistry
someApolloRegistry
=
assembleSomeApolloRegistry
(
someAppId
,
"someVersion"
);
ApolloConfig
previousConfig
=
null
;
doReturn
(
null
).
when
(
configLoaderManager
).
getPreviousApolloConfig
(
someApolloRegistry
);
when
(
someApolloConfig
.
getConfigurations
()).
thenReturn
(
someMap
);
when
(
configLoader
.
loadApolloConfig
(
someApolloRegistry
,
previousConfig
)).
thenReturn
(
someApolloConfig
);
ApolloConfig
result
=
configLoaderManager
.
loadSingleApolloConfig
(
someApolloRegistry
);
assertEquals
(
someMap
,
result
.
getConfigurations
());
}
@Test
public
void
testReloadPropertySource
()
throws
Exception
{
long
someAppId
=
100
;
ApolloRegistry
someApolloRegistry
=
assembleSomeApolloRegistry
(
someAppId
,
"someVersion"
);
ApolloConfig
someApolloConfig
=
mock
(
ApolloConfig
.
class
);
Map
<
String
,
Object
>
someMap
=
mock
(
Map
.
class
);
List
<
PropertyChange
>
someChanges
=
mock
(
List
.
class
);
ReflectionTestUtils
.
setField
(
configLoaderManager
,
"apolloRegistries"
,
Lists
.
newArrayList
(
someApolloRegistry
));
doReturn
(
someApolloConfig
).
when
(
configLoaderManager
).
loadSingleApolloConfig
(
someApolloRegistry
);
when
(
someApolloConfig
.
getAppId
()).
thenReturn
(
someAppId
);
when
(
someApolloConfig
.
getConfigurations
()).
thenReturn
(
someMap
);
doReturn
(
someChanges
).
when
(
configLoaderManager
).
calcPropertyChanges
(
anyList
(),
anyList
());
PropertySourceReloadResult
result
=
configLoaderManager
.
reloadPropertySource
();
assertEquals
(
1
,
result
.
getPropertySource
().
getPropertySources
().
size
());
assertEquals
(
someChanges
,
result
.
getChanges
());
List
<
Map
<
String
,
Object
>>
resultMaps
=
FluentIterable
.
from
(
result
.
getPropertySource
().
getPropertySources
()).
transform
(
new
Function
<
PropertySource
<?>,
Map
<
String
,
Object
>>()
{
@Override
public
Map
<
String
,
Object
>
apply
(
PropertySource
<?>
input
)
{
return
(
Map
<
String
,
Object
>)
input
.
getSource
();
}
}).
toList
();
assertTrue
(
resultMaps
.
containsAll
(
Lists
.
newArrayList
(
someMap
)));
}
@Test
public
void
testCalcPropertyChanges
()
throws
Exception
{
long
someAppId
=
1
;
Map
<
String
,
Object
>
someConfig
=
Maps
.
newHashMap
();
someConfig
.
put
(
"key1"
,
"val1"
);
someConfig
.
put
(
"key2"
,
"val2"
);
Map
<
String
,
Object
>
anotherConfig
=
Maps
.
newHashMap
();
anotherConfig
.
put
(
"key1"
,
"val11"
);
anotherConfig
.
put
(
"key3"
,
"val3"
);
List
<
ApolloConfig
>
previous
=
Lists
.
newArrayList
(
assembleApolloConfig
(
someAppId
,
someConfig
));
List
<
ApolloConfig
>
current
=
Lists
.
newArrayList
(
assembleApolloConfig
(
someAppId
,
anotherConfig
));
List
<
PropertyChange
>
changes
=
configLoaderManager
.
calcPropertyChanges
(
previous
,
current
);
assertEquals
(
3
,
changes
.
size
());
List
<
String
>
changeResult
=
FluentIterable
.
from
(
changes
).
transform
(
new
Function
<
PropertyChange
,
String
>()
{
@Override
public
String
apply
(
PropertyChange
input
)
{
return
String
.
format
(
"%s-%s"
,
input
.
getPropertyName
(),
input
.
getChangeType
());
}
}).
toList
();
assertTrue
(
changeResult
.
containsAll
(
Lists
.
newArrayList
(
"key1-"
+
PropertyChangeType
.
MODIFIED
,
"key2-"
+
PropertyChangeType
.
DELETED
,
"key3-"
+
PropertyChangeType
.
NEW
)));
}
ApolloConfig
assembleApolloConfig
(
long
appId
,
Map
<
String
,
Object
>
configurations
)
{
String
someCluster
=
"someCluster"
;
String
someVersion
=
"someVersion"
;
long
someReleaseId
=
1
;
ApolloConfig
config
=
new
ApolloConfig
(
appId
,
someCluster
,
someVersion
,
someReleaseId
);
config
.
setConfigurations
(
configurations
);
return
config
;
}
private
ApolloRegistry
assembleSomeApolloRegistry
(
long
someAppId
,
String
someVersion
)
{
ApolloRegistry
someApolloRegistry
=
new
ApolloRegistry
();
someApolloRegistry
.
setAppId
(
someAppId
);
someApolloRegistry
.
setVersion
(
someVersion
);
return
someApolloRegistry
;
}
}
apollo-client/src/test/java/com/ctrip/apollo/client/loader/impl/RemoteConfigLoaderTest.java
View file @
49c1b308
...
...
@@ -12,11 +12,16 @@ import org.mockito.Mock;
import
org.mockito.runners.MockitoJUnitRunner
;
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.web.client.RestTemplate
;
import
java.util.Map
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertNull
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
import
static
org
.
mockito
.
Mockito
.*;
...
...
@@ -29,6 +34,8 @@ public class RemoteConfigLoaderTest {
@Mock
private
RestTemplate
restTemplate
;
private
ConfigUtil
configUtil
;
@Mock
private
ResponseEntity
<
ApolloConfig
>
someResponse
;
@Before
public
void
setUp
()
{
...
...
@@ -37,57 +44,78 @@ public class RemoteConfigLoaderTest {
}
@Test
public
void
testLoad
PropertySource
()
throws
Exception
{
long
someAppId
=
100
;
long
anotherAppId
=
101
;
Apollo
Registry
someApolloRegistry
=
assembleSomeApolloRegistry
(
someAppId
,
"someVersion"
);
ApolloRegistry
anotherApolloRegistry
=
assembleSomeApolloRegistry
(
anotherAppId
,
"anotherVersion"
)
;
MapPropertySource
somePropertySource
=
mock
(
MapPropertySource
.
class
);
MapPropertySource
anotherPropertySource
=
mock
(
MapPropertySource
.
class
)
;
doReturn
(
Lists
.
newArrayList
(
someApolloRegistry
,
anotherApolloRegistry
)).
when
(
configUtil
).
loadApolloRegistries
(
);
doReturn
(
somePropertySource
).
when
(
remoteConfigLoader
).
loadSingleApolloConfig
(
someApolloRegistry
.
getAppId
(),
someApolloRegistry
.
getVersion
()
);
doReturn
(
a
notherPropertySource
).
when
(
remoteConfigLoader
).
loadSingleApolloConfig
(
anotherApolloRegistry
.
getAppId
(),
anotherApolloRegistry
.
getVersion
());
CompositePropertySource
result
=
remoteConfigLoader
.
loadPropertySource
();
assertEquals
(
2
,
result
.
getPropertySources
().
size
());
assert
True
(
result
.
getPropertySources
().
containsAll
(
Lists
.
newArrayList
(
somePropertySource
,
anotherPropertySource
))
);
public
void
testLoad
ApolloConfig
()
throws
Exception
{
String
someServerUrl
=
"http://someUrl"
;
String
someCluster
=
"some cluster"
;
Apollo
Config
apolloConfig
=
mock
(
ApolloConfig
.
class
);
long
someAppId
=
1
;
ApolloRegistry
apolloRegistry
=
assembleSomeApolloRegistry
(
someAppId
,
"someVersion"
);
ApolloConfig
previousConfig
=
null
;
when
(
configUtil
.
getConfigServerUrl
()).
thenReturn
(
someServerUrl
);
when
(
configUtil
.
getCluster
()).
thenReturn
(
someCluster
);
doReturn
(
a
polloConfig
).
when
(
remoteConfigLoader
)
.
getRemoteConfig
(
restTemplate
,
someServerUrl
,
someCluster
,
apolloRegistry
,
previousConfig
);
ApolloConfig
result
=
remoteConfigLoader
.
loadApolloConfig
(
apolloRegistry
,
previousConfig
);
assert
Equals
(
apolloConfig
,
result
);
}
@Test
public
void
testLoadPropertySourceWithNoApolloRegistry
()
throws
Exception
{
doReturn
(
null
).
when
(
configUtil
).
loadApolloRegistries
();
public
void
testGetRemoteConfig
()
throws
Exception
{
long
someAppId
=
1
;
String
someServerUrl
=
"http://someServer"
;
String
someClusterName
=
"someCluster"
;
String
someVersionName
=
"someVersion"
;
ApolloConfig
someApolloConfig
=
mock
(
ApolloConfig
.
class
);
ApolloRegistry
apolloRegistry
=
assembleSomeApolloRegistry
(
someAppId
,
someVersionName
);
ApolloConfig
previousConfig
=
null
;
CompositePropertySource
result
=
remoteConfigLoader
.
loadPropertySource
();
when
(
someResponse
.
getStatusCode
()).
thenReturn
(
HttpStatus
.
OK
);
when
(
someResponse
.
getBody
()).
thenReturn
(
someApolloConfig
);
when
(
restTemplate
.
exchange
(
anyString
(),
eq
(
HttpMethod
.
GET
),
any
(
HttpEntity
.
class
),
eq
(
ApolloConfig
.
class
),
anyMap
())).
thenReturn
(
someResponse
);
assertTrue
(
result
.
getPropertySources
().
isEmpty
());
ApolloConfig
result
=
remoteConfigLoader
.
getRemoteConfig
(
restTemplate
,
someServerUrl
,
someClusterName
,
apolloRegistry
,
previousConfig
);
assertEquals
(
someApolloConfig
,
result
);
}
@Test
(
expected
=
RuntimeException
.
class
)
public
void
testLoadPropertySourceWithError
()
throws
Exception
{
Exception
someException
=
mock
(
Exception
.
class
);
long
someAppId
=
100
;
ApolloRegistry
someApolloRegistry
=
assembleSomeApolloRegistry
(
someAppId
,
"someVersion"
);
doReturn
(
Lists
.
newArrayList
(
someApolloRegistry
)).
when
(
configUtil
).
loadApolloRegistries
();
doThrow
(
someException
).
when
(
remoteConfigLoader
).
loadSingleApolloConfig
(
someApolloRegistry
.
getAppId
(),
someApolloRegistry
.
getVersion
());
remoteConfigLoader
.
loadPropertySource
();
public
void
testGetRemoteConfigWithServerError
()
throws
Exception
{
long
someAppId
=
1
;
String
someServerUrl
=
"http://someServer"
;
String
someClusterName
=
"someCluster"
;
String
someVersionName
=
"someVersion"
;
ApolloRegistry
apolloRegistry
=
assembleSomeApolloRegistry
(
someAppId
,
someVersionName
);
ApolloConfig
previousConfig
=
null
;
HttpStatus
someErrorCode
=
HttpStatus
.
INTERNAL_SERVER_ERROR
;
when
(
someResponse
.
getStatusCode
()).
thenReturn
(
someErrorCode
);
when
(
restTemplate
.
exchange
(
anyString
(),
eq
(
HttpMethod
.
GET
),
any
(
HttpEntity
.
class
),
eq
(
ApolloConfig
.
class
),
anyMap
())).
thenReturn
(
someResponse
);
remoteConfigLoader
.
getRemoteConfig
(
restTemplate
,
someServerUrl
,
someClusterName
,
apolloRegistry
,
previousConfig
);
}
@Test
public
void
testLoadSingleApolloConfig
()
throws
Exception
{
ApolloConfig
someApolloConfig
=
mock
(
ApolloConfig
.
class
);
Map
<
String
,
Object
>
someMap
=
Maps
.
newHashMap
();
public
void
testGetRemoteConfigWith304Response
()
throws
Exception
{
long
someAppId
=
1
;
String
someServerUrl
=
"http://someServer"
;
String
someClusterName
=
"someCluster"
;
String
someVersionName
=
"someVersion"
;
ApolloRegistry
apolloRegistry
=
assembleSomeApolloRegistry
(
someAppId
,
someVersionName
);
ApolloConfig
previousConfig
=
null
;
when
(
someResponse
.
getStatusCode
()).
thenReturn
(
HttpStatus
.
NOT_MODIFIED
);
when
(
restTemplate
.
exchange
(
anyString
(),
eq
(
HttpMethod
.
GET
),
any
(
HttpEntity
.
class
),
eq
(
ApolloConfig
.
class
),
anyMap
())).
thenReturn
(
someResponse
);
when
(
someApolloConfig
.
getConfigurations
()).
thenReturn
(
someMap
);
doReturn
(
someApolloConfig
).
when
(
remoteConfigLoader
).
getRemoteConfig
(
any
(
RestTemplate
.
class
),
anyString
(),
anyLong
(),
anyString
(),
anyString
());
ApolloConfig
result
=
remoteConfigLoader
.
getRemoteConfig
(
restTemplate
,
someServerUrl
,
someClusterName
,
apolloRegistry
,
previousConfig
);
long
someAppId
=
100
;
MapPropertySource
result
=
remoteConfigLoader
.
loadSingleApolloConfig
(
someAppId
,
"someVersion"
);
assertNull
(
result
);
assertEquals
(
someMap
,
result
.
getSource
());
}
private
ApolloRegistry
assembleSomeApolloRegistry
(
long
someAppId
,
String
someVersion
)
{
...
...
apollo-configservice/src/main/java/com/ctrip/apollo/configservice/controller/ConfigController.java
View file @
49c1b308
...
...
@@ -3,10 +3,7 @@ package com.ctrip.apollo.configservice.controller;
import
com.ctrip.apollo.biz.entity.Version
;
import
com.ctrip.apollo.biz.service.ConfigService
;
import
com.ctrip.apollo.core.dto.ApolloConfig
;
import
org.springframework.web.bind.annotation.PathVariable
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RequestParam
;
import
org.springframework.web.bind.annotation.RestController
;
import
org.springframework.web.bind.annotation.*
;
import
javax.annotation.Resource
;
import
javax.servlet.http.HttpServletResponse
;
...
...
@@ -21,7 +18,7 @@ public class ConfigController {
@Resource
(
name
=
"configService"
)
private
ConfigService
configService
;
@RequestMapping
(
value
=
"/{appId}/{clusterName}/{versionName:.*}"
)
@RequestMapping
(
value
=
"/{appId}/{clusterName}/{versionName:.*}"
,
method
=
RequestMethod
.
GET
)
public
ApolloConfig
queryConfig
(
@PathVariable
long
appId
,
@PathVariable
String
clusterName
,
@PathVariable
String
versionName
,
...
...
apollo-core/src/main/java/com/ctrip/apollo/core/dto/ApolloConfig.java
View file @
49c1b308
...
...
@@ -4,12 +4,13 @@ import com.fasterxml.jackson.annotation.JsonCreator;
import
com.fasterxml.jackson.annotation.JsonProperty
;
import
com.google.common.base.MoreObjects
;
import
java.util.Comparator
;
import
java.util.Map
;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public
class
ApolloConfig
{
public
class
ApolloConfig
implements
Comparable
<
ApolloConfig
>
{
private
long
appId
;
...
...
@@ -21,6 +22,8 @@ public class ApolloConfig {
private
long
releaseId
;
private
int
order
;
@JsonCreator
public
ApolloConfig
(
@JsonProperty
(
"appId"
)
long
appId
,
@JsonProperty
(
"cluster"
)
String
cluster
,
...
...
@@ -57,6 +60,14 @@ public class ApolloConfig {
return
releaseId
;
}
public
int
getOrder
()
{
return
order
;
}
public
void
setOrder
(
int
order
)
{
this
.
order
=
order
;
}
@Override
public
String
toString
()
{
return
MoreObjects
.
toStringHelper
(
this
)
...
...
@@ -68,4 +79,15 @@ public class ApolloConfig {
.
add
(
"configurations"
,
configurations
)
.
toString
();
}
@Override
public
int
compareTo
(
ApolloConfig
o
)
{
if
(
o
==
null
||
this
.
getOrder
()
>
o
.
getOrder
())
{
return
1
;
}
if
(
o
.
getOrder
()
>
this
.
getOrder
())
{
return
-
1
;
}
return
0
;
}
}
apollo-demo/src/main/java/com/ctrip/apollo/demo/controller/DemoController.java
View file @
49c1b308
package
com
.
ctrip
.
apollo
.
demo
.
controller
;
import
com.ctrip.apollo.client.ApolloConfigManager
;
import
com.ctrip.apollo.client.model.ApolloRegistry
;
import
com.ctrip.apollo.client.model.PropertyChange
;
import
com.ctrip.apollo.client.util.ConfigUtil
;
import
com.ctrip.apollo.demo.model.Config
;
import
com.ctrip.apollo.demo.service.DemoService
;
...
...
@@ -27,11 +29,12 @@ public class DemoController {
private
Environment
env
;
@Autowired
private
DemoService
demoService
;
//Apollo config client internal impl, not intended to be used by application, only for this test page
//Apollo config client internal impl, not intended to be used by application, only for this test page
!
private
ConfigUtil
configUtil
=
ConfigUtil
.
getInstance
();
//ApolloConfigManager, not intended to be used by application, only for this test page!
@Autowired
private
RefreshScope
scope
;
private
ApolloConfigManager
apolloConfigManager
;
@RequestMapping
(
value
=
"/config/{configName:.*}"
,
method
=
RequestMethod
.
GET
)
public
Config
queryConfig
(
@PathVariable
String
configName
)
{
...
...
@@ -49,8 +52,8 @@ public class DemoController {
}
@RequestMapping
(
value
=
"/refresh"
,
method
=
RequestMethod
.
POST
)
public
String
refreshBeans
()
{
this
.
scope
.
refreshAll
();
return
"ok"
;
public
List
<
PropertyChange
>
refreshBeans
()
{
List
<
PropertyChange
>
changes
=
this
.
apolloConfigManager
.
updatePropertySource
();
return
changes
;
}
}
apollo-demo/src/main/webapp/s/scripts/app.js
View file @
49c1b308
...
...
@@ -7,11 +7,11 @@
]);
app
.
controller
(
'DemoController'
,
function
(
$scope
,
$http
,
$modal
,
toastr
)
{
//
var NONE = "none";
var
NONE
=
"none"
;
this
.
registries
=
{};
this
.
configQuery
=
{};
//
this.refreshResult = NONE;
this
.
refreshResult
=
NONE
;
this
.
injectedConfigValue
=
''
;
var
self
=
this
;
...
...
@@ -46,24 +46,26 @@
});
};
//this.refreshConfig = function() {
// $http.post("refresh")
// .success(function(data) {
// self.assembleRefreshResult(data);
// })
// .error(function(data, status) {
// toastr.error((data && data.msg) || 'Refresh config failed');
// });
//
//};
this
.
refreshConfig
=
function
()
{
$http
.
post
(
"demo/refresh"
)
.
success
(
function
(
data
)
{
self
.
assembleRefreshResult
(
data
);
})
.
error
(
function
(
data
,
status
)
{
toastr
.
error
((
data
&&
data
.
msg
)
||
'Refresh config failed'
);
});
//this.assembleRefreshResult = function(changedPropertyArray) {
// if(!changedPropertyArray || !changedPropertyArray.length) {
// this.refreshResult = NONE;
// return;
// }
// this.refreshResult = changedPropertyArray.join(',');
//};
};
this
.
assembleRefreshResult
=
function
(
changedPropertyArray
)
{
if
(
!
changedPropertyArray
||
!
changedPropertyArray
.
length
)
{
this
.
refreshResult
=
NONE
;
return
;
}
this
.
refreshResult
=
_
.
map
(
changedPropertyArray
,
function
(
propertyChange
)
{
return
propertyChange
.
propertyName
+
'('
+
propertyChange
.
changeType
+
')'
;
});
};
this
.
loadRegistries
();
...
...
apollo-demo/src/main/webapp/s/templates/list.html
View file @
49c1b308
...
...
@@ -70,12 +70,14 @@
</form>
</div>
<!--<div id="refresh-config-wrapper">-->
<!--<h3>Refresh Config:</h3>-->
<!--<button type="button" class="btn btn-primary" ng-click="demoCtrl.refreshConfig()">Refresh Config</button>-->
<!--<div id="refresh-result">-->
<!--<strong>Changed Properties:</strong> {{demoCtrl.refreshResult}}-->
<!--</div>-->
<!--</div>-->
<div
id=
"refresh-config-wrapper"
>
<h3>
Refresh Config:
</h3>
<button
type=
"button"
class=
"btn btn-primary"
ng-click=
"demoCtrl.refreshConfig()"
>
Refresh
Config
</button>
<div
id=
"refresh-result"
>
<strong>
Changed Properties:
</strong>
{{demoCtrl.refreshResult}}
</div>
</div>
</div>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment