From 23f0418337d14798b401009e51ada89b56c52b69 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 15 Dec 2016 15:43:40 +0100 Subject: [PATCH] LocaleContextHolder provides support for framework-level default settings Issue: SPR-15017 --- .../context/i18n/LocaleContextHolder.java | 62 ++++++++++++++++--- .../datetime/joda/JodaTimeContextHolder.java | 3 +- .../standard/DateTimeContextHolder.java | 3 +- .../i18n/LocaleContextHolderTests.java | 14 ++++- 4 files changed, 70 insertions(+), 12 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/i18n/LocaleContextHolder.java b/spring-context/src/main/java/org/springframework/context/i18n/LocaleContextHolder.java index 1d727a280d5..fd8875b9a2e 100644 --- a/spring-context/src/main/java/org/springframework/context/i18n/LocaleContextHolder.java +++ b/spring-context/src/main/java/org/springframework/context/i18n/LocaleContextHolder.java @@ -44,10 +44,16 @@ import org.springframework.core.NamedThreadLocal; public abstract class LocaleContextHolder { private static final ThreadLocal localeContextHolder = - new NamedThreadLocal<>("Locale context"); + new NamedThreadLocal<>("LocaleContext"); private static final ThreadLocal inheritableLocaleContextHolder = - new NamedInheritableThreadLocal<>("Locale context"); + new NamedInheritableThreadLocal<>("LocaleContext"); + + // Shared default locale at the framework level + private static Locale defaultLocale; + + // Shared default time zone at the framework level + private static TimeZone defaultTimeZone; /** @@ -152,18 +158,38 @@ public abstract class LocaleContextHolder { setLocaleContext(localeContext, inheritable); } + /** + * Set a shared default locale at the framework level, + * as an alternative to the JVM-wide default locale. + *

NOTE: This can be useful to set an application-level + * default locale which differs from the JVM-wide default locale. + * However, this requires each such application to operate against + * locally deployed Spring Framework jars. Do not deploy Spring + * as a shared library at the server level in such a scenario! + * @param locale the default locale (or {@code null} for none, + * letting lookups fall back to {@link Locale#getDefault()}) + * @since 4.3.5 + * @see #getLocale() + * @see Locale#getDefault() + */ + public static void setDefaultLocale(Locale locale) { + LocaleContextHolder.defaultLocale = locale; + } + /** * Return the Locale associated with the current thread, if any, - * or the system default Locale else. This is effectively a + * or the system default Locale otherwise. This is effectively a * replacement for {@link java.util.Locale#getDefault()}, * able to optionally respect a user-level Locale setting. - *

Note: This method has a fallback to the system default Locale. + *

Note: This method has a fallback to the shared default Locale, + * either at the framework level or at the JVM-wide system level. * If you'd like to check for the raw LocaleContext content * (which may indicate no specific locale through {@code null}, use * {@link #getLocaleContext()} and call {@link LocaleContext#getLocale()} * @return the current Locale, or the system default Locale if no * specific Locale has been associated with the current thread * @see LocaleContext#getLocale() + * @see #setDefaultLocale(Locale) * @see java.util.Locale#getDefault() */ public static Locale getLocale() { @@ -174,7 +200,7 @@ public abstract class LocaleContextHolder { return locale; } } - return Locale.getDefault(); + return (defaultLocale != null ? defaultLocale : Locale.getDefault()); } /** @@ -217,12 +243,31 @@ public abstract class LocaleContextHolder { setLocaleContext(localeContext, inheritable); } + /** + * Set a shared default time zone at the framework level, + * as an alternative to the JVM-wide default time zone. + *

NOTE: This can be useful to set an application-level + * default time zone which differs from the JVM-wide default time zone. + * However, this requires each such application to operate against + * locally deployed Spring Framework jars. Do not deploy Spring + * as a shared library at the server level in such a scenario! + * @param timeZone the default time zone (or {@code null} for none, + * letting lookups fall back to {@link TimeZone#getDefault()}) + * @since 4.3.5 + * @see #getTimeZone() + * @see TimeZone#getDefault() + */ + public static void setDefaultTimeZone(TimeZone timeZone) { + defaultTimeZone = timeZone; + } + /** * Return the TimeZone associated with the current thread, if any, - * or the system default TimeZone else. This is effectively a + * or the system default TimeZone otherwise. This is effectively a * replacement for {@link java.util.TimeZone#getDefault()}, * able to optionally respect a user-level TimeZone setting. - *

Note: This method has a fallback to the system default Locale. + *

Note: This method has a fallback to the shared default TimeZone, + * either at the framework level or at the JVM-wide system level. * If you'd like to check for the raw LocaleContext content * (which may indicate no specific time zone through {@code null}, use * {@link #getLocaleContext()} and call {@link TimeZoneAwareLocaleContext#getTimeZone()} @@ -230,6 +275,7 @@ public abstract class LocaleContextHolder { * @return the current TimeZone, or the system default TimeZone if no * specific TimeZone has been associated with the current thread * @see TimeZoneAwareLocaleContext#getTimeZone() + * @see #setDefaultTimeZone(TimeZone) * @see java.util.TimeZone#getDefault() */ public static TimeZone getTimeZone() { @@ -240,7 +286,7 @@ public abstract class LocaleContextHolder { return timeZone; } } - return TimeZone.getDefault(); + return (defaultTimeZone != null ? defaultTimeZone : TimeZone.getDefault()); } } diff --git a/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeContextHolder.java b/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeContextHolder.java index e6c9412e1cc..631420b2569 100644 --- a/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeContextHolder.java +++ b/spring-context/src/main/java/org/springframework/format/datetime/joda/JodaTimeContextHolder.java @@ -29,11 +29,12 @@ import org.springframework.core.NamedThreadLocal; * @author Keith Donald * @author Juergen Hoeller * @since 3.0 + * @see org.springframework.context.i18n.LocaleContextHolder */ public final class JodaTimeContextHolder { private static final ThreadLocal jodaTimeContextHolder = - new NamedThreadLocal<>("JodaTime Context"); + new NamedThreadLocal<>("JodaTimeContext"); /** diff --git a/spring-context/src/main/java/org/springframework/format/datetime/standard/DateTimeContextHolder.java b/spring-context/src/main/java/org/springframework/format/datetime/standard/DateTimeContextHolder.java index 2fd3cd806a2..8a7c0720400 100644 --- a/spring-context/src/main/java/org/springframework/format/datetime/standard/DateTimeContextHolder.java +++ b/spring-context/src/main/java/org/springframework/format/datetime/standard/DateTimeContextHolder.java @@ -26,11 +26,12 @@ import org.springframework.core.NamedThreadLocal; * * @author Juergen Hoeller * @since 4.0 + * @see org.springframework.context.i18n.LocaleContextHolder */ public final class DateTimeContextHolder { private static final ThreadLocal dateTimeContextHolder = - new NamedThreadLocal<>("DateTime Context"); + new NamedThreadLocal<>("DateTimeContext"); /** diff --git a/spring-context/src/test/java/org/springframework/context/i18n/LocaleContextHolderTests.java b/spring-context/src/test/java/org/springframework/context/i18n/LocaleContextHolderTests.java index 9a1a6578c47..06e51eaf6f6 100644 --- a/spring-context/src/test/java/org/springframework/context/i18n/LocaleContextHolderTests.java +++ b/spring-context/src/test/java/org/springframework/context/i18n/LocaleContextHolderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * 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. @@ -80,6 +80,11 @@ public class LocaleContextHolderTests { assertNull(LocaleContextHolder.getLocaleContext()); assertEquals(Locale.getDefault(), LocaleContextHolder.getLocale()); assertEquals(TimeZone.getDefault(), LocaleContextHolder.getTimeZone()); + + LocaleContextHolder.setDefaultLocale(Locale.GERMAN); + assertEquals(Locale.GERMAN, LocaleContextHolder.getLocale()); + LocaleContextHolder.setDefaultLocale(null); + assertEquals(Locale.getDefault(), LocaleContextHolder.getLocale()); } @Test @@ -99,9 +104,14 @@ public class LocaleContextHolderTests { assertEquals(TimeZone.getTimeZone("GMT+2"), ((TimeZoneAwareLocaleContext) LocaleContextHolder.getLocaleContext()).getTimeZone()); LocaleContextHolder.setTimeZone(null); + assertNull(LocaleContextHolder.getLocaleContext()); assertEquals(Locale.getDefault(), LocaleContextHolder.getLocale()); assertEquals(TimeZone.getDefault(), LocaleContextHolder.getTimeZone()); - assertNull(LocaleContextHolder.getLocaleContext()); + + LocaleContextHolder.setDefaultTimeZone(TimeZone.getTimeZone("GMT+1")); + assertEquals(TimeZone.getTimeZone("GMT+1"), LocaleContextHolder.getTimeZone()); + LocaleContextHolder.setDefaultTimeZone(null); + assertEquals(TimeZone.getDefault(), LocaleContextHolder.getTimeZone()); } @Test