Implemented client grpc service.

This commit is contained in:
WickedJack99
2024-05-05 18:31:01 +02:00
parent e9481996a9
commit fd2f2b2e57
30 changed files with 870 additions and 2 deletions

View File

@@ -0,0 +1,4 @@
{
"java.compile.nullAnalysis.mode": "disabled",
"java.configuration.updateBuildConfiguration": "automatic"
}

View File

@@ -0,0 +1,91 @@
<?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>vslab2.src</groupId>
<artifactId>labor2vs</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.63.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.63.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.63.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>
</dependencies>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.2</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.9.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.24.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>vslab2.src.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

Binary file not shown.

View File

@@ -0,0 +1,34 @@
package vslab2.src;
import vslab2.src.grpcStuff.GRPCInformation;
import vslab2.src.input.ClientInformation;
import vslab2.src.input.InputThread;
import vslab2.src.request.RequestExecuterThread;
import vslab2.src.request.queues.RequestQueue;
public class Main {
public static void main(String[] args) {
RequestQueue requestQueue = new RequestQueue();
ClientInformation clientInformation = new ClientInformation();
GRPCInformation grpcInformation = new GRPCInformation();
RequestExecuterThread requestExecuterThread = new RequestExecuterThread(requestQueue);
requestExecuterThread.start();
InputThread inputThread = new InputThread(clientInformation, grpcInformation, requestQueue);
inputThread.start();
try {
inputThread.join();
} catch (InterruptedException e) {
System.err.println("Error in Main:main, inputThread was interrupted, before join.");
}
try {
requestExecuterThread.join();
} catch (InterruptedException e) {
System.err.println("Error in Main:main, requestExecuterThread was interrupted, before join.");
}
}
}

View File

@@ -0,0 +1,33 @@
package vslab2.src.grpcStuff;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;
import vslab2.src.grpc.LogOuterClass.Log;
import vslab2.src.grpc.LogServiceGrpc;
import vslab2.src.response.observers.AddLogEmptyResponseObserver;
public class GRPCInformation {
private LogServiceGrpc.LogServiceStub stub = null;
private StreamObserver<Log> addLogStreamRequestObserver = null;
public void initializeStub(String serverIP, int serverPort) {
ManagedChannel channel = ManagedChannelBuilder.forAddress(serverIP, serverPort)
.usePlaintext()
.build();
stub = LogServiceGrpc.newStub(channel);
}
public LogServiceGrpc.LogServiceStub getStub() {
return stub;
}
public synchronized StreamObserver<Log> getAddLogStreamObserver() {
return addLogStreamRequestObserver;
}
public synchronized void initializeAddLogStreamObserver() {
this.addLogStreamRequestObserver = stub.addLog(new AddLogEmptyResponseObserver());
}
}

View File

@@ -0,0 +1,32 @@
package vslab2.src.input;
public class ClientInformation {
private String userId = null;
private String serverIP = null;
private int serverPort = 0;
public synchronized String getUserId() {
return userId;
}
public synchronized void setUserId(String userId) {
this.userId = userId;
}
public synchronized String getServerIP() {
return serverIP;
}
public synchronized void setServerIP(String serverIP) {
this.serverIP = serverIP;
}
public synchronized int getServerPort() {
return serverPort;
}
public synchronized void setServerPort(int serverPort) {
this.serverPort = serverPort;
}
}

View File

