Skip to content

Jackson2HashMapper breaks when using GraalVM - NotReadablePropertyException #2838

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

Closed
daniel-naegele opened this issue Jan 23, 2024 · 2 comments
Assignees
Labels
theme: aot An issue related to Ahead-Of-Time processing type: bug A general bug

Comments

@daniel-naegele
Copy link

When I run on spring boot version 3.2.0, spring-boot-starter-data-redis and Java 21 GraalVM the following code:

ObjectRecord<String, CustomObject> record = StreamRecords.newRecord()
                .in("stream-key")
                .ofObject(customObject);
var streamOps = redisTemplate.opsForStream(new Jackson2HashMapper(true));
streamOps.add(record);

with

@Data
@NoArgsConstructor
@AllArgsConstructor
public class CustomObject implements Serializable {

    @NotNull
    private String someText;
    @NotNull
    private long timestamp;
}

I get a NotReadablePropertyException. Logs:

org.springframework.beans.NotReadablePropertyException: Invalid property '_value' of bean class [com.fasterxml.jackson.databind.node.TextNode]: Could not find field for property during fallback access
        at org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper.getPropertyValue(DirectFieldAccessFallbackBeanWrapper.java:55) ~[na:na]
        at org.springframework.data.redis.hash.Jackson2HashMapper.flattenElement(Jackson2HashMapper.java:440) ~[na:na]
        at org.springframework.data.redis.hash.Jackson2HashMapper.doFlatten(Jackson2HashMapper.java:376) ~[na:na]
        at org.springframework.data.redis.hash.Jackson2HashMapper.flattenMap(Jackson2HashMapper.java:363) ~[na:na]
        at org.springframework.data.redis.hash.Jackson2HashMapper.toHash(Jackson2HashMapper.java:247) ~[na:na]
        at org.springframework.data.redis.connection.stream.ObjectRecord.toMapRecord(ObjectRecord.java:63) ~[de.radiohost.nxtradio.NxtRadioServiceApplication:3.2.0]
        at org.springframework.data.redis.core.StreamObjectMapper.toMapRecord(StreamObjectMapper.java:116) ~[de.radiohost.nxtradio.NxtRadioServiceApplication:3.2.0]
        at org.springframework.data.redis.core.DefaultStreamOperations.add(DefaultStreamOperations.java:132) ~[de.radiohost.nxtradio.NxtRadioServiceApplication:3.2.0]
        at com.example.packagename.CustomClass.doShit()
        at [email protected]/java.lang.reflect.Method.invoke(Method.java:580) ~[de.radiohost.nxtradio.NxtRadioServiceApplication:na]
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:352) ~[na:na]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:713) ~[na:na]
        at de.radiohost.nxtradio.ScheduledTasks$$SpringCGLIB$$0.fetchRadioInformation(<generated>) ~[de.radiohost.nxtradio.NxtRadioServiceApplication:na]
        at [email protected]/java.lang.reflect.Method.invoke(Method.java:580) ~[de.radiohost.nxtradio.NxtRadioServiceApplication:na]
        at org.springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) ~[na:na]
        at org.springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) ~[na:na]
        at io.micrometer.observation.Observation.observe(Observation.java:499) ~[de.radiohost.nxtradio.NxtRadioServiceApplication:1.12.0]
        at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) ~[na:na]
        at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[de.radiohost.nxtradio.NxtRadioServiceApplication:6.1.1]
        at [email protected]/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) ~[na:na]
        at [email protected]/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:358) ~[de.radiohost.nxtradio.NxtRadioServiceApplication:na]
        at [email protected]/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[na:na]
        at [email protected]/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[de.radiohost.nxtradio.NxtRadioServiceApplication:na]
        at [email protected]/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
        at [email protected]/java.lang.Thread.runWith(Thread.java:1596) ~[de.radiohost.nxtradio.NxtRadioServiceApplication:na]
        at [email protected]/java.lang.Thread.run(Thread.java:1583) ~[de.radiohost.nxtradio.NxtRadioServiceApplication:na]
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:837) ~[de.radiohost.nxtradio.NxtRadioServiceApplication:na]
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:211) ~[na:na]`

I can fix it with proper reflections registration, but I think this should do the module by itself, or not?

public class MyRuntimeHints implements RuntimeHintsRegistrar {

    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
        // The following did not work
        hints.serialization().registerType(CustomObject.class);
        // This does work
        try {
            Field field = TextNode.class.getDeclaredField("_value");
            hints.reflection().registerField(field);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
    }
}

Also I'd have to do this for LongNode and all other primitive Nodes as well I guess.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jan 23, 2024
@christophstrobl
Copy link
Member

@daniel-naegele thanks for reporting - we'll have a look.

@christophstrobl christophstrobl self-assigned this Feb 1, 2024
@christophstrobl christophstrobl added theme: aot An issue related to Ahead-Of-Time processing type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged labels Feb 1, 2024
christophstrobl added a commit to christophstrobl/spring-aot-smoke-tests that referenced this issue Feb 2, 2024
Data Redis HashMapper relies on internal Jackson2 node types.

Related to: spring-projects/spring-data-redis#2838
@christophstrobl
Copy link
Member

The code is using reflective access to the Jackson nodes to extract the raw _value. We've revised the implementation to evaluate the JsonNodeType before falling back to reflection. First snapshot builds for org.springframework.data:spring-data-redis:3.2.x-2838-SNAPSHOT should be available soon, in case you want to give them a try.

mp911de pushed a commit that referenced this issue Apr 19, 2024
…when reading values from JsonNode.

This commit changes the node value retrieval so that it first tries to determine the node type before falling back to reflective access of the _value field.

Closes #2838
Original pull request: #2842
mp911de pushed a commit that referenced this issue Apr 19, 2024
…when reading values from JsonNode.

This commit changes the node value retrieval so that it first tries to determine the node type before falling back to reflective access of the _value field.

Closes #2838
Original pull request: #2842
@mp911de mp911de added this to the 3.1.12 (2023.0.12) milestone Apr 19, 2024
christophstrobl added a commit to christophstrobl/spring-aot-smoke-tests that referenced this issue Apr 19, 2024
Data Redis HashMapper relies on internal Jackson2 node types.

Related to: spring-projects/spring-data-redis#2838
mhalbritter pushed a commit to spring-projects/spring-aot-smoke-tests that referenced this issue Apr 19, 2024
Data Redis HashMapper relies on internal Jackson2 node types.

Related to: spring-projects/spring-data-redis#2838

See gh-204
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
theme: aot An issue related to Ahead-Of-Time processing type: bug A general bug
Projects
None yet
4 participants