Skip to content
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

ClassLoaderHandler for Quarkus RuntimeClassLoader 1.13.3 #531

Closed
itmrat01 opened this issue Jul 1, 2021 · 7 comments
Closed

ClassLoaderHandler for Quarkus RuntimeClassLoader 1.13.3 #531

itmrat01 opened this issue Jul 1, 2021 · 7 comments

Comments

@itmrat01
Copy link
Contributor

itmrat01 commented Jul 1, 2021

Hey,

I run in a problem after a quarkus update to 1.13.3. I don't know since which version the Classloader at Runtime (fast-jar) changed, but it changed to "io.quarkus.bootstrap.runner.RunnerClassLoader".

I tried a little bit by myself and following code worked for me. Perhaps you want to use it, otherwise feel free to close this issue.

BR
Tobias

/**
 * Extract classpath entries from the Quarkus ClassLoader.
 */
class QuarkusClassLoaderHandler implements ClassLoaderHandler {
    // Classloader until Quarkus 1.2
    private static final String RUNTIME_CLASSLOADER         = "io.quarkus.runner.RuntimeClassLoader";

    // Classloader since Quarkus 1.3
    private static final String QUARKUS_CLASSLOADER         = "io.quarkus.bootstrap.classloading.QuarkusClassLoader";

    // Classloader since Quarkus 1.13
    private static final String QUARKUS_RUNTIME_CLASSLOADER = "io.quarkus.bootstrap.runner.RunnerClassLoader";

    /**
     * Class cannot be constructed.
     */
    private QuarkusClassLoaderHandler() {
    }

    /**
     * Can handle.
     *
     * @param classLoaderClass the classloader class
     * @param log              the log
     * @return true, if classLoaderClass is the Quarkus RuntimeClassloader or QuarkusClassloader
     */
    public static boolean canHandle(final Class<?> classLoaderClass, final LogNode log) {
        return RUNTIME_CLASSLOADER.equals(classLoaderClass.getName())
                || QUARKUS_CLASSLOADER.equals(classLoaderClass.getName())
                || QUARKUS_RUNTIME_CLASSLOADER.equals(classLoaderClass.getName());
    }

    /**
     * Find classloader order.
     *
     * @param classLoader      the class loader
     * @param classLoaderOrder the classloader order
     * @param log              the log
     */
    public static void findClassLoaderOrder(final ClassLoader classLoader, final ClassLoaderOrder classLoaderOrder,
            final LogNode log) {
        classLoaderOrder.delegateTo(classLoader.getParent(), /* isParent = */ true, log);
        classLoaderOrder.add(classLoader, log);
    }

    /**
     * Find the classpath entries for the associated {@link ClassLoader}.
     *
     * @param classLoader    the {@link ClassLoader} to find the classpath entries order for.
     * @param classpathOrder a {@link ClasspathOrder} object to update.
     * @param scanSpec       the {@link ScanSpec}.
     * @param log            the log.
     */
    public static void findClasspathOrder(final ClassLoader classLoader, final ClasspathOrder classpathOrder,
            final ScanSpec scanSpec, final LogNode log) {

        final String classLoaderName = classLoader.getClass().getName();
        if (RUNTIME_CLASSLOADER.equals(classLoaderName)) {
            findClasspathOrderForRuntimeClassloader(classLoader, classpathOrder, scanSpec, log);
        } else if (QUARKUS_CLASSLOADER.equals(classLoaderName)) {
            findClasspathOrderForQuarkusClassloader(classLoader, classpathOrder, scanSpec, log);
        } else if (QUARKUS_RUNTIME_CLASSLOADER.equals(classLoaderName)) {
            findClasspathOrderForQuarkusRuntimeClassloader(classLoader, classpathOrder, scanSpec, log);
        }
    }

