All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.kits.PropertiesKit Maven / Gradle / Ivy

The newest version!
package io.github.kits;

import io.github.kits.configuration.AutoConfigurationHandler;
import io.github.kits.enums.PropEnum;
import io.github.kits.log.Logger;
import io.github.kits.timer.TimedTask;

import java.io.File;
import java.io.StringReader;
import java.lang.ref.Reference;
import java.lang.reflect.Field;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Profile utility class, load and cache configuration file content
 * Check if the configuration file is modified every 5s, if the modification will be reloaded
 *
 * @project: kits
 * @created: with IDEA
 * @author: kits
 * @date: 2018 04 26 12:51PM | April. Thursday
 */
public final class PropertiesKit {

	private final static String LAST_MODIFY_TIME = "$$LMT";

	private final static String TIMER_TASK_NAME = "PropertiesKit-Reload";

	/**
	 * Properties cache
	 */
	private static ConcurrentHashMap PROPERTIES_CACHE_MAP;

	/**
	 * Get the default configuration file
	 *
	 * @return Default profile name
	 */
	public static String getDefaultConfig() {
		return PropEnum.DEFAULT_CONFIG_PROPERTIES.getProp();
	}

	/**
	 * Clear the Properties configuration
	 */
	public static void clearAll() {
		PROPERTIES_CACHE_MAP.clear();
	}

	/**
	 * Clear the contents of a specific profile
	 *
	 * @param filePath file path
	 */
	public static void clear(String filePath) {
		File propFile = getPropFile(filePath);
		clear(propFile);
	}

	/**
	 * Clear the contents of a specific profile
	 *
	 * @param file file
	 */
	public static void clear(File file) {
		if (FileKit.isNotNullOrEmpty(file)) {
			PROPERTIES_CACHE_MAP.remove(file);
		}
	}

	/**
	 * Settings value
	 *
	 * @param fileName file path
	 * @param key      key
	 * @param value    value
	 */
	public static void set(String fileName, Object key, Object value) {
		Properties properties = getProperties(fileName);
		set(properties, key, value);
	}

	/**
	 * Settings value
	 *
	 * @param file  file path
	 * @param key   keyValue
	 * @param value value
	 */
	public static void set(File file, Object key, Object value) {
		Properties properties = getProperties(file);
		set(properties, key, value);
	}

	/**
	 * Settings
	 *
	 * @param properties Properties file
	 * @param key        Key value
	 * @param value      value
	 */
	public static void set(Properties properties, Object key, Object value) {
		if (isNotNullOrEmpty(properties)) {
			properties.put(key, value);
		}
	}

	/**
	 * Add a new properties to the cache
	 *
	 * @param key        key
	 * @param properties Properties
	 */
	private static void put(File key, Properties properties) {
		properties.setProperty(LAST_MODIFY_TIME, String.valueOf(key.lastModified()));
		PROPERTIES_CACHE_MAP.put(key, properties);
	}

	/**
	 * Get the corresponding value from the path according to the key
	 *
	 * @param filePath      file path
	 * @param key           key
	 * @return The value obtained from the key
	 */
//    public static Optional getProperty(String filePath, String key) {
//        return get(filePath, key).map(val -> val);
//    }

	/**
	 * Get Properties, Return to default if it does not exist
	 *
	 * @param filePath      文件路径
	 * @param key           键
	 * @param defaultValue  默认值
	 * @return 根据键获取的值
	 */
//    public static Optional getPropertyOrDefault(String filePath, String key, Object defaultValue) {
//        return Optional.ofNullable(getOrDefault(filePath, key, defaultValue));
//    }

	/**
	 * Get the value corresponding to the key from the file
	 *
	 * @param file          File object
	 * @param key           key
	 * @return The value obtained from the key
	 */
//    public static Optional getProperty(File file, String key) {
//        return get(file, key).map(val -> {
//            Object ss = val;
//            return ss;
//        });
//    }

	/**
	 * Get Properties, return default if it does not exist
	 *
	 * @param file          File object
	 * @param key           key
	 * @param defaultValue  Defaults
	 * @return The value obtained from the key
	 */
//    public static Optional getPropertyOrDefault(File file, String key, Object defaultValue) {
//        return Optional.ofNullable(getOrDefault(file, key, defaultValue));
//    }

	/**
	 * Get the value from the default configuration file according to the key
	 *
	 * @param key           key
	 * @return The value obtained from the key
	 */
//    public static Optional getProperty(String key) {
//        return get(PropEnum.DEFAULT_CONFIG_PROPERTIES.getProp(), key);
//    }

