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;