diff --git a/backend/common/build.gradle b/backend/common/build.gradle index 1938e5a..1713b72 100644 --- a/backend/common/build.gradle +++ b/backend/common/build.gradle @@ -13,6 +13,9 @@ dependencies { testImplementation platform('org.junit:junit-bom:5.10.0') testImplementation 'org.junit.jupiter:junit-jupiter' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + implementation 'org.jline:jline:3.27.1' + compileOnly 'org.projectlombok:lombok:1.18.34' + annotationProcessor 'org.projectlombok:lombok:1.18.34' } test { diff --git a/backend/common/src/main/java/console/ConsoleReader.java b/backend/common/src/main/java/console/ConsoleReader.java new file mode 100644 index 0000000..827b9f5 --- /dev/null +++ b/backend/common/src/main/java/console/ConsoleReader.java @@ -0,0 +1,85 @@ +package console; + +import console.command.CommandDispatcher; +import lombok.Getter; +import lombok.Setter; +import org.jline.reader.EndOfFileException; +import org.jline.reader.LineReader; +import org.jline.reader.LineReaderBuilder; +import org.jline.reader.UserInterruptException; +import org.jline.reader.impl.completer.StringsCompleter; +import org.jline.terminal.Terminal; +import org.jline.terminal.TerminalBuilder; + +import java.io.IOException; + +@Setter +@Getter +public class ConsoleReader extends Thread { + private CommandDispatcher dispatcher; + private String prompt; + private Terminal terminal; + private LineReader reader; + + public ConsoleReader() { + try { + this.setName("ConsoleReader"); + this.prompt = "devbox>"; + this.dispatcher = new CommandDispatcher(); + this.terminal = TerminalBuilder.builder() + .system(true) + .dumb(true) + .build(); + this.reader = LineReaderBuilder.builder() + .terminal(terminal) + .build(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void flush() { + this.terminal.flush(); + } + + public void println(String msg) { + this.terminal.writer().println(msg); + } + + public void print(String msg) { + this.terminal.writer().print(msg); + } + + public ConsoleReader(String prompt) { + try { + this.setName("ConsoleReader"); + this.prompt = prompt; + this.dispatcher = new CommandDispatcher(); + this.terminal = TerminalBuilder.builder() + .system(true) + .build(); + this.reader = LineReaderBuilder.builder() + .terminal(terminal) + .completer(new StringsCompleter(this.dispatcher.getCommandNames())) + .build(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void run() { + while (!this.isInterrupted()) { + try { + String line = reader.readLine(this.prompt); + dispatcher.execute(line); + } catch (UserInterruptException | EndOfFileException e) { + break; + } + } + } + + public void shutdown() { + this.interrupt(); + } +} diff --git a/backend/common/src/main/java/console/command/Command.java b/backend/common/src/main/java/console/command/Command.java new file mode 100644 index 0000000..8a5d263 --- /dev/null +++ b/backend/common/src/main/java/console/command/Command.java @@ -0,0 +1,8 @@ +package console.command; + +public interface Command { + void execute(String[] args); + String getName(); + String getDescription(); + String getUsage(); +} diff --git a/backend/common/src/main/java/console/command/CommandDispatcher.java b/backend/common/src/main/java/console/command/CommandDispatcher.java new file mode 100644 index 0000000..6e6f300 --- /dev/null +++ b/backend/common/src/main/java/console/command/CommandDispatcher.java @@ -0,0 +1,60 @@ +package console.command; + +import java.util.Arrays; +import java.util.Collection; +import java.util.function.Consumer; + +public class CommandDispatcher { + private final CommandMap commandMap; + + public CommandDispatcher() { + this.commandMap = new CommandMap(); + } + + public void register(String name, String desc, String usage, Consumer command) { + this.commandMap.put(name.toLowerCase(), new Command() { + @Override + public void execute(String[] args) { + command.accept(args); + } + + @Override + public String getName() { + return name; + } + + @Override + public String getDescription() { + return desc; + } + + @Override + public String getUsage() { + return usage; + } + }); + } + + public Collection getCommandNames() { + return this.commandMap.keySet(); + } + + public void register(Command command) { + this.commandMap.put(command.getName().toLowerCase(), command); + } + + public void execute(String input) { + String[] split = input.trim().split("\\s+"); + if (split.length == 0) return; + + String cmd = split[0].toLowerCase(); + String[] args = Arrays.copyOfRange(split, 1, split.length); + + Command command = this.commandMap.get(cmd); + if (command != null) { + command.execute(args); + } else { + System.out.println("Unknown Command: " + cmd); + } + } +} diff --git a/backend/common/src/main/java/console/command/CommandMap.java b/backend/common/src/main/java/console/command/CommandMap.java new file mode 100644 index 0000000..279f2f6 --- /dev/null +++ b/backend/common/src/main/java/console/command/CommandMap.java @@ -0,0 +1,22 @@ +package console.command; + +import java.util.HashMap; + +public class CommandMap extends HashMap { + + public CommandMap() { + super(); + } + + public void addCommand(Command command) { + this.put(command.getName(), command); + } + + public Command getCommand(String name) { + return this.get(name); + } + + public void removeCommand(String name) { + this.remove(name); + } +} diff --git a/backend/master/build.gradle b/backend/master/build.gradle index be3e70d..b38b862 100644 --- a/backend/master/build.gradle +++ b/backend/master/build.gradle @@ -3,7 +3,7 @@ plugins { } dependencies { - + implementation project(":backend:common") } shadowJar { diff --git a/backend/master/src/main/java/com/interguess/devbox/backend/master/MasterBootstrap.java b/backend/master/src/main/java/com/interguess/devbox/backend/master/MasterBootstrap.java index 03ef1c2..e09ac94 100644 --- a/backend/master/src/main/java/com/interguess/devbox/backend/master/MasterBootstrap.java +++ b/backend/master/src/main/java/com/interguess/devbox/backend/master/MasterBootstrap.java @@ -1,8 +1,44 @@ package com.interguess.devbox.backend.master; +import console.ConsoleReader; + public class MasterBootstrap { + private ConsoleReader reader; - public static void main(String[] args) { + public MasterBootstrap(String[] args) { + this.reader = new ConsoleReader(); + + this.reader.getDispatcher().register( + "echo", + "This Command respond the inputed text", + "echo ", + (echoArgs) -> { + if(echoArgs.length == 0) return; + this.reader.println("[DevBox] Response from Master: " + echoArgs[0]); + } + ); + this.reader.getDispatcher().register( + "exit", + "This Command exit Master", + "exit", + (exitArgs) -> { + shutdown(); + } + ); + + this.reader.start(); + this.reader.println("[DevBox] Master started"); + } + + private void shutdown() { + this.reader.println("[DevBox] Master is shutting down"); + this.reader.flush(); + this.reader.shutdown(); + System.exit(0); + } + + public static void main(String[] args) { + new MasterBootstrap(args); } } diff --git a/backend/worker/build.gradle b/backend/worker/build.gradle index 86b3294..22d1fc3 100644 --- a/backend/worker/build.gradle +++ b/backend/worker/build.gradle @@ -3,7 +3,7 @@ plugins { } dependencies { - + implementation project(":backend:common") } shadowJar { diff --git a/backend/worker/src/main/java/com/interguess/devbox/backend/worker/WorkerBootstrap.java b/backend/worker/src/main/java/com/interguess/devbox/backend/worker/WorkerBootstrap.java index 7ce1d31..fbd2833 100644 --- a/backend/worker/src/main/java/com/interguess/devbox/backend/worker/WorkerBootstrap.java +++ b/backend/worker/src/main/java/com/interguess/devbox/backend/worker/WorkerBootstrap.java @@ -1,8 +1,44 @@ package com.interguess.devbox.backend.worker; +import console.ConsoleReader; + public class WorkerBootstrap { + private ConsoleReader reader; + + public WorkerBootstrap(String[] args) { + this.reader = new ConsoleReader(); + + this.reader.getDispatcher().register( + "echo", + "This Command respond the inputed text", + "echo ", + (echoArgs) -> { + if(echoArgs.length == 0) return; + this.reader.println("[DevBox] Response from Worker: " + echoArgs[0]); + } + ); + + this.reader.getDispatcher().register( + "exit", + "This Command exit Worker", + "exit", + (exitArgs) -> { + shutdown(); + } + ); + + this.reader.start(); + this.reader.println("[DevBox] Worker started"); + } + + private void shutdown() { + this.reader.println("[DevBox] Worker is shutting down"); + this.reader.flush(); + this.reader.shutdown(); + System.exit(0); + } public static void main(String[] args) { - + new WorkerBootstrap(args); } }