FINERACT-2094: Update to Java 21

modernize locale instantiations (fixes an error found by the modernizer plugin) - use Locale.of() instead of new Locale()

use recommended gradle toolchain syntax to specify Java class file format version see https://docs.gradle.org/current/userguide/java_plugin.html#toolchain_and_compatibility

mark html5 for later fix (it's the default)

use dummy byte arrays to work around mockito exception

```
org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: class java.nio.ByteBuffer.

If you're not sure why you're getting this error, please open an issue on GitHub.

Java               : 21
JVM vendor name    : Ubuntu
JVM vendor version : 21.0.6+7-Ubuntu-122.04.1
JVM name           : OpenJDK 64-Bit Server VM
JVM version        : 21.0.6+7-Ubuntu-122.04.1
JVM info           : mixed mode, sharing
OS name            : Linux
OS version         : 5.15.0-136-generic

You are seeing this disclaimer because Mockito is configured to create inlined mocks.
You can learn about inline mocks and their limitations under item #39 of the Mockito class javadoc.

Underlying exception : org.mockito.exceptions.base.MockitoException: Unsupported settings with this type 'java.nio.ByteBuffer'
	at app//org.apache.fineract.infrastructure.event.external.jobs.SendAsynchronousEventsTaskletTest.givenBatchSize2WhenEventSendFailsThenExecutionStops(SendAsynchronousEventsTaskletTest.java:170)
	at java.base@21.0.6/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base@21.0.6/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base@21.0.6/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: org.mockito.exceptions.base.MockitoException: Unsupported settings with this type 'java.nio.ByteBuffer'
	at app//net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:168)
	at app//net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:399)
	at app//net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:190)
	at app//net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:410)
	... 4 more
```

see also: https://stackoverflow.com/questions/47046996/java-lang-unsupportedoperationexception-when-mocking-java-nio-bytebuffer-class

wrap byte arrays with ByteBuffer

fix:

```
/home/adamm/git/apache/fineract/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/jobs/SendAsynchronousEventsTaskletTest.java:148: error: incompatible types: byte[] cannot be converted to ByteBuffer
                "anidempotencyKey", "aSchema", "dummy".getBytes());
                                                               ^
/home/adamm/git/apache/fineract/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/jobs/SendAsynchronousEventsTaskletTest.java:170: error: incompatible types: byte[] cannot be converted to ByteBuffer
                "anidempotencyKey", "aSchema", "dummy".getBytes());
                                                               ^
/home/adamm/git/apache/fineract/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/jobs/SendAsynchronousEventsTaskletTest.java:189: error: incompatible types: byte[] cannot be converted to ByteBuffer
                "anidempotencyKey", "aSchema", "dummy".getBytes());
                                                               ^
/home/adamm/git/apache/fineract/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/jobs/SendAsynchronousEventsTaskletTest.java:210: error: incompatible types: byte[] cannot be converted to ByteBuffer
                "anidempotencyKey", "aSchema", "dummy".getBytes());
```

...and use StandardCharsets with getBytes
This commit is contained in:
Victor Romero
2025-04-17 15:01:00 +02:00
committed by Adam Saghy
parent 60bc3acaf1
commit 9f4f1d4f17
25 changed files with 56 additions and 55 deletions
+2 -2
View File
@@ -12,10 +12,10 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4
with:
java-version: '17'
java-version: '21'
distribution: 'zulu'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
@@ -12,10 +12,10 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4
with:
java-version: '17'
java-version: '21'
distribution: 'zulu'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
+2 -2
View File
@@ -12,10 +12,10 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4
with:
java-version: '17'
java-version: '21'
distribution: 'zulu'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
@@ -10,10 +10,10 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4
with:
java-version: '17'
java-version: '21'
distribution: 'zulu'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
+2 -2
View File
@@ -28,10 +28,10 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4
with:
java-version: '17'
java-version: '21'
distribution: 'zulu'
- name: Setup Gradle and Validate Wrapper
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
+2 -2
View File
@@ -28,10 +28,10 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4
with:
java-version: '17'
java-version: '21'
distribution: 'zulu'
- name: Setup Gradle and Validate Wrapper
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
+2 -2
View File
@@ -29,10 +29,10 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4
with:
java-version: '17'
java-version: '21'
distribution: 'zulu'
- name: Setup Gradle and Validate Wrapper
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
+2 -2
View File
@@ -12,10 +12,10 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4
with:
java-version: '17'
java-version: '21'
distribution: 'zulu'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
+2 -2
View File
@@ -18,10 +18,10 @@ jobs:
with:
fetch-depth: 0
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4
with:
java-version: '17'
java-version: '21'
distribution: 'zulu'
- name: Setup Gradle
+2 -2
View File
@@ -12,10 +12,10 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4
with:
java-version: '17'
java-version: '21'
distribution: 'zulu'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
+2 -2
View File
@@ -12,10 +12,10 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4
with:
java-version: '17'
java-version: '21'
distribution: 'zulu'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
+2 -2
View File
@@ -22,10 +22,10 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4
with:
java-version: '17'
java-version: '21'
distribution: 'zulu'
- name: Setup Gradle and Validate Wrapper
uses: gradle/actions/setup-gradle@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
+2
View File
@@ -19,6 +19,8 @@
# https://www.gitpod.io
# https://www.gitpod.io/docs/41_Config_Gitpod_File/
# https://github.com/gitpod-io/workspace-images/tree/master/mysql
#
# FIXME - dead code? see https://github.com/apache/fineract/pull/4590#issuecomment-2809760331
image: gitpod/workspace-mysql:2022-08-04-13-40-17
ports:
+3 -3
View File
@@ -25,7 +25,7 @@ If you are interested in contributing to this project, but perhaps don't quite k
REQUIREMENTS
============
* `Java >= 17` (Azul Zulu JVM is tested by our CI on GitHub Actions)
* `Java >= 21` (Azul Zulu JVM is tested by our CI on GitHub Actions)
* MariaDB `11.5.2`
You can run the required version of the database server in a container, instead of having to install it, like this:
@@ -38,7 +38,7 @@ and stop and destroy it like this:
<br>Beware that this database container database keeps its state inside the container and not on the host filesystem. It is lost when you destroy (rm) this container. This is typically fine for development. See [Caveats: Where to Store Data on the database container documentation](https://hub.docker.com/_/mariadb) re. how to make it persistent instead of ephemeral.<br>
Tomcat v9 is only required if you wish to deploy the Fineract WAR to a separate external servlet container. Note that you do not require to install Tomcat to develop Fineract, or to run it in production if you use the self-contained JAR, which transparently embeds a servlet container using Spring Boot. (Until FINERACT-730, Tomcat 7/8 were also supported, but now Tomcat 9 is required.)
Tomcat v10 is only required if you wish to deploy the Fineract WAR to a separate external servlet container. Note that you do not require to install Tomcat to develop Fineract, or to run it in production if you use the self-contained JAR, which transparently embeds a servlet container using Spring Boot. (Until FINERACT-730, Tomcat 7/8 were also supported, but now Tomcat 10 is required.)
<br>IMPORTANT: If you use MySQL or MariaDB
============
@@ -137,7 +137,7 @@ FINERACT_SECURITY_2FA_ENABLED=true
============
1. Clone the repository or download and extract the archive file to your local directory.
2. Run `./gradlew :fineract-war:clean :fineract-war:war` to build a traditional WAR file which will be created at `fineract-war/build/libs` directory.
3. Deploy this WAR to your Tomcat v9 Servlet Container.
3. Deploy this WAR to your Tomcat v10 Servlet Container.
We recommend using the JAR instead of the WAR file deployment, because it's much easier.
+7 -9
View File
@@ -355,8 +355,10 @@ configure(project.fineractJavaProjects) {
apply plugin: 'idea'
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
toolchain {
languageVersion = JavaLanguageVersion.of(21)
vendor = JvmVendorSpec.AZUL
}
withSourcesJar()
withJavadocJar()
}
@@ -402,11 +404,6 @@ configure(project.fineractJavaProjects) {
group = 'org.apache.fineract'
/* define the valid syntax level for source files */
// sourceCompatibility = JavaVersion.VERSION_17
// /* define binary compatibility version */
// targetCompatibility = JavaVersion.VERSION_17
/* http://stackoverflow.com/questions/19653311/jpa-repository-works-in-idea-and-production-but-not-in-gradle */
sourceSets.main.output.resourcesDir = sourceSets.main.java.classesDirectory
sourceSets.test.output.resourcesDir = sourceSets.test.java.classesDirectory
@@ -741,10 +738,11 @@ configure(project.fineractJavaProjects) {
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
options.encoding = 'UTF-8'
// FIXME - html5 is the default since sometime before Java 17
// Disable strict checking to prevent build failures on invalid javadoc
options.addBooleanOption('html5', true)
// Add this if you're using Java 17 records or other modern features
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) {
// Add this if you're using Java 21 records or other modern features
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_21)) {
options.addBooleanOption('html5', true)
}
// Ignore any errors during javadoc generation
+1 -1
View File
@@ -24,7 +24,7 @@ apply from: "${rootDir}/buildSrc/src/main/groovy/org.apache.fineract.dependencie
jib {
from {
image = 'azul/zulu-openjdk-alpine:17'
image = 'azul/zulu-openjdk-alpine:21'
platforms {
platform {
architecture = System.getProperty("os.arch").equals("aarch64")?"arm64":"amd64"
@@ -53,19 +53,19 @@ public abstract class AbstractAuditableWithUTCDateTimeCustom<T extends Serializa
private static final long serialVersionUID = 141481953116476081L;
@Column(name = CREATED_BY_DB_FIELD, updatable = false, nullable = false)
@Setter(onMethod = @__(@Override))
@Setter(onMethod_ = @Override)
private Long createdBy;
@Column(name = CREATED_DATE_DB_FIELD, updatable = false, nullable = false)
@Setter(onMethod = @__(@Override))
@Setter(onMethod_ = @Override)
private OffsetDateTime createdDate;
@Column(name = LAST_MODIFIED_BY_DB_FIELD, nullable = false)
@Setter(onMethod = @__(@Override))
@Setter(onMethod_ = @Override)
private Long lastModifiedBy;
@Column(name = LAST_MODIFIED_DATE_DB_FIELD, nullable = false)
@Setter(onMethod = @__(@Override))
@Setter(onMethod_ = @Override)
private OffsetDateTime lastModifiedDate;
@Override
@@ -54,12 +54,12 @@ public abstract class AbstractPersistableCustom<T extends Serializable> implemen
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Getter(onMethod = @__(@Override))
@Getter(onMethod_ = @Override)
private T id;
@Transient
@Setter(value = AccessLevel.NONE)
@Getter(onMethod = @__(@Override))
@Getter(onMethod_ = @Override)
private boolean isNew = true;
@PrePersist
@@ -105,7 +105,7 @@ public final class GoogleGsonSerializerHelper {
public static void registerTypeAdapters(final GsonBuilder builder) {
builder.registerTypeAdapter(java.util.Date.class, new DateAdapter());
builder.registerTypeAdapter(LocalDate.class, new LocalDateAdapter());
// NOTE: was missing, necessary for GSON serialization with JDK 17's restrictive module access
// NOTE: was missing, necessary for GSON serialization with JDK 21's restrictive module access
builder.registerTypeAdapter(LocalTime.class, new LocalTimeAdapter());
builder.registerTypeAdapter(ZonedDateTime.class, new JodaDateTimeAdapter());
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
@@ -819,7 +819,7 @@ public class JsonParserHelper {
dataValidationErrors);
}
return new Locale(languageCode.toLowerCase(), courntryCode.toUpperCase(), variantCode);
return Locale.of(languageCode.toLowerCase(), courntryCode.toUpperCase(), variantCode);
}
private Locale extractLocaleValue(final JsonObject object) {
@@ -32,9 +32,9 @@ public class BasicPasswordEncodablePlatformUser implements PlatformUser {
@Getter
private Long id;
@Getter(onMethod = @__(@Override))
@Getter(onMethod_ = @Override)
private String username;
@Getter(onMethod = @__(@Override))
@Getter(onMethod_ = @Override)
private String password;
@Override
+1 -1
View File
@@ -258,7 +258,7 @@ bootJar {
jib {
from {
image = 'azul/zulu-openjdk-alpine:17'
image = 'azul/zulu-openjdk-alpine:21'
platforms {
platform {
architecture = System.getProperty("os.arch").equals("aarch64")?"arm64":"amd64"
@@ -46,7 +46,7 @@ public class DateSerializer implements JsonSerializer<LocalDate> {
public JsonElement serialize(LocalDate src, Type typeOfSrc, JsonSerializationContext context) {
DateTimeFormatter formatter;
if (StringUtils.isNotEmpty(localeCode)) {
formatter = DateTimeFormatter.ofPattern(dateFormat, new Locale(localeCode));
formatter = DateTimeFormatter.ofPattern(dateFormat, Locale.of(localeCode));
} else {
formatter = DateTimeFormatter.ofPattern(dateFormat);
}
@@ -890,7 +890,7 @@ public class LoanAssemblerImpl implements LoanAssembler {
loan.setClosedOnDate(withdrawnOn);
loan.setClosedBy(currentUser);
final Locale locale = new Locale(command.locale());
final Locale locale = Locale.of(command.locale());
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
actualChanges.put(Loan.PARAM_STATUS, LoanEnumerations.status(loan.getStatus()));
@@ -912,7 +912,7 @@ public class LoanAssemblerImpl implements LoanAssembler {
loan.setClosedOnDate(rejectedOn);
loan.setClosedBy(currentUser);
final Locale locale = new Locale(command.locale());
final Locale locale = Locale.of(command.locale());
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
actualChanges.put(Loan.PARAM_STATUS, LoanEnumerations.status(loan.getStatus()));
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
@@ -145,7 +146,7 @@ class SendAsynchronousEventsTaskletTest {
createExternalEventView("aType", "aCategory", "aSchema", new byte[0], "aIdempotencyKey", 1L));
// Dummy Message
MessageV1 dummyMessage = new MessageV1(1L, "aSource", "aType", "nocategory", "aCreateDate", "aBusinessDate", "aTenantId",
"anidempotencyKey", "aSchema", Mockito.mock(ByteBuffer.class));
"anidempotencyKey", "aSchema", ByteBuffer.wrap("dummy".getBytes(StandardCharsets.UTF_8)));
when(repository.findByStatusOrderByBusinessDateAscIdAsc(Mockito.any(), Mockito.any())).thenReturn(events);
when(messageFactory.createMessage(Mockito.any())).thenReturn(dummyMessage);
@@ -167,7 +168,7 @@ class SendAsynchronousEventsTaskletTest {
createExternalEventView("aType", "aCategory", "aSchema", new byte[0], "aIdempotencyKey", 1L),
createExternalEventView("aType", "aCategory", "aSchema", new byte[0], "aIdempotencyKey", 1L));
MessageV1 dummyMessage = new MessageV1(1L, "aSource", "aType", "nocategory", "aCreateDate", "aBusinessDate", "aTenantId",
"anidempotencyKey", "aSchema", Mockito.mock(ByteBuffer.class));
"anidempotencyKey", "aSchema", ByteBuffer.wrap("dummy".getBytes(StandardCharsets.UTF_8)));
when(repository.findByStatusOrderByBusinessDateAscIdAsc(Mockito.any(), Mockito.any())).thenReturn(events);
when(messageFactory.createMessage(Mockito.any())).thenReturn(dummyMessage);
when(byteBufferConverter.convert(Mockito.any(ByteBuffer.class))).thenReturn(new byte[0]);
@@ -186,7 +187,7 @@ class SendAsynchronousEventsTaskletTest {
List<ExternalEventView> events = Arrays
.asList(createExternalEventView("aType", "aCategory", "aSchema", new byte[0], "aIdempotencyKey", 1L));
MessageV1 dummyMessage = new MessageV1(1L, "aSource", "aType", "nocategory", "aCreateDate", "aBusinessDate", "aTenantId",
"anidempotencyKey", "aSchema", Mockito.mock(ByteBuffer.class));
"anidempotencyKey", "aSchema", ByteBuffer.wrap("dummy".getBytes(StandardCharsets.UTF_8)));
when(repository.findByStatusOrderByBusinessDateAscIdAsc(Mockito.any(), Mockito.any())).thenReturn(events);
when(messageFactory.createMessage(Mockito.any())).thenReturn(dummyMessage);
when(byteBufferConverter.convert(Mockito.any(ByteBuffer.class))).thenReturn(new byte[0]);
@@ -207,7 +208,7 @@ class SendAsynchronousEventsTaskletTest {
List<ExternalEventView> events = Arrays
.asList(createExternalEventView("aType", "aCategory", "aSchema", new byte[0], "aIdempotencyKey", null));
MessageV1 dummyMessage = new MessageV1(1L, "aSource", "aType", "nocategory", "aCreateDate", "aBusinessDate", "aTenantId",
"anidempotencyKey", "aSchema", Mockito.mock(ByteBuffer.class));
"anidempotencyKey", "aSchema", ByteBuffer.wrap("dummy".getBytes(StandardCharsets.UTF_8)));
when(repository.findByStatusOrderByBusinessDateAscIdAsc(Mockito.any(), Mockito.any())).thenReturn(events);
when(messageFactory.createMessage(Mockito.any())).thenReturn(dummyMessage);
byte[] byteMsg = new byte[0];