Source Code Cross Referenced for ReloadableResourceBundleMessageSource.java in  » J2EE » spring-framework-2.0.6 » org » springframework » context » support » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » J2EE » spring framework 2.0.6 » org.springframework.context.support 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * Copyright 2002-2007 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:         *      http://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:
017:        package org.springframework.context.support;
018:
019:        import java.io.File;
020:        import java.io.IOException;
021:        import java.io.InputStream;
022:        import java.io.InputStreamReader;
023:        import java.text.MessageFormat;
024:        import java.util.ArrayList;
025:        import java.util.HashMap;
026:        import java.util.Iterator;
027:        import java.util.List;
028:        import java.util.Locale;
029:        import java.util.Map;
030:        import java.util.Properties;
031:
032:        import org.springframework.context.ResourceLoaderAware;
033:        import org.springframework.core.io.DefaultResourceLoader;
034:        import org.springframework.core.io.Resource;
035:        import org.springframework.core.io.ResourceLoader;
036:        import org.springframework.util.Assert;
037:        import org.springframework.util.DefaultPropertiesPersister;
038:        import org.springframework.util.PropertiesPersister;
039:        import org.springframework.util.StringUtils;
040:
041:        /**
042:         * {@link org.springframework.context.MessageSource} implementation that
043:         * accesses resource bundles using specified basenames. This class uses
044:         * {@link java.util.Properties} instances as its custom data structure for
045:         * messages, loading them via a {@link org.springframework.util.PropertiesPersister}
046:         * strategy: The default strategy is capable of loading properties files
047:         * with a specific character encoding, if desired.
048:         *
049:         * <p>In contrast to {@link ResourceBundleMessageSource}, this class supports
050:         * reloading of properties files through the {@link #setCacheSeconds "cacheSeconds"}
051:         * setting, and also through programmatically clearing the properties cache.
052:         * Since application servers typically cache all files loaded from the classpath,
053:         * it is necessary to store resources somewhere else (for example, in the
054:         * "WEB-INF" directory of a web app). Otherwise changes of files in the
055:         * classpath will <i>not</i> be reflected in the application.
056:         *
057:         * <p>Note that the base names set as {@link #setBasenames "basenames"} property
058:         * are treated in a slightly different fashion than the "basenames" property of
059:         * {@link ResourceBundleMessageSource}. It follows the basic ResourceBundle rule of not
060:         * specifying file extension or language codes, but can refer to any Spring resource
061:         * location (instead of being restricted to classpath resources). With a "classpath:"
062:         * prefix, resources can still be loaded from the classpath, but "cacheSeconds" values
063:         * other than "-1" (caching forever) will not work in this case.
064:         *
065:         * <p>This MessageSource implementation is usually slightly faster than
066:         * {@link ResourceBundleMessageSource}, which builds on {@link java.util.ResourceBundle}
067:         * - in the default mode, i.e. when caching forever. With "cacheSeconds" set to 1,
068:         * message lookup takes about twice as long - with the benefit that changes in
069:         * individual properties files are detected with a maximum delay of 1 second.
070:         * Higher "cacheSeconds" values usually <i>do not</i> make a significant difference.
071:         *
072:         * <p>This MessageSource can easily be used outside of an
073:         * {@link org.springframework.context.ApplicationContext}: It will use a
074:         * {@link org.springframework.core.io.DefaultResourceLoader} as default,
075:         * simply getting overridden with the ApplicationContext's resource loader
076:         * if running in a context. It does not have any other specific dependencies.
077:         *
078:         * <p>Thanks to Thomas Achleitner for providing the initial implementation of
079:         * this message source!
080:         *
081:         * @author Juergen Hoeller
082:         * @see #setCacheSeconds
083:         * @see #setBasenames
084:         * @see #setDefaultEncoding
085:         * @see #setFileEncodings
086:         * @see #setPropertiesPersister
087:         * @see #setResourceLoader
088:         * @see org.springframework.util.DefaultPropertiesPersister
089:         * @see org.springframework.core.io.DefaultResourceLoader
090:         * @see ResourceBundleMessageSource
091:         * @see java.util.ResourceBundle
092:         */
093:        public class ReloadableResourceBundleMessageSource extends
094:                AbstractMessageSource implements  ResourceLoaderAware {
095:
096:            private static final String PROPERTIES_SUFFIX = ".properties";
097:
098:            private static final String XML_SUFFIX = ".xml";
099:
100:            private String[] basenames = new String[0];
101:
102:            private String defaultEncoding;
103:
104:            private Properties fileEncodings;
105:
106:            private boolean fallbackToSystemLocale = true;
107:
108:            private long cacheMillis = -1;
109:
110:            private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();
111:
112:            private ResourceLoader resourceLoader = new DefaultResourceLoader();
113:
114:            /** Cache to hold filename lists per Locale */
115:            private final Map cachedFilenames = new HashMap();
116:
117:            /** Cache to hold already loaded properties per filename */
118:            private final Map cachedProperties = new HashMap();
119:
120:            /** Cache to hold merged loaded properties per basename */
121:            private final Map cachedMergedProperties = new HashMap();
122:
123:            /**
124:             * Set a single basename, following the basic ResourceBundle convention of
125:             * not specifying file extension or language codes, but in contrast to
126:             * {@link ResourceBundleMessageSource} referring to a Spring resource location:
127:             * e.g. "WEB-INF/messages" for "WEB-INF/messages.properties",
128:             * "WEB-INF/messages_en.properties", etc.
129:             * <p>As of Spring 1.2.2, XML properties files are also supported:
130:             * e.g. "WEB-INF/messages" will find and load "WEB-INF/messages.xml",
131:             * "WEB-INF/messages_en.xml", etc as well. Note that this will only
132:             * work on JDK 1.5+.
133:             * @param basename the single basename
134:             * @see #setBasenames
135:             * @see org.springframework.core.io.ResourceEditor
136:             * @see java.util.ResourceBundle
137:             */
138:            public void setBasename(String basename) {
139:                setBasenames(new String[] { basename });
140:            }
141:
142:            /**
143:             * Set an array of basenames, each following the basic ResourceBundle convention
144:             * of not specifying file extension or language codes, but in contrast to
145:             * {@link ResourceBundleMessageSource} referring to a Spring resource location:
146:             * e.g. "WEB-INF/messages" for "WEB-INF/messages.properties",
147:             * "WEB-INF/messages_en.properties", etc.
148:             * <p>As of Spring 1.2.2, XML properties files are also supported:
149:             * e.g. "WEB-INF/messages" will find and load "WEB-INF/messages.xml",
150:             * "WEB-INF/messages_en.xml", etc as well. Note that this will only
151:             * work on JDK 1.5+.
152:             * <p>The associated resource bundles will be checked sequentially
153:             * when resolving a message code. Note that message definitions in a
154:             * <i>previous</i> resource bundle will override ones in a later bundle,
155:             * due to the sequential lookup.
156:             * @param basenames an array of basenames
157:             * @see #setBasename
158:             * @see java.util.ResourceBundle
159:             */
160:            public void setBasenames(String[] basenames) {
161:                if (basenames != null) {
162:                    this .basenames = new String[basenames.length];
163:                    for (int i = 0; i < basenames.length; i++) {
164:                        String basename = basenames[i];
165:                        Assert.hasText(basename, "Basename must not be empty");
166:                        this .basenames[i] = basename.trim();
167:                    }
168:                } else {
169:                    this .basenames = new String[0];
170:                }
171:            }
172:
173:            /**
174:             * Set the default charset to use for parsing properties files.
175:             * Used if no file-specific charset is specified for a file.
176:             * <p>Default is none, using the <code>java.util.Properties</code>
177:             * default encoding.
178:             * <p>Only applies to classic properties files, not to XML files.
179:             * @param defaultEncoding the default charset
180:             * @see #setFileEncodings
181:             * @see org.springframework.util.PropertiesPersister#load
182:             */
183:            public void setDefaultEncoding(String defaultEncoding) {
184:                this .defaultEncoding = defaultEncoding;
185:            }
186:
187:            /**
188:             * Set per-file charsets to use for parsing properties files.
189:             * <p>Only applies to classic properties files, not to XML files.
190:             * @param fileEncodings Properties with filenames as keys and charset
191:             * names as values. Filenames have to match the basename syntax,
192:             * with optional locale-specific appendices: e.g. "WEB-INF/messages"
193:             * or "WEB-INF/messages_en".
194:             * @see #setBasenames
195:             * @see org.springframework.util.PropertiesPersister#load
196:             */
197:            public void setFileEncodings(Properties fileEncodings) {
198:                this .fileEncodings = fileEncodings;
199:            }
200:
201:            /**
202:             * Set whether to fall back to the system Locale if no files for a specific
203:             * Locale have been found. Default is "true"; if this is turned off, the only
204:             * fallback will be the default file (e.g. "messages.properties" for
205:             * basename "messages").
206:             * <p>Falling back to the system Locale is the default behavior of
207:             * <code>java.util.ResourceBundle</code>. However, this is often not
208:             * desirable in an application server environment, where the system Locale
209:             * is not relevant to the application at all: Set this flag to "false"
210:             * in such a scenario.
211:             */
212:            public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) {
213:                this .fallbackToSystemLocale = fallbackToSystemLocale;
214:            }
215:
216:            /**
217:             * Set the number of seconds to cache loaded properties files.
218:             * <ul>
219:             * <li>Default is "-1", indicating to cache forever (just like
220:             * <code>java.util.ResourceBundle</code>).
221:             * <li>A positive number will cache loaded properties files for the given
222:             * number of seconds. This is essentially the interval between refresh checks.
223:             * Note that a refresh attempt will first check the last-modified timestamp
224:             * of the file before actually reloading it; so if files don't change, this
225:             * interval can be set rather low, as refresh attempts will not actually reload.
226:             * <li>A value of "0" will check the last-modified timestamp of the file on
227:             * every message access. <b>Do not use this in a production environment!</b>
228:             * </ul>
229:             */
230:            public void setCacheSeconds(int cacheSeconds) {
231:                this .cacheMillis = (cacheSeconds * 1000);
232:            }
233:
234:            /**
235:             * Set the PropertiesPersister to use for parsing properties files.
236:             * <p>The default is a DefaultPropertiesPersister.
237:             * @see org.springframework.util.DefaultPropertiesPersister
238:             */
239:            public void setPropertiesPersister(
240:                    PropertiesPersister propertiesPersister) {
241:                this .propertiesPersister = (propertiesPersister != null ? propertiesPersister
242:                        : new DefaultPropertiesPersister());
243:            }
244:
245:            /**
246:             * Set the ResourceLoader to use for loading bundle properties files.
247:             * <p>The default is a DefaultResourceLoader. Will get overridden by the
248:             * ApplicationContext if running in a context, as it implements the
249:             * ResourceLoaderAware interface. Can be manually overridden when
250:             * running outside of an ApplicationContext.
251:             * @see org.springframework.core.io.DefaultResourceLoader
252:             * @see org.springframework.context.ResourceLoaderAware
253:             */
254:            public void setResourceLoader(ResourceLoader resourceLoader) {
255:                this .resourceLoader = (resourceLoader != null ? resourceLoader
256:                        : new DefaultResourceLoader());
257:            }
258:
259:            /**
260:             * Resolves the given message code as key in the retrieved bundle files,
261:             * returning the value found in the bundle as-is (without MessageFormat parsing).
262:             */
263:            protected String resolveCodeWithoutArguments(String code,
264:                    Locale locale) {
265:                if (this .cacheMillis < 0) {
266:                    PropertiesHolder propHolder = getMergedProperties(locale);
267:                    String result = propHolder.getProperty(code);
268:                    if (result != null) {
269:                        return result;
270:                    }
271:                } else {
272:                    for (int i = 0; i < this .basenames.length; i++) {
273:                        List filenames = calculateAllFilenames(
274:                                this .basenames[i], locale);
275:                        for (int j = 0; j < filenames.size(); j++) {
276:                            String filename = (String) filenames.get(j);
277:                            PropertiesHolder propHolder = getProperties(filename);
278:                            String result = propHolder.getProperty(code);
279:                            if (result != null) {
280:                                return result;
281:                            }
282:                        }
283:                    }
284:                }
285:                return null;
286:            }
287:
288:            /**
289:             * Resolves the given message code as key in the retrieved bundle files,
290:             * using a cached MessageFormat instance per message code.
291:             */
292:            protected MessageFormat resolveCode(String code, Locale locale) {
293:                if (this .cacheMillis < 0) {
294:                    PropertiesHolder propHolder = getMergedProperties(locale);
295:                    MessageFormat result = propHolder.getMessageFormat(code,
296:                            locale);
297:                    if (result != null) {
298:                        return result;
299:                    }
300:                } else {
301:                    for (int i = 0; i < this .basenames.length; i++) {
302:                        List filenames = calculateAllFilenames(
303:                                this .basenames[i], locale);
304:                        for (int j = 0; j < filenames.size(); j++) {
305:                            String filename = (String) filenames.get(j);
306:                            PropertiesHolder propHolder = getProperties(filename);
307:                            MessageFormat result = propHolder.getMessageFormat(
308:                                    code, locale);
309:                            if (result != null) {
310:                                return result;
311:                            }
312:                        }
313:                    }
314:                }
315:                return null;
316:            }
317:
318:            /**
319:             * Get a PropertiesHolder that contains the actually visible properties
320:             * for a Locale, after merging all specified resource bundles.
321:             * Either fetches the holder from the cache or freshly loads it.
322:             * <p>Only used when caching resource bundle contents forever, i.e.
323:             * with cacheSeconds < 0. Therefore, merged properties are always
324:             * cached forever.
325:             */
326:            protected PropertiesHolder getMergedProperties(Locale locale) {
327:                synchronized (this .cachedMergedProperties) {
328:                    PropertiesHolder mergedHolder = (PropertiesHolder) this .cachedMergedProperties
329:                            .get(locale);
330:                    if (mergedHolder != null) {
331:                        return mergedHolder;
332:                    }
333:                    Properties mergedProps = new Properties();
334:                    mergedHolder = new PropertiesHolder(mergedProps, -1);
335:                    for (int i = this .basenames.length - 1; i >= 0; i--) {
336:                        List filenames = calculateAllFilenames(
337:                                this .basenames[i], locale);
338:                        for (int j = filenames.size() - 1; j >= 0; j--) {
339:                            String filename = (String) filenames.get(j);
340:                            PropertiesHolder propHolder = getProperties(filename);
341:                            if (propHolder.getProperties() != null) {
342:                                mergedProps.putAll(propHolder.getProperties());
343:                            }
344:                        }
345:                    }
346:                    this .cachedMergedProperties.put(locale, mergedHolder);
347:                    return mergedHolder;
348:                }
349:            }
350:
351:            /**
352:             * Calculate all filenames for the given bundle basename and Locale.
353:             * Will calculate filenames for the given Locale, the system Locale
354:             * (if applicable), and the default file.
355:             * @param basename the basename of the bundle
356:             * @param locale the locale
357:             * @return the List of filenames to check
358:             * @see #setFallbackToSystemLocale
359:             * @see #calculateFilenamesForLocale
360:             */
361:            protected List calculateAllFilenames(String basename, Locale locale) {
362:                synchronized (this .cachedFilenames) {
363:                    Map localeMap = (Map) this .cachedFilenames.get(basename);
364:                    if (localeMap != null) {
365:                        List filenames = (List) localeMap.get(locale);
366:                        if (filenames != null) {
367:                            return filenames;
368:                        }
369:                    }
370:                    List filenames = new ArrayList(7);
371:                    filenames.addAll(calculateFilenamesForLocale(basename,
372:                            locale));
373:                    if (this .fallbackToSystemLocale
374:                            && !locale.equals(Locale.getDefault())) {
375:                        List fallbackFilenames = calculateFilenamesForLocale(
376:                                basename, Locale.getDefault());
377:                        for (Iterator it = fallbackFilenames.iterator(); it
378:                                .hasNext();) {
379:                            String fallbackFilename = (String) it.next();
380:                            if (!filenames.contains(fallbackFilename)) {
381:                                // Entry for fallback locale that isn't already in filenames list.
382:                                filenames.add(fallbackFilename);
383:                            }
384:                        }
385:                    }
386:                    filenames.add(basename);
387:                    if (localeMap != null) {
388:                        localeMap.put(locale, filenames);
389:                    } else {
390:                        localeMap = new HashMap();
391:                        localeMap.put(locale, filenames);
392:                        this .cachedFilenames.put(basename, localeMap);
393:                    }
394:                    return filenames;
395:                }
396:            }
397:
398:            /**
399:             * Calculate the filenames for the given bundle basename and Locale,
400:             * appending language code, country code, and variant code.
401:             * E.g.: basename "messages", Locale "de_AT_oo" -> "messages_de_AT_OO",
402:             * "messages_de_AT", "messages_de".
403:             * @param basename the basename of the bundle
404:             * @param locale the locale
405:             * @return the List of filenames to check
406:             */
407:            protected List calculateFilenamesForLocale(String basename,
408:                    Locale locale) {
409:                List result = new ArrayList(3);
410:                String language = locale.getLanguage();
411:                String country = locale.getCountry();
412:                String variant = locale.getVariant();
413:                StringBuffer temp = new StringBuffer(basename);
414:
415:                if (language.length() > 0) {
416:                    temp.append('_').append(language);
417:                    result.add(0, temp.toString());
418:                }
419:
420:                if (country.length() > 0) {
421:                    temp.append('_').append(country);
422:                    result.add(0, temp.toString());
423:                }
424:
425:                if (variant.length() > 0) {
426:                    temp.append('_').append(variant);
427:                    result.add(0, temp.toString());
428:                }
429:
430:                return result;
431:            }
432:
433:            /**
434:             * Get a PropertiesHolder for the given filename, either from the
435:             * cache or freshly loaded.
436:             * @param filename the bundle filename (basename + Locale)
437:             * @return the current PropertiesHolder for the bundle
438:             */
439:            protected PropertiesHolder getProperties(String filename) {
440:                synchronized (this .cachedProperties) {
441:                    PropertiesHolder propHolder = (PropertiesHolder) this .cachedProperties
442:                            .get(filename);
443:                    if (propHolder != null
444:                            && (propHolder.getRefreshTimestamp() < 0 || propHolder
445:                                    .getRefreshTimestamp() > System
446:                                    .currentTimeMillis()
447:                                    - this .cacheMillis)) {
448:                        // up to date
449:                        return propHolder;
450:                    }
451:                    return refreshProperties(filename, propHolder);
452:                }
453:            }
454:
455:            /**
456:             * Refresh the PropertiesHolder for the given bundle filename.
457:             * The holder can be <code>null</code> if not cached before, or a timed-out cache entry
458:             * (potentially getting re-validated against the current last-modified timestamp).
459:             * @param filename the bundle filename (basename + Locale)
460:             * @param propHolder the current PropertiesHolder for the bundle
461:             */
462:            protected PropertiesHolder refreshProperties(String filename,
463:                    PropertiesHolder propHolder) {
464:                long refreshTimestamp = (this .cacheMillis < 0) ? -1 : System
465:                        .currentTimeMillis();
466:
467:                Resource resource = this .resourceLoader.getResource(filename
468:                        + PROPERTIES_SUFFIX);
469:                if (!resource.exists()) {
470:                    resource = this .resourceLoader.getResource(filename
471:                            + XML_SUFFIX);
472:                }
473:
474:                if (resource.exists()) {
475:                    try {
476:                        long fileTimestamp = -1;
477:
478:                        if (this .cacheMillis >= 0) {
479:                            // Last-modified timestamp of file will just be read if caching with timeout.
480:                            File file = null;
481:                            try {
482:                                file = resource.getFile();
483:                            } catch (IOException ex) {
484:                                // Probably a class path resource: cache it forever.
485:                                if (logger.isDebugEnabled()) {
486:                                    logger
487:                                            .debug(
488:                                                    resource
489:                                                            + " could not be resolved in the file system - assuming that is hasn't changed",
490:                                                    ex);
491:                                }
492:                                file = null;
493:                            }
494:                            if (file != null) {
495:                                fileTimestamp = file.lastModified();
496:                                if (fileTimestamp == 0) {
497:                                    throw new IOException("File ["
498:                                            + file.getAbsolutePath()
499:                                            + "] does not exist");
500:                                }
501:                                if (propHolder != null
502:                                        && propHolder.getFileTimestamp() == fileTimestamp) {
503:                                    if (logger.isDebugEnabled()) {
504:                                        logger
505:                                                .debug("Re-caching properties for filename ["
506:                                                        + filename
507:                                                        + "] - file hasn't been modified");
508:                                    }
509:                                    propHolder
510:                                            .setRefreshTimestamp(refreshTimestamp);
511:                                    return propHolder;
512:                                }
513:                            }
514:                        }
515:
516:                        Properties props = loadProperties(resource, filename);
517:                        propHolder = new PropertiesHolder(props, fileTimestamp);
518:                    }
519:
520:                    catch (IOException ex) {
521:                        if (logger.isWarnEnabled()) {
522:                            logger.warn("Could not parse properties file ["
523:                                    + resource.getFilename() + "]: "
524:                                    + ex.getMessage(), ex);
525:                        }
526:                        // Empty holder representing "not valid".
527:                        propHolder = new PropertiesHolder();
528:                    }
529:                }
530:
531:                else {
532:                    // Resource does not exist.
533:                    if (logger.isDebugEnabled()) {
534:                        logger.debug("No properties file found for ["
535:                                + filename
536:                                + "] - neither plain properties nor XML");
537:                    }
538:                    // Empty holder representing "not found".
539:                    propHolder = new PropertiesHolder();
540:                }
541:
542:                propHolder.setRefreshTimestamp(refreshTimestamp);
543:                this .cachedProperties.put(filename, propHolder);
544:                return propHolder;
545:            }
546:
547:            /**
548:             * Load the properties from the given resource.
549:             * @param resource the resource to load from
550:             * @param filename the original bundle filename (basename + Locale)
551:             * @return the populated Properties instance
552:             * @throws IOException if properties loading failed
553:             */
554:            protected Properties loadProperties(Resource resource,
555:                    String filename) throws IOException {
556:                InputStream is = resource.getInputStream();
557:                Properties props = new Properties();
558:                try {
559:                    if (resource.getFilename().endsWith(XML_SUFFIX)) {
560:                        if (logger.isDebugEnabled()) {
561:                            logger.debug("Loading properties ["
562:                                    + resource.getFilename() + "]");
563:                        }
564:                        this .propertiesPersister.loadFromXml(props, is);
565:                    } else {
566:                        String encoding = null;
567:                        if (this .fileEncodings != null) {
568:                            encoding = this .fileEncodings.getProperty(filename);
569:                        }
570:                        if (encoding == null) {
571:                            encoding = this .defaultEncoding;
572:                        }
573:                        if (encoding != null) {
574:                            if (logger.isDebugEnabled()) {
575:                                logger.debug("Loading properties ["
576:                                        + resource.getFilename()
577:                                        + "] with encoding '" + encoding + "'");
578:                            }
579:                            this .propertiesPersister.load(props,
580:                                    new InputStreamReader(is, encoding));
581:                        } else {
582:                            if (logger.isDebugEnabled()) {
583:                                logger.debug("Loading properties ["
584:                                        + resource.getFilename() + "]");
585:                            }
586:                            this .propertiesPersister.load(props, is);
587:                        }
588:                    }
589:                    return props;
590:                } finally {
591:                    is.close();
592:                }
593:            }
594:
595:            /**
596:             * Clear the resource bundle cache.
597:             * Subsequent resolve calls will lead to reloading of the properties files.
598:             */
599:            public void clearCache() {
600:                logger.debug("Clearing entire resource bundle cache");
601:                synchronized (this .cachedProperties) {
602:                    this .cachedProperties.clear();
603:                }
604:                synchronized (this .cachedMergedProperties) {
605:                    this .cachedMergedProperties.clear();
606:                }
607:            }
608:
609:            /**
610:             * Clear the resource bundle caches of this MessageSource and all its ancestors.
611:             * @see #clearCache
612:             */
613:            public void clearCacheIncludingAncestors() {
614:                clearCache();
615:                if (getParentMessageSource() instanceof  ReloadableResourceBundleMessageSource) {
616:                    ((ReloadableResourceBundleMessageSource) getParentMessageSource())
617:                            .clearCacheIncludingAncestors();
618:                }
619:            }
620:
621:            public String toString() {
622:                return getClass().getName()
623:                        + ": basenames=["
624:                        + StringUtils
625:                                .arrayToCommaDelimitedString(this .basenames)
626:                        + "]";
627:            }
628:
629:            /**
630:             * PropertiesHolder for caching.
631:             * Stores the last-modified timestamp of the source file for efficient
632:             * change detection, and the timestamp of the last refresh attempt
633:             * (updated every time the cache entry gets re-validated).
634:             */
635:            protected class PropertiesHolder {
636:
637:                private Properties properties;
638:
639:                private long fileTimestamp = -1;
640:
641:                private long refreshTimestamp = -1;
642:
643:                /** Cache to hold already generated MessageFormats per message code */
644:                private final Map cachedMessageFormats = new HashMap();
645:
646:                public PropertiesHolder(Properties properties,
647:                        long fileTimestamp) {
648:                    this .properties = properties;
649:                    this .fileTimestamp = fileTimestamp;
650:                }
651:
652:                public PropertiesHolder() {
653:                }
654:
655:                public Properties getProperties() {
656:                    return properties;
657:                }
658:
659:                public long getFileTimestamp() {
660:                    return fileTimestamp;
661:                }
662:
663:                public void setRefreshTimestamp(long refreshTimestamp) {
664:                    this .refreshTimestamp = refreshTimestamp;
665:                }
666:
667:                public long getRefreshTimestamp() {
668:                    return refreshTimestamp;
669:                }
670:
671:                public String getProperty(String code) {
672:                    if (this .properties == null) {
673:                        return null;
674:                    }
675:                    return this .properties.getProperty(code);
676:                }
677:
678:                public MessageFormat getMessageFormat(String code, Locale locale) {
679:                    if (this .properties == null) {
680:                        return null;
681:                    }
682:                    synchronized (this .cachedMessageFormats) {
683:                        Map localeMap = (Map) this .cachedMessageFormats
684:                                .get(code);
685:                        if (localeMap != null) {
686:                            MessageFormat result = (MessageFormat) localeMap
687:                                    .get(locale);
688:                            if (result != null) {
689:                                return result;
690:                            }
691:                        }
692:                        String msg = this .properties.getProperty(code);
693:                        if (msg != null) {
694:                            if (localeMap == null) {
695:                                localeMap = new HashMap();
696:                                this .cachedMessageFormats.put(code, localeMap);
697:                            }
698:                            MessageFormat result = createMessageFormat(msg,
699:                                    locale);
700:                            localeMap.put(locale, result);
701:                            return result;
702:                        }
703:                        return null;
704:                    }
705:                }
706:            }
707:
708:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.