[GERONIMO-6785] adding scanning feature to openapimojo
diff --git a/geronimo-openapi-maven-plugin/pom.xml b/geronimo-openapi-maven-plugin/pom.xml
index 48c7103..601034d 100644
--- a/geronimo-openapi-maven-plugin/pom.xml
+++ b/geronimo-openapi-maven-plugin/pom.xml
@@ -107,6 +107,11 @@
<artifactId>johnzon-jsonb</artifactId>
<version>${johnzon.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.xbean</groupId>
+ <artifactId>xbean-finder-shaded</artifactId>
+ <version>4.18</version>
+ </dependency>
<dependency>
<groupId>junit</groupId>
diff --git a/geronimo-openapi-maven-plugin/src/main/java/org/apache/geronimo/microprofile/openapi/mojo/OpenAPIMojo.java b/geronimo-openapi-maven-plugin/src/main/java/org/apache/geronimo/microprofile/openapi/mojo/OpenAPIMojo.java
index ba93e93..de4615d 100644
--- a/geronimo-openapi-maven-plugin/src/main/java/org/apache/geronimo/microprofile/openapi/mojo/OpenAPIMojo.java
+++ b/geronimo-openapi-maven-plugin/src/main/java/org/apache/geronimo/microprofile/openapi/mojo/OpenAPIMojo.java
@@ -16,31 +16,6 @@
*/
package org.apache.geronimo.microprofile.openapi.mojo;
-import static java.util.Optional.ofNullable;
-import static org.apache.maven.plugins.annotations.LifecyclePhase.PROCESS_CLASSES;
-import static org.apache.maven.plugins.annotations.ResolutionScope.COMPILE_PLUS_RUNTIME;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.Writer;
-import java.lang.reflect.InvocationTargetException;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Objects;
-import java.util.stream.Stream;
-
-import javax.json.bind.Jsonb;
-import javax.json.bind.JsonbBuilder;
-import javax.json.bind.JsonbConfig;
-
import org.apache.geronimo.microprofile.openapi.impl.model.InfoImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.OpenAPIImpl;
import org.apache.geronimo.microprofile.openapi.impl.processor.AnnotationProcessor;
@@ -53,6 +28,36 @@
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
+import org.apache.xbean.finder.AnnotationFinder;
+import org.apache.xbean.finder.archive.FileArchive;
+
+import javax.json.bind.Jsonb;
+import javax.json.bind.JsonbBuilder;
+import javax.json.bind.JsonbConfig;
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.Path;
+import java.io.File;
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+import static java.util.Optional.ofNullable;
+import static java.util.stream.Collectors.toList;
+import static org.apache.maven.plugins.annotations.LifecyclePhase.PROCESS_CLASSES;
+import static org.apache.maven.plugins.annotations.ResolutionScope.COMPILE_PLUS_RUNTIME;
@Mojo(name = "openapi.json", defaultPhase = PROCESS_CLASSES, requiresDependencyResolution = COMPILE_PLUS_RUNTIME, threadSafe = true)
public class OpenAPIMojo extends AbstractMojo {
@@ -102,9 +107,9 @@
try {
try (final URLClassLoader loader = new URLClassLoader(
Stream.concat(
- Stream.of(classes),
- ofNullable(project)
- .map(p -> p.getArtifacts().stream().map(Artifact::getFile)).orElseGet(Stream::empty))
+ Stream.of(classes),
+ ofNullable(project)
+ .map(p -> p.getArtifacts().stream().map(Artifact::getFile)).orElseGet(Stream::empty))
.filter(Objects::nonNull)
.map(file -> {
try {
@@ -125,6 +130,11 @@
super.close();
}
}) {
+ if ((application == null || hasNoEndpoint()) &&
+ classes != null && classes.exists()) {
+ scan(loader);
+ }
+
final AnnotationProcessor processor = new AnnotationProcessor(
(value, def) -> ofNullable(configuration).orElseGet(Collections::emptyMap).getOrDefault(value, def),
loadNamingStrategy(), null);
@@ -133,12 +143,12 @@
getLog().info("Processed application " + application);
}
if (endpointClasses != null) {
- final String binding = application == null ? "" : processor.getApplicationBinding(load(application));
+ final String binding = hasNoApplication() ? "" : processor.getApplicationBinding(load(application));
endpointClasses.stream().map(this::load)
.peek(c -> getLog().info("Processing class " + c.getName()))
.forEach(c -> processor.processClass(
- binding, api, new ClassElement(c),
- Stream.of(c.getMethods()).map(MethodElement::new)));
+ binding, api, new ClassElement(c),
+ Stream.of(c.getMethods()).map(MethodElement::new)));
} else {
getLog().warn("No <endpointClasses> registered, your OpenAPI will be empty.");
}
@@ -173,6 +183,29 @@
getLog().info("Wrote " + output);
}
+ private boolean hasNoApplication() {
+ return application == null || "none".equals(application);
+ }
+
+ private boolean hasNoEndpoint() {
+ return endpointClasses == null || endpointClasses.isEmpty();
+ }
+
+ private void scan(final ClassLoader loader) {
+ final Finder finder = new Finder(loader, classes);
+ if (application == null) {
+ final List<String> apps = finder.findByAnnotation(ApplicationPath.class.getName());
+ if (apps.size() > 1) {
+ throw new IllegalArgumentException("Ambiguous application class: " + apps);
+ } else if (apps.size() == 1) {
+ application = apps.iterator().next();
+ }
+ }
+ if (endpointClasses == null || endpointClasses.isEmpty()) {
+ endpointClasses = finder.findByAnnotation(Path.class.getName());
+ }
+ }
+
private NamingStrategy loadNamingStrategy() {
return ofNullable(operationNamingStrategy)
.map(String::trim)
@@ -198,4 +231,28 @@
}
}
+ private static class Finder extends AnnotationFinder {
+ private Finder(final ClassLoader loader, final File classes) {
+ super(new FileArchive(loader, classes), false);
+ }
+
+ // don't require the runtime to be loadable by the plugin
+ private List<String> findByAnnotation(final String annotationName) {
+ return super.getAnnotationInfos(annotationName).stream()
+ .filter(ClassInfo.class::isInstance)
+ .map(ClassInfo.class::cast)
+ .filter(it -> {
+ try {
+ final Class<?> type = it.get();
+ return type.isInterface() || !Modifier.isAbstract(type.getModifiers());
+ } catch (final Exception e) {
+ return true;
+ }
+ })
+ .map(ClassInfo::getName)
+ .distinct()
+ .sorted()
+ .collect(toList());
+ }
+ }
}
diff --git a/geronimo-openapi-maven-plugin/src/test/java/org/apache/geronimo/microprofile/openapi/mojo/OpenAPIMojoTest.java b/geronimo-openapi-maven-plugin/src/test/java/org/apache/geronimo/microprofile/openapi/mojo/OpenAPIMojoTest.java
index 6695ab6..170f45a 100644
--- a/geronimo-openapi-maven-plugin/src/test/java/org/apache/geronimo/microprofile/openapi/mojo/OpenAPIMojoTest.java
+++ b/geronimo-openapi-maven-plugin/src/test/java/org/apache/geronimo/microprofile/openapi/mojo/OpenAPIMojoTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.geronimo.microprofile.openapi.mojo;
+import static java.util.Collections.emptyList;
import static java.util.Collections.singleton;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -43,21 +44,34 @@
public class OpenAPIMojoTest {
@Test
+ public void scan() throws Exception {
+ final OpenAPIMojo mojo = new OpenAPIMojo();
+ mojo.output = new File("target/OpenAPIMojoTest_scan_1.json");
+ mojo.classes = new File("target/test-classes");
+ mojo.endpointClasses = emptyList();
+ doExecuteAndAssert(mojo);
+ }
+
+ @Test
public void run() throws Exception {
final OpenAPIMojo mojo = new OpenAPIMojo();
mojo.output = new File("target/OpenAPIMojoTest_run_1.json");
mojo.endpointClasses = singleton(HelloServiceImpl1.class.getName());
+ doExecuteAndAssert(mojo);
+ }
+
+ private void doExecuteAndAssert(final OpenAPIMojo mojo) throws Exception {
mojo.project = new MavenProject();
mojo.project.setVersion("1.2.3");
mojo.execute();
final OpenAPI openAPI = readOpenAPI(mojo.output);
assertNotNull(openAPI.getInfo());
assertEquals("1.2.3", openAPI.getInfo().getVersion());
- final Operation get = openAPI.getPaths().get("/sayHello/{a}").getGET();
+ final Operation get = openAPI.getPaths().getPathItem("/sayHello/{a}").getGET();
assertNotNull(get);
assertEquals(1, get.getParameters().size());
assertEquals("a", get.getParameters().iterator().next().getName());
- assertEquals(Schema.SchemaType.STRING, get.getResponses().get("200").getContent().get("text/plain").getSchema().getType());
+ assertEquals(Schema.SchemaType.STRING, get.getResponses().getAPIResponse("200").getContent().getMediaType("text/plain").getSchema().getType());
}
private OpenAPI readOpenAPI(final File output) throws Exception {