/*
 *  Copyright 2008 biaoping.yin
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.frameworkset.spi.support;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

import org.frameworkset.spi.BaseApplicationContext;
import org.frameworkset.spi.ResourceLoaderAware;
import org.frameworkset.util.Assert;
import org.frameworkset.util.PropertiesPersister;
import org.frameworkset.util.io.DefaultResourceLoader;
import org.frameworkset.util.io.Resource;
import org.frameworkset.util.io.ResourceEditor;
import org.frameworkset.util.io.ResourceLoader;

import com.frameworkset.util.DaemonThread;
import com.frameworkset.util.DefaultPropertiesPersister;
import com.frameworkset.util.ResourceInitial;
import com.frameworkset.util.SimpleStringUtil;

/**
 * <p>
 * Title: HotDeployResourceBundleMessageSource.java
 * </p>
 * <p>
 * Description:
 * </p>
 * <p>
 * bboss workgroup
 * </p>
 * <p>
 * Copyright (c) 2007
 * </p>
 * 
 * @Date 2012-5-6 下午8:47:20
 * @author biaoping.yin
 * @version 1.0
 */
public class HotDeployResourceBundleMessageSource extends AbstractMessageSource
		implements ResourceLoaderAware {

	private static final String PROPERTIES_SUFFIX = ".properties";

	private static final String XML_SUFFIX = ".xml";

	private String[] basenames = new String[0];

	private String defaultEncoding;

	private Properties fileEncodings;

	private boolean fallbackToSystemLocale = true;
	private ClassLoader bundleClassLoader;
	/**
	 * 是否需要对HotDeployResourceBundleMessageSource实例管理的资源文件启用热加载机制
	 * true 启用
	 * false 关闭
	 * 默认启用
	 */
	private boolean changemonitor = true;



	private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();

	private ResourceLoader resourceLoader = new HotResourceLoader();

	/** Cache to hold filename lists per Locale */
	private final Map cachedFilenames = new HashMap();

	/** Cache to hold already loaded properties per filename */
	private final Map cachedProperties = new HashMap();

	/** Cache to hold merged loaded properties per basename */
	private final Map cachedMergedProperties = new HashMap();
	private static final PropertiesHolder  NOTEXIST_propHolder = new PropertiesHolder();
	
	private static final PropertiesHolder  ERROR_propHolder = new PropertiesHolder();
	

	/**
	 * Set a single basename, following the basic ResourceBundle convention of
	 * not specifying file extension or language codes, but in contrast to
	 * {@link ResourceBundleMessageSource} referring to a Bboss resource
	 * location: e.g. "WEB-INF/messages" for "WEB-INF/messages.properties",
	 * "WEB-INF/messages_en.properties", etc.
	 * <p>
	 * As of Bboss 1.2.2, XML properties files are also supported: e.g.
	 * "WEB-INF/messages" will find and load "WEB-INF/messages.xml",
	 * "WEB-INF/messages_en.xml", etc as well. Note that this will only work on
	 * JDK 1.5+.
	 * 
	 * @param basename
	 *            the single basename
	 * @see #setBasenames
	 * @see ResourceEditor
	 * @see java.util.ResourceBundle
	 */
	public void setBasename(String basename) {
		String[] basenames = basename.split(",");
		setBasenames(basenames);
	}

	/**
	 * Set an array of basenames, each following the basic ResourceBundle
	 * convention of not specifying file extension or language codes, but in
	 * contrast to {@link ResourceBundleMessageSource} referring to a Bboss
	 * resource location: e.g. "WEB-INF/messages" for
	 * "WEB-INF/messages.properties", "WEB-INF/messages_en.properties", etc.
	 * <p>
	 * As of Bboss 1.2.2, XML properties files are also supported: e.g.
	 * "WEB-INF/messages" will find and load "WEB-INF/messages.xml",
	 * "WEB-INF/messages_en.xml", etc as well. Note that this will only work on
	 * JDK 1.5+.
	 * <p>
	 * The associated resource bundles will be checked sequentially when
	 * resolving a message code. Note that message definitions in a
	 * <i>previous</i> resource bundle will override ones in a later bundle, due
	 * to the sequential lookup.
	 * 
	 * @param basenames
	 *            an array of basenames
	 * @see #setBasename
	 * @see java.util.ResourceBundle
	 */
	public void setBasenames(String[] basenames) {
		if (basenames != null) {
			this.basenames = new String[basenames.length];
			for (int i = 0; i < basenames.length; i++) {
				String basename = basenames[i];
				Assert.hasText(basename, "Basename must not be empty");
				this.basenames[i] = basename.trim();
			}
		} else {
			this.basenames = new String[0];
		}
	}

	/**
	 * Set the default charset to use for parsing properties files. Used if no
	 * file-specific charset is specified for a file.
	 * <p>
	 * Default is none, using the <code>java.util.Properties</code> default
	 * encoding.
	 * <p>
	 * Only applies to classic properties files, not to XML files.
	 * 
	 * @param defaultEncoding
	 *            the default charset
	 * @see #setFileEncodings
	 */
	public void setDefaultEncoding(String defaultEncoding) {
		this.defaultEncoding = defaultEncoding;
	}

	/**
	 * Set per-file charsets to use for parsing properties files.
	 * <p>
	 * Only applies to classic properties files, not to XML files.
	 * 
	 * @param fileEncodings
	 *            Properties with filenames as keys and charset names as values.
	 *            Filenames have to match the basename syntax, with optional
	 *            locale-specific appendices: e.g. "WEB-INF/messages" or
	 *            "WEB-INF/messages_en".
	 * @see #setBasenames
	 */
	public void setFileEncodings(Properties fileEncodings) {
		this.fileEncodings = fileEncodings;
	}

	/**
	 * Set whether to fall back to the system Locale if no files for a specific
	 * Locale have been found. Default is "true"; if this is turned off, the
	 * only fallback will be the default file (e.g. "messages.properties" for
	 * basename "messages").
	 * <p>
	 * Falling back to the system Locale is the default behavior of
	 * <code>java.util.ResourceBundle</code>. However, this is often not
	 * desirable in an application server environment, where the system Locale
	 * is not relevant to the application at all: Set this flag to "false" in
	 * such a scenario.
	 */
	public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) {
		this.fallbackToSystemLocale = fallbackToSystemLocale;
	}

	

	/**
	 * Set the PropertiesPersister to use for parsing properties files.
	 * <p>
	 * The default is a DefaultPropertiesPersister.
	 */
	public void setPropertiesPersister(PropertiesPersister propertiesPersister) {
		this.propertiesPersister = (propertiesPersister != null ? propertiesPersister
				: new DefaultPropertiesPersister());
	}

	/**
	 * Set the ResourceLoader to use for loading bundle properties files.
	 * <p>
	 * The default is a DefaultResourceLoader. Will get overridden by the
	 * ApplicationContext if running in a context, as it implements the
	 * ResourceLoaderAware interface. Can be manually overridden when running
	 * outside of an ApplicationContext.
	 * 
	 * @see DefaultResourceLoader
	 * @see ResourceLoaderAware
	 */
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourceLoader = (resourceLoader != null ? resourceLoader
				: new DefaultResourceLoader());
	}

	/**
	 * Resolves the given message code as key in the retrieved bundle files,
	 * returning the value found in the bundle as-is (without MessageFormat
	 * parsing).
	 */
	protected String resolveCodeWithoutArguments(String code, Locale locale) {
		if (this.refresh_interval <= 0) {
			PropertiesHolder propHolder = getMergedProperties(locale);
			String result = propHolder.getProperty(code);
			if (result != null) {
				return result;
			}
		} else {
			for (int i = 0; i < this.basenames.length; i++) {
				List filenames = calculateAllFilenames(this.basenames[i],
						locale);
				for (int j = 0; j < filenames.size(); j++) {
					String filename = (String) filenames.get(j);
					PropertiesHolder propHolder = getProperties(filename);
					String result = propHolder.getProperty(code);
					if (result != null) {
						return result;
					}
				}
			}
		}
		return null;
	}

	/**
	 * Resolves the given message code as key in the retrieved bundle files,
	 * using a cached MessageFormat instance per message code.
	 */
	protected MessageFormat resolveCode(String code, Locale locale) {
		if (refresh_interval < 0) {
			PropertiesHolder propHolder = getMergedProperties(locale);
			MessageFormat result = propHolder.getMessageFormat(code, locale);
			if (result != null) {
				return result;
			}
		} else {
			for (int i = 0; i < this.basenames.length; i++) {
				List filenames = calculateAllFilenames(this.basenames[i],
						locale);
				for (int j = 0; j < filenames.size(); j++) {
					String filename = (String) filenames.get(j);
					PropertiesHolder propHolder = getProperties(filename);
					MessageFormat result = propHolder.getMessageFormat(code,
							locale);
					if (result != null) {
						return result;
					}
				}
			}
		}
		return null;
	}

	/**
	 * Get a PropertiesHolder that contains the actually visible properties for
	 * a Locale, after merging all specified resource bundles. Either fetches
	 * the holder from the cache or freshly loads it.
	 * <p>
	 * Only used when caching resource bundle contents forever, i.e. with
	 * cacheSeconds < 0. Therefore, merged properties are always cached forever.
	 */
	protected PropertiesHolder getMergedProperties(Locale locale) {
		PropertiesHolder mergedHolder = (PropertiesHolder) this.cachedMergedProperties
				.get(locale);
		if (mergedHolder != null) {
			return mergedHolder;
		}
		synchronized (this.cachedMergedProperties) {
			mergedHolder = (PropertiesHolder) this.cachedMergedProperties
					.get(locale);
			if (mergedHolder != null) {
				return mergedHolder;
			}
			Properties mergedProps = new Properties();
			mergedHolder = new PropertiesHolder(mergedProps);
			for (int i = this.basenames.length - 1; i >= 0; i--) {
				List filenames = calculateAllFilenames(this.basenames[i],
						locale);
				for (int j = filenames.size() - 1; j >= 0; j--) {
					String filename = (String) filenames.get(j);
					PropertiesHolder propHolder = getProperties(filename);
					if (propHolder.getProperties() != null) {
						mergedProps.putAll(propHolder.getProperties());
					}
				}
			}
			this.cachedMergedProperties.put(locale, mergedHolder);
			return mergedHolder;
		}
	}

	/**
	 * Calculate all filenames for the given bundle basename and Locale. Will
	 * calculate filenames for the given Locale, the system Locale (if
	 * applicable), and the default file.
	 * 
	 * @param basename
	 *            the basename of the bundle
	 * @param locale
	 *            the locale
	 * @return the List of filenames to check
	 * @see #setFallbackToSystemLocale
	 * @see #calculateFilenamesForLocale
	 */
	protected List calculateAllFilenames(String basename, Locale locale) {
		Map localeMap = (Map) this.cachedFilenames.get(basename);
		if (localeMap != null) {
			List filenames = (List) localeMap.get(locale);
			if (filenames != null) {
				return filenames;
			}
		}
		synchronized (this.cachedFilenames) {
			localeMap = (Map) this.cachedFilenames.get(basename);
			if (localeMap != null) {
				List filenames = (List) localeMap.get(locale);
				if (filenames != null) {
					return filenames;
				}
			}
			List filenames = new ArrayList(7);
			filenames.addAll(calculateFilenamesForLocale(basename, locale));
			if (this.fallbackToSystemLocale
					&& !locale.equals(Locale.getDefault())) {
				List fallbackFilenames = calculateFilenamesForLocale(basename,
						Locale.getDefault());
				for (Iterator it = fallbackFilenames.iterator(); it.hasNext();) {
					String fallbackFilename = (String) it.next();
					if (!filenames.contains(fallbackFilename)) {
						// Entry for fallback locale that isn't already in
						// filenames list.
						filenames.add(fallbackFilename);
					}
				}
			}
			filenames.add(basename);
			if (localeMap != null) {
				localeMap.put(locale, filenames);
			} else {
				localeMap = new HashMap();
				localeMap.put(locale, filenames);
				this.cachedFilenames.put(basename, localeMap);
			}
			return filenames;
		}
	}

	/**
	 * Get a PropertiesHolder for the given filename, either from the cache or
	 * freshly loaded.
	 * 
	 * @param filename
	 *            the bundle filename (basename + Locale)
	 * @return the current PropertiesHolder for the bundle
	 */
	protected PropertiesHolder getProperties(String filename) {
		PropertiesHolder propHolder = (PropertiesHolder) this.cachedProperties
				.get(filename);
		if(propHolder != null)
			return propHolder;
		synchronized (this.cachedProperties) {
			propHolder = (PropertiesHolder) this.cachedProperties
					.get(filename);
			if(propHolder != null)
				return propHolder;
//			if (propHolder != null
//					&& (propHolder.getRefreshTimestamp() < 0 || propHolder
//							.getRefreshTimestamp() > System.currentTimeMillis()
//							- this.cacheMillis)) {
//				// up to date
//				return propHolder;
//			}
			propHolder = this.firstLoadProperties(filename);
			if(propHolder.getResource() != null && this.isChangemonitor())
				checkResource(this,propHolder.getResource(),propHolder.getBasename(),propHolder.getRelativefile());
			cachedProperties.put(filename, propHolder);
			return propHolder;
		}
	}

	/**
	 * Refresh the PropertiesHolder for the given bundle filename. The holder
	 * can be <code>null</code> if not cached before, or a timed-out cache entry
	 * (potentially getting re-validated against the current last-modified
	 * timestamp).
	 * 
	 * @param filename
	 *            the bundle filename (basename + Locale)
	 * @param propHolder
	 *            the current PropertiesHolder for the bundle
	 */
