jjwt icon indicating copy to clipboard operation
jjwt copied to clipboard

graal support?

Open janjangao opened this issue 4 years ago • 19 comments

I use jjwt generate token for my micronaut app,I use graalvm build native-image, when generate token, I get this error:

io.jsonwebtoken.lang.UnknownClassException: Unable to load class named [io.jsonwebtoken.impl.DefaultJwtBuilder] from the thread context, current, or system/application ClassLoaders. All heuristics have been exhausted. Class could not be found. Have you remembered to include the jjwt-impl.jar in your runtime classpath?

I already setting jjwt-impl.jar in build.gradle, but because of native-image has limitation with reflection, so dynamic class loading can not work, I follow the graal manual add reflection-config.json in META-INF and its working!

[ { "name" : "io.jsonwebtoken.impl.DefaultJwtBuilder", "allDeclaredFields" : true, "allPublicMethods" : true, "allDeclaredConstructors" : true } ]

so can we just put the graal config in jjwt-impl.jar to support graal native-image?

janjangao avatar Dec 28 '20 10:12 janjangao

We'd welcome a pull request for this, but it's low priority for the core JJWT team until 1.0 is released.

lhazlewood avatar Feb 15 '21 23:02 lhazlewood

This issue has been automatically marked as stale due to inactivity for 60 or more days. It will be closed in 7 days if no further activity occurs.

stale[bot] avatar Aug 04 '21 23:08 stale[bot]

Only adding DefaultJwtBuilder will not be sufficient if you want to parse a JWT. I've got jjwt working in a Graal native image build now. Parsing a JWT can be done like this:

            DefaultJwtParserBuilder()
                .deserializeJsonWith(JacksonDeserializer())
                .build()
                .parse...

You'll also need some config files to get compression to work - which unfortunately you currently can't get rid of as it's always getting statically initialized even when not needed.

META-INF/native-image/someprojectname/resource-config.json

{
  "resources": {
    "includes": [
      {
        "pattern": "META-INF/services/io.jsonwebtoken.CompressionCodec"
      }
    ]
  }
}

META-INF/native-image/someprojectname/reflect-config.json

[
  {
    "name": "io.jsonwebtoken.impl.compression.GzipCompressionCodec",
    "methods": [
      {
        "name": "<init>",
        "parameterTypes": []
      }
    ]
  },
  {
    "name": "io.jsonwebtoken.impl.compression.DeflateCompressionCodec",
    "methods": [
      {
        "name": "<init>",
        "parameterTypes": []
      }
    ]
  }
]

These files could also be bundled with the jjwt artifact and then it would work more out of the box. Alternatively, it would be great if jjwt would use less dynamic classloading logic.

MarkusKramer avatar Sep 03 '21 22:09 MarkusKramer

Thanks for the update @MarkusKramer . Is it possible to declare DefaultJwtParserBuilder and DefaultJwtBuilder in resource-config.json as well, and then have it work 'out of the box' without having to use jjwt-impl in compile-scope?

Nothing in the impl module is guaranteed for backwards compatibility. If you directly use its classes as above, breaking changes could occur on future upgrades.

lhazlewood avatar Sep 06 '21 18:09 lhazlewood

Yes, you can use Jwts.parserBuilder() if you add to reflect-config.json

  {
    "name": "io.jsonwebtoken.impl.DefaultJwtParserBuilder",
    "methods": [
      {
        "name": "<init>",
        "parameterTypes": []
      }
    ]
  },

Just tested with Graal. And similar for the builder and other classes.

When including theses configs in jjwt-impl it should work out of the box. However... one key benefit of Graal is to get rid of unneeded code, and we're sort of preventing it this way. It would be better to have less reflection in jjwt, by Jwts doing normal class initialization that can be statically analyzed by tools. And DefaultJwtParserBuilder not initializing DefaultCompressionCodecResolver unless actually needed.

MarkusKramer avatar Sep 06 '21 20:09 MarkusKramer

A CompressionCodecResolver is always needed in case a zip header is encountered, but I understand your point around reflection.

We have discussed at times moving the impl package and its sub-packages into a single .jar and changing everything in there to package protected to prevent people from depending on those classes, but there needs to be some significant analysis to ensure nothing can 'leak' unexpectedly. It'll be a decent amount of work.

A reflect-config.json might be a better/faster stop-gap in the meantime however.

lhazlewood avatar Sep 06 '21 21:09 lhazlewood

This issue has been automatically marked as stale due to inactivity for 60 or more days. It will be closed in 7 days if no further activity occurs.

stale[bot] avatar Apr 16 '22 18:04 stale[bot]

Looks like significant work to support something like this will have to wait until after 1.0 - highest priority is to get JWE released.

lhazlewood avatar Apr 18 '22 18:04 lhazlewood

(at least unless someone submits a PR that can facilitate this for the current module structure)

