`

构建自己的Properties

阅读更多
开发到了一定时间,就会有很多的定式思维阻碍我们。 比如问到XML配置和Properties配置有什么区别,往往想都不想就回答: 更改Properties需要重新启动Server,而更改XML Schema则不需要重新启动Server就会生效。
         虽然上面的说法是错误的 ,但是还是值得我们去思考为什么。
         另一方面,我们要构建7*24小时运转的System,不能随意关闭 /启动 APP Server. 但是又要保证让我们更改系统配置,我们的Application又能立即感知。基于这方面的需求。 我们做了PropertyFileCache。 基本原理是模仿Properties,每次访问getProperty时,去检查文件修改的时间戳。如果时间为后来的,则代表文件已经修改,则需要作类似 Properties.load()的动作.
然后重新取得新的修改后的值。否则,采用Cache中的值。
           下面是直接的代码.

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;

//import org.apache.log4j.Logger;


//
// PropertyFileCache()	:	
//			This class reads a property file [full file name specified], then, put each line
//			with a key and value property to check.  For example, if the file contains IP address...
//
//				127.0.0.1=ok
//				202.3.8.4=blah
//
//			The above example will put the above IP addresses as key, and "ok" and "blah" as its
//			associated values in a cached array.  If the property file has changed, it will read it again.
//			If the property file has not changed, then, upon a request, it will simply
//			pull elements from cache/memory to check.
//
//			This class also allows multiple instance of file to be cached.
//
public class PropertyFileCache {
	private	static	final	String	ENCODING 				= "UTF-8";		// encoding type...
	private	static	final	String	PARAMETER_DELIMITER 	= "=";			// equate character for key and value
	private	static	final	String	COMMENT_DELIMITER 		= "#";			//comment delimiter inside the property file
	private static	final	int		PRINT_ELEMENT_LIMIT 	= 50;			// if more than X, do not print all read elements...
	
//	private	static	Logger	sysLog = Logger.getLogger(PropertyFileCache.class);
	
	protected static	PropertyFileCache	instance;					// singelton instance
	
	protected			Hashtable<String, FileInfo> spfCache;			// this stores different property file...
	
	protected PropertyFileCache() {
		spfCache = new Hashtable<String, FileInfo>();
		
	}
	
	//
	// getInstance():	get this instance - a singleton
	//
	public static PropertyFileCache getInstance() {
		if ( instance == null ) {
			instance = new PropertyFileCache();
		}
		return instance;
	}
	
	//
	// getAllKeys()	: return an enumeration of all keys
	//
	public Enumeration <String> getAllKeys(String filename) {
		Enumeration<String>  allkeys = null;
		FileInfo curFileInfo = getFileInfo(filename);
		if ( curFileInfo != null ) {
			checkConf(curFileInfo);
			allkeys = curFileInfo.cachedTokens.keys();
		}
		return allkeys;
	}
	
	//
	// keySet()	:	return a set for all keys available
	//
	public Set<String> keySet(String filename) {
		Set<String> keyset = new HashSet<String>();		// JP, Aug 16, 2006, initialize it to an empty Set
		FileInfo curFileInfo = getFileInfo(filename);
		if ( curFileInfo != null ) {
			checkConf(curFileInfo);
			keyset = curFileInfo.cachedTokens.keySet();
		}
		return keyset;
	}
	
	
	//
	// getProperty():	get the who hash table for internal processing
	//
	private Hashtable<String,String> getProperty(String filename) {
		Hashtable<String, String>  retTable = null;
		FileInfo curFileInfo = getFileInfo(filename);
		if ( curFileInfo != null ) {
			checkConf(curFileInfo);
			retTable = curFileInfo.cachedTokens;
		} else {
			retTable = new Hashtable<String, String>();		// return empty vector
		}
		return retTable;
	}
	//
	// get():	get the value based on the key and filename
	//
	public String get(String filename, String key) {
		String retStr = null;
		FileInfo curFileInfo = getFileInfo(filename);
		if ( curFileInfo != null ) {
			checkConf(curFileInfo);
			retStr = curFileInfo.cachedTokens.get(key);
		}
		return retStr;
	}
	
	//
	// getTimestamp()	:	return the timestamp of this file.  this is used by external routine to explicit triggers a re-read
	//
	public long getTimestamp(String filename) {
		long ts = 0;
		FileInfo curFileInfo = getFileInfo(filename);
		if ( curFileInfo != null) {
			checkConf(curFileInfo);
			ts = curFileInfo.timestamp;
		}
		return ts;
	}
	
	//
	// contains()	:	check if the item contains the token
	//
	public boolean contains(String filename, String token) {
		boolean status = false;
		FileInfo curFileInfo = getFileInfo(filename);
		if ( curFileInfo != null ) {
			checkConf(curFileInfo);
			status = contains(curFileInfo.cachedTokens, token);
		}
		return status;
	}

	//
	// containsKey()	:	check if key is there!
	//
	public boolean containsKey(String filename, String token) {
		boolean status = false;
		FileInfo curFileInfo = getFileInfo(filename);
		if ( curFileInfo != null ) {
			checkConf(curFileInfo);
			status = containsKey(curFileInfo.cachedTokens, token);
		}
		return status;
	}
	
	//
	// getSize()	:	return the number of items in the file
	//
	public int getSize(String filename) {
		int size = 0;
		FileInfo curFileInfo = getFileInfo(filename);
		if ( curFileInfo != null ) {
			checkConf(curFileInfo);
			size = curFileInfo.cachedTokens.size();
		}
		return size;
	}
	
	//
	// isUpdated()	:	check to see if time stamp has been updated.  This check DOES NOT trigger a re-read!
	//
	public boolean isUpdated(String filename) {
		boolean status = false;
		if ( filename != null && filename.length() > 0 ) {
			FileInfo curFileInfo = getFileInfo(filename);
			if ( curFileInfo != null ) {
				long newTimestamp = (new File(curFileInfo.filename)).lastModified();	
				// NOTE: if file does not exist, lastModified() will return '0', let 
				// it proceed to create a zero element HashTable
				if ( newTimestamp > curFileInfo.timestamp || newTimestamp == 0) {
					status = true;	// file has been updated!
				} // else - no need to do anything!
			} else {
				status = true;		// signal update is required because file has not been read before!
			}
		} // else - if file name is invalid, there is nothing to be 'updated', return false...
		return status;
	}
	
	private FileInfo getFileInfo(String filename) {
		FileInfo curFileInfo = null;
		if ( filename != null && filename.length() > 0 ) {
			try {
				if ( spfCache.containsKey(filename)) {
					curFileInfo = spfCache.get(filename);
				} else {
					curFileInfo = new FileInfo(filename);
					spfCache.put(filename, curFileInfo);
				}
				curFileInfo = spfCache.get(filename);
			} catch (Exception e) {
//				sysLog.error("Cannot read simple property file [" + filename + "]");
			}
		}
		return curFileInfo;
	}
	
	private String get(Hashtable<String, String> vs, String key) {
		return vs.get(key);
	}

	private boolean contains(Hashtable<String,String> vs, String token) {
		return vs.contains(token);
	}

	private boolean containsKey(Hashtable<String, String> vs, String key) {
		return vs.containsKey(key);
	}
	
	private void checkConf(FileInfo fi) {
		String curDir = null;
		try {
			curDir = (new File(".")).getAbsolutePath();
			// if 'fp' is null, meaning cannot read a file, will throw exception
			long newTimestamp = (new File(fi.filename)).lastModified();	

			// NOTE: if file does not exist, lastModified() will return '0', let 
			// it proceed to create a zero element HashTable
			if ( newTimestamp > fi.timestamp || newTimestamp == 0) {
				parseProperties(fi);	// similar to Properties.load(), but have to deal with UTF-8!
				fi.timestamp = newTimestamp;
//				sysLog.info("Parsed from DIR[" + curDir + "] + FILE["+ fi.filename + "] with timestamp =[" + fi.timestamp +"]");
			}
		} catch (Exception e) { 
//			sysLog.fatal("Cannot read file [" + fi.filename + "] from directory [" + curDir + "]..." + e.getMessage());
		}
	}

	private void parseProperties(FileInfo fi) {
		if (fi.filename != null && fi.filename.length() > 0 ) {
			Hashtable <String, String> tokensFromFile = new Hashtable<String, String>();
			
			try {
				BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fi.filename), ENCODING));
				String tmpValue;	// extracted value
				String tmpKey;		// extracted key
				String tmpLine;		// the line

				while ( (tmpLine = br.readLine()) != null ) {
					//
					// strip off any comment and trim it down
					//
					tmpLine = tmpLine.replaceAll(COMMENT_DELIMITER + ".*$", "").trim();
					int sindex = tmpLine.indexOf(PARAMETER_DELIMITER);

					if ( sindex != -1 ) {
						tmpKey = tmpLine.substring(0, sindex).trim();
						tmpValue = tmpLine.substring(sindex + 1).trim();

						if ( /* tmpValue.length() > 0 && */ tmpKey.length() > 0 ) {			// allow empty string to be a variable...
							tokensFromFile.put(tmpKey, tmpValue);
						} // else ignore the parameters
					} // else ignore the parameters
				}
				br.close();
//				sysLog.info("Property file [" + fi.filename + "] loaded with " + tokensFromFile.size() + " element(s).");
			} catch (Exception e) {
//				sysLog.error("Property file [" + fi.filename + "] cannot be loaded " + e.getMessage());
			} finally {
				if (tokensFromFile != null ) {
					if ( fi.cachedTokens != null ) {
						fi.cachedTokens.clear();		// remove old Vector
					}
					fi.cachedTokens = tokensFromFile;		// use new table
					if ( fi.cachedTokens.size() < PRINT_ELEMENT_LIMIT ) {		// if there are too many, do not print...
//						sysLog.debug("Property file containing elements...\n" + fi.cachedTokens.toString());
					} // else - don't bother printing...
				}
			}
		} else {
//			sysLog.error("Property file [" + fi.filename + "] is not defined");
		}
	}
	
	
	//
	// CAUTION:  Do not try to combine this with SimplePropertyFileCache.java.  THEY ARE DIFFERENT!!!
	//
	final class FileInfo {
		private FileInfo(String fn) {
			filename = fn;
			// no need to initialize timestamp and cachedTokens, it will be determined later
		}
		private String	filename;
		private long	timestamp;
		private Hashtable<String, String>	cachedTokens;
	}
}


使用的例子:
String value = PropertyFileCache.getInstance().get("./bin/servletmap/instr2url.properties", "key");


需要注意的是,它并不能取代java.util.Propertieshttp://gceclub.sun.com.cn/Java_Docs/html/zh_CN/api/java/util/Properties.html,很多的特殊字符转义暂时还没有考虑

1
1
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics