动态配置文件

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 [email protected]
 * @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());
		
	}
}

 

你可能感兴趣的:(thread)