From d80a4b3ab35af575fd06d4e2fb9a3726418c6727 Mon Sep 17 00:00:00 2001
From: Kai Reinhard <K.Reinhard@micromata.de>
Date: Thu, 15 Apr 2021 20:39:03 +0000
Subject: [PATCH] Package typo, java -> kotlin

---
 borgbutler-core/src/main/kotlin/de/micromata/borgbutler/BorgJob.kt                           |  135 +++++++++++++
 borgbutler-core/src/main/java/de/micromata/borgbutler/cache/JCSCache.java                    |    2 
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/Main.java                     |    2 
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/BorgInstallation.java         |    4 
 README.adoc                                                                                  |    2 
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ConfigurationRest.java   |    2 
 borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/Configuration.kt              |    2 
 borgbutler-core/src/main/kotlin/de/micromata/borgbutler/jobs/AbstractCommandLineJob.kt       |  194 +++++++++++++++++++
 borgbutler-core/src/test/java/de/micromata/borgbutler/cache/CacheTest.java                   |    4 
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java        |    2 
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/JobsRest.java            |    2 
 borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgInstallationTest.java     |    2 
 /dev/null                                                                                    |  202 --------------------
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/ServerConfiguration.java      |    4 
 borgbutler-core/src/main/java/de/micromata/borgbutler/demo/DemoRepos.java                    |    2 
 borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/YamlUtils.kt                  |    2 
 borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/ConfigurationHandler.kt       |    2 
 borgbutler-core/src/test/java/de/micromata/borgbutler/config/ConfigHandlerTest.java          |    2 
 borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java                 |    4 
 borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/Legacy.kt                     |    5 
 borgbutler-docker/app/Dockerfile                                                             |    2 
 borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/BorgRepoConfigsRest.java |    2 
 22 files changed, 352 insertions(+), 228 deletions(-)

diff --git a/README.adoc b/README.adoc
index cff912d..eef6d5b 100644
--- a/README.adoc
+++ b/README.adoc
@@ -60,7 +60,7 @@
 BorgButler working directory `$HOME/BorgButler` is assumed, but you may define any other.
 
 1. Create your local BorgButler directory: `mkdir $HOME/BorgButler` (for config, caches, backups and restoring of backuped files and directories)
