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

Pico CLI runtime issue on Graal 23 - picocli.CommandLine$InitializationException Cannot instantiate the class has no constructor #2357

Closed
Hakky54 opened this issue Dec 30, 2024 · 10 comments
Labels
theme: annotation-proc An issue or change related to the annotation processor type: bug 🐛
Milestone

Comments

@Hakky54
Copy link

Hakky54 commented Dec 30, 2024

I noticed that Pico CLI is working on Graal 22 but not on Graal 23 anymore when creating/running a native image. I wasn't quite sure where to create this issue as I am not sure where it could be fixed. So I was in doubt whether Graal has caused an issue and has regression. I wanted to raise this issue first here as you are the library maintainer and maybe can easily find the issue and if it is caused by the Graal changes itself we can provide context to them to resolve it on their side if that is the case.

I am using Pico CLI alongside with Graal to create a native image for Certificate Ripper. Recently I added VersionProvider class which implements CommandLine.IVersionProvider to provide the version. On Graal 22 this works, however on version 23 it gives me the following error:

❯ ./target/crip
Exception in thread "main" picocli.CommandLine$InitializationException: Cannot instantiate nl.altindag.crip.command.VersionProvider: the class has no constructor
        at picocli.CommandLine$DefaultFactory.create(CommandLine.java:5689)
        at picocli.CommandLine$DefaultFactory.createVersionProvider(CommandLine.java:5675)
        at picocli.CommandLine$Model$CommandSpec.updateVersionProvider(CommandLine.java:7494)
        at picocli.CommandLine$Model$CommandSpec.updateCommandAttributes(CommandLine.java:7459)
        at picocli.CommandLine$Model$CommandReflection.extractCommandSpec(CommandLine.java:11845)
        at picocli.CommandLine$Model$CommandSpec.forAnnotatedObject(CommandLine.java:6392)
        at picocli.CommandLine.<init>(CommandLine.java:230)
        at picocli.CommandLine.<init>(CommandLine.java:224)
        at picocli.CommandLine.<init>(CommandLine.java:199)
        at nl.altindag.crip.App.main(App.java:25)
        at [email protected]/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH)
Caused by: java.lang.NoSuchMethodException: nl.altindag.crip.command.VersionProvider.<init>()
        at [email protected]/java.lang.Class.checkConstructor(DynamicHub.java:1171)
        at [email protected]/java.lang.Class.getConstructor0(DynamicHub.java:1365)
        at [email protected]/java.lang.Class.getDeclaredConstructor(DynamicHub.java:3004)
        at picocli.CommandLine$DefaultFactory.create(CommandLine.java:5660)
        at picocli.CommandLine$DefaultFactory.create(CommandLine.java:5687)
        ... 10 more

How to reproduce?

  1. Install Graal 22 and 23 from https://www.graalvm.org/downloads/
  2. Make sure your java home is configured to point to version Graal 22
  3. Install maven
  4. run git clone https://github.com/Hakky54/certificate-ripper.git
  5. run mvn clean install -Pnative-image
  6. run ./target/crip (it does not give a runtime exception)
  7. configure java home to graal 23
  8. repeat step 5 and 6 and the runtime exception will be shown

Any idea what the root cause could be? Adding a default constructor to the VersionProvider did not work.

@remkop
Copy link
Owner

remkop commented Dec 30, 2024

This sounds like a bug in GraalVM 23, but just to double check, can you provide the contents of your reflect.json config file?

@Hakky54
Copy link
Author

Hakky54 commented Dec 30, 2024

I had that feeling as well, but wasn't sure.

my graalvm config files looks like:

[
  {
    "name": "nl.altindag.crip.command.CertificateRipper",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true
  },
  {
    "name": "nl.altindag.crip.command.SharedProperties",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "fields": [
      {
        "name": "urls"
      },
      {
        "name": "proxyHost"
      },
      {
        "name": "proxyPort"
      },
      {
        "name": "proxyUser"
      },
      {
        "name": "proxyPassword"
      },
      {
        "name": "timeoutInMilliseconds"
      },
      {
        "name": "resolveRootCa"
      }
    ]
  },
  {
    "name": "nl.altindag.crip.command.PrintCommand",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "fields": [
      {
        "name": "sharedProperties"
      },
      {
        "name": "format"
      }
    ]
  },
  {
    "name": "nl.altindag.crip.command.export.ExportCommand",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true
  },
  {
    "name": "nl.altindag.crip.command.export.FileExport",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "fields": [
      {
        "name": "sharedProperties"
      },
      {
        "name": "destination"
      }
    ]
  },
  {
    "name": "nl.altindag.crip.command.export.CombinableFileExport",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "fields": [
      {
        "name": "combined"
      }
    ]
  },
  {
    "name": "nl.altindag.crip.command.export.KeyStoreExportCommand",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "fields": [
      {
        "name": "password"
      }
    ]
  },
  {
    "name": "nl.altindag.crip.command.export.Pkcs12ExportCommand",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true
  },
  {
    "name": "nl.altindag.crip.command.export.JavaKeyStoreExportCommand",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true
  },
  {
    "name": "nl.altindag.crip.command.export.DerExportCommand",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true
  },
  {
    "name": "nl.altindag.crip.command.export.PemExportCommand",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true,
    "fields": [
      {
        "name": "includeHeader"
      }
    ]
  }
]

