@Nullable private String getExpressionString(String key, Locale locale) { if (this.cacheMillis < 0) { PropertiesHolder propHolder = getMergedProperties(locale); String result = propHolder.getProperty(key); if (result != null) { return result; } } else { for (String basename : this.basenames) { List<String> filenames = calculateAllFilenames(basename, locale); for (String filename : filenames) { PropertiesHolder propHolder = getProperties(filename); String result = propHolder.getProperty(key); if (result != null) { return result; } } } } return null; }
filenames.addAll(calculateFilenamesForLocale(basename, locale)); if (this.fallbackToSystemLocale && !locale.equals(Locale.getDefault())) { List<String> fallbackFilenames = calculateFilenamesForLocale(basename, Locale.getDefault()); for (String fallbackFilename : fallbackFilenames) { if (!filenames.contains(fallbackFilename)) {
/** * Resolves the given key in the retrieved bundle files to an Expression. */ @Override public Expression getExpression(String key, Locale locale) { String expressionString = getExpressionString(key, locale); if (expressionString != null) { return this.parser.parseExpression(expressionString); } 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. */ private PropertiesHolder getMergedProperties(Locale locale) { synchronized (this.cachedMergedProperties) { PropertiesHolder mergedHolder = this.cachedMergedProperties.get(locale); if (mergedHolder != null) { return mergedHolder; } Properties mergedProps = new Properties(); mergedHolder = new PropertiesHolder(mergedProps, -1); for (int i = this.basenames.length - 1; i >= 0; i--) { List<String> filenames = calculateAllFilenames(this.basenames[i], locale); for (int j = filenames.size() - 1; j >= 0; j--) { String filename = filenames.get(j); PropertiesHolder propHolder = getProperties(filename); if (propHolder.getProperties() != null) { mergedProps.putAll(propHolder.getProperties()); } } } this.cachedMergedProperties.put(locale, mergedHolder); return mergedHolder; } }
@Test public void expressionUpdate() throws Exception { ReloadableResourceBundleExpressionSource source = new ReloadableResourceBundleExpressionSource(); source.setBasename(basename); source.setCacheSeconds(0); DynamicExpression expression = new DynamicExpression(key, source); assertEquals("Hello World!", expression.getValue()); writeExpressionStringToFile("toUpperCase()"); assertEquals("FOO", expression.getValue("foo")); }
/** * Set a single basename, following the basic ResourceBundle convention of * not specifying file extension or language codes, but referring to a Spring * resource location: e.g. "META-INF/expressions" for "META-INF/expressions.properties", * "META-INF/expressions_en.properties", etc. * <p>XML properties files are also supported: .g. "META-INF/expressions" will find * and load "META-INF/expressions.xml", "META-INF/expressions_en.xml", etc as well. * @param basename the single basename * @see #setBasenames * @see org.springframework.core.io.ResourceEditor * @see java.util.ResourceBundle */ public void setBasename(String basename) { setBasenames(new String[] { basename }); }
/** * 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 */ private PropertiesHolder getProperties(String filename) { synchronized (this.cachedProperties) { PropertiesHolder propHolder = this.cachedProperties.get(filename); if (propHolder != null && (propHolder.getRefreshTimestamp() < 0 || propHolder.getRefreshTimestamp() > System.currentTimeMillis() - this.cacheMillis)) { return propHolder; } return refreshProperties(filename, 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 */ private Properties loadProperties(Resource resource, String filename) throws IOException { InputStream is = resource.getInputStream(); Properties props = new Properties(); try { String resourceFilename = resource.getFilename(); if (resourceFilename != null && resourceFilename.endsWith(XML_SUFFIX)) { if (logger.isDebugEnabled()) { logger.debug("Loading properties [" + resourceFilename + "]"); } this.propertiesPersister.loadFromXml(props, is); } else { loadFromProperties(resource, filename, is, props, resourceFilename); } return props; } finally { is.close(); } }
Properties props = loadProperties(resource, filename); propHolder = new PropertiesHolder(props, fileTimestamp);
/** * 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. */ private PropertiesHolder getMergedProperties(Locale locale) { synchronized (this.cachedMergedProperties) { PropertiesHolder mergedHolder = this.cachedMergedProperties.get(locale); if (mergedHolder != null) { return mergedHolder; } Properties mergedProps = new Properties(); mergedHolder = new PropertiesHolder(mergedProps, -1); for (int i = this.basenames.length - 1; i >= 0; i--) { List<String> filenames = calculateAllFilenames(this.basenames[i], locale); for (int j = filenames.size() - 1; j >= 0; j--) { String filename = filenames.get(j); PropertiesHolder propHolder = getProperties(filename); if (propHolder.getProperties() != null) { mergedProps.putAll(propHolder.getProperties()); } } } this.cachedMergedProperties.put(locale, mergedHolder); return mergedHolder; } }
/** * Set a single basename, following the basic ResourceBundle convention of * not specifying file extension or language codes, but referring to a Spring * resource location: e.g. "META-INF/expressions" for "META-INF/expressions.properties", * "META-INF/expressions_en.properties", etc. * <p>XML properties files are also supported: .g. "META-INF/expressions" will find * and load "META-INF/expressions.xml", "META-INF/expressions_en.xml", etc as well. * @param basename the single basename * @see #setBasenames * @see org.springframework.core.io.ResourceEditor * @see java.util.ResourceBundle */ public void setBasename(String basename) { setBasenames(new String[] { basename }); }
/** * 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 */ private PropertiesHolder getProperties(String filename) { synchronized (this.cachedProperties) { PropertiesHolder propHolder = this.cachedProperties.get(filename); if (propHolder != null && (propHolder.getRefreshTimestamp() < 0 || propHolder.getRefreshTimestamp() > System.currentTimeMillis() - this.cacheMillis)) { return propHolder; } return refreshProperties(filename, 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 */ private Properties loadProperties(Resource resource, String filename) throws IOException { InputStream is = resource.getInputStream(); Properties props = new Properties(); try { String resourceFilename = resource.getFilename(); if (resourceFilename != null && resourceFilename.endsWith(XML_SUFFIX)) { if (logger.isDebugEnabled()) { logger.debug("Loading properties [" + resourceFilename + "]"); } this.propertiesPersister.loadFromXml(props, is); } else { loadFromProperties(resource, filename, is, props, resourceFilename); } return props; } finally { is.close(); } }
Properties props = loadProperties(resource, filename); propHolder = new PropertiesHolder(props, fileTimestamp);
@Nullable private String getExpressionString(String key, Locale locale) { if (this.cacheMillis < 0) { PropertiesHolder propHolder = getMergedProperties(locale); String result = propHolder.getProperty(key); if (result != null) { return result; } } else { for (String basename : this.basenames) { List<String> filenames = calculateAllFilenames(basename, locale); for (String filename : filenames) { PropertiesHolder propHolder = getProperties(filename); String result = propHolder.getProperty(key); if (result != null) { return result; } } } } return null; }
/** * Resolves the given key in the retrieved bundle files to an Expression. */ @Override public Expression getExpression(String key, Locale locale) { String expressionString = getExpressionString(key, locale); if (expressionString != null) { return this.parser.parseExpression(expressionString); } return null; }
filenames.addAll(calculateFilenamesForLocale(basename, locale)); if (this.fallbackToSystemLocale && !locale.equals(Locale.getDefault())) { List<String> fallbackFilenames = calculateFilenamesForLocale(basename, Locale.getDefault()); for (String fallbackFilename : fallbackFilenames) { if (!filenames.contains(fallbackFilename)) {