//	protected PropertiesHolder refreshProperties(String filename,
//			PropertiesHolder propHolder) {
//		long refreshTimestamp = (this.cacheMillis < 0) ? -1 : System
//				.currentTimeMillis();
//
//		Resource resource = this.resourceLoader.getResource(filename
//				+ PROPERTIES_SUFFIX);
//		if (!resource.exists()) {
//			resource = this.resourceLoader.getResource(filename + XML_SUFFIX);
//		}
//
//		if (resource.exists()) {
//			long fileTimestamp = -1;
//			if (this.cacheMillis >= 0) {
//				// Last-modified timestamp of file will just be read if caching
//				// with timeout.
//				try {
//					fileTimestamp = resource.lastModified();
//					if (propHolder != null
//							&& propHolder.getFileTimestamp() == fileTimestamp) {
//						if (logger.isDebugEnabled()) {
//							logger.debug("Re-caching properties for filename ["
//									+ filename
//									+ "] - file hasn't been modified");
//						}
//						propHolder.setRefreshTimestamp(refreshTimestamp);
//						return propHolder;
//					}
//				} catch (IOException ex) {
//					// Probably a class path resource: cache it forever.
//					if (logger.isDebugEnabled()) {
//						logger.debug(
//								resource
//										+ " could not be resolved in the file system - assuming that is hasn't changed",
//								ex);
//					}
//					fileTimestamp = -1;
//				}
//			}
//			try {
//				Properties props = loadProperties(resource, filename);
//				propHolder = new PropertiesHolder(props, fileTimestamp);
//			} catch (IOException ex) {
//				{
//					logger.warn(
//							"Could not parse properties file ["
//									+ resource.getFilename() + "]", ex);
//				}
//				// Empty holder representing "not valid".
//				propHolder = new PropertiesHolder();
//			}
//		}
//
//		else {
//			// Resource does not exist.
//			if (logger.isDebugEnabled()) {
//				logger.debug("No properties file found for [" + filename
//						+ "] - neither plain properties nor XML");
//			}
//			// Empty holder representing "not found".
//			propHolder = new PropertiesHolder();
//		}
//
//		propHolder.setRefreshTimestamp(refreshTimestamp);
//		this.cachedProperties.put(filename, propHolder);
//		return propHolder;
//	}
	
	/**
	 * relativefile = "org/frameworkset/spi/support/messages_en_US.properties";
	 * basename = "org/frameworkset/spi/support/messages_en_US";
	 * filepath = "d:/workspace/org/frameworkset/spi/support/messages_en_US.properties";
	 * @param filename
	 * @param basename
	 * @param relativefile
	 * @return
	 */
	protected PropertiesHolder refreshProperties(File filepath,String basename,String relativefile) {
//		filename = "org/frameworkset/spi/support/messages_en_US.properties";
		Resource resource = this.resourceLoader.getResource(relativefile);
//		if (!resource.exists()) {
//			resource = this.resourceLoader.getResource(filename + XML_SUFFIX);
//		}
		
		PropertiesHolder  propHolder = null;
		try {
			Properties props = loadProperties(resource, relativefile);
			propHolder = new PropertiesHolder(props,filepath,basename,relativefile);
		} catch (IOException ex) {
			{
				logger.warn(
						"Could not parse properties file ["
								+ filepath + "]", ex);
			}
			// Empty holder representing "not valid".
			propHolder = ERROR_propHolder;
		}
	
		this.cachedProperties.put(basename, propHolder);
		return propHolder;
	}
	
	protected PropertiesHolder firstLoadProperties(String filename) {
		String name = filename
				+ PROPERTIES_SUFFIX;
		Resource resource = this.resourceLoader.getResource(name);
		boolean reset = false;
		if (!resource.exists()) {
			name = filename
					+ XML_SUFFIX;
			resource = this.resourceLoader.getResource(name);
			reset = true;
		}
		
		PropertiesHolder  propHolder = null;
		if (reset && !resource.exists()) {
			propHolder = NOTEXIST_propHolder;
		}
		else
		{
			try {
				Properties props = loadProperties(resource, name);
				File f = null;
				
				try
				{
					f = resource.getFile();
				
					
				}
				catch(Throwable e)
				{
					logger.warn(
							new StringBuffer().append("Get properties file from ").append( resource.getClass().getCanonicalName() ).append( " failed:"+e.getMessage()).toString());
				}
				propHolder = new PropertiesHolder(props,f,filename,name);
			} catch (IOException ex) {
				{
					try {
						logger.warn(
								new StringBuffer().append("Could not parse properties file [").append(
										resource.getFile().getCanonicalPath() ).append( "]").toString(), ex);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				// Empty holder representing "not valid".
				propHolder = ERROR_propHolder;
			}
		}
		

		

	
//		this.cachedProperties.put(filename, propHolder);
		return propHolder;
	}

	/**
	 * Load the properties from the given resource.
	 * 
	 * @param resource
	 *            the resource to load from
	 * @param filename
	 *            the original bundle filename (basename + Locale)
	 * @return the populated Properties instance
	 * @throws IOException
	 *             if properties loading failed
	 */
	protected Properties loadProperties(Resource resource, String filename)
			throws IOException {
		InputStream is = resource.getInputStream();
		Properties props = new Properties();
		try {
			if (resource.getFilename().endsWith(XML_SUFFIX)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Loading properties ["
							+ resource.getFilename() + "]");
				}
				this.propertiesPersister.loadFromXml(props, is);
			} else {
				String encoding = null;
				if (this.fileEncodings != null) {
					encoding = this.fileEncodings.getProperty(filename);
				}
				if (encoding == null) {
					encoding = this.defaultEncoding;
				}
				if (encoding != null) {
					if (logger.isDebugEnabled()) {
						logger.debug("Loading properties ["
								+ resource.getFilename() + "] with encoding '"
								+ encoding + "'");
					}
					this.propertiesPersister.load(props, new InputStreamReader(
							is, encoding));
				} else {
					if (logger.isDebugEnabled()) {
						logger.debug("Loading properties ["
								+ resource.getFilename() + "]");
					}
					this.propertiesPersister.load(props, is);
				}
			}
			return props;
		} finally {
			is.close();
		}
	}

	/**
	 * Clear the resource bundle cache. Subsequent resolve calls will lead to
	 * reloading of the properties files.
	 */
	public void clearCache() {
		logger.debug("Clearing entire resource bundle cache");
		synchronized (this.cachedProperties) {
			this.cachedProperties.clear();
		}
		synchronized (this.cachedMergedProperties) {
			this.cachedMergedProperties.clear();
		}
	}

	/**
	 * Clear the resource bundle caches of this MessageSource and all its
	 * ancestors.
	 * 
	 * @see #clearCache
	 */
	public void clearCacheIncludingAncestors() {
		clearCache();
		if (getParentMessageSource() instanceof ReloadableResourceBundleMessageSource) {
			((ReloadableResourceBundleMessageSource) getParentMessageSource())
					.clearCacheIncludingAncestors();
		}
	}

	public String toString() {
		return getClass().getName() + ": basenames=["
				+ SimpleStringUtil.arrayToCommaDelimitedString(this.basenames)
				+ "]";
	}

	/**
	 * PropertiesHolder for caching. Stores the last-modified timestamp of the
	 * source file for efficient change detection, and the timestamp of the last
	 * refresh attempt (updated every time the cache entry gets re-validated).
	 */
	protected static class PropertiesHolder {

		private Properties properties;
		private File resource;
		private String basename;
		private String relativefile;

//		private long fileTimestamp = -1;
//
//		private long refreshTimestamp = -1;

		/** Cache to hold already generated MessageFormats per message code */
		private final Map cachedMessageFormats = new HashMap();

		public PropertiesHolder(Properties properties,File resource,String basename,String relativefile) {
			this.properties = properties;
			this.resource = resource;
			this.basename = basename;
			this.relativefile = relativefile;
//			this.fileTimestamp = fileTimestamp;
		}
		
		public PropertiesHolder(Properties properties) {
			this.properties = properties;
//			this.resource = resource;
//			this.fileTimestamp = fileTimestamp;
		}

		public PropertiesHolder() {
		}

		public Properties getProperties() {
			return properties;
		}

//		public long getFileTimestamp() {
//			return fileTimestamp;
//		}
//
//		public void setRefreshTimestamp(long refreshTimestamp) {
//			this.refreshTimestamp = refreshTimestamp;
//		}
//
//		public long getRefreshTimestamp() {
//			return refreshTimestamp;
//		}

		public String getProperty(String code) {
			if (this.properties == null) {
				return null;
			}
			return this.properties.getProperty(code);
		}

		public MessageFormat getMessageFormat(String code, Locale locale) {
			if (this.properties == null) {
				return null;
			}
			synchronized (this.cachedMessageFormats) {
				Map localeMap = (Map) this.cachedMessageFormats.get(code);
				if (localeMap != null) {
					MessageFormat result = (MessageFormat) localeMap
							.get(locale);
					if (result != null) {
						return result;
					}
				}
				String msg = this.properties.getProperty(code);
				if (msg != null) {
					if (localeMap == null) {
						localeMap = new HashMap();
						this.cachedMessageFormats.put(code, localeMap);
					}
					MessageFormat result = createMessageFormat(msg, locale);
					localeMap.put(locale, result);
					return result;
				}
				return null;
			}
		}

		public File getResource() {
			return resource;
		}

		public String getBasename() {
			return basename;
		}

		public String getRelativefile() {
			return relativefile;
		}
	}

	/**
	 * Calculate the filenames for the given bundle basename and Locale,
	 * appending language code, country code, and variant code. E.g.: basename
	 * "messages", Locale "de_AT_oo" -> "messages_de_AT_OO", "messages_de_AT",
	 * "messages_de".
	 * 
	 * @param basename
	 *            the basename of the bundle
	 * @param locale
	 *            the locale
	 * @return the List of filenames to check
	 */
	protected List calculateFilenamesForLocale(String basename, Locale locale) {
		List result = new ArrayList(3);
		String language = locale.getLanguage();
		String country = locale.getCountry();
		String variant = locale.getVariant();
		StringBuffer temp = new StringBuffer(basename);

		if (language.length() > 0) {
			temp.append('_').append(language);
			result.add(0, temp.toString());
		}

		if (country.length() > 0) {
			temp.append('_').append(country);
			result.add(0, temp.toString());
		}

		if (variant.length() > 0) {
			temp.append('_').append(variant);
			result.add(0, temp.toString());
		}

		return result;
	}
	/**
	 * 属性文件变更检测时间间隔，单位为毫秒，默认为5秒间隔
	 * 在配置属性文件时全局指定
	 */
	private static long refresh_interval = 5000;
	private static DaemonThread damon = null; 
	private static Object lock = new Object();
	private static void checkResource(HotDeployResourceBundleMessageSource messagesource,File file,String basename,String filename){
		
		refresh_interval = BaseApplicationContext.getResourceFileRefreshInterval();
		if(refresh_interval > 0)
		{
			if(damon == null)
			{
				synchronized(lock)
				{
					if(damon == null)
					{
						damon = new DaemonThread(refresh_interval,"Message files Refresh Worker"); 
						damon.start();
						
					}
				}
			}
			damon.addFile(file, new ResourceFileRefresh(messagesource,file,basename,filename));
		}
		
	}
	static class ResourceFileRefresh implements ResourceInitial
	{
		private HotDeployResourceBundleMessageSource messagesource ;
		private String filename;
		private String basename;
		private File file;
		public ResourceFileRefresh(HotDeployResourceBundleMessageSource messagesource,File file,String basename,String filename)
		{
			this.messagesource = messagesource;
			this.filename = filename;
			this.basename = basename;
			this.file = file;
		}
		public void reinit() {
			messagesource.refreshProperties(file,basename,filename);
		}
		
	}
	
	public static void stopmonitor()
	{
		try {
			if(damon != null)
			{
				damon.stopped();
				damon = null;
			}
			
		} catch (Throwable e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
		}
	}

	public ClassLoader getBundleClassLoader() {
		return bundleClassLoader;
	}

	public void setBundleClassLoader(ClassLoader bundleClassLoader) {
		this.bundleClassLoader = bundleClassLoader;
	}

	public boolean isChangemonitor() {
		return changemonitor;
	}

	public void setChangemonitor(boolean changemonitor) {
		this.changemonitor = changemonitor;
	}
	
	/**
	 * 注销国际化资源
	 */
	public void destroy()
	{
		this.cachedFilenames.clear();
		this.cachedMergedProperties.clear();
		this.cachedProperties.clear();
		super.destroy();
		
	}
}