Closed
Description
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 java.base@21.0.1/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 java.base@21.0.1/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 java.base@21.0.1/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) ~[na:na]
at java.base@21.0.1/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:358) ~[de.radiohost.nxtradio.NxtRadioServiceApplication:na]
at java.base@21.0.1/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) ~[na:na]
at java.base@21.0.1/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[de.radiohost.nxtradio.NxtRadioServiceApplication:na]
at java.base@21.0.1/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na]
at java.base@21.0.1/java.lang.Thread.runWith(Thread.java:1596) ~[de.radiohost.nxtradio.NxtRadioServiceApplication:na]
at java.base@21.0.1/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.