March 26, 2021

Getting Started with NPM and Simple Express Web App

Prerequisite

Install Node.js and NPM. See https://magnus-k-karlsson.blogspot.com/2021/03/how-to-install-nodejs-on-fedora-33.html.

Create new Project with NPM

$ mkdir chapter1; cd chapter1

$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (chapter1) 
version: (1.0.0) 
description: Simple Express Web App
git repository: 
author: Magnus K Karlsson
license: (ISC) 
About to write to /home/magnuskkarlsson/WebstormProjects/Nodde.js_from_Ground_Up_for_Beginners/chapter1/package.json:

{
  "name": "chapter1",
  "version": "1.0.0",
  "description": "Simple Express Web App",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "Magnus K Karlsson",
  "license": "ISC"
}


Is this OK? (yes) yes

Add Express NPM package

All NPM package can be search at https://www.npmjs.com/ and Express can be found at https://www.npmjs.com/package/express.

$ npm install express

$ cat package.json
{
  "name": "chapter1",
  "version": "1.0.0",
  "description": "Simple Express Web App",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "Magnus K Karlsson",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1"
  }
}

Now lets write a simple Express web app.

$ vi server.js
const express = require('express')
const app = express()

app.get('/', function (req, res) {
    res.send('Hello World')
})

app.listen(3000)

And to run and test it.

$ node server.js

$ curl localhost:3000
Hello World

How to Install Node.js on Fedora 33

Prerequisite

$ cat /etc/fedora-release 
Fedora release 33 (Thirty Three)

Installation

$ sudo dnf install nodejs 

$ node -v
v14.15.1

$ npm -v
6.14.8

Check official Node.js LTS version at https://nodejs.org/.

Optional Demo/Test

Write a small Express program and test Noe.js installation.

$ vi http_demo_server.js
var http = require('http');

http.createServer(function (req, res) {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Welcome Node.js');
}).listen(3001, "127.0.0.1");

console.log('Server running at http://127.0.0.1:3001/');

$ node --inspect http_demo_server.js

And from another terminal test.

$ curl http://127.0.0.1:3001/
Welcome Node.js

How to Install Visual Studio Code on Fedora 33

Prerequisite

$ cat /etc/fedora-release 
Fedora release 33 (Thirty Three)

Installation

https://computingforgeeks.com/install-visual-studio-code-on-fedora/

In short.

$ sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc

$ cat <<EOF | sudo tee /etc/yum.repos.d/vscode.repo
[code]
name=Visual Studio Code
baseurl=https://packages.microsoft.com/yumrepos/vscode
enabled=1
gpgcheck=1
gpgkey=https://packages.microsoft.com/keys/microsoft.asc
EOF

$ sudo dnf check-update; sudo dnf install code

$ code

March 25, 2021

gRPC and SSL/TLS Ciphers and Protocol Version

Documentation

https://grpc.io/docs/guides/auth/

https://github.com/grpc/grpc-java/blob/master/SECURITY.md

Setup

Generate a self-signed server certificate in PEM format.

$ keytool -genkeypair -alias localhost -keyalg RSA -keysize 2048 -dname "CN=localhost,O=Antigo,ST=Stockholm,C=SE" -validity 720 -storetype PKCS12 -keystore localhost.p12 -storepass changeit -keypass changeit -v
Generating 2,048 bit RSA key pair and self-signed certificate (SHA256withRSA) with a validity of 720 days
	for: CN=localhost, O=Antigo, ST=Stockholm, C=SE
[Storing localhost.p12]

$ openssl pkcs12 -in localhost.p12 -out localhost.pem -nodes
Enter Import Password:

Then manually split pem file into a .crt.pem and .key.pem file.

Java

Server

package se.magnuskkarlsson.example_grpc.server;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

import io.grpc.Server;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder;

public class HelloServer {

