Browse Source

Migrate to Java 25

Co-Authored-By: Claude <noreply@anthropic.com>
pull/14976/head
Viacheslav Klimov 6 months ago
parent
commit
8f9c4b70d7
  1. 2
      .github/workflows/license-header-format.yml
  2. 10
      application/src/main/data/json/edge/instructions/install/centos/instructions.md
  3. 10
      application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md
  4. 134
      common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTest.java
  5. 36
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java
  6. 12
      dao/src/main/java/org/thingsboard/server/dao/sql/audit/AuditLogRepository.java
  7. 2
      msa/tb/docker-cassandra/Dockerfile
  8. 2
      msa/tb/docker-postgres/Dockerfile
  9. 4
      packaging/java/build.gradle
  10. 8
      packaging/java/scripts/windows/install.bat
  11. 2
      packaging/js/build.gradle
  12. 20
      pom.xml
  13. 81
      tbel-date-note.md

2
.github/workflows/license-header-format.yml

@ -35,7 +35,7 @@ jobs:
uses: actions/setup-java@v4
with:
distribution: 'corretto' # https://github.com/actions/setup-java?tab=readme-ov-file#supported-distributions
java-version: '21'
java-version: '25'
cache: 'maven' # https://github.com/actions/setup-java?tab=readme-ov-file#caching-sbt-dependencies
- name: License header format

10
application/src/main/data/json/edge/instructions/install/centos/instructions.md

@ -8,15 +8,15 @@ sudo yum install -y nano wget && sudo yum install -y https://dl.fedoraproject.or
{:copy-code}
```
#### Step 1. Install Java 17 (OpenJDK)
ThingsBoard service is running on Java 17. To install OpenJDK 17, follow these instructions:
#### Step 1. Install Java 25 (OpenJDK)
ThingsBoard service is running on Java 25. To install OpenJDK 25, follow these instructions:
```bash
sudo dnf install java-17-openjdk
sudo dnf install java-25-openjdk
{:copy-code}
```
Configure your operating system to use OpenJDK 17 by default. You can configure the default version by running the following command:
Configure your operating system to use OpenJDK 25 by default. You can configure the default version by running the following command:
```bash
sudo update-alternatives --config java
@ -33,7 +33,7 @@ java -version
The expected result is:
```text
openjdk version "17.x.xx"
openjdk version "25.x.xx"
OpenJDK Runtime Environment (...)
OpenJDK 64-Bit Server VM (build ...)
```

10
application/src/main/data/json/edge/instructions/install/ubuntu/instructions.md