	/**
	 * Get Properties, return default if it does not exist
	 *
	 * @param key           key
	 * @param defaultValue  Defaults
	 * @return The value obtained from the key
	 */
//    public static Optional getPropertyOrDefault(String key, Object defaultValue) {
//        return Optional.ofNullable(getOrDefault(PropEnum.DEFAULT_CONFIG_PROPERTIES.getProp(), key, defaultValue));
//    }

	/**
	 * Get the value from the specified path file according to the key
	 *
	 * @param filePath file path
	 * @param key      key
	 * @return The value obtained from the key
	 */
	public static Optional getString(String filePath, String key) {
		return get(filePath, key).map(String::valueOf);
	}

	/**
	 * Get value, String from the specified file
	 *
	 * @param file file
	 * @param key  key
	 * @return The value obtained from the key
	 */
	public static Optional getString(File file, String key) {
		return get(file, key).map(String::valueOf);
	}

	/**
	 * Get the String type value from the default configuration file
	 *
	 * @param key key
	 * @return The value obtained from the key
	 */
	public static Optional getString(String key) {
		return get(PropEnum.DEFAULT_CONFIG_PROPERTIES.getProp(), key).map(String::valueOf);
	}

	/**
	 * Get the value of int type from the specified path
	 *
	 * @param filePath file path
	 * @param key      key
	 * @return The value obtained from the key
	 */
	public static Optional getInt(String filePath, String key) {
		return get(filePath, key).map(Integer::valueOf);
	}

	/**
	 * Get the value of type int from the specified file
	 *
	 * @param file File object
	 * @param key  key
	 * @return The value obtained from the key
	 */
	public static Optional getInt(File file, String key) {
		return get(file, key).map(Integer::valueOf);
	}

	/**
	 * Get the int type value from the default configuration file
	 *
	 * @param key key
	 * @return The value obtained from the key
	 */
	public static Optional getInt(String key) {
		return getInt(PropEnum.DEFAULT_CONFIG_PROPERTIES.getProp(), key);
	}

	/**
	 * Get value from the specified file path
	 *
	 * @param filePath file path
	 * @param key      key
	 * @return The value obtained from the key
	 */
	public static Optional getShort(String filePath, String key) {
		return get(filePath, key).map(Short::valueOf);
	}

	/**
	 * Get value from the specified file
	 *
	 * @param file File object
	 * @param key  key
	 * @return The value obtained from the key
	 */
	public static Optional getShort(File file, String key) {
		return get(file, key).map(Short::valueOf);
	}

	/**
	 * Get value from the default configuration file
	 *
	 * @param key key
	 * @return The value obtained from the key
	 */
	public static Optional getShort(String key) {
		return getShort(PropEnum.DEFAULT_CONFIG_PROPERTIES.getProp(), key);
	}

	/**
	 * Get value from the specified file path
	 *
	 * @param filePath file path
	 * @param key      key
	 * @return The value obtained from the key
	 */
	public static Optional getLong(String filePath, String key) {
		return get(filePath, key).map(Long::valueOf);
	}

	/**
	 * Get value from the specified file
	 *
	 * @param file File object
	 * @param key  key
	 * @return The value obtained from the key
	 */
	public static Optional getLong(File file, String key) {
		return get(file, key).map(Long::valueOf);
	}

	/**
	 * Get value from the default configuration file
	 *
	 * @param key key
	 * @return The value obtained from the key
	 */
	public static Optional getLong(String key) {
		return getLong(PropEnum.DEFAULT_CONFIG_PROPERTIES.getProp(), key);
	}

	/**
	 * Get the float type value from the specified file path
	 *
	 * @param filePath file path
	 * @param key      key
	 * @return The value obtained from the key
	 */
	public static Optional getFloat(String filePath, String key) {
		return get(filePath, key).map(Float::valueOf);
	}

	/**
	 * Get the float type value from the specified file
	 *
	 * @param file File object
	 * @param key  key
	 * @return The value obtained from the key
	 */
	public static Optional getFloat(File file, String key) {
		return get(file, key).map(Float::valueOf);
	}

	/**
	 * Get the float type value from the default configuration file
	 *
	 * @param key key
	 * @return The value obtained from the key
	 */
	public static Optional getFloat(String key) {
		return getFloat(PropEnum.DEFAULT_CONFIG_PROPERTIES.getProp(), key);
	}

	/**
	 * Get the double-precision floating-point type value from the specified file path
	 *
	 * @param filePath file path
	 * @param key      key
	 * @return The value obtained from the key
	 */
	public static Optional getDouble(String filePath, String key) {
		return get(filePath, key).map(Double::valueOf);
	}