I also attempted to add:

{
  "name": "nl.altindag.crip.command.VersionProvider",
  "allDeclaredConstructors": true,
  "allPublicConstructors": true,
  "allDeclaredMethods": true,
  "allPublicMethods": true
}

However that resulted in

Exception in thread "main" picocli.CommandLine$InitializationException: picocli.CommandLine$AutoHelpMixin@1e293ee2 is not a command: it has no @Command, @Option, @Parameters or @Unmatched annotations
        at picocli.CommandLine$Model$CommandReflection.validateCommandSpec(CommandLine.java:12029)
        at picocli.CommandLine$Model$CommandReflection.extractCommandSpec(CommandLine.java:11859)
        at picocli.CommandLine$Model$CommandSpec.forAnnotatedObject(CommandLine.java:6392)
        at picocli.CommandLine$Model$CommandSpec.mixinStandardHelpOptions(CommandLine.java:7389)
        at picocli.CommandLine$Model$CommandReflection.extractCommandSpec(CommandLine.java:11854)
        at picocli.CommandLine$Model$CommandSpec.forAnnotatedObject(CommandLine.java:6392)
        at picocli.CommandLine.<init>(CommandLine.java:230)
        at picocli.CommandLine.toCommandLine(CommandLine.java:3635)
        at picocli.CommandLine.access$16700(CommandLine.java:148)
        at picocli.CommandLine$Model$CommandReflection.initSubcommands(CommandLine.java:11884)
        at picocli.CommandLine$Model$CommandReflection.extractCommandSpec(CommandLine.java:11850)
        at picocli.CommandLine$Model$CommandSpec.forAnnotatedObject(CommandLine.java:6392)
        at picocli.CommandLine.<init>(CommandLine.java:230)
        at picocli.CommandLine.<init>(CommandLine.java:224)
        at picocli.CommandLine.<init>(CommandLine.java:199)
        at nl.altindag.crip.App.main(App.java:25)
        at [email protected]/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH)

@Hakky54
Copy link
Author

Hakky54 commented Dec 30, 2024

@remkop
Copy link
Owner

remkop commented Jan 21, 2025

In addition to adding

{
  "name": "nl.altindag.crip.command.VersionProvider",
  "allDeclaredConstructors": true,
  "allPublicConstructors": true,
  "allDeclaredMethods": true,
  "allPublicMethods": true
}

Can you also try adding this?

  {
    "name" : "picocli.CommandLine$AutoHelpMixin",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true,
    "fields" : [
      { "name" : "helpRequested" },
      { "name" : "versionRequested" }
    ]
  }

These should have been added by the picocli annotation processor. This may be a bug in the picocli annotation processor.

@Hakky54
Copy link
Author

Hakky54 commented Jan 21, 2025

This did the trick, amazing that you figured it out! Any idea why this issue is popping up with GraalVM 23? and not on version 22?

@Hakky54
Copy link
Author

Hakky54 commented Jan 27, 2025

Hi @remkop I am wondering what would be the proper follow up. So is this something you can fix on your side in the library?
If that is the case, should I inform Graal team to close the issue on their side?

Or do you think I should just update the json property for graal on my side in my project and close this issue as well as the issue on graal project?

@remkop remkop added this to the 4.7.7 milestone Jan 28, 2025
@remkop remkop added type: bug 🐛 theme: annotation-proc An issue or change related to the annotation processor labels Jan 28, 2025
@remkop
Copy link
Owner

remkop commented Jan 28, 2025

From the GraalVM point of view, it is strange that the configuration works in Graal 22 but not on Graal 23. So that may still be interesting for the GraalVM team to look at. You may want to give them the info that we got it to work in Graal 23 by adding this extra config; question is why did it not fail in Graal 22?

