infinispan / infinispan/protostream
Nested container types failed to marshall/unmarshall
- Dominant language
- Java
- Stars
- 56
- Forks
- 39
- Avg merge
- 5h 36m
- Merged PRs (30d)
- 5
Description
We have a few container types designed to wrap collections in the `types` module. Upon closer inspection, the generated code for these containers has two methods:
* `T read(ReadContext ctx)`: which reads the context and creates the object of type `T`.
* `void write(WriteContext ctx, T t)`: which reads the object of type `T` and marshall into the context.
The problem is that the `write` method is **always** empty. Usually, this seems fine, but there are cases where this is a problem:
```java
public class RangeQuery {
private final Map> books;
@ProtoFactory
public RangeQuery(Map> books) {
this.books = books;
}
@SuppressWarnings({"rawtypes", "unchecked"})
@ProtoField(number = 1, mapImplementation = HashMap.class)
public Map getBooks() {
return (Map) ((Object) books);
}
@ProtoSchema(
syntax = ProtoSyntax.PROTO3,
dependsOn = {
BookSchema.class,
CommonContainerTypes.class,
},
includeClasses = RangeQuery.class,
schemaFileName = "range-query.proto",
schemaFilePath = "proto/",
schemaPackageName = "queries"
)
public interface RangeQuerySchema extends GeneratedSchema { }
}
```
With test:
```java
@Test
public void testRangeQuery() throws IOException {
ArrayList books = new ArrayList<>(
List.of(
new Book("Fellowship", "Tiny people and rings", 1954),
new Book("Towers", "Marsh with fun smell", 1954),
new Book("King", "Big mountain and eagles", 1955)
)
);
var query = new RangeQuery(Map.of("tolkien", books));
var bytes = ProtobufUtil.toWrappedByteArray(context, query, 512);
System.out.println(Arrays.toString(bytes));
var copy = ProtobufUtil.fromWrappedByteArray(context, bytes);
System.out.println(copy);
}
```
Will fail to marshall the object. It returns the following byte array:
```
bytes = [-126, 1, 18, 113, 117, 101, 114, 105, 101, 115, 46, 82, 97, 110, 103, 101, 81, 117, 101, 114, 121, -118, 1, 13, 10, 11, 10, 7, 116, 111, 108, 107, 105, 101, 110, 18, 0]
```
Which contains only the name of the type, the field number, and the "tolkien" key. Everything else inside the map is ignored because the write method in the generated container is empty.
When invoking the method to unmarshall, it will fail because the parameters for the value are interpreted as nulls because they weren't marshalled:
```java
java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()" because "__v$sizeParam" is null
at org.infinispan.protostream.types.java.collections.ArrayListAdapter$___Marshaller_644b926b0d3f3b832ca16b720ed12a15fdedccbf32741bf201b2c4bb4245d40c.read(ArrayListAdapter$___Marshaller_644b926b0d3f3b832ca16b720ed12a15fdedccbf32741bf201b2c4bb4245d40c.java:38)
at org.infinispan.protostream.types.java.collections.ArrayListAdapter$___Marshaller_644b926b0d3f3b832ca16b720ed12a15fdedccbf32741bf201b2c4bb4245d40c.read(ArrayListAdapter$___Marshaller_644b926b0d3f3b832ca16b720ed12a15fdedccbf32741bf201b2c4bb4245d40c.java:13)
at org.infinispan.protostream.impl.ProtobufTagMarshallerDelegate.unmarshall(ProtobufTagMarshallerDelegate.java:32)
at org.infinispan.protostream.annotations.impl.GeneratedMarshallerBase.readMessage(GeneratedMarshallerBase.java:25)
at org.infinispan.protostream.types.java.RangeQuery$___Marshaller_dac22d3300a708d417ab87b73fde2e08e9e21c43dae8e85e158495c972f749a5.read(RangeQuery$___Marshaller_dac22d3300a708d417ab87b73fde2e08e9e21c43dae8e85e158495c972f749a5.java:54)
at org.infinispan.protostream.types.java.RangeQuery$___Marshaller_dac22d3300a708d417ab87b73fde2e08e9e21c43dae8e85e158495c972f749a5.read(RangeQuery$___Marshaller_dac22d3300a708d417ab87b73fde2e08e9e21c43dae8e85e158495c972f749a5.java:13)
at org.infinispan.protostream.impl.ProtobufTagMarshallerDelegate.unmarshall(ProtobufTagMarshallerDelegate.java:32)
at org.infinispan.protostream.WrappedMessage.readCustomObject(WrappedMessage.java:588)
at org.infinispan.protostream.WrappedMessage.readMessage(WrappedMessage.java:411)
at org.infinispan.protostream.WrappedMessage.read(WrappedMessage.java:401)
at org.infinispan.protostream.ProtobufUtil.fromWrappedByteArray(ProtobufUtil.java:132)
at org.infinispan.protostream.ProtobufUtil.fromWrappedByteArray(ProtobufUtil.java:128)
at org.infinispan.protostream.types.java.TypesMarshallingTest.testRangeQuery(TypesMarshallingTest.java:103)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
```
Serializing just the list with the books objects works fine. The problem seems to be with these nested containers. I also believe it needs a map in the mix.
Contributor guide
Research direction
Start in the types module and inspect the generated container marshallers, especially the empty write method used by nested ArrayList values. Run TypesMarshallingTest.testRangeQuery and compare it with the existing standalone list serialization test. Done means a Map> round-trips without missing values or a NullPointerException.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100