`

构建自己的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
分享到:
评论

相关推荐

    properties

    6. **建立JDBC连接** 加载配置后,我们可以使用这些信息创建JDBC连接: ```java Class.forName(driver); // 注册JDBC驱动 Connection conn = DriverManager.getConnection(url, username, password); ``` 7. ...

    properties插件

    7. 打包集成:在构建过程中,properties插件可以将配置文件打包到最终的应用程序中,确保部署时包含所有必要的配置。 8. 运行时热更新:某些插件允许在应用程序运行时动态更新配置,无需重启服务,这对于快速调试和...

    设置Eclipse中properties文件打开方式myeclipse一样有source和properties两个视图方法

    - 在弹出的窗口中,您可以查看到当前Eclipse的版本信息,包括平台版本和构建日期等。 #### 2. 下载与Eclipse版本匹配的JBossTools插件 接下来,访问JBossTools官方网站(http://tools.jboss.org/downloads),找到...

    Properties读取资源文件经典应用

    在Java编程中,Properties类是用于处理属性列表的,这些属性...通过正确地处理资源加载、异常处理和数据安全,我们可以构建更健壮、安全的应用程序。在实际编程中,务必遵循最佳实践,以提高代码质量并降低潜在风险。

    Properties Editor

    在Java Web开发中,Struts框架扮演着重要的角色,它提供了一种强大的MVC(Model-View-Controller)设计模式实现方式,帮助开发者构建可维护、可扩展的Web应用程序。在Struts应用中,配置文件是不可或缺的一部分,...

    eclipse properties editor插件

    - **项目设置**:在项目属性中,你可以使用Properties Editor来快速调整构建路径、源代码管理设置等。 - **资源管理**:对于文件或文件夹的属性,如编码格式、读写权限等,这个插件提供了更友好的编辑方式。 - **...

    jquery-i18n-properties-1.0.9.js

    《jQuery i18n Properties 1.0.9:实现多语言支持的关键库》 在Web开发中,为用户提供多语言支持是至关重要的,尤其是在全球化的互联网环境...通过合理利用这个库,开发者可以构建出更加友好、适应不同地区的Web应用。

    eclipse myeclipse 插件 properties 编辑器

    它支持多语言环境,允许开发者创建、修改和组织不同语言版本的资源文件,这对于构建国际化应用非常有用。该插件还具有语法高亮、自动完成和错误检测等功能,帮助开发者避免常见的语法错误。 另一个插件是...

    Properties插件文件

    - **同步更新**:当修改了Properties文件后,记得刷新项目或重新构建,确保代码能够获取到最新的配置信息。 在压缩包文件“eclipse”中,可能包含了与MyEclipse相关的Properties文件插件安装包或配置文件。安装或...

    使用Ant打包 来调用properties 文件

    在IT行业中,构建自动化是开发流程...通过调用properties文件,我们可以实现配置的灵活管理和共享,使得构建过程更加适应各种环境和需求。无论是简单的Java项目还是复杂的系统,Ant都能提供有效的支持,提高开发效率。

    properties读取工具类设计

    通过以上设计,我们构建了一个功能丰富的`PropertiesUtil`工具类,它不仅能轻松读取单个或多个`properties`文件,还能与Spring框架良好集成,方便我们在各种场景下管理和使用配置信息。这样的工具类大大提高了代码的...

    eclipse properties editor plugin

    1. **分类视图**:Properties Editor Plugin将项目属性按照类别进行划分,如Java、资源、构建等,这样用户可以根据需要快速定位到特定的属性设置。 2. **多选与批量编辑**:允许用户选择多个属性并同时进行修改,...

    spring读取properties

    Spring框架提供了强大且灵活的机制来处理Properties文件的读取和注入,这对于构建可配置性强、易于维护的应用程序至关重要。通过上述步骤,开发者能够轻松地管理和利用外部配置信息,使应用更加适应多变的运行环境。...

    eclipse中Properties Editor插件

    这些属性可能包括但不限于编译设置、构建路径、源代码编码、运行时环境等。通过这个插件,你可以更有效地管理和定制你的开发环境,以适应特定项目的需求。 首先,我们来了解一下Properties Editor的基本操作。在...

    国际化插件--读取 .properties的中文文件

    4. 编译与打包:在构建项目时,确保所有.properties文件被正确地包含在最终的JAR或WAR文件中。 在实际开发过程中,Eclipse的国际化插件还可以帮助进行以下操作: - 比较不同语言版本的.properties文件,查看翻译的...

    jquery.i18n.properties.zip

    《HTML与JSP前端页面的国际化实践:jQuery.i18n.properties详解》 在Web开发领域,为了满足全球用户的...在实际项目中,结合其他前端框架如Angular或React,以及后台服务的支持,可以构建出更为强大的国际化解决方案。

    PropertiesEditor

    - 在自动化构建和部署流程中,PropertiesEditor可以作为工具链的一部分,处理配置文件的生成和更新,确保配置信息的正确性。 总之,PropertiesEditor插件是软件开发过程中不可或缺的工具之一,尤其对于处理和管理...

    gradle.properties

    《优化Android Studio构建速度:详解gradle.properties配置》 在Android开发过程中,Gradle作为主要的构建工具,其性能直接影响到项目的开发效率。当遇到Android Studio构建项目速度过慢的问题时,开发者通常会寻求...

    ResourceBundle与Properties读取maven中resources目录下的资源文件

    ### ResourceBundle与Properties读取Maven中Resources目录下的资源文件 #### 一、概述 在Java开发过程中,我们经常需要读取资源文件中的配置信息。在Maven项目中,`src/main/resources`目录下通常存放了各种配置...

Global site tag (gtag.js) - Google Analytics