Then, from the picocli point of view, I need to spend time creating a test in picocli-codegen-tests-java9plus with something like your application, and step through the debugger to see what the annotation processor is doing and why it is missing your VersionProvider and the built-in AutoHelpMixin.
I suspect there is a bug in the picocli annotation processor.
However, I cannot promise anything about when I will have time to look at it.

(If anyone volunteers to track this down and provide a fix, that would be awesome.)

@Hakky54
Copy link
Author

Hakky54 commented Jan 28, 2025

Indeed if it works on graalvm 22 then it should also have worked on version 23. I will update them with the new finding.

I suspect there is a bug in the picocli annotation processor.
However, I cannot promise anything about when I will have time to look at it.
(If anyone volunteers to track this down and provide a fix, that would be awesome.)

I would like to give it a shot, let me see whether I can figure it out.

@Hakky54
Copy link
Author

Hakky54 commented Jan 28, 2025

I found the issue... And it was not related to your library. The codegen works correct. I was just manually creating my graalvm_config.json file and updating also manually. By adding the versionprovider and also the help function in the code I didn't added that part in the graalvm config json file as I didn't knew it was needed.

I went through the documentation of picocli-codegen to understand how it works here. I made the adjustments to my project in this PR: Hakky54/certificate-ripper#57

The generated reflec-configt.json looks like:

Generated GraalVM config file
[
  {
    "name" : "java.lang.Boolean",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true
  },
  {
    "name" : "java.lang.Integer",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true
  },
  {
    "name" : "java.util.Collections$UnmodifiableRandomAccessList",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true
  },
  {
    "name" : "nl.altindag.crip.command.CertificateRipper",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true
  },
  {
    "name" : "nl.altindag.crip.command.PrintCommand",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true,
    "fields" : [
      { "name" : "format" },
      { "name" : "sharedProperties" }
    ]
  },
  {
    "name" : "nl.altindag.crip.command.PrintCommand$Format",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true
  },
  {
    "name" : "nl.altindag.crip.command.SharedProperties",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true,
    "fields" : [
      { "name" : "proxyHost" },
      { "name" : "proxyPassword" },
      { "name" : "proxyPort" },
      { "name" : "proxyUser" },
      { "name" : "resolveRootCa" },
      { "name" : "timeoutInMilliseconds" },
      { "name" : "urls" }
    ]
  },
  {
    "name" : "nl.altindag.crip.command.VersionProvider",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true
  },
  {
    "name" : "nl.altindag.crip.command.export.CombinableFileExport",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true,
    "fields" : [
      { "name" : "combined" }
    ]
  },
  {
    "name" : "nl.altindag.crip.command.export.DerExportCommand",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true
  },
  {
    "name" : "nl.altindag.crip.command.export.ExportCommand",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true
  },
  {
    "name" : "nl.altindag.crip.command.export.FileExport",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true,
    "fields" : [
      { "name" : "destination" },
      { "name" : "sharedProperties" }
    ]
  },
  {
    "name" : "nl.altindag.crip.command.export.JavaKeyStoreExportCommand",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true
  },
  {
    "name" : "nl.altindag.crip.command.export.KeyStoreExportCommand",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true,
    "fields" : [
      { "name" : "password" }
    ]
  },
  {
    "name" : "nl.altindag.crip.command.export.PemExportCommand",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true,
    "fields" : [
      { "name" : "includeHeader" }
    ]
  },
  {
    "name" : "nl.altindag.crip.command.export.Pkcs12ExportCommand",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true
  },
  {
    "name" : "picocli.CommandLine$AutoHelpMixin",
    "allDeclaredConstructors" : true,
    "allPublicConstructors" : true,
    "allDeclaredMethods" : true,
    "allPublicMethods" : true,
    "fields" : [
      { "name" : "helpRequested" },
      { "name" : "versionRequested" }
    ]
  }
]

I still find it strange that my manually created config file is working with graalvm 22 and not anymore with graalvm 23. But that is something which I will share with the graal vm developers.

I created the native executable with graalvm 23 and it works. I think we can close this issue as there is no issue at all with picocli., wdyt?

@remkop
Copy link
Owner

remkop commented Jan 29, 2025

Oh 😅 phew, that is good to hear!

Yes, happy to close if there's no issue with picocli-codegen.

@remkop remkop closed this as completed Jan 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
theme: annotation-proc An issue or change related to the annotation processor type: bug 🐛
Projects
None yet
Development

No branches or pull requests

2 participants