@ -1,14 +1,14 @@
Here is the list of commands that can be used to quickly install ThingsBoard Edge on Ubuntu Server and connect to the server.
#### Step 1. Install Java 17 (OpenJDK)
ThingsBoard service is running on Java 17. To install OpenJDK 17, follow these instructions:
#### Step 1. Install Java 25 (OpenJDK)
ThingsBoard service is running on Java 25. To install OpenJDK 25, follow these instructions:
```bash
sudo apt update && sudo apt install openjdk-17-jdk
sudo apt update && sudo apt install openjdk-25-jdk
{:copy-code}
```
Configure your operating system to use OpenJDK 17 by default. You can configure the default version by running the following command:
Configure your operating system to use OpenJDK 25 by default. You can configure the default version by running the following command:
```bash
sudo update-alternatives --config java
@ -25,7 +25,7 @@ java -version
The expected result is:
```text
openjdk version "17.x.xx"
openjdk version "25.x.xx"
OpenJDK Runtime Environment (...)
OpenJDK 64-Bit Server VM (build ...)
```

134
common/script/script-api/src/test/java/org/thingsboard/script/api/tbel/TbDateTest.java

@ -209,7 +209,9 @@ class TbDateTest {
.put("timeZone", "America/New_York")
.put("dateStyle", "full")
.toString()));
Assertions.assertEquals("середа, 6 вересня 2023 р.", d.toLocaleDateString("uk-UA", JacksonUtil.newObjectNode()
// Java 21+ uses narrow no-break space (U+202F) before "р." per CLDR 42+
String expectedDate_ukUA = Runtime.version().feature() >= 21 ? "середа, 6 вересня 2023\u202Fр." : "середа, 6 вересня 2023 р.";
Assertions.assertEquals(expectedDate_ukUA, d.toLocaleDateString("uk-UA", JacksonUtil.newObjectNode()
.put("timeZone", "Europe/Kiev")
.put("dateStyle", "full")
.toString()));
@ -244,12 +246,18 @@ class TbDateTest {
Assertions.assertNotNull(d.toLocaleTimeString());
Assertions.assertNotNull(d.toLocaleTimeString("en-US"));
Assertions.assertEquals("9:04:05 PM", d.toLocaleTimeString("en-US", "America/New_York"));
// Java 21+ uses narrow no-break space (U+202F) before AM/PM per CLDR 42+
String expectedTime_enUS = Runtime.version().feature() >= 21 ? "9:04:05\u202FPM" : "9:04:05 PM";
Assertions.assertEquals(expectedTime_enUS, d.toLocaleTimeString("en-US", "America/New_York"));
Assertions.assertEquals("오후 9:04:05", d.toLocaleTimeString("ko-KR", "America/New_York"));
Assertions.assertEquals("04:04:05", d.toLocaleTimeString( "uk-UA", "Europe/Kiev"));
Assertions.assertEquals("9:04:05 م", d.toLocaleTimeString( "ar-EG", "America/New_York"));
Assertions.assertEquals("9:04:05 PM Eastern Daylight Time", d.toLocaleTimeString("en-US", JacksonUtil.newObjectNode()
// Java 21+ uses narrow no-break space (U+202F) before AM/PM per CLDR 42+
String expectedFullTime_enUS = Runtime.version().feature() >= 21
? "9:04:05\u202FPM Eastern Daylight Time"
: "9:04:05 PM Eastern Daylight Time";
Assertions.assertEquals(expectedFullTime_enUS, d.toLocaleTimeString("en-US", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("timeStyle", "full")
.toString()));
@ -292,14 +300,27 @@ class TbDateTest {
Assertions.assertNotNull(d.toLocaleString());
Assertions.assertNotNull(d.toLocaleString("en-US"));
Assertions.assertEquals("9/5/23, 9:04:05 PM", d.toLocaleString("en-US", "America/New_York"));
// Java 21+ uses narrow no-break space (U+202F) before AM/PM per CLDR 42+
String expectedLocale_enUS = Runtime.version().feature() >= 21 ? "9/5/23, 9:04:05\u202FPM" : "9/5/23, 9:04:05 PM";
Assertions.assertEquals(expectedLocale_enUS, d.toLocaleString("en-US", "America/New_York"));
Assertions.assertEquals("23. 9. 5. 오후 9:04:05", d.toLocaleString("ko-KR", "America/New_York"));
Assertions.assertEquals("06.09.23, 04:04:05", d.toLocaleString( "uk-UA", "Europe/Kiev"));
String expected_ver = Runtime.version().feature() == 11 ? "5\u200F/9\u200F/2023 9:04:05 م" :
"5\u200F/9\u200F/2023, 9:04:05 م";
Assertions.assertEquals(expected_ver, d.toLocaleString( "ar-EG", "America/New_York"));
// Java 11 has no comma, Java 17 uses ASCII comma, Java 21+ uses Arabic comma (U+060C)
String expected_ar;
if (Runtime.version().feature() == 11) {
expected_ar = "5\u200F/9\u200F/2023 9:04:05 م";
} else if (Runtime.version().feature() >= 21) {
expected_ar = "5\u200F/9\u200F/2023\u060C 9:04:05 م";
} else {
expected_ar = "5\u200F/9\u200F/2023, 9:04:05 م";
}
Assertions.assertEquals(expected_ar, d.toLocaleString( "ar-EG", "America/New_York"));
Assertions.assertEquals("Tuesday, September 5, 2023 at 9:04:05 PM Eastern Daylight Time", d.toLocaleString("en-US", JacksonUtil.newObjectNode()
// Java 21+ changed "at" to "," and uses NNBSP before AM/PM per CLDR 42+
String expected_enUS_full = Runtime.version().feature() >= 21
? "Tuesday, September 5, 2023, 9:04:05\u202FPM Eastern Daylight Time"
: "Tuesday, September 5, 2023 at 9:04:05 PM Eastern Daylight Time";
Assertions.assertEquals(expected_enUS_full, d.toLocaleString("en-US", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("dateStyle", "full")
.put("timeStyle", "full")
@ -309,15 +330,26 @@ class TbDateTest {
.put("dateStyle", "full")
.put("timeStyle", "full")
.toString()));
Assertions.assertEquals("середа, 6 вересня 2023 р. о 04:04:05 за східноєвропейським літнім часом", d.toLocaleString("uk-UA", JacksonUtil.newObjectNode()
// Java 21+ changed Ukrainian locale: "о" replaced with "," and NNBSP before "р." per CLDR 42+
String expected_ukUA_full_summer = Runtime.version().feature() >= 21
? "середа, 6 вересня 2023\u202Fр., 04:04:05 за східноєвропейським літнім часом"
: "середа, 6 вересня 2023 р. о 04:04:05 за східноєвропейським літнім часом";
Assertions.assertEquals(expected_ukUA_full_summer, d.toLocaleString("uk-UA", JacksonUtil.newObjectNode()
.put("timeZone", "Europe/Kiev")
.put("dateStyle", "full")
.put("timeStyle", "full")
.toString()));
expected_ver = Runtime.version().feature() == 11 ? "الثلاثاء، 5 سبتمبر 2023 9:04:05 م التوقيت الصيفي الشرقي لأمريكا الشمالية" :
"الثلاثاء، 5 سبتمبر 2023 في 9:04:05 م التوقيت الصيفي الشرقي لأمريكا الشمالية";
Assertions.assertEquals(expected_ver, d.toLocaleString("ar-EG", JacksonUtil.newObjectNode()
// Java 11 has no connector, Java 17 uses "في", Java 21+ uses Arabic comma "،"
String expected_ar_full;
if (Runtime.version().feature() == 11) {
expected_ar_full = "الثلاثاء، 5 سبتمبر 2023 9:04:05 م التوقيت الصيفي الشرقي لأمريكا الشمالية";
} else if (Runtime.version().feature() >= 21) {
expected_ar_full = "الثلاثاء، 5 سبتمبر 2023\u060C 9:04:05 م التوقيت الصيفي الشرقي لأمريكا الشمالية";
} else {
expected_ar_full = "الثلاثاء، 5 سبتمبر 2023 في 9:04:05 م التوقيت الصيفي الشرقي لأمريكا الشمالية";
}
Assertions.assertEquals(expected_ar_full, d.toLocaleString("ar-EG", JacksonUtil.newObjectNode()
.put("timeZone", "America/New_York")
.put("dateStyle", "full")
.put("timeStyle", "full")
@ -825,16 +857,44 @@ class TbDateTest {
@Test
public void toStringAsJs() {
TbDate d1 = new TbDate(1975, 12, 31, 23,15,30, 567,"-04:00");
Assertions.assertEquals("четвер, 1 січня 1976 р. о 06:15:30 за східноєвропейським стандартним часом", d1.toString("uk-UA", "Europe/Kyiv"));
Assertions.assertEquals("Thursday, January 1, 1976 at 6:15:30 AM Eastern European Standard Time", d1.toString("en-US", "Europe/Kyiv"));
Assertions.assertEquals("1976 Jan 1, Thu 06:15:30 Eastern European Time", d1.toString("UTC", "Europe/Kyiv"));
Assertions.assertEquals("Wednesday, December 31, 1975 at 10:15:30 PM Eastern Standard Time", d1.toString("en-US", "America/New_York"));
Assertions.assertEquals("1975 Dec 31, Wed 22:15:30 Eastern Standard Time", d1.toString("GMT", "America/New_York"));
Assertions.assertEquals("1975 Dec 31, Wed 22:15:30 Eastern Standard Time", d1.toString("UTC", "America/New_York"));
// Java 21+ changed Ukrainian locale: "о" (at) replaced with "," and NNBSP before "р." per CLDR 42+
String expected_ukUA_full = Runtime.version().feature() >= 21
? "четвер, 1 січня 1976\u202Fр., 06:15:30 за східноєвропейським стандартним часом"
: "четвер, 1 січня 1976 р. о 06:15:30 за східноєвропейським стандартним часом";
Assertions.assertEquals(expected_ukUA_full, d1.toString("uk-UA", "Europe/Kyiv"));
// Java 21+ changed "at" to "," and uses NNBSP before AM/PM per CLDR 42+
String expected_enUS_Kyiv = Runtime.version().feature() >= 21
? "Thursday, January 1, 1976, 6:15:30\u202FAM Eastern European Standard Time"
: "Thursday, January 1, 1976 at 6:15:30 AM Eastern European Standard Time";
Assertions.assertEquals(expected_enUS_Kyiv, d1.toString("en-US", "Europe/Kyiv"));
// Java 21+ changed timezone display for UTC locale
String expected_UTC_Kyiv = Runtime.version().feature() >= 21
? "1976 Jan 1, Thu 06:15:30 Kyiv (+0)"
: "1976 Jan 1, Thu 06:15:30 Eastern European Time";
Assertions.assertEquals(expected_UTC_Kyiv, d1.toString("UTC", "Europe/Kyiv"));
// Java 21+ changed "at" to "," and uses NNBSP before AM/PM per CLDR 42+
String expected_enUS_NY = Runtime.version().feature() >= 21
? "Wednesday, December 31, 1975, 10:15:30\u202FPM Eastern Standard Time"
: "Wednesday, December 31, 1975 at 10:15:30 PM Eastern Standard Time";
Assertions.assertEquals(expected_enUS_NY, d1.toString("en-US", "America/New_York"));
// Java 21+ changed timezone display for GMT/UTC locales
String expected_GMT_NY = Runtime.version().feature() >= 21
? "1975 Dec 31, Wed 22:15:30 New York (+0)"
: "1975 Dec 31, Wed 22:15:30 Eastern Standard Time";
Assertions.assertEquals(expected_GMT_NY, d1.toString("GMT", "America/New_York"));
Assertions.assertEquals(expected_GMT_NY, d1.toString("UTC", "America/New_York"));
Assertions.assertEquals(d1.toUTCString("UTC"), d1.toUTCString());
Assertions.assertEquals("четвер, 1 січня 1976 р., 03:15:30", d1.toUTCString("uk-UA"));
Assertions.assertEquals("Thursday, January 1, 1976, 3:15:30 AM", d1.toUTCString("en-US"));
// Java 21+ uses NNBSP before "р." per CLDR 42+
String expected_ukUA_utc = Runtime.version().feature() >= 21
? "четвер, 1 січня 1976\u202Fр., 03:15:30"
: "четвер, 1 січня 1976 р., 03:15:30";
Assertions.assertEquals(expected_ukUA_utc, d1.toUTCString("uk-UA"));
// Java 21+ uses NNBSP before AM/PM per CLDR 42+
String expected_enUS_utc = Runtime.version().feature() >= 21
? "Thursday, January 1, 1976, 3:15:30\u202FAM"
: "Thursday, January 1, 1976, 3:15:30 AM";
Assertions.assertEquals(expected_enUS_utc, d1.toUTCString("en-US"));
Assertions.assertEquals("1976-01-01T03:15:30.567Z", d1.toJSON());
Assertions.assertEquals("1976-01-01T03:15:30.567Z", d1.toISOString());
@ -842,22 +902,44 @@ class TbDateTest {
Assertions.assertEquals("1976-01-01 06:15:30", d1.toLocaleString("UTC", "Europe/Kyiv"));
Assertions.assertEquals("01.01.76, 06:15:30", d1.toLocaleString("uk-UA", "Europe/Kyiv"));
Assertions.assertEquals("1975-12-31 22:15:30", d1.toLocaleString("UTC", "America/New_York"));
Assertions.assertEquals("12/31/75, 10:15:30 PM", d1.toLocaleString("en-US", "America/New_York"));
// Java 21+ uses NNBSP before AM/PM per CLDR 42+
String expected_enUS_locale_NY = Runtime.version().feature() >= 21
? "12/31/75, 10:15:30\u202FPM"
: "12/31/75, 10:15:30 PM";
Assertions.assertEquals(expected_enUS_locale_NY, d1.toLocaleString("en-US", "America/New_York"));
Assertions.assertEquals("1976 Jan 1, Thu", d1.toDateString("UTC", "Europe/Kyiv"));
Assertions.assertEquals("четвер, 1 січня 1976 р.", d1.toDateString("uk-UA", "Europe/Kyiv"));
// Java 21+ uses NNBSP before "р." per CLDR 42+
String expected_ukUA_date = Runtime.version().feature() >= 21
? "четвер, 1 січня 1976\u202Fр."
: "четвер, 1 січня 1976 р.";
Assertions.assertEquals(expected_ukUA_date, d1.toDateString("uk-UA", "Europe/Kyiv"));
Assertions.assertEquals("1975 Dec 31, Wed", d1.toDateString("UTC", "America/New_York"));
Assertions.assertEquals("Wednesday, December 31, 1975", d1.toDateString("en-US", "America/New_York"));
Assertions.assertEquals("06:15:30", d1.toLocaleTimeString("uk-UA", "Europe/Kyiv"));
Assertions.assertEquals("06:15:30", d1.toLocaleTimeString("UTC", "Europe/Kyiv"));
Assertions.assertEquals("10:15:30 PM", d1.toLocaleTimeString("en-US", "America/New_York"));
// Java 21+ uses NNBSP before AM/PM per CLDR 42+
String expected_enUS_time = Runtime.version().feature() >= 21 ? "10:15:30\u202FPM" : "10:15:30 PM";
Assertions.assertEquals(expected_enUS_time, d1.toLocaleTimeString("en-US", "America/New_York"));
Assertions.assertEquals("22:15:30", d1.toLocaleTimeString("UTC", "America/New_York"));
Assertions.assertEquals("06:15:30 за східноєвропейським стандартним часом", d1.toTimeString("uk-UA", "Europe/Kyiv"));
Assertions.assertEquals("06:15:30 Eastern European Time", d1.toTimeString("UTC", "Europe/Kyiv"));
Assertions.assertEquals("10:15:30 PM Eastern Standard Time", d1.toTimeString("en-US", "America/New_York"));
Assertions.assertEquals("22:15:30 Eastern Standard Time", d1.toTimeString("UTC", "America/New_York"));
// Java 21+ changed timezone display for UTC locale
String expected_UTC_time_Kyiv = Runtime.version().feature() >= 21
? "06:15:30 Kyiv (+0)"
: "06:15:30 Eastern European Time";
Assertions.assertEquals(expected_UTC_time_Kyiv, d1.toTimeString("UTC", "Europe/Kyiv"));
// Java 21+ uses NNBSP before AM/PM per CLDR 42+
String expected_enUS_timeStr = Runtime.version().feature() >= 21
? "10:15:30\u202FPM Eastern Standard Time"
: "10:15:30 PM Eastern Standard Time";
Assertions.assertEquals(expected_enUS_timeStr, d1.toTimeString("en-US", "America/New_York"));
// Java 21+ changed timezone display for UTC locale
String expected_UTC_time_NY = Runtime.version().feature() >= 21
? "22:15:30 New York (+0)"
: "22:15:30 Eastern Standard Time";
Assertions.assertEquals(expected_UTC_time_NY, d1.toTimeString("UTC", "America/New_York"));
}
@Test

36
dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java

@ -98,10 +98,8 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
"AND ea.entityType = :affectedEntityType " +
"AND (:startTime IS NULL OR (a.createdTime >= :startTime AND ea.createdTime >= :startTime)) " +
"AND (:endTime IS NULL OR (a.createdTime <= :endTime AND ea.createdTime <= :endTime)) " +
"AND ((:#{#alarmTypes == null} = true) OR a.type IN (:alarmTypes)) " + //HHH-15968
"AND ((:#{#alarmSeverities == null} = true) OR a.severity IN (:alarmSeverities)) " + //HHH-15968
// "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " +
// "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " +
"AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " +
"AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " +
"AND ((:clearFilterEnabled) = FALSE OR a.cleared = :clearFilter) " +
"AND ((:ackFilterEnabled) = FALSE OR a.acknowledged = :ackFilter) " +
"AND (:assigneeId IS NULL OR a.assigneeId = :assigneeId) " +
@ -119,10 +117,8 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
"AND ea.entityType = :affectedEntityType " +
"AND (:startTime IS NULL OR (a.createdTime >= :startTime AND ea.createdTime >= :startTime)) " +
"AND (:endTime IS NULL OR (a.createdTime <= :endTime AND ea.createdTime <= :endTime)) " +
"AND ((:#{#alarmTypes == null} = true) OR a.type IN (:alarmTypes)) " + //HHH-15968
"AND ((:#{#alarmSeverities == null} = true) OR a.severity IN (:alarmSeverities)) " + //HHH-15968
// "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " +
// "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " +
"AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " +
"AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " +
"AND ((:clearFilterEnabled) = FALSE OR a.cleared = :clearFilter) " +
"AND ((:ackFilterEnabled) = FALSE OR a.acknowledged = :ackFilter) " +
"AND (:assigneeId IS NULL OR a.assigneeId = :assigneeId) " +
@ -183,10 +179,8 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
"WHERE a.tenantId = :tenantId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
"AND ((:#{#alarmTypes == null} = true) OR a.type IN (:alarmTypes)) " + //HHH-15968
"AND ((:#{#alarmSeverities == null} = true) OR a.severity IN (:alarmSeverities)) " + //HHH-15968
// "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " +
// "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " +
"AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " +
"AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " +
"AND ((:clearFilterEnabled) = FALSE OR a.cleared = :clearFilter) " +
"AND ((:ackFilterEnabled) = FALSE OR a.acknowledged = :ackFilter) " +
"AND (:assigneeId IS NULL OR a.assigneeId = :assigneeId) " +
@ -199,10 +193,8 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
"WHERE a.tenantId = :tenantId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
"AND ((:#{#alarmTypes == null} = true) OR a.type IN (:alarmTypes)) " + //HHH-15968
"AND ((:#{#alarmSeverities == null} = true) OR a.severity IN (:alarmSeverities)) " + //HHH-15968
// "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " +
// "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " +
"AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " +
"AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " +
"AND ((:clearFilterEnabled) = FALSE OR a.cleared = :clearFilter) " +
"AND ((:ackFilterEnabled) = FALSE OR a.acknowledged = :ackFilter) " +
"AND (:assigneeId IS NULL OR a.assigneeId = :assigneeId) " +
@ -263,10 +255,8 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
"WHERE a.tenantId = :tenantId AND a.customerId = :customerId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
"AND ((:#{#alarmTypes == null} = true) OR a.type IN (:alarmTypes)) " + //HHH-15968
"AND ((:#{#alarmSeverities == null} = true) OR a.severity IN (:alarmSeverities)) " + //HHH-15968
// "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " +
// "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " +
"AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " +
"AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " +
"AND ((:clearFilterEnabled) = FALSE OR a.cleared = :clearFilter) " +
"AND ((:ackFilterEnabled) = FALSE OR a.acknowledged = :ackFilter) " +
"AND (:assigneeId IS NULL OR a.assigneeId = :assigneeId) " +
@ -280,10 +270,8 @@ public interface AlarmRepository extends JpaRepository<AlarmEntity, UUID> {
"WHERE a.tenantId = :tenantId AND a.customerId = :customerId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
"AND ((:#{#alarmTypes == null} = true) OR a.type IN (:alarmTypes)) " + //HHH-15968
"AND ((:#{#alarmSeverities == null} = true) OR a.severity IN (:alarmSeverities)) " + //HHH-15968
// "AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " +
// "AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " +
"AND ((:alarmTypes) IS NULL OR a.type IN (:alarmTypes)) " +
"AND ((:alarmSeverities) IS NULL OR a.severity IN (:alarmSeverities)) " +
"AND ((:clearFilterEnabled) = FALSE OR a.cleared = :clearFilter) " +
"AND ((:ackFilterEnabled) = FALSE OR a.acknowledged = :ackFilter) " +
"AND (:assigneeId IS NULL OR a.assigneeId = :assigneeId) " +

12
dao/src/main/java/org/thingsboard/server/dao/sql/audit/AuditLogRepository.java

@ -33,8 +33,7 @@ public interface AuditLogRepository extends JpaRepository<AuditLogEntity, UUID>
"a.tenantId = :tenantId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
"AND ((:#{#actionTypes == null} = true) OR a.actionType IN (:actionTypes)) " + //HHH-15968
// "AND ((:actionTypes) IS NULL OR a.actionType in (:actionTypes)) " +
"AND ((:actionTypes) IS NULL OR a.actionType IN (:actionTypes)) " +
"AND (:textSearch IS NULL OR ilike(a.entityType, CONCAT('%', :textSearch, '%')) = true " +
"OR ilike(a.entityName, CONCAT('%', :textSearch, '%')) = true " +
"OR ilike(a.userName, CONCAT('%', :textSearch, '%')) = true " +
@ -54,8 +53,7 @@ public interface AuditLogRepository extends JpaRepository<AuditLogEntity, UUID>
"AND a.entityType = :entityType AND a.entityId = :entityId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
"AND ((:#{#actionTypes == null} = true) OR a.actionType IN (:actionTypes)) " + //HHH-15968
// "AND ((:actionTypes) IS NULL OR a.actionType in (:actionTypes)) " +
"AND ((:actionTypes) IS NULL OR a.actionType IN (:actionTypes)) " +
"AND (:textSearch IS NULL OR ilike(a.entityName, CONCAT('%', :textSearch, '%')) = true " +
"OR ilike(a.userName, CONCAT('%', :textSearch, '%')) = true " +
"OR ilike(a.actionType, CONCAT('%', :textSearch, '%')) = true " +
@ -75,8 +73,7 @@ public interface AuditLogRepository extends JpaRepository<AuditLogEntity, UUID>
"AND a.customerId = :customerId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
"AND ((:#{#actionTypes == null} = true) OR a.actionType IN (:actionTypes)) " + //HHH-15968
// "AND ((:actionTypes) IS NULL OR a.actionType in (:actionTypes)) " +
"AND ((:actionTypes) IS NULL OR a.actionType IN (:actionTypes)) " +
"AND (:textSearch IS NULL OR ilike(a.entityType, CONCAT('%', :textSearch, '%')) = true " +
"OR ilike(a.entityName, CONCAT('%', :textSearch, '%')) = true " +
"OR ilike(a.userName, CONCAT('%', :textSearch, '%')) = true " +
@ -96,8 +93,7 @@ public interface AuditLogRepository extends JpaRepository<AuditLogEntity, UUID>
"AND a.userId = :userId " +
"AND (:startTime IS NULL OR a.createdTime >= :startTime) " +
"AND (:endTime IS NULL OR a.createdTime <= :endTime) " +
"AND ((:#{#actionTypes == null} = true) OR a.actionType IN (:actionTypes)) " + //HHH-15968
// "AND ((:actionTypes) IS NULL OR a.actionType in (:actionTypes)) " +
"AND ((:actionTypes) IS NULL OR a.actionType IN (:actionTypes)) " +
"AND (:textSearch IS NULL OR ilike(a.entityType, CONCAT('%', :textSearch, '%')) = true " +
"OR ilike(a.entityName, CONCAT('%', :textSearch, '%')) = true " +
"OR ilike(a.actionType, CONCAT('%', :textSearch, '%')) = true " +

2
msa/tb/docker-cassandra/Dockerfile

@ -14,7 +14,7 @@
# limitations under the License.
#
FROM thingsboard/openjdk17:bookworm-slim
FROM thingsboard/openjdk25:trixie-slim
ENV PG_MAJOR=16

2
msa/tb/docker-postgres/Dockerfile

@ -14,7 +14,7 @@
# limitations under the License.
#
FROM thingsboard/openjdk17:bookworm-slim
FROM thingsboard/openjdk25:trixie-slim
ENV PG_MAJOR 12

4
packaging/java/build.gradle

@ -16,7 +16,7 @@
import org.apache.tools.ant.filters.ReplaceTokens
plugins {
id "nebula.ospackage" version "8.6.3"
id "com.netflix.nebula.ospackage" version "12.1.1"
}
buildDir = projectBuildDir
@ -92,7 +92,7 @@ buildRpm {
archiveVersion = projectVersion.replace('-', '')
archiveFileName = "${pkgName}.rpm"
// Support Java 17 (existing), plus Java 21 and Java 25 for RPM-based distros
// Support Java 25 (current), plus Java 21 and Java 17 (backward compatibility) for RPM-based distros
// Keep using RPM boolean expression syntax since .or() chaining is for DEB only
requires("(java-17 or java-17-headless or jre-17 or jre-17-headless or " +
"java-21 or java-21-headless or jre-21 or jre-21-headless or " +

8
packaging/java/scripts/windows/install.bat

@ -7,11 +7,11 @@ setlocal ENABLEEXTENSIONS
for /f tokens^=2-5^ delims^=.-_^" %%j in ('java -fullversion 2^>^&1') do set "jver=%%j%%k"
@ECHO CurrentVersion %jver%
if %jver% NEQ 170 GOTO JAVA_NOT_INSTALLED
if %jver% NEQ 250 GOTO JAVA_NOT_INSTALLED
:JAVA_INSTALLED
@ECHO Java 17 found!
@ECHO Java 25 found!
@ECHO Installing thingsboard ...
SET loadDemo=false
@ -50,8 +50,8 @@ POPD
GOTO END
:JAVA_NOT_INSTALLED
@ECHO Java 17 is not installed. Only Java 17 is supported
@ECHO Please go to https://adoptopenjdk.net/index.html and install Java 17. Then retry installation.
@ECHO Java 25 is not installed. Only Java 25 is supported
@ECHO Please go to https://adoptopenjdk.net/index.html and install Java 25. Then retry installation.
PAUSE
GOTO END

2
packaging/js/build.gradle

@ -16,7 +16,7 @@
import org.apache.tools.ant.filters.ReplaceTokens
plugins {
id "nebula.ospackage" version "8.6.3"
id "com.netflix.nebula.ospackage" version "12.1.1"
}
buildDir = projectBuildDir

20
pom.xml

@ -28,8 +28,8 @@
<inceptionYear>2016</inceptionYear>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>25</maven.compiler.source>
<maven.compiler.target>25</maven.compiler.target>
<main.dir>${basedir}</main.dir>
<pkg.disabled>true</pkg.disabled>
<pkg.process-resources.phase>none</pkg.process-resources.phase>
@ -65,7 +65,7 @@
<protobuf.version>3.25.5</protobuf.version> <!-- A Major v4 does not support by the pubsub yet-->
<grpc.version>1.76.0</grpc.version>
<tbel.version>1.2.8</tbel.version>
<lombok.version>1.18.38</lombok.version>
<lombok.version>1.18.40</lombok.version>
<paho.client.version>1.2.5</paho.client.version>
<paho.mqttv5.client.version>1.2.5</paho.mqttv5.client.version>
<os-maven-plugin.version>1.7.1</os-maven-plugin.version>
@ -111,6 +111,7 @@
<jakarta.el.version>4.0.2</jakarta.el.version>
<antisamy.version>1.7.5</antisamy.version>
<snmp4j.version>3.8.0</snmp4j.version>
<bytebuddy.version>1.18.4</bytebuddy.version>
<langchain4j.version>1.8.0-TB</langchain4j.version>
<error_prone_annotations.version>2.38.0</error_prone_annotations.version>
<animal-sniffer-annotations.version>1.24</animal-sniffer-annotations.version>
@ -605,7 +606,7 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>17</release>
<release>25</release>
<compilerArgs>
<arg>-Xlint:deprecation</arg>
<arg>-Xlint:removal</arg>
@ -663,6 +664,7 @@
<configuration>
<argLine>
-XX:+UseStringDeduplication -XX:MaxGCPauseMillis=200
-XX:+EnableDynamicAgentLoading
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED
-Dqueue.edqs.local.rocksdb_path="target/rocksdb/fork_${surefire.forkNumber}/edqs"
-Dqueue.calculated_fields.rocks_db_path="target/rocksdb/fork_${surefire.forkNumber}/cf"
@ -913,6 +915,16 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>${bytebuddy.version}</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
<version>${bytebuddy.version}</version>
</dependency>
<dependency>
<groupId>org.thingsboard.langchain4j</groupId>
<artifactId>langchain4j-bom</artifactId>

81
tbel-date-note.md

@ -0,0 +1,81 @@
## Release Note: TBEL Date Formatting Changes (Java 21+)
### Summary
When upgrading ThingsBoard to Java 21 or later, users may notice changes in locale-formatted date/time strings produced by the `TbDate` class in TBEL scripts. These changes are caused by updates to the Unicode CLDR (Common Locale Data Repository) in Java 21+.
### What Changed
| Format | Java 17 | Java 21+ |
|--------|---------|----------|
| Time with AM/PM | `9:04:05 PM` | `9:04:05 PM` (narrow no-break space before AM/PM) |
| Full datetime (English) | `Tuesday, September 5, 2023 at 9:04:05 PM` | `Tuesday, September 5, 2023, 9:04:05 PM` |
| Full datetime (Ukrainian) | `середа, 6 вересня 2023 р. о 04:04:05` | `середа, 6 вересня 2023 р., 04:04:05` |
| Short datetime (Arabic) | `5/9/2023, 9:04:05 م` | `5/9/2023، 9:04:05 م` (Arabic comma) |
| UTC timezone display | `Eastern European Time` | `Kyiv (+0)` |
### Impact
TBEL scripts that rely on exact string matching or parsing of locale-formatted dates may behave differently after upgrading. For example:
```javascript
// ⚠️ May break after upgrade - string comparison
var dateStr = new Date(ts).toLocaleString("en-US", "America/New_York");
if (dateStr == "9/5/23, 9:04:05 PM") {
// Will fail - invisible character difference
}
// ⚠️ May break after upgrade - string splitting
var parts = new Date(ts).toLocaleTimeString("en-US", tz).split(" ");
// "PM" now preceded by narrow no-break space (U+202F), not regular space
```
### Recommendations
1. **Use ISO formats for comparisons and storage**
```javascript
// ✅ Stable across Java versions
var dateStr = new Date(ts).toISOString(); // "2023-09-05T21:04:05Z"
var dateJson = new Date(ts).toJSON(); // "2023-09-05T21:04:05.000Z"
```
2. **Use explicit patterns for consistent formatting**
```javascript
// ✅ Explicit pattern - stable output
var dateStr = new Date(ts).toLocaleString("en-US", '{"pattern": "M/d/yyyy, h:mm:ss a"}');
```
3. **Use numeric getters for comparisons**
```javascript
// ✅ Compare numeric values instead of strings
var d = new Date(ts);
if (d.getHours() == 21 && d.getMinutes() == 4) { ... }
```
4. **Avoid string equality checks on formatted dates**
```javascript
// ❌ Avoid
if (date.toLocaleString() == storedDateString) { ... }
// ✅ Prefer
if (date.getTime() == storedTimestamp) { ... }
```
### Affected Methods
The following `TbDate` methods may produce different output:
- `toLocaleString()`
- `toLocaleDateString()`
- `toLocaleTimeString()`
- `toString()`
- `toDateString()`
- `toTimeString()`
- `toUTCString()`
### Unaffected Methods
These methods produce consistent output across Java versions:
- `toISOString()`
- `toJSON()`
- `getTime()` / `valueOf()`
- All numeric getters (`getFullYear()`, `getMonth()`, `getDate()`, `getHours()`, etc.)
Loading…
Cancel
Save