-2. `docker run -v $HOME/BorgButler:/Borgbutler -p 127.0.0.1:9042:9042 --name borgbuttler kreinhard/borgbutler` (exporting of `.ssh` is useful for ssh remotes, otherwise skip this setting.)
+2. `docker run -v $HOME/BorgButler:/BorgButler -v  $HOME/.ssh:/home/borgbutler/.ssh -p 127.0.0.1:9042:9042 --name borgbuttler kreinhard/borgbutler` (exporting of `.ssh` is useful for ssh remotes, otherwise skip this setting.)
 3. Stopping: simly click `CTRL-C`.
 4. Restart: `docker start`
 5. Stop: `docker stop`
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgJob.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgJob.java
deleted file mode 100644
index b661974..0000000
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/BorgJob.java
+++ /dev/null
@@ -1,154 +0,0 @@
-package de.micromata.borgbutler;
-
-import de.micromata.borgbutler.config.BorgRepoConfig;
-import org.micromata.borgbutler.config.ConfigurationHandler;
-import de.micromata.borgbutler.data.Archive;
-import de.micromata.borgbutler.demo.DemoRepos;
-import de.micromata.borgbutler.jobs.AbstractCommandLineJob;
-import de.micromata.borgbutler.jobs.JobResult;
-import de.micromata.borgbutler.json.JsonUtils;
-import de.micromata.borgbutler.json.borg.ProgressInfo;
-import org.apache.commons.exec.CommandLine;
-import org.apache.commons.exec.environment.EnvironmentUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.Map;
-
-/**
- * A queue is important because Borg doesn't support parallel calls for one repository.
- * For each repository one single queue is allocated.
- */
-public class BorgJob<T> extends AbstractCommandLineJob implements Cloneable {
-    private Logger log = LoggerFactory.getLogger(BorgJob.class);
-    private BorgCommand command;
-    /**
-     * Some jobs may store here the result of the command (e. g. {@link BorgCommands#listArchiveContent(BorgRepoConfig, Archive)}).
-     */
-    protected T payload;
-
-    private ProgressInfo progressInfo;
-
-    public BorgJob(BorgCommand command) {
-        this.command = command;
-        setWorkingDirectory(command.getWorkingDir());
-        setDescription(command.getDescription());
-    }
-
-    private BorgJob() {
-    }
-
-    @Override
-    protected CommandLine buildCommandLine() {
-        if (command == null) {
-            return null;
-        }
-        String borgCommand = ConfigurationHandler.getConfiguration().getBorgCommand();
-        if (StringUtils.isBlank(borgCommand)) {
-            log.error("Can't run empty borg command.");
-            return null;
-        }
-        CommandLine commandLine = new CommandLine(borgCommand);
-        commandLine.addArgument(command.getCommand());
-        if (command.getParams() != null) {
-            for (String param : command.getParams()) {
-                if (param != null)
-                    commandLine.addArgument(param);
-            }
-        }
-        if (command.getRepoArchive() != null) {
-            commandLine.addArgument(command.getRepoArchive());
-        }
-        if (command.getArgs() != null) {
-            for (String arg : command.getArgs()) {
-                if (arg != null)
-                    commandLine.addArgument(arg);
-            }
-        }
-        return commandLine;
-    }
-
-    public void processStdErrLine(String line, int level) {
-        if (StringUtils.startsWith(line, "{\"message")) {
-            ProgressInfo message = JsonUtils.fromJson(ProgressInfo.class, line);
-            if (message != null) {
-                progressInfo = message;
-                return;
-            }
-        }
-        super.processStdErrLine(line, level);
-    }
-
-    @Override
-    protected Map<String, String> getEnvironment() throws IOException {
-        BorgRepoConfig repoConfig = command.getRepoConfig();
-        if (repoConfig == null) {
-            return null;
-        }
-        Map<String, String> env = EnvironmentUtils.getProcEnvironment();
-        String[] variables = repoConfig.getEnvironmentVariables(true);
-        for (String variable : variables) {
-            // For MacOS BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
-            String environmentVariable = variable.replace("$USER", System.getProperty("user.name"));
-            addEnvironmentVariable(env, environmentVariable);
-        }
-        return env;
-    }
-
-    @Override
-    public JobResult<String> execute() {
-        if (command.getRepoConfig() != null && DemoRepos.isDemo(command.getRepoConfig().getRepo())) {
-            return DemoRepos.execute(this);
-        }
-        return super.execute();
-    }
-
-    @Override
-    public BorgJob<?> clone() {
-        BorgJob<?> clone = new BorgJob<>();
-        if (command != null) {
-            // Needed for getting environment variables: JsonJob of borgbutler-server.
-            clone.command = new BorgCommand().setRepoConfig(command.getRepoConfig());
-        }
-        clone.setUniqueJobNumber(getUniqueJobNumber());
-        clone.setTitle(getTitle());
-        clone.setExecuteStarted(isExecuteStarted());
-        clone.setCommandLineAsString(getCommandLineAsString());
-        clone.setCancellationRequested(isCancellationRequested());
-        clone.setStatus(getStatus());
-        clone.setWorkingDirectory(getWorkingDirectory());
-        clone.setDescription(getDescription());
-        if (progressInfo != null) {
-            clone.setProgressInfo(progressInfo.clone());
-        }
-        clone.setCreateTime(getCreateTime());
-        clone.setStartTime(getStartTime());
-        clone.setStopTime(getStopTime());
-        return clone;
-    }
-
-    @Override
-    public void cleanUp() {
-        super.cleanUp();
-        payload = null;
-    }
-
-    public BorgCommand getCommand() {
-        return this.command;
-    }
-
-    public T getPayload() {
-        return this.payload;
-    }
-
-    public ProgressInfo getProgressInfo() {
-        return this.progressInfo;
-    }
-
-    protected BorgJob<T> setProgressInfo(ProgressInfo progressInfo) {
-        this.progressInfo = progressInfo;
-        return this;
-    }
-}
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java
index ce585cc..9ed4852 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/ButlerCache.java
@@ -3,8 +3,8 @@
 import de.micromata.borgbutler.BorgCommandResult;
 import de.micromata.borgbutler.BorgCommands;
 import de.micromata.borgbutler.config.BorgRepoConfig;
-import org.micromata.borgbutler.config.Configuration;
-import org.micromata.borgbutler.config.ConfigurationHandler;
+import de.micromata.borgbutler.config.Configuration;
+import de.micromata.borgbutler.config.ConfigurationHandler;
 import de.micromata.borgbutler.data.Archive;
 import de.micromata.borgbutler.data.ArchiveShortInfo;
 import de.micromata.borgbutler.data.FileSystemFilter;
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/JCSCache.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/JCSCache.java
index ea85a0a..7cea260 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/JCSCache.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/cache/JCSCache.java
@@ -1,6 +1,6 @@
 package de.micromata.borgbutler.cache;
 
-import org.micromata.borgbutler.config.ConfigurationHandler;
+import de.micromata.borgbutler.config.ConfigurationHandler;
 import org.apache.commons.jcs.JCS;
 import org.apache.commons.jcs.access.CacheAccess;
 import org.slf4j.Logger;
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/demo/DemoRepos.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/demo/DemoRepos.java
index c94d2bf..df4865d 100644
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/demo/DemoRepos.java
+++ b/borgbutler-core/src/main/java/de/micromata/borgbutler/demo/DemoRepos.java
@@ -3,7 +3,7 @@
 import de.micromata.borgbutler.BorgCommand;
 import de.micromata.borgbutler.BorgJob;
 import de.micromata.borgbutler.config.BorgRepoConfig;
-import org.micromata.borgbutler.config.ConfigurationHandler;
+import de.micromata.borgbutler.config.ConfigurationHandler;
 import de.micromata.borgbutler.config.Definitions;
 import de.micromata.borgbutler.data.Repository;
 import de.micromata.borgbutler.jobs.JobResult;