@@ -0,0 +1,98 @@
package vslab2.src.input;
import java.util.Scanner;
import vslab2.src.grpcStuff.GRPCInformation;
import vslab2.src.input.commands.CommandFactory;
import vslab2.src.input.commands.Executable;
import vslab2.src.request.queues.RequestQueue;
public class InputThread extends Thread{
private boolean inputThreadRunning = false;
private ClientInformation clientInformation = null;
private GRPCInformation grpcInformation = null;
private RequestQueue requestQueue = null;
public InputThread(ClientInformation clientInformation, GRPCInformation grpcInformation, RequestQueue requestQueue) {
inputThreadRunning = true;
this.clientInformation = clientInformation;
this.requestQueue = requestQueue;
this.grpcInformation = grpcInformation;
}
@Override
public void run() {
Scanner inputScanner = new Scanner(System.in);
setUserId(inputScanner);
setServerInformation(inputScanner);
grpcInformation.initializeStub(clientInformation.getServerIP(), clientInformation.getServerPort());
while (inputThreadRunning) {
System.out.println("-----------------------------------------------");
System.out.println("Enter command:");
String inputString = null;
try {
inputString = inputScanner.nextLine();
} catch (Exception e) {
System.err.println("Error at InputThread:run, unable to resolve command from input scanner.");
}
if ((inputString != null) && (!inputString.equals(""))) {
Executable command = CommandFactory.createCommandFromString(inputString, clientInformation, grpcInformation);
if (command != null) {
command.execute(requestQueue);
}
}
}
inputScanner.close();
}
private void setUserId(Scanner inputScanner) {
boolean validUserId = false;
while (!validUserId) {
System.out.println("-----------------------------------------------");
System.out.println("Enter user id:");
try {
clientInformation.setUserId(inputScanner.nextLine());
validUserId = true;
} catch (Exception e) {
System.err.println("Error: re-enter user id:");
}
}
}
private void setServerInformation(Scanner inputScanner) {
boolean validIP = false;
boolean validPort = false;
while (!(validIP || validPort)) {
System.out.println("-----------------------------------------------");
System.out.println("Enter IP of server:");
String ip = null;
try {
ip = inputScanner.nextLine();
validIP = true;
} catch (Exception e) {
System.err.println("Error: enter ip in format x.x.x.x");
validIP = false;
}
System.out.println("-----------------------------------------------");
System.out.println("Enter Port of server:");
int port = -1;
try {
port = Integer.valueOf(inputScanner.nextLine());
validPort = (port > 0);
} catch (Exception e) {
System.err.println("Error: enter number.");
validPort = false;
}
if (validIP && validPort) {
clientInformation.setServerIP(ip);
clientInformation.setServerPort(port);
}
}
}
}

View File

@@ -0,0 +1,37 @@
package vslab2.src.input.commands;
import vslab2.src.grpc.LogOuterClass.Log;
import vslab2.src.grpcStuff.GRPCInformation;
import vslab2.src.input.ClientInformation;
import vslab2.src.request.queues.RequestQueue;
import vslab2.src.request.requests.RequestAddLog;
public class CommandAddLog implements Executable {
private String text = null;
private ClientInformation clientInformation = null;
private GRPCInformation grpcInformation = null;
public CommandAddLog(String text, ClientInformation clientInformation, GRPCInformation grpcInformation) {
this.text = text;
this.clientInformation = clientInformation;
this.grpcInformation = grpcInformation;
}
@Override
public void execute(RequestQueue requestQueue) {
Log log = Log.newBuilder().setUserId(clientInformation.getUserId()).setText(text).build();
requestQueue.add(new RequestAddLog(log, grpcInformation));
}
@Override
public ECommandType getType() {
return ECommandType.AddLog;
}
@Override
public int getTypeId() {
return getType().ordinal();
}
}

View File

@@ -0,0 +1,22 @@
package vslab2.src.input.commands;
import vslab2.src.request.queues.RequestQueue;
public class CommandExit implements Executable {
@Override
public void execute(RequestQueue requestQueue) {
// TODO stopp all threads.
}
@Override
public ECommandType getType() {
return ECommandType.UnlistenLog;
}
@Override
public int getTypeId() {
return getType().ordinal();
}
}

View File

@@ -0,0 +1,50 @@
package vslab2.src.input.commands;
import vslab2.src.grpcStuff.GRPCInformation;
import vslab2.src.input.ClientInformation;
public class CommandFactory {
/**
* Creates command from given string which contains command name and arguments.
* @param commandString string which contains command and arguments.
* @return executable command.
*/
public static Executable createCommandFromString(String commandString, ClientInformation clientInformation, GRPCInformation grpcInformation) {
String[] commandStringParts = commandString.split(" ");
String command = commandStringParts[Constants.COMMAND];
switch (command) {
case (Constants.ADD_LOG): {
return new CommandAddLog(commandStringParts[Constants.LOG_TEXT], clientInformation, grpcInformation);
}
case (Constants.GET_LOG): {
return new CommandGetLog(grpcInformation);
}
case (Constants.LISTEN_LOG): {
return new CommandListenLog(clientInformation, grpcInformation);
}
case (Constants.UNLISTEN_LOG): {
return new CommandUnlistenLog(clientInformation, grpcInformation);
}
case (Constants.HELP): {
return new CommandHelp();
}
case (Constants.EXIT): {
return new CommandExit();
}
default: {
System.err.println("Error at CommandFactory:createCommandFromString, unknown command: " + command);
}break;
}
return null;
}
}

