diff --git a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/interceptor/LoginHandlerInterceptor.java b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/interceptor/LoginHandlerInterceptor.java index 49eacaa59c34..e4d292b2cc85 100644 --- a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/interceptor/LoginHandlerInterceptor.java +++ b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/interceptor/LoginHandlerInterceptor.java @@ -91,7 +91,7 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons return false; } request.setAttribute(Constants.SESSION_USER, user); - ThreadLocalContext.getTimezoneThreadLocal().set(user.getTimeZone()); + ThreadLocalContext.setTimezone(user.getTimeZone()); return true; } @@ -100,7 +100,6 @@ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { - ThreadLocalContext.getTimezoneThreadLocal().remove(); int code = response.getStatus(); if (code >= 200 && code < 300) { @@ -113,4 +112,12 @@ public void postHandle(HttpServletRequest request, ApiServerMetrics.incApiResponse5xxCount(); } } + + @Override + public void afterCompletion(HttpServletRequest request, + HttpServletResponse response, + Object handler, + Exception ex) { + ThreadLocalContext.removeTimezone(); + } } diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/thread/ThreadLocalContext.java b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/thread/ThreadLocalContext.java index 9d523d815ac8..14365c9903bf 100644 --- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/thread/ThreadLocalContext.java +++ b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/thread/ThreadLocalContext.java @@ -18,13 +18,47 @@ package org.apache.dolphinscheduler.common.thread; /** - * thread local context + * A utility class to manage timezone context using ThreadLocal. + * This allows each thread to have its own timezone value, which is useful in + * multi-threaded environments such as web applications where each request + * may need to operate in a different timezone. + * + * Note: Always call {@link #removeTimezone()} at the end of a request or task + * to prevent memory leaks and context pollution in thread pool environments. */ public class ThreadLocalContext { - public static final ThreadLocal timezoneThreadLocal = new ThreadLocal<>(); + /** + * ThreadLocal variable to hold the timezone string for the current thread. + * Each thread will have its own copy of the timezone value. + */ + private static final ThreadLocal TIMEZONE_THREAD_LOCAL = new ThreadLocal<>(); + + /** + * Sets the timezone for the current thread. + * + * @param timezone the timezone ID (e.g., "UTC", "Asia/Shanghai", "America/New_York") + */ + public static void setTimezone(String timezone) { + TIMEZONE_THREAD_LOCAL.set(timezone); + } + + /** + * Retrieves the timezone for the current thread. + * + * @return the timezone string set for the current thread, or null if not set + */ + public static String getTimezone() { + return TIMEZONE_THREAD_LOCAL.get(); + } - public static ThreadLocal getTimezoneThreadLocal() { - return timezoneThreadLocal; + /** + * Removes the timezone value for the current thread. + * This method should be called to clean up the thread-local value, especially + * when using thread pools (e.g., in web servers), to prevent memory leaks + * and unintended data leakage between requests. + */ + public static void removeTimezone() { + TIMEZONE_THREAD_LOCAL.remove(); } } diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/DateUtils.java b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/DateUtils.java index 18370ae3788f..50a5afcf3768 100644 --- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/DateUtils.java +++ b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/DateUtils.java @@ -62,13 +62,13 @@ private DateUtils() { * @return local datetime */ private static LocalDateTime date2LocalDateTime(Date date) { - String timezone = ThreadLocalContext.getTimezoneThreadLocal().get(); + String timezone = ThreadLocalContext.getTimezone(); ZoneId zoneId = StringUtils.isNotEmpty(timezone) ? ZoneId.of(timezone) : ZoneId.systemDefault(); return date2LocalDateTime(date, zoneId); } public static String getTimezone() { - String timezone = ThreadLocalContext.getTimezoneThreadLocal().get(); + String timezone = ThreadLocalContext.getTimezone(); return StringUtils.isNotEmpty(timezone) ? timezone : ZoneId.systemDefault().getId(); } @@ -90,7 +90,7 @@ private static LocalDateTime date2LocalDateTime(Date date, ZoneId zoneId) { * @return date */ private static Date localDateTime2Date(LocalDateTime localDateTime) { - String timezone = ThreadLocalContext.getTimezoneThreadLocal().get(); + String timezone = ThreadLocalContext.getTimezone(); ZoneId zoneId = StringUtils.isNotEmpty(timezone) ? ZoneId.of(timezone) : ZoneId.systemDefault(); return localDateTime2Date(localDateTime, zoneId); } diff --git a/dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/DateUtilsTest.java b/dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/DateUtilsTest.java index dc07c1cdaa6a..e05e3500987e 100644 --- a/dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/DateUtilsTest.java +++ b/dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/DateUtilsTest.java @@ -37,12 +37,12 @@ public class DateUtilsTest { @BeforeEach public void before() { - ThreadLocalContext.getTimezoneThreadLocal().remove(); + ThreadLocalContext.removeTimezone(); } @AfterEach public void after() { - ThreadLocalContext.getTimezoneThreadLocal().remove(); + ThreadLocalContext.removeTimezone(); } @Test @@ -242,11 +242,11 @@ public void testGetTimezone() { public void testTimezone() { String time = "2019-01-28 00:00:00"; - ThreadLocalContext.timezoneThreadLocal.set("UTC"); + ThreadLocalContext.setTimezone("UTC"); Date utcDate = DateUtils.stringToDate(time); Assertions.assertEquals(time, DateUtils.dateToString(utcDate)); - ThreadLocalContext.timezoneThreadLocal.set("Asia/Shanghai"); + ThreadLocalContext.setTimezone("Asia/Shanghai"); Date shanghaiDate = DateUtils.stringToDate(time); Assertions.assertEquals(time, DateUtils.dateToString(shanghaiDate));