Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import java.util.Set;
import java.util.stream.Collectors;

import io.github.classgraph.ClassInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -209,6 +210,26 @@ public Set<Method> getMethodsWithAnnotation(Class<? extends Annotation> annotati
return result;
}

/**
* Returns the URL of the classpath element (JAR or directory) from which the given class was
* loaded during scanning.
*
* <p>This is determined from ClassGraph's scan metadata at scan time — before the class is
* actually loaded by the JVM — and is therefore not affected by {@link SecurityManager}
* restrictions on {@link Class#getProtectionDomain()} nor by JDK version differences.
* Returns {@code null} if the class name is not present in the scan result.
*
* @param className the binary class name (e.g. {@code "com.example.MyKeywords"})
* @return the URL of the JAR or directory that contains the class, or {@code null}
*/
public URL getClasspathElementUrl(String className) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This new public method should be covered by unit tests in AnnotationScannerTest.java. Please add tests for at least the following cases:

  • A class that is found in the scan result.
  • A class that is not found in the scan result (should return null).
  • A null class name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Junit tests added

if (className == null) {
throw new IllegalArgumentException("className must not be null");
}
ClassInfo classInfo = scanResult.getClassInfo(className);
return classInfo != null ? classInfo.getClasspathElementURL() : null;
Comment on lines +229 to +230

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The scanResult.getClassInfo(className) method from the classgraph library will throw an IllegalArgumentException if className is null. To make this method more robust, it's a good practice to handle the null case explicitly by returning null, which is consistent with the behavior when a class is not found.

        if (className == null) {
            return null;
        }
        ClassInfo classInfo = scanResult.getClassInfo(className);
        return classInfo != null ? classInfo.getClasspathElementURL() : null;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Classname value check added, but we do not accept null value and now throw an IllegalArgumentException

}

/**
* Alternative implementation of {@link Class#isAnnotationPresent(Class)} which
* doesn't rely on class equality but class names. The class loaders of the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@
package step.core.scanner;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -97,6 +101,57 @@ public void testAnnotationScannerForSpecificJarsWithSpacesAndSpecialCharsInPath(
}
}

@Test
public void testGetClasspathElementUrl_found() {
File file = FileHelper.getClassLoaderResourceAsFile(this.getClass().getClassLoader(), "annotation-test.jar");
try (AnnotationScanner annotationScanner = AnnotationScanner.forSpecificJar(file)) {
URL url = annotationScanner.getClasspathElementUrl("step.core.scanner.AnnotatedClass");
assertNotNull(url);
assertEquals(file.toURI().toURL(), url);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

@Test
public void testGetClasspathElementUrl_notFound() {
File file = FileHelper.getClassLoaderResourceAsFile(this.getClass().getClassLoader(), "annotation-test.jar");
try (AnnotationScanner annotationScanner = AnnotationScanner.forSpecificJar(file)) {
URL url = annotationScanner.getClasspathElementUrl("com.example.NonExistentClass");
assertNull(url);
}
}

@Test(expected = IllegalArgumentException.class)
public void testGetClasspathElementUrl_nullClassName() {
try (AnnotationScanner annotationScanner = AnnotationScanner.forAllClassesFromContextClassLoader()) {
annotationScanner.getClasspathElementUrl(null);
}
}

@Test
public void testGetClasspathElementUrl_multipleJars_returnsCorrectJar() throws Exception {
File annotationTestJar = FileHelper.getClassLoaderResourceAsFile(this.getClass().getClassLoader(), "annotation-test.jar");
// Use the classgraph JAR (already a compile dependency) as a second distinct JAR
URL classgraphJarUrl = io.github.classgraph.ClassGraph.class.getProtectionDomain().getCodeSource().getLocation();
File classgraphJar = new File(classgraphJarUrl.toURI());

URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{
annotationTestJar.toURI().toURL(),
classgraphJarUrl
}, null);

try (AnnotationScanner annotationScanner = AnnotationScanner.forSpecificJarFromURLClassLoader(urlClassLoader)) {
URL annotatedClassUrl = annotationScanner.getClasspathElementUrl("step.core.scanner.AnnotatedClass");
assertNotNull(annotatedClassUrl);
assertEquals(annotationTestJar.getCanonicalFile(), new File(annotatedClassUrl.toURI()).getCanonicalFile());

URL classgraphClassUrl = annotationScanner.getClasspathElementUrl("io.github.classgraph.ClassGraph");
assertNotNull(classgraphClassUrl);
assertEquals(classgraphJar.getCanonicalFile(), new File(classgraphClassUrl.toURI()).getCanonicalFile());
}
}

// Don't remove this class
// It is here to ensure that annotation scanning performed in
// testGetMethodsWithAnnotation() isn't finding other methods that the
Expand Down