com.landawn.abacus.util.PropertiesUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of abacus-android Show documentation
Show all versions of abacus-android Show documentation
A general and simple library for Android
/*
* Copyright (C) 2015 HaiYang Li
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.landawn.abacus.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import com.landawn.abacus.exception.AbacusException;
import com.landawn.abacus.exception.ParseException;
import com.landawn.abacus.exception.UncheckedIOException;
import com.landawn.abacus.logging.Logger;
import com.landawn.abacus.logging.LoggerFactory;
import com.landawn.abacus.parser.Exclusion;
import com.landawn.abacus.parser.XMLSerializationConfig;
import com.landawn.abacus.parser.XMLSerializationConfig.XSC;
import com.landawn.abacus.type.Type;
import com.landawn.abacus.util.SQLExecutor.JdbcSettings;
import com.landawn.abacus.util.SQLExecutor.ResultExtractor;
/**
*
* @since 0.8
*
* @author Haiyang Li
*/
public final class PropertiesUtil {
private static final Logger logger = LoggerFactory.getLogger(PropertiesUtil.class);
private static final String TYPE = "type";
private static final XMLSerializationConfig xsc = XSC.of(true, true, DateTimeFormat.ISO_8601_DATETIME, Exclusion.NONE, null);
private static final ResultExtractor CONFIG_ENTITY_RESULT_SET_EXTRACTOR = new ResultExtractor() {
@Override
public ConfigEntity extractData(ResultSet rs, JdbcSettings jdbcSettings) throws SQLException {
long offset = jdbcSettings.getOffset();
long count = jdbcSettings.getCount();
if (JdbcUtil.skip(rs, offset) >= offset) {
while ((count-- > 0) && rs.next()) {
ConfigEntity entity = new ConfigEntity();
List columnLabelList = JdbcUtil.getColumnLabelList(rs);
int columnCount = columnLabelList.size();
Method method = null;
Object propValue = null;
for (int i = 0; i < columnCount; i++) {
method = ClassUtil.getPropSetMethod(ConfigEntity.class, columnLabelList.get(i));
propValue = JdbcUtil.getColumnValue(rs, i + 1);
if (method != null) {
if (method.getName().equals("setIncludedServers") || method.getName().equals("setExcludedServers")) {
if (propValue == null || N.isNullOrEmpty(propValue.toString().trim())) {
propValue = new ArrayList<>();
} else {
propValue = Splitter.defauLt().trim(true).split(propValue.toString().trim());
}
} else if (method.getName().equals("setStatus")) {
if (propValue == null || N.isNullOrEmpty(propValue.toString().trim())) {
propValue = null;
} else {
propValue = Status.valueOf(propValue.toString().trim());
}
}
ClassUtil.setPropValue(entity, method, propValue);
}
}
return entity;
}
}
return null;
}
};
private static final ScheduledExecutorService scheduledExecutor;
static {
final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
executor.setRemoveOnCancelPolicy(true);
scheduledExecutor = MoreExecutors.getExitingScheduledExecutorService(executor);
}
private static final Map> registeredAutoRefreshProperties = new ConcurrentHashMap<>(256);
static {
final Runnable refreshTask = new TimerTask() {
@Override
public void run() {
synchronized (registeredAutoRefreshProperties) {
Map, Resource> m = null;
Properties, ?> properties = null;
Resource resource = null;
File file = null;
SQLExecutor sqlExecutor = null;
String sql = null;
ResultExtractor resultExtractor = null;
for (Map.Entry> entry : registeredAutoRefreshProperties.entrySet()) {
resource = entry.getKey();
properties = entry.getValue();
file = resource.getFile();
if (file != null) {
if (file.lastModified() > resource.getLastLoadTime()) {
long lastLoadTime = file.lastModified();
InputStream is = null;
if (logger.isWarnEnabled()) {
logger.warn("Start to refresh properties with the updated file: " + file.getAbsolutePath());
logger.warn("[PROPERTIES]" + properties);
}
try {
is = new FileInputStream(resource.getFile());
if (resource.getType() == ResourceType.PROPERTIES) {
load((Properties) properties, is);
} else {
loadFromXML(properties, properties.getClass(), is);
}
if (m == null) {
m = new HashMap<>();
}
m.put(properties, resource);
resource.setLastLoadTime(lastLoadTime);
} catch (Exception e) {
logger.error("Failed to refresh properties: " + properties, e);
} finally {
IOUtil.close(is);
}
if (logger.isWarnEnabled()) {
logger.warn("End to refresh properties with the updated file: " + file.getAbsolutePath());
logger.warn("[NEW PROPERTIES]" + properties);
}
}
} else {
sqlExecutor = resource.getSqlExecutor();
sql = resource.getSql();
resultExtractor = resource.getResultSetExtractor();
try {
if (resultExtractor == null) {
resultExtractor = CONFIG_ENTITY_RESULT_SET_EXTRACTOR;
}
ConfigEntity entity = sqlExecutor.query(sql, resultExtractor);
if (entity == null || N.isNullOrEmpty(entity.getContent())) {
throw new AbacusException("No record found or the content of properties is empty");
}
if (entity.getLastUpdateTime().getTime() > resource.getLastLoadTime()) {
boolean isIncluded = false;
if (N.notNullOrEmpty(entity.getExcludedServers())) {
isIncluded = true;
for (String serverName : entity.getExcludedServers()) {
if (IOUtil.HOST_NAME.matches(serverName)) {
isIncluded = false;
break;
}
}
} else if (N.notNullOrEmpty(entity.getIncludedServers())) {
for (String serverName : entity.getIncludedServers()) {
if (IOUtil.HOST_NAME.matches(serverName)) {
isIncluded = true;
break;
}
}
} else {
isIncluded = true;
}
if (isIncluded) {
if (logger.isWarnEnabled()) {
logger.warn("Start to refresh properties with sql: " + sql);
logger.warn("[PROPERTIES]" + properties);
}
if (resource.getType() == ResourceType.PROPERTIES) {
load((Properties) properties, IOUtil.string2InputStream(entity.getContent()));
} else {
loadFromXML(properties, properties.getClass(), IOUtil.string2InputStream(entity.getContent()));
}
if (m == null) {
m = new HashMap<>();
}
m.put(properties, resource);
if (logger.isWarnEnabled()) {
logger.warn("End to refresh properties with sql: " + sql);
logger.warn("[NEW PROPERTIES]" + properties);
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("Properties is not refreshed because it's excluded or not included by: [excludedServers]: "
+ entity.getExcludedServers() + ". [includedServers]: " + entity.getIncludedServers());
}
}
resource.setLastLoadTime(entity.getLastUpdateTime().getTime());
}
} catch (Exception e) {
logger.error("Failed to refresh properties: " + properties, e);
}
}
}
}
}
};
scheduledExecutor.scheduleWithFixedDelay(refreshTask, 1000, 1000, TimeUnit.MICROSECONDS);
}
private PropertiesUtil() {
// singleton.
}
public static File findFile(String configFileName) {
return Configuration.findFile(configFileName);
}
public static File findDir(String configDir) {
return Configuration.findDir(configDir);
}
public static Properties load(File file) {
return load(file, false);
}
public static Properties load(File file, boolean autoRefresh) {
Properties properties = null;
InputStream is = null;
try {
is = new FileInputStream(file);
if (autoRefresh) {
Resource resource = new Resource(Properties.class, file, ResourceType.PROPERTIES);
resource.setLastLoadTime(file.lastModified());
synchronized (registeredAutoRefreshProperties) {
properties = (Properties) registeredAutoRefreshProperties.get(resource);
if (properties == null) {
properties = load(is);
registeredAutoRefreshProperties.put(resource, properties);
}
}
} else {
properties = load(is);
}
return properties;
} catch (FileNotFoundException e) {
throw new UncheckedIOException(e);
} finally {
IOUtil.close(is);
}
}
public static Properties load(InputStream is) {
return load(null, is);
}
private static Properties load(Properties targetProperties, InputStream is) {
java.util.Properties tmp = new java.util.Properties();
try {
tmp.load(is);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return create(targetProperties, tmp);
}
public static Properties load(Reader reader) {
java.util.Properties tmp = new java.util.Properties();
try {
tmp.load(reader);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
return create(null, tmp);
}
public static Properties load(SQLExecutor sqlExecutor, String sql, boolean autoRefresh) {
return load(null, sqlExecutor, sql, autoRefresh);
}
private static Properties load(Properties targetProperties, SQLExecutor sqlExecutor, String sql, boolean autoRefresh) {
ConfigEntity entity = sqlExecutor.query(sql, CONFIG_ENTITY_RESULT_SET_EXTRACTOR);
if (entity == null || N.isNullOrEmpty(entity.getContent())) {
throw new AbacusException("No record found or the content of properties is empty");
}
Properties properties = null;
if (autoRefresh) {
Class> targetClass = targetProperties == null ? Properties.class : targetProperties.getClass();
Resource resource = new Resource(targetClass, sqlExecutor, sql, CONFIG_ENTITY_RESULT_SET_EXTRACTOR, ResourceType.PROPERTIES);
resource.setLastLoadTime(entity.getLastUpdateTime().getTime());
synchronized (registeredAutoRefreshProperties) {
properties = (Properties) registeredAutoRefreshProperties.get(resource);
if (properties == null) {
properties = load(targetProperties, IOUtil.string2InputStream(entity.getContent()));
registeredAutoRefreshProperties.put(resource, properties);
}
}
} else {
properties = load(targetProperties, IOUtil.string2InputStream(entity.getContent()));
}
return properties;
}
private static Properties create(Properties targetProperties, java.util.Properties newProperties) {
Properties properties = null;
if (targetProperties == null) {
properties = new Properties<>();
} else {
properties = targetProperties;
}
Set newKeySet = new HashSet<>();
Enumeration> it = newProperties.propertyNames();
String propName = null;
while (it.hasMoreElements()) {
propName = it.nextElement().toString();
properties.set(propName, newProperties.getProperty(propName));
newKeySet.add(propName);
}
if (targetProperties != null) {
Set oldKeySet = new HashSet<>(properties.keySet());
for (String key : oldKeySet) {
if (!newKeySet.contains(key)) {
properties.remove(key);
}
}
}
return properties;
}
public static Properties loadFromXML(File file) {
return loadFromXML(file, false);
}
public static Properties loadFromXML(File file, boolean autoRefresh) {
return loadFromXML(Properties.class, file, autoRefresh);
}
public static Properties loadFromXML(InputStream is) {
return loadFromXML(Properties.class, is);
}
public static Properties loadFromXML(SQLExecutor sqlExecutor, String sql, boolean autoRefresh) {
return loadFromXML(Properties.class, sqlExecutor, sql, autoRefresh);
}
public static > T loadFromXML(Class targetClass, File file) {
return loadFromXML(targetClass, file, false);
}
/**
*
* @param targetClass
* @param file
* @param autoRefresh
* @return
*/
public static > T loadFromXML(Class targetClass, File file, boolean autoRefresh) {
T properties = null;
InputStream is = null;
try {
is = new FileInputStream(file);
if (autoRefresh) {
Resource resource = new Resource(targetClass, file, ResourceType.XML);
resource.setLastLoadTime(file.lastModified());
synchronized (registeredAutoRefreshProperties) {
properties = (T) registeredAutoRefreshProperties.get(resource);
if (properties == null) {
properties = loadFromXML(targetClass, is);
registeredAutoRefreshProperties.put(resource, properties);
}
}
} else {
properties = loadFromXML(targetClass, is);
}
return properties;
} catch (FileNotFoundException e) {
throw new UncheckedIOException(e);
} finally {
IOUtil.close(is);
}
}
public static > T loadFromXML(Class targetClass, InputStream is) {
return loadFromXML(null, targetClass, is);
}
public static > T loadFromXML(Class targetClass, SQLExecutor sqlExecutor, String sql, boolean autoRefresh) {
ConfigEntity entity = sqlExecutor.query(sql, CONFIG_ENTITY_RESULT_SET_EXTRACTOR);
if (entity == null || N.isNullOrEmpty(entity.getContent())) {
throw new AbacusException("No record found or the content of properties is empty");
}
T properties = null;
if (autoRefresh) {
Resource resource = new Resource(targetClass, sqlExecutor, sql, CONFIG_ENTITY_RESULT_SET_EXTRACTOR, ResourceType.XML);
resource.setLastLoadTime(entity.getLastUpdateTime().getTime());
synchronized (registeredAutoRefreshProperties) {
properties = (T) registeredAutoRefreshProperties.get(resource);
if (properties == null) {
properties = loadFromXML(targetClass, IOUtil.string2InputStream(entity.getContent()));
registeredAutoRefreshProperties.put(resource, properties);
}
}
} else {
properties = loadFromXML(targetClass, IOUtil.string2InputStream(entity.getContent()));
}
return properties;
}
private static > T loadFromXML(Object targetProperties, Class targetClass, InputStream is) {
DocumentBuilder docBuilder = XMLUtil.createDOMParser(true, true);
Document doc;
try {
doc = docBuilder.parse(is);
} catch (SAXException e) {
throw new ParseException(e);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
Node node = doc.getFirstChild();
return loadFromXML(targetProperties, targetClass, node, null, true);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private static > T loadFromXML(Object targetProperties, Class inputClass, Node node, Method propSetMethod,
boolean isFirstCall) {
// TODO it's difficult to support duplicated property and may be misused.
if (hasDuplicatedPropName(node)) {
throw new AbacusException("The source xml document contains duplicated properties which has same node tag name in the same root.");
}
Class> targetClass = null;
if (isFirstCall) {
targetClass = targetProperties == null ? (inputClass == null ? Properties.class : inputClass) : targetProperties.getClass();
} else {
targetClass = (propSetMethod == null) ? Properties.class : propSetMethod.getParameterTypes()[0];
}
T properties = (T) (targetProperties == null ? N.newInstance(targetClass) : targetProperties);
NodeList propNodes = node.getChildNodes();
int propNodeLength = (propNodes == null) ? 0 : propNodes.getLength();
Set newKeySet = new HashSet<>();
Node propNode = null;
String typeAttr = null;
String propName = null;
Object propValue = null;
for (int i = 0; i < propNodeLength; i++) {
propNode = propNodes.item(i);
if (propNode.getNodeType() != Document.ELEMENT_NODE) {
continue;
}
propName = ClassUtil.formalizePropName(propNode.getNodeName());
newKeySet.add(propName);
typeAttr = XMLUtil.getAttribute(propNode, TYPE);
propSetMethod = ClassUtil.getPropSetMethod(targetClass, propName);
if (XMLUtil.isTextElement(propNode)) {
if (N.isNullOrEmpty(typeAttr)) {
propValue = Configuration.getTextContent(propNode);
} else {
propValue = N.typeOf(typeAttr).valueOf(Configuration.getTextContent(propNode));
}
} else {
// TODO it's difficult to support duplicated property and may be misused.
// How to get target property value for auto-refresh if it's list of Properties or entities.
Object targetPropValue = properties.get(propName);
Class propClass = (Class) (propSetMethod == null ? Properties.class : propSetMethod.getParameterTypes()[0]);
propValue = loadFromXML(targetPropValue, propClass, propNode, propSetMethod, false);
}
Object oldPropValue = properties.get(propName);
if (oldPropValue != null && oldPropValue.getClass().equals(propValue.getClass())
&& (oldPropValue instanceof Collection || oldPropValue instanceof Map) && !(oldPropValue instanceof Properties)) {
if (oldPropValue instanceof Collection) {
((Collection) oldPropValue).clear();
((Collection) oldPropValue).addAll((Collection) propValue);
} else if (oldPropValue instanceof Map) {
((Map) oldPropValue).clear();
((Map) oldPropValue).putAll((Map) propValue);
}
} else {
if (propSetMethod == null) {
// TODO it's difficult to support duplicated property and may be misused.
// if (properties.containsKey(propName)) {
// String listPropName = propName + "List";
// List