/* * Copyright 2002-2016 the original author or authors. * * 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.springframework.context.support; import java.util.LinkedHashSet; import java.util.Set; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; /** * Abstract base class for {@code MessageSource} implementations based on * resource bundle conventions, such as {@link ResourceBundleMessageSource} * and {@link ReloadableResourceBundleMessageSource}. Provides common * configuration methods and corresponding semantic definitions. * * @author Juergen Hoeller * @since 4.3 * @see ResourceBundleMessageSource * @see ReloadableResourceBundleMessageSource */ public abstract class AbstractResourceBasedMessageSource extends AbstractMessageSource { private final Set<String> basenameSet = new LinkedHashSet<>(4); private String defaultEncoding; private boolean fallbackToSystemLocale = true; private long cacheMillis = -1; /** * Set a single basename, following the basic ResourceBundle convention * of not specifying file extension or language codes. The resource location * format is up to the specific {@code MessageSource} implementation. * <p>Regular and XMl properties files are supported: e.g. "messages" will find * a "messages.properties", "messages_en.properties" etc arrangement as well * as "messages.xml", "messages_en.xml" etc. * @param basename the single basename * @see #setBasenames * @see org.springframework.core.io.ResourceEditor * @see java.util.ResourceBundle */ public void setBasename(String basename) { setBasenames(basename); } /** * Set an array of basenames, each following the basic ResourceBundle convention * of not specifying file extension or language codes. The resource location * format is up to the specific {@code MessageSource} implementation. * <p>Regular and XMl properties files are supported: e.g. "messages" will find * a "messages.properties", "messages_en.properties" etc arrangement as well * as "messages.xml", "messages_en.xml" etc. * <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. * <p>Note: In contrast to {@link #addBasenames}, this replaces existing entries * with the given names and can therefore also be used to reset the configuration. * @param basenames an array of basenames * @see #setBasename * @see java.util.ResourceBundle */ public void setBasenames(String... basenames) { this.basenameSet.clear(); addBasenames(basenames); } /** * Add the specified basenames to the existing basename configuration. * <p>Note: If a given basename already exists, the position of its entry * will remain as in the original set. New entries will be added at the * end of the list, to be searched after existing basenames. * @since 4.3 * @see #setBasenames * @see java.util.ResourceBundle */ public void addBasenames(String... basenames) { if (!ObjectUtils.isEmpty(basenames)) { for (String basename : basenames) { Assert.hasText(basename, "Basename must not be empty"); this.basenameSet.add(basename.trim()); } } } /** * Return this {@code MessageSource}'s basename set, containing entries * in the order of registration. * <p>Calling code may introspect this set as well as add or remove entries. * @since 4.3 * @see #addBasenames */ public Set<String> getBasenameSet() { return this.basenameSet; } /** * 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} * default encoding: ISO-8859-1. * <p>Only applies to classic properties files, not to XML files. * @param defaultEncoding the default charset */ public void setDefaultEncoding(String defaultEncoding) { this.defaultEncoding = defaultEncoding; } /** * Return the default charset to use for parsing properties files, if any. * @since 4.3 */ protected String getDefaultEncoding() { return this.defaultEncoding; } /** * 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}. 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; } /** * Return whether to fall back to the system Locale if no files for a specific * Locale have been found. * @since 4.3 */ protected boolean isFallbackToSystemLocale() { return this.fallbackToSystemLocale; } /** * Set the number of seconds to cache loaded properties files. * <ul> * <li>Default is "-1", indicating to cache forever (just like * {@code java.util.ResourceBundle}). * <li>A positive number will cache loaded properties files for the given * number of seconds. This is essentially the interval between refresh checks. * Note that a refresh attempt will first check the last-modified timestamp * of the file before actually reloading it; so if files don't change, this * interval can be set rather low, as refresh attempts will not actually reload. * <li>A value of "0" will check the last-modified timestamp of the file on * every message access. <b>Do not use this in a production environment!</b> * </ul> * <p><b>Note that depending on your ClassLoader, expiration might not work reliably * since the ClassLoader may hold on to a cached version of the bundle file.</b> * Prefer {@link ReloadableResourceBundleMessageSource} over * {@link ResourceBundleMessageSource} in such a scenario, in combination with * a non-classpath location. */ public void setCacheSeconds(int cacheSeconds) { this.cacheMillis = (cacheSeconds * 1000); } /** * Set the number of milliseconds to cache loaded properties files. * Note that it is common to set seconds instead: {@link #setCacheSeconds}. * <ul> * <li>Default is "-1", indicating to cache forever (just like * {@code java.util.ResourceBundle}). * <li>A positive number will cache loaded properties files for the given * number of milliseconds. This is essentially the interval between refresh checks. * Note that a refresh attempt will first check the last-modified timestamp * of the file before actually reloading it; so if files don't change, this * interval can be set rather low, as refresh attempts will not actually reload. * <li>A value of "0" will check the last-modified timestamp of the file on * every message access. <b>Do not use this in a production environment!</b> * </ul> * @since 4.3 * @see #setCacheSeconds */ public void setCacheMillis(long cacheMillis) { this.cacheMillis = cacheMillis; } /** * Return the number of milliseconds to cache loaded properties files. * @since 4.3 */ protected long getCacheMillis() { return this.cacheMillis; } }