diff --git a/pom.xml b/pom.xml index 9d760f2..be01e29 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,7 @@ 1.8.0 2.7.0-01 2.0.4-04 + 4.8.149 1.0.8 2.1.7 2.3.0 @@ -64,7 +65,6 @@ 3.6.0 3.3.0 3.2.0 - 0.9.12 1.7.29 @@ -105,9 +105,9 @@ ${org.apache.maven.version} - org.reflections - reflections - ${org.reflections.version} + io.github.classgraph + classgraph + ${io.github.classgraph.version} javax.xml.bind diff --git a/src/main/java/io/openapitools/swagger/GenerateMojo.java b/src/main/java/io/openapitools/swagger/GenerateMojo.java index fab7629..5cc5643 100644 --- a/src/main/java/io/openapitools/swagger/GenerateMojo.java +++ b/src/main/java/io/openapitools/swagger/GenerateMojo.java @@ -110,40 +110,29 @@ public void execute() throws MojoExecutionException, MojoFailureException { return; } - ClassLoader origClzLoader = Thread.currentThread().getContextClassLoader(); - ClassLoader clzLoader = createClassLoader(origClzLoader); + Reader reader = new Reader(swaggerConfig == null ? new OpenAPI() : swaggerConfig.createSwaggerModel()); - try { - // set the TCCL before everything else - Thread.currentThread().setContextClassLoader(clzLoader); - - Reader reader = new Reader(swaggerConfig == null ? new OpenAPI() : swaggerConfig.createSwaggerModel()); + JaxRSScanner reflectiveScanner = new JaxRSScanner(getLog(), createClassLoader(), resourcePackages, useResourcePackagesChildren); - JaxRSScanner reflectiveScanner = new JaxRSScanner(getLog(), resourcePackages, useResourcePackagesChildren); + Application application = resolveApplication(reflectiveScanner); + reader.setApplication(application); - Application application = resolveApplication(reflectiveScanner); - reader.setApplication(application); + OpenAPI swagger = OpenAPISorter.sort(reader.read(reflectiveScanner.classes())); - OpenAPI swagger = OpenAPISorter.sort(reader.read(reflectiveScanner.classes())); - - if (outputDirectory.mkdirs()) { - getLog().debug("Created output directory " + outputDirectory); - } + if (outputDirectory.mkdirs()) { + getLog().debug("Created output directory " + outputDirectory); + } - outputFormats.forEach(format -> { - try { - File outputFile = new File(outputDirectory, outputFilename + "." + format.name().toLowerCase()); - format.write(swagger, outputFile, prettyPrint); - if (attachSwaggerArtifact) { - projectHelper.attachArtifact(project, format.name().toLowerCase(), "swagger", outputFile); - } - } catch (IOException e) { - throw new RuntimeException("Unable write " + outputFilename + " document", e); + try { + for (OutputFormat format : outputFormats) { + File outputFile = new File(outputDirectory, outputFilename + "." + format.name().toLowerCase()); + format.write(swagger, outputFile, prettyPrint); + if (attachSwaggerArtifact) { + projectHelper.attachArtifact(project, format.name().toLowerCase(), "swagger", outputFile); } - }); - } finally { - // reset the TCCL back to the original class loader - Thread.currentThread().setContextClassLoader(origClzLoader); + } + } catch (IOException e) { + throw new RuntimeException("Unable write " + outputFilename + " document", e); } } @@ -164,7 +153,7 @@ private Application resolveApplication(JaxRSScanner reflectiveScanner) { return ClassUtils.createInstance(appClazz); } - private URLClassLoader createClassLoader(ClassLoader parent) { + private URLClassLoader createClassLoader() { try { Collection dependencies = getDependentClasspathElements(); URL[] urls = new URL[dependencies.size()]; @@ -172,7 +161,7 @@ private URLClassLoader createClassLoader(ClassLoader parent) { for (String dependency : dependencies) { urls[index++] = Paths.get(dependency).toUri().toURL(); } - return new URLClassLoader(urls, parent); + return new URLClassLoader(urls, Thread.currentThread().getContextClassLoader()); } catch (MalformedURLException e) { throw new RuntimeException("Unable to create class loader with compiled classes", e); } catch (DependencyResolutionRequiredException e) { diff --git a/src/main/java/io/openapitools/swagger/JaxRSScanner.java b/src/main/java/io/openapitools/swagger/JaxRSScanner.java index 2c9c6d7..9f5982c 100644 --- a/src/main/java/io/openapitools/swagger/JaxRSScanner.java +++ b/src/main/java/io/openapitools/swagger/JaxRSScanner.java @@ -10,12 +10,11 @@ import javax.ws.rs.core.Application; import org.apache.maven.plugin.logging.Log; -import org.reflections.Reflections; -import org.reflections.scanners.ResourcesScanner; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.scanners.TypeAnnotationsScanner; -import org.reflections.util.ConfigurationBuilder; +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ClassInfoList; +import io.github.classgraph.ScanResult; import io.swagger.v3.oas.annotations.OpenAPIDefinition; /** @@ -26,53 +25,54 @@ class JaxRSScanner { private final Log log; + private final ClassGraph classGraph; + private final Set resourcePackages; private final boolean useResourcePackagesChildren; - public JaxRSScanner(Log log, Set resourcePackages, Boolean useResourcePackagesChildren) { + public JaxRSScanner(Log log, ClassLoader clzLoader, Set resourcePackages, Boolean useResourcePackagesChildren) { this.log = log; + this.classGraph = new ClassGraph().enableClassInfo().enableAnnotationInfo() + .addClassLoader(clzLoader); this.resourcePackages = resourcePackages == null ? Collections.emptySet() : new HashSet<>(resourcePackages); - this.useResourcePackagesChildren = useResourcePackagesChildren != null && useResourcePackagesChildren; + this.useResourcePackagesChildren = Boolean.TRUE.equals(useResourcePackagesChildren); } Application applicationInstance() { - ConfigurationBuilder config = ConfigurationBuilder - .build(resourcePackages) - .setScanners(new ResourcesScanner(), new TypeAnnotationsScanner(), new SubTypesScanner()); - Reflections reflections = new Reflections(config); - Set> applicationClasses = reflections.getSubTypesOf(Application.class) - .stream() - .filter(this::filterClassByResourcePackages) - .collect(Collectors.toSet()); - if (applicationClasses.isEmpty()) { - return null; - } - if (applicationClasses.size() > 1) { - log.warn("More than one javax.ws.rs.core.Application classes found on the classpath, skipping"); - return null; + Application applicationInstance = null; + try (ScanResult scanResult = classGraph.scan()) { + ClassInfoList applicationClasses = scanResult.getSubclasses(Application.class.getName()) + .filter(this::filterClassByResourcePackages); + if (applicationClasses.size() == 1) { + applicationInstance = ClassUtils.createInstance(applicationClasses.get(0).loadClass(Application.class)); + } else if (applicationClasses.size() > 1) { + log.warn("More than one javax.ws.rs.core.Application classes found on the classpath, skipping"); + } } - return ClassUtils.createInstance(applicationClasses.iterator().next()); + return applicationInstance; } Set> classes() { - ConfigurationBuilder config = ConfigurationBuilder - .build(resourcePackages) - .setScanners(new ResourcesScanner(), new TypeAnnotationsScanner(), new SubTypesScanner()); - Reflections reflections = new Reflections(config); - Stream> apiClasses = reflections.getTypesAnnotatedWith(Path.class) - .stream() - .filter(this::filterClassByResourcePackages); - Stream> defClasses = reflections.getTypesAnnotatedWith(OpenAPIDefinition.class) - .stream() - .filter(this::filterClassByResourcePackages); - return Stream.concat(apiClasses, defClasses).collect(Collectors.toSet()); + Set> classes; + try (ScanResult scanResult = classGraph.scan()) { + ClassInfoList apiClasses = scanResult.getClassesWithAnnotation(Path.class.getName()); + ClassInfoList defClasses = scanResult.getClassesWithAnnotation(OpenAPIDefinition.class.getName()); + classes = Stream.of(apiClasses, defClasses) + .flatMap(classList -> classList.filter(this::filterClassByResourcePackages).stream()) + .map(ClassInfo::loadClass) + .collect(Collectors.toSet()); + } + if (classes.isEmpty()) { + log.warn("No @Path or @OpenAPIDefinition annotated classes found in given resource packages: " + resourcePackages); + } + return classes; } - private boolean filterClassByResourcePackages(Class cls) { + private boolean filterClassByResourcePackages(ClassInfo cls) { return resourcePackages.isEmpty() - || resourcePackages.contains(cls.getPackage().getName()) - || (useResourcePackagesChildren && resourcePackages.stream().anyMatch(p -> cls.getPackage().getName().startsWith(p))); + || resourcePackages.contains(cls.getPackageName()) + || (useResourcePackagesChildren && resourcePackages.stream().anyMatch(p -> cls.getPackageName().startsWith(p))); } }