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

Artifact transforms have incorrect current thread context classloader set and that affects java.xml.bind implementation discovery #29114

Open
tudortimi opened this issue May 13, 2024 · 4 comments
Labels
a:bug has:workaround Indicates that the issue has a workaround in:artifact-transforms

Comments

@tudortimi
Copy link

tudortimi commented May 13, 2024

Current Behavior

I was trying to use javax.xml.bind in a plugin I’m developing. I see issues related to missing classes when I try to run in Java 11, but these only happen in a part of the code that uses JAXB and not in others.

I'm running it on both Java 8 and Java 11. Since JAXB is not part of the JDK anymore from Java 9, I added a dependency in the plugin build:

dependencies {
    implementation("com.sun.xml.bind:jaxb-ri:2.3.9") {
        because("javax.xml.bind is not part of the JDK anymore starring with JDK9")
    }
}

I have two places in my plugin where I use JAXB. One place is in a task:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

public class WriteCompileSpecFile extends DefaultTask {
    // ...

    @TaskAction
    protected void generate() {
        DefaultHDVLCompileSpec compileSpec = new DefaultHDVLCompileSpec(getSvSource().getFiles());
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(DefaultHDVLCompileSpec.class);
            Marshaller marshaller = jaxbContext.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

            FileAdapter fileAdapter = new FileAdapter(getProject().getProjectDir());
            marshaller.setAdapter(fileAdapter);

            marshaller.marshal(compileSpec, destination.get().getAsFile());
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
    }
}

I can execute this task without any problems.

The other place where I use JAXB is in an artifact transform:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

public abstract class WriteXrunArgsFile implements TransformAction<TransformParameters.None> {
    // ...

    private static DefaultHDVLCompileSpec getCompileSpec(File input) {
        File compileSpec = new File(input, ".gradle-hdvl/compile-spec.xml");
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(DefaultHDVLCompileSpec.class);
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();

            FileAdapter fileAdapter = new FileAdapter(input);
            unmarshaller.setAdapter(fileAdapter);

            DefaultHDVLCompileSpec result = (DefaultHDVLCompileSpec) unmarshaller.unmarshal(compileSpec);
            for (File svSourceFile : result.getSvSourceFiles()) {
                assert svSourceFile.isAbsolute() : "not absolute: " + svSourceFile;
                assert svSourceFile.exists() : "doesn't exist: " + svSourceFile;
            }

            return result;
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
    }
}

When the artifact transform gets executed, I get:

      > Execution failed for WriteXrunArgsFile: /home/tudor/.gradle/caches/transforms-4/2f8f369cd18dbcfb90e34fd250b46eaa/transformed/some-published-dependency-0.1.0.zip.
         > javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
            - with linked exception:
           [java.lang.ClassNotFoundException: com.sun.xml.bind.v2.ContextFactory]

Expected Behavior

I would expect the dependency to be enough. I discussed this here in the Gradle forum with @Vampire and he suggested I open an issue.

Context (optional)

I switched to using a newer version of JAXB, which works on both Java 8 and 11 (version 3.0.2), but this required changing the namespace as well. I'm not blocked by the issue, but would like to understand whether I'm doing something wrong or whether something can be improved in Gradle.

Steps to Reproduce

I see the issue in this commit of my plugin: tudortimi/gradle-hdvl@3347a4e

To reproduce, after cloning that repo and selecting the commit, with Java 11 installed, run:

cd examples/using-published/some-published-dependency
../../../gradlew publish  # executes task that writes XML using JAXB, works

cd examples/using-published/some-project
../../../gradlew genFullXrunArgsFile  # consumes artifact produced by previous project, triggers artifact transform, using JAXB fails

Gradle version

8.7

Build scan URL (optional)

https://scans.gradle.com/s/2micrd4yj3ahe

Your Environment (optional)

Java 11

@Vampire
Copy link
Contributor

Vampire commented May 13, 2024

To have the essential information here too. When executing a task action, the thread context class loader gets set to the class loader of the task action that is executed. But for artifact transforms this thread context class loader setting is not done and the JAXB implementation is searched via the thread context class loader which then causes it not being found during the artifact transform.

I guess that the thread context class loader should also be set for executing transform actions unless there is a reason not to.

@ljacomet ljacomet added in:artifact-transforms 👋 team-triage Issues that need to be triaged by a specific team and removed to-triage labels May 16, 2024
@ljacomet
Copy link
Member

This issue needs a decision from the team responsible for that area. They have been informed. Response time may vary.


One question to answer here is whether transforms implementations can have dependencies.

@Vampire
Copy link
Contributor

Vampire commented May 16, 2024

Maybe I misunderstand, but why should they not?
I have many artifact transforms that need dependencies.
Just as one example I have an artifact transforms that signs DLLs contained within JARs on-the-fly.
I wouldn't want to implement this myself, so I use JSign for it.

@asodja
Copy link
Member

asodja commented Jun 10, 2024

Yes, I think we should set a thread context classloader as suggested by @Vampire in that discussion. We should probably set it here here in the similar way as we do for tasks.

A workaround for your problem is to do something like:

     public void transform(TransformOutputs outputs) {
         File input = getInputArtifact().get().getAsFile();
         File xrunArgsFile = outputs.file(input.getName() + ".xrun_args.f");
-        DefaultHDVLCompileSpec compileSpec = getCompileSpec(input);
-        writeXrunArgsFile(xrunArgsFile, compileSpec);
+        ClassLoader original = Thread.currentThread().getContextClassLoader();
+        Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+        try {
+            DefaultHDVLCompileSpec compileSpec = getCompileSpec(input);
+            writeXrunArgsFile(xrunArgsFile, compileSpec);
+        } finally {
+            Thread.currentThread().setContextClassLoader(original);
+        }
     }

This change could have some different consequences so it needs more research.

The issue is now in the backlog of the relevant team, but the existence of a workaround means it might take a while before a fix is made.

@asodja asodja added has:workaround Indicates that the issue has a workaround and removed 👋 team-triage Issues that need to be triaged by a specific team labels Jun 10, 2024
@asodja asodja changed the title Classloader issue related to java.xml.bind in ArtifactTransform Artifact transforms have incorrect current thread context classloader set and that affects java.xml.bind implementation discovery Jun 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a:bug has:workaround Indicates that the issue has a workaround in:artifact-transforms
Projects
None yet
Development

No branches or pull requests

5 participants