    public static void main(String[] args) throws IOException, InterruptedException {
        System.out.println("Starting Server ...");

        InputStream certChainFile = HelloServer.class.getClassLoader().getResourceAsStream("localhost.crt.pem");
        InputStream privateKeyFile = HelloServer.class.getClassLoader().getResourceAsStream("localhost.key.pem");
//        final Server server = ServerBuilder //
//                .forPort(50051) //
////                https://grpc.io/docs/guides/auth/#java
//                .useTransportSecurity(certChainFile, privateKeyFile) //
//                .addService(new HelloServiceImpl()) //
//                .build();

        Listlt;Stringgt; ciphers = Arrays.asList(/* "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", */
                "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", /* "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", */
                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");

//        https://github.com/grpc/grpc-java/blob/master/SECURITY.md
        final Server server = NettyServerBuilder //
                .forPort(50051) //
                .sslContext( //
                        GrpcSslContexts //
                                .forServer(certChainFile, privateKeyFile) //
                                .protocols("TLSv1.2") //
                                .ciphers(ciphers)
//                                .trustManager(clientCAsFile) //
//                                .clientAuth(ClientAuth.REQUIRE) //
                                .build()) //
                .addService(new HelloServiceImpl()) //
                .build();

        server.start();

        Runtime.getRuntime().addShutdownHook(new Thread(() -gt; {
            System.out.println("Shutting down Server ....");
            server.shutdown();
            System.out.println("Successfully stopped Server");
        }));

        server.awaitTermination();
    }

}

Client

package se.magnuskkarlsson.example_grpc.client;

import java.io.InputStream;
import java.util.Iterator;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import example_grpc.HelloServiceGrpc;
import example_grpc.HelloServiceGrpc.HelloServiceBlockingStub;
import example_grpc.HelloServiceGrpc.HelloServiceStub;
import example_grpc.HelloServiceOuterClass.HelloBiDirectionalStreamingRequest;
import example_grpc.HelloServiceOuterClass.HelloBiDirectionalStreamingResponse;
import example_grpc.HelloServiceOuterClass.HelloClientStreamingRequest;
import example_grpc.HelloServiceOuterClass.HelloClientStreamingResponse;
import example_grpc.HelloServiceOuterClass.HelloRequest;
import example_grpc.HelloServiceOuterClass.HelloResponse;
import example_grpc.HelloServiceOuterClass.HelloServerStreamingRequest;
import example_grpc.HelloServiceOuterClass.HelloServerStreamingResponse;
import io.grpc.ManagedChannel;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
import io.grpc.stub.StreamObserver;

// https://github.com/grpc/grpc-java/blob/master/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideClient.java
public class HelloClient {

    public static void main(String[] args) throws Exception {
        System.out.println("Starting Client ...");

//        ManagedChannel channel = ManagedChannelBuilder //
//                .forAddress("localhost", 50051) //
//                .usePlaintext() //
//                .build();

        // https://grpc.io/docs/guides/auth/#java
        InputStream truststore = HelloClient.class.getClassLoader().getResourceAsStream("localhost.crt.pem");
        ManagedChannel channel = NettyChannelBuilder //
                .forAddress("localhost", 50051) //
                .sslContext(GrpcSslContexts.forClient().trustManager(truststore).build()) //
                .build();

        new HelloClient().unary(channel);
//        new HelloClient().serverStreaming(channel);
//        new HelloClient().clientStreaming(channel);
//        new HelloClient().biDirectionalStreaming(channel);

        System.out.println("Shutting down Client");
        channel.shutdown();
    }

