GoodforGod / graalvm-hint

🧰 Generates GraalVM configuration hints for native-image applications.
http://goodforgod.dev/graalvm-hint/
Apache License 2.0
33 stars 4 forks source link

Reflection registration for getter / setter #19

Open cmdjulian opened 3 months ago

cmdjulian commented 3 months ago

Is your feature request related to a problem? Please describe. Lets say I have the following Java class:

@ReflectionHint
public class RootFs {
   private final String type;
   private final List diffIds;

   public RootFs(String type, List diffIds) {
      this.type = type;
      this.diffIds = diffIds;
   }

   public String getType() {
      return this.type;
   }

   public List getDiffIds() {
      return this.diffIds;
   }
}

What actually gets generated is:

[{
  "name": "de.cmdjulian.kirc.spec.image.RootFs",
  "allDeclaredConstructors": true,
  "allDeclaredFields": true,
  "allDeclaredMethods": true
}]

On the runtime, this registers querying of all the different methods, but it does not register the getters nor the constructor. When I know try to serialize / deserialize such a class with Jackson, I get an error as nor the constructor, nor the getters are registered.

Describe the solution you'd like I'm not 100% sure how to resolve this, but I think maybe having a boolean flag in the annotation includeAccessors and includeConstructors with a default true setting would be the way to go. Additionally the annotation could be allowed to be placed on methods and constructors to register the annotated element as well.

Describe alternatives you've considered

Additional context Atm is quite impossible to use the lib with classes which do depend on getter / setter / constructor serialization like jackson does

cmdjulian commented 3 months ago

Okay seems like this was my bad. The problem seems to be the missing allDeclaredClasses property. From my feeling this should be included in the the ALL_DECLARED category.
Regardless, I would still vouch for having the ability to annotate constructor, field or method directly and register those

GoodforGod commented 3 months ago

Hi, I’ll check behavior, last time everything was working and I have working prototypes with such examples that were fine, will back when recheck Jackson behavior, maybe some changes in GraalVM

what GraalVM native image version/image you are using?

cmdjulian commented 3 months ago

I use the newest GraalVM version for Java 21 and gradle native build tool 0.10.2.

I have the following Kotlin code:

@ReflectionHint
class Repository(@JsonValue private val value: String) : Comparable<Repository> {
    override fun compareTo(other: Repository): Int = value.compareTo(other.value)
    override fun equals(other: Any?): Boolean = other is Repository && other.value == value
    override fun hashCode(): Int = value.hashCode()
    override fun toString(): String = value

    companion object {
        @JvmStatic
        @JsonCreator
        fun of(repository: String) = Repository(repository)
    }
}

This translates to the following java code:

public final class Repository implements Comparable<Repository> {

    public static final Companion Companion = new Companion();

    @JsonValue
    private final String value;

    public Repository(String value) {
        this.value = value;
    }

    public int compareTo(Repository other) {
        return this.value.compareTo(other.value);
    }

    public boolean equals(Object other) {
        return other instanceof Repository && Objects.equals(((Repository) other).value, this.value);
    }

    public int hashCode() {
        return this.value.hashCode();
    }

    public String toString() {
        return this.value;
    }

    @JsonCreator
    public static Repository of(String repository) {
        return Companion.of(repository);
    }

    public static final class Companion {
        private Companion() {
        }

        @JsonCreator
        public Repository of(String repository) {
            return new Repository(repository);
        }
    }
}

As you can see Kotlin creates a inner static class. When looking at the generated reflection data from the tracing agent, the following is request during runtime:

[
  {
    "name": "de.cmdjulian.kirc.image.Repository",
    "allDeclaredFields": true,
    "queryAllDeclaredMethods": true,
    "queryAllDeclaredConstructors": true,
    "methods": [
      {
        "name": "<init>",
        "parameterTypes": [
          "java.lang.String"
        ]
      }
    ]
  },
  {
    "name": "de.cmdjulian.kirc.image.Repository$Companion",
    "allDeclaredFields": true,
    "queryAllDeclaredMethods": true,
    "queryAllDeclaredConstructors": true,
    "methods": [
      {
        "name": "of",
        "parameterTypes": [
          "java.lang.String"
        ]
      }
    ]
  }
]

When I also annotate the companion object, everything works @ReflectionHint companion object { ... }