package com.umpay.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
/** 动态配置文件
* @author liwei2@umpay.com
* @since 2007-12-16
* 用JProfiler观察可以看出只使用了一个监视线程
* String fn1 = "config/test111.properties";
* String fn2 = "config/test222.properties";
* DynamicProperties dp1 = new DynamicProperties(fn1,0,20);//如果第三参数20,修改成0,则成为静态配置文件.
* DynamicProperties dp2 = new DynamicProperties(fn2,0,20);//如果第三参数20,修改成0,则成为静态配置文件.
* while(true) {
* System.err.println("#1="+dp1.getProperty("key"));
* System.err.println("#2="+dp2.getProperty("key"));
* Thread.sleep(1000*5);
* }
*
* */
public final class DynamicProperties {
/** 动态配置文件磁盘数据*/
protected File file;
/** 动态配置文件内存数据( {@link DynamicProperties} 的目的就是保持从磁盘数据到内存数据的单向一致性,
* 暂不支持从内存到磁盘的数据同步,因此{@link DynamicProperties}不提供set方法,只提供get方法.*/
protected Properties property;
/** 动态检测相关参数:第一次检测前的延迟时间,单位ms*/
protected long delay;
/** 动态检测相关参数:检测周期,单位ms*/
protected long period;
/** 动态检测相关参数:{@link FileMonitor} 最后检测时间,单位ms*/
protected long lastMonitorTime;
public String getFileName() {
return file.getName();
}
long getLastModified() {
return this.file.lastModified();
}
public long getDelay() {
return delay;
}
public long getPeriod() {
return period;
}
long getLastMonitorTime() {
return this.lastMonitorTime;
}
void setLastMonitorTime(long lastMonitorTime) {
this.lastMonitorTime = lastMonitorTime;
}
/**
* 所有 {@link DynamicProperties} 实例共享一个 {@link FileMonitor}
* */
private static FileMonitor monitor = null;
private synchronized static void initFileMonitor() {
if(monitor == null) {
monitor = new FileMonitor();
}
}
/**
* @param file 属性文件
* @param delay 从<code>DynamicProperties</code>被创建到第一次动态监视的时间间隔. 约束范围delay > 0
* @param period 动态监视的时间间隔. 约束范围period >= 0;等于0表示不执行动态监视,退化为静态配置文件.
*
* */
public DynamicProperties(File file,long delay,long period)
throws IOException
{
if(delay < 0 || period < 0) {
throw new IllegalArgumentException("参数delay和period都必须大于等于0");
}
this.file = file;
this.delay = delay;
this.period = period;
this.property = new Properties();
this.initAndLoad();//初始构造时,执行第一次加载.
}
public DynamicProperties(String fileName,long delay,long period)
throws IOException
{
this(new File(fileName),delay,period);
}
public DynamicProperties(File file,Date firstTime,long period)
throws IOException
{
this(file,firstTime.getTime()-System.currentTimeMillis(),period);
}
public DynamicProperties(String fileName,Date firstTime,long period)
throws IOException
{
this(new File(fileName),firstTime.getTime()-System.currentTimeMillis(),period);
}
public DynamicProperties(File file,long period)
throws IOException
{
this(file,0,period);
}
public DynamicProperties(String fileName,long period)
throws IOException
{
this(new File(fileName),period);
}
private void initAndLoad() throws IOException {
this.lastMonitorTime = System.currentTimeMillis();
update();//首次将配置信息从文件加载到内存
if(period > 0) {//如果period=0,则表示静态配置文件,不需要进行动态更新的监视
initFileMonitor();
monitor.addDetected(this);//启动FileMonitor,以监测磁盘文件内容的变化,并在变化时,由监视线程回调update()方法,进行重新加载
}
}
/**
* {@link FileMonitor} 线程回调 {@link #update()} 方法,一定会在{@link #initAndLoad()}之后,
* 所以尽管 {@link #update()}方法会被两个线程执行,一个是构造 {@link DynamicProperties}实例所在的线程,
* 另一个是 {@link FileMonitor}线程,但是它们是顺序执行的,实例构造完成后,只有 {@link FileMonitor}
* 线程执行 {@link #update()}方法。因此 {@link #update()}不同担心线程安全的问题.
* */
void update() throws IOException {
InputStream in = new FileInputStream(file);
this.property.load(in);
}
public String getProperty(String key, String defaultValue) {
String val = this.property.getProperty(key);
return val == null ? defaultValue : val.trim();
}
public String getProperty(String key) {
String val = this.property.getProperty(key);
return val == null ? null : val.trim();
}
public boolean getBoolean(String key) {
String val = this.getProperty(key);
return Boolean.parseBoolean(val);
}
public boolean getBoolean(String key,boolean defaultValue) {
String val = this.getProperty(key);
return val == null ? defaultValue : Boolean.parseBoolean(val);
}
public int getInt(String key) {
String val = this.getProperty(key);
return Integer.parseInt(val);
}
public int getInt(String key,int defaultValue) {
String val = this.getProperty(key);
return val == null ? defaultValue : Integer.parseInt(val);
}
public double getDouble(String key) {
String val = this.getProperty(key);
return Double.parseDouble(val);
}
public double getDouble(String key,double defaultValue) {
String val = this.getProperty(key);
return val == null ? defaultValue : Double.parseDouble(val);
}
/* 为了方便使用,避免对象实例在各个类中传递,提供些静态方法*/
public static final void initInstance(String instanceName,DynamicProperties instance) {
if(instanceName == null || instance == null) {
throw new IllegalArgumentException("参数不能为空");
}
synchronized (context) {
if(context.containsKey(instanceName)) {
throw new IllegalStateException("名称为"+instanceName+"的实例已经存在");
}
context.put(instanceName, instance);
}
}
public static final DynamicProperties getInstance(String instanceName) {
synchronized (context) {
return (DynamicProperties)context.get(instanceName);
}
}
private static final String DEFAULT = DynamicProperties.class.getName()+"#DEFAULT";
public static final void initDefaultInstance(String fileName,long delay,long period)
{
try {
initInstance(DEFAULT, new DynamicProperties(fileName,delay,period));
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
public static final DynamicProperties getDefaultInstance() {
DynamicProperties dp = getInstance(DEFAULT);
if(dp == null) {
throw new IllegalStateException("默认实例尚未初始化,请用initDefaultInstance方法进行初始化");
}
return dp;
}
private static Map context = new HashMap();//Collections.synchronizedMap(new HashMap());
/** 小工具:将应用程序的命令行参数转化成 {@link Properties}*/
public static Properties parseArgs(String[] args) {
Properties config = new Properties();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter(baos);
for (int i = 0; i < args.length; i++) {
writer.println(args[i]);
}
writer.flush();
try {
config.load(new ByteArrayInputStream(baos.toByteArray()));
} catch (IOException e) {
throw new IllegalStateException(e);
}
return config;
}
public static void main(String[] args) throws Exception {
//用JProfiler观察可以看出只使用了一个监视线程
String fn1 = "config/test111.properties";
String fn2 = "config/test222.properties";
DynamicProperties dp1 = new DynamicProperties(fn1,0,20);//如果第三参数20,修改成0,则成为静态配置文件.
DynamicProperties dp2 = new DynamicProperties(fn2,0,20);//如果第三参数20,修改成0,则成为静态配置文件.
DynamicProperties.initDefaultInstance("config/test.properties", 0, 20);
while(true) {
System.out.println("#1="+dp1.getProperty("key"));
System.out.println("#2="+dp2.getProperty("key"));
System.out.println("#Default="+DynamicProperties.getDefaultInstance().getProperty("key"));
Thread.sleep(1000*5);
}
}
}
class FileMonitor {
private Timer timer = new Timer("DynamicPropertiesTimer");
public synchronized void addDetected(final DynamicProperties detected) {
if(detected.getDelay() < 0) ;//if (delay < 0) delay = 0L;
if(detected.getPeriod() <= 0) return;
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
long t = detected.getLastModified();
if(t > detected.getLastMonitorTime()) {
detected.setLastMonitorTime(t);
try {
detected.update();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
}, detected.getDelay(), detected.getPeriod());
}
}
动态配置文件
动态配置文件管理
最新推荐文章于 2024-04-26 04:00:00 发布
本文介绍了一个用于Java环境的动态配置文件管理类DynamicProperties。该类能够实现配置文件的实时更新,而无需重启应用。通过定时任务监测配置文件的变化并自动加载最新的配置。
377

被折叠的 条评论
为什么被折叠?



