package org.dayatang.configuration.impl; import org.apache.commons.lang3.StringUtils; import org.dayatang.configuration.ConfigurationException; import org.dayatang.configuration.WritableConfiguration; import org.dayatang.utils.Slf4JLogger; import java.io.*; import java.util.Date; import java.util.Hashtable; import java.util.Map; import java.util.Properties; /** * <P>ConfigurationFileImpl为读取/回写配置文件的工具类,一个实例大概对应了一个物理配置文件,可以使用 * getXxx("aa.conf"),getXxx("/a/b","xx.conf") 获得/a/b/xx.conf配置文件。 具体配置大致采用 * ConfigurationFileImpl.getXxx(key)的方式读取。</P> * <P>每个配置项用key --> value 的方式组织,推荐采用点分字符串的方式编制key部分。 usePrefix()激活 * 配置项前缀功能,你可以通过usePrefix("xxx.xxx")设置某个具体实例的前缀。</P> * <P>前缀的作用在于减少复杂性,如果我们在配置文件里有org.dayatang.smbserverhost=smbserverhost.com * 这一项,并且不更改默认前缀的话,getXxx("smbserverhost")和get("org.dayatang.smbserverhost") * 将会返回同样的结果。</P> * <P>配置文件的格式符合标准的java属性文件格式,采用UTF8的编码方式,支持中文,不需native2ascii。</P> * <P>注意:为了避免日期格式的转换等复杂问题,日期是转化为long类型的数据保存的(采用date.getTime()方法)。</P> * * @author yyang (<a href="mailto:gdyangyu@gmail.com">gdyangyu@gmail.com</a>) */ public class ConfigurationFileImpl extends AbstractConfiguration implements WritableConfiguration { private static final Slf4JLogger LOGGER = Slf4JLogger.getLogger(ConfigurationFileImpl.class); private PropertiesFileUtils pfu = new PropertiesFileUtils("utf-8"); private File file; public static ConfigurationFileImpl fromFile(final File file) { return new ConfigurationFileImpl(file); } public ConfigurationFileImpl(final String pathname) { this(new File(pathname)); } public ConfigurationFileImpl(final String dirPath, final String fileName) { this(new File(dirPath, fileName)); } public ConfigurationFileImpl(final File file) { if (file == null) { throw new ConfigurationException("File " + file.getName() + " is null!"); } if (!file.exists()) { throw new ConfigurationException("File " + file.getName() + " not found!"); } if (!file.canRead()) { throw new ConfigurationException("File " + file.getName() + " is unreadable!"); } this.file = file; load(); } @Override public void load() { hTable = new Hashtable<String, String>(); Properties props = new Properties(); InputStream in = null; try { in = new FileInputStream(file); props.load(in); hTable = pfu.rectifyProperties(props); LOGGER.debug("Load configuration from {} at {}", file.getAbsolutePath(), new Date()); } catch (IOException e) { throw new ConfigurationException("Cannot load config file: " + file, e); } finally { if (in != null) { try { in.close(); } catch (IOException e) { throw new ConfigurationException("Cannot close input stream.", e); } } } } /* (non-Javadoc) * @see org.dayatang.configuration.WritableConfiguration#save() */ @Override public void save() { BufferedWriter out = null; try { out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), PropertiesFileUtils.ISO_8859_1)); store(getProperties(), out, "Config file for " + file); } catch (Exception e) { throw new ConfigurationException(e); } finally { if (out != null) { try { out.close(); } catch (IOException e) { throw new ConfigurationException("Cannot close input stream.", e); } } } } private void store(Properties props, BufferedWriter out, String comments) throws IOException { if (StringUtils.isNotEmpty(comments)) { out.append("#" + comments); out.newLine(); } out.write("#" + new Date().toString()); out.newLine(); synchronized (this) { for (Map.Entry<Object, Object> each : props.entrySet()) { String key = convertString((String) each.getKey(), true); String value = convertString((String) each.getValue(), false); out.write(key + "=" + value); out.newLine(); } out.flush(); } LOGGER.debug("Save configuration to {} at {}", file.getAbsolutePath(), new Date()); } private String convertString(String theString, boolean escapeSpace) { int len = theString.length(); int bufLen = len * 2; if (bufLen < 0) { bufLen = Integer.MAX_VALUE; } StringBuilder outBuffer = new StringBuilder(bufLen); for (int x = 0; x < len; x++) { char aChar = theString.charAt(x); // Handle common case first, selecting largest block that // avoids the specials below if ((aChar > 61) && (aChar < 127)) { if (aChar == '\\') { outBuffer.append('\\'); outBuffer.append('\\'); continue; } outBuffer.append(aChar); continue; } switch (aChar) { case ' ': if (x == 0 || escapeSpace) { outBuffer.append('\\'); } outBuffer.append(' '); break; case '\t': outBuffer.append('\\'); outBuffer.append('t'); break; case '\n': outBuffer.append('\\'); outBuffer.append('n'); break; case '\r': outBuffer.append('\\'); outBuffer.append('r'); break; case '\f': outBuffer.append('\\'); outBuffer.append('f'); break; case '=': // Fall through case ':': // Fall through case '#': // Fall through case '!': outBuffer.append('\\'); outBuffer.append(aChar); break; default: outBuffer.append(aChar); } } return outBuffer.toString(); } @Override public String toString() { return getClass().getSimpleName() + "{" + file + "}"; } }