diff --git a/borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractCommandLineJob.java b/borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractCommandLineJob.java
deleted file mode 100644
index 5281607..0000000
--- a/borgbutler-core/src/main/java/de/micromata/borgbutler/jobs/AbstractCommandLineJob.java
+++ /dev/null
@@ -1,202 +0,0 @@
-package de.micromata.borgbutler.jobs;
-
-import de.micromata.borgbutler.config.Definitions;
-import org.apache.commons.exec.*;
-import org.apache.commons.exec.environment.EnvironmentUtils;
-import org.apache.commons.io.output.ByteArrayOutputStream;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Map;
-
-/**
- * A queue is important because Borg doesn't support parallel calls for one repository.
- * For each repository one single queue is allocated.
- */
-public abstract class AbstractCommandLineJob extends AbstractJob<String> {
-    private Logger log = LoggerFactory.getLogger(AbstractCommandLineJob.class);
-    private ExecuteWatchdog watchdog;
-    private boolean executeStarted;
-    private CommandLine commandLine;
-    /**
-     * The command line as string. This property is also used as ID for detecting multiple borg calls.
-     */
-    private String commandLineAsString;
-    private File workingDirectory;
-    private String description;
-    protected ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-    protected ByteArrayOutputStream errorOutputStream = new ByteArrayOutputStream();
-    protected boolean logError = true;
-
-    protected abstract CommandLine buildCommandLine();
-
-    @Override
-    public Object getId() {
-        return getCommandLineAsString();
-    }
-
-    public String getCommandLineAsString() {
-        if (commandLine == null) {
-            commandLine = buildCommandLine();
-        }
-        if (commandLine == null) {
-            return null;
-        }
-        if (commandLineAsString == null) {
-            commandLineAsString = commandLine.getExecutable() + " " + StringUtils.join(commandLine.getArguments(), " ");
-        }
-        return commandLineAsString;
-    }
-
-    @Override
-    public JobResult<String> execute() {
-        getCommandLineAsString();
-        if (commandLine == null) {
-            return null;
-        }
-        DefaultExecutor executor = new DefaultExecutor();
-        if (workingDirectory != null) {
-            executor.setWorkingDirectory(workingDirectory);
-        }
-        //executor.setExitValue(2);
-        this.watchdog = new ExecuteWatchdog(ExecuteWatchdog.INFINITE_TIMEOUT);
-        executor.setWatchdog(watchdog);
-        //  ExecuteResultHandler handler = new DefaultExecuteResultHandler();
-        PumpStreamHandler streamHandler = new PumpStreamHandler(new LogOutputStream() {
-            @Override
-            protected void processLine(String line, int level) {
-                processStdOutLine(line, level);
-            }
-        }, new LogOutputStream() {
-            @Override
-            protected void processLine(String line, int level) {
-                processStdErrLine(line, level);
-            }
-        });
-        executor.setStreamHandler(streamHandler);
-        String msg = StringUtils.isNotBlank(this.description) ? description + " ('" + commandLineAsString + "')..."
-                : "Executing '" + commandLineAsString + "'...";
-        log.info(msg);
-        this.executeStarted = true;
-        JobResult<String> result = new JobResult<>();
-        try {
-            executor.execute(commandLine, getEnvironment());
-            result.setStatus(JobResult.Status.OK);
-            log.info(msg + " Done.");
-        } catch (Exception ex) {
-            result.setStatus(JobResult.Status.ERROR);
-            if (logError && !isCancellationRequested() && getStatus() != Status.CANCELLED) {
-                log.error("Execution failed for job: '" + commandLineAsString + "': " + ex.getMessage());
-                log.error("Error output of job '" + commandLineAsString + "': "
-                        + getErrorString(2000));
-            }
-            failed();
-        }
-        result.setResultObject(outputStream.toString(Definitions.STD_CHARSET));
-        return result;
-    }
-
-    /**
-     * @param maxlength The result string will be abbreviated (in the middle).
-     * @return
-     * @see StringUtils#abbreviateMiddle(String, String, int)
-     */
-    public String getErrorString(int maxlength) {
-        return StringUtils.abbreviateMiddle(errorOutputStream.toString(Definitions.STD_CHARSET),
-                "\n    [... ***** error log abbreviated ***** ...]\n", maxlength);
-    }
-
-    public void processStdOutLine(String line, int level) {
-        //log.info(line);
-        try {
-            outputStream.write(line.getBytes());
-            outputStream.write("\n".getBytes());
-        } catch (IOException ex) {
-            log.error(ex.getMessage(), ex);
-        }
-    }
-
-    public void processStdErrLine(String line, int level) {
-        //log.info(line);
-        try {
-            errorOutputStream.write(line.getBytes());
-            errorOutputStream.write("\n".getBytes());
-        } catch (IOException ex) {
-            log.error(ex.getMessage(), ex);
-        }
-    }
-
-    @Override
-    protected void cancelRunningProcess() {
-        if (watchdog != null) {
-            log.info("Cancelling job #" + getUniqueJobNumber() + ": " + getId());
-            watchdog.destroyProcess();
-            watchdog = null;
-        }
-    }
-
-    protected Map<String, String> getEnvironment() throws IOException {
-        return null;
-    }
-
-    protected void addEnvironmentVariable(Map<String, String> env, String name, String value) {
-        if (StringUtils.isNotBlank(value)) {
-            EnvironmentUtils.addVariableToEnvironment(env, name + "=" + value);
-        }
-    }
-
-    /**
-     * @param env
-     * @param variable Variable in format "variable=value".
-     */
-    protected void addEnvironmentVariable(Map<String, String> env, String variable) {
-        if (StringUtils.isNotBlank(variable)) {
-            EnvironmentUtils.addVariableToEnvironment(env, variable);
-        }
-    }
-
-    /**
-     * Frees the output streams.
-     * Should be called after a job was done, failed or cancelled while running.
-     */
-    public void cleanUp() {
-        log.debug("Freeing resources of job: " + commandLineAsString);
-        outputStream = null;
-        errorOutputStream = null;
-    }
-
-    public boolean isExecuteStarted() {
-        return this.executeStarted;
-    }
-
-    public File getWorkingDirectory() {
-        return this.workingDirectory;
-    }
-
-    public String getDescription() {
-        return this.description;
-    }
-
-    protected AbstractCommandLineJob setExecuteStarted(boolean executeStarted) {
-        this.executeStarted = executeStarted;
-        return this;
-    }
-
-    protected AbstractCommandLineJob setCommandLineAsString(String commandLineAsString) {
-        this.commandLineAsString = commandLineAsString;
-        return this;
-    }
-
-    public AbstractCommandLineJob setWorkingDirectory(File workingDirectory) {
-        this.workingDirectory = workingDirectory;
-        return this;
-    }
-
-    public AbstractCommandLineJob setDescription(String description) {
-        this.description = description;
-        return this;
-    }
-}
diff --git a/borgbutler-core/src/main/kotlin/de/micromata/borgbutler/BorgJob.kt b/borgbutler-core/src/main/kotlin/de/micromata/borgbutler/BorgJob.kt
new file mode 100644
index 0000000..6f41b00
--- /dev/null
+++ b/borgbutler-core/src/main/kotlin/de/micromata/borgbutler/BorgJob.kt
@@ -0,0 +1,135 @@
+package de.micromata.borgbutler
+
+import de.micromata.borgbutler.config.ConfigurationHandler.Companion.getConfiguration
+import de.micromata.borgbutler.demo.DemoRepos
+import de.micromata.borgbutler.jobs.AbstractCommandLineJob
+import de.micromata.borgbutler.jobs.JobResult
+import de.micromata.borgbutler.json.JsonUtils
+import de.micromata.borgbutler.json.borg.ProgressInfo
+import org.apache.commons.exec.CommandLine
+import org.apache.commons.exec.environment.EnvironmentUtils
+import org.apache.commons.lang3.StringUtils
+import org.slf4j.LoggerFactory
+import java.io.IOException
+
+/**
+ * A queue is important because Borg doesn't support parallel calls for one repository.
+ * For each repository one single queue is allocated.
+ */
+open class BorgJob<T> : AbstractCommandLineJob, Cloneable {
+    private val log = LoggerFactory.getLogger(BorgJob::class.java)
+    var command: BorgCommand? = null
+        private set
+
+    /**
+     * Some jobs may store here the result of the command (e. g. [BorgCommands.listArchiveContent]).
+     */
+    @JvmField
+    var payload: T? = null
+    var progressInfo: ProgressInfo? = null
+        private set
+
+    constructor(command: BorgCommand) {
+        this.command = command
+        setWorkingDirectory(command.workingDir)
+        setDescription(command.description)
+    }
+
+    private constructor() {}
+
+    override fun buildCommandLine(): CommandLine? {
+        if (command == null) {
+            return null
+        }
+        val borgCommand = getConfiguration()!!.borgCommand
+        if (StringUtils.isBlank(borgCommand)) {
+            log.error("Can't run empty borg command.")
+            return null
+        }
+        val commandLine = CommandLine(borgCommand)
+        commandLine.addArgument(command!!.command)
+        if (command!!.params != null) {
+            for (param in command!!.params) {
+                if (param != null) commandLine.addArgument(param)
+            }
+        }
+        if (command!!.repoArchive != null) {
+            commandLine.addArgument(command!!.repoArchive)
+        }
+        if (command!!.args != null) {
+            for (arg in command!!.args) {
+                if (arg != null) commandLine.addArgument(arg)
+            }
+        }
+        return commandLine
+    }
+
+    override fun processStdErrLine(line: String, level: Int) {
+        if (StringUtils.startsWith(line, "{\"message")) {
+            val message = JsonUtils.fromJson(ProgressInfo::class.java, line)
+            if (message != null) {
+                progressInfo = message
+                return
+            }
+        }
+        super.processStdErrLine(line, level)
+    }
+
+    // For MacOS BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
+    @get:Throws(IOException::class)
+    override val environment: Map<String?, String?>?
+        protected get() {
+            val repoConfig = command!!.repoConfig ?: return null
+            val env = EnvironmentUtils.getProcEnvironment()
+            val variables = repoConfig.getEnvironmentVariables(true)
+            for (variable in variables) {
+                // For MacOS BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
+                val environmentVariable = variable.replace("\$USER", System.getProperty("user.name"))
+                addEnvironmentVariable(env, environmentVariable)
+            }
+            return env
+        }
+
+    override fun execute(): JobResult<String>? {
+        return if (command!!.repoConfig != null && DemoRepos.isDemo(command!!.repoConfig.repo)) {
+            DemoRepos.execute(this)
+        } else super.execute()
+    }
+
+    public override fun clone(): BorgJob<*> {
+        val clone: BorgJob<*> = BorgJob<Any>()
+        if (command != null) {
+            // Needed for getting environment variables: JsonJob of borgbutler-server.
+            clone.command = BorgCommand().setRepoConfig(command!!.repoConfig)
+        }
+        clone.uniqueJobNumber = uniqueJobNumber
+        clone.title = title
+        clone.setExecuteStarted(isExecuteStarted)
+        clone.setCommandLineAsString(getCommandLineAsString())
+        clone.isCancellationRequested = isCancellationRequested
+        clone.status = status
+        clone.setWorkingDirectory(workingDirectory)
+        clone.setDescription(description)
+        if (progressInfo != null) {
+            clone.setProgressInfo(progressInfo!!.clone())
+        }
+        clone.createTime = createTime
+        clone.startTime = startTime
+        clone.stopTime = stopTime
+        return clone
+    }
+
+    override fun cleanUp() {
+        super.cleanUp()
+        payload = null
+    }
+
+    fun getPayload(): T? {
+        return payload
+    }
+
+    protected fun setProgressInfo(progressInfo: ProgressInfo?): BorgJob<T> {
+        this.progressInfo = progressInfo
+        return this
+    }
+}
diff --git a/borgbutler-core/src/main/kotlin/org/micromata/borgbutler/config/Configuration.kt b/borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/Configuration.kt
similarity index 98%
rename from borgbutler-core/src/main/kotlin/org/micromata/borgbutler/config/Configuration.kt
rename to borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/Configuration.kt
index 07bbd39..7eb3cbf 100644
--- a/borgbutler-core/src/main/kotlin/org/micromata/borgbutler/config/Configuration.kt
+++ b/borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/Configuration.kt
@@ -1,4 +1,4 @@
-package org.micromata.borgbutler.config
+package de.micromata.borgbutler.config
 
 import com.fasterxml.jackson.annotation.JsonIgnore
 import com.fasterxml.jackson.annotation.JsonProperty
