diff --git a/cms-api/src/main/java/com/condation/cms/api/eventbus/events/RepoCheckoutEvent.java b/cms-api/src/main/java/com/condation/cms/api/eventbus/events/RepoCheckoutEvent.java new file mode 100644 index 000000000..780d90dee --- /dev/null +++ b/cms-api/src/main/java/com/condation/cms/api/eventbus/events/RepoCheckoutEvent.java @@ -0,0 +1,33 @@ +package com.condation.cms.api.eventbus.events; + +/*- + * #%L + * cms-api + * %% + * Copyright (C) 2023 - 2025 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.condation.cms.api.eventbus.Event; + +/** + * + * @author t.marx + */ +public record RepoCheckoutEvent(String repo) implements Event { + +} diff --git a/cms-git/src/main/java/com/condation/cms/git/Config.java b/cms-git/src/main/java/com/condation/cms/git/Config.java index 5ef3d8349..3fbb626cf 100644 --- a/cms-git/src/main/java/com/condation/cms/git/Config.java +++ b/cms-git/src/main/java/com/condation/cms/git/Config.java @@ -27,6 +27,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +import java.util.Optional; import lombok.Getter; import lombok.Setter; import org.yaml.snakeyaml.Yaml; @@ -46,5 +47,7 @@ public static Config load (final Path file) throws IOException { @Setter List repos; - + public Optional find (String name) { + return repos.stream().filter(repo -> repo.getName().equals(name)).findFirst(); + } } diff --git a/cms-git/src/main/java/com/condation/cms/git/GitScheduler.java b/cms-git/src/main/java/com/condation/cms/git/GitScheduler.java index a6e89bab3..06751286d 100644 --- a/cms-git/src/main/java/com/condation/cms/git/GitScheduler.java +++ b/cms-git/src/main/java/com/condation/cms/git/GitScheduler.java @@ -43,13 +43,11 @@ public class GitScheduler { private final Scheduler scheduler; - private final TaskRunner taskRunner; public void schedule(final Repo repo) { JobDataMap data = new JobDataMap(); data.put("repo", repo); - data.put("taskRunner", taskRunner); JobDetail jobDetail = JobBuilder .newJob(UpdateRepoJob.class) .withIdentity(repo.getName(), "update-repo") diff --git a/cms-git/src/main/java/com/condation/cms/git/RepositoryManager.java b/cms-git/src/main/java/com/condation/cms/git/RepositoryManager.java index 0a6c48baa..64de68b61 100644 --- a/cms-git/src/main/java/com/condation/cms/git/RepositoryManager.java +++ b/cms-git/src/main/java/com/condation/cms/git/RepositoryManager.java @@ -23,7 +23,7 @@ */ import com.condation.cms.git.tasks.CloneTask; -import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -40,22 +40,24 @@ public class RepositoryManager { private final Scheduler scheduler; Config config; - TaskRunner taskRunner; GitScheduler gitScheduler; - public void init(final Path configFile) throws IOException { + public void init(final Path configFile) throws Exception { + if (!Files.exists(configFile)) { + log.info("no repository configuration found"); + return; + } config = Config.load(configFile); - taskRunner = new TaskRunner(); - gitScheduler = new GitScheduler(scheduler, taskRunner); + gitScheduler = new GitScheduler(scheduler); if (config.getRepos() != null) { log.debug("initial clone repositories"); for (var repo : config.getRepos()) { log.debug("clone {}", repo.getName()); - var result = taskRunner.execute(new CloneTask(repo)); + var result = new CloneTask(repo).call(); try { - log.debug("result : {} ", result.get()); + log.debug("result : {} ", result); log.debug("schedule repo"); gitScheduler.schedule(repo); } catch (Exception ex) { @@ -65,9 +67,20 @@ public void init(final Path configFile) throws IOException { } } - - public void close() throws IOException { - taskRunner.executor.shutdown(); + + public void updateRepo (String name) { + log.debug("try updating git repo: {}", name); + if (config == null) { + log.warn("config not loaded"); + return; + } + var repo = config.find(name); + if (repo.isEmpty()) { + log.warn("repository {} not found", name); + return; + } + log.debug("updating git repo: {}", name); + new UpdateRepoJob().execute(repo.get()); + log.debug("get repo {} updated", name); } - } diff --git a/cms-git/src/main/java/com/condation/cms/git/UpdateRepoJob.java b/cms-git/src/main/java/com/condation/cms/git/UpdateRepoJob.java index 37fd1a0c3..3fc0c36e1 100644 --- a/cms-git/src/main/java/com/condation/cms/git/UpdateRepoJob.java +++ b/cms-git/src/main/java/com/condation/cms/git/UpdateRepoJob.java @@ -21,8 +21,6 @@ * . * #L% */ - - import com.condation.cms.git.tasks.FetchTask; import com.condation.cms.git.tasks.MergeTask; import com.condation.cms.git.tasks.ResetTask; @@ -37,24 +35,27 @@ */ @Slf4j public class UpdateRepoJob implements Job { - + @Override public void execute(JobExecutionContext context) throws JobExecutionException { - + Repo repo = (Repo) context.getJobDetail().getJobDataMap().get("repo"); - TaskRunner taskRunner = (TaskRunner) context.getJobDetail().getJobDataMap().get("taskRunner"); - + + execute(repo); + } + + protected void execute(Repo repo) { try { - var fetch = taskRunner.execute(new FetchTask(repo)); - - if (fetch.get()) { + var fetch = new FetchTask(repo).call(); + + if (fetch) { log.debug("fetch {} done", repo.getName()); - var merge = taskRunner.execute(new MergeTask(repo)); - if (merge.get()) { + var merge = new MergeTask(repo).call(); + if (merge) { log.debug("{} merged", repo.getName()); } else { log.error("merge {} error", repo.getName()); - var reset = taskRunner.execute(new ResetTask(repo)); + var reset = new ResetTask(repo).call(); log.debug("reset {}", repo.getName()); } } else { diff --git a/cms-git/src/main/java/com/condation/cms/git/tasks/FetchTask.java b/cms-git/src/main/java/com/condation/cms/git/tasks/FetchTask.java index 31d2bc384..f22748730 100644 --- a/cms-git/src/main/java/com/condation/cms/git/tasks/FetchTask.java +++ b/cms-git/src/main/java/com/condation/cms/git/tasks/FetchTask.java @@ -26,7 +26,6 @@ import com.condation.cms.git.Repo; import com.condation.cms.git.Task; -import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import lombok.RequiredArgsConstructor; diff --git a/cms-git/src/main/java/com/condation/cms/git/tasks/MergeTask.java b/cms-git/src/main/java/com/condation/cms/git/tasks/MergeTask.java index f30287ab5..9b3de59b2 100644 --- a/cms-git/src/main/java/com/condation/cms/git/tasks/MergeTask.java +++ b/cms-git/src/main/java/com/condation/cms/git/tasks/MergeTask.java @@ -26,7 +26,6 @@ import com.condation.cms.git.Repo; import com.condation.cms.git.Task; -import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import lombok.RequiredArgsConstructor; diff --git a/cms-git/src/test/java/com/condation/cms/git/FlowTest.java b/cms-git/src/test/java/com/condation/cms/git/FlowTest.java index e95940a96..1c09bd28c 100644 --- a/cms-git/src/test/java/com/condation/cms/git/FlowTest.java +++ b/cms-git/src/test/java/com/condation/cms/git/FlowTest.java @@ -23,8 +23,6 @@ */ -import com.condation.cms.git.TaskRunner; -import com.condation.cms.git.Config; import com.condation.cms.git.tasks.CloneTask; import com.condation.cms.git.tasks.FetchTask; import com.condation.cms.git.tasks.MergeTask; @@ -32,7 +30,6 @@ import java.io.IOException; import java.nio.file.Path; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; import org.junit.jupiter.api.Test; /** @@ -41,28 +38,26 @@ */ public class FlowTest { - TaskRunner runner = new TaskRunner(); - @Test - void flow_test () throws IOException, InterruptedException, ExecutionException { + void flow_test () throws Exception { var config = Config.load(Path.of("git.yaml")); - Future clone = runner.execute(new CloneTask(config.getRepos().get(0))); + Boolean clone = new CloneTask(config.getRepos().get(0)).call(); - if (clone.get()) { + if (clone) { System.out.println("clone done"); - var fetch = runner.execute(new FetchTask(config.getRepos().get(0))); + var fetch = new FetchTask(config.getRepos().get(0)).call(); - if (fetch.get()) { + if (fetch) { System.out.println("fetch done"); - var merge = runner.execute(new MergeTask(config.getRepos().get(0))); - if (merge.get()) { + var merge = new MergeTask(config.getRepos().get(0)).call(); + if (merge) { System.out.println("merged"); } else { System.out.println("merge error"); - var reset = runner.execute(new ResetTask(config.getRepos().get(0))); - System.out.println("reset " + reset.get()); + var reset = new ResetTask(config.getRepos().get(0)).call(); + System.out.println("reset " + reset); } } else { System.out.println("fetch error"); diff --git a/cms-git/src/test/java/com/condation/cms/git/GitSchedulerTest.java b/cms-git/src/test/java/com/condation/cms/git/GitSchedulerTest.java index b11381f9d..418a7bec2 100644 --- a/cms-git/src/test/java/com/condation/cms/git/GitSchedulerTest.java +++ b/cms-git/src/test/java/com/condation/cms/git/GitSchedulerTest.java @@ -23,9 +23,6 @@ */ -import com.condation.cms.git.GitScheduler; -import com.condation.cms.git.TaskRunner; -import com.condation.cms.git.Config; import java.io.IOException; import java.nio.file.Path; import java.time.Duration; @@ -44,7 +41,6 @@ public class GitSchedulerTest { static GitScheduler gitScheduler; - static TaskRunner runner = new TaskRunner(); static Scheduler scheduler; @@ -55,12 +51,11 @@ static void setup () throws Exception { scheduler = schedulerFactory.getScheduler(); scheduler.start(); - gitScheduler = new GitScheduler(scheduler, runner); + gitScheduler = new GitScheduler(scheduler); } @AfterAll static void shutdown () throws Exception { scheduler.shutdown(); - runner.executor.shutdown(); } @Test diff --git a/cms-server/src/main/java/com/condation/cms/cli/CLICommand.java b/cms-server/src/main/java/com/condation/cms/cli/CLICommand.java index 36f320352..2c7726b83 100644 --- a/cms-server/src/main/java/com/condation/cms/cli/CLICommand.java +++ b/cms-server/src/main/java/com/condation/cms/cli/CLICommand.java @@ -32,7 +32,7 @@ * @author t.marx */ @CommandLine.Command(name = "", subcommands = { - ServerCommand.class, HostCommands.class, ExtensionCommands.class, ModuleCommands.class, ThemeCommands.class}) + ServerCommand.class, HostCommands.class, ExtensionCommands.class, ModuleCommands.class, ThemeCommands.class, RepoCommands.class}) @Slf4j public class CLICommand implements Runnable { diff --git a/cms-git/src/main/java/com/condation/cms/git/TaskRunner.java b/cms-server/src/main/java/com/condation/cms/cli/commands/RepoCommands.java similarity index 66% rename from cms-git/src/main/java/com/condation/cms/git/TaskRunner.java rename to cms-server/src/main/java/com/condation/cms/cli/commands/RepoCommands.java index 14c2232bc..b2c051e6b 100644 --- a/cms-git/src/main/java/com/condation/cms/git/TaskRunner.java +++ b/cms-server/src/main/java/com/condation/cms/cli/commands/RepoCommands.java @@ -1,8 +1,8 @@ -package com.condation.cms.git; +package com.condation.cms.cli.commands; /*- * #%L - * cms-git + * cms-server * %% * Copyright (C) 2023 - 2024 CondationCMS * %% @@ -23,19 +23,24 @@ */ -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; +import com.condation.cms.cli.commands.repo.Checkout; +import lombok.extern.slf4j.Slf4j; +import picocli.CommandLine; /** * * @author t.marx */ -public class TaskRunner { - - ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); - - public Future execute (Task task) { - return executor.submit(task); +@CommandLine.Command( + name = "repo", + subcommands = { + Checkout.class + }) +@Slf4j +public class RepoCommands implements Runnable { + + @Override + public void run() { + System.out.println("Subcommand needed: 'checkout'"); } } diff --git a/cms-server/src/main/java/com/condation/cms/cli/commands/repo/Checkout.java b/cms-server/src/main/java/com/condation/cms/cli/commands/repo/Checkout.java new file mode 100644 index 000000000..f49aa1893 --- /dev/null +++ b/cms-server/src/main/java/com/condation/cms/cli/commands/repo/Checkout.java @@ -0,0 +1,92 @@ +package com.condation.cms.cli.commands.repo; + +/*- + * #%L + * cms-server + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + + + +import com.condation.cms.api.Constants; +import com.condation.cms.api.ServerProperties; +import com.condation.cms.api.utils.ServerUtil; +import com.condation.cms.core.configuration.ConfigurationFactory; +import com.condation.cms.core.configuration.properties.ExtendedServerProperties; +import com.condation.cms.ipc.Command; +import com.condation.cms.ipc.IPCClient; +import com.google.common.base.Strings; +import java.nio.file.Files; +import java.util.Optional; +import lombok.extern.slf4j.Slf4j; +import picocli.CommandLine; + +/** + * + * @author t.marx + */ +@CommandLine.Command(name = "checkout") +@Slf4j +public class Checkout implements Runnable { + + @CommandLine.Parameters( + paramLabel = "", + index = "0", + description = "The name of the repository in the git.yaml." + ) + private String repo = ""; + + @Override + public void run() { + try { + + Optional handle = getCMSProcess(); + + if (Strings.isNullOrEmpty(repo)) { + System.err.println("no repository specified"); + System.exit(1); + } + + if (handle.isEmpty()) { + System.out.println("server not running"); + } else { + ServerProperties properties = new ExtendedServerProperties(ConfigurationFactory.serverConfiguration()); + + IPCClient ipcClient = new IPCClient(properties.ipc()); + + ipcClient.send(new Command("repo_checkout").setHeader("repo", repo)); + } + + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static Optional getCMSProcess () throws Exception { + var pidFile = ServerUtil.getPath(Constants.PID_FILE); + if (!Files.exists(pidFile)) { + return Optional.empty(); + } + var pid = Files.readString(pidFile); + return ProcessHandle.of(Long.parseLong(pid.trim())); + } + + +} diff --git a/cms-server/src/main/java/com/condation/cms/cli/commands/server/Startup.java b/cms-server/src/main/java/com/condation/cms/cli/commands/server/Startup.java index f49aafd5e..e0b38dc48 100644 --- a/cms-server/src/main/java/com/condation/cms/cli/commands/server/Startup.java +++ b/cms-server/src/main/java/com/condation/cms/cli/commands/server/Startup.java @@ -35,13 +35,10 @@ import com.condation.cms.server.configs.ServerGlobalModule; import com.condation.cms.server.JettyServer; import com.google.inject.Guice; -import com.google.inject.Injector; import java.io.IOException; import java.nio.file.Files; -import java.nio.file.Path; import java.util.Properties; import lombok.extern.slf4j.Slf4j; -import org.quartz.Scheduler; import picocli.CommandLine; /** @@ -58,6 +55,7 @@ public void run() { System.setProperty("polyglot.engine.WarnInterpreterOnly", "false"); System.setProperty("polyglotimpl.DisableClassPathIsolation", "true"); + //System.setProperty("polyglot.engine.WarnVirtualThreadSupport", "false"); var globalInjector = Guice.createInjector(new ServerGlobalModule()); ServerProperties properties = globalInjector.getInstance(ServerProperties.class); @@ -69,7 +67,7 @@ public void run() { ServerContext.IS_DEV = properties.dev(); - initGitRepositoryManager(globalInjector); + globalInjector.getInstance(RepositoryManager.class); var server = new JettyServer(globalInjector); @@ -117,24 +115,6 @@ private static void writePidFile () throws IOException { Files.writeString(ServerUtil.getPath(Constants.PID_FILE), String.valueOf(ProcessHandle.current().pid())); } - private static void initGitRepositoryManager(Injector globaInjector) throws IOException { - Path gitConfig = ServerUtil.getPath("git.yaml"); - if (!Files.exists(gitConfig)) { - log.info("no repository configuration found"); - return; - } - log.info("repository configuration found"); - final RepositoryManager repositoryManager = new RepositoryManager(globaInjector.getInstance(Scheduler.class)); - repositoryManager.init(gitConfig); - - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - try { - repositoryManager.close(); - } catch (IOException ex) { - log.error("error closing repo manager", ex); - } - })); - } private static void printStartup(ServerProperties properties) throws IOException { try (var in = com.condation.cms.Startup.class.getResourceAsStream("application.properties")) { diff --git a/cms-server/src/main/java/com/condation/cms/ipc/IPCProtocol.java b/cms-server/src/main/java/com/condation/cms/ipc/IPCProtocol.java index 61cbd462c..914e8e826 100644 --- a/cms-server/src/main/java/com/condation/cms/ipc/IPCProtocol.java +++ b/cms-server/src/main/java/com/condation/cms/ipc/IPCProtocol.java @@ -26,6 +26,7 @@ import com.condation.cms.api.IPCProperties; import com.condation.cms.api.eventbus.Event; +import com.condation.cms.api.eventbus.events.RepoCheckoutEvent; import com.condation.cms.api.eventbus.events.lifecycle.ReloadHostEvent; import com.condation.cms.api.eventbus.events.lifecycle.ServerShutdownInitiated; import java.util.function.Consumer; @@ -71,6 +72,14 @@ public void processInput(final String theInput) { } log.debug("trigger reload host event"); eventConsumer.accept(new ReloadHostEvent((String)hostHeader.get())); + } else if ("repo_checkout".equals(command.getCommand())) { + var repoHeader = command.getHeader("repo"); + if (!repoHeader.isPresent()) { + log.warn("repo header not set"); + return; + } + log.debug("trigger checkout of repo {}", repoHeader); + eventConsumer.accept(new RepoCheckoutEvent((String)repoHeader.get())); } } } diff --git a/cms-server/src/main/java/com/condation/cms/server/JettyServer.java b/cms-server/src/main/java/com/condation/cms/server/JettyServer.java index ca03566e2..f27632da7 100644 --- a/cms-server/src/main/java/com/condation/cms/server/JettyServer.java +++ b/cms-server/src/main/java/com/condation/cms/server/JettyServer.java @@ -29,6 +29,7 @@ import com.condation.cms.api.configuration.configs.ServerConfiguration; import com.condation.cms.api.eventbus.Event; import com.condation.cms.api.eventbus.EventBus; +import com.condation.cms.api.eventbus.events.RepoCheckoutEvent; import com.condation.cms.api.eventbus.events.lifecycle.HostReadyEvent; import com.condation.cms.api.eventbus.events.lifecycle.ReloadHostEvent; import com.condation.cms.api.eventbus.events.lifecycle.ServerReadyEvent; @@ -36,6 +37,7 @@ import com.condation.cms.api.utils.ServerUtil; import com.condation.cms.api.utils.SiteUtil; import com.condation.cms.core.eventbus.DefaultEventBus; +import com.condation.cms.git.RepositoryManager; import com.google.inject.Injector; import java.io.IOException; import java.nio.file.Files; @@ -121,6 +123,9 @@ public void startup() throws IOException { serverEventBus.register(ReloadHostEvent.class, (event) -> { reloadVHost(event.host()); }); + serverEventBus.register(RepoCheckoutEvent.class, (event) -> { + globalInjector.getInstance(RepositoryManager.class).updateRepo(event.repo()); + }); Runtime.getRuntime().addShutdownHook(new Thread(() -> { log.debug("shutting down"); diff --git a/cms-server/src/main/java/com/condation/cms/server/configs/ServerGlobalModule.java b/cms-server/src/main/java/com/condation/cms/server/configs/ServerGlobalModule.java index ebab206e1..269282501 100644 --- a/cms-server/src/main/java/com/condation/cms/server/configs/ServerGlobalModule.java +++ b/cms-server/src/main/java/com/condation/cms/server/configs/ServerGlobalModule.java @@ -24,12 +24,15 @@ import com.condation.cms.api.ServerProperties; +import com.condation.cms.api.utils.ServerUtil; import com.condation.cms.core.configuration.ConfigurationFactory; import com.condation.cms.core.configuration.properties.ExtendedServerProperties; +import com.condation.cms.git.RepositoryManager; import com.google.inject.Binder; import com.google.inject.Provides; import com.google.inject.Singleton; import java.io.IOException; +import java.nio.file.Path; import lombok.extern.slf4j.Slf4j; import org.graalvm.polyglot.Engine; import org.quartz.Scheduler; @@ -82,4 +85,16 @@ public Engine engine() throws IOException { .option("engine.WarnInterpreterOnly", "false") .build(); } + + @Provides + @Singleton + public RepositoryManager repositoryManager (Scheduler scheduler) throws Exception { + Path gitConfig = ServerUtil.getPath("git.yaml"); + + log.info("repository configuration found"); + final RepositoryManager repositoryManager = new RepositoryManager(scheduler); + repositoryManager.init(gitConfig); + + return repositoryManager; + } }