From 5802b87028673ee4048042f0843b754c0071cc01 Mon Sep 17 00:00:00 2001 From: jonx8 Date: Mon, 21 Aug 2023 23:43:37 +0300 Subject: [PATCH] Implement continue command --- src/main/java/ru/etu/petci/Main.java | 9 +- .../etu/petci/configuration/Configurator.java | 66 ++++++-------- .../RepositoryNotFoundException.java | 7 ++ .../ru/etu/petci/handlers/CommandHandler.java | 2 +- .../handlers/ContinueCommandHandler.java | 45 ++++++++++ .../petci/handlers/InitCommandHandler.java | 14 ++- src/main/java/ru/etu/petci/jobs/Job.java | 68 ++++++++++++++ .../java/ru/etu/petci/jobs/JobsExecutor.java | 39 ++++++++ .../petci/observers/RepositoryObserver.java | 88 ++++++++++++------- 9 files changed, 260 insertions(+), 78 deletions(-) create mode 100644 src/main/java/ru/etu/petci/exceptions/RepositoryNotFoundException.java create mode 100644 src/main/java/ru/etu/petci/handlers/ContinueCommandHandler.java create mode 100644 src/main/java/ru/etu/petci/jobs/Job.java create mode 100644 src/main/java/ru/etu/petci/jobs/JobsExecutor.java diff --git a/src/main/java/ru/etu/petci/Main.java b/src/main/java/ru/etu/petci/Main.java index 144616f..4cab25d 100644 --- a/src/main/java/ru/etu/petci/Main.java +++ b/src/main/java/ru/etu/petci/Main.java @@ -1,6 +1,7 @@ package ru.etu.petci; import ru.etu.petci.handlers.CommandHandler; +import ru.etu.petci.handlers.ContinueCommandHandler; import ru.etu.petci.handlers.InitCommandHandler; import java.io.IOException; @@ -10,7 +11,7 @@ public class Main { public static final String SETTINGS_FILE_NAME = "settings.properties"; - public static String JOBS_DIR_NAME = ""; + public static final String JOBS_DIR_NAME = ""; private static final Logger LOGGER = Logger.getLogger(Main.class.getName()); static { @@ -38,7 +39,11 @@ public static void main(String[] args) { handler = new InitCommandHandler(); exitStatus = handler.handle(args.length > 1 ? args[1] : ""); } - case "continue" -> LOGGER.info("User entered \"continue\" command."); + case "continue" -> { + LOGGER.info("User entered \"continue\" command."); + handler = new ContinueCommandHandler(); + exitStatus = handler.handle(""); + } case "add" -> LOGGER.info("User entered \"add\" command."); default -> { System.out.printf("petCI: \"%s\" is not a command. Use \"help\" command%n", args[0]); diff --git a/src/main/java/ru/etu/petci/configuration/Configurator.java b/src/main/java/ru/etu/petci/configuration/Configurator.java index 9da8974..e652a54 100644 --- a/src/main/java/ru/etu/petci/configuration/Configurator.java +++ b/src/main/java/ru/etu/petci/configuration/Configurator.java @@ -1,67 +1,57 @@ package ru.etu.petci.configuration; +import ru.etu.petci.jobs.Job; import ru.etu.petci.observers.RepositoryObserver; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.nio.file.Files; +import java.io.*; import java.nio.file.Path; import java.util.Objects; import java.util.Properties; import static ru.etu.petci.Main.JOBS_DIR_NAME; -import static ru.etu.petci.Main.SETTINGS_FILE_NAME; public class Configurator { - public RepositoryObserver readRepositoryConfig() { + public RepositoryObserver readRepositoryConfig() throws IOException { RepositoryObserver observer = new RepositoryObserver(); - try { - Properties repoConfig = new Properties(); - repoConfig.load(new FileReader("repository.properties")); - if (!observer.setRepositoryPath(repoConfig.getProperty("repository_path"))) { - System.exit(1); - } + Properties repoConfig = new Properties(); + try (var propertyReader = new FileReader("repository.properties")) { + repoConfig.load(propertyReader); + observer.setRepositoryPath(repoConfig.getProperty("repository_path")); observer.setBranchName(repoConfig.getProperty("branch_name", "master")); - observer.setLastHash(repoConfig.getProperty("last_hash", null)); - } catch (IOException e) { - System.out.println("Error while reading repository config"); - System.exit(1); + observer.setLastHash(repoConfig.getProperty("last_hash")); } return observer; } - public void saveRepositoryConfig(String repoPath, String branchName, String lastHash) { - try { - var properties = new Properties(); - properties.setProperty("repository_path", Path.of(repoPath).toAbsolutePath().normalize().toString()); - properties.setProperty("branch_name", branchName); - properties.setProperty("last_hash", lastHash.length() == 40 ? lastHash : "null"); - properties.store(new FileWriter("repository.properties"), "Repository configuration"); - } catch (IOException e) { - throw new RuntimeException(e); + public void saveRepositoryConfig(String repoPath, String branchName, String lastHash) throws IOException { + var properties = new Properties(); + properties.setProperty("repository_path", Path.of(repoPath).toAbsolutePath().normalize().toString()); + properties.setProperty("branch_name", branchName); + properties.setProperty("last_hash", lastHash.length() == 40 ? lastHash : "null"); + try (var propertyWriter = new FileWriter("repository.properties")) { + properties.store(propertyWriter, "Repository configuration"); } } - public void saveRepositoryConfig(String repoPath, String branchName) { + public void saveRepositoryConfig(String repoPath, String branchName) throws IOException { saveRepositoryConfig(repoPath, branchName, ""); } - - /** - *

This method performs the check whether the current folder - * is an application folder. An application folder must consist - * of a configuration file with name {@link} - * and a directory with name {@link}.

- *

If there is no application directory, then the method returned false, - * user should use "init" command.

- */ - public boolean checkApplicationDir() { - return Files.isDirectory(Path.of(JOBS_DIR_NAME)) && Files.isRegularFile(Path.of(SETTINGS_FILE_NAME)); + public void saveJobConfig(Job job) { + Objects.requireNonNull(job); + try (var out = new BufferedWriter(new FileWriter(JOBS_DIR_NAME + File.separator + job.getName()))) { + out.append(job.getName()); + out.newLine(); + out.append(job.getScriptFile().toString()); + out.newLine(); + out.append(String.valueOf(job.isActive())); + } catch (IOException e) { + System.out.println("Error with writing jobFile"); + System.exit(1); + } } } diff --git a/src/main/java/ru/etu/petci/exceptions/RepositoryNotFoundException.java b/src/main/java/ru/etu/petci/exceptions/RepositoryNotFoundException.java new file mode 100644 index 0000000..ac3e1ea --- /dev/null +++ b/src/main/java/ru/etu/petci/exceptions/RepositoryNotFoundException.java @@ -0,0 +1,7 @@ +package ru.etu.petci.exceptions; + +public class RepositoryNotFoundException extends Exception { + public RepositoryNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/etu/petci/handlers/CommandHandler.java b/src/main/java/ru/etu/petci/handlers/CommandHandler.java index 72eeacf..73a4846 100644 --- a/src/main/java/ru/etu/petci/handlers/CommandHandler.java +++ b/src/main/java/ru/etu/petci/handlers/CommandHandler.java @@ -6,7 +6,7 @@ public interface CommandHandler { /** * This method should handle a certain command which is entered by user. * - * @param arg argument after name of the command. You should check if arg is null. + * @param arg argument after name of the command. Arg is NotNull. * @return exit code for the program */ int handle(String arg); diff --git a/src/main/java/ru/etu/petci/handlers/ContinueCommandHandler.java b/src/main/java/ru/etu/petci/handlers/ContinueCommandHandler.java new file mode 100644 index 0000000..e6868ff --- /dev/null +++ b/src/main/java/ru/etu/petci/handlers/ContinueCommandHandler.java @@ -0,0 +1,45 @@ +package ru.etu.petci.handlers; + + +import ru.etu.petci.configuration.Configurator; +import ru.etu.petci.exceptions.RepositoryNotFoundException; +import ru.etu.petci.jobs.Job; +import ru.etu.petci.jobs.JobsExecutor; +import ru.etu.petci.observers.RepositoryObserver; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class ContinueCommandHandler implements CommandHandler { + + private static final Logger LOGGER; + + static { + LOGGER = Logger.getLogger(ContinueCommandHandler.class.getName()); + LOGGER.setLevel(Level.WARNING); + } + + @Override + public int handle(String arg) { + Objects.requireNonNull(arg); + var configurator = new Configurator(); + try { + RepositoryObserver repositoryObserver = configurator.readRepositoryConfig(); + List jobs = new ArrayList<>(); + // TODO Reading jobs from properties + repositoryObserver.setExecutor(new JobsExecutor(jobs)); + repositoryObserver.start(); + } catch (InterruptedException e) { + LOGGER.log(Level.SEVERE, "The program was interrupted"); + Thread.currentThread().interrupt(); + } catch (IOException | RepositoryNotFoundException e) { + LOGGER.log(Level.SEVERE, e.getMessage()); + return 1; + } + return 1; + } +} diff --git a/src/main/java/ru/etu/petci/handlers/InitCommandHandler.java b/src/main/java/ru/etu/petci/handlers/InitCommandHandler.java index a3c06cd..02551a0 100644 --- a/src/main/java/ru/etu/petci/handlers/InitCommandHandler.java +++ b/src/main/java/ru/etu/petci/handlers/InitCommandHandler.java @@ -2,6 +2,7 @@ import ru.etu.petci.configuration.Configurator; +import java.io.IOException; import java.util.Objects; import java.util.Scanner; import java.util.logging.Level; @@ -37,10 +38,15 @@ public int handle(String arg) { // If branchName is empty, using master-branch System.out.print("Name of observing branch (default - master): "); var branchName = scanner.nextLine().strip(); - if (branchName.isEmpty()) { - configurator.saveRepositoryConfig(repositoryPath, "master"); - } else { - configurator.saveRepositoryConfig(repositoryPath, branchName); + try { + if (branchName.isEmpty()) { + configurator.saveRepositoryConfig(repositoryPath, "master"); + } else { + configurator.saveRepositoryConfig(repositoryPath, branchName); + } + } catch (IOException e) { + LOGGER.severe(e.getMessage()); + return 1; } scanner.close(); System.out.println("Successful initialization!"); diff --git a/src/main/java/ru/etu/petci/jobs/Job.java b/src/main/java/ru/etu/petci/jobs/Job.java new file mode 100644 index 0000000..d892881 --- /dev/null +++ b/src/main/java/ru/etu/petci/jobs/Job.java @@ -0,0 +1,68 @@ +package ru.etu.petci.jobs; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Path; +import java.util.Objects; + +public class Job { + private boolean isActive = true; + private String name; + private final Path scriptFile; + + public Job(Path scriptFile, String name) { + this.scriptFile = scriptFile.toAbsolutePath().normalize(); + this.name = name; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public Path getScriptFile() { + return scriptFile; + } + + + public void setActive(boolean active) { + isActive = active; + } + + public boolean isActive() { + return isActive; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Job job)) return false; + return Objects.equals(name, job.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } + + + public int execute() throws IOException { + Process scriptProcess = Runtime.getRuntime().exec(scriptFile.toString()); + try { + var input = new BufferedReader(new InputStreamReader(scriptProcess.getInputStream())); + String line; + while ((line = input.readLine()) != null) { + System.out.println(line); + } + scriptProcess.waitFor(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + return scriptProcess.exitValue(); + } +} diff --git a/src/main/java/ru/etu/petci/jobs/JobsExecutor.java b/src/main/java/ru/etu/petci/jobs/JobsExecutor.java new file mode 100644 index 0000000..7c5d6a0 --- /dev/null +++ b/src/main/java/ru/etu/petci/jobs/JobsExecutor.java @@ -0,0 +1,39 @@ +package ru.etu.petci.jobs; + +import java.io.IOException; +import java.util.List; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class JobsExecutor { + + private final List jobs; + private static final Logger LOGGER = Logger.getLogger(JobsExecutor.class.getName()); + + static { + LOGGER.setLevel(Level.WARNING); + } + + public JobsExecutor(List jobs) { + this.jobs = jobs; + } + + public void runJobs() throws IOException { + Objects.requireNonNull(jobs); + for (Job job : jobs) { + System.out.println(job.getScriptFile().toAbsolutePath()); + System.out.printf("Run job \"%s\"...%n%n", job.getName()); + if (!job.isActive()) { + System.out.println(": Deactivated"); + continue; + } + if (job.execute() == 0) { + System.out.printf("%n--Succeed--%n"); + } else { + System.out.printf("%n--Failed--%n"); + } + } + } + +} diff --git a/src/main/java/ru/etu/petci/observers/RepositoryObserver.java b/src/main/java/ru/etu/petci/observers/RepositoryObserver.java index 6b7f2b3..488915a 100644 --- a/src/main/java/ru/etu/petci/observers/RepositoryObserver.java +++ b/src/main/java/ru/etu/petci/observers/RepositoryObserver.java @@ -1,10 +1,11 @@ package ru.etu.petci.observers; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; +import ru.etu.petci.exceptions.RepositoryNotFoundException; +import ru.etu.petci.jobs.JobsExecutor; + +import java.io.*; import java.nio.file.Path; +import java.util.Objects; import java.util.Properties; import java.util.Scanner; import java.util.concurrent.Executors; @@ -15,9 +16,11 @@ public class RepositoryObserver { + public static final String REPOSITORY_PROPERTIES = "repository.properties"; private Path repositoryPath; - private String lastHash; // Hash of last commit - private String branchName; // Name of the observed branch + private String lastHash; // Hash of last commit + private String branchName; // Name of the observed branch + private JobsExecutor executor; private final ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); private static final Logger LOGGER = Logger.getLogger(RepositoryObserver.class.getName()); @@ -25,21 +28,17 @@ public class RepositoryObserver { LOGGER.setLevel(Level.INFO); } - public boolean setRepositoryPath(Path path) { - boolean isSet = false; - path = path.toAbsolutePath().normalize(); + public boolean isRepositoryExists() throws InterruptedException { + if (Objects.isNull(repositoryPath)) { + return false; + } Process gitProcess = null; - + int exitStatus = -1; try { - gitProcess = Runtime.getRuntime().exec("git ls-remote %s".formatted(path)); - if (gitProcess.exitValue() == 0) { - repositoryPath = path; - isSet = true; - } else { - LOGGER.log(Level.WARNING, "Unable to find repository by path: {0}", repositoryPath); - System.out.println("Unable to find repository by path"); - } + gitProcess = Runtime.getRuntime().exec("git ls-remote %s".formatted(repositoryPath)); + gitProcess.waitFor(); + exitStatus = gitProcess.exitValue(); } catch (IOException e) { LOGGER.warning("Error while executing 'git ls-remote'.%n" + e.getMessage()); } finally { @@ -47,7 +46,14 @@ public boolean setRepositoryPath(Path path) { gitProcess.destroy(); } } - return isSet; + return exitStatus == 0; + } + + + public void setRepositoryPath(Path path) { + if (Objects.isNull(path)) + return; + repositoryPath = path.toAbsolutePath().normalize(); } @@ -63,8 +69,10 @@ public String getBranchName() { return branchName; } - public boolean setRepositoryPath(String path) { - return setRepositoryPath(Path.of(path)); + public void setRepositoryPath(String path) { + if (Objects.isNull(path)) + return; + setRepositoryPath(Path.of(path)); } public Path getRepositoryPath() { @@ -78,37 +86,51 @@ private void checkRepositoryUpdate() { String currentHash = scanner.nextLine(); if (currentHash.length() == 40 && !currentHash.equals(lastHash)) { lastHash = currentHash; - try { - Properties properties = new Properties(); - properties.load(new FileReader("repository.properties")); + + + // Save new hash to properties + Properties properties = new Properties(); + try (var propertyReader = new FileReader(REPOSITORY_PROPERTIES); + var propertyWriter = new FileWriter(REPOSITORY_PROPERTIES)) { + properties.load(propertyReader); properties.setProperty("last_hash", lastHash); - properties.store(new FileWriter("repository.properties"), "repository settings"); - } catch (IOException e) { - LOGGER.log(Level.WARNING, "Error while working with repository properties"); + properties.store(propertyWriter, "repository settings"); } + LOGGER.log(Level.INFO, "Commits checked. New commit was found. Hash: {0}", lastHash); + executor.runJobs(); } else { LOGGER.info("Commits checked. No new commits found."); } } catch (FileNotFoundException e) { - LOGGER.warning(e.getMessage()); - System.out.printf("Branch \"%s\" does not exist!%n", getBranchName()); + LOGGER.severe(e.getMessage()); + System.out.printf("Unable to find branch \"%s\"%n", getBranchName()); + service.shutdown(); + System.exit(1); + } catch (IOException e) { + LOGGER.severe(e.getMessage()); service.shutdown(); System.exit(1); } } - public void start() throws InterruptedException { - // Check for a new commit per 3 seconds - if (repositoryPath != null) { + public void start() throws InterruptedException, RepositoryNotFoundException { + if (isRepositoryExists()) { + // Check for a new commit per 3 seconds service.scheduleWithFixedDelay(this::checkRepositoryUpdate, 0, 3, TimeUnit.SECONDS); Thread.currentThread().join(); + } else { + throw new RepositoryNotFoundException("Unable to find git repository by path: %s".formatted(repositoryPath)); } } public void setLastHash(String lastHash) { - if (lastHash.length() == 40) { + if (Objects.nonNull(lastHash) && lastHash.length() == 40) { this.lastHash = lastHash; } } + + public void setExecutor(JobsExecutor executor) { + this.executor = executor; + } }