diff --git a/borgbutler-core/src/main/kotlin/org/micromata/borgbutler/config/ConfigurationHandler.kt b/borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/ConfigurationHandler.kt
similarity index 99%
rename from borgbutler-core/src/main/kotlin/org/micromata/borgbutler/config/ConfigurationHandler.kt
rename to borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/ConfigurationHandler.kt
index af3dbf4..2715560 100644
--- a/borgbutler-core/src/main/kotlin/org/micromata/borgbutler/config/ConfigurationHandler.kt
+++ b/borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/ConfigurationHandler.kt
@@ -1,4 +1,4 @@
-package org.micromata.borgbutler.config
+package de.micromata.borgbutler.config
 
 import de.micromata.borgbutler.config.Definitions
 import de.micromata.borgbutler.json.JsonUtils
diff --git a/borgbutler-core/src/main/kotlin/org/micromata/borgbutler/config/Legacy.kt b/borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/Legacy.kt
similarity index 90%
rename from borgbutler-core/src/main/kotlin/org/micromata/borgbutler/config/Legacy.kt
rename to borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/Legacy.kt
index a9f8828..b2b2a09 100644
--- a/borgbutler-core/src/main/kotlin/org/micromata/borgbutler/config/Legacy.kt
+++ b/borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/Legacy.kt
@@ -1,12 +1,9 @@
-package org.micromata.borgbutler.config
+package de.micromata.borgbutler.config
 