    private void unary(ManagedChannel channel) {
        HelloServiceBlockingStub syncClient = HelloServiceGrpc.newBlockingStub(channel);
//        HelloServiceStub asyncClient = HelloServiceGrpc.newStub(channel);

        HelloRequest req1 = HelloRequest.newBuilder().setName("Magnus").build();
        HelloResponse resp1 = syncClient.hello(req1);
        System.out.println("[Async] " + resp1);

        HelloRequest req2 = HelloRequest.newBuilder().setName("John Doe").build();
        HelloResponse resp2 = syncClient.hello(req2);
        System.out.println("[Async] " + resp2);
    }
    
}

Overvew gRPC (Remote Procedure Call)

Introduction

"gRPC is a modern open source high performance Remote Procedure Call (RPC) framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services." https://grpc.io/

4 Types of API in gRPC

https://medium.com/@yangli907/grpc-learning-part-1-cdcf59e52707

API protobuf

src/main/proto/example_grpc/hello_service.proto

syntax = "proto3";

// "Package name should be in lowercase, and should correspond 
// to the directory hierarchy. e.g., if a file is in my/package/, 
// then the package name should be my.package."
// [https://developers.google.com/protocol-buffers/docs/style]
package example_grpc;

service HelloService {

    // Unary RPC
    rpc Hello(HelloRequest) returns (HelloResponse) {};

    // Server Streaming RPC
    rpc HelloServerStreaming(HelloServerStreamingRequest) returns (stream HelloServerStreamingResponse) {};

    // Client Streaming RPC
    rpc HelloClientStreaming(stream HelloClientStreamingRequest) returns (HelloClientStreamingResponse) {};

    // Bidirectional Streaming RPC
    rpc HelloBiDirectionalStreaming(stream HelloBiDirectionalStreamingRequest) returns (stream HelloBiDirectionalStreamingResponse) {};

}

message HelloRequest {
  string name = 1;
}

message HelloResponse {
  string message = 1;
}

message HelloServerStreamingRequest {
  string name = 1;
}

message HelloServerStreamingResponse {
  string message = 1;
}

message HelloClientStreamingRequest {
  string name = 1;
}

message HelloClientStreamingResponse {
  string message = 1;
}

message HelloBiDirectionalStreamingRequest {
  string name = 1;
}

message HelloBiDirectionalStreamingResponse {
  string message = 1;
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>se.magnuskkarlsson</groupId>
    <artifactId>example-grpc</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.build.outputEncoding>UTF-8</project.build.outputEncoding>
        <failOnMissingWebXml>false</failOnMissingWebXml>
    </properties>

    <dependencies>
        <!-- https://github.com/grpc/grpc-java Chapter: Download -->
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty-shaded</artifactId>
            <version>1.36.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>1.36.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>1.36.0</version>
        </dependency>
        <dependency> <!-- necessary for Java 9+ -->
            <groupId>org.apache.tomcat</groupId>
            <artifactId>annotations-api</artifactId>
            <version>6.0.53</version>
            <scope>provided</scope>
        </dependency>

        <!-- Test Support -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.10.19</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <!-- https://github.com/grpc/grpc-java Chapter: Generated Code -->
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.6.2</version>
            </extension>
        </extensions>
        <finalName>${project.artifactId}</finalName>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.1</version>
                    <configuration>
                        <release>11</release>
                        <showDeprecation>true</showDeprecation>
                        <showWarnings>true</showWarnings>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>

            <!-- https://github.com/grpc/grpc-java Chapter: Generated Code -->
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.6.1</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.36.0:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <!-- <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version> 
                <configuration> <protocExecutable>/usr/bin/protoc</protocExecutable> </configuration> <executions> <execution> <goals> <goal>compile</goal> 
                </goals> </execution> </executions> </plugin> -->
        </plugins>
    </build>
</project> 

gRPC Server

A typical gRPC Server is created and started as a standalone server.

Below is the boilerplate code for a standalone server.

package se.magnuskkarlsson.example_grpc.server;

import java.io.IOException;

import io.grpc.Server;
import io.grpc.ServerBuilder;

public class HelloServer {

    public static void main(String[] args) throws IOException, InterruptedException {
        System.out.println("Starting Server ...");

        final Server server = ServerBuilder //
                .forPort(50051) //
                .addService(new HelloServiceImpl()) //
                .build();

        server.start();

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("Shutting down Server ....");
            server.shutdown();
            System.out.println("Successfully stopped Server");
        }));

        server.awaitTermination();
    }

}

gRPC Client

Below is the boilerplate code for a standalone client.

package se.magnuskkarlsson.example_grpc.client;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;

// https://github.com/grpc/grpc-java/blob/master/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideClient.java
public class HelloClient {

