001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.context.support; 018 019import java.util.LinkedHashSet; 020import java.util.Set; 021 022import org.springframework.util.Assert; 023import org.springframework.util.ObjectUtils; 024 025/** 026 * Abstract base class for {@code MessageSource} implementations based on 027 * resource bundle conventions, such as {@link ResourceBundleMessageSource} 028 * and {@link ReloadableResourceBundleMessageSource}. Provides common 029 * configuration methods and corresponding semantic definitions. 030 * 031 * @author Juergen Hoeller 032 * @since 4.3 033 * @see ResourceBundleMessageSource 034 * @see ReloadableResourceBundleMessageSource 035 */ 036public abstract class AbstractResourceBasedMessageSource extends AbstractMessageSource { 037 038 private final Set<String> basenameSet = new LinkedHashSet<String>(4); 039 040 private String defaultEncoding; 041 042 private boolean fallbackToSystemLocale = true; 043 044 private long cacheMillis = -1; 045 046 047 /** 048 * Set a single basename, following the basic ResourceBundle convention 049 * of not specifying file extension or language codes. The resource location 050 * format is up to the specific {@code MessageSource} implementation. 051 * <p>Regular and XMl properties files are supported: e.g. "messages" will find 052 * a "messages.properties", "messages_en.properties" etc arrangement as well 053 * as "messages.xml", "messages_en.xml" etc. 054 * @param basename the single basename 055 * @see #setBasenames 056 * @see org.springframework.core.io.ResourceEditor 057 * @see java.util.ResourceBundle 058 */ 059 public void setBasename(String basename) { 060 setBasenames(basename); 061 } 062 063 /** 064 * Set an array of basenames, each following the basic ResourceBundle convention 065 * of not specifying file extension or language codes. The resource location 066 * format is up to the specific {@code MessageSource} implementation. 067 * <p>Regular and XMl properties files are supported: e.g. "messages" will find 068 * a "messages.properties", "messages_en.properties" etc arrangement as well 069 * as "messages.xml", "messages_en.xml" etc. 070 * <p>The associated resource bundles will be checked sequentially when resolving 071 * a message code. Note that message definitions in a <i>previous</i> resource 072 * bundle will override ones in a later bundle, due to the sequential lookup. 073 * <p>Note: In contrast to {@link #addBasenames}, this replaces existing entries 074 * with the given names and can therefore also be used to reset the configuration. 075 * @param basenames an array of basenames 076 * @see #setBasename 077 * @see java.util.ResourceBundle 078 */ 079 public void setBasenames(String... basenames) { 080 this.basenameSet.clear(); 081 addBasenames(basenames); 082 } 083 084 /** 085 * Add the specified basenames to the existing basename configuration. 086 * <p>Note: If a given basename already exists, the position of its entry 087 * will remain as in the original set. New entries will be added at the 088 * end of the list, to be searched after existing basenames. 089 * @since 4.3 090 * @see #setBasenames 091 * @see java.util.ResourceBundle 092 */ 093 public void addBasenames(String... basenames) { 094 if (!ObjectUtils.isEmpty(basenames)) { 095 for (String basename : basenames) { 096 Assert.hasText(basename, "Basename must not be empty"); 097 this.basenameSet.add(basename.trim()); 098 } 099 } 100 } 101 102 /** 103 * Return this {@code MessageSource}'s basename set, containing entries 104 * in the order of registration. 105 * <p>Calling code may introspect this set as well as add or remove entries. 106 * @since 4.3 107 * @see #addBasenames 108 */ 109 public Set<String> getBasenameSet() { 110 return this.basenameSet; 111 } 112 113 /** 114 * Set the default charset to use for parsing properties files. 115 * Used if no file-specific charset is specified for a file. 116 * <p>Default is none, using the {@code java.util.Properties} 117 * default encoding: ISO-8859-1. 118 * <p>Only applies to classic properties files, not to XML files. 119 * @param defaultEncoding the default charset 120 */ 121 public void setDefaultEncoding(String defaultEncoding) { 122 this.defaultEncoding = defaultEncoding; 123 } 124 125 /** 126 * Return the default charset to use for parsing properties files, if any. 127 * @since 4.3 128 */ 129 protected String getDefaultEncoding() { 130 return this.defaultEncoding; 131 } 132 133 /** 134 * Set whether to fall back to the system Locale if no files for a specific 135 * Locale have been found. Default is "true"; if this is turned off, the only 136 * fallback will be the default file (e.g. "messages.properties" for 137 * basename "messages"). 138 * <p>Falling back to the system Locale is the default behavior of 139 * {@code java.util.ResourceBundle}. However, this is often not desirable 140 * in an application server environment, where the system Locale is not relevant 141 * to the application at all: set this flag to "false" in such a scenario. 142 */ 143 public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) { 144 this.fallbackToSystemLocale = fallbackToSystemLocale; 145 } 146 147 /** 148 * Return whether to fall back to the system Locale if no files for a specific 149 * Locale have been found. 150 * @since 4.3 151 */ 152 protected boolean isFallbackToSystemLocale() { 153 return this.fallbackToSystemLocale; 154 } 155 156 /** 157 * Set the number of seconds to cache loaded properties files. 158 * <ul> 159 * <li>Default is "-1", indicating to cache forever (just like 160 * {@code java.util.ResourceBundle}). 161 * <li>A positive number will cache loaded properties files for the given 162 * number of seconds. This is essentially the interval between refresh checks. 163 * Note that a refresh attempt will first check the last-modified timestamp 164 * of the file before actually reloading it; so if files don't change, this 165 * interval can be set rather low, as refresh attempts will not actually reload. 166 * <li>A value of "0" will check the last-modified timestamp of the file on 167 * every message access. <b>Do not use this in a production environment!</b> 168 * </ul> 169 * <p><b>Note that depending on your ClassLoader, expiration might not work reliably 170 * since the ClassLoader may hold on to a cached version of the bundle file.</b> 171 * Prefer {@link ReloadableResourceBundleMessageSource} over 172 * {@link ResourceBundleMessageSource} in such a scenario, in combination with 173 * a non-classpath location. 174 */ 175 public void setCacheSeconds(int cacheSeconds) { 176 this.cacheMillis = cacheSeconds * 1000L; 177 } 178 179 /** 180 * Set the number of milliseconds to cache loaded properties files. 181 * Note that it is common to set seconds instead: {@link #setCacheSeconds}. 182 * <ul> 183 * <li>Default is "-1", indicating to cache forever (just like 184 * {@code java.util.ResourceBundle}). 185 * <li>A positive number will cache loaded properties files for the given 186 * number of milliseconds. This is essentially the interval between refresh checks. 187 * Note that a refresh attempt will first check the last-modified timestamp 188 * of the file before actually reloading it; so if files don't change, this 189 * interval can be set rather low, as refresh attempts will not actually reload. 190 * <li>A value of "0" will check the last-modified timestamp of the file on 191 * every message access. <b>Do not use this in a production environment!</b> 192 * </ul> 193 * @since 4.3 194 * @see #setCacheSeconds 195 */ 196 public void setCacheMillis(long cacheMillis) { 197 this.cacheMillis = cacheMillis; 198 } 199 200 /** 201 * Return the number of milliseconds to cache loaded properties files. 202 * @since 4.3 203 */ 204 protected long getCacheMillis() { 205 return this.cacheMillis; 206 } 207 208}