fixed issue #2099 , support remote config poll
This commit is contained in:
@@ -16,5 +16,16 @@
|
||||
<artifactId>canal.instance.core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.otter</groupId>
|
||||
<artifactId>canal.instance.spring</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- junit -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package com.alibaba.otter.canal.instance.manager;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
import com.alibaba.otter.canal.common.CanalException;
|
||||
import com.alibaba.otter.canal.instance.core.CanalInstance;
|
||||
import com.alibaba.otter.canal.instance.core.CanalInstanceGenerator;
|
||||
import com.alibaba.otter.canal.instance.manager.plain.PlainCanal;
|
||||
import com.alibaba.otter.canal.instance.manager.plain.PlainCanalConfigClient;
|
||||
import com.alibaba.otter.canal.instance.spring.SpringCanalInstanceGenerator;
|
||||
|
||||
/**
|
||||
* 基于manager生成对应的{@linkplain CanalInstance}
|
||||
*
|
||||
* @author jianghang 2012-7-12 下午05:37:09
|
||||
* @version 1.0.0
|
||||
*/
|
||||
public class PlainCanalInstanceGenerator implements CanalInstanceGenerator {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SpringCanalInstanceGenerator.class);
|
||||
private String springXml;
|
||||
private PlainCanalConfigClient canalConfigClient;
|
||||
private String defaultName = "instance";
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
public CanalInstance generate(String destination) {
|
||||
synchronized (CanalInstanceGenerator.class) {
|
||||
try {
|
||||
PlainCanal canal = canalConfigClient.findInstance(destination, null);
|
||||
if (canal == null) {
|
||||
throw new CanalException("instance : " + destination + " config is not found");
|
||||
}
|
||||
// 设置动态properties,替换掉本地properties
|
||||
com.alibaba.otter.canal.instance.spring.support.PropertyPlaceholderConfigurer.propertiesLocal.set(canal.getProperties());
|
||||
// 设置当前正在加载的通道,加载spring查找文件时会用到该变量
|
||||
System.setProperty("canal.instance.destination", destination);
|
||||
this.beanFactory = getBeanFactory(springXml);
|
||||
String beanName = destination;
|
||||
if (!beanFactory.containsBean(beanName)) {
|
||||
beanName = defaultName;
|
||||
}
|
||||
|
||||
return (CanalInstance) beanFactory.getBean(beanName);
|
||||
} catch (Throwable e) {
|
||||
logger.error("generator instance failed.", e);
|
||||
throw new CanalException(e);
|
||||
} finally {
|
||||
System.setProperty("canal.instance.destination", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ================ setter / getter ================
|
||||
|
||||
private BeanFactory getBeanFactory(String springXml) {
|
||||
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(springXml);
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
public void setCanalConfigClient(PlainCanalConfigClient canalConfigClient) {
|
||||
this.canalConfigClient = canalConfigClient;
|
||||
}
|
||||
|
||||
public void setSpringXml(String springXml) {
|
||||
this.springXml = springXml;
|
||||
}
|
||||
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
package com.alibaba.otter.canal.instance.manager.plain;
|
||||
|
||||
import static org.apache.http.client.config.RequestConfig.custom;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.protocol.HttpClientContext;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.config.RegistryBuilder;
|
||||
import org.apache.http.conn.socket.ConnectionSocketFactory;
|
||||
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.apache.http.ssl.SSLContextBuilder;
|
||||
import org.apache.http.ssl.TrustStrategy;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
/**
|
||||
* http client 工具类
|
||||
*
|
||||
* @author rewerma 2019-08-26 上午09:40:36
|
||||
* @version 1.0.0
|
||||
*/
|
||||
public class HttpHelper {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(HttpHelper.class);
|
||||
|
||||
public static final Integer REST_STATE_OK = 20000;
|
||||
public static final Integer REST_STATE_TOKEN_INVALID = 50014;
|
||||
public static final Integer REST_STATE_ERROR = 50000;
|
||||
|
||||
private CloseableHttpClient httpclient;
|
||||
|
||||
public HttpHelper(){
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setMaxConnPerRoute(50);
|
||||
builder.setMaxConnTotal(100);
|
||||
|
||||
// 创建支持忽略证书的https
|
||||
try {
|
||||
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
|
||||
|
||||
@Override
|
||||
public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
|
||||
return true;
|
||||
}
|
||||
}).build();
|
||||
|
||||
httpclient = HttpClientBuilder.create()
|
||||
.setSSLContext(sslContext)
|
||||
.setConnectionManager(new PoolingHttpClientConnectionManager(RegistryBuilder.<ConnectionSocketFactory> create()
|
||||
.register("http", PlainConnectionSocketFactory.INSTANCE)
|
||||
.register("https", new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE))
|
||||
.build()))
|
||||
.build();
|
||||
} catch (Throwable e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
public String get(String url, Map<String, String> heads, int timeout) {
|
||||
url = url.trim();
|
||||
CloseableHttpResponse response = null;
|
||||
HttpGet httpGet = null;
|
||||
try {
|
||||
URI uri = new URIBuilder(url).build();
|
||||
RequestConfig config = custom().setConnectTimeout(timeout)
|
||||
.setConnectionRequestTimeout(timeout)
|
||||
.setSocketTimeout(timeout)
|
||||
.build();
|
||||
httpGet = new HttpGet(uri);
|
||||
if (heads != null) {
|
||||
for (Map.Entry<String, String> entry : heads.entrySet()) {
|
||||
httpGet.setHeader(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
HttpClientContext context = HttpClientContext.create();
|
||||
context.setRequestConfig(config);
|
||||
response = httpclient.execute(httpGet, context);
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode == HttpStatus.SC_OK) {
|
||||
return EntityUtils.toString(response.getEntity());
|
||||
} else {
|
||||
String errorMsg = EntityUtils.toString(response.getEntity());
|
||||
throw new RuntimeException("requestGet remote error, url=" + uri.toString() + ", code=" + statusCode
|
||||
+ ", error msg=" + errorMsg);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("requestGet remote error, request : " + url, t);
|
||||
} finally {
|
||||
if (response != null) {
|
||||
try {
|
||||
response.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (httpGet != null) {
|
||||
httpGet.releaseConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String post(String url, Map<String, String> heads, Object requestBody, int timeout) {
|
||||
return post0(url, heads, JSON.toJSONString(requestBody), timeout);
|
||||
}
|
||||
|
||||
public String post0(String url, Map<String, String> heads, String requestBody, int timeout) {
|
||||
url = url.trim();
|
||||
HttpPost httpPost = null;
|
||||
CloseableHttpResponse response = null;
|
||||
try {
|
||||
URI uri = new URIBuilder(url).build();
|
||||
RequestConfig config = custom().setConnectTimeout(timeout)
|
||||
.setConnectionRequestTimeout(timeout)
|
||||
.setSocketTimeout(timeout)
|
||||
.build();
|
||||
httpPost = new HttpPost(uri);
|
||||
StringEntity entity = new StringEntity(requestBody, "UTF-8");
|
||||
httpPost.setEntity(entity);
|
||||
httpPost.setHeader("Content-Type", "application/json;charset=utf8");
|
||||
if (heads != null) {
|
||||
for (Map.Entry<String, String> entry : heads.entrySet()) {
|
||||
httpPost.setHeader(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
HttpClientContext context = HttpClientContext.create();
|
||||
context.setRequestConfig(config);
|
||||
|
||||
response = httpclient.execute(httpPost, context);
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
if (statusCode == HttpStatus.SC_OK) {
|
||||
return EntityUtils.toString(response.getEntity());
|
||||
} else {
|
||||
throw new RuntimeException("requestPost remote error, request : " + url + ", statusCode=" + statusCode
|
||||
+ ";" + EntityUtils.toString(response.getEntity()));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("requestPost remote error, request : " + url, t);
|
||||
} finally {
|
||||
if (response != null) {
|
||||
try {
|
||||
response.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (httpPost != null) {
|
||||
httpPost.releaseConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (httpclient != null) {
|
||||
try {
|
||||
httpclient.close();
|
||||
} catch (IOException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package com.alibaba.otter.canal.instance.manager.plain;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* plain远程配置,提供基于properties纯文本的配置
|
||||
*
|
||||
* @author rewerma 2019-01-25 下午05:20:16
|
||||
* @author agapple
|
||||
* @version 1.0.0
|
||||
*/
|
||||
public class PlainCanal {
|
||||
|
||||
private Properties properties;
|
||||
private String md5;
|
||||
private String status;
|
||||
|
||||
public PlainCanal(){
|
||||
}
|
||||
|
||||
public PlainCanal(Properties properties, String status, String md5){
|
||||
this.properties = properties;
|
||||
this.md5 = md5;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setProperties(Properties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public String getMd5() {
|
||||
return md5;
|
||||
}
|
||||
|
||||
public void setMd5(String md5) {
|
||||
this.md5 = md5;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PlainCanal [properties=" + properties + ", md5=" + md5 + ", status=" + status + "]";
|
||||
}
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package com.alibaba.otter.canal.instance.manager.plain;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.alibaba.otter.canal.common.AbstractCanalLifeCycle;
|
||||
import com.alibaba.otter.canal.common.CanalException;
|
||||
import com.alibaba.otter.canal.common.CanalLifeCycle;
|
||||
import com.alibaba.otter.canal.protocol.SecurityUtil;
|
||||
|
||||
/**
|
||||
* 远程配置获取
|
||||
*
|
||||
* @author rewerma 2019-01-25 下午05:20:16
|
||||
* @author agapple 2019年8月26日 下午7:52:06
|
||||
* @since 1.1.4
|
||||
*/
|
||||
public class PlainCanalConfigClient extends AbstractCanalLifeCycle implements CanalLifeCycle {
|
||||
|
||||
private final static Integer REQUEST_TIMEOUT = 5000;
|
||||
private String configURL;
|
||||
private String user;
|
||||
private String passwd;
|
||||
private HttpHelper httpHelper;
|
||||
|
||||
public PlainCanalConfigClient(String configURL, String user, String passwd){
|
||||
this.configURL = configURL;
|
||||
if (!StringUtils.startsWithIgnoreCase(configURL, "http")) {
|
||||
this.configURL = "http://" + configURL;
|
||||
} else {
|
||||
this.configURL = configURL;
|
||||
}
|
||||
this.user = user;
|
||||
this.passwd = passwd;
|
||||
this.httpHelper = new HttpHelper();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载canal.properties文件
|
||||
*
|
||||
* @return 远程配置的properties
|
||||
*/
|
||||
public PlainCanal findServer(String md5) {
|
||||
if (StringUtils.isEmpty(md5)) {
|
||||
md5 = "";
|
||||
}
|
||||
String url = configURL + "/api/v1/config/server_poll?md5=" + md5;
|
||||
return queryConfig(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载远程的instance.properties
|
||||
*/
|
||||
public PlainCanal findInstance(String destination, String md5) {
|
||||
if (StringUtils.isEmpty(md5)) {
|
||||
md5 = "";
|
||||
}
|
||||
String url = configURL + "/api/v1/config/instance_poll/" + destination + "?md5=" + md5;
|
||||
return queryConfig(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回需要运行的instance列表
|
||||
*/
|
||||
public String findInstances(String ip, String port, String md5) {
|
||||
if (StringUtils.isEmpty(md5)) {
|
||||
md5 = "";
|
||||
}
|
||||
String url = configURL + "/api/v1/config/instances_poll?md5=" + md5 + "&ip=" + ip + "&port=" + port;
|
||||
ResponseModel<CanalConfig> config = doQuery(url);
|
||||
if (config.data != null) {
|
||||
return config.data.content;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private PlainCanal queryConfig(String url) {
|
||||
try {
|
||||
ResponseModel<CanalConfig> config = doQuery(url);
|
||||
return processData(config.data);
|
||||
} catch (Throwable e) {
|
||||
throw new CanalException("load manager config failed.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private ResponseModel<CanalConfig> doQuery(String url) {
|
||||
Map<String, String> heads = new HashMap<>();
|
||||
heads.put("user", user);
|
||||
heads.put("passwd", passwd);
|
||||
String response = httpHelper.get(url, heads, REQUEST_TIMEOUT);
|
||||
ResponseModel<CanalConfig> resp = JSONObject.parseObject(response,
|
||||
new TypeReference<ResponseModel<CanalConfig>>() {
|
||||
});
|
||||
|
||||
if (!HttpHelper.REST_STATE_OK.equals(resp.code)) {
|
||||
throw new CanalException("requestGet for canal config error: " + resp.message);
|
||||
}
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
private PlainCanal processData(CanalConfig config) throws IOException, NoSuchAlgorithmException {
|
||||
Properties properties = new Properties();
|
||||
String md5 = null;
|
||||
String status = null;
|
||||
if (config != null && StringUtils.isNotEmpty(config.content)) {
|
||||
md5 = SecurityUtil.md5String(config.content);
|
||||
status = config.status;
|
||||
properties.load(new ByteArrayInputStream(config.content.getBytes(StandardCharsets.UTF_8)));
|
||||
} else {
|
||||
// null代表没有新配置变更
|
||||
return null;
|
||||
}
|
||||
|
||||
return new PlainCanal(properties, status, md5);
|
||||
}
|
||||
|
||||
private static class ResponseModel<T> {
|
||||
|
||||
public Integer code;
|
||||
public String message;
|
||||
public T data;
|
||||
}
|
||||
|
||||
private static class CanalConfig {
|
||||
|
||||
public String content;
|
||||
public String status;
|
||||
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.alibaba.otter.canal.instance.manager;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.alibaba.otter.canal.instance.manager.plain.PlainCanal;
|
||||
import com.alibaba.otter.canal.instance.manager.plain.PlainCanalConfigClient;
|
||||
|
||||
public class PlainCanalConfigClientIntegration {
|
||||
|
||||
@Test
|
||||
public void testSimple() {
|
||||
PlainCanalConfigClient client = new PlainCanalConfigClient("http://127.0.0.1:8089",
|
||||
"admin",
|
||||
"4ACFE3202A5FF5CF467898FC58AAB1D615029441");
|
||||
|
||||
PlainCanal plain = client.findServer(null);
|
||||
Assert.notNull(plain);
|
||||
|
||||
plain = client.findServer(plain.getMd5());
|
||||
Assert.isNull(plain);
|
||||
|
||||
plain = client.findInstance("example", null);
|
||||
Assert.notNull(plain);
|
||||
|
||||
plain = client.findInstance("example", plain.getMd5());
|
||||
Assert.isNull(plain);
|
||||
}
|
||||
}
|
||||
+33
-12
@@ -1,9 +1,12 @@
|
||||
package com.alibaba.otter.canal.instance.spring;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
import com.alibaba.otter.canal.common.CanalException;
|
||||
import com.alibaba.otter.canal.instance.core.CanalInstance;
|
||||
import com.alibaba.otter.canal.instance.core.CanalInstanceGenerator;
|
||||
|
||||
@@ -11,22 +14,40 @@ import com.alibaba.otter.canal.instance.core.CanalInstanceGenerator;
|
||||
* @author zebin.xuzb @ 2012-7-12
|
||||
* @version 1.0.0
|
||||
*/
|
||||
public class SpringCanalInstanceGenerator implements CanalInstanceGenerator, BeanFactoryAware {
|
||||
public class SpringCanalInstanceGenerator implements CanalInstanceGenerator {
|
||||
|
||||
private String defaultName = "instance";
|
||||
private BeanFactory beanFactory;
|
||||
private static final Logger logger = LoggerFactory.getLogger(SpringCanalInstanceGenerator.class);
|
||||
private String springXml;
|
||||
private String defaultName = "instance";
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
public CanalInstance generate(String destination) {
|
||||
String beanName = destination;
|
||||
if (!beanFactory.containsBean(beanName)) {
|
||||
beanName = defaultName;
|
||||
synchronized (CanalInstanceGenerator.class) {
|
||||
try {
|
||||
// 设置当前正在加载的通道,加载spring查找文件时会用到该变量
|
||||
System.setProperty("canal.instance.destination", destination);
|
||||
this.beanFactory = getBeanFactory(springXml);
|
||||
String beanName = destination;
|
||||
if (!beanFactory.containsBean(beanName)) {
|
||||
beanName = defaultName;
|
||||
}
|
||||
|
||||
return (CanalInstance) beanFactory.getBean(beanName);
|
||||
} catch (Throwable e) {
|
||||
logger.error("generator instance failed.", e);
|
||||
throw new CanalException(e);
|
||||
} finally {
|
||||
System.setProperty("canal.instance.destination", "");
|
||||
}
|
||||
}
|
||||
|
||||
return (CanalInstance) beanFactory.getBean(beanName);
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
private BeanFactory getBeanFactory(String springXml) {
|
||||
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(springXml);
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
public void setSpringXml(String springXml) {
|
||||
this.springXml = springXml;
|
||||
}
|
||||
}
|
||||
|
||||
+36
-9
@@ -21,10 +21,19 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class PropertyPlaceholderConfigurer extends org.springframework.beans.factory.config.PropertyPlaceholderConfigurer implements ResourceLoaderAware, InitializingBean {
|
||||
|
||||
private static final String PLACEHOLDER_PREFIX = "${";
|
||||
private static final String PLACEHOLDER_SUFFIX = "}";
|
||||
private ResourceLoader loader;
|
||||
private String[] locationNames;
|
||||
private static final String PLACEHOLDER_PREFIX = "${";
|
||||
private static final String PLACEHOLDER_SUFFIX = "}";
|
||||
public static ThreadLocal<Properties> propertiesLocal = new ThreadLocal<Properties>() {
|
||||
|
||||
@Override
|
||||
protected Properties initialValue() {
|
||||
return new Properties();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private ResourceLoader loader;
|
||||
private String[] locationNames;
|
||||
|
||||
public PropertyPlaceholderConfigurer(){
|
||||
setIgnoreUnresolvablePlaceholders(true);
|
||||
@@ -117,13 +126,31 @@ public class PropertyPlaceholderConfigurer extends org.springframework.beans.fac
|
||||
@Override
|
||||
protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) {
|
||||
DefaultablePlaceholder dp = new DefaultablePlaceholder(placeholder);
|
||||
String value = super.resolvePlaceholder(dp.placeholder, props, systemPropertiesMode);
|
||||
|
||||
if (value == null) {
|
||||
value = dp.defaultValue;
|
||||
String propVal = null;
|
||||
// 以system为准覆盖本地配置, 适用于docker
|
||||
if (systemPropertiesMode == SYSTEM_PROPERTIES_MODE_OVERRIDE) {
|
||||
propVal = resolveSystemProperty(dp.placeholder);
|
||||
}
|
||||
|
||||
return trimToEmpty(value);
|
||||
// 以threadlocal的为准覆盖file properties
|
||||
if (propVal == null) {
|
||||
Properties localProperties = propertiesLocal.get();
|
||||
propVal = resolvePlaceholder(dp.placeholder, localProperties);
|
||||
}
|
||||
|
||||
if (propVal == null) {
|
||||
propVal = resolvePlaceholder(dp.placeholder, props);
|
||||
}
|
||||
|
||||
if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) {
|
||||
propVal = resolveSystemProperty(dp.placeholder);
|
||||
}
|
||||
|
||||
if (propVal == null) {
|
||||
propVal = dp.defaultValue;
|
||||
}
|
||||
|
||||
return trimToEmpty(propVal);
|
||||
}
|
||||
|
||||
private static class DefaultablePlaceholder {
|
||||
|
||||
Reference in New Issue
Block a user