Skip to content

Commit

Permalink
feat(standalone): replace classgraph with spring boot bytecode scanner
Browse files Browse the repository at this point in the history
Co-authored-by: sam0r040 <93372330+sam0r040@users.noreply.github.com>
  • Loading branch information
timonback and sam0r040 committed Feb 14, 2025
1 parent 026df6d commit 68db892
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 111 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,32 @@
package io.github.springwolf.core.asyncapi.scanners.classes.spring;

import io.github.springwolf.core.asyncapi.scanners.classes.ClassScanner;
import io.github.springwolf.core.asyncapi.scanners.classes.spring.annotations.AnnotationClassScanner;
import io.github.springwolf.core.asyncapi.scanners.classes.spring.annotations.ClassScannerUtil;
import io.github.springwolf.core.configuration.docket.AsyncApiDocketService;
import lombok.AllArgsConstructor;
import org.springframework.core.env.Environment;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.HashSet;
import java.util.Set;

@AllArgsConstructor
public class ComponentClassScanner implements ClassScanner {

private final AnnotationClassScanner<Component> scanner;
private final AsyncApiDocketService asyncApiDocketService;

public ComponentClassScanner(AsyncApiDocketService asyncApiDocketService, Environment environment) {
this.scanner = new AnnotationClassScanner<>(Component.class, asyncApiDocketService, environment);
}
private final Environment environment;

@Override
public Set<Class<?>> scan() {
return scanner.scan();
String basePackages = asyncApiDocketService.getAsyncApiDocket().getBasePackage();
if (!StringUtils.hasText(basePackages)) {
throw new IllegalArgumentException("Base package must not be blank");
}

AnnotationTypeFilter filter = new AnnotationTypeFilter(Component.class);
return new HashSet<>(ClassScannerUtil.getClasses(basePackages, filter, environment));
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.springwolf.core.asyncapi.scanners.classes.spring.annotations;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.env.Environment;
import org.springframework.core.type.filter.TypeFilter;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import static java.util.stream.Collectors.toList;

@Slf4j
public abstract class ClassScannerUtil {
public static List<Class<?>> getClasses(String basePackages, TypeFilter filter, Environment environment) {
ClassPathScanningCandidateComponentProvider provider =
new ClassPathScanningCandidateComponentProvider(false, environment);

provider.addIncludeFilter(filter);

return Arrays.stream(basePackages.replaceAll("\\s", "").split(","))
.flatMap(basePackage -> provider.findCandidateComponents(basePackage).stream()
.map(BeanDefinition::getBeanClassName)
.map(ClassScannerUtil::getClass)
.filter(Optional::isPresent)
.map(Optional::get))
.collect(toList());
}

private static Optional<Class<?>> getClass(String className) {
try {
log.debug("Found candidate class: {}", className);
return Optional.of(Class.forName(className));
} catch (ClassNotFoundException e) {
log.warn("Failed to get class for name: {}", className);
return Optional.empty();
}
}
}
2 changes: 0 additions & 2 deletions springwolf-examples/springwolf-kafka-example/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,6 @@ dependencies {

testImplementation libs.junit.jupiter.api
testRuntimeOnly libs.junit.jupiter

testImplementation 'io.github.classgraph:classgraph:4.8.154' // TODO:
}

docker {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,32 +48,18 @@
import io.github.springwolf.examples.kafka.standalone.common.SpringwolfConfigPropertiesLoader;
import io.github.springwolf.examples.kafka.standalone.plugin.StandalonePlugin;
import io.github.springwolf.examples.kafka.standalone.plugin.StandalonePluginContext;
import io.github.springwolf.examples.kafka.standalone.plugin.StandalonePluginDiscovery;
import io.github.springwolf.examples.kafka.standalone.plugin.StandalonePluginResult;
import io.swagger.v3.core.converter.ModelConverter;
import org.springframework.core.env.Environment;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

public class DefaultStandaloneFactory implements StandaloneFactory {
private final AsyncApiService asyncApiService;

public DefaultStandaloneFactory(String basePackage)
throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException,
IOException {
this(basePackage, StandalonePluginDiscovery.scan());
}

public DefaultStandaloneFactory(String basePackage, List<StandalonePlugin> plugins) throws IOException {
this(basePackage, plugins, List.of());
}

public DefaultStandaloneFactory(String basePackage, List<StandalonePlugin> plugins, List<String> profiles)
throws IOException {
Environment environment = new SpringwolfConfigPropertiesLoader().loadEnvironment(profiles);
public DefaultStandaloneFactory(String basePackage, List<StandalonePlugin> plugins, Environment environment) {
StringValueResolverProxy stringValueResolver = new StringValueResolverProxy();

stringValueResolver.setEmbeddedValueResolver(new StandaloneStringValueResolver(environment));
Expand All @@ -92,7 +78,8 @@ public DefaultStandaloneFactory(String basePackage, List<StandalonePlugin> plugi

AsyncApiDocketService asyncApiDocketService = autoConfiguration.asyncApiDocketService(properties);

ComponentClassScanner componentClassScanner = scannerAutoConfiguration.componentClassScanner(asyncApiDocketService, environment);
ComponentClassScanner componentClassScanner =
scannerAutoConfiguration.componentClassScanner(asyncApiDocketService, environment);
BeanMethodsScanner beanMethodsScanner = new DefaultBeanMethodsScanner(componentClassScanner);
SpringwolfClassScanner springwolfClassScanner =
new SpringwolfClassScanner(componentClassScanner, beanMethodsScanner);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import io.github.springwolf.asyncapi.v3.jackson.DefaultAsyncApiSerializerService;
import io.github.springwolf.asyncapi.v3.model.AsyncAPI;
import io.github.springwolf.core.asyncapi.AsyncApiService;
import io.github.springwolf.examples.kafka.standalone.common.SpringwolfConfigPropertiesLoader;
import io.github.springwolf.examples.kafka.standalone.plugin.StandalonePlugin;
import io.github.springwolf.examples.kafka.standalone.plugin.StandalonePluginDiscovery;
import org.junit.jupiter.api.Test;
import org.springframework.core.env.Environment;

import java.io.IOException;
import java.io.InputStream;
Expand All @@ -28,9 +30,11 @@ public void scanApplication()
IllegalAccessException {
// given
String basePackage = "io.github.springwolf.examples.kafka";
List<StandalonePlugin> plugins = StandalonePluginDiscovery.scan();

List<String> profiles = List.of();
StandaloneFactory standaloneFactory = new DefaultStandaloneFactory(basePackage, plugins, profiles);
Environment environment = new SpringwolfConfigPropertiesLoader().loadEnvironment(profiles);
List<StandalonePlugin> plugins = StandalonePluginDiscovery.scan(environment);
StandaloneFactory standaloneFactory = new DefaultStandaloneFactory(basePackage, plugins, environment);
AsyncApiService asyncApiService = standaloneFactory.getAsyncApiService();

// when
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.springwolf.examples.kafka.standalone.plugin;

import io.github.classgraph.ClassGraph;
import io.github.classgraph.ScanResult;
import io.github.springwolf.core.asyncapi.scanners.classes.spring.annotations.ClassScannerUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.core.type.filter.AssignableTypeFilter;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
Expand All @@ -14,31 +16,30 @@
* <p/>
* Requires that these plugins have a no-args constructor.
*/
@Slf4j
public class StandalonePluginDiscovery {
private static final String SPRINGWOLF_BASE_PACKAGE = "io.github.springwolf";

public static List<StandalonePlugin> scan()
public static List<StandalonePlugin> scan(Environment environment)
throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
return scan(SPRINGWOLF_BASE_PACKAGE);
return scan(SPRINGWOLF_BASE_PACKAGE, environment);
}

public static List<StandalonePlugin> scan(String basePackage)
public static List<StandalonePlugin> scan(String basePackages, Environment environment)
throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
try (ScanResult scanResult =
new ClassGraph().enableAllInfo().acceptPackages(basePackage).scan()) {
List<Class<?>> classList =
scanResult.getClassesImplementing(StandalonePlugin.class).loadClasses();
sortByOrderAnnotation(classList);

List<StandalonePlugin> instances = new ArrayList<>();
for (Class<?> clazz : classList) {
@SuppressWarnings("unchecked") // ensured due to scanResult getClassesImplementing filter
Class<StandalonePlugin> pluginClass = (Class<StandalonePlugin>) clazz;

instances.add(pluginClass.getConstructor().newInstance());
}
return instances;
AssignableTypeFilter filter = new AssignableTypeFilter(StandalonePlugin.class);
List<Class<?>> classList = ClassScannerUtil.getClasses(basePackages, filter, environment);

sortByOrderAnnotation(classList);

List<StandalonePlugin> instances = new ArrayList<>();
for (Class<?> clazz : classList) {
@SuppressWarnings("unchecked") // ensured due to scanResult getClassesImplementing filter
Class<StandalonePlugin> pluginClass = (Class<StandalonePlugin>) clazz;

instances.add(pluginClass.getConstructor().newInstance());
}
return instances;
}

private static void sortByOrderAnnotation(List<Class<?>> classList) {
Expand Down

0 comments on commit 68db892

Please sign in to comment.