    public static void main(String[] args) throws Exception {
        System.out.println("Starting Client ...");
        ManagedChannel channel = ManagedChannelBuilder //
                .forAddress("localhost", 50051) //
                .usePlaintext() //
                .build();

        HelloServiceBlockingStub syncClient = HelloServiceGrpc.newBlockingStub(channel);

        System.out.println("Shutting down Client");
        channel.shutdown();
    }

Unary RPC

Unary RPC is the most common call, it is the classical Request and Response pattern.

Server

package se.magnuskkarlsson.example_grpc.server;

import java.util.ArrayList;
import java.util.List;

import example_grpc.HelloServiceGrpc.HelloServiceImplBase;
import example_grpc.HelloServiceOuterClass.HelloRequest;
import example_grpc.HelloServiceOuterClass.HelloResponse;
import io.grpc.stub.StreamObserver;

public class HelloServiceImpl extends HelloServiceImplBase {

    // Unary RPC
    @Override
    public void hello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
        String name = request.getName();
        System.out.println("[Unary] Hello name=" + name + " ...");

        HelloResponse resp = HelloResponse.newBuilder() //
                .setMessage("Hello from Async " + name) //
                .build();

        // send the response
        responseObserver.onNext(resp);

        responseObserver.onCompleted();
    }

}

Client - Blocking Client; HelloServiceBlockingStub syncClient = HelloServiceGrpc.newBlockingStub(channel);

package se.magnuskkarlsson.example_grpc.client;

import java.util.Iterator;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import example_grpc.HelloServiceGrpc;
import example_grpc.HelloServiceGrpc.HelloServiceBlockingStub;
import example_grpc.HelloServiceGrpc.HelloServiceStub;
import example_grpc.HelloServiceOuterClass.HelloRequest;
import example_grpc.HelloServiceOuterClass.HelloResponse;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;

// https://github.com/grpc/grpc-java/blob/master/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideClient.java
public class HelloClient {

    public static void main(String[] args) throws Exception {
        System.out.println("Starting Client ...");
        ManagedChannel channel = ManagedChannelBuilder //
                .forAddress("localhost", 50051) //
                .usePlaintext() //
                .build();

        new HelloClient().unary(channel);

        System.out.println("Shutting down Client");
        channel.shutdown();
    }

    private void unary(ManagedChannel channel) {
        HelloServiceBlockingStub syncClient = HelloServiceGrpc.newBlockingStub(channel);
//        HelloServiceStub asyncClient = HelloServiceGrpc.newStub(channel);

        HelloRequest req1 = HelloRequest.newBuilder().setName("Magnus").build();
        HelloResponse resp1 = syncClient.hello(req1);
        System.out.println("[Async] " + resp1);

        HelloRequest req2 = HelloRequest.newBuilder().setName("John Doe").build();
        HelloResponse resp2 = syncClient.hello(req2);
        System.out.println("[Async] " + resp2);
    }

}

Server Streaming RPC

Streaming Server is well suited for when Server needs to PUSH data to client. This is especially true for Big Data.

