Search code examples
javajsonsocketsserversocketgenson

Very weird Genson's behaviour while reading from socket stream


I'm trying to perform communication between server and client using Genson library. I've detected the following problem: trying to send a message to the server my application stalls when genson on the server is trying to read the message.

Meanwhile, if I shutdown the client, the message is perfectly read and processed. I've thought it to be deadlock but not sure.

There is no such a problem with native Java serialization.

Here is my server:

import com.owlike.genson.Genson;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;

public class Server {
    public static void main(String[] args) throws IOException {
        Genson genson = new Genson();

        try (ServerSocket server = new ServerSocket(9991)) {
            try (Socket socket = server.accept()) {
                int[] loc = genson.deserialize(socket.getInputStream(), int[].class);
                System.out.println("Server: " + Arrays.toString(loc));
                genson.serialize(loc, socket.getOutputStream());
            }
        }
    }
}

Here is the client:

import com.owlike.genson.Genson;

import java.io.IOException;
import java.net.Socket;
import java.util.Arrays;

public class Client {
    public static void main(String[] args) throws IOException {
        Genson genson = new Genson();

        try (Socket socket = new Socket("localhost", 9991)) {
            genson.serialize(new int[] {1, 2, 3}, socket.getOutputStream());
            int[] loc = genson.deserialize(socket.getInputStream(), int[].class);
            System.out.println("Client: " + Arrays.toString(loc));
        }
    }
}

I wound highly appreciate any help with this question. Thanks in advance.


Edit: This is really wierd. I've made some additional tests and here is what i get:

Additional class:

import com.owlike.genson.annotation.JsonProperty;

import java.io.Serializable;

public class Tester implements Serializable {
    public static final Tester TEST = new Tester(Math.E);

    private double val = Math.PI;

    public Tester(@JsonProperty("val") double val) {
        this.val = val;
    }

    public Tester() {}

    public String toString() {
        return "" + val;
    }
}

Having written genson.serialize(Tester.TEST, socket.getOutputStream()) in the client request I have the same strange result. But having written genson.serialize(new Tester(Double.NaN), socket.getOutputStream()) the result is the expexted one.

Furthermore, if I define the only field in Tester class to be of type int[], lets say, it only works with values of null or new int[0].

In addition to that, if I'm trying to serialize and transmit int for integers in range 0..9 I observe the following behaviour: the same strange thing except that when I shutdown the client, server always shows 0 value.

Besides, for constants like Double.NaN, Double.POSITIVE_INFINITY, Integer.MAX_VALUE and similar there is nothing strange at all (everything works as expected).

For those additional tests Genson class was defined as follows:

Genson genson = new GensonBuilder()
        .useMethods(false)
        .setFieldFilter(VisibilityFilter.PRIVATE)
        .create();

Note that there is no such issue when ser/deser to/from a file using streams:

import com.owlike.genson.Genson;
import com.owlike.genson.GensonBuilder;
import com.owlike.genson.reflect.VisibilityFilter;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class FileTest {
    private static final String FILENAME = "test.json";

    public static void main(String[] args) throws IOException {
        Genson genson = new GensonBuilder()
                .useMethods(false)
                .setFieldFilter(VisibilityFilter.PRIVATE)
                .useIndentation(true)
                .create();

        try (OutputStream stream = new FileOutputStream(FILENAME)) {
            genson.serialize(Tester.TEST, stream);
        }

        try (InputStream stream = new FileInputStream(FILENAME)) {
            System.out.println(genson.deserialize(stream, Tester.class));
        }
    }
}

Solution

  • Some code paths in the reading API will try to eagerly ensure that there are at least N bytes available or EOF has been reached. This happens all the time when parsing a number (that is not a constant) as you noted.

    An option could be to implement a small layer that serializes to a byte array or string, gets the message length and writes it to the output, followed by the payload. On the reading side you would first read the length and then read from the stream in a while loop until you reached that length or EOF.

    Then you would just pass to Genson.deseralize this in memory message.