diff --git a/.editorconfig b/.editorconfig
index a9a165bae..6ef5d690a 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -12,6 +12,9 @@ end_of_line = lf
[{*.bat,*.cmd}]
end_of_line = crlf
+[{*.mustache,*.ftl}]
+insert_final_newline = false
+
[*.java]
indent_size = 4
tab_width = 4
diff --git a/.github/workflows/graalvm-latest.yml b/.github/workflows/graalvm-latest.yml
index bfbfff345..d150aa606 100644
--- a/.github/workflows/graalvm-latest.yml
+++ b/.github/workflows/graalvm-latest.yml
@@ -34,7 +34,9 @@ jobs:
runs-on: ubuntu-latest
strategy:
max-parallel: 6
- matrix: ${{ fromJson(needs.build_matrix.outputs.matrix) }}
+ matrix:
+ java: ['17', '21']
+ native_test_task: ${{ fromJson(needs.build_matrix.outputs.matrix).native_test_task }}
env:
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USERNAME }}
@@ -46,7 +48,7 @@ jobs:
id: pre-build
with:
distribution: 'graalvm'
- java: '17'
+ java: ${{ matrix.java }}
- name: Build Steps
uses: micronaut-projects/github-actions/graalvm/build@master
id: build
@@ -60,4 +62,4 @@ jobs:
uses: micronaut-projects/github-actions/graalvm/post-build@master
id: post-build
with:
- java: '17'
+ java: ${{ matrix.java }}
diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index f60182451..7c0d4941b 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- java: ['17']
+ java: ['17', '21']
env:
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USERNAME }}
@@ -64,7 +64,7 @@ jobs:
./gradlew check --no-daemon --continue
- name: "🔎 Run static analysis"
- if: env.SONAR_TOKEN != ''
+ if: env.SONAR_TOKEN != '' && matrix.java == '17'
run: |
./gradlew sonar
diff --git a/settings.gradle b/settings.gradle
index 2b5613f09..6aca0e2c8 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -9,7 +9,7 @@ pluginManagement {
}
plugins {
- id 'io.micronaut.build.shared.settings' version '6.5.6'
+ id 'io.micronaut.build.shared.settings' version '6.6.0'
}
rootProject.name = 'testresources-parent'
diff --git a/test-resources-server/src/test/resources/logback.xml b/test-resources-server/src/test/resources/logback.xml
index 44b79c40d..0c171e80f 100644
--- a/test-resources-server/src/test/resources/logback.xml
+++ b/test-resources-server/src/test/resources/logback.xml
@@ -11,4 +11,6 @@
+
+
diff --git a/test-resources-testcontainers/src/main/java/io/micronaut/testresources/testcontainers/TestContainers.java b/test-resources-testcontainers/src/main/java/io/micronaut/testresources/testcontainers/TestContainers.java
index 9e5ba0d21..8b9335bdd 100644
--- a/test-resources-testcontainers/src/main/java/io/micronaut/testresources/testcontainers/TestContainers.java
+++ b/test-resources-testcontainers/src/main/java/io/micronaut/testresources/testcontainers/TestContainers.java
@@ -52,6 +52,21 @@ private TestContainers() {
}
+ private static B withLock(String description, Supplier supplier) {
+ LOCK.lock();
+ if (LOGGER.isTraceEnabled()) {
+ LOGGER.trace("Locked for {}", description);
+ }
+ try {
+ return supplier.get();
+ } finally {
+ if (LOGGER.isTraceEnabled()) {
+ LOGGER.trace("Unlocked for {}", description);
+ }
+ LOCK.unlock();
+ }
+ }
+
/**
* Returns a test container and caches it, so that if the same owner
* and properties are requested, we can return an existing container.
@@ -71,19 +86,18 @@ static > T getOrCreate(String requestedP
Map query,
Supplier creator) {
Key key = Key.of(owner, name, Scope.from(query), query);
- LOCK.lock();
- @SuppressWarnings("unchecked")
- T container = (T) CONTAINERS_BY_KEY.get(key);
- if (container == null) {
- container = creator.get();
- LOGGER.info("Starting test container {}", name);
- container.start();
- CONTAINERS_BY_KEY.put(key, container);
- }
- CONTAINERS_BY_PROPERTY.computeIfAbsent(requestedProperty, e -> new LinkedHashSet<>())
- .add(container);
- LOCK.unlock();
- return container;
+ return withLock("getOrCreate", () -> {
+ T container = (T) CONTAINERS_BY_KEY.get(key);
+ if (container == null) {
+ container = creator.get();
+ LOGGER.info("Starting test container {}", name);
+ container.start();
+ CONTAINERS_BY_KEY.put(key, container);
+ }
+ CONTAINERS_BY_PROPERTY.computeIfAbsent(requestedProperty, e -> new LinkedHashSet<>())
+ .add(container);
+ return container;
+ });
}
/**
@@ -101,9 +115,8 @@ public static Map>> listByScope(String id) {
}
private static Map>> listByScope(Scope scope) {
- LOCK.lock();
- try {
- return CONTAINERS_BY_KEY.entrySet()
+ return withLock("listByScope", () ->
+ CONTAINERS_BY_KEY.entrySet()
.stream()
.filter(entry -> scope.includes(entry.getKey().scope))
.collect(Collectors.groupingBy(
@@ -112,26 +125,22 @@ private static Map>> listByScope(Scope scope) {
Map.Entry::getValue,
Collectors.toList()
)
- ));
- } finally {
- LOCK.unlock();
- }
+ ))
+ );
}
+ @SuppressWarnings("java:S6204") // toList() breaks the return type
private static List> filterByScope(Scope scope, Set> containers) {
if (containers.isEmpty()) {
return Collections.emptyList();
}
- LOCK.lock();
- try {
- return CONTAINERS_BY_KEY.entrySet()
+ return withLock("filterByScope", () ->
+ CONTAINERS_BY_KEY.entrySet()
.stream()
.filter(entry -> containers.contains(entry.getValue()) && scope.includes(entry.getKey().scope))
.map(Map.Entry::getValue)
- .collect(Collectors.toList());
- } finally {
- LOCK.unlock();
- }
+ .collect(Collectors.toList())
+ );
}
public static Network network(String name) {
@@ -139,18 +148,18 @@ public static Network network(String name) {
}
public static boolean closeAll() {
- LOCK.lock();
- boolean closed = false;
- for (GenericContainer> container : CONTAINERS_BY_KEY.values()) {
- container.close();
- closed = true;
- }
- CONTAINERS_BY_KEY.clear();
- CONTAINERS_BY_PROPERTY.clear();
- NETWORKS_BY_KEY.values().forEach(Network::close);
- NETWORKS_BY_KEY.clear();
- LOCK.unlock();
- return closed;
+ return withLock("closeAll", () -> {
+ boolean closed = false;
+ for (GenericContainer> container : CONTAINERS_BY_KEY.values()) {
+ container.close();
+ closed = true;
+ }
+ CONTAINERS_BY_KEY.clear();
+ CONTAINERS_BY_PROPERTY.clear();
+ NETWORKS_BY_KEY.values().forEach(Network::close);
+ NETWORKS_BY_KEY.clear();
+ return closed;
+ });
}
public static Map getNetworks() {
@@ -159,44 +168,41 @@ public static Map getNetworks() {
public static boolean closeScope(String id) {
Scope scope = Scope.of(id);
- LOCK.lock();
- boolean closed = false;
- Iterator>> iterator = CONTAINERS_BY_KEY.entrySet().iterator();
- while (iterator.hasNext()) {
- Map.Entry> entry = iterator.next();
- var existingScope = entry.getKey().scope;
- if (scope.includes(existingScope)) {
- iterator.remove();
- GenericContainer> container = entry.getValue();
- LOGGER.debug("Stopping container {}", container.getContainerId());
- container.close();
- closed = true;
- for (Set> value : CONTAINERS_BY_PROPERTY.values()) {
- value.remove(container);
+ return withLock("closeScope", () -> {
+ boolean closed = false;
+ Iterator>> iterator = CONTAINERS_BY_KEY.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry> entry = iterator.next();
+ var existingScope = entry.getKey().scope;
+ if (scope.includes(existingScope)) {
+ iterator.remove();
+ GenericContainer> container = entry.getValue();
+ LOGGER.debug("Stopping container {}", container.getContainerId());
+ container.close();
+ closed = true;
+ for (Set> value : CONTAINERS_BY_PROPERTY.values()) {
+ value.remove(container);
+ }
}
}
- }
- LOCK.unlock();
- return closed;
+ return closed;
+ });
}
public static List> findByRequestedProperty(Scope scope, String property) {
- LOCK.lock();
- try {
+ return withLock("findByRequestedProperty", () -> {
Set> byProperty = CONTAINERS_BY_PROPERTY.getOrDefault(property, Collections.emptySet());
LOGGER.debug("Found {} containers for property {}. All properties: {}", byProperty.size(), property, CONTAINERS_BY_PROPERTY.keySet());
return filterByScope(scope, byProperty);
- } finally {
- LOCK.unlock();
- }
+ });
}
private static final class Key {
private final Class> type;
private final String name;
- private final Scope scope;
private final Map properties;
private final int hashCode;
+ final Scope scope;
private Key(Class> type, String name, Scope scope, Map properties) {
this.type = type;