In the previous article we talked about download file from server using java and grpc. We assumed that client knows file location and name. In this part we will examine that get available file names from server.

Proto

we add new service that name is “getAvailableFiles”. In this part there are 2 important points. One of them is “google.protobuf.Empty”. Actually we want to just send empty request and waiting for String list. In the proto syntax we provide list structure with stream keyword.

rpc getAvailableFiles(google.protobuf.Empty) returns (stream FileName);message FileName {
string fileName = 1;
}

Server

In the server side we assumed that our server has 1 folder for available files. We just read this folder and add file names to our list.

@Override
public void getAvailableFiles(Empty request, StreamObserver<FileName> responseObserver) {
String[] pathnames;
File f = new File("files");
pathnames = f.list();
for (String pathname : pathnames) {
responseObserver.onNext(FileName.newBuilder().setFileName(pathname).build());
}

responseObserver.onCompleted();

}

Client

In the client side we have one simple “streamObserver” that contains “onNext”, “onError”, “onCompleted”.

StreamObserver<FileName> streamObserver = new StreamObserver<FileName>() {
@Override
public void onNext(FileName value) {
try{
fileNames.add(value.getFileName());
}catch (Exception e){
log.error(e.getMessage());
}

}

@Override
public void onError(Throwable t) {
log.error(t.getMessage());
finishLatch.countDown();
}

@Override
public void onCompleted() {
log.info("completed file names");
completed.compareAndSet(false, true);
finishLatch.countDown();
}
};

and call the methods with empty request and our streamObserver.

nonBlockingStub.getAvailableFiles(Empty.newBuilder().build(), streamObserver);

all of the getFiles()

public List<String> getFiles(){

List<String> fileNames = new ArrayList<>();
final CountDownLatch finishLatch = new CountDownLatch(1);
final AtomicBoolean completed = new AtomicBoolean(false);

StreamObserver<FileName> streamObserver = new StreamObserver<FileName>() {
@Override
public void onNext(FileName value) {
try{
fileNames.add(value.getFileName());
}catch (Exception e){
log.error(e.getMessage());
}

}

@Override
public void onError(Throwable t) {
log.error(t.getMessage());
finishLatch.countDown();
}

@Override
public void onCompleted() {
log.info("completed file names");
completed.compareAndSet(false, true);
finishLatch.countDown();
}
};

try {
nonBlockingStub.getAvailableFiles(Empty.newBuilder().build(), streamObserver);
finishLatch.await(5, TimeUnit.MINUTES);

if (!completed.get()) {
throw new Exception("The downloadFile() method did not complete");
}

} catch (Exception e) {
log.error("The getFiles() method did not complete");
}

return fileNames;
}

What is the next?

Now we have 1 client and 1 server side simple api. We can get file names and that files but that is not exactly look like server-client architecture. Let’s configure the project and run on docker or ec2 on part 3.

For all source code

https://github.com/munirKarsli/grpc-file-service

For part 1

https://munirkarsli.medium.com/file-transfer-application-with-java-and-grpc-spring-services-part-1-4f9595b746bd

--

--