Serialisation: Protocol Buffers

Java

Compiling prototype files

Installing the protoc compiler was discussed in the last section. It can then be run on the example specification file personv3.proto by


      protoc --java_out=java-src-dir personv3.proto
  
where java-src-dir is where the generated files will be placed. As the specification file gave the package name as person, a subdirectory will be created java-src-dir/person with contents Personv3.java (note the capitalisation of the class name).

Structure of Java files

This file contains a definition of the class Personv3 in package person. For each message type in the prototype specification there is an inner class, here

The Introduction to Creational Design Patterns: Builder design pattern has been adopted for the generated Java code. So instead of e.g. a Person() constructor, you use a PersonBuilder() to construct a builder, which is then populated by its attributes using methods such as setName() and addEmail(). The Person object is then created by calling build() on the builder. Method chaining (see Method Chaining In Java with Examples) is used to avoid repeated calls on the same object.

The advantage is that code such as


      Personv3.Person.Name.Builder nameBldr = Personv3.Person.Name.newBuilder();
      nameBldr.setPersonal("Jan");
      nameBldr.setFamily("Newmarch");
      Personv3.Person.Name name = nameBldr.build();
  
can be reduced to

      Personv3.Person.Name name = Personv3.Person.Name.newBuilder()
                                          .setPersonal("Jan")
                                          .setFamily("Newmarch")
                                          .build();
  

The methods to populate a NameBuilder are setPersonal() and setFamily(). The methods to populate an EmailBuilder are setKind()and setAddress(), while the methods to populate a PersonBuilder are setName() and for the repeated email field, addEmail().

In addition, a Person object has methods

to allow reading and writing bytes or to streams.

Person client

The client creates the example Person using the various builders. It then connects to a server and sends the serialized binary form on an OutputStream to the server. Then it terminates.

The code is PersonClient.java:


import person.*;
import java.util.Arrays;
import java.io.*;
import java.net.*;


public class PersonClient {

    public static final int SERVER_PORT = 2001;
 
    public static void main(String[] args) {
	
	if (args.length != 1) {
            System.err.println("Usage: Client address");
            System.exit(1);
        }

	Personv3.Person.Name name = Personv3.Person.Name.newBuilder()
	    .setPersonal("Jan")
	    .setFamily("Newmarch")
	    .build();
	
	Personv3.Person.Email email1 = Personv3.Person.Email.newBuilder()
	    .setKind("private")
	    .setAddress("jan@newmarch.name")
	    .build();

	Personv3.Person.Email email2 = Personv3.Person.Email.newBuilder()
	    .setKind("work")
	    .setAddress("j.newmarch@boxhill.edu.au")
	    .build();

	Personv3.Person person = Personv3.Person.newBuilder()
	    .setName(name)
	    .addEmail(email1)
	    .addEmail(email2)
	    .build();
	
	String str = person.toString();
	System.out.println("Sending: " + str);

        InetAddress address = null;
        try {
            address = InetAddress.getByName(args[0]);
        } catch(UnknownHostException e) {
            e.printStackTrace();
            System.exit(2);
        }

        Socket sock = null;
        try {
            sock = new Socket(address, SERVER_PORT);
	    System.out.println("Connected");
        } catch(IOException e) {
            e.printStackTrace();
            System.exit(3);
        }
	
        OutputStream out = null;
        try {
            out = sock.getOutputStream();
        } catch(IOException e) {
            e.printStackTrace();
            System.exit(5);
        }
	try {
	    person.writeTo(out);
	} catch(IOException e) {
	    e.printStackTrace();
            System.exit(6);

	}
    }

}

To build the client, you need to include the source for the client, the generated Java files and the jar file for the Protobuf support. With the person directory, the jar file and the client file in the same directory, the Linux compile command is


      javac PersonClient.java person/Personv3.java -cp protobuf-java-3.12.2.jar
  
and the run command is

      java  -cp .:protobuf-java-3.12.2.jar PersonClient localhost
  

Person server

The server listens for connections and then reads a single Person on the connection's InputStream. It then prints the Person to stdout and terminates the connection. It is PersonServer.java:


import person.*;
import java.util.Arrays;
import java.io.*;
import java.net.*;


public class PersonServer {

    public static final int SERVER_PORT = 2001;
 
    public static void main(String[] args){
	
	ServerSocket s = null;
        try {
            s = new ServerSocket(SERVER_PORT);
        } catch(IOException e) {
            System.out.println(e);
            System.exit(1);
        }
        while (true) {
            Socket incoming = null;
            try {
                incoming = s.accept();
                System.out.println("Connected");
            } catch(IOException e) {
                System.out.println(e);
                continue;
            }

            handleSocket(incoming);
        }
    }

    public static void handleSocket(Socket incoming) {
        InputStream in;
	
	try {
	    in = incoming.getInputStream();
        }  catch(IOException e) {
            System.err.println(e.toString());
            return;
        }

	Personv3.Person person;
	try {
	    person = Personv3.Person.parseFrom(in);
	    System.out.println("Receiving: " + person.toString());
	} catch(IOException e) {
	    System.err.println(e.toString());
	    return;
	}
    }
}

the Linux compile command is


      javac PeersonServer.java person/Personv3.java -cp protobuf-java-3.12.2.jar
  
and the run command is

      java  -cp .:protobuf-java-3.12.2.jar PersonServer
  

Both client and the server print the Person to stdout. The output from each should be


name {
  family: "Newmarch"
  personal: "Jan"
}
email {
  kind: "private"
  address: "jan@newmarch.name"
}
email {
  kind: "work"
  address: "j.newmarch@boxhill.edu.au"
}
  


Copyright © Jan Newmarch, jan@newmarch.name
Creative Commons License
" Network Programming using Java, Go, Python, Rust, JavaScript and Julia" by Jan Newmarch is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License .
Based on a work at https://jan.newmarch.name/NetworkProgramming/ .

If you like this book, please contribute using PayPal