remove dependency on org.reflections#18295
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.
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
Show resolved
Hide resolved
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/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.
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
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/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); | ||
| } |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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); | ||
| } |
There was a problem hiding this comment.
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.
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
Outdated
Show resolved
Hide resolved
logstash-core/src/main/java/org/logstash/plugins/discovery/PackageScanner.java
Outdated
Show resolved
Hide resolved
andsel
left a comment
There was a problem hiding this comment.
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.
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.
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("$");
}Fix URLConnection resource leak by checking URL protocol directly instead of opening a connection. Refactor scanForAnnotation into stream pipeline with helper methods and log-and-skip unloadable classes instead of throwing. Add dedicated PackageScannerTest and remove stale @NotThreadSafe annotations.
c18eefb to
164230d
Compare
💛 Build succeeded, but was flaky
Failed CI StepsHistory
|
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