-import de.micromata.borgbutler.config.Definitions
 import de.micromata.borgbutler.json.JsonUtils
 import mu.KotlinLogging
 import org.apache.commons.io.FileUtils
-import org.slf4j.LoggerFactory
 import java.io.File
-import java.io.IOException
 import java.text.SimpleDateFormat
 import java.util.*
 
diff --git a/borgbutler-core/src/main/kotlin/org/micromata/borgbutler/config/YamlUtils.kt b/borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/YamlUtils.kt
similarity index 97%
rename from borgbutler-core/src/main/kotlin/org/micromata/borgbutler/config/YamlUtils.kt
rename to borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/YamlUtils.kt
index fc8b5f4..faa3fa1 100644
--- a/borgbutler-core/src/main/kotlin/org/micromata/borgbutler/config/YamlUtils.kt
+++ b/borgbutler-core/src/main/kotlin/de/micromata/borgbutler/config/YamlUtils.kt
@@ -1,4 +1,4 @@
-package org.micromata.borgbutler.config
+package de.micromata.borgbutler.config
 
 import com.fasterxml.jackson.annotation.JsonInclude
 import com.fasterxml.jackson.core.type.TypeReference
diff --git a/borgbutler-core/src/main/kotlin/de/micromata/borgbutler/jobs/AbstractCommandLineJob.kt b/borgbutler-core/src/main/kotlin/de/micromata/borgbutler/jobs/AbstractCommandLineJob.kt
new file mode 100644
index 0000000..527abbf
--- /dev/null
+++ b/borgbutler-core/src/main/kotlin/de/micromata/borgbutler/jobs/AbstractCommandLineJob.kt
@@ -0,0 +1,194 @@
+package de.micromata.borgbutler.jobs
+
+import de.micromata.borgbutler.config.Definitions
+import mu.KotlinLogging
+import org.apache.commons.exec.*
+import org.apache.commons.exec.environment.EnvironmentUtils
+import org.apache.commons.io.output.ByteArrayOutputStream
+import org.apache.commons.lang3.StringUtils
+import java.io.File
+import java.io.IOException
+
+private val log = KotlinLogging.logger {}
+
+/**
+ * A queue is important because Borg doesn't support parallel calls for one repository.
+ * For each repository one single queue is allocated.
+ */
+abstract class AbstractCommandLineJob : AbstractJob<String>() {
+    private var watchdog: ExecuteWatchdog? = null
+    var isExecuteStarted = false
+        private set
+    private var commandLine: CommandLine? = null
+
+    /**
+     * The command line as string. This property is also used as ID for detecting multiple borg calls.
+     */
+    private var commandLineAsString: String? = null
+    var workingDirectory: File? = null
+        private set
+    var description: String? = null
+        private set
+    protected var outputStream = ByteArrayOutputStream()
+    protected var errorOutputStream = ByteArrayOutputStream()
+
+    @JvmField
+    protected var logError = true
+    protected abstract fun buildCommandLine(): CommandLine?
+    override fun getId(): Any {
+        return getCommandLineAsString()!!
+    }
+
+    fun getCommandLineAsString(): String? {
+        if (commandLine == null) {
+            commandLine = buildCommandLine()
+        }
+        if (commandLine == null) {
+            return null
+        }
+        if (commandLineAsString == null) {
+            commandLineAsString = commandLine!!.executable + " " + StringUtils.join(
+                commandLine!!.arguments, " "
+            )
+        }
+        return commandLineAsString
+    }
+
+    override fun execute(): JobResult<String>? {
+        getCommandLineAsString()
+        if (commandLine == null) {
+            return null
+        }
+        val executor = DefaultExecutor()
+        if (workingDirectory != null) {
+            executor.workingDirectory = workingDirectory
+        }
+        //executor.setExitValue(2);
+        watchdog = ExecuteWatchdog(ExecuteWatchdog.INFINITE_TIMEOUT)
+        executor.watchdog = watchdog
+        //  ExecuteResultHandler handler = new DefaultExecuteResultHandler();
+        val streamHandler = PumpStreamHandler(object : LogOutputStream() {
+            override fun processLine(line: String, level: Int) {
+                processStdOutLine(line, level)
+            }
+        }, object : LogOutputStream() {
+            override fun processLine(line: String, level: Int) {
+                processStdErrLine(line, level)
+            }
+        })
+        executor.streamHandler = streamHandler
+        val msg =
+            if (StringUtils.isNotBlank(description)) "$description ('$commandLineAsString')..." else "Executing '$commandLineAsString'..."
+        log.info(msg)
+        //log.info("Environment: ${environment?.entries?.joinToString { "${it.key}='${it.value}'" }}")
+        isExecuteStarted = true
+        val result = JobResult<String>()
+        try {
+            executor.execute(commandLine, environment)
+            result.status = JobResult.Status.OK
+            log.info("$msg Done.")
+        } catch (ex: Exception) {
+            result.status = JobResult.Status.ERROR
+            if (logError && !isCancellationRequested && status != Status.CANCELLED) {
+                log.error("Execution failed for job: '" + commandLineAsString + "': " + ex.message)
+                log.error(
+                    "Error output of job '" + commandLineAsString + "': "
+                            + getErrorString(2000)
+                )
+            }
+            failed()
+        }
+        result.resultObject = outputStream.toString(Definitions.STD_CHARSET)
+        return result
+    }
+
+    /**
+     * @param maxlength The result string will be abbreviated (in the middle).
+     * @return
+     * @see StringUtils.abbreviateMiddle
+     */
+    fun getErrorString(maxlength: Int): String {
+        return StringUtils.abbreviateMiddle(
+            errorOutputStream.toString(Definitions.STD_CHARSET),
+            "\n    [... ***** error log abbreviated ***** ...]\n", maxlength
+        )
+    }
+
+    open fun processStdOutLine(line: String, level: Int) {
+        //log.info(line);
+        try {
+            outputStream.write(line.toByteArray())
+            outputStream.write("\n".toByteArray())
+        } catch (ex: IOException) {
+            log.error(ex.message, ex)
+        }
+    }
+
+    open fun processStdErrLine(line: String, level: Int) {
+        //log.info(line);
+        try {
+            errorOutputStream.write(line.toByteArray())
+            errorOutputStream.write("\n".toByteArray())
+        } catch (ex: IOException) {
+            log.error(ex.message, ex)
+        }
+    }
+
+    override fun cancelRunningProcess() {
+        if (watchdog != null) {
+            log.info("Cancelling job #$uniqueJobNumber: $id")
+            watchdog!!.destroyProcess()
+            watchdog = null
+        }
+    }
+
+    @get:Throws(IOException::class)
+    protected open val environment: Map<String?, String?>?
+        get() = null
+
+    protected fun addEnvironmentVariable(env: Map<String?, String?>?, name: String, value: String) {
+        if (StringUtils.isNotBlank(value)) {
+            EnvironmentUtils.addVariableToEnvironment(env, "$name=$value")
+        }
+    }
+
+    /**
+     * @param env
+     * @param variable Variable in format "variable=value".
+     */
+    protected fun addEnvironmentVariable(env: Map<String?, String?>?, variable: String?) {
+        if (StringUtils.isNotBlank(variable)) {
+            EnvironmentUtils.addVariableToEnvironment(env, variable)
+        }
+    }
+
+    /**
+     * Frees the output streams.
+     * Should be called after a job was done, failed or cancelled while running.
+     */
+    open fun cleanUp() {
+        log.debug("Freeing resources of job: $commandLineAsString")
+        outputStream = ByteArrayOutputStream()
+        errorOutputStream = ByteArrayOutputStream()
+    }
+
+    protected fun setExecuteStarted(executeStarted: Boolean): AbstractCommandLineJob {
+        isExecuteStarted = executeStarted
+        return this
+    }
+
+    protected fun setCommandLineAsString(commandLineAsString: String?): AbstractCommandLineJob {
+        this.commandLineAsString = commandLineAsString
+        return this
+    }
+
+    fun setWorkingDirectory(workingDirectory: File?): AbstractCommandLineJob {
+        this.workingDirectory = workingDirectory
+        return this
+    }
+
+    fun setDescription(description: String?): AbstractCommandLineJob {
+        this.description = description
+        return this
+    }
+}
diff --git a/borgbutler-core/src/test/java/de/micromata/borgbutler/cache/CacheTest.java b/borgbutler-core/src/test/java/de/micromata/borgbutler/cache/CacheTest.java
index f65bb69..ec173b6 100644
--- a/borgbutler-core/src/test/java/de/micromata/borgbutler/cache/CacheTest.java
+++ b/borgbutler-core/src/test/java/de/micromata/borgbutler/cache/CacheTest.java
@@ -1,8 +1,8 @@
 package de.micromata.borgbutler.cache;
 
 import de.micromata.borgbutler.config.BorgRepoConfig;