	/**
	 * Obtain the double-precision floating-point type value from the specified file;
	 *
	 * @param file File object
	 * @param key  key
	 * @return The value obtained from the key
	 */
	public static Optional getDouble(File file, String key) {
		return get(file, key).map(Double::valueOf);
	}

	/**
	 * Get the double-precision floating-point type value from the default configuration file
	 *
	 * @param key key
	 * @return The value obtained from the key
	 */
	public static Optional getDouble(String key) {
		return getDouble(PropEnum.DEFAULT_CONFIG_PROPERTIES.getProp(), key);
	}

	/**
	 * Get the boolean value from the specified file path
	 *
	 * @param filePath file path
	 * @param key      key
	 * @return The value obtained from the key
	 */
	public static Optional getBoolean(String filePath, String key) {
		return get(filePath, key).map(Boolean::valueOf);
	}

	/**
	 * Get the boolean value from the specified configuration file
	 *
	 * @param file File object
	 * @param key  key
	 * @return The value obtained from the key
	 */
	public static Optional getBoolean(File file, String key) {
		return get(file, key).map(Boolean::valueOf);
	}

	/**
	 * Get the boolean value from the default configuration file
	 *
	 * @param key key
	 * @return The value obtained from the key
	 */
	public static Optional getBoolean(String key) {
		return getBoolean(PropEnum.DEFAULT_CONFIG_PROPERTIES.getProp(), key);
	}

	/**
	 * Get properties, if there is direct fetch in the cache,
	 * if it does not exist in the cache, load from the file
	 *
	 * @param fileOrPath file path
	 * @return Properties Object
	 */
	public static Properties getProperties(Object fileOrPath) {
		File       proFile    = getPropFile(fileOrPath);
		Properties properties = null;
		if (Objects.nonNull(proFile)) {
			properties = PROPERTIES_CACHE_MAP.get(proFile);
		}
		if (isNullOrEmpty(properties) && FileKit.isNotNullOrEmpty(proFile)) {
			properties = loadProperties(proFile);
			String message = "[ " + proFile.getAbsolutePath() + " ]";
			Logger.infof("Load properties from {}", ColorKit.toBuleLessBold(message));
		}
		return properties;
	}

	/**
	 * Determine if it is empty
	 *
	 * @param properties Properties
	 * @return true-empty | false-not empty
	 */
	public static boolean isNullOrEmpty(Properties properties) {
		return properties == null || properties.isEmpty();
	}

	/**
	 * Determine if it is not empty
	 *
	 * @param properties Properties
	 * @return true-not empty | false-empty
	 */
	public static boolean isNotNullOrEmpty(Properties properties) {
		return !isNullOrEmpty(properties);
	}

	/**
	 * Get the value corresponding to the key from the specified file or path
	 *
	 * @param file File object
	 * @param key  key
	 * @return The value obtained from the key
	 */
	private static Optional get(Object file, String key) {
		Properties properties = getProperties(file);
		return isNullOrEmpty(properties) || StringKit.isNullOrEmpty(key) ? Optional.empty() : Optional.ofNullable(properties.getProperty(key));
	}

	/**
	 * Get the configuration property from the file or path, or return the default value if it does not exist
	 *
	 * @param fileOrPath   File or path
	 * @param key          key
	 * @param defaultValue Defaults
	 * @return The value obtained from the key
	 */
	private static Object getOrDefault(Object fileOrPath, String key, Object defaultValue) {
		if (StringKit.isNullOrEmpty(key)) {
			return defaultValue;
		}
		Properties properties = getProperties(fileOrPath);
		return isNullOrEmpty(properties) ? defaultValue : properties.getOrDefault(key, defaultValue);
	}

	/**
	 * Load Properties file
	 *
	 * @param proFile file
	 * @return Properties
	 */
	protected static Properties loadProperties(File proFile) {
		byte[]     bytes      = FileKit.loadFile(proFile);
		Properties properties = null;
		if (null != bytes) {
			try {
				String content = new String(bytes);
				if (StringKit.isNotNullOrEmpty(content)) {
					properties = new Properties();
					properties.load(new StringReader(content));
					put(proFile, properties);
				}
			} catch (Exception ex) {
				Logger.errorf("Load Properties error", ex);
			}
		}
		return properties;
	}

