aws / serverless-java-container

A Java wrapper to run Spring, Spring Boot, Jersey, and other apps inside AWS Lambda.
https://aws.amazon.com/serverless/
Apache License 2.0
1.48k stars 551 forks source link

Jackson afterburner module is not compatible with Java11 and GraalVM native image #428

Open mokmnovatti opened 2 years ago

mokmnovatti commented 2 years ago

Not sure i'm missing something or not but Jackson AfterBurner module is not compatible with java11 and throwing following error when it is compiled with graalvm-21.01 native image

Error loading class my.service.StreamLambdaHandler: Target_java_lang_ClassLoader.getClassLoadingLock(String): com.oracle.svm.core.jdk.UnsupportedFeatureError
--
com.oracle.svm.core.jdk.UnsupportedFeatureError: Target_java_lang_ClassLoader.getClassLoadingLock(String)
at com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:88)
at java.lang.ClassLoader.getClassLoadingLock(ClassLoader.java:259)
at com.fasterxml.jackson.module.afterburner.util.MyClassLoader.loadAndResolve(MyClassLoader.java:79)
at com.fasterxml.jackson.module.afterburner.deser.PropertyMutatorCollector.generateMutatorClass(PropertyMutatorCollector.java:176)
at com.fasterxml.jackson.module.afterburner.deser.PropertyMutatorCollector.buildMutator(PropertyMutatorCollector.java:102)
at com.fasterxml.jackson.module.afterburner.deser.DeserializerModifier.updateBuilder(DeserializerModifier.java:65)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:287)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:150)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:414)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:349)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:591)
at com.fasterxml.jackson.databind.ObjectReader._prefetchRootDeserializer(ObjectReader.java:2340)
at com.fasterxml.jackson.databind.ObjectReader.<init>(ObjectReader.java:192)
at com.fasterxml.jackson.databind.ObjectMapper._newReader(ObjectMapper.java:736)
at com.fasterxml.jackson.databind.ObjectMapper.readerFor(ObjectMapper.java:4051)
at com.amazonaws.serverless.proxy.internal.LambdaContainerHandler.<init>(LambdaContainerHandler.java:107)
at com.amazonaws.serverless.proxy.internal.LambdaContainerHandler.<init>(LambdaContainerHandler.java:118)
at com.amazonaws.serverless.proxy.internal.servlet.AwsLambdaServletContainerHandler.<init>(AwsLambdaServletContainerHandler.java:75)
at com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler.<init>(SpringBootLambdaContainerHandler.java:135)
at com.amazonaws.serverless.proxy.spring.SpringBootProxyHandlerBuilder.build(SpringBootProxyHandlerBuilder.java:61)
at com.amazonaws.serverless.proxy.spring.SpringBootProxyHandlerBuilder.buildAndInitialize(SpringBootProxyHandlerBuilder.java:80)
at com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler.getAwsProxyHandler(SpringBootLambdaContainerHandler.java:93)
at my.service.StreamLambdaHandler.<clinit>(StreamLambdaHandler.java:27)
at com.oracle.svm.core.classinitialization.ClassInitializationInfo.invokeClassInitializer(ClassInitializationInfo.java:375)
at com.oracle.svm.core.classinitialization.ClassInitializationInfo.initialize(ClassInitializationInfo.java:295)
at java.lang.Class.ensureInitialized(DynamicHub.java:548)
at com.oracle.svm.core.hub.ClassForNameSupport.forNameOrNull(ClassForNameSupport.java:63)
at com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:69)
at java.lang.Class.forName(DynamicHub.java:1319)

https://github.com/awslabs/aws-serverless-java-container/blob/a72bad46a263d07d71fc2765be94899bfddbba11/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandler.java#L88

but when the afterburner module is removed it is working fine !

might be good idea to migrate to https://github.com/stevenschlansker/jackson-blackbird which is promising with Java11

Find the code at https://github.com/mokmnovatti/my-service

deki commented 2 years ago

Yeah, that is a known issue, see #369.

https://github.com/FasterXML/jackson-modules-base/tree/master/blackbird looks like the way to go. Would you be willing to create a PR for that?

mokmnovatti commented 2 years ago

Sure thing !

mokmnovatti commented 2 years ago

After some testings realizes that blackbird also not supporting native image generation with graal and it is throwing the exception below

Error loading class my.service.StreamLambdaHandler: Defining anonymous classes at runtime is not supported.: com.oracle.svm.core.jdk.UnsupportedFeatureError
com.oracle.svm.core.jdk.UnsupportedFeatureError: Defining anonymous classes at runtime is not supported.
at com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:88)
at jdk.internal.misc.Unsafe.defineAnonymousClass(Unsafe.java:211)
at java.lang.invoke.InnerClassLambdaMetafactory.spinInnerClass(InnerClassLambdaMetafactory.java:302)
at java.lang.invoke.InnerClassLambdaMetafactory.buildCallSite(InnerClassLambdaMetafactory.java:193)
at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:329)
at com.fasterxml.jackson.module.blackbird.deser.BBDeserializerModifier.createSetter(BBDeserializerModifier.java:204)
at com.fasterxml.jackson.module.blackbird.deser.BBDeserializerModifier.nextProperty(BBDeserializerModifier.java:193)
at com.fasterxml.jackson.module.blackbird.deser.BBDeserializerModifier.findOptimizableProperties(BBDeserializerModifier.java:126)
at com.fasterxml.jackson.module.blackbird.deser.BBDeserializerModifier.updateBuilder(BBDeserializerModifier.java:79)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:287)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:150)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:414)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:349)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:591)
at com.fasterxml.jackson.databind.ObjectReader._prefetchRootDeserializer(ObjectReader.java:2340)
at com.fasterxml.jackson.databind.ObjectReader.<init>(ObjectReader.java:192)
at com.fasterxml.jackson.databind.ObjectMapper._newReader(ObjectMapper.java:736)
at com.fasterxml.jackson.databind.ObjectMapper.readerFor(ObjectMapper.java:4051)
at com.amazonaws.serverless.proxy.internal.LambdaContainerHandler.<init>(LambdaContainerHandler.java:111)
at com.amazonaws.serverless.proxy.internal.LambdaContainerHandler.<init>(LambdaContainerHandler.java:122)
at com.amazonaws.serverless.proxy.internal.servlet.AwsLambdaServletContainerHandler.<init>(AwsLambdaServletContainerHandler.java:75)

image

To support native-image generation, need to get rid of this after burner module as in the,

https://github.com/awslabs/aws-serverless-java-container/blob/3e55220f53182017a39cfe4905cb82cd149d45e1/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandler.java#L83

going forward jdk > 16 is not supporting the Unsafe.defineAnonymousClass https://bugs.openjdk.java.net/browse/JDK-8243287 so better to wait for graal 21.3

mokmnovatti commented 2 years ago

Workaround ->

import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;

@TargetClass(className = "com.amazonaws.serverless.proxy.internal.LambdaContainerHandler.class")
final class LambdaContainerHandlerSubstitute {

    @Substitute
    private static void registerAfterBurner() {
        System.out.println("Registering after burner is not supported by the Graal VM yet ! ");
    }
}
siddharthjain210 commented 2 years ago

Is there any fix available for jackson for JAVA-11, serverless-container 1.6? I am getting the same issue.

deki commented 2 years ago

@siddharthjain210 have you read the previous comments?

We are open for suggestions on how to fix it. For now the workaround above can be used.

siddharthjain210 commented 2 years ago

I am not able to understand where should i add this class. Should it be added in the application module. How would the imports from com.oracle.svm would be resolved.

mokmnovatti commented 2 years ago

@siddharthjain210

Please use it like below

Add the following dependency to your project.

        <dependency>
            <groupId>org.graalvm.nativeimage</groupId>
            <artifactId>svm</artifactId>
            <version>${graalvm-nativeimage-svm.version}</version>
            <scope>provided</scope>
        </dependency>

Create a class in your project with the above content.

When you build your native image, it will load this substitution from the classpath.

siddharthjain210 commented 2 years ago

Thanks @mokmnovatti @deki. Able to resolve the issue with the temporary fix. Just needed to add a little change:

import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;

@TargetClass(com.amazonaws.serverless.proxy.internal.LambdaContainerHandler.class)
public final class LambdaContainerHandlerSubstitute {

    @Substitute
    private static void registerAfterBurner() {
        System.out.println("Registering after burner is not supported by the Graal VM yet ! ");
    }
}

The GraalVm 21.3 throws an error on using className on @Target.

joain946 commented 2 years ago

Is there a workaround for projects that doesn't use GraalVM? I have a regular Java 11 Lambda which shows this warning during each cold start. Which is a bit annoying. Since the method is static it means that I can't override it and there is no way of creating my own ObjectMapper that then can be used. So a way of override it or if it would default use Blackbird would solve my issue.

deki commented 2 years ago

@joain946 as mentioned above we are open for suggestions.

The critical part is in https://github.com/awslabs/aws-serverless-java-container/blob/3b5f1f021dc102678e2fb4da24c798b13b2457dd/aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/LambdaContainerHandler.java#L88 so in case you want to create a pull request with a proposal on how to change it, we will definitely review it.

joain946 commented 2 years ago

The problem is that I don't have enough knowledge on what is going on here to be able to do a PR.

First of all, is Afterburner needed at all? Because removing it would solve everything. Not sure if it was added for performance or functionality.

Second would be to not use a static method for registering since that can't be overriden. But maybe that is causing issues for GraalVM instead?

deki commented 2 years ago

Well in my view it's not required but the performance is worse without it. So for 2.0 we should probably replace Afterburner with Blackbird and make it configurable so it can be disabled.

deki commented 2 years ago

Just found https://github.com/quarkusio/qson, maybe also worth trying, works well with GraalVM and Quarkus.

Muthuveerappanv commented 2 years ago

@mokmnovatti I'm trying to create a native-springboot app with graal 21.1 and spring-native. your sample code was helpful in resolving some of the reflection issues. I'm facing this issue now:

2022-05-30 21:22:26.675  WARN 10 --- [           main] w.s.c.ServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.

Have you faced this issue? any help would be appreciated, thanks

Another user has posted in stackoverflow about the same issue - https://stackoverflow.com/questions/72162962/native-image-spring-boot-aws-serverless-java-container-startup-error-mis

jsyrjala commented 1 year ago

Any plans, estimates or guesses when aws-serverless-java-container 2.0 might be available?

deki commented 7 months ago

We've added a sample for native https://github.com/aws/serverless-java-container/tree/main/samples/springboot3/pet-store-native which is working well. Removing the 2.0 milestone as this issue is no longer a blocker for GraalVM.