Next create the proto file named bank-service.proto in src/main/proto folder.
A proto file in gRPC is a special type of file that helps define the structure and communication between
different software components. It acts like a blueprint for creating and interacting with these components.
This protocol defines the format of messages for a banking system. It includes a message for requesting address proof with account holder name, account number, and PDF file. It also has a message for the server's response. The service "BankService" allows clients to upload address proof documents using client streaming for efficiency.
syntax = "proto3";
package banking;
option java_multiple_files = true;
option java_package = "com.javainuse.banking";
message AddressProofRequest {
string account_holder_name = 1; // Provide the name of the account holder
string account_number = 2; // Specify the account number
// Stream the PDF file as bytes
// The client will send chunks of the file using streaming
// By doing so, the client can send large files in multiple parts
// to enhance efficiency.
// The server will concatenate all the chunks at the end to form the complete document.
// If you wish to receive multiple files, you can define a repeated field of bytes.
// Make sure to add appropriate validation and error handling logic.
bytes pdf_file = 3;
}
message AddressProofResponse {
bool success = 1; // Whether the upload was successful
string message = 2; // A response message from the server
}
service BankService {
// Upload the address proof document as a PDF file using client streaming.
rpc UploadAddressProof(stream AddressProofRequest) returns (AddressProofResponse) {}
}
Next create the spring bootstrap class as follows-
package com.javainuse.bank;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootGrpcServerExampleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootGrpcServerExampleApplication.class, args);
}
}
Run the maven install command which will generate the classes using the proto file.
Create the class named UploadAddressProofObserver. This class
UploadAddressProofObserver implements a stream observer for address proof requests. It receives chunks of data for account holder name, account number, and PDF file. It processes the PDF file by writing it
to a file and sends a response message confirming successful upload. It handles errors during streaming.
package com.javainuse.bank.service;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import com.javainuse.banking.AddressProofRequest;
import com.javainuse.banking.AddressProofResponse;
import io.grpc.stub.StreamObserver;
public class UploadAddressProofObserver implements StreamObserver<AddressProofRequest> {
private String accountHolderName;
private String accountNumber;
private ByteArrayOutputStream pdfBytes = new ByteArrayOutputStream();
private final StreamObserver<AddressProofResponse> responseObserver;
public UploadAddressProofObserver(StreamObserver<AddressProofResponse> responseObserver) {
this.responseObserver = responseObserver;
}
@Override
public void onNext(AddressProofRequest request) {
// Retrieve data from each request chunk
if (request.hasField(request.getDescriptor().findFieldByName("account_holder_name"))) {
accountHolderName = request.getAccountHolderName();
}
if (request.hasField(request.getDescriptor().findFieldByName("account_number"))) {
accountNumber = request.getAccountNumber();
}
if (request.hasField(request.getDescriptor().findFieldByName("pdf_file"))) {
try {
// Append the received bytes from the PDF file
pdfBytes.write(request.getPdfFile().toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onError(Throwable t) {
// Handle any errors that occur during streaming
}
@Override
public void onCompleted() {
// Process the complete PDF file
// Implement the required logic to handle the address proof document
// You can write the byte array stored in pdfBytes to a file or store it in a
// database, etc.
try {
FileOutputStream fileOutputStream = new FileOutputStream("D://data/address_proof.pdf");
pdfBytes.writeTo(fileOutputStream);
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
// Create and send a response message
AddressProofResponse response = AddressProofResponse.newBuilder().setSuccess(true)
.setMessage("Address proof document uploaded successfully for " + accountHolderName
+ " having account number " + accountNumber)
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
ProcessAddressService is a class that extends BankServiceGrpc.BankServiceImplBase and is annotated as @GrpcService.
We use the @GrpcService to expose this class as a grpc service and expose it over the gRPC protocol.
BankServiceGrpc.BankServiceImplBase is the abstract base class
generated by the gRPC framework based on the protocol buffer definition file bank-service.proto for the service.
By extending the generated base class, you get a template with the necessary methods and can focus on
implementing the business logic without worrying
about handling low-level details of the gRPC communication.
It overrides a method to upload an address proof request and returns a stream observer for handling the response.
The method returns a new instance of UploadAddressProofObserver to handle the upload process.
package com.javainuse.bank.service;
import com.javainuse.banking.AddressProofRequest;
import com.javainuse.banking.AddressProofResponse;
import com.javainuse.banking.BankServiceGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;
@GrpcService
public class ProcessAddressService extends BankServiceGrpc.BankServiceImplBase {
@Override
public StreamObserver<AddressProofRequest> uploadAddressProof(
StreamObserver<AddressProofResponse> responseObserver) {
return new UploadAddressProofObserver(responseObserver);
}
}
Next create a file named application.properties where we specify the grpc port configuration-
grpc.server.port=8090
Test using BloomRPC
Start the spring boot project created. Start BloomRPC and load the proto file. This will create the gRPC client.
Spring Boot Client Streaming gRPC Client
We will be creating a maven project as follows -

The pom.xml will be as follows-
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.javainuse</groupId>
<artifactId>boot-client-streaming-grpc-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<protobuf.version>3.17.3</protobuf.version>
<protobuf-plugin.version>0.6.1</protobuf-plugin.version>
<grpc.version>1.59.0</grpc.version>
</properties>
<dependencies>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
<version>2.15.0.RELEASE</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>annotations-api</artifactId>
<version>6.0.53</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.1</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>
com.google.protobuf:protoc:3.24.0:exe:${os.detected.classifier}
</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>
io.grpc:protoc-gen-grpc-java:1.59.0:exe:${os.detected.classifier}
</pluginArtifact>
<protoSourceRoot>
${basedir}/src/main/proto/
</protoSourceRoot>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Next create the proto file named bank-service.proto in src/main/proto folder
syntax = "proto3";
package banking;
option java_multiple_files = true;
option java_package = "com.javainuse.banking";
message AddressProofRequest {
string account_holder_name = 1; // Provide the name of the account holder
string account_number = 2; // Specify the account number
// Stream the PDF file as bytes
// The client will send chunks of the file using streaming
// By doing so, the client can send large files in multiple parts
// to enhance efficiency.
// The server will concatenate all the chunks at the end to form the complete document.
// If you wish to receive multiple files, you can define a repeated field of bytes.
// Make sure to add appropriate validation and error handling logic.
bytes pdf_file = 3;
}
message AddressProofResponse {
bool success = 1; // Whether the upload was successful
string message = 2; // A response message from the server
}
service BankService {
// Upload the address proof document as a PDF file using client streaming.
rpc UploadAddressProof(stream AddressProofRequest) returns (AddressProofResponse) {}
}
Next create the spring bootstrap class as follows-
package com.javainuse.bank;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class SpringBootGrpcClientExampleApplication {
public static void main(String[] args) throws InterruptedException {
SpringApplication.run(SpringBootGrpcClientExampleApplication.class, args);
}
}
Run the maven install command which will generate the classes using the proto file.
Create a ResponseObserver as follows.
ResponseObserver is a class that implements StreamObserver for AddressProofResponse.
It has methods to handle server responses, errors, and completion of file uploads. It uses a lock and boolean
flag to ensure synchronization and provides a method to await completion of the response.
package com.javainuse.bank.service;
import com.javainuse.banking.AddressProofResponse;
import io.grpc.stub.StreamObserver;
public class ResponseObserver implements StreamObserver<AddressProofResponse> {
private final Object lock = new Object();
private boolean completed = false;
@Override
public void onNext(AddressProofResponse response) {
System.out.println("Server response: " + response.getMessage());
}
@Override
public void onError(Throwable throwable) {
System.err.println("Error occurred: " + throwable.getMessage());
synchronized (lock) {
completed = true;
lock.notifyAll();
}
}
@Override
public void onCompleted() {
System.out.println("File uploaded successfully!");
synchronized (lock) {
completed = true;
lock.notifyAll();
}
}
public void awaitCompletion() {
synchronized (lock) {
while (!completed) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
The BankService class is used to upload address proof documents to a gRPC server. It establishes a connection to the server, sends the file in chunks, and waits for a response. Once the upload is complete,
it closes the connection. The upload process is handled using gRPC stubs and observers.
package com.javainuse.bank.service;
import java.io.FileInputStream;
import java.io.IOException;
import org.springframework.stereotype.Service;
import com.javainuse.banking.AddressProofRequest;
import com.javainuse.banking.BankServiceGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;
@Service
public class BankService {
public void uploadAddressProof() {
String filePath = "C:\\test\\test.pdf";
String accountHolderName = "javainuse";
String accountNumber = "account5";
// Create a channel to connect to the gRPC server
ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8090).usePlaintext().build();
// Create a gRPC stub
BankServiceGrpc.BankServiceStub stub = BankServiceGrpc.newStub(channel);
// Create an instance of the ResponseObserver class to handle the response from
// the server
ResponseObserver responseObserver = new ResponseObserver();
// Create a StreamObserver to send the request to the server
StreamObserver<AddressProofRequest> requestObserver = stub.uploadAddressProof(responseObserver);
// Read the PDF file from disk and send it to the server in chunks
try (FileInputStream fileInputStream = new FileInputStream(filePath)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
// Create an AddressProofRequest with the account holder name, account number,
// and file chunk
AddressProofRequest request = AddressProofRequest.newBuilder().setAccountHolderName(accountHolderName)
.setAccountNumber(accountNumber)
.setPdfFile(com.google.protobuf.ByteString.copyFrom(buffer, 0, bytesRead)).build();
// Send the request to the server
requestObserver.onNext(request);
}
} catch (IOException e) {
e.printStackTrace();
}
// Mark the client-side stream as complete
requestObserver.onCompleted();
// Wait for the response from the server
responseObserver.awaitCompletion();
// Close the channel after the upload is completed
channel.shutdown();
}
}
Finally modify the spring bootstrap class to call the uploadAddressProof method of the BankService-
package com.javainuse.bank;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.javainuse.bank.service.BankService;
@SpringBootApplication
public class SpringBootGrpcClientExampleApplication {
public static void main(String[] args) throws InterruptedException {
ApplicationContext context = SpringApplication.run(SpringBootGrpcClientExampleApplication.class, args);
BankService bankService = context.getBean(BankService.class);
bankService.uploadAddressProof();
}
}
Run the client to get the response.
Download Source Code
Download it -
Spring Boot + gRPC Client Streaming Client Example
Download it -
Spring Boot + gRPC Client Streaming Server Example