From f6e1a5de094e6aa450a205f71644693ad59590bd Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 6 Nov 2024 11:21:05 +0000 Subject: [PATCH 1/2] Prefer modified resources over the originals in TestCompiler Previously, when the test compiler had been seeded with a resource file, any modifications to this resource performed during compilation would be lost as this original content would always be returned. This commit updates the DynamicJavaFileManager to always store the dynamic resource in the dynamicResourceFiles map, irrespective of whether it's being created afresh or from some existing resource content. This ensures that any modifications made to the resource can be retrieved later on. Similarly, DynamicClassLoader has been updated to prefer dynamic resource files over any original resource files. This ensures that the resource that it finds reflects any modifications that have been made to it. See gh-33850 --- .../core/test/tools/DynamicClassLoader.java | 10 ++--- .../test/tools/DynamicJavaFileManager.java | 15 ++++---- .../tools/DynamicJavaFileManagerTests.java | 20 +++++++++- .../core/test/tools/TestCompilerTests.java | 37 ++++++++++++++++++- 4 files changed, 67 insertions(+), 15 deletions(-) diff --git a/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicClassLoader.java b/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicClassLoader.java index 73b88758a00..c55d6640119 100644 --- a/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicClassLoader.java +++ b/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -121,14 +121,14 @@ public class DynamicClassLoader extends ClassLoader { return createResourceUrl(name, () -> classBytes); } } - ResourceFile resourceFile = this.resourceFiles.get(name); - if (resourceFile != null) { - return createResourceUrl(resourceFile.getPath(), resourceFile::getBytes); - } DynamicResourceFileObject dynamicResourceFile = this.dynamicResourceFiles.get(name); if (dynamicResourceFile != null && dynamicResourceFile.getBytes() != null) { return createResourceUrl(dynamicResourceFile.getName(), dynamicResourceFile::getBytes); } + ResourceFile resourceFile = this.resourceFiles.get(name); + if (resourceFile != null) { + return createResourceUrl(resourceFile.getPath(), resourceFile::getBytes); + } return super.findResource(name); } diff --git a/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicJavaFileManager.java b/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicJavaFileManager.java index f359069e0cc..4ac7da9ef3d 100644 --- a/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicJavaFileManager.java +++ b/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicJavaFileManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -70,13 +70,12 @@ class DynamicJavaFileManager extends ForwardingJavaFileManager } @Override - public FileObject getFileForOutput(Location location, String packageName, - String relativeName, FileObject sibling) { - ResourceFile resourceFile = this.resourceFiles.get(relativeName); - if (resourceFile != null) { - return new DynamicResourceFileObject(relativeName, resourceFile.getContent()); - } - return this.dynamicResourceFiles.computeIfAbsent(relativeName, DynamicResourceFileObject::new); + public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) { + return this.dynamicResourceFiles.computeIfAbsent(relativeName, name -> { + ResourceFile resourceFile = this.resourceFiles.get(name); + return (resourceFile != null) ? new DynamicResourceFileObject(name, resourceFile.getContent()) + : new DynamicResourceFileObject(name); + }); } @Override diff --git a/spring-core-test/src/test/java/org/springframework/core/test/tools/DynamicJavaFileManagerTests.java b/spring-core-test/src/test/java/org/springframework/core/test/tools/DynamicJavaFileManagerTests.java index 474c506bf70..b32813b26a1 100644 --- a/spring-core-test/src/test/java/org/springframework/core/test/tools/DynamicJavaFileManagerTests.java +++ b/spring-core-test/src/test/java/org/springframework/core/test/tools/DynamicJavaFileManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.core.test.tools; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.util.EnumSet; @@ -154,6 +155,23 @@ class DynamicJavaFileManagerTests { "META-INF/second.properties"); } + @Test + void existingResourceFileCanBeUpdated() throws IOException { + try (InputStream input = getResourceOne().openInputStream()) { + assertThat(input).hasContent("a"); + } + try (OutputStream output = getResourceOne().openOutputStream()) { + output.write('b'); + } + try (InputStream input = getResourceOne().openInputStream()) { + assertThat(input).hasContent("b"); + } + } + + private FileObject getResourceOne() { + return this.fileManager.getFileForOutput(this.location, "", "com/example/one/resource.one", null); + } + private void writeDummyBytecode(JavaFileObject fileObject) throws IOException { try (OutputStream outputStream = fileObject.openOutputStream()) { StreamUtils.copy(DUMMY_BYTECODE, outputStream); diff --git a/spring-core-test/src/test/java/org/springframework/core/test/tools/TestCompilerTests.java b/spring-core-test/src/test/java/org/springframework/core/test/tools/TestCompilerTests.java index 48bd7e8df73..504c5699559 100644 --- a/spring-core-test/src/test/java/org/springframework/core/test/tools/TestCompilerTests.java +++ b/spring-core-test/src/test/java/org/springframework/core/test/tools/TestCompilerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,10 @@ package org.springframework.core.test.tools; +import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; +import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -27,6 +30,8 @@ import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.element.TypeElement; +import javax.tools.FileObject; +import javax.tools.StandardLocation; import com.example.PublicInterface; import org.junit.jupiter.api.Test; @@ -367,6 +372,14 @@ class TestCompilerTests { }); } + @Test + void getUpdatedResourceAsStream() { + SourceFile sourceFile = SourceFile.of(HELLO_WORLD); + TestCompiler.forSystem().withResources(ResourceFile.of("com/example/resource", new byte[] { 'a' })) + .withProcessors(new ResourceModifyingProcessor()).compile(sourceFile, compiled -> assertThat( + compiled.getClassLoader().getResourceAsStream("com/example/resource")).hasContent("b")); + } + private void assertSuppliesHelloWorld(Compiled compiled) { assertThat(compiled.getInstance(Supplier.class).get()).isEqualTo("Hello World!"); } @@ -392,4 +405,26 @@ class TestCompilerTests { } } + @SupportedAnnotationTypes("java.lang.Deprecated") + static class ResourceModifyingProcessor extends AbstractProcessor { + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + try { + FileObject resource = this.processingEnv.getFiler() + .createResource(StandardLocation.CLASS_OUTPUT, "", "com/example/resource"); + try (OutputStream output = resource.openOutputStream()) { + output.write('b'); + } + } + catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + return true; + } + + } + } From 0219ee656f8c352c7c1852547fb708db03530887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Nicoll?= Date: Wed, 6 Nov 2024 23:03:11 +0900 Subject: [PATCH 2/2] Polish "Prefer modified resources over the originals in TestCompiler" See gh-33850 --- .../core/test/tools/DynamicJavaFileManager.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicJavaFileManager.java b/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicJavaFileManager.java index 4ac7da9ef3d..d8d2659fa9e 100644 --- a/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicJavaFileManager.java +++ b/spring-core-test/src/main/java/org/springframework/core/test/tools/DynamicJavaFileManager.java @@ -70,11 +70,12 @@ class DynamicJavaFileManager extends ForwardingJavaFileManager } @Override - public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) { + public FileObject getFileForOutput(Location location, String packageName, + String relativeName, FileObject sibling) { return this.dynamicResourceFiles.computeIfAbsent(relativeName, name -> { ResourceFile resourceFile = this.resourceFiles.get(name); - return (resourceFile != null) ? new DynamicResourceFileObject(name, resourceFile.getContent()) - : new DynamicResourceFileObject(name); + return (resourceFile != null) ? new DynamicResourceFileObject(name, resourceFile.getContent()) : + new DynamicResourceFileObject(name); }); }