View File

@@ -0,0 +1,29 @@
package vslab2.src.input.commands;
import vslab2.src.grpcStuff.GRPCInformation;
import vslab2.src.request.queues.RequestQueue;
import vslab2.src.request.requests.RequestGetLog;
public class CommandGetLog implements Executable {
private GRPCInformation grpcInformation = null;
public CommandGetLog(GRPCInformation grpcInformation) {
this.grpcInformation = grpcInformation;
}
@Override
public void execute(RequestQueue requestQueue) {
requestQueue.add(new RequestGetLog(grpcInformation));
}
@Override
public ECommandType getType() {
return ECommandType.GetLog;
}
@Override
public int getTypeId() {
return getType().ordinal();
}
}

View File

@@ -0,0 +1,28 @@
package vslab2.src.input.commands;
import vslab2.src.request.queues.RequestQueue;
public class CommandHelp implements Executable {
@Override
public void execute(RequestQueue requestQueue) {
System.out.println("-----------------------------------------------");
System.out.println("Available commands:");
for (String command : Constants.COMMANDS) {
System.out.println(command);
}
}
@Override
public ECommandType getType() {
return ECommandType.Help;
}
@Override
public int getTypeId() {
return getType().ordinal();
}
}

View File

@@ -0,0 +1,35 @@
package vslab2.src.input.commands;
import vslab2.src.grpc.LogOuterClass.User;
import vslab2.src.grpcStuff.GRPCInformation;
import vslab2.src.input.ClientInformation;
import vslab2.src.request.queues.RequestQueue;
import vslab2.src.request.requests.RequestListenLog;
public class CommandListenLog implements Executable {
private ClientInformation clientInformation = null;
private GRPCInformation grpcInformation = null;
public CommandListenLog(ClientInformation clientInformation, GRPCInformation grpcInformation) {
this.clientInformation = clientInformation;
this.grpcInformation = grpcInformation;
}
@Override
public void execute(RequestQueue requestQueue) {
User user = User.newBuilder().setUserId(clientInformation.getUserId()).build();
requestQueue.add(new RequestListenLog(user, grpcInformation));
}
@Override
public ECommandType getType() {
return ECommandType.ListenLog;
}
@Override
public int getTypeId() {
return getType().ordinal();
}
}

View File

@@ -0,0 +1,35 @@
package vslab2.src.input.commands;
import vslab2.src.grpc.LogOuterClass.User;
import vslab2.src.grpcStuff.GRPCInformation;
import vslab2.src.input.ClientInformation;
import vslab2.src.request.queues.RequestQueue;
import vslab2.src.request.requests.RequestUnlistenLog;
public class CommandUnlistenLog implements Executable {
private ClientInformation clientInformation = null;
private GRPCInformation grpcInformation = null;
public CommandUnlistenLog(ClientInformation clientInformation, GRPCInformation grpcInformation) {
this.clientInformation = clientInformation;
this.grpcInformation = grpcInformation;
}
@Override
public void execute(RequestQueue requestQueue) {
User user = User.newBuilder().setUserId(clientInformation.getUserId()).build();
requestQueue.add(new RequestUnlistenLog(user, grpcInformation));
}
@Override
public ECommandType getType() {
return ECommandType.UnlistenLog;
}
@Override
public int getTypeId() {
return getType().ordinal();
}
}

View File

@@ -0,0 +1,22 @@
package vslab2.src.input.commands;
public class Constants {
public static final int COMMAND = 0;
public static final int LOG_TEXT = 1;
public static final String ADD_LOG = "AddLog";
public static final String GET_LOG = "GetLog";
public static final String LISTEN_LOG = "ListenLog";
public static final String UNLISTEN_LOG = "UnlistenLog";
public static final String HELP = "Help";
public static final String EXIT = "Exit";
public static final String[] COMMANDS = {
"AddLog <LogText> // Add entry to log",
"GetLog // Get whole log at once",
"ListenLog // Subscribe to log stream",
"UnlistenLog // Unsubscribe from log stream",
"Help // Show help",
"Exit // Exit application"
};
}

View File

@@ -0,0 +1,10 @@
package vslab2.src.input.commands;
public enum ECommandType {
AddLog,
GetLog,
ListenLog,
UnlistenLog,
Help,
Exit
}

View File