lhazlewood avatar Apr 18 '22 18:04 lhazlewood

  • Considering that the 1.0 version involved in this issue is likely to be far away from us, I suggest closing this issue.

  • A more reasonable move is to submit the corresponding GraalVM metadata in Oracle's https://github.com/oracle/graalvm-reachability-metadata repository. This will help deal with the problem right away.

linghengqian avatar Aug 12 '22 16:08 linghengqian

@linghengqian thanks for the comment and insight! I think we could leave this open just to ensure we can track it until after the 1.0 release. JJWT's jwe work is mostly finished (mostly working on documentation now), and given the additions and package changes, I agree it doesn't make as much sense to work on this ticket until that is released. At least that way we can have a stable package structure to use to identify any GraalVM-related package metadata.

There will be final API changes after the jwe merge, but before the 1.0 release - mostly dropping or adding certain things to ensure best use of Java 8 APIs (functions, etc). But also perhaps some package changes due to the way the JDK Module System works (api vs impl packages). Then once 1.0 is done, any package-related metadata would make sense since the package structure should be more than stable at that point.

I do agree that, if someone wants this now, adding a PR to the https://github.com/oracle/graalvm-reachability-metadata repository would be the way to go, and then issue another PR after the JJWT 1.0 release.

lhazlewood avatar Aug 12 '22 16:08 lhazlewood

  • Late reply, I've been busy with other work before. The current PR is at https://github.com/oracle/graalvm-reachability-metadata/pull/116 . If anyone thinks that the GraalVM reachability metadata generated by my unit test is missing, point it out in the issue at https://github.com/oracle/graalvm-reachability-metadata.

linghengqian avatar Nov 17 '22 16:11 linghengqian

  • All Done in https://github.com/oracle/graalvm-reachability-metadata/pull/116. The problems encountered in the middle need to go to https://github.com/jwtk/jjwt/issues/762.

linghengqian avatar Jan 10 '23 13:01 linghengqian

UPD: Nvm, realised the version is out of date, works fine on newer ones.

Hi, I'm using just jjwt-api:0.10.5 and experiencing the following exception with native images:

io.jsonwebtoken.lang.UnknownClassException: Unable to load class named [io.jsonwebtoken.impl.io.RuntimeClasspathSerializerLocator] from the thread context, current, or system/application ClassLoaders.  All heuristics have been exhausted.  Class could not be found.
        at io.jsonwebtoken.lang.Classes.forName(Classes.java:92) ~[na:na]
        at io.jsonwebtoken.lang.Classes.newInstance(Classes.java:136) ~[na:na]
        at io.jsonwebtoken.impl.DefaultJwtBuilder.compact(DefaultJwtBuilder.java:299) ~[core:na]

Am I missing something or is the API library itself just missing from graalvm-reachability-metadata?

Haarolean avatar Jun 24 '23 18:06 Haarolean

Hey! Worked fine with 0.11.5 but to be broken/unsupported since 0.12.x again.

Caused by: io.jsonwebtoken.lang.UnknownClassException: Unable to load class named [io.jsonwebtoken.impl.security.StandardSecureDigestAlgorithms] from the thread context, current, or system/application ClassLoaders.  All heuristics have been exhausted.  Class could not be found.  Have you remembered to include the jjwt-impl.jar in your runtime classpath?
	at io.jsonwebtoken.lang.Classes.forName(Classes.java:90) ~[na:na]
	at io.jsonwebtoken.lang.Classes.newInstance(Classes.java:173) ~[na:na]
	at io.jsonwebtoken.Jwts$SIG.<clinit>(Jwts.java:174) ~[na:na]

@linghengqian any chance you would want to make this happen again? 🙏

akaiser avatar Dec 03 '23 16:12 akaiser

Hey! Worked fine with 0.11.5 but to be broken/unsupported since 0.12.x again.

Caused by: io.jsonwebtoken.lang.UnknownClassException: Unable to load class named [io.jsonwebtoken.impl.security.StandardSecureDigestAlgorithms] from the thread context, current, or system/application ClassLoaders.  All heuristics have been exhausted.  Class could not be found.  Have you remembered to include the jjwt-impl.jar in your runtime classpath?
	at io.jsonwebtoken.lang.Classes.forName(Classes.java:90) ~[na:na]
	at io.jsonwebtoken.lang.Classes.newInstance(Classes.java:173) ~[na:na]
	at io.jsonwebtoken.Jwts$SIG.<clinit>(Jwts.java:174) ~[na:na]

@linghengqian any chance you would want to make this happen again? 🙏

  • This involves additional JSON entries. You only need to submit additional PRs to https://github.com/oracle/graalvm-reachability-metadata , or add relevant JSON entries within your own project.

  • Unfortunately I haven't had time to deal with this issue in recent months. Any friend interested in this error can submit a PR to https://github.com/oracle/graalvm-reachability-metadata .

linghengqian avatar Dec 03 '23 16:12 linghengqian