Server

    // Server Streaming RPC
    @Override
    public void helloServerStreaming(HelloServerStreamingRequest request,
            StreamObserver<HelloServerStreamingResponse> responseObserver) {

        String name = request.getName();
        System.out.println("[Server Streaming] Hello name=" + name + " ...");

        try {
            for (int i = 0; i < 10; ++i) {

                HelloServerStreamingResponse resp = HelloServerStreamingResponse.newBuilder() //
                        .setMessage("Hello from Server Streaming " + name + " " + i) //
                        .build();
                responseObserver.onNext(resp);

                Thread.sleep(1000L);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            responseObserver.onCompleted();
        }
    }

Blocking Client

    private void serverStreaming(ManagedChannel channel) {
        HelloServiceBlockingStub syncClient = HelloServiceGrpc.newBlockingStub(channel);
//        HelloServiceStub asyncClient = HelloServiceGrpc.newStub(channel);

        HelloServerStreamingRequest req1 = HelloServerStreamingRequest.newBuilder().setName("Magnus").build();
        Iterator<HelloServerStreamingResponse> respIter = syncClient.helloServerStreaming(req1);
        respIter.forEachRemaining(resp -> {
            System.out.println("[Server Streaming]" + resp.getMessage());
        });
    }

Client Streaming RPC

Client Streaming is suited when the client wants to send Big Data. Or when server processing is possible to do in parallell, then can client split jobs.

Server

    // Client Streaming RPC
    @Override
    public StreamObserver<HelloClientStreamingRequest> helloClientStreaming(
            StreamObserver<HelloClientStreamingResponse> responseObserver) {

        StreamObserver<HelloClientStreamingRequest> requestObserver = new StreamObserver<HelloClientStreamingRequest>() {

            private List<String> names = new ArrayList<String>();

            @Override
            public void onNext(HelloClientStreamingRequest request) {
                // client sends a message
                String name = request.getName();
                names.add(name);
            }

            @Override
            public void onError(Throwable t) {
                // client sends a error
            }

            @Override
            public void onCompleted() {
                // client is done
                HelloClientStreamingResponse response = HelloClientStreamingResponse.newBuilder() //
                        .setMessage("[Client Streaming] Hello " + names.toString()) //
                        .build();
                responseObserver.onNext(response);
                responseObserver.onCompleted();
            }

        };

        return requestObserver;
    }

Asynch Client

    private void clientStreaming(ManagedChannel channel) throws InterruptedException {
        HelloServiceStub asyncClient = HelloServiceGrpc.newStub(channel);

        CountDownLatch latch = new CountDownLatch(1);

        StreamObserver<HelloClientStreamingResponse> responseObserver = new StreamObserver<HelloClientStreamingResponse>() {

            @Override
            public void onNext(HelloClientStreamingResponse response) {
                // we get a response from the server
                String message = response.getMessage();
                System.out.println("[Client Streaming] Recieved message from server " + message);

                // onNext will be called only once
            }

            @Override
            public void onError(Throwable t) {
                // we get an error from server
                latch.countDown();
            }

            @Override
            public void onCompleted() {
                // the server is done sending us data

                // onComplete will be called right after onNext()
                System.out.println("[Client Streaming] Server has completed sending us data/stream");
                latch.countDown();
            }

        };

        StreamObserver<HelloClientStreamingRequest> requestObserver = asyncClient
                .helloClientStreaming(responseObserver);

        HelloClientStreamingRequest request1 = HelloClientStreamingRequest.newBuilder() //
                .setName("John") //
                .build();

        requestObserver.onNext(request1);

        HelloClientStreamingRequest request2 = HelloClientStreamingRequest.newBuilder() //
                .setName("Nisse") //
                .build();

        requestObserver.onNext(request2);

        HelloClientStreamingRequest request3 = HelloClientStreamingRequest.newBuilder() //
                .setName("Klara") //
                .build();

        requestObserver.onNext(request3);

        // tell the server, the client is done sending data/streaming
        requestObserver.onCompleted();

        latch.await(5, TimeUnit.SECONDS);
    }

Bidirectional Streaming RPC

Bidirectional Streaming is like a chat protocol and have typical a long running connection.

Server

    // Bidirectional streaming RPC
    @Override
    public StreamObserver<HelloBiDirectionalStreamingRequest> helloBiDirectionalStreaming(
            StreamObserver<HelloBiDirectionalStreamingResponse> responseObserver) {

        StreamObserver<HelloBiDirectionalStreamingRequest> requestObserver = new StreamObserver<HelloBiDirectionalStreamingRequest>() {

            @Override
            public void onNext(HelloBiDirectionalStreamingRequest request) {
                // client sends a message
                String name = request.getName();

                HelloBiDirectionalStreamingResponse response = HelloBiDirectionalStreamingResponse.newBuilder() //
                        .setMessage("[Bi-Directional] Hello " + name) //
                        .build();
                responseObserver.onNext(response);
            }

            @Override
            public void onError(Throwable t) {
                // client sends a error
            }

            @Override
            public void onCompleted() {
                // client is done sending data/stream
                responseObserver.onCompleted();
            }

        };

        return requestObserver;
    }

Asynch Client

    private void biDirectionalStreaming(ManagedChannel channel) throws InterruptedException {
        HelloServiceStub asyncClient = HelloServiceGrpc.newStub(channel);

        CountDownLatch latch = new CountDownLatch(1);

        StreamObserver<HelloBiDirectionalStreamingResponse> responseObserver = new StreamObserver<HelloBiDirectionalStreamingResponse>() {

            @Override
            public void onNext(HelloBiDirectionalStreamingResponse response) {
                // we get a response from the server
                String message = response.getMessage();
                System.out.println("[Bi-Directional Streaming] Recieved message from server " + message);
            }

            @Override
            public void onError(Throwable t) {
                // we get an error from server
                latch.countDown();
            }

            @Override
            public void onCompleted() {
                // the server is done sending/streaming us data
                System.out.println("[Bi-Directional Streaming] Server has completed sending us data/stream");
                latch.countDown();
            }

        };

        StreamObserver<HelloBiDirectionalStreamingRequest> requestObserver = asyncClient
                .helloBiDirectionalStreaming(responseObserver);

        HelloBiDirectionalStreamingRequest request1 = HelloBiDirectionalStreamingRequest.newBuilder() //
                .setName("John") //
                .build();
        requestObserver.onNext(request1);
        System.out.println("[Bi-Directional Streaming] Sending request1");

        HelloBiDirectionalStreamingRequest request2 = HelloBiDirectionalStreamingRequest.newBuilder() //
                .setName("Nisse") //
                .build();
        requestObserver.onNext(request2);
        System.out.println("[Bi-Directional Streaming] Sending request2");

        HelloBiDirectionalStreamingRequest request3 = HelloBiDirectionalStreamingRequest.newBuilder() //
                .setName("Bertil") //
                .build();
        requestObserver.onNext(request3);
        System.out.println("[Bi-Directional Streaming] Sending request3");

        // tell the server, the client is done sending data/streaming
        requestObserver.onCompleted();

        latch.await(5, TimeUnit.SECONDS);
    }

March 22, 2021

Comparison HTTP/1.1 and HTTP/2

HTTP 1.1 was released 1997 and  

  • Was originally composed for HTTP GET and POST.
  • Works with Request/Response and not server push
  • Do not compress HTTP headers. Today are many HTTP headers used.
  • Opens a new TCP connection to a server at each request

 
HTTP/2 was released in 2015
 

  • Supports multiplexing. Client and server can push messages in parallel over the same TCP connection, which reduces latency.
  • Supports server push streams (multiple messages) for one request from the client.
  • Supports header compression.

Reference: https://developers.google.com/web/fundamentals/performance/http2

Protocol Buffers (protobuf) Compiler/Code Generator - protoc

Introduction

In Protocol Buffers (protobuf) is API described in a .proto file, next step is to generate language specific code for that API. That is done with Protocol Buffer Compiler (protoc). Here we will generate Java Code, but there are many language supported with protobuf.

Graddle

The official supported Java plugin from Google (the creator of profobuf) is gradle https://github.com/google/protobuf-gradle-plugin. But changing build tool might not suite your overall CI/DI infrastructure, so here I will show Maven plugin.

Maven

https://www.xolstice.org/protobuf-maven-plugin/

Install Protocol Buffer Compiler (protoc)

On Fedora 33 ($ cat /etc/fedora-release) it is easy to install protoc.

$ sudo dnf install protobuf-compiler

protobuf-maven-plugin

https://www.xolstice.org/protobuf-maven-plugin/

.proto files are stored in src/main/proto. Lets create a simple .proto file

syntax = "proto3";

package se.magnuskkarlsson.example_protobuf;

message SimpleMessage {
  int32 id = 1;
  bool is_simple = 2;
  string name = 3;
  repeated int32 sample_list = 4;
}

And pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>se.magnuskkarlsson</groupId>
    <artifactId>example-protobuf</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.build.outputEncoding>UTF-8</project.build.outputEncoding>
        <failOnMissingWebXml>false</failOnMissingWebXml>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.15.6</version>
        </dependency>

        <!-- Test Support -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.10.19</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>${project.artifactId}</finalName>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.1</version>
                    <configuration>
                        <release>11</release>
                        <showDeprecation>true</showDeprecation>
                        <showWarnings>true</showWarnings>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.6.1</version>
                <configuration>
                    <protocExecutable>/usr/bin/protoc</protocExecutable>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project> 

Then build with maven: '$ mvn clean install'. Generated source code is stored in target/generated-sources/protobuf/java

Now lets write a simple a class to test our new generated code.

package se.magnuskkarlsson.example_protobuf;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;

import se.magnuskkarlsson.example_protobuf.Message.SimpleMessage;

public class Main {

    public static void main(String[] args) throws FileNotFoundException, IOException {
        SimpleMessage message = SimpleMessage.newBuilder() //
                .setId(42) //
                .setIsSimple(false) //
                .setName("FOO") //
                .addAllSampleList(Arrays.asList(3, 4, 5)) //
                .build();
        System.out.println(message);

        File file = File.createTempFile("message", ".bin");
        try (FileOutputStream fos = new FileOutputStream(file)) {
            message.writeTo(fos);
        }
        System.out.println("Wrote " + file.getAbsolutePath());
    }

}

March 19, 2021

Introduction to Protocol Buffers, protobuf

What is Protocol Buffers, protobuf

"Protocol buffers are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data – think XML, but smaller, faster, and simpler." https://developers.google.com/protocol-buffers

Advantages

  • Data is binary (more efficient bandwidth)
  • Custom parser for binary data (more CPU efficient, compared with JSON)
  • Data is typed.
  • Wide variety of supported language (Java, C#, Go, Python, JavaScript).
  • Schema based (.proto)

Disadvantages

  • Since data is sent in binary format, you cannot read it as plaintext or JSON.
  • Some language is not supported.

Protocol Buffers, protobuf was deloped by Google and is used for almost all their internal applications.

Data Types (Scalar Value Types)

https://developers.google.com/protocol-buffers/docs/proto3#scalar

  • Integer numbers: int32, int64
  • Decimal numbers: float (32), double (64)
  • Boolean (True or False): bool
  • String: string (String must always be encoded UTF-8 or 7-bit ASCII (US))
  • Byte Array: bytes

Schema .proto file

"Files should be named lower_snake_case.proto" https://developers.google.com/protocol-buffers/docs/style

Message Type

In protobuf we define Messages, e.g. 'message Person'

Field Type, e.g. string

Field Name, e.g. first_name

Field Tag (order), e.g. '= 1;'

// The syntax for this file is proto3
syntax = "proto3";

/* Person is used to identity
 * across our system. */
message Person {
  int32 age = 1;
  string first_name = 2;
  string last_name = 3;
  bytes picture = 4;
  bool is_profile_verified = 5;
  float height = 6;

  // array/list
  repeated string phone_numbers = 7;

  enum EyeColor {
    // first enum is always the default
    EYE_COLOR_UNSPECIFIED = 0;
    EYE_COLOR_GREEN = 1;
    EYE_COLOR_BROWN = 2;
    EYE_COLOR_BLUE = 3;
  }

  EyeColor eye_color = 8;
}

Style Guide

https://developers.google.com/protocol-buffers/docs/style

  • "Keep the line length to 80 characters."
  • "Use an indent of 2 spaces."
  • "Package name should be in lowercase, and should correspond to the directory hierarchy. e.g., if a file is in my/package/, then the package name should be my.package."
  • "Use CamelCase (with an initial capital) for message names"
  • "Use underscore_separated_names for field names (including oneof field and extension names) – for example, song_name."
  • "Use pluralized names for repeated fields."
  • "Use CamelCase (with an initial capital) for enum type names and CAPITALS_WITH_UNDERSCORES for value names:"
  • "If your .proto defines an RPC service, you should use CamelCase (with an initial capital) for both the service name and any RPC method names"

Default Values for Fields

All fields, if not specified, will have default value.

  • bool: false
  • number (int32, etc): 0
  • string: empty string
  • bytes: empty byte array/list
  • enum: first value
  • repeated: empty array/list

How to Install Visual Studio Code on Fedora 33

Prerequisite

$ cat /etc/fedora-release 
Fedora release 33 (Thirty Three)

Installation

Reference: https://code.visualstudio.com/docs/setup/linux

$ sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc

$ sudo sh -c 'echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com/yumrepos/vscode\nenabled=1\ngpgcheck=1\ngpgkey=https://packages.microsoft.com/keys/microsoft.asc" > /etc/yum.repos.d/vscode.repo'

$ sudo dnf check-update; sudo dnf install code