Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Index Java runtime classes in Maven and Gradle plugins #2002

Merged
merged 1 commit into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions tools/gradle-plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,10 @@ plugins {

group = "io.smallrye"

if (JavaVersion.current().isJava9Compatible()) {
compileJava.options.compilerArgs.addAll(['--release', '8'])
}

compileJava {
options.encoding = 'UTF-8'
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
sourceCompatibility = '11'
targetCompatibility = '11'
}

compileTestJava {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class Configs implements SmallryeOpenApiProperties {
final MapProperty<String, String> scanResourceClasses;
final Property<String> outputFileTypeFilter;
final Property<String> encoding;
final ListProperty<String> includeStandardJavaModules;

Configs(ObjectFactory objects) {
configProperties = objects.fileProperty();
Expand Down Expand Up @@ -99,6 +100,7 @@ class Configs implements SmallryeOpenApiProperties {
scanResourceClasses = objects.mapProperty(String.class, String.class);
outputFileTypeFilter = objects.property(String.class).convention("ALL");
encoding = objects.property(String.class).convention(StandardCharsets.UTF_8.name());
includeStandardJavaModules = objects.listProperty(String.class);
}

Configs(ObjectFactory objects, SmallryeOpenApiExtension ext) {
Expand Down Expand Up @@ -137,6 +139,7 @@ class Configs implements SmallryeOpenApiProperties {
scanResourceClasses = objects.mapProperty(String.class, String.class).convention(ext.getScanResourceClasses());
outputFileTypeFilter = objects.property(String.class).convention(ext.getOutputFileTypeFilter());
encoding = objects.property(String.class).convention(ext.getEncoding());
includeStandardJavaModules = objects.listProperty(String.class).convention(ext.getIncludeStandardJavaModules());
}

Config asMicroprofileConfig() {
Expand Down Expand Up @@ -349,4 +352,7 @@ public Property<String> getEncoding() {
return encoding;
}

public ListProperty<String> getIncludeStandardJavaModules() {
return includeStandardJavaModules;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@

import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
Expand All @@ -32,12 +37,24 @@ public GradleDependencyIndexCreator(Logger logger) {
this.logger = logger;
}

IndexView createIndex(Set<File> dependencies, FileCollection classesDirs)
IndexView createIndex(SmallryeOpenApiTask task)
throws IOException {

Set<File> dependencies = task.getScanDependenciesDisable().get().booleanValue()
? Collections.emptySet()
: task.getClasspath().getFiles();
FileCollection classesDirs = task.getClassesDirs();

List<Entry<File, Duration>> indexDurations = new ArrayList<>();
List<IndexView> indexes = new ArrayList<>();

List<String> includeStandardJavaModules = task.getIncludeStandardJavaModules().getOrElse(Collections.emptyList());
logger.info("includeStandardJavaModules: " + includeStandardJavaModules);

for (String moduleName : includeStandardJavaModules) {
indexes.add(indexJdkModule(moduleName));
}

for (File f : classesDirs.getFiles()) {
indexes.add(indexModuleClasses(f));
}
Expand Down Expand Up @@ -65,6 +82,37 @@ IndexView createIndex(Set<File> dependencies, FileCollection classesDirs)
return CompositeIndex.create(indexes);
}

private Index indexJdkModule(String moduleName) throws IOException {
Indexer indexer = new Indexer();
FileSystem jrt = FileSystems.getFileSystem(URI.create("jrt:/"));

for (Path root : jrt.getRootDirectories()) {
try (var walker = Files.walk(root)) {
walker
.filter(path -> path.startsWith("/modules/" + moduleName))
.filter(path -> path.getFileName().toString().endsWith(".class"))
.map(path -> {
try {
return Files.newInputStream(path);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
})
.forEach(stream -> {
try {
indexer.index(stream);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

return indexer.complete();
}

private Index index(File artifact) throws IOException {
Result result = JarIndexer.createJarIndex(artifact, new Indexer(), false,
false, false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,9 @@ public interface SmallryeOpenApiProperties {
* Output encoding for openapi document.
*/
Property<String> getEncoding();

/**
* List of standard Java modules that should be made available to annotation scanning for introspection.
*/
ListProperty<String> getIncludeStandardJavaModules();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Stream;

import javax.inject.Inject;
Expand All @@ -33,6 +31,7 @@
import org.gradle.api.tasks.CacheableTask;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.PathSensitive;
Expand Down Expand Up @@ -93,13 +92,7 @@ public SmallryeOpenApiTask(
public void generate() {
try {
clearOutput();

Set<File> dependencies = properties.scanDependenciesDisable.get().booleanValue()
? Collections.emptySet()
: classpath.getFiles();

IndexView index = new GradleDependencyIndexCreator(getLogger()).createIndex(dependencies,
classesDirs);
IndexView index = new GradleDependencyIndexCreator(getLogger()).createIndex(this);
SmallRyeOpenAPI openAPI = generateOpenAPI(index, resourcesSrcDirs);
write(openAPI);
} catch (Exception ex) {
Expand All @@ -108,6 +101,16 @@ public void generate() {
}
}

@Internal
FileCollection getClasspath() {
return this.classpath;
}

@Internal
FileCollection getClassesDirs() {
return this.classesDirs;
}

private SmallRyeOpenAPI generateOpenAPI(IndexView index, FileCollection resourcesSrcDirs) {
return SmallRyeOpenAPI.builder()
.withConfig(properties.asMicroprofileConfig())
Expand Down Expand Up @@ -460,4 +463,11 @@ public Property<String> getOutputFileTypeFilter() {
public Property<String> getEncoding() {
return properties.encoding;
}

@Input
@Optional
@Override
public ListProperty<String> getIncludeStandardJavaModules() {
return properties.includeStandardJavaModules;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@
import org.gradle.testfixtures.ProjectBuilder;
import org.gradle.testkit.runner.GradleRunner;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.CleanupMode;
import org.junit.jupiter.api.io.TempDir;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.TextNode;

import io.smallrye.openapi.api.OpenApiConfig.OperationIdStrategy;

Expand Down Expand Up @@ -138,7 +141,7 @@ void taskPropertiesInheritance() {
}

@Test
void simpleProject(@TempDir Path buildDir) throws Exception {
void simpleProject(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path buildDir) throws Exception {
// "Simple" Gradle project
smokeProject(buildDir, false, SmallryeOpenApiPlugin.TASK_NAME);
}
Expand All @@ -156,13 +159,13 @@ void simpleProjectWithJustJsonOutput(@TempDir Path buildDir) throws Exception {
}

@Test
void quarkusProjectGenApiOnly(@TempDir Path buildDir) throws Exception {
void quarkusProjectGenApiOnly(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path buildDir) throws Exception {
// Quarkus Gradle project, just call the generateOpenApiSpec task
smokeProject(buildDir, true, SmallryeOpenApiPlugin.TASK_NAME);
}

@Test
void quarkusProject(@TempDir Path buildDir) throws Exception {
void quarkusProject(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path buildDir) throws Exception {
// Quarkus Gradle project, perform a "full Quarkus build"
smokeProject(buildDir, true, "quarkusBuild");
}
Expand Down Expand Up @@ -190,8 +193,8 @@ void smokeProject(Path buildDir, boolean withQuarkus, String taskName, String ou
"}",
"",
"dependencies {",
" implementation(\"javax.ws.rs:javax.ws.rs-api:2.1.1\")",
" implementation(\"org.eclipse.microprofile.openapi:microprofile-openapi-api:3.0\")",
" implementation(\"jakarta.ws.rs:jakarta.ws.rs-api:3.1.0\")",
" implementation(\"org.eclipse.microprofile.openapi:microprofile-openapi-api:4.0.1\")",
"}",
"",
"smallryeOpenApi {",
Expand All @@ -210,6 +213,7 @@ void smokeProject(Path buildDir, boolean withQuarkus, String taskName, String ou
" operationIdStrategy.set(OperationIdStrategy.METHOD)",
" filter.set(\"testcases.CustomOASFilter\")",
" outputFileTypeFilter.set(\"" + outputFileTypeFilter + "\")",
" includeStandardJavaModules.set([ \"java.base\" ])",
"}"));

Path javaDir = Paths.get("src/main/java/testcases");
Expand All @@ -218,12 +222,13 @@ void smokeProject(Path buildDir, boolean withQuarkus, String taskName, String ou
Files.write(buildDir.resolve(javaDir.resolve("DummyJaxRs.java")),
asList("package testcases;",
"",
"import javax.ws.rs.GET;",
"import javax.ws.rs.Path;",
"import javax.ws.rs.Produces;",
"import javax.ws.rs.core.MediaType;",
"import jakarta.ws.rs.GET;",
"import jakarta.ws.rs.Path;",
"import jakarta.ws.rs.Produces;",
"import jakarta.ws.rs.core.MediaType;",
"import org.eclipse.microprofile.openapi.annotations.Operation;",
"import org.eclipse.microprofile.openapi.annotations.media.Content;",
"import org.eclipse.microprofile.openapi.annotations.media.Schema;",
"import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;",
"import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;",
"",
Expand All @@ -235,10 +240,11 @@ void smokeProject(Path buildDir, boolean withQuarkus, String taskName, String ou
" @APIResponses({",
" @APIResponse(",
" description = \"Dummy get thing.\",",
" content = @Content(mediaType = \"application/text\")",
" content = @Content(mediaType = \"application/text\",",
" schema = @Schema(implementation = java.util.concurrent.TimeUnit.class))",
" )})",
" public String dummyThing() {",
" return \"foo\";",
" public java.util.concurrent.TimeUnit dummyThing() {",
" return java.util.concurrent.TimeUnit.HOURS;",
" }",
"}"));

Expand Down Expand Up @@ -309,6 +315,9 @@ private static void checkGeneratedFiles(Path buildDir, String expectedOutputFile

JsonNode paths = root.get("paths");
assertThat(paths.get("/mypath").get("get").get("operationId").asText()).isEqualTo("dummyThing");

JsonNode schemas = root.get("components").get("schemas");
assertThat(((ArrayNode) schemas.get("TimeUnit").get("enum"))).contains(new TextNode("HOURS"));
}

if ("YAML".equals(expectedOutputFileType)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ public class GenerateSchemaMojo extends AbstractMojo {
@Parameter(defaultValue = "jar", property = "includeDependenciesTypes")
private List<String> includeDependenciesTypes;

/**
* List of standard Java modules that should be made available to annotation
* scanning for introspection.
*/
@Parameter(property = "includeStandardJavaModules")
private List<String> includeStandardJavaModules;

/**
* Skip execution of the plugin.
*/
Expand Down Expand Up @@ -279,7 +286,7 @@ public void execute() throws MojoExecutionException {
if (!skip) {
try {
IndexView index = mavenDependencyIndexCreator.createIndex(mavenProject, scanDependenciesDisable,
includeDependenciesScopes, includeDependenciesTypes);
includeDependenciesScopes, includeDependenciesTypes, includeStandardJavaModules);
SmallRyeOpenAPI openAPI = generateOpenAPI(index);
write(openAPI);
} catch (Exception ex) {
Expand Down
Loading