-import org.micromata.borgbutler.config.Configuration;
-import org.micromata.borgbutler.config.ConfigurationHandler;
+import de.micromata.borgbutler.config.Configuration;
+import de.micromata.borgbutler.config.ConfigurationHandler;
 import de.micromata.borgbutler.data.Archive;
 import de.micromata.borgbutler.data.Repository;
 import de.micromata.borgbutler.json.borg.BorgFilesystemItem;
diff --git a/borgbutler-core/src/test/java/de/micromata/borgbutler/config/ConfigHandlerTest.java b/borgbutler-core/src/test/java/de/micromata/borgbutler/config/ConfigHandlerTest.java
index 57e396a..e0240ef 100644
--- a/borgbutler-core/src/test/java/de/micromata/borgbutler/config/ConfigHandlerTest.java
+++ b/borgbutler-core/src/test/java/de/micromata/borgbutler/config/ConfigHandlerTest.java
@@ -2,7 +2,7 @@
 
 import org.apache.commons.io.FileUtils;
 import org.junit.jupiter.api.Test;
-import org.micromata.borgbutler.config.ConfigurationHandler;
+import de.micromata.borgbutler.config.ConfigurationHandler;
 
 import java.io.File;
 import java.io.IOException;
diff --git a/borgbutler-docker/app/Dockerfile b/borgbutler-docker/app/Dockerfile
index 49a3341..e24fca2 100644
--- a/borgbutler-docker/app/Dockerfile
+++ b/borgbutler-docker/app/Dockerfile
@@ -30,6 +30,6 @@
 # Variable expansion doesn't work for ENTRYPOINT definition as array, but array is required, because graceful shutdown of
 # container isn't given if java is started via 'sh -c' as it will be done by ENTRYPOINT java .....
 # Java options are modifiable by user through own ENTRYPOINT definition on docker run or in docker-compose.yml.