    @SuppressWarnings("unchecked")
    private static void findClasspathOrderForQuarkusClassloader(final ClassLoader classLoader,
            final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) {
        for (final Object element : (Collection<Object>) ReflectionUtils.getFieldVal(classLoader, "elements", false)) {
            final String elementClassName = element.getClass().getName();
            if ("io.quarkus.bootstrap.classloading.JarClassPathElement".equals(elementClassName)) {
                classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(element, "file", false), classLoader,
                        scanSpec, log);
            } else if ("io.quarkus.bootstrap.classloading.DirectoryClassPathElement".equals(elementClassName)) {
                classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(element, "root", false), classLoader,
                        scanSpec, log);
            }
        }
    }

    @SuppressWarnings("unchecked")
    private static void findClasspathOrderForRuntimeClassloader(final ClassLoader classLoader,
            final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) {
        final Collection<Path> applicationClassDirectories = (Collection<Path>) ReflectionUtils.getFieldVal(classLoader,
                "applicationClassDirectories", false);
        if (applicationClassDirectories != null) {
            for (final Path path : applicationClassDirectories) {
                classpathOrder.addClasspathEntryObject(path.toUri(), classLoader, scanSpec, log);
            }
        }
    }

    @SuppressWarnings("unchecked")
    private static void findClasspathOrderForQuarkusRuntimeClassloader(final ClassLoader classLoader,
            final ClasspathOrder classpathOrder, final ScanSpec scanSpec, final LogNode log) {
        for (final Object[] elementArray : ((Map<String, Object[]>) ReflectionUtils.getFieldVal(classLoader,
                "resourceDirectoryMap", false)).values()) {
            for (Object element : elementArray) {
                final String elementClassName = element.getClass().getName();
                if ("io.quarkus.bootstrap.runner.JarResource".equals(elementClassName)) {
                    classpathOrder.addClasspathEntry(ReflectionUtils.getFieldVal(element, "jarPath", false), classLoader,
                            scanSpec, log);
                }
            }
        }
    }
}
@lukehutch
Copy link
Member

Added and released in version 4.8.109. Thanks for the contribution and the pull request!

@tivrfoa
Copy link

tivrfoa commented Jul 7, 2021

Hi @itmrat01 !

Could you show an example of how you use Classgraph in a Quarkus application, please?

@itmrat01
Copy link
Contributor Author

itmrat01 commented Jul 7, 2021

This is an example to get all files from classpath with pattern '*-default.properties' from package 'properties'

// Load from 3rd Party Libs
Set<String> resources;
try (ScanResult scanResult = new ClassGraph().acceptPathsNonRecursive("properties").scan()) {
    resources = new HashSet<>(
            scanResult.getResourcesMatchingPattern(Pattern.compile(".*-default\\.properties")).getPaths());
}

@lukehutch
Copy link
Member

@tivrfoa -- I think you're asking about how to scan in general, given the compiling via GraalVM in Quarkus means that your application is no longer running as bytecode, or even in a jar?

If so, this may help -- build-time scanning:

https://github.com/classgraph/classgraph/wiki/Build-Time-Scanning

@tivrfoa
Copy link

tivrfoa commented Jul 8, 2021

This is an example to get all files from classpath with pattern '*-default.properties' from package 'properties'

// Load from 3rd Party Libs
Set<String> resources;
try (ScanResult scanResult = new ClassGraph().acceptPathsNonRecursive("properties").scan()) {
    resources = new HashSet<>(
            scanResult.getResourcesMatchingPattern(Pattern.compile(".*-default\\.properties")).getPaths());
}

Thank you very much! =)

@tivrfoa
Copy link

tivrfoa commented Jul 8, 2021

@tivrfoa -- I think you're asking about how to scan in general, given the compiling via GraalVM in Quarkus means that your application is no longer running as bytecode, or even in a jar?

I was just wondering if there were some Quarkus specific thing needed to generate the class graph.

If so, this may help -- build-time scanning:

https://github.com/classgraph/classgraph/wiki/Build-Time-Scanning

I'll take a look.
Thank you Luke! =)

@lukehutch
Copy link
Member

I was just wondering if there were some Quarkus specific thing needed to generate the class graph.

There is nothing specific needed for Quarkus when your project is in classfile form, with the classfiles either in directories or in jarfiles, as far as I know. But if you do use GraalVM to compile to native code, you have to do the scan before that compilation step, or it won't work. (Android has the same constraint -- you have to scan before compilation to Dalvik bytecodes.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants