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

Failed to generate method handle for GraalVM 21.1.0 for dynamodb-enhanced client #3386

Open
Aleksandr-Filichkin opened this issue Apr 29, 2021 · 15 comments
Assignees

Comments

@Aleksandr-Filichkin
Copy link

Aleksandr-Filichkin commented Apr 29, 2021

Hi,

I'm trying to compile dynamodb-enhanced client with Graalvm 21.1(graalvm-ce-java11-21.1.0) and awssdk.version=2.16.50
Here the code:
DynamoDbTable<Book> dynamoDbTable = dynamoDbEnhancedClient.table(TABLE_NAME, TableSchema.fromBean(Book.class));

Reflection config doesn't help. I generated native-image artifacts with -agentlib: but it still doesn't help.
GraalVM 21.1 supports Method Handler, I hope it can be fixed.
https://github.com/aws/aws-sdk-java-v2/blob/master/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/mapper/LambdaToMethodBridgeBuilder.java#L76

Error: Exception in thread "main" java.lang.ExceptionInInitializerError at com.oracle.svm.core.classinitialization.ClassInitializationInfo.initialize(ClassInitializationInfo.java:315) Caused by: java.lang.IllegalArgumentException: Failed to generate method handle. at software.amazon.awssdk.enhanced.dynamodb.internal.mapper.LambdaToMethodBridgeBuilder.build(LambdaToMethodBridgeBuilder.java:92) at software.amazon.awssdk.enhanced.dynamodb.internal.mapper.ObjectConstructor.create(ObjectConstructor.java:37) at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.newObjectSupplierForClass(BeanTableSchema.java:361) at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.createStaticTableSchema(BeanTableSchema.java:172) at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.create(BeanTableSchema.java:129) at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.create(BeanTableSchema.java:121) at software.amazon.awssdk.enhanced.dynamodb.TableSchema.fromBean(TableSchema.java:81) at com.filichkin.blog.lambda.v3.handler.test.Main.initDispatcher(Main.java:39)

@Aleksandr-Filichkin Aleksandr-Filichkin changed the title Failed to generate method handle for GraalVM 21.1.0 for dynamodb-enhanced Failed to generate method handle for GraalVM 21.1.0 for dynamodb-enhanced client Apr 29, 2021
@rohit-gandhe
Copy link

I have this problem with 20.0.0.2 as well

Caused by: java.lang.IllegalArgumentException: Failed to generate method handle. at software.amazon.awssdk.enhanced.dynamodb.internal.mapper.LambdaToMethodBridgeBuilder.build(LambdaToMethodBridgeBuilder.java:92) at software.amazon.awssdk.enhanced.dynamodb.internal.mapper.ObjectConstructor.create(ObjectConstructor.java:37) at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.newObjectSupplierForClass(BeanTableSchema.java:361) at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.createStaticTableSchema(BeanTableSchema.java:172) at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.create(BeanTableSchema.java:129) at software.amazon.awssdk.enhanced.dynamodb.mapper.BeanTableSchema.create(BeanTableSchema.java:121) at software.amazon.awssdk.enhanced.dynamodb.TableSchema.fromBean(TableSchema.java:81) at software.amazon.awssdk.enhanced.dynamodb.TableSchema.fromClass(TableSchema.java:121) at c.a.d.QxxxxxResource.createQuanta(QxxxxxxResource.java:35) at java.lang.reflect.Method.invoke(Method.java:566) at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:170) at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130) at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:660) at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:524) at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:474) at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364) at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:476) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:434) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:408) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:69) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:492) ... 17 more Caused by: com.oracle.svm.core.jdk.UnsupportedFeatureError: Defining new classes at run time is not supported. All classes need to be known at image build time. at com.oracle.svm.core.util.VMError.unsupportedFeature(VMError.java:87) at jdk.internal.misc.Unsafe.defineAnonymousClass(Unsafe.java:246) at java.lang.invoke.InnerClassLambdaMetafactory.spinInnerClass(InnerClassLambdaMetafactory.java:302)

@Aleksandr-Filichkin
Copy link
Author

any updates?

@dynaxis
Copy link

dynaxis commented Aug 9, 2021

It seems I'm experiencing the same w/ 21.2.

@loicottet
Copy link
Member

The code being used is using the lambda metafactory feature, which tries to define a new class at runtime, and that is currently not supported by Native Image. We are working to support this use case but it still requires some implementation work to be done.

@Alex100
Copy link

Alex100 commented Aug 9, 2021

As workaround you can write a function which creates a StaticTableSchema. I hacked a simple one which uses Micronauts introspection API (can be replaced by Javas reflection calls https://www.graalvm.org/reference-manual/native-image/Reflection/). It supports beans with fields annotated with @DynamoDbPartitionKey and @DynamoDbAttribute. Furthermore nesting ofLists of @DynamoDbBeans is supported:

fun <T> createWorkaroundSchema(beanClass: Class<T>): StaticTableSchema<T>? {
    val builder = StaticTableSchema.builder(beanClass).newItemSupplier { beanClass.getConstructor().newInstance() }
    val introspection = BeanIntrospection.getIntrospection(beanClass)
    introspection.beanProperties
        .filter { it.annotationMetadata.hasAnnotation(DynamoDbPartitionKey::class.java) }
        .forEach { prop ->
            builder.addAttribute(prop.type, Consumer { attr ->
                attr
                    .name(prop.name)
                    .getter(prop::get)
                    .setter(prop::set)
                    .tags(StaticAttributeTags.primaryPartitionKey())
            })
        }

    introspection.beanProperties
        .filter { prop -> prop.annotationMetadata.hasAnnotation(DynamoDbAttribute::class.java) }
        .forEach { prop ->
            if (prop.type == List::class.java) {
                val itemClass = prop.asArgument().typeParameters.first().type

                val itemType = if (itemClass.isPrimitive || itemClass == String::class.java) {
                    EnhancedType.of(itemClass)
                }
                else {
                    val itemSchema = createWorkaroundSchema(itemClass)
                    EnhancedType.documentOf(itemClass, itemSchema)
                }

                val listType = EnhancedType.listOf(itemType)

                builder.addAttribute(listType) { attr ->
                    attr.name(prop.name)
                        .getter { instance -> prop.get(instance) as List<Any> }
                        .name(prop.name)
                        .setter(prop::set)
                }
            } else {
                builder.addAttribute(prop.type) { attr ->
                    attr.name(prop.name)
                        .getter(prop::get)
                        .setter(prop::set)
                }
            }
        }

    return builder.build()
} 

@deandelponte
Copy link

This issue persists with GraalVM version 21.3.0.r11 as well

@deandelponte
Copy link

deandelponte commented Nov 17, 2021

@Alex100 do you happen to have an example app demonstrating how your workaround is used?

@deandelponte
Copy link

As a workaround, I've been able to use StaticTableSchema for mapping simple objects, but run into issues when mapping more complex attributes. For example, how would I map something like this using StaticTableSchema?

Please note, CustomerOrgData is a bean and all fields are Strings.

@DynamoDbBean
public class CustomerData {
  private String id = UUID.randomUUID().toString();
  private String name;
  private String description;
  private List<CustomerOrgData> customerOrgData = new ArrayList<CustomerOrgData>();

  @DynamoDbPartitionKey
  public String getId() {
    return id;
  }

  public void setId(@NonNull @NotBlank String id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(@NonNull @NotBlank String name) {
    this.name = name;
  }

  public String getDescription() {
    return description;
  }

  public void setDescription(String description) {
    this.description = description;
  }

  public List<CustomerOrgData> getCustomerOrgData() {
    if (customerOrgData == null || customerOrgData.isEmpty()) {
      return new ArrayList<>();
    }
    return CustomerDataUtils.sortCustomerOrgData(customerOrgData);
  }

  public void setCustomerOrgData(List<CustomerOrgData> customerOrgData) {
    this.customerOrgData = customerOrgData;
  }

  public void addCustomerOrgData(CustomerOrgData customerOrgData) {
    this.customerOrgData.add(customerOrgData);
  }
}

@joshtexas
Copy link

joshtexas commented Nov 29, 2021

@loicottet Just wondering if you have an update on this? Is this something that is still going to be a number of months away? Thanks.

@rafalwrzeszcz
Copy link

rafalwrzeszcz commented Dec 2, 2021

@deandelponte I also run into trouble with that - had to look into BeanTableDefinition to see how it's handled, as documentation says nothing about it.

You need a schema for each "document" class, regardless if it's table-level entity or nested one:

    private val addressGeoLocationSchema by lazy {
        TableSchema.builder(Address.GeoLocation::class.java)
            .newItemSupplier { Address.GeoLocation() }
            .addAttribute(Double::class.java) {
                it
                    .name("latitude")
                    .getter(Address.GeoLocation::latitude::get)
                    .setter(Address.GeoLocation::latitude::set)
            }
            .addAttribute(Double::class.java) {
                it
                    .name("longitude")
                    .getter(Address.GeoLocation::longitude::get)
                    .setter(Address.GeoLocation::longitude::set)
            }
            .addAttribute(Double::class.java) {
                it
                    .name("altitude")
                    .getter(Address.GeoLocation::altitude::get)
                    .setter(Address.GeoLocation::altitude::set)
            }
            .build()
    }

    private val addressTableSchema by lazy {
        // temporarily not possible for GraalVM - https://github.com/oracle/graal/issues/3386
        //TableSchema.fromClass(Address::class.java)
        TableSchema.builder(Address::class.java)
            .newItemSupplier { Address() }
            .addAttribute(EnhancedType.documentOf(Address.GeoLocation::class.java, addressGeoLocationSchema)) {
                it
                    .name("geoLocation")
                    .getter(Address::geoLocation::get)
                    .setter(Address::geoLocation::set)
            }
            .build()
    }

@JouperCoding
Copy link

JouperCoding commented Oct 10, 2022

Hello,

I got the same limitation, tested this with GraalVM 22.2.0 (Windows) seems to be related to how this library is using java.awt. Started a discussion here to see if something can be done to improve the out of box experience. aws/aws-sdk-java-v2#3473

It may be possible to use @DynamoDbBean and @DynamoDbAttributes with TableSchema.fromClass() with proper NI configuration, but using java.awt may bloat the native-image unnecessarily. My setup is very similar to @Alex100 where we use the micronaut framework.

See: #4659 which seems to be somewhat similar to this.
and
See. #2545 Add java AWT support

@gvart
Copy link

gvart commented Feb 15, 2023

Any progress on that?

@loicottet
Copy link
Member

LambdaMetafactory work has been pushed back by higher-priority issues recently, we're still hoping to get to it soon.

@joshtexas
Copy link

LambdaMetafactory work has been pushed back by higher-priority issues recently, we're still hoping to get to it soon.

Thanks for the update!

@amitmelbourne
Copy link

Hi Team @graemerocher is there any update on the above issue.

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

No branches or pull requests