-
Notifications
You must be signed in to change notification settings - Fork 3.5k
remove dependency on org.reflections #18295
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
base: main
Are you sure you want to change the base?
Conversation
🤖 GitHub commentsExpand to view the GitHub comments
Just comment with:
|
|
This pull request does not have a backport label. Could you fix it @jsvd? 🙏
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR removes the dependency on org.reflections library and replaces it with a custom lightweight package scanner. The change eliminates verbose logging from the reflections library while maintaining the same plugin discovery functionality.
Key Changes:
- Implemented a custom
PackageScannerclass to scan for annotated plugin classes - Refactored
PluginRegistryto use the new scanner instead of the reflections library - Removed the
org.reflections:reflectionsdependency from build configuration and licensing files
Reviewed Changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
logstash-core/src/main/java/org/logstash/plugins/discovery/PackageScanner.java |
New custom scanner implementation for finding annotated classes in packages |
logstash-core/src/main/java/org/logstash/plugins/discovery/PluginRegistry.java |
Updated to use PackageScanner instead of reflections library, simplified plugin discovery logic |
logstash-core/src/test/java/org/logstash/plugins/discovery/PluginRegistryTest.java |
New test suite verifying plugin discovery works for built-in plugins |
logstash-core/build.gradle |
Removed org.reflections:reflections dependency |
tools/dependencies-report/src/main/resources/licenseMapping.csv |
Removed license mapping entry for reflections |
tools/dependencies-report/src/main/resources/notices/org.reflections!reflections-NOTICE.txt |
Removed license notice file |
NOTICE.TXT |
Removed reflections library attribution |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
logstash-core/src/main/java/org/logstash/plugins/discovery/PackageScanner.java
Outdated
Show resolved
Hide resolved
logstash-core/src/main/java/org/logstash/plugins/discovery/PackageScanner.java
Outdated
Show resolved
Hide resolved
logstash-core/src/main/java/org/logstash/plugins/discovery/PluginRegistry.java
Outdated
Show resolved
Hide resolved
andsel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a couple of notes
logstash-core/src/main/java/org/logstash/plugins/discovery/PluginRegistry.java
Outdated
Show resolved
Hide resolved
logstash-core/src/main/java/org/logstash/plugins/discovery/PackageScanner.java
Outdated
Show resolved
Hide resolved
26942ce to
9c4aaa6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
logstash-core/src/main/java/org/logstash/plugins/discovery/PluginRegistry.java
Show resolved
Hide resolved
logstash-core/src/main/java/org/logstash/plugins/discovery/PackageScanner.java
Outdated
Show resolved
Hide resolved
logstash-core/src/main/java/org/logstash/plugins/discovery/PackageScanner.java
Show resolved
Hide resolved
| if ("file".equals(protocol)) { | ||
| scanDirectory(resource, basePackage, classNames); | ||
| } else if ("jar".equals(protocol)) { | ||
| scanJar(resource, resourcePath, classNames); | ||
| } else if (resource.openConnection() instanceof JarURLConnection) { | ||
| scanJar(resource, resourcePath, classNames); | ||
| } |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The protocol check at lines 64-67 calls resource.openConnection() potentially multiple times (once at line 66 to check the protocol, then again at line 96 inside scanJar). This creates unnecessary connection overhead and could lead to resource leaks. Consider checking the connection type once and reusing it.
d2c7b47 to
c18eefb
Compare
💛 Build succeeded, but was flaky
Failed CI StepsHistory
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| URLConnection connection = resource.openConnection(); | ||
| if (connection instanceof JarURLConnection) { | ||
| scanJar((JarURLConnection) connection, resourcePath, classNames); | ||
| } |
Copilot
AI
Dec 19, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The URLConnection opened on line 67 is not explicitly closed. While JarURLConnection is handled with try-with-resources in scanJar, if the connection is not a JarURLConnection but is some other type of URLConnection that requires cleanup, it may lead to a resource leak. Consider closing the connection in a finally block or using try-with-resources if the connection type is uncertain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scanJar cover with try-with-resources just the JarFile obtained from connection.getJarFile() not the URLConnection itself.
@jsvd maybe we could use a try-with-resources when we get the connection at resource.openConnection();
| URLConnection connection = resource.openConnection(); | |
| if (connection instanceof JarURLConnection) { | |
| scanJar((JarURLConnection) connection, resourcePath, classNames); | |
| } | |
| try (URLConnection connection = resource.openConnection()) { | |
| if (connection instanceof JarURLConnection) { | |
| scanJar((JarURLConnection) connection, resourcePath, classNames); | |
| } | |
| } |
logstash-core/src/main/java/org/logstash/plugins/discovery/PackageScanner.java
Show resolved
Hide resolved
logstash-core/src/main/java/org/logstash/plugins/discovery/PackageScanner.java
Show resolved
Hide resolved
logstash-core/src/main/java/org/logstash/plugins/discovery/PackageScanner.java
Show resolved
Hide resolved
andsel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a couple of suggestion to cover the issue that Copilot partially surfaced, and an readability improvement of the class names scanning.
| URLConnection connection = resource.openConnection(); | ||
| if (connection instanceof JarURLConnection) { | ||
| scanJar((JarURLConnection) connection, resourcePath, classNames); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scanJar cover with try-with-resources just the JarFile obtained from connection.getJarFile() not the URLConnection itself.
@jsvd maybe we could use a try-with-resources when we get the connection at resource.openConnection();
| URLConnection connection = resource.openConnection(); | |
| if (connection instanceof JarURLConnection) { | |
| scanJar((JarURLConnection) connection, resourcePath, classNames); | |
| } | |
| try (URLConnection connection = resource.openConnection()) { | |
| if (connection instanceof JarURLConnection) { | |
| scanJar((JarURLConnection) connection, resourcePath, classNames); | |
| } | |
| } |
| Set<Class<?>> result = new HashSet<>(); | ||
| for (String className : classNames) { | ||
| if (className.contains("$")) { | ||
| continue; | ||
| } | ||
| try { | ||
| Class<?> candidate = Class.forName(className, false, loader); | ||
| if (candidate.isAnnotationPresent(annotation)) { | ||
| result.add(candidate); | ||
| } | ||
| } catch (ClassNotFoundException | LinkageError e) { | ||
| throw new IllegalStateException("Unable to load class discovered during scanning: " + className, e); | ||
| } | ||
| } | ||
| return result; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that the size of classNames is more than some tens, I think we could use stream syntax to separate looping from logic to be applied:
| Set<Class<?>> result = new HashSet<>(); | |
| for (String className : classNames) { | |
| if (className.contains("$")) { | |
| continue; | |
| } | |
| try { | |
| Class<?> candidate = Class.forName(className, false, loader); | |
| if (candidate.isAnnotationPresent(annotation)) { | |
| result.add(candidate); | |
| } | |
| } catch (ClassNotFoundException | LinkageError e) { | |
| throw new IllegalStateException("Unable to load class discovered during scanning: " + className, e); | |
| } | |
| } | |
| return result; | |
| Set<Class<?>> result =classNames.stream() | |
| .filter(PackageScanner::isInnerClass) | |
| .map(className -> loadClass(loader, className)) | |
| .filter(cls -> cls.isAnnotationPresent(annotation)) | |
| .collect(Collectors.toSet()); |
where the isInenrClass and loadClass methods are:
private static Class<?> loadClass(ClassLoader loader, String className) {
try {
return Class.forName(className, false, loader);
} catch (ClassNotFoundException | LinkageError e) {
throw new IllegalStateException("Unable to load class discovered during scanning: " + className, e);
}
}
private static boolean isInnerClass(String name) {
return name.contains("$");
}
This removes the annoying log info:
Given we only use reflectiong to grab locally installed java plugin classes by annotation, we implement here a tiny scanner to do the same, and then we can remove the dependency.
exhaustive test suite run: https://buildkite.com/elastic/logstash-exhaustive-tests-pipeline/builds/2701/steps/canvas