-ENTRYPOINT ["java", "-Xms1g", "-Xmx1g", "-cp", "app/web/*:app/lib/*", "-DborgbutlerHome=/BorgButler/", "-DapplicationHome=/app", "-DbindAddress=0.0.0.0", "-DallowedClientIps=172.17.", "de.micromata.borgbutler.server.Main", "-q"]
+ENTRYPOINT ["java", "-Xms4g", "-Xmx4g", "-cp", "app/web/*:app/lib/*", "-DborgbutlerHome=/BorgButler/", "-DapplicationHome=/app", "-DbindAddress=0.0.0.0", "-DallowedClientIps=172.17.", "de.micromata.borgbutler.server.Main", "-q"]
 
 MAINTAINER Micromata
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/BorgInstallation.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/BorgInstallation.java
index 520e818..2f288ff 100644
--- a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/BorgInstallation.java
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/BorgInstallation.java
@@ -1,8 +1,8 @@
 package de.micromata.borgbutler.server;
 
 import de.micromata.borgbutler.BorgCommands;
-import org.micromata.borgbutler.config.Configuration;
-import org.micromata.borgbutler.config.ConfigurationHandler;
+import de.micromata.borgbutler.config.Configuration;
+import de.micromata.borgbutler.config.ConfigurationHandler;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.http.HttpResponse;
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/Main.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/Main.java
index f783ebc..036d7fe 100644
--- a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/Main.java
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/Main.java
@@ -1,7 +1,7 @@
 package de.micromata.borgbutler.server;
 
 import de.micromata.borgbutler.cache.ButlerCache;