@@ -0,0 +1,10 @@
package vslab2.src.input.commands;
import vslab2.src.request.queues.RequestQueue;
public interface Executable {
public void execute(RequestQueue requestQueue);
public ECommandType getType();
public int getTypeId();
public String toString();
}

View File

@@ -0,0 +1,29 @@
package vslab2.src.request;
import vslab2.src.request.queues.RequestQueue;
import vslab2.src.request.requests.Requestable;
public class RequestExecuterThread extends Thread {
private boolean requestExecuterThreadRunning = false;
private RequestQueue requestQueue = null;
public RequestExecuterThread(RequestQueue requestQueue) {
requestExecuterThreadRunning = true;
this.requestQueue = requestQueue;
}
@Override
public void run() {
while (requestExecuterThreadRunning) {
if (requestQueue != null) {
try {
Requestable request = requestQueue.take();
request.request();
} catch (InterruptedException e) {
System.err.println("Error at RequestExecuterThread:run, request queue interrupted.");
}
}
}
}
}

View File

@@ -0,0 +1,10 @@
package vslab2.src.request.queues;
import java.util.concurrent.LinkedBlockingQueue;
import vslab2.src.request.requests.Requestable;
/**
* Queue containing requests that will be send over stub.
*/
public class RequestQueue extends LinkedBlockingQueue<Requestable> {}

View File

@@ -0,0 +1,8 @@
package vslab2.src.request.requests;
public enum ERequestType {
AddLog,
GetLog,
ListenLog,
UnlistenLog
}

View File

@@ -0,0 +1,36 @@
package vslab2.src.request.requests;
import vslab2.src.grpc.LogOuterClass.Log;
import vslab2.src.grpcStuff.GRPCInformation;
/**
* AddLog request if request method is executed, will send new addLog request to server.
*/
public class RequestAddLog implements Requestable {
private GRPCInformation grpcInformation = null;
private Log log = null;
public RequestAddLog(Log log, GRPCInformation grpcInformation) {
this.grpcInformation = grpcInformation;
this.log = log;
}
@Override
public void request() {
if (grpcInformation.getAddLogStreamObserver() == null) {
grpcInformation.initializeAddLogStreamObserver();
}
grpcInformation.getAddLogStreamObserver().onNext(log);
}
@Override
public ERequestType getType() {
return ERequestType.AddLog;
}
@Override
public int getTypeId() {
return getType().ordinal();
}
}

View File

@@ -0,0 +1,30 @@
package vslab2.src.request.requests;
import vslab2.src.grpc.LogOuterClass.Empty;
import vslab2.src.grpcStuff.GRPCInformation;
import vslab2.src.response.observers.GetLogLogsResponseObserver;
public class RequestGetLog implements Requestable {
private GRPCInformation grpcInformation = null;
public RequestGetLog(GRPCInformation grpcInformation) {
this.grpcInformation = grpcInformation;
}
@Override
public void request() {
grpcInformation.getStub().getLog(Empty.getDefaultInstance(), new GetLogLogsResponseObserver());;
}
@Override
public ERequestType getType() {
return ERequestType.GetLog;
}
@Override
public int getTypeId() {
return getType().ordinal();
}
}

View File

@@ -0,0 +1,32 @@
package vslab2.src.request.requests;
import vslab2.src.grpc.LogOuterClass.User;
import vslab2.src.grpcStuff.GRPCInformation;
import vslab2.src.response.observers.ListenLogLogStreamResponseObserver;
public class RequestListenLog implements Requestable {
private User user = null;
private GRPCInformation grpcInformation = null;
public RequestListenLog(User user, GRPCInformation grpcInformation) {
this.user = user;
this.grpcInformation = grpcInformation;
}
@Override
public void request() {
grpcInformation.getStub().listenLog(user, new ListenLogLogStreamResponseObserver());
}
@Override
public ERequestType getType() {
return ERequestType.ListenLog;
}
@Override
public int getTypeId() {
return getType().ordinal();
}
}

View File

@@ -0,0 +1,32 @@
package vslab2.src.request.requests;
import vslab2.src.grpc.LogOuterClass.User;
import vslab2.src.grpcStuff.GRPCInformation;
import vslab2.src.response.observers.UnlistenLogEmptyResponseObserver;
public class RequestUnlistenLog implements Requestable {
private User user = null;
private GRPCInformation grpcInformation = null;
public RequestUnlistenLog(User user, GRPCInformation grpcInformation) {
this.user = user;
this.grpcInformation = grpcInformation;
}
@Override
public void request() {
grpcInformation.getStub().unlistenLog(user, new UnlistenLogEmptyResponseObserver());
}
@Override
public ERequestType getType() {
return ERequestType.UnlistenLog;
}
@Override
public int getTypeId() {
return getType().ordinal();
}
}