	/**
	 * Get file
	 *
	 * @param file File or path
	 * @return File object
	 */
	private static File getPropFile(Object file) {
		Map map = getFile(file);
		File resourceFile = null;
		String filePath = map.get("filePath").toString();
		try {
			if (map.get("isExists").equals(false)) {
				put(new File(filePath), new Properties());
			} else {
				resourceFile = (File) map.get("file");
			}
		} catch (Exception ex) {
			put(new File(filePath), new Properties());
			String message = "[ " + filePath + " ]";
			Logger.warn(ColorKit.toRedBold(message) + " file is not found.");
			resourceFile = new File(filePath);
		}
		return resourceFile;
	}

	private static Map getFile(Object file) {
		String filePath = "";
		if (file == null) {
			filePath = PropEnum.DEFAULT_CONFIG_PROPERTIES.getProp();
		} else if (file instanceof File && ((File) file).exists()) {
			return fileMap(true, ((File) file).getAbsolutePath(), (File) file);
		} else if (file instanceof String) {
			filePath = file.toString();
		}
		filePath = complexEnd(filePath);
		if (MapKit.isNotNullOrEmpty(PROPERTIES_CACHE_MAP)) {
//			Enumeration keys = PROPERTIES_CACHE_MAP.keys();
//			while (keys.hasMoreElements()) {
//				File existsFile = keys.nextElement();
//				if (filePath.equals(existsFile.getName())) {
//					return existsFile;
//				}
//			}
			for (Map.Entry entry : PROPERTIES_CACHE_MAP.entrySet()) {
				File memFile = entry.getKey();
				if (filePath.equals(memFile.getName()) && memFile.exists()) {
					return fileMap(true, memFile.getAbsolutePath(), memFile);
				}
			}
		}
		File resourceFile = FileKit.getResourceFile(filePath);
		if (Objects.isNull(resourceFile) || !resourceFile.exists()) {
			return fileMap(false, filePath, null);
		} else {
			return fileMap(true, resourceFile.getAbsolutePath(), resourceFile);
		}
	}

	private static Map fileMap(boolean isExists, String filePath, File file) {
		Map fileMap = new HashMap<>();
		fileMap.put("isExists", isExists);
		fileMap.put("file", file);
		fileMap.put("filePath", filePath);
		return fileMap;
	}

	/**
	 * Improve profile extension
	 *
	 * @param filePath file path
	 * @return Full file name
	 */
	private static String complexEnd(String filePath) {
		if (StringKit.isNotNullOrEmpty(filePath) && !filePath.endsWith(".properties")) {
			filePath += ".properties";
		}
		return filePath;
	}

	public static boolean isExist(Object prop) {
		return (boolean) getFile(prop).get("isExists");
	}

	static {
		/*
		 * Initialize, periodically check whether the configuration file is modified.
		 */
		PROPERTIES_CACHE_MAP = new ConcurrentHashMap<>();
//        new Timer(TIMER_TASK_NAME).schedule(new TimerTask() {
//            @Override
//            public void run() {
//                if (MapKit.isNotNullOrEmpty(PROPERTIES_CACHE_MAP)) {
//                    PROPERTIES_CACHE_MAP.forEach((file, properties) -> {
//                        if(file.exists() && properties.containsKey(LAST_MODIFY_TIME)) {
//                            String lastTimeStamp = String.valueOf(file.lastModified());
//                            String cachedTimeStamp = properties.getProperty(LAST_MODIFY_TIME);
//                            if (!lastTimeStamp.equals(cachedTimeStamp)) {
//                                PROPERTIES_CACHE_MAP.remove(file);
//                                loadProperties(file);
//                                Logger.infof("The {} properties has been changed!", ColorKit.toYellowBold("[ " + file.getAbsolutePath() + " ]"));
//                                AutoConfigurationHandler.configValue(file.getName());
//                            }
//                        }
//                    });
//                }
//            }
//        }, 0, 5 * 1000);
		TimedTask.addTask(TIMER_TASK_NAME, prop -> {
			if (MapKit.isNotNullOrEmpty(PROPERTIES_CACHE_MAP)) {
				PROPERTIES_CACHE_MAP.forEach((file, properties) -> {
					if (file.exists() && properties.containsKey(LAST_MODIFY_TIME)) {
						String lastTimeStamp   = String.valueOf(file.lastModified());
						String cachedTimeStamp = properties.getProperty(LAST_MODIFY_TIME);
						if (!lastTimeStamp.equals(cachedTimeStamp)) {
							PROPERTIES_CACHE_MAP.remove(file);
							loadProperties(file);
							Logger.infof("The {} properties has been changed!", ColorKit.toYellowBold("[ " + file.getAbsolutePath() + " ]"));
							AutoConfigurationHandler.configValue(file.getName());
						}
					}
				});
			}
		}, 5 * 1000);
	}

}