infinispan / infinispan/protostream

Nested container types failed to marshall/unmarshall

Open
#451 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.