-import org.micromata.borgbutler.config.ConfigurationHandler;
+import de.micromata.borgbutler.config.ConfigurationHandler;
 import de.micromata.borgbutler.json.borg.BorgFilesystemItem;
 import de.micromata.borgbutler.server.jetty.JettyServer;
 import de.micromata.borgbutler.server.user.SingleUserManager;
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/ServerConfiguration.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/ServerConfiguration.java
index 6dd2b23..8e24c4e 100644
--- a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/ServerConfiguration.java
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/ServerConfiguration.java
@@ -1,8 +1,8 @@
 package de.micromata.borgbutler.server;
 
 import org.apache.commons.lang3.StringUtils;
-import org.micromata.borgbutler.config.Configuration;
-import org.micromata.borgbutler.config.ConfigurationHandler;
+import de.micromata.borgbutler.config.Configuration;
+import de.micromata.borgbutler.config.ConfigurationHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java
index 0897c94..90fe225 100644
--- a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ArchivesRest.java
@@ -3,7 +3,7 @@
 import de.micromata.borgbutler.BorgCommands;
 import de.micromata.borgbutler.cache.ButlerCache;
 import de.micromata.borgbutler.config.BorgRepoConfig;
-import org.micromata.borgbutler.config.ConfigurationHandler;
+import de.micromata.borgbutler.config.ConfigurationHandler;
 import de.micromata.borgbutler.data.Archive;
 import de.micromata.borgbutler.data.DiffFileSystemFilter;
 import de.micromata.borgbutler.data.FileSystemFilter;
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/BorgRepoConfigsRest.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/BorgRepoConfigsRest.java
index 408d55a..adfe573 100644
--- a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/BorgRepoConfigsRest.java
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/BorgRepoConfigsRest.java
@@ -4,7 +4,7 @@
 import de.micromata.borgbutler.BorgCommands;
 import de.micromata.borgbutler.cache.ButlerCache;
 import de.micromata.borgbutler.config.BorgRepoConfig;
-import org.micromata.borgbutler.config.ConfigurationHandler;
+import de.micromata.borgbutler.config.ConfigurationHandler;
 import de.micromata.borgbutler.data.Repository;
 import de.micromata.borgbutler.jobs.JobResult;
 import de.micromata.borgbutler.json.JsonUtils;
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ConfigurationRest.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ConfigurationRest.java
index 9a1d5d8..a5b2ad4 100644
--- a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ConfigurationRest.java
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/ConfigurationRest.java
@@ -1,7 +1,7 @@
 package de.micromata.borgbutler.server.rest;
 
 import de.micromata.borgbutler.cache.ButlerCache;
-import org.micromata.borgbutler.config.ConfigurationHandler;
+import de.micromata.borgbutler.config.ConfigurationHandler;
 import de.micromata.borgbutler.json.JsonUtils;
 import de.micromata.borgbutler.server.BorgInstallation;
 import de.micromata.borgbutler.server.ServerConfiguration;
diff --git a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/JobsRest.java b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/JobsRest.java
index 151dfbf..be744f0 100644
--- a/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/JobsRest.java
+++ b/borgbutler-server/src/main/java/de/micromata/borgbutler/server/rest/JobsRest.java
@@ -3,7 +3,7 @@
 import de.micromata.borgbutler.BorgJob;
 import de.micromata.borgbutler.BorgQueueExecutor;
 import de.micromata.borgbutler.config.BorgRepoConfig;
-import org.micromata.borgbutler.config.ConfigurationHandler;
+import de.micromata.borgbutler.config.ConfigurationHandler;
 import de.micromata.borgbutler.jobs.AbstractJob;
 import de.micromata.borgbutler.json.JsonUtils;
 import de.micromata.borgbutler.json.borg.ProgressInfo;
diff --git a/borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgInstallationTest.java b/borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgInstallationTest.java
index 490506f..dc97e98 100644
--- a/borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgInstallationTest.java
+++ b/borgbutler-server/src/test/java/de/micromata/borgbutler/server/BorgInstallationTest.java
@@ -1,6 +1,6 @@
 package de.micromata.borgbutler.server;
 
-import org.micromata.borgbutler.config.ConfigurationHandler;
+import de.micromata.borgbutler.config.ConfigurationHandler;
 import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

--
Gitblit v1.10.0