View File

@@ -0,0 +1,7 @@
package vslab2.src.request.requests;
public interface Requestable {
public void request();
public ERequestType getType();
public int getTypeId();
}

View File

@@ -0,0 +1,26 @@
package vslab2.src.response.observers;
import io.grpc.stub.StreamObserver;
import vslab2.src.grpc.LogOuterClass.Empty;
public class AddLogEmptyResponseObserver implements StreamObserver<Empty> {
@Override
public void onNext(Empty value) {
System.out.println("-----------------------------------------------");
System.out.println("Log was added successfully.");
}
@Override
public void onError(Throwable t) {
System.out.println("-----------------------------------------------");
System.out.println("Error at AddLogEmptyResponseObserver.\n" + t.toString());
}
@Override
public void onCompleted() {
System.out.println("-----------------------------------------------");
System.out.println("AddLogEmptyResponseObserver closed.");
}
}

View File

@@ -0,0 +1,31 @@
package vslab2.src.response.observers;
import io.grpc.stub.StreamObserver;
import vslab2.src.grpc.LogOuterClass.Log;
import vslab2.src.grpc.LogOuterClass.Logs;
public class GetLogLogsResponseObserver implements StreamObserver<Logs> {
@Override
public void onNext(Logs value) {
System.out.println("-----------------------------------------------");
System.out.println("Row Number\tUser Id\tText\n");
for (Log log : value.getLogsList()) {
System.out.format("%010d\t%07d\t%s", log.getRowNumber(), log.getUserId(), log.getText());
}
}
@Override
public void onError(Throwable t) {
System.out.println("-----------------------------------------------");
System.err.println("Error at GetLogLogsResponseObserver.\n" + t.toString());
}
@Override
public void onCompleted() {
System.out.println("-----------------------------------------------");
System.out.println("GetLogLogsResponseObserver was closed.");
}
}

View File

@@ -0,0 +1,26 @@
package vslab2.src.response.observers;
import io.grpc.stub.StreamObserver;
import vslab2.src.grpc.LogOuterClass.Log;
public class ListenLogLogStreamResponseObserver implements StreamObserver<Log> {
@Override
public void onNext(Log log) {
System.out.println("-----------------------------------------------");
System.out.format("%010d\t%07d\t%s", log.getRowNumber(), log.getUserId(), log.getText());
}
@Override
public void onError(Throwable t) {
System.out.println("-----------------------------------------------");
System.err.println("Error at ListenLogLogStreamResponseObserver.\n" + t.toString());
}
@Override
public void onCompleted() {
System.out.println("-----------------------------------------------");
System.out.println("ListenLogLogStreamResponseObserver closed.");
}
}

View File

@@ -0,0 +1,26 @@
package vslab2.src.response.observers;
import io.grpc.stub.StreamObserver;
import vslab2.src.grpc.LogOuterClass.Empty;
public class UnlistenLogEmptyResponseObserver implements StreamObserver<Empty>{
@Override
public void onNext(Empty value) {
System.out.println("-----------------------------------------------");
System.out.println("Unsubscribed from log stream.");
}
@Override
public void onError(Throwable t) {
System.out.println("-----------------------------------------------");
System.err.println("Error at UnlistenLogEmptyResponseObserver.\n" + t.toString());
}
@Override
public void onCompleted() {
System.out.println("-----------------------------------------------");
System.out.println("UnlistenLogEmptyResponseObserver closed.");
}
}

View File

@@ -1,4 +1,5 @@
syntax = "proto3";
package vslab2.src.grpc;
message Log {
int32 row_number = 1;
@@ -12,9 +13,13 @@ message Logs {
message Empty {}
message User {
string user_id = 1;
}
service LogService {
rpc AddLog(stream Log) returns (Empty);
rpc GetLog(Empty) returns (Logs);
rpc ListenLog(Empty) returns (stream Log);
rpc UnlistenLog(Empty) returns (Empty);
rpc ListenLog(User) returns (stream Log);
rpc UnlistenLog(User) returns (Empty);
}